mirror of
https://github.com/intel/llvm.git
synced 2026-02-09 01:52:26 +08:00
[ASTImporter] Fix redecl chain of classes and class templates
Summary: The crux of the issue that is being fixed is that lookup could not find previous decls of a friend class. The solution involves making the friend declarations visible in their decl context (i.e. adding them to the lookup table). Also, we simplify `VisitRecordDecl` greatly. This fix involves two other repairs (without these the unittests fail): (1) We could not handle the addition of injected class types properly when a redecl chain was involved, now this is fixed. (2) DeclContext::removeDecl failed if the lookup table in Vector form did not contain the to be removed element. This caused troubles in ASTImporter::ImportDeclContext. This is also fixed. Reviewers: a_sidorin, balazske, a.sidorin Subscribers: rnkovacs, dkrupp, Szelethus, cfe-commits Differential Revision: https://reviews.llvm.org/D53655 llvm-svn: 349349
This commit is contained in:
@@ -300,6 +300,16 @@ Example matches f
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>></td><td class="name" onclick="toggle('indirectFieldDecl0')"><a name="indirectFieldDecl0Anchor">indirectFieldDecl</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1IndirectFieldDecl.html">IndirectFieldDecl</a>>...</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="indirectFieldDecl0"><pre>Matches indirect field declarations.
|
||||
|
||||
Given
|
||||
struct X { struct { int a; }; };
|
||||
indirectFieldDecl()
|
||||
matches 'a'.
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>></td><td class="name" onclick="toggle('labelDecl0')"><a name="labelDecl0Anchor">labelDecl</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1LabelDecl.html">LabelDecl</a>>...</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="labelDecl0"><pre>Matches a declaration of label.
|
||||
|
||||
|
||||
@@ -1158,6 +1158,17 @@ extern const internal::VariadicDynCastAllOfMatcher<Decl, VarDecl> varDecl;
|
||||
/// matches 'm'.
|
||||
extern const internal::VariadicDynCastAllOfMatcher<Decl, FieldDecl> fieldDecl;
|
||||
|
||||
/// Matches indirect field declarations.
|
||||
///
|
||||
/// Given
|
||||
/// \code
|
||||
/// struct X { struct { int a; }; };
|
||||
/// \endcode
|
||||
/// indirectFieldDecl()
|
||||
/// matches 'a'.
|
||||
extern const internal::VariadicDynCastAllOfMatcher<Decl, IndirectFieldDecl>
|
||||
indirectFieldDecl;
|
||||
|
||||
/// Matches function declarations.
|
||||
///
|
||||
/// Example matches f
|
||||
|
||||
@@ -122,6 +122,8 @@ namespace clang {
|
||||
return getCanonicalForwardRedeclChain<FunctionDecl>(FD);
|
||||
if (auto *VD = dyn_cast<VarDecl>(D))
|
||||
return getCanonicalForwardRedeclChain<VarDecl>(VD);
|
||||
if (auto *TD = dyn_cast<TagDecl>(D))
|
||||
return getCanonicalForwardRedeclChain<TagDecl>(TD);
|
||||
llvm_unreachable("Bad declaration kind");
|
||||
}
|
||||
|
||||
@@ -2607,10 +2609,9 @@ ExpectedDecl ASTNodeImporter::VisitRecordDecl(RecordDecl *D) {
|
||||
return std::move(Err);
|
||||
IDNS = Decl::IDNS_Ordinary;
|
||||
} else if (Importer.getToContext().getLangOpts().CPlusPlus)
|
||||
IDNS |= Decl::IDNS_Ordinary;
|
||||
IDNS |= Decl::IDNS_Ordinary | Decl::IDNS_TagFriend;
|
||||
|
||||
// We may already have a record of the same name; try to find and match it.
|
||||
RecordDecl *AdoptDecl = nullptr;
|
||||
RecordDecl *PrevDecl = nullptr;
|
||||
if (!DC->isFunctionOrMethod()) {
|
||||
SmallVector<NamedDecl *, 4> ConflictingDecls;
|
||||
@@ -2643,26 +2644,22 @@ ExpectedDecl ASTNodeImporter::VisitRecordDecl(RecordDecl *D) {
|
||||
}
|
||||
|
||||
if (auto *FoundRecord = dyn_cast<RecordDecl>(Found)) {
|
||||
if (!SearchName) {
|
||||
// Do not emit false positive diagnostic in case of unnamed
|
||||
// struct/union and in case of anonymous structs. Would be false
|
||||
// because there may be several anonymous/unnamed structs in a class.
|
||||
// E.g. these are both valid:
|
||||
// struct A { // unnamed structs
|
||||
// struct { struct A *next; } entry0;
|
||||
// struct { struct A *next; } entry1;
|
||||
// };
|
||||
// struct X { struct { int a; }; struct { int b; }; }; // anon structs
|
||||
if (!SearchName)
|
||||
if (!IsStructuralMatch(D, FoundRecord, false))
|
||||
continue;
|
||||
} else {
|
||||
if (!IsStructuralMatch(D, FoundRecord)) {
|
||||
ConflictingDecls.push_back(FoundDecl);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
PrevDecl = FoundRecord;
|
||||
|
||||
if (RecordDecl *FoundDef = FoundRecord->getDefinition()) {
|
||||
if ((SearchName && !D->isCompleteDefinition() && !IsFriendTemplate)
|
||||
|| (D->isCompleteDefinition() &&
|
||||
D->isAnonymousStructOrUnion()
|
||||
== FoundDef->isAnonymousStructOrUnion())) {
|
||||
// The record types structurally match, or the "from" translation
|
||||
// unit only had a forward declaration anyway; call it the same
|
||||
// function.
|
||||
if (IsStructuralMatch(D, FoundRecord)) {
|
||||
RecordDecl *FoundDef = FoundRecord->getDefinition();
|
||||
if (D->isThisDeclarationADefinition() && FoundDef) {
|
||||
// FIXME: Structural equivalence check should check for same
|
||||
// user-defined methods.
|
||||
Importer.MapImported(D, FoundDef);
|
||||
@@ -2670,46 +2667,20 @@ ExpectedDecl ASTNodeImporter::VisitRecordDecl(RecordDecl *D) {
|
||||
auto *FoundCXX = dyn_cast<CXXRecordDecl>(FoundDef);
|
||||
assert(FoundCXX && "Record type mismatch");
|
||||
|
||||
if (D->isCompleteDefinition() && !Importer.isMinimalImport())
|
||||
if (!Importer.isMinimalImport())
|
||||
// FoundDef may not have every implicit method that D has
|
||||
// because implicit methods are created only if they are used.
|
||||
if (Error Err = ImportImplicitMethods(DCXX, FoundCXX))
|
||||
return std::move(Err);
|
||||
}
|
||||
return FoundDef;
|
||||
}
|
||||
if (IsFriendTemplate)
|
||||
continue;
|
||||
} else if (!D->isCompleteDefinition()) {
|
||||
// We have a forward declaration of this type, so adopt that forward
|
||||
// declaration rather than building a new one.
|
||||
|
||||
// If one or both can be completed from external storage then try one
|
||||
// last time to complete and compare them before doing this.
|
||||
|
||||
if (FoundRecord->hasExternalLexicalStorage() &&
|
||||
!FoundRecord->isCompleteDefinition())
|
||||
FoundRecord->getASTContext().getExternalSource()->CompleteType(FoundRecord);
|
||||
if (D->hasExternalLexicalStorage())
|
||||
D->getASTContext().getExternalSource()->CompleteType(D);
|
||||
|
||||
if (FoundRecord->isCompleteDefinition() &&
|
||||
D->isCompleteDefinition() &&
|
||||
!IsStructuralMatch(D, FoundRecord)) {
|
||||
ConflictingDecls.push_back(FoundDecl);
|
||||
continue;
|
||||
}
|
||||
|
||||
AdoptDecl = FoundRecord;
|
||||
continue;
|
||||
PrevDecl = FoundRecord->getMostRecentDecl();
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
} else if (isa<ValueDecl>(Found))
|
||||
continue;
|
||||
}
|
||||
|
||||
ConflictingDecls.push_back(FoundDecl);
|
||||
}
|
||||
} // for
|
||||
|
||||
if (!ConflictingDecls.empty() && SearchName) {
|
||||
Name = Importer.HandleNameConflict(Name, DC, IDNS,
|
||||
@@ -2725,79 +2696,90 @@ ExpectedDecl ASTNodeImporter::VisitRecordDecl(RecordDecl *D) {
|
||||
return BeginLocOrErr.takeError();
|
||||
|
||||
// Create the record declaration.
|
||||
RecordDecl *D2 = AdoptDecl;
|
||||
if (!D2) {
|
||||
CXXRecordDecl *D2CXX = nullptr;
|
||||
if (auto *DCXX = dyn_cast<CXXRecordDecl>(D)) {
|
||||
if (DCXX->isLambda()) {
|
||||
auto TInfoOrErr = import(DCXX->getLambdaTypeInfo());
|
||||
if (!TInfoOrErr)
|
||||
return TInfoOrErr.takeError();
|
||||
if (GetImportedOrCreateSpecialDecl(
|
||||
D2CXX, CXXRecordDecl::CreateLambda, D, Importer.getToContext(),
|
||||
DC, *TInfoOrErr, Loc, DCXX->isDependentLambda(),
|
||||
DCXX->isGenericLambda(), DCXX->getLambdaCaptureDefault()))
|
||||
return D2CXX;
|
||||
ExpectedDecl CDeclOrErr = import(DCXX->getLambdaContextDecl());
|
||||
if (!CDeclOrErr)
|
||||
return CDeclOrErr.takeError();
|
||||
D2CXX->setLambdaMangling(DCXX->getLambdaManglingNumber(), *CDeclOrErr);
|
||||
} else if (DCXX->isInjectedClassName()) {
|
||||
// We have to be careful to do a similar dance to the one in
|
||||
// Sema::ActOnStartCXXMemberDeclarations
|
||||
CXXRecordDecl *const PrevDecl = nullptr;
|
||||
const bool DelayTypeCreation = true;
|
||||
if (GetImportedOrCreateDecl(D2CXX, D, Importer.getToContext(),
|
||||
D->getTagKind(), DC, *BeginLocOrErr, Loc,
|
||||
Name.getAsIdentifierInfo(), PrevDecl,
|
||||
DelayTypeCreation))
|
||||
return D2CXX;
|
||||
Importer.getToContext().getTypeDeclType(
|
||||
D2CXX, dyn_cast<CXXRecordDecl>(DC));
|
||||
} else {
|
||||
if (GetImportedOrCreateDecl(D2CXX, D, Importer.getToContext(),
|
||||
D->getTagKind(), DC, *BeginLocOrErr, Loc,
|
||||
Name.getAsIdentifierInfo(),
|
||||
cast_or_null<CXXRecordDecl>(PrevDecl)))
|
||||
return D2CXX;
|
||||
}
|
||||
RecordDecl *D2 = nullptr;
|
||||
CXXRecordDecl *D2CXX = nullptr;
|
||||
if (auto *DCXX = dyn_cast<CXXRecordDecl>(D)) {
|
||||
if (DCXX->isLambda()) {
|
||||
auto TInfoOrErr = import(DCXX->getLambdaTypeInfo());
|
||||
if (!TInfoOrErr)
|
||||
return TInfoOrErr.takeError();
|
||||
if (GetImportedOrCreateSpecialDecl(
|
||||
D2CXX, CXXRecordDecl::CreateLambda, D, Importer.getToContext(),
|
||||
DC, *TInfoOrErr, Loc, DCXX->isDependentLambda(),
|
||||
DCXX->isGenericLambda(), DCXX->getLambdaCaptureDefault()))
|
||||
return D2CXX;
|
||||
ExpectedDecl CDeclOrErr = import(DCXX->getLambdaContextDecl());
|
||||
if (!CDeclOrErr)
|
||||
return CDeclOrErr.takeError();
|
||||
D2CXX->setLambdaMangling(DCXX->getLambdaManglingNumber(), *CDeclOrErr);
|
||||
} else if (DCXX->isInjectedClassName()) {
|
||||
// We have to be careful to do a similar dance to the one in
|
||||
// Sema::ActOnStartCXXMemberDeclarations
|
||||
const bool DelayTypeCreation = true;
|
||||
if (GetImportedOrCreateDecl(
|
||||
D2CXX, D, Importer.getToContext(), D->getTagKind(), DC,
|
||||
*BeginLocOrErr, Loc, Name.getAsIdentifierInfo(),
|
||||
cast_or_null<CXXRecordDecl>(PrevDecl), DelayTypeCreation))
|
||||
return D2CXX;
|
||||
Importer.getToContext().getTypeDeclType(
|
||||
D2CXX, dyn_cast<CXXRecordDecl>(DC));
|
||||
} else {
|
||||
if (GetImportedOrCreateDecl(D2CXX, D, Importer.getToContext(),
|
||||
D->getTagKind(), DC, *BeginLocOrErr, Loc,
|
||||
Name.getAsIdentifierInfo(),
|
||||
cast_or_null<CXXRecordDecl>(PrevDecl)))
|
||||
return D2CXX;
|
||||
}
|
||||
|
||||
D2 = D2CXX;
|
||||
D2->setAccess(D->getAccess());
|
||||
D2->setLexicalDeclContext(LexicalDC);
|
||||
if (!DCXX->getDescribedClassTemplate() || DCXX->isImplicit())
|
||||
LexicalDC->addDeclInternal(D2);
|
||||
D2 = D2CXX;
|
||||
D2->setAccess(D->getAccess());
|
||||
D2->setLexicalDeclContext(LexicalDC);
|
||||
if (!DCXX->getDescribedClassTemplate() || DCXX->isImplicit())
|
||||
LexicalDC->addDeclInternal(D2);
|
||||
|
||||
if (ClassTemplateDecl *FromDescribed =
|
||||
DCXX->getDescribedClassTemplate()) {
|
||||
ClassTemplateDecl *ToDescribed;
|
||||
if (Error Err = importInto(ToDescribed, FromDescribed))
|
||||
return std::move(Err);
|
||||
D2CXX->setDescribedClassTemplate(ToDescribed);
|
||||
if (!DCXX->isInjectedClassName() && !IsFriendTemplate) {
|
||||
// In a record describing a template the type should be an
|
||||
// InjectedClassNameType (see Sema::CheckClassTemplate). Update the
|
||||
// previously set type to the correct value here (ToDescribed is not
|
||||
// available at record create).
|
||||
// FIXME: The previous type is cleared but not removed from
|
||||
// ASTContext's internal storage.
|
||||
CXXRecordDecl *Injected = nullptr;
|
||||
for (NamedDecl *Found : D2CXX->noload_lookup(Name)) {
|
||||
auto *Record = dyn_cast<CXXRecordDecl>(Found);
|
||||
if (Record && Record->isInjectedClassName()) {
|
||||
Injected = Record;
|
||||
break;
|
||||
}
|
||||
}
|
||||
D2CXX->setTypeForDecl(nullptr);
|
||||
Importer.getToContext().getInjectedClassNameType(D2CXX,
|
||||
ToDescribed->getInjectedClassNameSpecialization());
|
||||
if (Injected) {
|
||||
Injected->setTypeForDecl(nullptr);
|
||||
Importer.getToContext().getTypeDeclType(Injected, D2CXX);
|
||||
if (LexicalDC != DC && D->isInIdentifierNamespace(Decl::IDNS_TagFriend))
|
||||
DC->makeDeclVisibleInContext(D2);
|
||||
|
||||
if (ClassTemplateDecl *FromDescribed =
|
||||
DCXX->getDescribedClassTemplate()) {
|
||||
ClassTemplateDecl *ToDescribed;
|
||||
if (Error Err = importInto(ToDescribed, FromDescribed))
|
||||
return std::move(Err);
|
||||
D2CXX->setDescribedClassTemplate(ToDescribed);
|
||||
if (!DCXX->isInjectedClassName() && !IsFriendTemplate) {
|
||||
// In a record describing a template the type should be an
|
||||
// InjectedClassNameType (see Sema::CheckClassTemplate). Update the
|
||||
// previously set type to the correct value here (ToDescribed is not
|
||||
// available at record create).
|
||||
// FIXME: The previous type is cleared but not removed from
|
||||
// ASTContext's internal storage.
|
||||
CXXRecordDecl *Injected = nullptr;
|
||||
for (NamedDecl *Found : D2CXX->noload_lookup(Name)) {
|
||||
auto *Record = dyn_cast<CXXRecordDecl>(Found);
|
||||
if (Record && Record->isInjectedClassName()) {
|
||||
Injected = Record;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (MemberSpecializationInfo *MemberInfo =
|
||||
// Create an injected type for the whole redecl chain.
|
||||
SmallVector<Decl *, 2> Redecls =
|
||||
getCanonicalForwardRedeclChain(D2CXX);
|
||||
for (auto *R : Redecls) {
|
||||
auto *RI = cast<CXXRecordDecl>(R);
|
||||
RI->setTypeForDecl(nullptr);
|
||||
// Below we create a new injected type and assign that to the
|
||||
// canonical decl, subsequent declarations in the chain will reuse
|
||||
// that type.
|
||||
Importer.getToContext().getInjectedClassNameType(
|
||||
RI, ToDescribed->getInjectedClassNameSpecialization());
|
||||
}
|
||||
// Set the new type for the previous injected decl too.
|
||||
if (Injected) {
|
||||
Injected->setTypeForDecl(nullptr);
|
||||
Importer.getToContext().getTypeDeclType(Injected, D2CXX);
|
||||
}
|
||||
}
|
||||
} else if (MemberSpecializationInfo *MemberInfo =
|
||||
DCXX->getMemberSpecializationInfo()) {
|
||||
TemplateSpecializationKind SK =
|
||||
MemberInfo->getTemplateSpecializationKind();
|
||||
@@ -2814,27 +2796,24 @@ ExpectedDecl ASTNodeImporter::VisitRecordDecl(RecordDecl *D) {
|
||||
*POIOrErr);
|
||||
else
|
||||
return POIOrErr.takeError();
|
||||
}
|
||||
|
||||
} else {
|
||||
if (GetImportedOrCreateDecl(D2, D, Importer.getToContext(),
|
||||
D->getTagKind(), DC, *BeginLocOrErr, Loc,
|
||||
Name.getAsIdentifierInfo(), PrevDecl))
|
||||
return D2;
|
||||
D2->setLexicalDeclContext(LexicalDC);
|
||||
LexicalDC->addDeclInternal(D2);
|
||||
}
|
||||
|
||||
if (auto QualifierLocOrErr = import(D->getQualifierLoc()))
|
||||
D2->setQualifierInfo(*QualifierLocOrErr);
|
||||
else
|
||||
return QualifierLocOrErr.takeError();
|
||||
|
||||
if (D->isAnonymousStructOrUnion())
|
||||
D2->setAnonymousStructOrUnion(true);
|
||||
} else {
|
||||
if (GetImportedOrCreateDecl(D2, D, Importer.getToContext(),
|
||||
D->getTagKind(), DC, *BeginLocOrErr, Loc,
|
||||
Name.getAsIdentifierInfo(), PrevDecl))
|
||||
return D2;
|
||||
D2->setLexicalDeclContext(LexicalDC);
|
||||
LexicalDC->addDeclInternal(D2);
|
||||
}
|
||||
|
||||
Importer.MapImported(D, D2);
|
||||
if (auto QualifierLocOrErr = import(D->getQualifierLoc()))
|
||||
D2->setQualifierInfo(*QualifierLocOrErr);
|
||||
else
|
||||
return QualifierLocOrErr.takeError();
|
||||
|
||||
if (D->isAnonymousStructOrUnion())
|
||||
D2->setAnonymousStructOrUnion(true);
|
||||
|
||||
if (D->isCompleteDefinition())
|
||||
if (Error Err = ImportDefinition(D, D2, IDK_Default))
|
||||
@@ -4990,14 +4969,12 @@ static ClassTemplateDecl *getDefinition(ClassTemplateDecl *D) {
|
||||
ExpectedDecl ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) {
|
||||
bool IsFriend = D->getFriendObjectKind() != Decl::FOK_None;
|
||||
|
||||
// If this record has a definition in the translation unit we're coming from,
|
||||
// but this particular declaration is not that definition, import the
|
||||
// If this template has a definition in the translation unit we're coming
|
||||
// from, but this particular declaration is not that definition, import the
|
||||
// definition and map to that.
|
||||
auto *Definition =
|
||||
cast_or_null<CXXRecordDecl>(D->getTemplatedDecl()->getDefinition());
|
||||
if (Definition && Definition != D->getTemplatedDecl() && !IsFriend) {
|
||||
if (ExpectedDecl ImportedDefOrErr = import(
|
||||
Definition->getDescribedClassTemplate()))
|
||||
ClassTemplateDecl *Definition = getDefinition(D);
|
||||
if (Definition && Definition != D && !IsFriend) {
|
||||
if (ExpectedDecl ImportedDefOrErr = import(Definition))
|
||||
return Importer.MapImported(D, *ImportedDefOrErr);
|
||||
else
|
||||
return ImportedDefOrErr.takeError();
|
||||
@@ -5013,38 +4990,29 @@ ExpectedDecl ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) {
|
||||
if (ToD)
|
||||
return ToD;
|
||||
|
||||
ClassTemplateDecl *FoundByLookup = nullptr;
|
||||
|
||||
// We may already have a template of the same name; try to find and match it.
|
||||
if (!DC->isFunctionOrMethod()) {
|
||||
SmallVector<NamedDecl *, 4> ConflictingDecls;
|
||||
SmallVector<NamedDecl *, 2> FoundDecls;
|
||||
DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
|
||||
for (auto *FoundDecl : FoundDecls) {
|
||||
if (!FoundDecl->isInIdentifierNamespace(Decl::IDNS_Ordinary))
|
||||
if (!FoundDecl->isInIdentifierNamespace(Decl::IDNS_Ordinary |
|
||||
Decl::IDNS_TagFriend))
|
||||
continue;
|
||||
|
||||
Decl *Found = FoundDecl;
|
||||
if (auto *FoundTemplate = dyn_cast<ClassTemplateDecl>(Found)) {
|
||||
|
||||
// The class to be imported is a definition.
|
||||
if (D->isThisDeclarationADefinition()) {
|
||||
// Lookup will find the fwd decl only if that is more recent than the
|
||||
// definition. So, try to get the definition if that is available in
|
||||
// the redecl chain.
|
||||
ClassTemplateDecl *TemplateWithDef = getDefinition(FoundTemplate);
|
||||
if (TemplateWithDef)
|
||||
FoundTemplate = TemplateWithDef;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
auto *FoundTemplate = dyn_cast<ClassTemplateDecl>(Found);
|
||||
if (FoundTemplate) {
|
||||
|
||||
if (IsStructuralMatch(D, FoundTemplate)) {
|
||||
if (!IsFriend) {
|
||||
Importer.MapImported(D->getTemplatedDecl(),
|
||||
FoundTemplate->getTemplatedDecl());
|
||||
return Importer.MapImported(D, FoundTemplate);
|
||||
ClassTemplateDecl *TemplateWithDef = getDefinition(FoundTemplate);
|
||||
if (D->isThisDeclarationADefinition() && TemplateWithDef) {
|
||||
return Importer.MapImported(D, TemplateWithDef);
|
||||
}
|
||||
|
||||
continue;
|
||||
FoundByLookup = FoundTemplate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5081,18 +5049,39 @@ ExpectedDecl ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) {
|
||||
|
||||
ToTemplated->setDescribedClassTemplate(D2);
|
||||
|
||||
if (ToTemplated->getPreviousDecl()) {
|
||||
assert(
|
||||
ToTemplated->getPreviousDecl()->getDescribedClassTemplate() &&
|
||||
"Missing described template");
|
||||
D2->setPreviousDecl(
|
||||
ToTemplated->getPreviousDecl()->getDescribedClassTemplate());
|
||||
}
|
||||
D2->setAccess(D->getAccess());
|
||||
D2->setLexicalDeclContext(LexicalDC);
|
||||
if (!IsFriend)
|
||||
|
||||
if (D->getDeclContext()->containsDeclAndLoad(D))
|
||||
DC->addDeclInternal(D2);
|
||||
if (DC != LexicalDC && D->getLexicalDeclContext()->containsDeclAndLoad(D))
|
||||
LexicalDC->addDeclInternal(D2);
|
||||
|
||||
if (FoundByLookup) {
|
||||
auto *Recent =
|
||||
const_cast<ClassTemplateDecl *>(FoundByLookup->getMostRecentDecl());
|
||||
|
||||
// It is possible that during the import of the class template definition
|
||||
// we start the import of a fwd friend decl of the very same class template
|
||||
// and we add the fwd friend decl to the lookup table. But the ToTemplated
|
||||
// had been created earlier and by that time the lookup could not find
|
||||
// anything existing, so it has no previous decl. Later, (still during the
|
||||
// import of the fwd friend decl) we start to import the definition again
|
||||
// and this time the lookup finds the previous fwd friend class template.
|
||||
// In this case we must set up the previous decl for the templated decl.
|
||||
if (!ToTemplated->getPreviousDecl()) {
|
||||
CXXRecordDecl *PrevTemplated =
|
||||
FoundByLookup->getTemplatedDecl()->getMostRecentDecl();
|
||||
if (ToTemplated != PrevTemplated)
|
||||
ToTemplated->setPreviousDecl(PrevTemplated);
|
||||
}
|
||||
|
||||
D2->setPreviousDecl(Recent);
|
||||
}
|
||||
|
||||
if (LexicalDC != DC && IsFriend)
|
||||
DC->makeDeclVisibleInContext(D2);
|
||||
|
||||
if (FromTemplated->isCompleteDefinition() &&
|
||||
!ToTemplated->isCompleteDefinition()) {
|
||||
// FIXME: Import definition!
|
||||
|
||||
@@ -1463,7 +1463,9 @@ void DeclContext::removeDecl(Decl *D) {
|
||||
if (Map) {
|
||||
StoredDeclsMap::iterator Pos = Map->find(ND->getDeclName());
|
||||
assert(Pos != Map->end() && "no lookup entry for decl");
|
||||
if (Pos->second.getAsVector() || Pos->second.getAsDecl() == ND)
|
||||
// Remove the decl only if it is contained.
|
||||
StoredDeclsList::DeclsTy *Vec = Pos->second.getAsVector();
|
||||
if ((Vec && is_contained(*Vec, ND)) || Pos->second.getAsDecl() == ND)
|
||||
Pos->second.remove(ND);
|
||||
}
|
||||
} while (DC->isTransparentContext() && (DC = DC->getParent()));
|
||||
|
||||
@@ -606,6 +606,8 @@ const internal::VariadicDynCastAllOfMatcher<Decl, CXXConversionDecl>
|
||||
cxxConversionDecl;
|
||||
const internal::VariadicDynCastAllOfMatcher<Decl, VarDecl> varDecl;
|
||||
const internal::VariadicDynCastAllOfMatcher<Decl, FieldDecl> fieldDecl;
|
||||
const internal::VariadicDynCastAllOfMatcher<Decl, IndirectFieldDecl>
|
||||
indirectFieldDecl;
|
||||
const internal::VariadicDynCastAllOfMatcher<Decl, FunctionDecl> functionDecl;
|
||||
const internal::VariadicDynCastAllOfMatcher<Decl, FunctionTemplateDecl>
|
||||
functionTemplateDecl;
|
||||
|
||||
@@ -212,6 +212,7 @@ RegistryMaps::RegistryMaps() {
|
||||
REGISTER_MATCHER(expr);
|
||||
REGISTER_MATCHER(exprWithCleanups);
|
||||
REGISTER_MATCHER(fieldDecl);
|
||||
REGISTER_MATCHER(indirectFieldDecl);
|
||||
REGISTER_MATCHER(floatLiteral);
|
||||
REGISTER_MATCHER(forEach);
|
||||
REGISTER_MATCHER(forEachArgumentWithParam);
|
||||
|
||||
@@ -11,9 +11,10 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/AST/ASTImporter.h"
|
||||
#include "MatchVerifier.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/ASTImporter.h"
|
||||
#include "clang/AST/DeclContextInternals.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "clang/ASTMatchers/ASTMatchers.h"
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
@@ -1808,6 +1809,65 @@ TEST_P(ASTImporterTestBase, ObjectsWithUnnamedStructType) {
|
||||
EXPECT_NE(To0->getCanonicalDecl(), To1->getCanonicalDecl());
|
||||
}
|
||||
|
||||
TEST_P(ASTImporterTestBase, AnonymousRecords) {
|
||||
auto *Code =
|
||||
R"(
|
||||
struct X {
|
||||
struct { int a; };
|
||||
struct { int b; };
|
||||
};
|
||||
)";
|
||||
Decl *FromTU0 = getTuDecl(Code, Lang_C, "input0.c");
|
||||
|
||||
Decl *FromTU1 = getTuDecl(Code, Lang_C, "input1.c");
|
||||
|
||||
auto *X0 =
|
||||
FirstDeclMatcher<RecordDecl>().match(FromTU0, recordDecl(hasName("X")));
|
||||
auto *X1 =
|
||||
FirstDeclMatcher<RecordDecl>().match(FromTU1, recordDecl(hasName("X")));
|
||||
Import(X0, Lang_C);
|
||||
Import(X1, Lang_C);
|
||||
|
||||
auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
|
||||
// We expect no (ODR) warning during the import.
|
||||
EXPECT_EQ(0u, ToTU->getASTContext().getDiagnostics().getNumWarnings());
|
||||
EXPECT_EQ(1u,
|
||||
DeclCounter<RecordDecl>().match(ToTU, recordDecl(hasName("X"))));
|
||||
}
|
||||
|
||||
TEST_P(ASTImporterTestBase, AnonymousRecordsReversed) {
|
||||
Decl *FromTU0 = getTuDecl(
|
||||
R"(
|
||||
struct X {
|
||||
struct { int a; };
|
||||
struct { int b; };
|
||||
};
|
||||
)",
|
||||
Lang_C, "input0.c");
|
||||
|
||||
Decl *FromTU1 = getTuDecl(
|
||||
R"(
|
||||
struct X { // reversed order
|
||||
struct { int b; };
|
||||
struct { int a; };
|
||||
};
|
||||
)",
|
||||
Lang_C, "input1.c");
|
||||
|
||||
auto *X0 =
|
||||
FirstDeclMatcher<RecordDecl>().match(FromTU0, recordDecl(hasName("X")));
|
||||
auto *X1 =
|
||||
FirstDeclMatcher<RecordDecl>().match(FromTU1, recordDecl(hasName("X")));
|
||||
Import(X0, Lang_C);
|
||||
Import(X1, Lang_C);
|
||||
|
||||
auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
|
||||
// We expect one (ODR) warning during the import.
|
||||
EXPECT_EQ(1u, ToTU->getASTContext().getDiagnostics().getNumWarnings());
|
||||
EXPECT_EQ(2u,
|
||||
DeclCounter<RecordDecl>().match(ToTU, recordDecl(hasName("X"))));
|
||||
}
|
||||
|
||||
TEST_P(ASTImporterTestBase, ImportDoesUpdateUsedFlag) {
|
||||
auto Pattern = varDecl(hasName("x"));
|
||||
VarDecl *Imported1;
|
||||
@@ -2930,93 +2990,6 @@ TEST_P(ASTImporterTestBase, ImportUnnamedFieldsInCorrectOrder) {
|
||||
EXPECT_EQ(FromIndex, 3u);
|
||||
}
|
||||
|
||||
TEST_P(
|
||||
ASTImporterTestBase,
|
||||
ImportOfFriendRecordDoesNotMergeDefinition) {
|
||||
Decl *FromTU = getTuDecl(
|
||||
R"(
|
||||
class A {
|
||||
template <int I> class F {};
|
||||
class X {
|
||||
template <int I> friend class F;
|
||||
};
|
||||
};
|
||||
)",
|
||||
Lang_CXX, "input0.cc");
|
||||
|
||||
auto *FromClass = FirstDeclMatcher<CXXRecordDecl>().match(
|
||||
FromTU, cxxRecordDecl(hasName("F"), isDefinition()));
|
||||
auto *FromFriendClass = LastDeclMatcher<CXXRecordDecl>().match(
|
||||
FromTU, cxxRecordDecl(hasName("F")));
|
||||
|
||||
ASSERT_TRUE(FromClass);
|
||||
ASSERT_TRUE(FromFriendClass);
|
||||
ASSERT_NE(FromClass, FromFriendClass);
|
||||
ASSERT_EQ(FromFriendClass->getDefinition(), FromClass);
|
||||
ASSERT_EQ(FromFriendClass->getPreviousDecl(), FromClass);
|
||||
ASSERT_EQ(
|
||||
FromFriendClass->getDescribedClassTemplate()->getPreviousDecl(),
|
||||
FromClass->getDescribedClassTemplate());
|
||||
|
||||
auto *ToClass = cast<CXXRecordDecl>(Import(FromClass, Lang_CXX));
|
||||
auto *ToFriendClass = cast<CXXRecordDecl>(Import(FromFriendClass, Lang_CXX));
|
||||
|
||||
EXPECT_TRUE(ToClass);
|
||||
EXPECT_TRUE(ToFriendClass);
|
||||
EXPECT_NE(ToClass, ToFriendClass);
|
||||
EXPECT_EQ(ToFriendClass->getDefinition(), ToClass);
|
||||
EXPECT_EQ(ToFriendClass->getPreviousDecl(), ToClass);
|
||||
EXPECT_EQ(
|
||||
ToFriendClass->getDescribedClassTemplate()->getPreviousDecl(),
|
||||
ToClass->getDescribedClassTemplate());
|
||||
}
|
||||
|
||||
TEST_P(
|
||||
ASTImporterTestBase,
|
||||
ImportOfRecursiveFriendClass) {
|
||||
Decl *FromTu = getTuDecl(
|
||||
R"(
|
||||
class declToImport {
|
||||
friend class declToImport;
|
||||
};
|
||||
)",
|
||||
Lang_CXX, "input.cc");
|
||||
|
||||
auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(
|
||||
FromTu, cxxRecordDecl(hasName("declToImport")));
|
||||
auto *ToD = Import(FromD, Lang_CXX);
|
||||
auto Pattern = cxxRecordDecl(hasName("declToImport"), has(friendDecl()));
|
||||
ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern));
|
||||
EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern));
|
||||
}
|
||||
|
||||
TEST_P(
|
||||
ASTImporterTestBase,
|
||||
ImportOfRecursiveFriendClassTemplate) {
|
||||
Decl *FromTu = getTuDecl(
|
||||
R"(
|
||||
template <class A> class declToImport {
|
||||
template <class A1> friend class declToImport;
|
||||
};
|
||||
)",
|
||||
Lang_CXX, "input.cc");
|
||||
|
||||
auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match(
|
||||
FromTu, classTemplateDecl(hasName("declToImport")));
|
||||
auto *ToD = Import(FromD, Lang_CXX);
|
||||
|
||||
auto Pattern = classTemplateDecl(
|
||||
has(cxxRecordDecl(has(friendDecl(has(classTemplateDecl()))))));
|
||||
ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern));
|
||||
EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern));
|
||||
|
||||
auto *Class =
|
||||
FirstDeclMatcher<ClassTemplateDecl>().match(ToD, classTemplateDecl());
|
||||
auto *Friend = FirstDeclMatcher<FriendDecl>().match(ToD, friendDecl());
|
||||
EXPECT_NE(Friend->getFriendDecl(), Class);
|
||||
EXPECT_EQ(Friend->getFriendDecl()->getPreviousDecl(), Class);
|
||||
}
|
||||
|
||||
TEST_P(ASTImporterTestBase, MergeFieldDeclsOfClassTemplateSpecialization) {
|
||||
std::string ClassTemplate =
|
||||
R"(
|
||||
@@ -3381,6 +3354,542 @@ TEST_P(ImportVariables, InitAndDefinitionAreInTheFromContext) {
|
||||
EXPECT_TRUE(ImportedD->getDefinition());
|
||||
}
|
||||
|
||||
struct ImportClasses : ASTImporterTestBase {};
|
||||
|
||||
TEST_P(ImportClasses,
|
||||
PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition) {
|
||||
Decl *FromTU = getTuDecl("class X;", Lang_CXX);
|
||||
auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
|
||||
auto FromD = FirstDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
|
||||
|
||||
Decl *ImportedD = Import(FromD, Lang_CXX);
|
||||
Decl *ToTU = ImportedD->getTranslationUnitDecl();
|
||||
|
||||
EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 1u);
|
||||
auto ToD = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
|
||||
EXPECT_TRUE(ImportedD == ToD);
|
||||
EXPECT_FALSE(ToD->isThisDeclarationADefinition());
|
||||
}
|
||||
|
||||
TEST_P(ImportClasses, ImportPrototypeAfterImportedPrototype) {
|
||||
Decl *FromTU = getTuDecl("class X; class X;", Lang_CXX);
|
||||
auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
|
||||
auto From0 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
|
||||
auto From1 = LastDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
|
||||
|
||||
Decl *Imported0 = Import(From0, Lang_CXX);
|
||||
Decl *Imported1 = Import(From1, Lang_CXX);
|
||||
Decl *ToTU = Imported0->getTranslationUnitDecl();
|
||||
|
||||
EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 2u);
|
||||
auto To0 = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
|
||||
auto To1 = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
|
||||
EXPECT_TRUE(Imported0 == To0);
|
||||
EXPECT_TRUE(Imported1 == To1);
|
||||
EXPECT_FALSE(To0->isThisDeclarationADefinition());
|
||||
EXPECT_FALSE(To1->isThisDeclarationADefinition());
|
||||
EXPECT_EQ(To1->getPreviousDecl(), To0);
|
||||
}
|
||||
|
||||
TEST_P(ImportClasses, DefinitionShouldBeImportedAsADefinition) {
|
||||
Decl *FromTU = getTuDecl("class X {};", Lang_CXX);
|
||||
auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
|
||||
auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
|
||||
|
||||
Decl *ImportedD = Import(FromD, Lang_CXX);
|
||||
Decl *ToTU = ImportedD->getTranslationUnitDecl();
|
||||
|
||||
EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 1u);
|
||||
EXPECT_TRUE(cast<CXXRecordDecl>(ImportedD)->isThisDeclarationADefinition());
|
||||
}
|
||||
|
||||
TEST_P(ImportClasses, ImportPrototypeFromDifferentTUAfterImportedPrototype) {
|
||||
Decl *FromTU0 = getTuDecl("class X;", Lang_CXX, "input0.cc");
|
||||
Decl *FromTU1 = getTuDecl("class X;", Lang_CXX, "input1.cc");
|
||||
auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
|
||||
auto From0 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU0, Pattern);
|
||||
auto From1 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU1, Pattern);
|
||||
|
||||
Decl *Imported0 = Import(From0, Lang_CXX);
|
||||
Decl *Imported1 = Import(From1, Lang_CXX);
|
||||
Decl *ToTU = Imported0->getTranslationUnitDecl();
|
||||
|
||||
EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 2u);
|
||||
auto To0 = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
|
||||
auto To1 = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
|
||||
EXPECT_TRUE(Imported0 == To0);
|
||||
EXPECT_TRUE(Imported1 == To1);
|
||||
EXPECT_FALSE(To0->isThisDeclarationADefinition());
|
||||
EXPECT_FALSE(To1->isThisDeclarationADefinition());
|
||||
EXPECT_EQ(To1->getPreviousDecl(), To0);
|
||||
}
|
||||
|
||||
TEST_P(ImportClasses, ImportDefinitions) {
|
||||
Decl *FromTU0 = getTuDecl("class X {};", Lang_CXX, "input0.cc");
|
||||
Decl *FromTU1 = getTuDecl("class X {};", Lang_CXX, "input1.cc");
|
||||
auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
|
||||
auto From0 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU0, Pattern);
|
||||
auto From1 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU1, Pattern);
|
||||
|
||||
Decl *Imported0 = Import(From0, Lang_CXX);
|
||||
Decl *Imported1 = Import(From1, Lang_CXX);
|
||||
Decl *ToTU = Imported0->getTranslationUnitDecl();
|
||||
|
||||
EXPECT_EQ(Imported0, Imported1);
|
||||
EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 1u);
|
||||
auto To0 = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
|
||||
EXPECT_TRUE(Imported0 == To0);
|
||||
EXPECT_TRUE(To0->isThisDeclarationADefinition());
|
||||
}
|
||||
|
||||
TEST_P(ImportClasses, ImportDefinitionThenPrototype) {
|
||||
Decl *FromTU0 = getTuDecl("class X {};", Lang_CXX, "input0.cc");
|
||||
Decl *FromTU1 = getTuDecl("class X;", Lang_CXX, "input1.cc");
|
||||
auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
|
||||
auto FromDef = FirstDeclMatcher<CXXRecordDecl>().match(FromTU0, Pattern);
|
||||
auto FromProto = FirstDeclMatcher<CXXRecordDecl>().match(FromTU1, Pattern);
|
||||
|
||||
Decl *ImportedDef = Import(FromDef, Lang_CXX);
|
||||
Decl *ImportedProto = Import(FromProto, Lang_CXX);
|
||||
Decl *ToTU = ImportedDef->getTranslationUnitDecl();
|
||||
|
||||
EXPECT_NE(ImportedDef, ImportedProto);
|
||||
EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 2u);
|
||||
auto ToDef = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
|
||||
auto ToProto = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
|
||||
EXPECT_TRUE(ImportedDef == ToDef);
|
||||
EXPECT_TRUE(ImportedProto == ToProto);
|
||||
EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
|
||||
EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
|
||||
EXPECT_EQ(ToProto->getPreviousDecl(), ToDef);
|
||||
}
|
||||
|
||||
TEST_P(ImportClasses, ImportPrototypeThenDefinition) {
|
||||
Decl *FromTU0 = getTuDecl("class X;", Lang_CXX, "input0.cc");
|
||||
Decl *FromTU1 = getTuDecl("class X {};", Lang_CXX, "input1.cc");
|
||||
auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
|
||||
auto FromProto = FirstDeclMatcher<CXXRecordDecl>().match(FromTU0, Pattern);
|
||||
auto FromDef = FirstDeclMatcher<CXXRecordDecl>().match(FromTU1, Pattern);
|
||||
|
||||
Decl *ImportedProto = Import(FromProto, Lang_CXX);
|
||||
Decl *ImportedDef = Import(FromDef, Lang_CXX);
|
||||
Decl *ToTU = ImportedDef->getTranslationUnitDecl();
|
||||
|
||||
EXPECT_NE(ImportedDef, ImportedProto);
|
||||
EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 2u);
|
||||
auto ToProto = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
|
||||
auto ToDef = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
|
||||
EXPECT_TRUE(ImportedDef == ToDef);
|
||||
EXPECT_TRUE(ImportedProto == ToProto);
|
||||
EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
|
||||
EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
|
||||
EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
|
||||
}
|
||||
|
||||
struct ImportClassTemplates : ASTImporterTestBase {};
|
||||
|
||||
TEST_P(ImportClassTemplates,
|
||||
PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition) {
|
||||
Decl *FromTU = getTuDecl("template <class T> class X;", Lang_CXX);
|
||||
auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
|
||||
auto FromD = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, Pattern);
|
||||
|
||||
Decl *ImportedD = Import(FromD, Lang_CXX);
|
||||
Decl *ToTU = ImportedD->getTranslationUnitDecl();
|
||||
|
||||
EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 1u);
|
||||
auto ToD = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
|
||||
EXPECT_TRUE(ImportedD == ToD);
|
||||
ASSERT_TRUE(ToD->getTemplatedDecl());
|
||||
EXPECT_FALSE(ToD->isThisDeclarationADefinition());
|
||||
}
|
||||
|
||||
TEST_P(ImportClassTemplates, ImportPrototypeAfterImportedPrototype) {
|
||||
Decl *FromTU = getTuDecl(
|
||||
"template <class T> class X; template <class T> class X;", Lang_CXX);
|
||||
auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
|
||||
auto From0 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, Pattern);
|
||||
auto From1 = LastDeclMatcher<ClassTemplateDecl>().match(FromTU, Pattern);
|
||||
|
||||
Decl *Imported0 = Import(From0, Lang_CXX);
|
||||
Decl *Imported1 = Import(From1, Lang_CXX);
|
||||
Decl *ToTU = Imported0->getTranslationUnitDecl();
|
||||
|
||||
EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 2u);
|
||||
auto To0 = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
|
||||
auto To1 = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
|
||||
EXPECT_TRUE(Imported0 == To0);
|
||||
EXPECT_TRUE(Imported1 == To1);
|
||||
ASSERT_TRUE(To0->getTemplatedDecl());
|
||||
ASSERT_TRUE(To1->getTemplatedDecl());
|
||||
EXPECT_FALSE(To0->isThisDeclarationADefinition());
|
||||
EXPECT_FALSE(To1->isThisDeclarationADefinition());
|
||||
EXPECT_EQ(To1->getPreviousDecl(), To0);
|
||||
EXPECT_EQ(To1->getTemplatedDecl()->getPreviousDecl(),
|
||||
To0->getTemplatedDecl());
|
||||
}
|
||||
|
||||
TEST_P(ImportClassTemplates, DefinitionShouldBeImportedAsADefinition) {
|
||||
Decl *FromTU = getTuDecl("template <class T> class X {};", Lang_CXX);
|
||||
auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
|
||||
auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, Pattern);
|
||||
|
||||
Decl *ImportedD = Import(FromD, Lang_CXX);
|
||||
Decl *ToTU = ImportedD->getTranslationUnitDecl();
|
||||
|
||||
EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 1u);
|
||||
auto ToD = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
|
||||
ASSERT_TRUE(ToD->getTemplatedDecl());
|
||||
EXPECT_TRUE(ToD->isThisDeclarationADefinition());
|
||||
}
|
||||
|
||||
TEST_P(ImportClassTemplates,
|
||||
ImportPrototypeFromDifferentTUAfterImportedPrototype) {
|
||||
Decl *FromTU0 =
|
||||
getTuDecl("template <class T> class X;", Lang_CXX, "input0.cc");
|
||||
Decl *FromTU1 =
|
||||
getTuDecl("template <class T> class X;", Lang_CXX, "input1.cc");
|
||||
auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
|
||||
auto From0 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU0, Pattern);
|
||||
auto From1 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU1, Pattern);
|
||||
|
||||
Decl *Imported0 = Import(From0, Lang_CXX);
|
||||
Decl *Imported1 = Import(From1, Lang_CXX);
|
||||
Decl *ToTU = Imported0->getTranslationUnitDecl();
|
||||
|
||||
EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 2u);
|
||||
auto To0 = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
|
||||
auto To1 = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
|
||||
EXPECT_TRUE(Imported0 == To0);
|
||||
EXPECT_TRUE(Imported1 == To1);
|
||||
ASSERT_TRUE(To0->getTemplatedDecl());
|
||||
ASSERT_TRUE(To1->getTemplatedDecl());
|
||||
EXPECT_FALSE(To0->isThisDeclarationADefinition());
|
||||
EXPECT_FALSE(To1->isThisDeclarationADefinition());
|
||||
EXPECT_EQ(To1->getPreviousDecl(), To0);
|
||||
EXPECT_EQ(To1->getTemplatedDecl()->getPreviousDecl(),
|
||||
To0->getTemplatedDecl());
|
||||
}
|
||||
|
||||
TEST_P(ImportClassTemplates, ImportDefinitions) {
|
||||
Decl *FromTU0 =
|
||||
getTuDecl("template <class T> class X {};", Lang_CXX, "input0.cc");
|
||||
Decl *FromTU1 =
|
||||
getTuDecl("template <class T> class X {};", Lang_CXX, "input1.cc");
|
||||
auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
|
||||
auto From0 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU0, Pattern);
|
||||
auto From1 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU1, Pattern);
|
||||
|
||||
Decl *Imported0 = Import(From0, Lang_CXX);
|
||||
Decl *Imported1 = Import(From1, Lang_CXX);
|
||||
Decl *ToTU = Imported0->getTranslationUnitDecl();
|
||||
|
||||
EXPECT_EQ(Imported0, Imported1);
|
||||
EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 1u);
|
||||
auto To0 = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
|
||||
EXPECT_TRUE(Imported0 == To0);
|
||||
ASSERT_TRUE(To0->getTemplatedDecl());
|
||||
EXPECT_TRUE(To0->isThisDeclarationADefinition());
|
||||
}
|
||||
|
||||
TEST_P(ImportClassTemplates, ImportDefinitionThenPrototype) {
|
||||
Decl *FromTU0 =
|
||||
getTuDecl("template <class T> class X {};", Lang_CXX, "input0.cc");
|
||||
Decl *FromTU1 =
|
||||
getTuDecl("template <class T> class X;", Lang_CXX, "input1.cc");
|
||||
auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
|
||||
auto FromDef = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU0, Pattern);
|
||||
auto FromProto =
|
||||
FirstDeclMatcher<ClassTemplateDecl>().match(FromTU1, Pattern);
|
||||
|
||||
Decl *ImportedDef = Import(FromDef, Lang_CXX);
|
||||
Decl *ImportedProto = Import(FromProto, Lang_CXX);
|
||||
Decl *ToTU = ImportedDef->getTranslationUnitDecl();
|
||||
|
||||
EXPECT_NE(ImportedDef, ImportedProto);
|
||||
EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 2u);
|
||||
auto ToDef = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
|
||||
auto ToProto = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
|
||||
EXPECT_TRUE(ImportedDef == ToDef);
|
||||
EXPECT_TRUE(ImportedProto == ToProto);
|
||||
ASSERT_TRUE(ToDef->getTemplatedDecl());
|
||||
ASSERT_TRUE(ToProto->getTemplatedDecl());
|
||||
EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
|
||||
EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
|
||||
EXPECT_EQ(ToProto->getPreviousDecl(), ToDef);
|
||||
EXPECT_EQ(ToProto->getTemplatedDecl()->getPreviousDecl(),
|
||||
ToDef->getTemplatedDecl());
|
||||
}
|
||||
|
||||
TEST_P(ImportClassTemplates, ImportPrototypeThenDefinition) {
|
||||
Decl *FromTU0 =
|
||||
getTuDecl("template <class T> class X;", Lang_CXX, "input0.cc");
|
||||
Decl *FromTU1 =
|
||||
getTuDecl("template <class T> class X {};", Lang_CXX, "input1.cc");
|
||||
auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
|
||||
auto FromProto =
|
||||
FirstDeclMatcher<ClassTemplateDecl>().match(FromTU0, Pattern);
|
||||
auto FromDef = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU1, Pattern);
|
||||
|
||||
Decl *ImportedProto = Import(FromProto, Lang_CXX);
|
||||
Decl *ImportedDef = Import(FromDef, Lang_CXX);
|
||||
Decl *ToTU = ImportedDef->getTranslationUnitDecl();
|
||||
|
||||
EXPECT_NE(ImportedDef, ImportedProto);
|
||||
EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 2u);
|
||||
auto ToProto = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
|
||||
auto ToDef = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
|
||||
EXPECT_TRUE(ImportedDef == ToDef);
|
||||
EXPECT_TRUE(ImportedProto == ToProto);
|
||||
ASSERT_TRUE(ToProto->getTemplatedDecl());
|
||||
ASSERT_TRUE(ToDef->getTemplatedDecl());
|
||||
EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
|
||||
EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
|
||||
EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
|
||||
EXPECT_EQ(ToDef->getTemplatedDecl()->getPreviousDecl(),
|
||||
ToProto->getTemplatedDecl());
|
||||
}
|
||||
|
||||
struct ImportFriendClasses : ASTImporterTestBase {};
|
||||
|
||||
TEST_P(ImportFriendClasses, ImportOfFriendRecordDoesNotMergeDefinition) {
|
||||
Decl *FromTU = getTuDecl(
|
||||
R"(
|
||||
class A {
|
||||
template <int I> class F {};
|
||||
class X {
|
||||
template <int I> friend class F;
|
||||
};
|
||||
};
|
||||
)",
|
||||
Lang_CXX, "input0.cc");
|
||||
|
||||
auto *FromClass = FirstDeclMatcher<CXXRecordDecl>().match(
|
||||
FromTU, cxxRecordDecl(hasName("F"), isDefinition()));
|
||||
auto *FromFriendClass = LastDeclMatcher<CXXRecordDecl>().match(
|
||||
FromTU, cxxRecordDecl(hasName("F")));
|
||||
|
||||
ASSERT_TRUE(FromClass);
|
||||
ASSERT_TRUE(FromFriendClass);
|
||||
ASSERT_NE(FromClass, FromFriendClass);
|
||||
ASSERT_EQ(FromFriendClass->getDefinition(), FromClass);
|
||||
ASSERT_EQ(FromFriendClass->getPreviousDecl(), FromClass);
|
||||
ASSERT_EQ(FromFriendClass->getDescribedClassTemplate()->getPreviousDecl(),
|
||||
FromClass->getDescribedClassTemplate());
|
||||
|
||||
auto *ToClass = cast<CXXRecordDecl>(Import(FromClass, Lang_CXX));
|
||||
auto *ToFriendClass = cast<CXXRecordDecl>(Import(FromFriendClass, Lang_CXX));
|
||||
|
||||
EXPECT_TRUE(ToClass);
|
||||
EXPECT_TRUE(ToFriendClass);
|
||||
EXPECT_NE(ToClass, ToFriendClass);
|
||||
EXPECT_EQ(ToFriendClass->getDefinition(), ToClass);
|
||||
EXPECT_EQ(ToFriendClass->getPreviousDecl(), ToClass);
|
||||
EXPECT_EQ(ToFriendClass->getDescribedClassTemplate()->getPreviousDecl(),
|
||||
ToClass->getDescribedClassTemplate());
|
||||
}
|
||||
|
||||
TEST_P(ImportFriendClasses, ImportOfRecursiveFriendClass) {
|
||||
Decl *FromTu = getTuDecl(
|
||||
R"(
|
||||
class declToImport {
|
||||
friend class declToImport;
|
||||
};
|
||||
)",
|
||||
Lang_CXX, "input.cc");
|
||||
|
||||
auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(
|
||||
FromTu, cxxRecordDecl(hasName("declToImport")));
|
||||
auto *ToD = Import(FromD, Lang_CXX);
|
||||
auto Pattern = cxxRecordDecl(has(friendDecl()));
|
||||
ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern));
|
||||
EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern));
|
||||
}
|
||||
|
||||
TEST_P(ImportFriendClasses, ImportOfRecursiveFriendClassTemplate) {
|
||||
Decl *FromTu = getTuDecl(
|
||||
R"(
|
||||
template<class A> class declToImport {
|
||||
template<class A1> friend class declToImport;
|
||||
};
|
||||
)",
|
||||
Lang_CXX, "input.cc");
|
||||
|
||||
auto *FromD =
|
||||
FirstDeclMatcher<ClassTemplateDecl>().match(FromTu, classTemplateDecl());
|
||||
auto *ToD = Import(FromD, Lang_CXX);
|
||||
|
||||
auto Pattern = classTemplateDecl(
|
||||
has(cxxRecordDecl(has(friendDecl(has(classTemplateDecl()))))));
|
||||
ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern));
|
||||
EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern));
|
||||
|
||||
auto *Class =
|
||||
FirstDeclMatcher<ClassTemplateDecl>().match(ToD, classTemplateDecl());
|
||||
auto *Friend = FirstDeclMatcher<FriendDecl>().match(ToD, friendDecl());
|
||||
EXPECT_NE(Friend->getFriendDecl(), Class);
|
||||
EXPECT_EQ(Friend->getFriendDecl()->getPreviousDecl(), Class);
|
||||
}
|
||||
|
||||
TEST_P(ImportFriendClasses, ProperPrevDeclForClassTemplateDecls) {
|
||||
auto Pattern = classTemplateSpecializationDecl(hasName("X"));
|
||||
|
||||
ClassTemplateSpecializationDecl *Imported1;
|
||||
{
|
||||
Decl *FromTU = getTuDecl("template<class T> class X;"
|
||||
"struct Y { friend class X<int>; };",
|
||||
Lang_CXX, "input0.cc");
|
||||
auto *FromD = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
|
||||
FromTU, Pattern);
|
||||
|
||||
Imported1 = cast<ClassTemplateSpecializationDecl>(Import(FromD, Lang_CXX));
|
||||
}
|
||||
ClassTemplateSpecializationDecl *Imported2;
|
||||
{
|
||||
Decl *FromTU = getTuDecl("template<class T> class X;"
|
||||
"template<> class X<int>{};"
|
||||
"struct Z { friend class X<int>; };",
|
||||
Lang_CXX, "input1.cc");
|
||||
auto *FromD = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
|
||||
FromTU, Pattern);
|
||||
|
||||
Imported2 = cast<ClassTemplateSpecializationDecl>(Import(FromD, Lang_CXX));
|
||||
}
|
||||
|
||||
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
|
||||
EXPECT_EQ(DeclCounter<ClassTemplateSpecializationDecl>().match(ToTU, Pattern),
|
||||
2u);
|
||||
ASSERT_TRUE(Imported2->getPreviousDecl());
|
||||
EXPECT_EQ(Imported2->getPreviousDecl(), Imported1);
|
||||
}
|
||||
|
||||
TEST_P(ImportFriendClasses, TypeForDeclShouldBeSetInTemplated) {
|
||||
Decl *FromTU0 = getTuDecl(
|
||||
R"(
|
||||
class X {
|
||||
class Y;
|
||||
};
|
||||
class X::Y {
|
||||
template <typename T>
|
||||
friend class F; // The decl context of F is the global namespace.
|
||||
};
|
||||
)",
|
||||
Lang_CXX, "input0.cc");
|
||||
auto *Fwd = FirstDeclMatcher<ClassTemplateDecl>().match(
|
||||
FromTU0, classTemplateDecl(hasName("F")));
|
||||
auto *Imported0 = cast<ClassTemplateDecl>(Import(Fwd, Lang_CXX));
|
||||
Decl *FromTU1 = getTuDecl(
|
||||
R"(
|
||||
template <typename T>
|
||||
class F {};
|
||||
)",
|
||||
Lang_CXX, "input1.cc");
|
||||
auto *Definition = FirstDeclMatcher<ClassTemplateDecl>().match(
|
||||
FromTU1, classTemplateDecl(hasName("F")));
|
||||
auto *Imported1 = cast<ClassTemplateDecl>(Import(Definition, Lang_CXX));
|
||||
EXPECT_EQ(Imported0->getTemplatedDecl()->getTypeForDecl(),
|
||||
Imported1->getTemplatedDecl()->getTypeForDecl());
|
||||
}
|
||||
|
||||
TEST_P(ImportFriendClasses, DeclsFromFriendsShouldBeInRedeclChains) {
|
||||
Decl *From, *To;
|
||||
std::tie(From, To) =
|
||||
getImportedDecl("class declToImport {};", Lang_CXX,
|
||||
"class Y { friend class declToImport; };", Lang_CXX);
|
||||
auto *Imported = cast<CXXRecordDecl>(To);
|
||||
|
||||
EXPECT_TRUE(Imported->getPreviousDecl());
|
||||
}
|
||||
|
||||
TEST_P(ImportFriendClasses,
|
||||
ImportOfClassTemplateDefinitionShouldConnectToFwdFriend) {
|
||||
Decl *ToTU = getToTuDecl(
|
||||
R"(
|
||||
class X {
|
||||
class Y;
|
||||
};
|
||||
class X::Y {
|
||||
template <typename T>
|
||||
friend class F; // The decl context of F is the global namespace.
|
||||
};
|
||||
)",
|
||||
Lang_CXX);
|
||||
auto *ToDecl = FirstDeclMatcher<ClassTemplateDecl>().match(
|
||||
ToTU, classTemplateDecl(hasName("F")));
|
||||
Decl *FromTU = getTuDecl(
|
||||
R"(
|
||||
template <typename T>
|
||||
class F {};
|
||||
)",
|
||||
Lang_CXX, "input0.cc");
|
||||
auto *Definition = FirstDeclMatcher<ClassTemplateDecl>().match(
|
||||
FromTU, classTemplateDecl(hasName("F")));
|
||||
auto *ImportedDef = cast<ClassTemplateDecl>(Import(Definition, Lang_CXX));
|
||||
EXPECT_TRUE(ImportedDef->getPreviousDecl());
|
||||
EXPECT_EQ(ToDecl, ImportedDef->getPreviousDecl());
|
||||
EXPECT_EQ(ToDecl->getTemplatedDecl(),
|
||||
ImportedDef->getTemplatedDecl()->getPreviousDecl());
|
||||
}
|
||||
|
||||
TEST_P(ImportFriendClasses,
|
||||
ImportOfClassTemplateDefinitionAndFwdFriendShouldBeLinked) {
|
||||
Decl *FromTU0 = getTuDecl(
|
||||
R"(
|
||||
class X {
|
||||
class Y;
|
||||
};
|
||||
class X::Y {
|
||||
template <typename T>
|
||||
friend class F; // The decl context of F is the global namespace.
|
||||
};
|
||||
)",
|
||||
Lang_CXX, "input0.cc");
|
||||
auto *Fwd = FirstDeclMatcher<ClassTemplateDecl>().match(
|
||||
FromTU0, classTemplateDecl(hasName("F")));
|
||||
auto *ImportedFwd = cast<ClassTemplateDecl>(Import(Fwd, Lang_CXX));
|
||||
Decl *FromTU1 = getTuDecl(
|
||||
R"(
|
||||
template <typename T>
|
||||
class F {};
|
||||
)",
|
||||
Lang_CXX, "input1.cc");
|
||||
auto *Definition = FirstDeclMatcher<ClassTemplateDecl>().match(
|
||||
FromTU1, classTemplateDecl(hasName("F")));
|
||||
auto *ImportedDef = cast<ClassTemplateDecl>(Import(Definition, Lang_CXX));
|
||||
EXPECT_TRUE(ImportedDef->getPreviousDecl());
|
||||
EXPECT_EQ(ImportedFwd, ImportedDef->getPreviousDecl());
|
||||
EXPECT_EQ(ImportedFwd->getTemplatedDecl(),
|
||||
ImportedDef->getTemplatedDecl()->getPreviousDecl());
|
||||
}
|
||||
|
||||
TEST_P(ImportFriendClasses, ImportOfClassDefinitionAndFwdFriendShouldBeLinked) {
|
||||
Decl *FromTU0 = getTuDecl(
|
||||
R"(
|
||||
class X {
|
||||
class Y;
|
||||
};
|
||||
class X::Y {
|
||||
friend class F; // The decl context of F is the global namespace.
|
||||
};
|
||||
)",
|
||||
Lang_CXX, "input0.cc");
|
||||
auto *Friend = FirstDeclMatcher<FriendDecl>().match(FromTU0, friendDecl());
|
||||
QualType FT = Friend->getFriendType()->getType();
|
||||
FT = FromTU0->getASTContext().getCanonicalType(FT);
|
||||
auto *Fwd = cast<TagType>(FT)->getDecl();
|
||||
auto *ImportedFwd = Import(Fwd, Lang_CXX);
|
||||
Decl *FromTU1 = getTuDecl(
|
||||
R"(
|
||||
class F {};
|
||||
)",
|
||||
Lang_CXX, "input1.cc");
|
||||
auto *Definition = FirstDeclMatcher<CXXRecordDecl>().match(
|
||||
FromTU1, cxxRecordDecl(hasName("F")));
|
||||
auto *ImportedDef = Import(Definition, Lang_CXX);
|
||||
EXPECT_TRUE(ImportedDef->getPreviousDecl());
|
||||
EXPECT_EQ(ImportedFwd, ImportedDef->getPreviousDecl());
|
||||
}
|
||||
|
||||
struct DeclContextTest : ASTImporterTestBase {};
|
||||
|
||||
TEST_P(DeclContextTest, removeDeclOfClassTemplateSpecialization) {
|
||||
@@ -3410,6 +3919,40 @@ TEST_P(DeclContextTest, removeDeclOfClassTemplateSpecialization) {
|
||||
EXPECT_FALSE(NS->containsDecl(Spec));
|
||||
}
|
||||
|
||||
TEST_P(DeclContextTest,
|
||||
removeDeclShouldNotFailEvenIfWeHaveExternalVisibleStorage) {
|
||||
Decl *TU = getTuDecl("extern int A; int A;", Lang_CXX);
|
||||
auto *A0 = FirstDeclMatcher<VarDecl>().match(TU, varDecl(hasName("A")));
|
||||
auto *A1 = LastDeclMatcher<VarDecl>().match(TU, varDecl(hasName("A")));
|
||||
|
||||
// Investigate the list.
|
||||
auto *DC = A0->getDeclContext();
|
||||
ASSERT_TRUE(DC->containsDecl(A0));
|
||||
ASSERT_TRUE(DC->containsDecl(A1));
|
||||
|
||||
// Investigate the lookup table.
|
||||
auto *Map = DC->getLookupPtr();
|
||||
ASSERT_TRUE(Map);
|
||||
auto I = Map->find(A0->getDeclName());
|
||||
ASSERT_NE(I, Map->end());
|
||||
StoredDeclsList &L = I->second;
|
||||
// The lookup table contains the most recent decl of A.
|
||||
ASSERT_NE(L.getAsDecl(), A0);
|
||||
ASSERT_EQ(L.getAsDecl(), A1);
|
||||
|
||||
ASSERT_TRUE(L.getAsDecl());
|
||||
// Simulate the private function DeclContext::reconcileExternalVisibleStorage.
|
||||
// The point here is to have a Vec with only one element, which is not the
|
||||
// one we are going to delete from the DC later.
|
||||
L.setHasExternalDecls();
|
||||
ASSERT_TRUE(L.getAsVector());
|
||||
ASSERT_EQ(1u, L.getAsVector()->size());
|
||||
|
||||
// This asserts in the old implementation.
|
||||
DC->removeDecl(A0);
|
||||
EXPECT_FALSE(DC->containsDecl(A0));
|
||||
}
|
||||
|
||||
struct ImportFunctionTemplateSpecializations : ASTImporterTestBase {};
|
||||
|
||||
TEST_P(ImportFunctionTemplateSpecializations,
|
||||
@@ -3827,6 +4370,15 @@ INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctions,
|
||||
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendFunctions,
|
||||
DefaultTestValuesForRunOptions, );
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClasses,
|
||||
DefaultTestValuesForRunOptions, );
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClassTemplates,
|
||||
DefaultTestValuesForRunOptions, );
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendClasses,
|
||||
DefaultTestValuesForRunOptions, );
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(ParameterizedTests,
|
||||
ImportFunctionTemplateSpecializations,
|
||||
DefaultTestValuesForRunOptions, );
|
||||
|
||||
@@ -597,6 +597,77 @@ TEST_F(StructuralEquivalenceRecordTest, UnnamedRecordsShouldBeInequivalent) {
|
||||
EXPECT_FALSE(testStructuralMatch(R0, R1));
|
||||
}
|
||||
|
||||
TEST_F(StructuralEquivalenceRecordTest, AnonymousRecordsShouldBeInequivalent) {
|
||||
auto t = makeTuDecls(
|
||||
R"(
|
||||
struct X {
|
||||
struct {
|
||||
int a;
|
||||
};
|
||||
struct {
|
||||
int b;
|
||||
};
|
||||
};
|
||||
)",
|
||||
"", Lang_C);
|
||||
auto *TU = get<0>(t);
|
||||
auto *A = FirstDeclMatcher<IndirectFieldDecl>().match(
|
||||
TU, indirectFieldDecl(hasName("a")));
|
||||
auto *FA = cast<FieldDecl>(A->chain().front());
|
||||
RecordDecl *RA = cast<RecordType>(FA->getType().getTypePtr())->getDecl();
|
||||
auto *B = FirstDeclMatcher<IndirectFieldDecl>().match(
|
||||
TU, indirectFieldDecl(hasName("b")));
|
||||
auto *FB = cast<FieldDecl>(B->chain().front());
|
||||
RecordDecl *RB = cast<RecordType>(FB->getType().getTypePtr())->getDecl();
|
||||
|
||||
ASSERT_NE(RA, RB);
|
||||
EXPECT_TRUE(testStructuralMatch(RA, RA));
|
||||
EXPECT_TRUE(testStructuralMatch(RB, RB));
|
||||
EXPECT_FALSE(testStructuralMatch(RA, RB));
|
||||
}
|
||||
|
||||
TEST_F(StructuralEquivalenceRecordTest,
|
||||
RecordsAreInequivalentIfOrderOfAnonRecordsIsDifferent) {
|
||||
auto t = makeTuDecls(
|
||||
R"(
|
||||
struct X {
|
||||
struct { int a; };
|
||||
struct { int b; };
|
||||
};
|
||||
)",
|
||||
R"(
|
||||
struct X { // The order is reversed.
|
||||
struct { int b; };
|
||||
struct { int a; };
|
||||
};
|
||||
)",
|
||||
Lang_C);
|
||||
|
||||
auto *TU = get<0>(t);
|
||||
auto *A = FirstDeclMatcher<IndirectFieldDecl>().match(
|
||||
TU, indirectFieldDecl(hasName("a")));
|
||||
auto *FA = cast<FieldDecl>(A->chain().front());
|
||||
RecordDecl *RA = cast<RecordType>(FA->getType().getTypePtr())->getDecl();
|
||||
|
||||
auto *TU1 = get<1>(t);
|
||||
auto *A1 = FirstDeclMatcher<IndirectFieldDecl>().match(
|
||||
TU1, indirectFieldDecl(hasName("a")));
|
||||
auto *FA1 = cast<FieldDecl>(A1->chain().front());
|
||||
RecordDecl *RA1 = cast<RecordType>(FA1->getType().getTypePtr())->getDecl();
|
||||
|
||||
RecordDecl *X =
|
||||
FirstDeclMatcher<RecordDecl>().match(TU, recordDecl(hasName("X")));
|
||||
RecordDecl *X1 =
|
||||
FirstDeclMatcher<RecordDecl>().match(TU1, recordDecl(hasName("X")));
|
||||
ASSERT_NE(X, X1);
|
||||
EXPECT_FALSE(testStructuralMatch(X, X1));
|
||||
|
||||
ASSERT_NE(RA, RA1);
|
||||
EXPECT_TRUE(testStructuralMatch(RA, RA));
|
||||
EXPECT_TRUE(testStructuralMatch(RA1, RA1));
|
||||
EXPECT_FALSE(testStructuralMatch(RA1, RA));
|
||||
}
|
||||
|
||||
TEST_F(StructuralEquivalenceRecordTest,
|
||||
UnnamedRecordsShouldBeInequivalentEvenIfTheSecondIsBeingDefined) {
|
||||
auto Code =
|
||||
|
||||
Reference in New Issue
Block a user