diff options
Diffstat (limited to 'clang/unittests/AST/ASTImporterTest.cpp')
-rw-r--r-- | clang/unittests/AST/ASTImporterTest.cpp | 579 |
1 files changed, 551 insertions, 28 deletions
diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp index 3407f7da6d1..aaadeb0b9d6 100644 --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -15,6 +15,8 @@ #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" @@ -308,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); } }; @@ -339,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) { @@ -359,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; @@ -375,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); @@ -420,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(); } @@ -432,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() { @@ -2727,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); @@ -3486,6 +3502,82 @@ TEST_P(ImportClasses, ImportPrototypeThenDefinition) { 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, @@ -3890,6 +3982,24 @@ TEST_P(ImportFriendClasses, ImportOfClassDefinitionAndFwdFriendShouldBeLinked) { 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) { @@ -4339,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()), ); @@ -4352,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, ); @@ -4367,10 +4890,10 @@ INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterTestBase, INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctions, DefaultTestValuesForRunOptions, ); -INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendFunctions, +INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClasses, DefaultTestValuesForRunOptions, ); -INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClasses, +INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendFunctions, DefaultTestValuesForRunOptions, ); INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClassTemplates, |