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");
}
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;
}
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);
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,
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);
-
- 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);
+ D2 = D2CXX;
+ D2->setAccess(D->getAccess());
+ D2->setLexicalDeclContext(LexicalDC);
+ if (!DCXX->getDescribedClassTemplate() || DCXX->isImplicit())
+ LexicalDC->addDeclInternal(D2);
+
+ 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();
*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))
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();
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;
}
}
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!
//
//===----------------------------------------------------------------------===//
+#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"
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;
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"(
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) {
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,
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, );