summaryrefslogtreecommitdiff
path: root/unittests/AST/ASTImporterTest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'unittests/AST/ASTImporterTest.cpp')
-rw-r--r--unittests/AST/ASTImporterTest.cpp2027
1 files changed, 1974 insertions, 53 deletions
diff --git a/unittests/AST/ASTImporterTest.cpp b/unittests/AST/ASTImporterTest.cpp
index 21b76173c1057..c6acf573e582d 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"
@@ -37,10 +40,10 @@ createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName,
std::unique_ptr<llvm::MemoryBuffer> &&Buffer) {
assert(ToAST);
ASTContext &ToCtx = ToAST->getASTContext();
- auto *OFS = static_cast<vfs::OverlayFileSystem *>(
+ auto *OFS = static_cast<llvm::vfs::OverlayFileSystem *>(
ToCtx.getSourceManager().getFileManager().getVirtualFileSystem().get());
- auto *MFS =
- static_cast<vfs::InMemoryFileSystem *>(OFS->overlays_begin()->get());
+ auto *MFS = static_cast<llvm::vfs::InMemoryFileSystem *>(
+ OFS->overlays_begin()->get());
MFS->addFile(FileName, 0, std::move(Buffer));
}
@@ -140,6 +143,7 @@ class TestImportBase : public ParameterizedTestsFixture {
if (!Imported)
return testing::AssertionFailure() << "Import failed, nullptr returned!";
+
return Verifier.match(Imported, WrapperMatcher);
}
@@ -306,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);
}
};
@@ -337,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) {
@@ -357,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;
@@ -373,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);
@@ -418,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();
}
@@ -430,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() {
@@ -502,7 +519,6 @@ TEST_P(CanonicalRedeclChain, ShouldBeSameForAllDeclInTheChain) {
EXPECT_THAT(RedeclsD1, ::testing::ContainerEq(RedeclsD2));
}
-
TEST_P(ImportExpr, ImportStringLiteral) {
MatchVerifier<Decl> Verifier;
testImport(
@@ -553,6 +569,14 @@ TEST_P(ImportExpr, ImportFloatinglLiteralExpr) {
floatLiteral(equals(1.0e-5f), hasType(asString("float"))))));
}
+TEST_P(ImportExpr, ImportImaginaryLiteralExpr) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ "void declToImport() { (void)1.0i; }",
+ Lang_CXX14, "", Lang_CXX14, Verifier,
+ functionDecl(hasDescendant(imaginaryLiteral())));
+}
+
TEST_P(ImportExpr, ImportCompoundLiteralExpr) {
MatchVerifier<Decl> Verifier;
testImport(
@@ -645,7 +669,6 @@ TEST_P(ImportExpr, ImportSwitch) {
TEST_P(ImportExpr, ImportStmtExpr) {
MatchVerifier<Decl> Verifier;
- // NOTE: has() ignores implicit casts, using hasDescendant() to match it
testImport(
"void declToImport() { int b; int a = b ?: 1; int C = ({int X=4; X;}); }",
Lang_C, "", Lang_C, Verifier,
@@ -712,19 +735,18 @@ TEST_P(ImportExpr, ImportDesignatedInitExpr) {
initListExpr(
has(designatedInitExpr(
designatorCountIs(2),
- has(floatLiteral(equals(1.0))),
- has(integerLiteral(equals(2))))),
+ hasDescendant(floatLiteral(equals(1.0))),
+ hasDescendant(integerLiteral(equals(2))))),
has(designatedInitExpr(
designatorCountIs(2),
- has(floatLiteral(equals(2.0))),
- has(integerLiteral(equals(2))))),
+ hasDescendant(floatLiteral(equals(2.0))),
+ hasDescendant(integerLiteral(equals(2))))),
has(designatedInitExpr(
designatorCountIs(2),
- has(floatLiteral(equals(1.0))),
- has(integerLiteral(equals(0)))))))));
+ hasDescendant(floatLiteral(equals(1.0))),
+ hasDescendant(integerLiteral(equals(0)))))))));
}
-
TEST_P(ImportExpr, ImportPredefinedExpr) {
MatchVerifier<Decl> Verifier;
// __func__ expands as StringLiteral("declToImport")
@@ -806,9 +828,6 @@ TEST_P(ImportDecl, ImportFunctionTemplateDecl) {
functionTemplateDecl());
}
-const internal::VariadicDynCastAllOfMatcher<Expr, CXXDependentScopeMemberExpr>
- cxxDependentScopeMemberExpr;
-
TEST_P(ImportExpr, ImportCXXDependentScopeMemberExpr) {
MatchVerifier<Decl> Verifier;
testImport(
@@ -989,7 +1008,7 @@ TEST_P(ASTImporterTestBase, ImportRecordTypeInFunc) {
" return 0;"
"}",
Lang_C, "input.c");
- auto FromVar =
+ auto *FromVar =
FirstDeclMatcher<VarDecl>().match(FromTU, varDecl(hasName("d")));
ASSERT_TRUE(FromVar);
auto ToType =
@@ -999,12 +1018,41 @@ TEST_P(ASTImporterTestBase, ImportRecordTypeInFunc) {
TEST_P(ASTImporterTestBase, ImportRecordDeclInFuncParams) {
// This construct is not supported by ASTImporter.
- Decl *FromTU =
- getTuDecl("int declToImport(struct data_t{int a;int b;} *d){ return 0; }",
- Lang_C, "input.c");
- auto From = FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());
+ Decl *FromTU = getTuDecl(
+ "int declToImport(struct data_t{int a;int b;} ***d){ return 0; }",
+ Lang_C, "input.c");
+ auto *From = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("declToImport")));
ASSERT_TRUE(From);
- auto To = Import(From, Lang_C);
+ auto *To = Import(From, Lang_C);
+ EXPECT_EQ(To, nullptr);
+}
+
+TEST_P(ASTImporterTestBase, ImportRecordDeclInFuncFromMacro) {
+ Decl *FromTU = getTuDecl(
+ "#define NONAME_SIZEOF(type) sizeof(struct{type *dummy;}) \n"
+ "int declToImport(){ return NONAME_SIZEOF(int); }",
+ Lang_C, "input.c");
+ auto *From = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("declToImport")));
+ ASSERT_TRUE(From);
+ auto *To = Import(From, Lang_C);
+ ASSERT_TRUE(To);
+ EXPECT_TRUE(MatchVerifier<FunctionDecl>().match(
+ To, functionDecl(hasName("declToImport"),
+ hasDescendant(unaryExprOrTypeTraitExpr()))));
+}
+
+TEST_P(ASTImporterTestBase, ImportRecordDeclInFuncParamsFromMacro) {
+ // This construct is not supported by ASTImporter.
+ Decl *FromTU = getTuDecl(
+ "#define PAIR_STRUCT(type) struct data_t{type a;type b;} \n"
+ "int declToImport(PAIR_STRUCT(int) ***d){ return 0; }",
+ Lang_C, "input.c");
+ auto *From = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("declToImport")));
+ ASSERT_TRUE(From);
+ auto *To = Import(From, Lang_C);
EXPECT_EQ(To, nullptr);
}
@@ -1776,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;
@@ -1795,13 +1902,62 @@ TEST_P(ASTImporterTestBase, ImportDoesUpdateUsedFlag) {
{
Decl *FromTU =
getTuDecl("extern int x; int f() { return x; }", Lang_CXX, "input2.cc");
- auto *FromD =
- FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
Import(FromD, Lang_CXX);
}
EXPECT_TRUE(Imported2->isUsed(false));
}
+TEST_P(ASTImporterTestBase, ImportDoesUpdateUsedFlag2) {
+ auto Pattern = varDecl(hasName("x"));
+ VarDecl *ExistingD;
+ {
+ Decl *ToTU = getToTuDecl("int x = 1;", Lang_CXX);
+ ExistingD = FirstDeclMatcher<VarDecl>().match(ToTU, Pattern);
+ }
+ EXPECT_FALSE(ExistingD->isUsed(false));
+ {
+ Decl *FromTU = getTuDecl(
+ "int x = 1; int f() { return x; }", Lang_CXX, "input1.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+ Import(FromD, Lang_CXX);
+ }
+ EXPECT_TRUE(ExistingD->isUsed(false));
+}
+
+TEST_P(ASTImporterTestBase, ImportDoesUpdateUsedFlag3) {
+ auto Pattern = varDecl(hasName("a"));
+ VarDecl *ExistingD;
+ {
+ Decl *ToTU = getToTuDecl(
+ R"(
+ struct A {
+ static const int a = 1;
+ };
+ )", Lang_CXX);
+ ExistingD = FirstDeclMatcher<VarDecl>().match(ToTU, Pattern);
+ }
+ EXPECT_FALSE(ExistingD->isUsed(false));
+ {
+ Decl *FromTU = getTuDecl(
+ R"(
+ struct A {
+ static const int a = 1;
+ };
+ const int *f() { return &A::a; } // requires storage,
+ // thus used flag will be set
+ )", Lang_CXX, "input1.cc");
+ auto *FromFunD = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+ auto *FromD = FirstDeclMatcher<VarDecl>().match(FromTU, Pattern);
+ ASSERT_TRUE(FromD->isUsed(false));
+ Import(FromFunD, Lang_CXX);
+ }
+ EXPECT_TRUE(ExistingD->isUsed(false));
+}
+
TEST_P(ASTImporterTestBase, ReimportWithUsedFlag) {
auto Pattern = varDecl(hasName("x"));
@@ -2241,6 +2397,211 @@ TEST_P(ImportFriendFunctions, ImportFriendFunctionFromMultipleTU) {
EXPECT_EQ(ImportedD1->getPreviousDecl(), ImportedD);
}
+TEST_P(ImportFriendFunctions, Lookup) {
+ auto FunctionPattern = functionDecl(hasName("f"));
+ auto ClassPattern = cxxRecordDecl(hasName("X"));
+
+ TranslationUnitDecl *FromTU =
+ getTuDecl("struct X { friend void f(); };", Lang_CXX, "input0.cc");
+ auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, FunctionPattern);
+ ASSERT_TRUE(FromD->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+ ASSERT_FALSE(FromD->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+ {
+ auto FromName = FromD->getDeclName();
+ auto *Class = FirstDeclMatcher<CXXRecordDecl>().match(FromTU, ClassPattern);
+ auto LookupRes = Class->noload_lookup(FromName);
+ ASSERT_EQ(LookupRes.size(), 0u);
+ LookupRes = FromTU->noload_lookup(FromName);
+ ASSERT_EQ(LookupRes.size(), 1u);
+ }
+
+ auto *ToD = cast<FunctionDecl>(Import(FromD, Lang_CXX));
+ auto ToName = ToD->getDeclName();
+
+ TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ auto *Class = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, ClassPattern);
+ auto LookupRes = Class->noload_lookup(ToName);
+ EXPECT_EQ(LookupRes.size(), 0u);
+ LookupRes = ToTU->noload_lookup(ToName);
+ EXPECT_EQ(LookupRes.size(), 1u);
+
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, FunctionPattern), 1u);
+ auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, FunctionPattern);
+ EXPECT_TRUE(To0->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+ EXPECT_FALSE(To0->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+}
+
+TEST_P(ImportFriendFunctions, DISABLED_LookupWithProtoAfter) {
+ auto FunctionPattern = functionDecl(hasName("f"));
+ auto ClassPattern = cxxRecordDecl(hasName("X"));
+
+ TranslationUnitDecl *FromTU = getTuDecl(
+ "struct X { friend void f(); };"
+ // This proto decl makes f available to normal
+ // lookup, otherwise it is hidden.
+ // Normal C++ lookup (implemented in
+ // `clang::Sema::CppLookupName()` and in `LookupDirect()`)
+ // returns the found `NamedDecl` only if the set IDNS is matched
+ "void f();",
+ Lang_CXX, "input0.cc");
+ auto *FromFriend =
+ FirstDeclMatcher<FunctionDecl>().match(FromTU, FunctionPattern);
+ auto *FromNormal =
+ LastDeclMatcher<FunctionDecl>().match(FromTU, FunctionPattern);
+ ASSERT_TRUE(FromFriend->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+ ASSERT_FALSE(FromFriend->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+ ASSERT_FALSE(FromNormal->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+ ASSERT_TRUE(FromNormal->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+
+ auto FromName = FromFriend->getDeclName();
+ auto *FromClass =
+ FirstDeclMatcher<CXXRecordDecl>().match(FromTU, ClassPattern);
+ auto LookupRes = FromClass->noload_lookup(FromName);
+ ASSERT_EQ(LookupRes.size(), 0u);
+ LookupRes = FromTU->noload_lookup(FromName);
+ ASSERT_EQ(LookupRes.size(), 1u);
+
+ auto *ToFriend = cast<FunctionDecl>(Import(FromFriend, Lang_CXX));
+ auto ToName = ToFriend->getDeclName();
+
+ TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ auto *ToClass = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, ClassPattern);
+ LookupRes = ToClass->noload_lookup(ToName);
+ EXPECT_EQ(LookupRes.size(), 0u);
+ LookupRes = ToTU->noload_lookup(ToName);
+ // Test is disabled because this result is 2.
+ EXPECT_EQ(LookupRes.size(), 1u);
+
+ ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, FunctionPattern), 2u);
+ ToFriend = FirstDeclMatcher<FunctionDecl>().match(ToTU, FunctionPattern);
+ auto *ToNormal = LastDeclMatcher<FunctionDecl>().match(ToTU, FunctionPattern);
+ EXPECT_TRUE(ToFriend->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+ EXPECT_FALSE(ToFriend->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+ EXPECT_FALSE(ToNormal->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+ EXPECT_TRUE(ToNormal->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+}
+
+TEST_P(ImportFriendFunctions, LookupWithProtoBefore) {
+ auto FunctionPattern = functionDecl(hasName("f"));
+ auto ClassPattern = cxxRecordDecl(hasName("X"));
+
+ TranslationUnitDecl *FromTU = getTuDecl(
+ "void f();"
+ "struct X { friend void f(); };",
+ Lang_CXX, "input0.cc");
+ auto *FromNormal =
+ FirstDeclMatcher<FunctionDecl>().match(FromTU, FunctionPattern);
+ auto *FromFriend =
+ LastDeclMatcher<FunctionDecl>().match(FromTU, FunctionPattern);
+ ASSERT_FALSE(FromNormal->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+ ASSERT_TRUE(FromNormal->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+ ASSERT_TRUE(FromFriend->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+ ASSERT_TRUE(FromFriend->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+
+ auto FromName = FromNormal->getDeclName();
+ auto *FromClass =
+ FirstDeclMatcher<CXXRecordDecl>().match(FromTU, ClassPattern);
+ auto LookupRes = FromClass->noload_lookup(FromName);
+ ASSERT_EQ(LookupRes.size(), 0u);
+ LookupRes = FromTU->noload_lookup(FromName);
+ ASSERT_EQ(LookupRes.size(), 1u);
+
+ auto *ToNormal = cast<FunctionDecl>(Import(FromNormal, Lang_CXX));
+ auto ToName = ToNormal->getDeclName();
+ TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+
+ auto *ToClass = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, ClassPattern);
+ LookupRes = ToClass->noload_lookup(ToName);
+ EXPECT_EQ(LookupRes.size(), 0u);
+ LookupRes = ToTU->noload_lookup(ToName);
+ EXPECT_EQ(LookupRes.size(), 1u);
+
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, FunctionPattern), 2u);
+ ToNormal = FirstDeclMatcher<FunctionDecl>().match(ToTU, FunctionPattern);
+ auto *ToFriend = LastDeclMatcher<FunctionDecl>().match(ToTU, FunctionPattern);
+ EXPECT_FALSE(ToNormal->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+ EXPECT_TRUE(ToNormal->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+ EXPECT_TRUE(ToFriend->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+ EXPECT_TRUE(ToFriend->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+}
+
+TEST_P(ImportFriendFunctions, ImportFriendChangesLookup) {
+ auto Pattern = functionDecl(hasName("f"));
+
+ TranslationUnitDecl *FromNormalTU =
+ getTuDecl("void f();", Lang_CXX, "input0.cc");
+ auto *FromNormalF =
+ FirstDeclMatcher<FunctionDecl>().match(FromNormalTU, Pattern);
+ TranslationUnitDecl *FromFriendTU =
+ getTuDecl("class X { friend void f(); };", Lang_CXX, "input1.cc");
+ auto *FromFriendF =
+ FirstDeclMatcher<FunctionDecl>().match(FromFriendTU, Pattern);
+ auto FromNormalName = FromNormalF->getDeclName();
+ auto FromFriendName = FromFriendF->getDeclName();
+
+ ASSERT_TRUE(FromNormalF->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+ ASSERT_FALSE(FromNormalF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+ ASSERT_FALSE(FromFriendF->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+ ASSERT_TRUE(FromFriendF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+ auto LookupRes = FromNormalTU->noload_lookup(FromNormalName);
+ ASSERT_EQ(LookupRes.size(), 1u);
+ LookupRes = FromFriendTU->noload_lookup(FromFriendName);
+ ASSERT_EQ(LookupRes.size(), 1u);
+
+ auto *ToNormalF = cast<FunctionDecl>(Import(FromNormalF, Lang_CXX));
+ TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ auto ToName = ToNormalF->getDeclName();
+ EXPECT_TRUE(ToNormalF->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+ EXPECT_FALSE(ToNormalF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+ LookupRes = ToTU->noload_lookup(ToName);
+ EXPECT_EQ(LookupRes.size(), 1u);
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
+
+ auto *ToFriendF = cast<FunctionDecl>(Import(FromFriendF, Lang_CXX));
+ LookupRes = ToTU->noload_lookup(ToName);
+ EXPECT_EQ(LookupRes.size(), 1u);
+ EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
+
+ EXPECT_TRUE(ToNormalF->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+ EXPECT_FALSE(ToNormalF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+
+ EXPECT_TRUE(ToFriendF->isInIdentifierNamespace(Decl::IDNS_Ordinary));
+ EXPECT_TRUE(ToFriendF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
+}
+
+TEST_P(ImportFriendFunctions, ImportFriendList) {
+ TranslationUnitDecl *FromTU = getTuDecl(
+ "struct X { friend void f(); };"
+ "void f();",
+ Lang_CXX, "input0.cc");
+ auto *FromFriendF = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+
+ auto *FromClass = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("X")));
+ auto *FromFriend = FirstDeclMatcher<FriendDecl>().match(FromTU, friendDecl());
+ auto FromFriends = FromClass->friends();
+ unsigned int FrN = 0;
+ for (auto Fr : FromFriends) {
+ ASSERT_EQ(Fr, FromFriend);
+ ++FrN;
+ }
+ ASSERT_EQ(FrN, 1u);
+
+ Import(FromFriendF, Lang_CXX);
+ TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ auto *ToClass = FirstDeclMatcher<CXXRecordDecl>().match(
+ ToTU, cxxRecordDecl(hasName("X")));
+ auto *ToFriend = FirstDeclMatcher<FriendDecl>().match(ToTU, friendDecl());
+ auto ToFriends = ToClass->friends();
+ FrN = 0;
+ for (auto Fr : ToFriends) {
+ EXPECT_EQ(Fr, ToFriend);
+ ++FrN;
+ }
+ EXPECT_EQ(FrN, 1u);
+}
+
AST_MATCHER_P(TagDecl, hasTypedefForAnonDecl, Matcher<TypedefNameDecl>,
InnerMatcher) {
if (auto *Typedef = Node.getTypedefNameForAnonDecl())
@@ -2327,9 +2688,6 @@ TEST_P(ImportExpr, DependentNameType) {
cxxRecordDecl(has(typedefDecl(has(dependentNameType())))))));
}
-const internal::VariadicDynCastAllOfMatcher<Expr, UnresolvedMemberExpr>
- unresolvedMemberExpr;
-
TEST_P(ImportExpr, UnresolvedMemberExpr) {
MatchVerifier<Decl> Verifier;
testImport("struct S { template <typename T> void mem(); };"
@@ -2384,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);
@@ -2474,7 +2833,7 @@ TEST_P(ASTImporterTestBase, ImportOfEquivalentRecord) {
ToR2 = Import(FromR, Lang_CXX);
}
-
+
EXPECT_EQ(ToR1, ToR2);
}
@@ -2612,6 +2971,1035 @@ TEST_P(ASTImporterTestBase, ImportUnnamedStructsWithRecursingField) {
R1, recordDecl(has(fieldDecl(hasName("next"))))));
}
+TEST_P(ASTImporterTestBase, ImportUnnamedFieldsInCorrectOrder) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ void f(int X, int Y, bool Z) {
+ (void)[X, Y, Z] { (void)Z; };
+ }
+ )",
+ Lang_CXX11, "input0.cc");
+ auto *FromF = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+ auto *ToF = cast_or_null<FunctionDecl>(Import(FromF, Lang_CXX11));
+ EXPECT_TRUE(ToF);
+
+ CXXRecordDecl *FromLambda =
+ cast<LambdaExpr>(cast<CStyleCastExpr>(cast<CompoundStmt>(
+ FromF->getBody())->body_front())->getSubExpr())->getLambdaClass();
+
+ auto *ToLambda = cast_or_null<CXXRecordDecl>(Import(FromLambda, Lang_CXX11));
+ EXPECT_TRUE(ToLambda);
+
+ // Check if the fields of the lambda class are imported in correct order.
+ unsigned FromIndex = 0u;
+ for (auto *FromField : FromLambda->fields()) {
+ ASSERT_FALSE(FromField->getDeclName());
+ auto *ToField = cast_or_null<FieldDecl>(Import(FromField, Lang_CXX11));
+ EXPECT_TRUE(ToField);
+ Optional<unsigned> ToIndex = ASTImporter::getFieldIndex(ToField);
+ EXPECT_TRUE(ToIndex);
+ EXPECT_EQ(*ToIndex, FromIndex);
+ ++FromIndex;
+ }
+
+ EXPECT_EQ(FromIndex, 3u);
+}
+
+TEST_P(ASTImporterTestBase, MergeFieldDeclsOfClassTemplateSpecialization) {
+ std::string ClassTemplate =
+ R"(
+ template <typename T>
+ struct X {
+ int a{0}; // FieldDecl with InitListExpr
+ X(char) : a(3) {} // (1)
+ X(int) {} // (2)
+ };
+ )";
+ Decl *ToTU = getToTuDecl(ClassTemplate +
+ R"(
+ void foo() {
+ // ClassTemplateSpec with ctor (1): FieldDecl without InitlistExpr
+ X<char> xc('c');
+ }
+ )", Lang_CXX11);
+ auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ ToTU, classTemplateSpecializationDecl(hasName("X")));
+ // FieldDecl without InitlistExpr:
+ auto *ToField = *ToSpec->field_begin();
+ ASSERT_TRUE(ToField);
+ ASSERT_FALSE(ToField->getInClassInitializer());
+ Decl *FromTU = getTuDecl(ClassTemplate +
+ R"(
+ void bar() {
+ // ClassTemplateSpec with ctor (2): FieldDecl WITH InitlistExpr
+ X<char> xc(1);
+ }
+ )", Lang_CXX11);
+ auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ FromTU, classTemplateSpecializationDecl(hasName("X")));
+ // FieldDecl with InitlistExpr:
+ auto *FromField = *FromSpec->field_begin();
+ ASSERT_TRUE(FromField);
+ ASSERT_TRUE(FromField->getInClassInitializer());
+
+ auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
+ ASSERT_TRUE(ImportedSpec);
+ EXPECT_EQ(ImportedSpec, ToSpec);
+ // After the import, the FieldDecl has to be merged, thus it should have the
+ // InitListExpr.
+ EXPECT_TRUE(ToField->getInClassInitializer());
+}
+
+TEST_P(ASTImporterTestBase, MergeFunctionOfClassTemplateSpecialization) {
+ std::string ClassTemplate =
+ R"(
+ template <typename T>
+ struct X {
+ void f() {}
+ void g() {}
+ };
+ )";
+ Decl *ToTU = getToTuDecl(ClassTemplate +
+ R"(
+ void foo() {
+ X<char> x;
+ x.f();
+ }
+ )", Lang_CXX11);
+ Decl *FromTU = getTuDecl(ClassTemplate +
+ R"(
+ void bar() {
+ X<char> x;
+ x.g();
+ }
+ )", Lang_CXX11);
+ auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ FromTU, classTemplateSpecializationDecl(hasName("X")));
+ auto FunPattern = functionDecl(hasName("g"),
+ hasParent(classTemplateSpecializationDecl()));
+ auto *FromFun =
+ FirstDeclMatcher<FunctionDecl>().match(FromTU, FunPattern);
+ auto *ToFun =
+ FirstDeclMatcher<FunctionDecl>().match(ToTU, FunPattern);
+ ASSERT_TRUE(FromFun->hasBody());
+ ASSERT_FALSE(ToFun->hasBody());
+ auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
+ ASSERT_TRUE(ImportedSpec);
+ auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ ToTU, classTemplateSpecializationDecl(hasName("X")));
+ EXPECT_EQ(ImportedSpec, ToSpec);
+ EXPECT_TRUE(ToFun->hasBody());
+}
+
+TEST_P(ASTImporterTestBase,
+ ODRViolationOfClassTemplateSpecializationsShouldBeReported) {
+ std::string ClassTemplate =
+ R"(
+ template <typename T>
+ struct X {};
+ )";
+ Decl *ToTU = getToTuDecl(ClassTemplate +
+ R"(
+ template <>
+ struct X<char> {
+ int a;
+ };
+ void foo() {
+ X<char> x;
+ }
+ )",
+ Lang_CXX11);
+ Decl *FromTU = getTuDecl(ClassTemplate +
+ R"(
+ template <>
+ struct X<char> {
+ int b;
+ };
+ void foo() {
+ X<char> x;
+ }
+ )",
+ Lang_CXX11);
+ auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ FromTU, classTemplateSpecializationDecl(hasName("X")));
+ auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
+
+ // We expect one (ODR) warning during the import.
+ EXPECT_EQ(1u, ToTU->getASTContext().getDiagnostics().getNumWarnings());
+
+ // The second specialization is different from the first, thus it violates
+ // ODR, consequently we expect to keep the first specialization only, which is
+ // already in the "To" context.
+ EXPECT_TRUE(ImportedSpec);
+ auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ ToTU, classTemplateSpecializationDecl(hasName("X")));
+ EXPECT_EQ(ImportedSpec, ToSpec);
+ EXPECT_EQ(1u, DeclCounter<ClassTemplateSpecializationDecl>().match(
+ ToTU, classTemplateSpecializationDecl()));
+}
+
+TEST_P(ASTImporterTestBase, MergeCtorOfClassTemplateSpecialization) {
+ std::string ClassTemplate =
+ R"(
+ template <typename T>
+ struct X {
+ X(char) {}
+ X(int) {}
+ };
+ )";
+ Decl *ToTU = getToTuDecl(ClassTemplate +
+ R"(
+ void foo() {
+ X<char> x('c');
+ }
+ )", Lang_CXX11);
+ Decl *FromTU = getTuDecl(ClassTemplate +
+ R"(
+ void bar() {
+ X<char> x(1);
+ }
+ )", Lang_CXX11);
+ auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ FromTU, classTemplateSpecializationDecl(hasName("X")));
+ // Match the void(int) ctor.
+ auto CtorPattern =
+ cxxConstructorDecl(hasParameter(0, varDecl(hasType(asString("int")))),
+ hasParent(classTemplateSpecializationDecl()));
+ auto *FromCtor =
+ FirstDeclMatcher<CXXConstructorDecl>().match(FromTU, CtorPattern);
+ auto *ToCtor =
+ FirstDeclMatcher<CXXConstructorDecl>().match(ToTU, CtorPattern);
+ ASSERT_TRUE(FromCtor->hasBody());
+ ASSERT_FALSE(ToCtor->hasBody());
+ auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
+ ASSERT_TRUE(ImportedSpec);
+ auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ ToTU, classTemplateSpecializationDecl(hasName("X")));
+ EXPECT_EQ(ImportedSpec, ToSpec);
+ EXPECT_TRUE(ToCtor->hasBody());
+}
+
+TEST_P(ASTImporterTestBase,
+ ClassTemplatePartialSpecializationsShouldNotBeDuplicated) {
+ auto Code =
+ R"(
+ // primary template
+ template<class T1, class T2, int I>
+ class A {};
+
+ // partial specialization
+ template<class T, int I>
+ class A<T, T*, I> {};
+ )";
+ Decl *ToTU = getToTuDecl(Code, Lang_CXX11);
+ Decl *FromTU = getTuDecl(Code, Lang_CXX11);
+ auto *FromSpec =
+ FirstDeclMatcher<ClassTemplatePartialSpecializationDecl>().match(
+ FromTU, classTemplatePartialSpecializationDecl());
+ auto *ToSpec =
+ FirstDeclMatcher<ClassTemplatePartialSpecializationDecl>().match(
+ ToTU, classTemplatePartialSpecializationDecl());
+
+ auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
+ EXPECT_EQ(ImportedSpec, ToSpec);
+ EXPECT_EQ(1u, DeclCounter<ClassTemplatePartialSpecializationDecl>().match(
+ ToTU, classTemplatePartialSpecializationDecl()));
+}
+
+TEST_P(ASTImporterTestBase, ClassTemplateSpecializationsShouldNotBeDuplicated) {
+ auto Code =
+ R"(
+ // primary template
+ template<class T1, class T2, int I>
+ class A {};
+
+ // full specialization
+ template<>
+ class A<int, int, 1> {};
+ )";
+ Decl *ToTU = getToTuDecl(Code, Lang_CXX11);
+ Decl *FromTU = getTuDecl(Code, Lang_CXX11);
+ auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ FromTU, classTemplateSpecializationDecl());
+ auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ ToTU, classTemplateSpecializationDecl());
+
+ auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
+ EXPECT_EQ(ImportedSpec, ToSpec);
+ EXPECT_EQ(1u, DeclCounter<ClassTemplateSpecializationDecl>().match(
+ ToTU, classTemplateSpecializationDecl()));
+}
+
+TEST_P(ASTImporterTestBase, ClassTemplateFullAndPartialSpecsShouldNotBeMixed) {
+ std::string PrimaryTemplate =
+ R"(
+ template<class T1, class T2, int I>
+ class A {};
+ )";
+ auto PartialSpec =
+ R"(
+ template<class T, int I>
+ class A<T, T*, I> {};
+ )";
+ auto FullSpec =
+ R"(
+ template<>
+ class A<int, int, 1> {};
+ )";
+ Decl *ToTU = getToTuDecl(PrimaryTemplate + FullSpec, Lang_CXX11);
+ Decl *FromTU = getTuDecl(PrimaryTemplate + PartialSpec, Lang_CXX11);
+ auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ FromTU, classTemplateSpecializationDecl());
+
+ auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
+ EXPECT_TRUE(ImportedSpec);
+ // Check the number of partial specializations.
+ EXPECT_EQ(1u, DeclCounter<ClassTemplatePartialSpecializationDecl>().match(
+ ToTU, classTemplatePartialSpecializationDecl()));
+ // Check the number of full specializations.
+ EXPECT_EQ(1u, DeclCounter<ClassTemplateSpecializationDecl>().match(
+ ToTU, classTemplateSpecializationDecl(
+ unless(classTemplatePartialSpecializationDecl()))));
+}
+
+TEST_P(ASTImporterTestBase, InitListExprValueKindShouldBeImported) {
+ Decl *TU = getTuDecl(
+ R"(
+ const int &init();
+ void foo() { const int &a{init()}; }
+ )", Lang_CXX11, "input0.cc");
+ auto *FromD = FirstDeclMatcher<VarDecl>().match(TU, varDecl(hasName("a")));
+ ASSERT_TRUE(FromD->getAnyInitializer());
+ auto *InitExpr = FromD->getAnyInitializer();
+ ASSERT_TRUE(InitExpr);
+ ASSERT_TRUE(InitExpr->isGLValue());
+
+ auto *ToD = Import(FromD, Lang_CXX11);
+ EXPECT_TRUE(ToD);
+ auto *ToInitExpr = cast<VarDecl>(ToD)->getAnyInitializer();
+ EXPECT_TRUE(ToInitExpr);
+ EXPECT_TRUE(ToInitExpr->isGLValue());
+}
+
+struct ImportVariables : ASTImporterTestBase {};
+
+TEST_P(ImportVariables, ImportOfOneDeclBringsInTheWholeChain) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ struct A {
+ static const int a = 1 + 2;
+ };
+ const int A::a;
+ )", Lang_CXX, "input1.cc");
+
+ auto *FromDWithInit = FirstDeclMatcher<VarDecl>().match(
+ FromTU, varDecl(hasName("a"))); // Decl with init
+ auto *FromDWithDef = LastDeclMatcher<VarDecl>().match(
+ FromTU, varDecl(hasName("a"))); // Decl with definition
+ ASSERT_NE(FromDWithInit, FromDWithDef);
+ ASSERT_EQ(FromDWithDef->getPreviousDecl(), FromDWithInit);
+
+ auto *ToD0 = cast<VarDecl>(Import(FromDWithInit, Lang_CXX11));
+ auto *ToD1 = cast<VarDecl>(Import(FromDWithDef, Lang_CXX11));
+ ASSERT_TRUE(ToD0);
+ ASSERT_TRUE(ToD1);
+ EXPECT_NE(ToD0, ToD1);
+ EXPECT_EQ(ToD1->getPreviousDecl(), ToD0);
+}
+
+TEST_P(ImportVariables, InitAndDefinitionAreInDifferentTUs) {
+ auto StructA =
+ R"(
+ struct A {
+ static const int a = 1 + 2;
+ };
+ )";
+ Decl *ToTU = getToTuDecl(StructA, Lang_CXX);
+ Decl *FromTU = getTuDecl(std::string(StructA) + "const int A::a;", Lang_CXX,
+ "input1.cc");
+
+ auto *FromDWithInit = FirstDeclMatcher<VarDecl>().match(
+ FromTU, varDecl(hasName("a"))); // Decl with init
+ auto *FromDWithDef = LastDeclMatcher<VarDecl>().match(
+ FromTU, varDecl(hasName("a"))); // Decl with definition
+ ASSERT_EQ(FromDWithInit, FromDWithDef->getPreviousDecl());
+ ASSERT_TRUE(FromDWithInit->getInit());
+ ASSERT_FALSE(FromDWithInit->isThisDeclarationADefinition());
+ ASSERT_TRUE(FromDWithDef->isThisDeclarationADefinition());
+ ASSERT_FALSE(FromDWithDef->getInit());
+
+ auto *ToD = FirstDeclMatcher<VarDecl>().match(
+ ToTU, varDecl(hasName("a"))); // Decl with init
+ ASSERT_TRUE(ToD->getInit());
+ ASSERT_FALSE(ToD->getDefinition());
+
+ auto *ImportedD = cast<VarDecl>(Import(FromDWithDef, Lang_CXX11));
+ EXPECT_TRUE(ImportedD->getAnyInitializer());
+ EXPECT_TRUE(ImportedD->getDefinition());
+}
+
+TEST_P(ImportVariables, InitAndDefinitionAreInTheFromContext) {
+ auto StructA =
+ R"(
+ struct A {
+ static const int a;
+ };
+ )";
+ Decl *ToTU = getToTuDecl(StructA, Lang_CXX);
+ Decl *FromTU = getTuDecl(std::string(StructA) + "const int A::a = 1 + 2;",
+ Lang_CXX, "input1.cc");
+
+ auto *FromDDeclarationOnly = FirstDeclMatcher<VarDecl>().match(
+ FromTU, varDecl(hasName("a")));
+ auto *FromDWithDef = LastDeclMatcher<VarDecl>().match(
+ FromTU, varDecl(hasName("a"))); // Decl with definition and with init.
+ ASSERT_EQ(FromDDeclarationOnly, FromDWithDef->getPreviousDecl());
+ ASSERT_FALSE(FromDDeclarationOnly->getInit());
+ ASSERT_FALSE(FromDDeclarationOnly->isThisDeclarationADefinition());
+ ASSERT_TRUE(FromDWithDef->isThisDeclarationADefinition());
+ ASSERT_TRUE(FromDWithDef->getInit());
+
+ auto *ToD = FirstDeclMatcher<VarDecl>().match(
+ ToTU, varDecl(hasName("a")));
+ ASSERT_FALSE(ToD->getInit());
+ ASSERT_FALSE(ToD->getDefinition());
+
+ auto *ImportedD = cast<VarDecl>(Import(FromDWithDef, Lang_CXX11));
+ EXPECT_TRUE(ImportedD->getAnyInitializer());
+ 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) {
@@ -2641,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,
@@ -2956,6 +4378,487 @@ TEST_P(ImportFunctionTemplateSpecializations, DefinitionThenPrototype) {
EXPECT_EQ(To1->getPreviousDecl(), To0);
}
+TEST_P(ASTImporterTestBase,
+ ImportShouldNotReportFalseODRErrorWhenRecordIsBeingDefined) {
+ {
+ Decl *FromTU = getTuDecl(
+ R"(
+ template <typename T>
+ struct B;
+ )",
+ Lang_CXX, "input0.cc");
+ auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU, classTemplateDecl(hasName("B")));
+
+ Import(FromD, Lang_CXX);
+ }
+
+ {
+ Decl *FromTU = getTuDecl(
+ R"(
+ template <typename T>
+ struct B {
+ void f();
+ B* b;
+ };
+ )",
+ Lang_CXX, "input1.cc");
+ FunctionDecl *FromD = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("f")));
+ Import(FromD, Lang_CXX);
+ auto *FromCTD = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU, classTemplateDecl(hasName("B")));
+ auto *ToCTD = cast<ClassTemplateDecl>(Import(FromCTD, Lang_CXX));
+ EXPECT_TRUE(ToCTD->isThisDeclarationADefinition());
+
+ // We expect no (ODR) warning during the import.
+ auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ EXPECT_EQ(0u, ToTU->getASTContext().getDiagnostics().getNumWarnings());
+ }
+}
+
+TEST_P(ASTImporterTestBase, ImportingTypedefShouldImportTheCompleteType) {
+ // We already have an incomplete underlying type in the "To" context.
+ auto Code =
+ R"(
+ template <typename T>
+ struct S {
+ void foo();
+ };
+ using U = S<int>;
+ )";
+ Decl *ToTU = getToTuDecl(Code, Lang_CXX11);
+ auto *ToD = FirstDeclMatcher<TypedefNameDecl>().match(ToTU,
+ typedefNameDecl(hasName("U")));
+ ASSERT_TRUE(ToD->getUnderlyingType()->isIncompleteType());
+
+ // The "From" context has the same typedef, but the underlying type is
+ // complete this time.
+ Decl *FromTU = getTuDecl(std::string(Code) +
+ R"(
+ void foo(U* u) {
+ u->foo();
+ }
+ )", Lang_CXX11);
+ auto *FromD = FirstDeclMatcher<TypedefNameDecl>().match(FromTU,
+ typedefNameDecl(hasName("U")));
+ ASSERT_FALSE(FromD->getUnderlyingType()->isIncompleteType());
+
+ // The imported type should be complete.
+ auto *ImportedD = cast<TypedefNameDecl>(Import(FromD, Lang_CXX11));
+ 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()), );
@@ -2969,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, );
@@ -2984,12 +4890,27 @@ 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, );
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportImplicitMethods,
+ DefaultTestValuesForRunOptions, );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportVariables,
+ DefaultTestValuesForRunOptions, );
+
} // end namespace ast_matchers
} // end namespace clang