diff options
Diffstat (limited to 'unittests/AST/ASTImporterTest.cpp')
-rw-r--r-- | unittests/AST/ASTImporterTest.cpp | 1301 |
1 files changed, 1188 insertions, 113 deletions
diff --git a/unittests/AST/ASTImporterTest.cpp b/unittests/AST/ASTImporterTest.cpp index 0450cb41a4..c6acf573e5 100644 --- a/unittests/AST/ASTImporterTest.cpp +++ b/unittests/AST/ASTImporterTest.cpp @@ -11,9 +11,12 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/ASTImporter.h" #include "MatchVerifier.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/DeclContextInternals.h" #include "clang/AST/ASTImporter.h" +#include "clang/AST/ASTImporterLookupTable.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/Tooling/Tooling.h" @@ -307,24 +310,27 @@ class ASTImporterTestBase : public ParameterizedTestsFixture { Unit->enableSourceFileDiagnostics(); } - void lazyInitImporter(ASTUnit *ToAST) { + void lazyInitImporter(ASTImporterLookupTable &LookupTable, ASTUnit *ToAST) { assert(ToAST); if (!Importer) { - Importer.reset(new ASTImporter( - ToAST->getASTContext(), ToAST->getFileManager(), - Unit->getASTContext(), Unit->getFileManager(), false)); + Importer.reset( + new ASTImporter(ToAST->getASTContext(), ToAST->getFileManager(), + Unit->getASTContext(), Unit->getFileManager(), + false, &LookupTable)); } assert(&ToAST->getASTContext() == &Importer->getToContext()); createVirtualFileIfNeeded(ToAST, FileName, Code); } - Decl *import(ASTUnit *ToAST, Decl *FromDecl) { - lazyInitImporter(ToAST); + Decl *import(ASTImporterLookupTable &LookupTable, ASTUnit *ToAST, + Decl *FromDecl) { + lazyInitImporter(LookupTable, ToAST); return Importer->Import(FromDecl); - } + } - QualType import(ASTUnit *ToAST, QualType FromType) { - lazyInitImporter(ToAST); + QualType import(ASTImporterLookupTable &LookupTable, ASTUnit *ToAST, + QualType FromType) { + lazyInitImporter(LookupTable, ToAST); return Importer->Import(FromType); } }; @@ -338,13 +344,23 @@ class ASTImporterTestBase : public ParameterizedTestsFixture { // vector is expanding, with the list we won't have these issues. std::list<TU> FromTUs; - void lazyInitToAST(Language ToLang) { + // Initialize the lookup table if not initialized already. + void lazyInitLookupTable(TranslationUnitDecl *ToTU) { + assert(ToTU); + if (!LookupTablePtr) + LookupTablePtr = llvm::make_unique<ASTImporterLookupTable>(*ToTU); + } + + void lazyInitToAST(Language ToLang, StringRef ToSrcCode, StringRef FileName) { if (ToAST) return; ArgVector ToArgs = getArgVectorForLanguage(ToLang); + // Source code must be a valid live buffer through the tests lifetime. + ToCode = ToSrcCode; // Build the AST from an empty file. - ToAST = tooling::buildASTFromCodeWithArgs(/*Code=*/"", ToArgs, "empty.cc"); + ToAST = tooling::buildASTFromCodeWithArgs(ToCode, ToArgs, FileName); ToAST->enableSourceFileDiagnostics(); + lazyInitLookupTable(ToAST->getASTContext().getTranslationUnitDecl()); } TU *findFromTU(Decl *From) { @@ -358,6 +374,10 @@ class ASTImporterTestBase : public ParameterizedTestsFixture { return &*It; } +protected: + + std::unique_ptr<ASTImporterLookupTable> LookupTablePtr; + public: // We may have several From context but only one To context. std::unique_ptr<ASTUnit> ToAST; @@ -374,26 +394,23 @@ public: FromTUs.emplace_back(FromSrcCode, InputFileName, FromArgs); TU &FromTU = FromTUs.back(); - ToCode = ToSrcCode; assert(!ToAST); - ToAST = tooling::buildASTFromCodeWithArgs(ToCode, ToArgs, OutputFileName); - ToAST->enableSourceFileDiagnostics(); + lazyInitToAST(ToLang, ToSrcCode, OutputFileName); ASTContext &FromCtx = FromTU.Unit->getASTContext(); - createVirtualFileIfNeeded(ToAST.get(), InputFileName, FromTU.Code); - IdentifierInfo *ImportedII = &FromCtx.Idents.get(Identifier); assert(ImportedII && "Declaration with the given identifier " "should be specified in test!"); DeclarationName ImportDeclName(ImportedII); - SmallVector<NamedDecl *, 4> FoundDecls; + SmallVector<NamedDecl *, 1> FoundDecls; FromCtx.getTranslationUnitDecl()->localUncachedLookup(ImportDeclName, FoundDecls); assert(FoundDecls.size() == 1); - Decl *Imported = FromTU.import(ToAST.get(), FoundDecls.front()); + Decl *Imported = + FromTU.import(*LookupTablePtr, ToAST.get(), FoundDecls.front()); assert(Imported); return std::make_tuple(*FoundDecls.begin(), Imported); @@ -419,11 +436,8 @@ public: // Creates the To context with the given source code and returns the TU decl. TranslationUnitDecl *getToTuDecl(StringRef ToSrcCode, Language ToLang) { ArgVector ToArgs = getArgVectorForLanguage(ToLang); - ToCode = ToSrcCode; assert(!ToAST); - ToAST = tooling::buildASTFromCodeWithArgs(ToCode, ToArgs, OutputFileName); - ToAST->enableSourceFileDiagnostics(); - + lazyInitToAST(ToLang, ToSrcCode, OutputFileName); return ToAST->getASTContext().getTranslationUnitDecl(); } @@ -431,15 +445,17 @@ public: // May be called several times in a given test. // The different instances of the param From may have different ASTContext. Decl *Import(Decl *From, Language ToLang) { - lazyInitToAST(ToLang); + lazyInitToAST(ToLang, "", OutputFileName); TU *FromTU = findFromTU(From); - return FromTU->import(ToAST.get(), From); + assert(LookupTablePtr); + return FromTU->import(*LookupTablePtr, ToAST.get(), From); } QualType ImportType(QualType FromType, Decl *TUDecl, Language ToLang) { - lazyInitToAST(ToLang); + lazyInitToAST(ToLang, "", OutputFileName); TU *FromTU = findFromTU(TUDecl); - return FromTU->import(ToAST.get(), FromType); + assert(LookupTablePtr); + return FromTU->import(*LookupTablePtr, ToAST.get(), FromType); } ~ASTImporterTestBase() { @@ -1808,6 +1824,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; @@ -2667,6 +2742,7 @@ private: CXXMethodDecl *Method = FirstDeclMatcher<CXXMethodDecl>().match(ToClass, MethodMatcher); ToClass->removeDecl(Method); + LookupTablePtr->remove(Method); } ASSERT_EQ(DeclCounter<CXXMethodDecl>().match(ToClass, MethodMatcher), 0u); @@ -2930,93 +3006,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 +3370,636 @@ 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); +} + +TEST_P(ImportClasses, ImportDefinitionWhenProtoIsInToContext) { + Decl *ToTU = getToTuDecl("struct X;", Lang_C); + Decl *FromTU1 = getTuDecl("struct X {};", Lang_C, "input1.cc"); + auto Pattern = recordDecl(hasName("X"), unless(isImplicit())); + auto ToProto = FirstDeclMatcher<RecordDecl>().match(ToTU, Pattern); + auto FromDef = FirstDeclMatcher<RecordDecl>().match(FromTU1, Pattern); + + Decl *ImportedDef = Import(FromDef, Lang_C); + + EXPECT_NE(ImportedDef, ToProto); + EXPECT_EQ(DeclCounter<RecordDecl>().match(ToTU, Pattern), 2u); + auto ToDef = LastDeclMatcher<RecordDecl>().match(ToTU, Pattern); + EXPECT_TRUE(ImportedDef == ToDef); + EXPECT_TRUE(ToDef->isThisDeclarationADefinition()); + EXPECT_FALSE(ToProto->isThisDeclarationADefinition()); + EXPECT_EQ(ToDef->getPreviousDecl(), ToProto); +} + +TEST_P(ImportClasses, ImportDefinitionWhenProtoIsInNestedToContext) { + Decl *ToTU = getToTuDecl("struct A { struct X *Xp; };", Lang_C); + Decl *FromTU1 = getTuDecl("struct X {};", Lang_C, "input1.cc"); + auto Pattern = recordDecl(hasName("X"), unless(isImplicit())); + auto ToProto = FirstDeclMatcher<RecordDecl>().match(ToTU, Pattern); + auto FromDef = FirstDeclMatcher<RecordDecl>().match(FromTU1, Pattern); + + Decl *ImportedDef = Import(FromDef, Lang_C); + + EXPECT_NE(ImportedDef, ToProto); + EXPECT_EQ(DeclCounter<RecordDecl>().match(ToTU, Pattern), 2u); + auto ToDef = LastDeclMatcher<RecordDecl>().match(ToTU, Pattern); + EXPECT_TRUE(ImportedDef == ToDef); + EXPECT_TRUE(ToDef->isThisDeclarationADefinition()); + EXPECT_FALSE(ToProto->isThisDeclarationADefinition()); + EXPECT_EQ(ToDef->getPreviousDecl(), ToProto); +} + +TEST_P(ImportClasses, ImportDefinitionWhenProtoIsInNestedToContextCXX) { + Decl *ToTU = getToTuDecl("struct A { struct X *Xp; };", Lang_CXX); + Decl *FromTU1 = getTuDecl("struct X {};", Lang_CXX, "input1.cc"); + auto Pattern = recordDecl(hasName("X"), unless(isImplicit())); + auto ToProto = FirstDeclMatcher<RecordDecl>().match(ToTU, Pattern); + auto FromDef = FirstDeclMatcher<RecordDecl>().match(FromTU1, Pattern); + + Decl *ImportedDef = Import(FromDef, Lang_CXX); + + EXPECT_NE(ImportedDef, ToProto); + EXPECT_EQ(DeclCounter<RecordDecl>().match(ToTU, Pattern), 2u); + auto ToDef = LastDeclMatcher<RecordDecl>().match(ToTU, Pattern); + EXPECT_TRUE(ImportedDef == ToDef); + EXPECT_TRUE(ToDef->isThisDeclarationADefinition()); + EXPECT_FALSE(ToProto->isThisDeclarationADefinition()); + EXPECT_EQ(ToDef->getPreviousDecl(), ToProto); +} + +TEST_P(ImportClasses, ImportNestedPrototypeThenDefinition) { + Decl *FromTU0 = getTuDecl("struct A { struct X *Xp; };", Lang_C, "input0.cc"); + Decl *FromTU1 = getTuDecl("struct X {};", Lang_C, "input1.cc"); + auto Pattern = recordDecl(hasName("X"), unless(isImplicit())); + auto FromProto = FirstDeclMatcher<RecordDecl>().match(FromTU0, Pattern); + auto FromDef = FirstDeclMatcher<RecordDecl>().match(FromTU1, Pattern); + + Decl *ImportedProto = Import(FromProto, Lang_C); + Decl *ImportedDef = Import(FromDef, Lang_C); + Decl *ToTU = ImportedDef->getTranslationUnitDecl(); + + EXPECT_NE(ImportedDef, ImportedProto); + EXPECT_EQ(DeclCounter<RecordDecl>().match(ToTU, Pattern), 2u); + auto ToProto = FirstDeclMatcher<RecordDecl>().match(ToTU, Pattern); + auto ToDef = LastDeclMatcher<RecordDecl>().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()); +} + +TEST_P(ASTImporterTestBase, FriendFunInClassTemplate) { + auto *Code = R"( + template <class T> + struct X { + friend void foo(){} + }; + )"; + TranslationUnitDecl *ToTU = getToTuDecl(Code, Lang_CXX); + auto *ToFoo = FirstDeclMatcher<FunctionDecl>().match( + ToTU, functionDecl(hasName("foo"))); + + TranslationUnitDecl *FromTU = getTuDecl(Code, Lang_CXX, "input.cc"); + auto *FromFoo = FirstDeclMatcher<FunctionDecl>().match( + FromTU, functionDecl(hasName("foo"))); + auto *ImportedFoo = Import(FromFoo, Lang_CXX); + EXPECT_EQ(ImportedFoo, ToFoo); +} + struct DeclContextTest : ASTImporterTestBase {}; TEST_P(DeclContextTest, removeDeclOfClassTemplateSpecialization) { @@ -3410,6 +4029,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, @@ -3796,6 +4449,416 @@ TEST_P(ASTImporterTestBase, ImportingTypedefShouldImportTheCompleteType) { EXPECT_FALSE(ImportedD->getUnderlyingType()->isIncompleteType()); } +struct ASTImporterLookupTableTest : ASTImporterTestBase {}; + +TEST_P(ASTImporterLookupTableTest, OneDecl) { + auto *ToTU = getToTuDecl("int a;", Lang_CXX); + auto *D = FirstDeclMatcher<VarDecl>().match(ToTU, varDecl(hasName("a"))); + ASTImporterLookupTable LT(*ToTU); + auto Res = LT.lookup(ToTU, D->getDeclName()); + ASSERT_EQ(Res.size(), 1u); + EXPECT_EQ(*Res.begin(), D); +} + +static Decl *findInDeclListOfDC(DeclContext *DC, DeclarationName Name) { + for (Decl *D : DC->decls()) { + if (auto *ND = dyn_cast<NamedDecl>(D)) + if (ND->getDeclName() == Name) + return ND; + } + return nullptr; +} + +TEST_P(ASTImporterLookupTableTest, + FriendWhichIsnotFoundByNormalLookupShouldBeFoundByImporterSpecificLookup) { + auto *Code = R"( + template <class T> + struct X { + friend void foo(){} + }; + )"; + TranslationUnitDecl *ToTU = getToTuDecl(Code, Lang_CXX); + auto *X = FirstDeclMatcher<ClassTemplateDecl>().match( + ToTU, classTemplateDecl(hasName("X"))); + auto *Foo = FirstDeclMatcher<FunctionDecl>().match( + ToTU, functionDecl(hasName("foo"))); + DeclContext *FooDC = Foo->getDeclContext(); + DeclContext *FooLexicalDC = Foo->getLexicalDeclContext(); + ASSERT_EQ(cast<Decl>(FooLexicalDC), X->getTemplatedDecl()); + ASSERT_EQ(cast<Decl>(FooDC), ToTU); + DeclarationName FooName = Foo->getDeclName(); + + // Cannot find in the LookupTable of its DC (TUDecl) + SmallVector<NamedDecl *, 2> FoundDecls; + FooDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls); + EXPECT_EQ(FoundDecls.size(), 0u); + + // Cannot find in the LookupTable of its LexicalDC (X) + FooLexicalDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls); + EXPECT_EQ(FoundDecls.size(), 0u); + + // Can't find in the list of Decls of the DC. + EXPECT_EQ(findInDeclListOfDC(FooDC, FooName), nullptr); + + // Can't find in the list of Decls of the LexicalDC + EXPECT_EQ(findInDeclListOfDC(FooLexicalDC, FooName), nullptr); + + // ASTImporter specific lookup finds it. + ASTImporterLookupTable LT(*ToTU); + auto Res = LT.lookup(FooDC, Foo->getDeclName()); + ASSERT_EQ(Res.size(), 1u); + EXPECT_EQ(*Res.begin(), Foo); +} + +TEST_P(ASTImporterLookupTableTest, + FwdDeclStructShouldBeFoundByImporterSpecificLookup) { + TranslationUnitDecl *ToTU = + getToTuDecl("struct A { struct Foo *p; };", Lang_C); + auto *Foo = + FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("Foo"))); + auto *A = + FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("A"))); + DeclContext *FooDC = Foo->getDeclContext(); + DeclContext *FooLexicalDC = Foo->getLexicalDeclContext(); + ASSERT_EQ(cast<Decl>(FooLexicalDC), A); + ASSERT_EQ(cast<Decl>(FooDC), ToTU); + DeclarationName FooName = Foo->getDeclName(); + + // Cannot find in the LookupTable of its DC (TUDecl). + SmallVector<NamedDecl *, 2> FoundDecls; + FooDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls); + EXPECT_EQ(FoundDecls.size(), 0u); + + // Cannot find in the LookupTable of its LexicalDC (A). + FooLexicalDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls); + EXPECT_EQ(FoundDecls.size(), 0u); + + // Can't find in the list of Decls of the DC. + EXPECT_EQ(findInDeclListOfDC(FooDC, FooName), nullptr); + + // Can find in the list of Decls of the LexicalDC. + EXPECT_EQ(findInDeclListOfDC(FooLexicalDC, FooName), Foo); + + // ASTImporter specific lookup finds it. + ASTImporterLookupTable LT(*ToTU); + auto Res = LT.lookup(FooDC, Foo->getDeclName()); + ASSERT_EQ(Res.size(), 1u); + EXPECT_EQ(*Res.begin(), Foo); +} + +TEST_P(ASTImporterLookupTableTest, LookupFindsNamesInDifferentDC) { + TranslationUnitDecl *ToTU = + getToTuDecl("int V; struct A { int V; }; struct B { int V; };", Lang_C); + DeclarationName VName = FirstDeclMatcher<VarDecl>() + .match(ToTU, varDecl(hasName("V"))) + ->getDeclName(); + auto *A = + FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("A"))); + auto *B = + FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("B"))); + + ASTImporterLookupTable LT(*ToTU); + + auto Res = LT.lookup(cast<DeclContext>(A), VName); + ASSERT_EQ(Res.size(), 1u); + EXPECT_EQ(*Res.begin(), FirstDeclMatcher<FieldDecl>().match( + ToTU, fieldDecl(hasName("V"), + hasParent(recordDecl(hasName("A")))))); + Res = LT.lookup(cast<DeclContext>(B), VName); + ASSERT_EQ(Res.size(), 1u); + EXPECT_EQ(*Res.begin(), FirstDeclMatcher<FieldDecl>().match( + ToTU, fieldDecl(hasName("V"), + hasParent(recordDecl(hasName("B")))))); + Res = LT.lookup(ToTU, VName); + ASSERT_EQ(Res.size(), 1u); + EXPECT_EQ(*Res.begin(), FirstDeclMatcher<VarDecl>().match( + ToTU, varDecl(hasName("V"), + hasParent(translationUnitDecl())))); +} + +TEST_P(ASTImporterLookupTableTest, LookupFindsOverloadedNames) { + TranslationUnitDecl *ToTU = getToTuDecl( + R"( + void foo(); + void foo(int); + void foo(int, int); + )", + Lang_CXX); + + ASTImporterLookupTable LT(*ToTU); + auto *F0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, functionDecl()); + auto *F2 = LastDeclMatcher<FunctionDecl>().match(ToTU, functionDecl()); + DeclarationName Name = F0->getDeclName(); + auto Res = LT.lookup(ToTU, Name); + EXPECT_EQ(Res.size(), 3u); + EXPECT_EQ(Res.count(F0), 1u); + EXPECT_EQ(Res.count(F2), 1u); +} + +static const RecordDecl * getRecordDeclOfFriend(FriendDecl *FD) { + QualType Ty = FD->getFriendType()->getType(); + QualType NamedTy = cast<ElaboratedType>(Ty)->getNamedType(); + return cast<RecordType>(NamedTy)->getDecl(); +} + +TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendClassDecl) { + TranslationUnitDecl *ToTU = getToTuDecl( + R"( + class Y { friend class F; }; + )", + Lang_CXX); + + // In this case, the CXXRecordDecl is hidden, the FriendDecl is not a parent. + // So we must dig up the underlying CXXRecordDecl. + ASTImporterLookupTable LT(*ToTU); + auto *FriendD = FirstDeclMatcher<FriendDecl>().match(ToTU, friendDecl()); + const RecordDecl *RD = getRecordDeclOfFriend(FriendD); + auto *Y = FirstDeclMatcher<CXXRecordDecl>().match( + ToTU, cxxRecordDecl(hasName("Y"))); + + DeclarationName Name = RD->getDeclName(); + auto Res = LT.lookup(ToTU, Name); + EXPECT_EQ(Res.size(), 1u); + EXPECT_EQ(*Res.begin(), RD); + + Res = LT.lookup(Y, Name); + EXPECT_EQ(Res.size(), 0u); +} + +TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendClassTemplateDecl) { + TranslationUnitDecl *ToTU = getToTuDecl( + R"( + class Y { template <class T> friend class F; }; + )", + Lang_CXX); + + ASTImporterLookupTable LT(*ToTU); + auto *F = FirstDeclMatcher<ClassTemplateDecl>().match( + ToTU, classTemplateDecl(hasName("F"))); + DeclarationName Name = F->getDeclName(); + auto Res = LT.lookup(ToTU, Name); + EXPECT_EQ(Res.size(), 2u); + EXPECT_EQ(Res.count(F), 1u); + EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u); +} + +TEST_P(ASTImporterLookupTableTest, DependentFriendClass) { + TranslationUnitDecl *ToTU = getToTuDecl( + R"( + template <typename T> + class F; + + template <typename T> + class Y { + friend class F<T>; + }; + )", + Lang_CXX); + + ASTImporterLookupTable LT(*ToTU); + auto *F = FirstDeclMatcher<ClassTemplateDecl>().match( + ToTU, classTemplateDecl(hasName("F"))); + DeclarationName Name = F->getDeclName(); + auto Res = LT.lookup(ToTU, Name); + EXPECT_EQ(Res.size(), 2u); + EXPECT_EQ(Res.count(F), 1u); + EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u); +} + +TEST_P(ASTImporterLookupTableTest, FriendClassTemplateSpecialization) { + TranslationUnitDecl *ToTU = getToTuDecl( + R"( + template <typename T> + class F; + + class Y { + friend class F<int>; + }; + )", + Lang_CXX); + + ASTImporterLookupTable LT(*ToTU); + auto *F = FirstDeclMatcher<ClassTemplateDecl>().match( + ToTU, classTemplateDecl(hasName("F"))); + DeclarationName Name = F->getDeclName(); + auto Res = LT.lookup(ToTU, Name); + ASSERT_EQ(Res.size(), 3u); + EXPECT_EQ(Res.count(F), 1u); + EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u); + EXPECT_EQ(Res.count(*F->spec_begin()), 1u); +} + +TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendFunctionDecl) { + TranslationUnitDecl *ToTU = getToTuDecl( + R"( + class Y { friend void F(); }; + )", + Lang_CXX); + + ASTImporterLookupTable LT(*ToTU); + auto *F = + FirstDeclMatcher<FunctionDecl>().match(ToTU, functionDecl(hasName("F"))); + DeclarationName Name = F->getDeclName(); + auto Res = LT.lookup(ToTU, Name); + EXPECT_EQ(Res.size(), 1u); + EXPECT_EQ(*Res.begin(), F); +} + +TEST_P(ASTImporterLookupTableTest, + LookupFindsDeclsInClassTemplateSpecialization) { + TranslationUnitDecl *ToTU = getToTuDecl( + R"( + template <typename T> + struct X { + int F; + }; + void foo() { + X<char> xc; + } + )", + Lang_CXX); + + ASTImporterLookupTable LT(*ToTU); + + auto *Template = FirstDeclMatcher<ClassTemplateDecl>().match( + ToTU, classTemplateDecl(hasName("X"))); + auto *FieldInTemplate = FirstDeclMatcher<FieldDecl>().match( + ToTU, + fieldDecl(hasParent(cxxRecordDecl(hasParent(classTemplateDecl()))))); + + auto *Spec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match( + ToTU, classTemplateSpecializationDecl(hasName("X"))); + FieldDecl *FieldInSpec = *Spec->field_begin(); + ASSERT_TRUE(FieldInSpec); + + DeclarationName Name = FieldInSpec->getDeclName(); + auto TemplateDC = cast<DeclContext>(Template->getTemplatedDecl()); + + SmallVector<NamedDecl *, 2> FoundDecls; + TemplateDC->getRedeclContext()->localUncachedLookup(Name, FoundDecls); + EXPECT_EQ(FoundDecls.size(), 1u); + EXPECT_EQ(FoundDecls[0], FieldInTemplate); + + auto Res = LT.lookup(TemplateDC, Name); + ASSERT_EQ(Res.size(), 1u); + EXPECT_EQ(*Res.begin(), FieldInTemplate); + + cast<DeclContext>(Spec)->getRedeclContext()->localUncachedLookup(Name, + FoundDecls); + EXPECT_EQ(FoundDecls.size(), 1u); + EXPECT_EQ(FoundDecls[0], FieldInSpec); + + Res = LT.lookup(cast<DeclContext>(Spec), Name); + ASSERT_EQ(Res.size(), 1u); + EXPECT_EQ(*Res.begin(), FieldInSpec); +} + +TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendFunctionTemplateDecl) { + TranslationUnitDecl *ToTU = getToTuDecl( + R"( + class Y { template <class T> friend void F(); }; + )", + Lang_CXX); + + ASTImporterLookupTable LT(*ToTU); + auto *F = FirstDeclMatcher<FunctionTemplateDecl>().match( + ToTU, functionTemplateDecl(hasName("F"))); + DeclarationName Name = F->getDeclName(); + auto Res = LT.lookup(ToTU, Name); + EXPECT_EQ(Res.size(), 2u); + EXPECT_EQ(Res.count(F), 1u); + EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u); +} + +TEST_P(ASTImporterLookupTableTest, MultipleBefriendingClasses) { + TranslationUnitDecl *ToTU = getToTuDecl( + R"( + struct X; + struct A { + friend struct X; + }; + struct B { + friend struct X; + }; + )", + Lang_CXX); + + ASTImporterLookupTable LT(*ToTU); + auto *X = FirstDeclMatcher<CXXRecordDecl>().match( + ToTU, cxxRecordDecl(hasName("X"))); + auto *FriendD0 = FirstDeclMatcher<FriendDecl>().match(ToTU, friendDecl()); + auto *FriendD1 = LastDeclMatcher<FriendDecl>().match(ToTU, friendDecl()); + const RecordDecl *RD0 = getRecordDeclOfFriend(FriendD0); + const RecordDecl *RD1 = getRecordDeclOfFriend(FriendD1); + ASSERT_EQ(RD0, RD1); + ASSERT_EQ(RD1, X); + + DeclarationName Name = X->getDeclName(); + auto Res = LT.lookup(ToTU, Name); + EXPECT_EQ(Res.size(), 1u); + EXPECT_EQ(*Res.begin(), X); +} + +TEST_P(ASTImporterLookupTableTest, EnumConstantDecl) { + TranslationUnitDecl *ToTU = getToTuDecl( + R"( + enum E { + A, + B + }; + )", + Lang_C); + + ASTImporterLookupTable LT(*ToTU); + auto *E = FirstDeclMatcher<EnumDecl>().match(ToTU, enumDecl(hasName("E"))); + auto *A = FirstDeclMatcher<EnumConstantDecl>().match( + ToTU, enumConstantDecl(hasName("A"))); + + DeclarationName Name = A->getDeclName(); + // Redecl context is the TU. + ASSERT_EQ(E->getRedeclContext(), ToTU); + + SmallVector<NamedDecl *, 2> FoundDecls; + // Normal lookup finds in the DC. + E->localUncachedLookup(Name, FoundDecls); + EXPECT_EQ(FoundDecls.size(), 1u); + + // Normal lookup finds in the Redecl context. + ToTU->localUncachedLookup(Name, FoundDecls); + EXPECT_EQ(FoundDecls.size(), 1u); + + // Import specific lookup finds in the DC. + auto Res = LT.lookup(E, Name); + ASSERT_EQ(Res.size(), 1u); + EXPECT_EQ(*Res.begin(), A); + + // Import specific lookup finds in the Redecl context. + Res = LT.lookup(ToTU, Name); + ASSERT_EQ(Res.size(), 1u); + EXPECT_EQ(*Res.begin(), A); +} + +TEST_P(ASTImporterLookupTableTest, LookupSearchesInTheWholeRedeclChain) { + TranslationUnitDecl *ToTU = getToTuDecl( + R"( + namespace N { + int A; + } + namespace N { + } + )", + Lang_CXX); + auto *N1 = + LastDeclMatcher<NamespaceDecl>().match(ToTU, namespaceDecl(hasName("N"))); + auto *A = FirstDeclMatcher<VarDecl>().match(ToTU, varDecl(hasName("A"))); + DeclarationName Name = A->getDeclName(); + + ASTImporterLookupTable LT(*ToTU); + auto Res = LT.lookup(N1, Name); + ASSERT_EQ(Res.size(), 1u); + EXPECT_EQ(*Res.begin(), A); +} + INSTANTIATE_TEST_CASE_P(ParameterizedTests, DeclContextTest, ::testing::Values(ArgVector()), ); @@ -3809,6 +4872,9 @@ auto DefaultTestValuesForRunOptions = ::testing::Values( ArgVector{"-fms-compatibility"}, ArgVector{"-fdelayed-template-parsing", "-fms-compatibility"}); +INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterLookupTableTest, + DefaultTestValuesForRunOptions, ); + INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportExpr, DefaultTestValuesForRunOptions, ); @@ -3824,9 +4890,18 @@ INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterTestBase, INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctions, DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClasses, + DefaultTestValuesForRunOptions, ); + INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendFunctions, DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClassTemplates, + DefaultTestValuesForRunOptions, ); + +INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendClasses, + DefaultTestValuesForRunOptions, ); + INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctionTemplateSpecializations, DefaultTestValuesForRunOptions, ); |