[ASTImporter] Add importer specific lookup
Summary:
There are certain cases when normal C/C++ lookup (localUncachedLookup)
does not find AST nodes. E.g.:
Example 1:
template <class T>
struct X {
friend void foo(); // this is never found in the DC of the TU.
};
Example 2:
// The fwd decl to Foo is not found in the lookupPtr of the DC of the
// translation unit decl.
struct A { struct Foo *p; };
In these cases we create a new node instead of returning with the old one.
To fix it we create a new lookup table which holds every node and we are
not interested in any C++ specific visibility considerations.
Simply, we must know if there is an existing Decl in a given DC.
Reviewers: a_sidorin, a.sidorin
Subscribers: mgorny, rnkovacs, dkrupp, Szelethus, cfe-commits
Differential Revision: https://reviews.llvm.org/D53708
llvm-svn: 349351
diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp
index 3407f7d..aaadeb0 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 @@
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 @@
// 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 @@
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 @@
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 @@
// 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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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, 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,