summaryrefslogtreecommitdiffstats
path: root/unittests
diff options
context:
space:
mode:
Diffstat (limited to 'unittests')
-rw-r--r--unittests/AST/ASTContextParentMapTest.cpp11
-rw-r--r--unittests/AST/ASTImporterTest.cpp1301
-rw-r--r--unittests/AST/CMakeLists.txt4
-rw-r--r--unittests/AST/StructuralEquivalenceTest.cpp71
-rw-r--r--unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp12
-rw-r--r--unittests/Analysis/ExprMutationAnalyzerTest.cpp5
-rw-r--r--unittests/Basic/FileManagerTest.cpp29
-rw-r--r--unittests/Driver/DistroTest.cpp36
-rw-r--r--unittests/Format/FormatTestJS.cpp2
-rw-r--r--unittests/Lex/PPCallbacksTest.cpp77
-rw-r--r--unittests/Sema/CodeCompleteTest.cpp27
-rw-r--r--unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp56
-rw-r--r--unittests/Tooling/CompilationDatabaseTest.cpp52
-rw-r--r--unittests/Tooling/RecursiveASTVisitorTests/LambdaExpr.cpp62
-rw-r--r--unittests/libclang/LibclangTest.cpp42
15 files changed, 1583 insertions, 204 deletions
diff --git a/unittests/AST/ASTContextParentMapTest.cpp b/unittests/AST/ASTContextParentMapTest.cpp
index f06f32bf76..fb9d517069 100644
--- a/unittests/AST/ASTContextParentMapTest.cpp
+++ b/unittests/AST/ASTContextParentMapTest.cpp
@@ -106,5 +106,16 @@ TEST(GetParents, RespectsTraversalScope) {
EXPECT_THAT(Ctx.getParents(Foo), ElementsAre(DynTypedNode::create(TU)));
}
+TEST(GetParents, ImplicitLambdaNodes) {
+ MatchVerifier<Decl> LambdaVerifier;
+ EXPECT_TRUE(LambdaVerifier.match(
+ "auto x = []{int y;};",
+ varDecl(hasName("y"), hasAncestor(functionDecl(
+ hasOverloadedOperatorName("()"),
+ hasParent(cxxRecordDecl(
+ isImplicit(), hasParent(lambdaExpr())))))),
+ Lang_CXX11));
+}
+
} // end namespace ast_matchers
} // end namespace clang
diff --git a/unittests/AST/ASTImporterTest.cpp b/unittests/AST/ASTImporterTest.cpp
index 0450cb41a4..c6acf573e5 100644
--- a/unittests/AST/ASTImporterTest.cpp
+++ b/unittests/AST/ASTImporterTest.cpp
@@ -11,9 +11,12 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/AST/ASTImporter.h"
#include "MatchVerifier.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclContextInternals.h"
#include "clang/AST/ASTImporter.h"
+#include "clang/AST/ASTImporterLookupTable.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Tooling/Tooling.h"
@@ -307,24 +310,27 @@ class ASTImporterTestBase : public ParameterizedTestsFixture {
Unit->enableSourceFileDiagnostics();
}
- void lazyInitImporter(ASTUnit *ToAST) {
+ void lazyInitImporter(ASTImporterLookupTable &LookupTable, ASTUnit *ToAST) {
assert(ToAST);
if (!Importer) {
- Importer.reset(new ASTImporter(
- ToAST->getASTContext(), ToAST->getFileManager(),
- Unit->getASTContext(), Unit->getFileManager(), false));
+ Importer.reset(
+ new ASTImporter(ToAST->getASTContext(), ToAST->getFileManager(),
+ Unit->getASTContext(), Unit->getFileManager(),
+ false, &LookupTable));
}
assert(&ToAST->getASTContext() == &Importer->getToContext());
createVirtualFileIfNeeded(ToAST, FileName, Code);
}
- Decl *import(ASTUnit *ToAST, Decl *FromDecl) {
- lazyInitImporter(ToAST);
+ Decl *import(ASTImporterLookupTable &LookupTable, ASTUnit *ToAST,
+ Decl *FromDecl) {
+ lazyInitImporter(LookupTable, ToAST);
return Importer->Import(FromDecl);
- }
+ }
- QualType import(ASTUnit *ToAST, QualType FromType) {
- lazyInitImporter(ToAST);
+ QualType import(ASTImporterLookupTable &LookupTable, ASTUnit *ToAST,
+ QualType FromType) {
+ lazyInitImporter(LookupTable, ToAST);
return Importer->Import(FromType);
}
};
@@ -338,13 +344,23 @@ class ASTImporterTestBase : public ParameterizedTestsFixture {
// vector is expanding, with the list we won't have these issues.
std::list<TU> FromTUs;
- void lazyInitToAST(Language ToLang) {
+ // Initialize the lookup table if not initialized already.
+ void lazyInitLookupTable(TranslationUnitDecl *ToTU) {
+ assert(ToTU);
+ if (!LookupTablePtr)
+ LookupTablePtr = llvm::make_unique<ASTImporterLookupTable>(*ToTU);
+ }
+
+ void lazyInitToAST(Language ToLang, StringRef ToSrcCode, StringRef FileName) {
if (ToAST)
return;
ArgVector ToArgs = getArgVectorForLanguage(ToLang);
+ // Source code must be a valid live buffer through the tests lifetime.
+ ToCode = ToSrcCode;
// Build the AST from an empty file.
- ToAST = tooling::buildASTFromCodeWithArgs(/*Code=*/"", ToArgs, "empty.cc");
+ ToAST = tooling::buildASTFromCodeWithArgs(ToCode, ToArgs, FileName);
ToAST->enableSourceFileDiagnostics();
+ lazyInitLookupTable(ToAST->getASTContext().getTranslationUnitDecl());
}
TU *findFromTU(Decl *From) {
@@ -358,6 +374,10 @@ class ASTImporterTestBase : public ParameterizedTestsFixture {
return &*It;
}
+protected:
+
+ std::unique_ptr<ASTImporterLookupTable> LookupTablePtr;
+
public:
// We may have several From context but only one To context.
std::unique_ptr<ASTUnit> ToAST;
@@ -374,26 +394,23 @@ public:
FromTUs.emplace_back(FromSrcCode, InputFileName, FromArgs);
TU &FromTU = FromTUs.back();
- ToCode = ToSrcCode;
assert(!ToAST);
- ToAST = tooling::buildASTFromCodeWithArgs(ToCode, ToArgs, OutputFileName);
- ToAST->enableSourceFileDiagnostics();
+ lazyInitToAST(ToLang, ToSrcCode, OutputFileName);
ASTContext &FromCtx = FromTU.Unit->getASTContext();
- createVirtualFileIfNeeded(ToAST.get(), InputFileName, FromTU.Code);
-
IdentifierInfo *ImportedII = &FromCtx.Idents.get(Identifier);
assert(ImportedII && "Declaration with the given identifier "
"should be specified in test!");
DeclarationName ImportDeclName(ImportedII);
- SmallVector<NamedDecl *, 4> FoundDecls;
+ SmallVector<NamedDecl *, 1> FoundDecls;
FromCtx.getTranslationUnitDecl()->localUncachedLookup(ImportDeclName,
FoundDecls);
assert(FoundDecls.size() == 1);
- Decl *Imported = FromTU.import(ToAST.get(), FoundDecls.front());
+ Decl *Imported =
+ FromTU.import(*LookupTablePtr, ToAST.get(), FoundDecls.front());
assert(Imported);
return std::make_tuple(*FoundDecls.begin(), Imported);
@@ -419,11 +436,8 @@ public:
// Creates the To context with the given source code and returns the TU decl.
TranslationUnitDecl *getToTuDecl(StringRef ToSrcCode, Language ToLang) {
ArgVector ToArgs = getArgVectorForLanguage(ToLang);
- ToCode = ToSrcCode;
assert(!ToAST);
- ToAST = tooling::buildASTFromCodeWithArgs(ToCode, ToArgs, OutputFileName);
- ToAST->enableSourceFileDiagnostics();
-
+ lazyInitToAST(ToLang, ToSrcCode, OutputFileName);
return ToAST->getASTContext().getTranslationUnitDecl();
}
@@ -431,15 +445,17 @@ public:
// May be called several times in a given test.
// The different instances of the param From may have different ASTContext.
Decl *Import(Decl *From, Language ToLang) {
- lazyInitToAST(ToLang);
+ lazyInitToAST(ToLang, "", OutputFileName);
TU *FromTU = findFromTU(From);
- return FromTU->import(ToAST.get(), From);
+ assert(LookupTablePtr);
+ return FromTU->import(*LookupTablePtr, ToAST.get(), From);
}
QualType ImportType(QualType FromType, Decl *TUDecl, Language ToLang) {
- lazyInitToAST(ToLang);
+ lazyInitToAST(ToLang, "", OutputFileName);
TU *FromTU = findFromTU(TUDecl);
- return FromTU->import(ToAST.get(), FromType);
+ assert(LookupTablePtr);
+ return FromTU->import(*LookupTablePtr, ToAST.get(), FromType);
}
~ASTImporterTestBase() {
@@ -1808,6 +1824,65 @@ TEST_P(ASTImporterTestBase, ObjectsWithUnnamedStructType) {
EXPECT_NE(To0->getCanonicalDecl(), To1->getCanonicalDecl());
}
+TEST_P(ASTImporterTestBase, AnonymousRecords) {
+ auto *Code =
+ R"(
+ struct X {
+ struct { int a; };
+ struct { int b; };
+ };
+ )";
+ Decl *FromTU0 = getTuDecl(Code, Lang_C, "input0.c");
+
+ Decl *FromTU1 = getTuDecl(Code, Lang_C, "input1.c");
+
+ auto *X0 =
+ FirstDeclMatcher<RecordDecl>().match(FromTU0, recordDecl(hasName("X")));
+ auto *X1 =
+ FirstDeclMatcher<RecordDecl>().match(FromTU1, recordDecl(hasName("X")));
+ Import(X0, Lang_C);
+ Import(X1, Lang_C);
+
+ auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ // We expect no (ODR) warning during the import.
+ EXPECT_EQ(0u, ToTU->getASTContext().getDiagnostics().getNumWarnings());
+ EXPECT_EQ(1u,
+ DeclCounter<RecordDecl>().match(ToTU, recordDecl(hasName("X"))));
+}
+
+TEST_P(ASTImporterTestBase, AnonymousRecordsReversed) {
+ Decl *FromTU0 = getTuDecl(
+ R"(
+ struct X {
+ struct { int a; };
+ struct { int b; };
+ };
+ )",
+ Lang_C, "input0.c");
+
+ Decl *FromTU1 = getTuDecl(
+ R"(
+ struct X { // reversed order
+ struct { int b; };
+ struct { int a; };
+ };
+ )",
+ Lang_C, "input1.c");
+
+ auto *X0 =
+ FirstDeclMatcher<RecordDecl>().match(FromTU0, recordDecl(hasName("X")));
+ auto *X1 =
+ FirstDeclMatcher<RecordDecl>().match(FromTU1, recordDecl(hasName("X")));
+ Import(X0, Lang_C);
+ Import(X1, Lang_C);
+
+ auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ // We expect one (ODR) warning during the import.
+ EXPECT_EQ(1u, ToTU->getASTContext().getDiagnostics().getNumWarnings());
+ EXPECT_EQ(2u,
+ DeclCounter<RecordDecl>().match(ToTU, recordDecl(hasName("X"))));
+}
+
TEST_P(ASTImporterTestBase, ImportDoesUpdateUsedFlag) {
auto Pattern = varDecl(hasName("x"));
VarDecl *Imported1;
@@ -2667,6 +2742,7 @@ private:
CXXMethodDecl *Method =
FirstDeclMatcher<CXXMethodDecl>().match(ToClass, MethodMatcher);
ToClass->removeDecl(Method);
+ LookupTablePtr->remove(Method);
}
ASSERT_EQ(DeclCounter<CXXMethodDecl>().match(ToClass, MethodMatcher), 0u);
@@ -2930,93 +3006,6 @@ TEST_P(ASTImporterTestBase, ImportUnnamedFieldsInCorrectOrder) {
EXPECT_EQ(FromIndex, 3u);
}
-TEST_P(
- ASTImporterTestBase,
- ImportOfFriendRecordDoesNotMergeDefinition) {
- Decl *FromTU = getTuDecl(
- R"(
- class A {
- template <int I> class F {};
- class X {
- template <int I> friend class F;
- };
- };
- )",
- Lang_CXX, "input0.cc");
-
- auto *FromClass = FirstDeclMatcher<CXXRecordDecl>().match(
- FromTU, cxxRecordDecl(hasName("F"), isDefinition()));
- auto *FromFriendClass = LastDeclMatcher<CXXRecordDecl>().match(
- FromTU, cxxRecordDecl(hasName("F")));
-
- ASSERT_TRUE(FromClass);
- ASSERT_TRUE(FromFriendClass);
- ASSERT_NE(FromClass, FromFriendClass);
- ASSERT_EQ(FromFriendClass->getDefinition(), FromClass);
- ASSERT_EQ(FromFriendClass->getPreviousDecl(), FromClass);
- ASSERT_EQ(
- FromFriendClass->getDescribedClassTemplate()->getPreviousDecl(),
- FromClass->getDescribedClassTemplate());
-
- auto *ToClass = cast<CXXRecordDecl>(Import(FromClass, Lang_CXX));
- auto *ToFriendClass = cast<CXXRecordDecl>(Import(FromFriendClass, Lang_CXX));
-
- EXPECT_TRUE(ToClass);
- EXPECT_TRUE(ToFriendClass);
- EXPECT_NE(ToClass, ToFriendClass);
- EXPECT_EQ(ToFriendClass->getDefinition(), ToClass);
- EXPECT_EQ(ToFriendClass->getPreviousDecl(), ToClass);
- EXPECT_EQ(
- ToFriendClass->getDescribedClassTemplate()->getPreviousDecl(),
- ToClass->getDescribedClassTemplate());
-}
-
-TEST_P(
- ASTImporterTestBase,
- ImportOfRecursiveFriendClass) {
- Decl *FromTu = getTuDecl(
- R"(
- class declToImport {
- friend class declToImport;
- };
- )",
- Lang_CXX, "input.cc");
-
- auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(
- FromTu, cxxRecordDecl(hasName("declToImport")));
- auto *ToD = Import(FromD, Lang_CXX);
- auto Pattern = cxxRecordDecl(hasName("declToImport"), has(friendDecl()));
- ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern));
- EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern));
-}
-
-TEST_P(
- ASTImporterTestBase,
- ImportOfRecursiveFriendClassTemplate) {
- Decl *FromTu = getTuDecl(
- R"(
- template <class A> class declToImport {
- template <class A1> friend class declToImport;
- };
- )",
- Lang_CXX, "input.cc");
-
- auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match(
- FromTu, classTemplateDecl(hasName("declToImport")));
- auto *ToD = Import(FromD, Lang_CXX);
-
- auto Pattern = classTemplateDecl(
- has(cxxRecordDecl(has(friendDecl(has(classTemplateDecl()))))));
- ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern));
- EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern));
-
- auto *Class =
- FirstDeclMatcher<ClassTemplateDecl>().match(ToD, classTemplateDecl());
- auto *Friend = FirstDeclMatcher<FriendDecl>().match(ToD, friendDecl());
- EXPECT_NE(Friend->getFriendDecl(), Class);
- EXPECT_EQ(Friend->getFriendDecl()->getPreviousDecl(), Class);
-}
-
TEST_P(ASTImporterTestBase, MergeFieldDeclsOfClassTemplateSpecialization) {
std::string ClassTemplate =
R"(
@@ -3381,6 +3370,636 @@ TEST_P(ImportVariables, InitAndDefinitionAreInTheFromContext) {
EXPECT_TRUE(ImportedD->getDefinition());
}
+struct ImportClasses : ASTImporterTestBase {};
+
+TEST_P(ImportClasses,
+ PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition) {
+ Decl *FromTU = getTuDecl("class X;", Lang_CXX);
+ auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
+ auto FromD = FirstDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
+
+ Decl *ImportedD = Import(FromD, Lang_CXX);
+ Decl *ToTU = ImportedD->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 1u);
+ auto ToD = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedD == ToD);
+ EXPECT_FALSE(ToD->isThisDeclarationADefinition());
+}
+
+TEST_P(ImportClasses, ImportPrototypeAfterImportedPrototype) {
+ Decl *FromTU = getTuDecl("class X; class X;", Lang_CXX);
+ auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
+ auto From0 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
+ auto From1 = LastDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
+
+ Decl *Imported0 = Import(From0, Lang_CXX);
+ Decl *Imported1 = Import(From1, Lang_CXX);
+ Decl *ToTU = Imported0->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 2u);
+ auto To0 = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+ auto To1 = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(Imported0 == To0);
+ EXPECT_TRUE(Imported1 == To1);
+ EXPECT_FALSE(To0->isThisDeclarationADefinition());
+ EXPECT_FALSE(To1->isThisDeclarationADefinition());
+ EXPECT_EQ(To1->getPreviousDecl(), To0);
+}
+
+TEST_P(ImportClasses, DefinitionShouldBeImportedAsADefinition) {
+ Decl *FromTU = getTuDecl("class X {};", Lang_CXX);
+ auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
+ auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
+
+ Decl *ImportedD = Import(FromD, Lang_CXX);
+ Decl *ToTU = ImportedD->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 1u);
+ EXPECT_TRUE(cast<CXXRecordDecl>(ImportedD)->isThisDeclarationADefinition());
+}
+
+TEST_P(ImportClasses, ImportPrototypeFromDifferentTUAfterImportedPrototype) {
+ Decl *FromTU0 = getTuDecl("class X;", Lang_CXX, "input0.cc");
+ Decl *FromTU1 = getTuDecl("class X;", Lang_CXX, "input1.cc");
+ auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
+ auto From0 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU0, Pattern);
+ auto From1 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU1, Pattern);
+
+ Decl *Imported0 = Import(From0, Lang_CXX);
+ Decl *Imported1 = Import(From1, Lang_CXX);
+ Decl *ToTU = Imported0->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 2u);
+ auto To0 = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+ auto To1 = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(Imported0 == To0);
+ EXPECT_TRUE(Imported1 == To1);
+ EXPECT_FALSE(To0->isThisDeclarationADefinition());
+ EXPECT_FALSE(To1->isThisDeclarationADefinition());
+ EXPECT_EQ(To1->getPreviousDecl(), To0);
+}
+
+TEST_P(ImportClasses, ImportDefinitions) {
+ Decl *FromTU0 = getTuDecl("class X {};", Lang_CXX, "input0.cc");
+ Decl *FromTU1 = getTuDecl("class X {};", Lang_CXX, "input1.cc");
+ auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
+ auto From0 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU0, Pattern);
+ auto From1 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU1, Pattern);
+
+ Decl *Imported0 = Import(From0, Lang_CXX);
+ Decl *Imported1 = Import(From1, Lang_CXX);
+ Decl *ToTU = Imported0->getTranslationUnitDecl();
+
+ EXPECT_EQ(Imported0, Imported1);
+ EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 1u);
+ auto To0 = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(Imported0 == To0);
+ EXPECT_TRUE(To0->isThisDeclarationADefinition());
+}
+
+TEST_P(ImportClasses, ImportDefinitionThenPrototype) {
+ Decl *FromTU0 = getTuDecl("class X {};", Lang_CXX, "input0.cc");
+ Decl *FromTU1 = getTuDecl("class X;", Lang_CXX, "input1.cc");
+ auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
+ auto FromDef = FirstDeclMatcher<CXXRecordDecl>().match(FromTU0, Pattern);
+ auto FromProto = FirstDeclMatcher<CXXRecordDecl>().match(FromTU1, Pattern);
+
+ Decl *ImportedDef = Import(FromDef, Lang_CXX);
+ Decl *ImportedProto = Import(FromProto, Lang_CXX);
+ Decl *ToTU = ImportedDef->getTranslationUnitDecl();
+
+ EXPECT_NE(ImportedDef, ImportedProto);
+ EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 2u);
+ auto ToDef = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+ auto ToProto = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedDef == ToDef);
+ EXPECT_TRUE(ImportedProto == ToProto);
+ EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+ EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+ EXPECT_EQ(ToProto->getPreviousDecl(), ToDef);
+}
+
+TEST_P(ImportClasses, ImportPrototypeThenDefinition) {
+ Decl *FromTU0 = getTuDecl("class X;", Lang_CXX, "input0.cc");
+ Decl *FromTU1 = getTuDecl("class X {};", Lang_CXX, "input1.cc");
+ auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
+ auto FromProto = FirstDeclMatcher<CXXRecordDecl>().match(FromTU0, Pattern);
+ auto FromDef = FirstDeclMatcher<CXXRecordDecl>().match(FromTU1, Pattern);
+
+ Decl *ImportedProto = Import(FromProto, Lang_CXX);
+ Decl *ImportedDef = Import(FromDef, Lang_CXX);
+ Decl *ToTU = ImportedDef->getTranslationUnitDecl();
+
+ EXPECT_NE(ImportedDef, ImportedProto);
+ EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 2u);
+ auto ToProto = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+ auto ToDef = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedDef == ToDef);
+ EXPECT_TRUE(ImportedProto == ToProto);
+ EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+ EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+ EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
+}
+
+TEST_P(ImportClasses, ImportDefinitionWhenProtoIsInToContext) {
+ Decl *ToTU = getToTuDecl("struct X;", Lang_C);
+ Decl *FromTU1 = getTuDecl("struct X {};", Lang_C, "input1.cc");
+ auto Pattern = recordDecl(hasName("X"), unless(isImplicit()));
+ auto ToProto = FirstDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+ auto FromDef = FirstDeclMatcher<RecordDecl>().match(FromTU1, Pattern);
+
+ Decl *ImportedDef = Import(FromDef, Lang_C);
+
+ EXPECT_NE(ImportedDef, ToProto);
+ EXPECT_EQ(DeclCounter<RecordDecl>().match(ToTU, Pattern), 2u);
+ auto ToDef = LastDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedDef == ToDef);
+ EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+ EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+ EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
+}
+
+TEST_P(ImportClasses, ImportDefinitionWhenProtoIsInNestedToContext) {
+ Decl *ToTU = getToTuDecl("struct A { struct X *Xp; };", Lang_C);
+ Decl *FromTU1 = getTuDecl("struct X {};", Lang_C, "input1.cc");
+ auto Pattern = recordDecl(hasName("X"), unless(isImplicit()));
+ auto ToProto = FirstDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+ auto FromDef = FirstDeclMatcher<RecordDecl>().match(FromTU1, Pattern);
+
+ Decl *ImportedDef = Import(FromDef, Lang_C);
+
+ EXPECT_NE(ImportedDef, ToProto);
+ EXPECT_EQ(DeclCounter<RecordDecl>().match(ToTU, Pattern), 2u);
+ auto ToDef = LastDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedDef == ToDef);
+ EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+ EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+ EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
+}
+
+TEST_P(ImportClasses, ImportDefinitionWhenProtoIsInNestedToContextCXX) {
+ Decl *ToTU = getToTuDecl("struct A { struct X *Xp; };", Lang_CXX);
+ Decl *FromTU1 = getTuDecl("struct X {};", Lang_CXX, "input1.cc");
+ auto Pattern = recordDecl(hasName("X"), unless(isImplicit()));
+ auto ToProto = FirstDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+ auto FromDef = FirstDeclMatcher<RecordDecl>().match(FromTU1, Pattern);
+
+ Decl *ImportedDef = Import(FromDef, Lang_CXX);
+
+ EXPECT_NE(ImportedDef, ToProto);
+ EXPECT_EQ(DeclCounter<RecordDecl>().match(ToTU, Pattern), 2u);
+ auto ToDef = LastDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedDef == ToDef);
+ EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+ EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+ EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
+}
+
+TEST_P(ImportClasses, ImportNestedPrototypeThenDefinition) {
+ Decl *FromTU0 = getTuDecl("struct A { struct X *Xp; };", Lang_C, "input0.cc");
+ Decl *FromTU1 = getTuDecl("struct X {};", Lang_C, "input1.cc");
+ auto Pattern = recordDecl(hasName("X"), unless(isImplicit()));
+ auto FromProto = FirstDeclMatcher<RecordDecl>().match(FromTU0, Pattern);
+ auto FromDef = FirstDeclMatcher<RecordDecl>().match(FromTU1, Pattern);
+
+ Decl *ImportedProto = Import(FromProto, Lang_C);
+ Decl *ImportedDef = Import(FromDef, Lang_C);
+ Decl *ToTU = ImportedDef->getTranslationUnitDecl();
+
+ EXPECT_NE(ImportedDef, ImportedProto);
+ EXPECT_EQ(DeclCounter<RecordDecl>().match(ToTU, Pattern), 2u);
+ auto ToProto = FirstDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+ auto ToDef = LastDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedDef == ToDef);
+ EXPECT_TRUE(ImportedProto == ToProto);
+ EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+ EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+ EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
+}
+
+struct ImportClassTemplates : ASTImporterTestBase {};
+
+TEST_P(ImportClassTemplates,
+ PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition) {
+ Decl *FromTU = getTuDecl("template <class T> class X;", Lang_CXX);
+ auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+ auto FromD = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, Pattern);
+
+ Decl *ImportedD = Import(FromD, Lang_CXX);
+ Decl *ToTU = ImportedD->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 1u);
+ auto ToD = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedD == ToD);
+ ASSERT_TRUE(ToD->getTemplatedDecl());
+ EXPECT_FALSE(ToD->isThisDeclarationADefinition());
+}
+
+TEST_P(ImportClassTemplates, ImportPrototypeAfterImportedPrototype) {
+ Decl *FromTU = getTuDecl(
+ "template <class T> class X; template <class T> class X;", Lang_CXX);
+ auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+ auto From0 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, Pattern);
+ auto From1 = LastDeclMatcher<ClassTemplateDecl>().match(FromTU, Pattern);
+
+ Decl *Imported0 = Import(From0, Lang_CXX);
+ Decl *Imported1 = Import(From1, Lang_CXX);
+ Decl *ToTU = Imported0->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 2u);
+ auto To0 = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+ auto To1 = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(Imported0 == To0);
+ EXPECT_TRUE(Imported1 == To1);
+ ASSERT_TRUE(To0->getTemplatedDecl());
+ ASSERT_TRUE(To1->getTemplatedDecl());
+ EXPECT_FALSE(To0->isThisDeclarationADefinition());
+ EXPECT_FALSE(To1->isThisDeclarationADefinition());
+ EXPECT_EQ(To1->getPreviousDecl(), To0);
+ EXPECT_EQ(To1->getTemplatedDecl()->getPreviousDecl(),
+ To0->getTemplatedDecl());
+}
+
+TEST_P(ImportClassTemplates, DefinitionShouldBeImportedAsADefinition) {
+ Decl *FromTU = getTuDecl("template <class T> class X {};", Lang_CXX);
+ auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+ auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, Pattern);
+
+ Decl *ImportedD = Import(FromD, Lang_CXX);
+ Decl *ToTU = ImportedD->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 1u);
+ auto ToD = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+ ASSERT_TRUE(ToD->getTemplatedDecl());
+ EXPECT_TRUE(ToD->isThisDeclarationADefinition());
+}
+
+TEST_P(ImportClassTemplates,
+ ImportPrototypeFromDifferentTUAfterImportedPrototype) {
+ Decl *FromTU0 =
+ getTuDecl("template <class T> class X;", Lang_CXX, "input0.cc");
+ Decl *FromTU1 =
+ getTuDecl("template <class T> class X;", Lang_CXX, "input1.cc");
+ auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+ auto From0 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU0, Pattern);
+ auto From1 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU1, Pattern);
+
+ Decl *Imported0 = Import(From0, Lang_CXX);
+ Decl *Imported1 = Import(From1, Lang_CXX);
+ Decl *ToTU = Imported0->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 2u);
+ auto To0 = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+ auto To1 = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(Imported0 == To0);
+ EXPECT_TRUE(Imported1 == To1);
+ ASSERT_TRUE(To0->getTemplatedDecl());
+ ASSERT_TRUE(To1->getTemplatedDecl());
+ EXPECT_FALSE(To0->isThisDeclarationADefinition());
+ EXPECT_FALSE(To1->isThisDeclarationADefinition());
+ EXPECT_EQ(To1->getPreviousDecl(), To0);
+ EXPECT_EQ(To1->getTemplatedDecl()->getPreviousDecl(),
+ To0->getTemplatedDecl());
+}
+
+TEST_P(ImportClassTemplates, ImportDefinitions) {
+ Decl *FromTU0 =
+ getTuDecl("template <class T> class X {};", Lang_CXX, "input0.cc");
+ Decl *FromTU1 =
+ getTuDecl("template <class T> class X {};", Lang_CXX, "input1.cc");
+ auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+ auto From0 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU0, Pattern);
+ auto From1 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU1, Pattern);
+
+ Decl *Imported0 = Import(From0, Lang_CXX);
+ Decl *Imported1 = Import(From1, Lang_CXX);
+ Decl *ToTU = Imported0->getTranslationUnitDecl();
+
+ EXPECT_EQ(Imported0, Imported1);
+ EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 1u);
+ auto To0 = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(Imported0 == To0);
+ ASSERT_TRUE(To0->getTemplatedDecl());
+ EXPECT_TRUE(To0->isThisDeclarationADefinition());
+}
+
+TEST_P(ImportClassTemplates, ImportDefinitionThenPrototype) {
+ Decl *FromTU0 =
+ getTuDecl("template <class T> class X {};", Lang_CXX, "input0.cc");
+ Decl *FromTU1 =
+ getTuDecl("template <class T> class X;", Lang_CXX, "input1.cc");
+ auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+ auto FromDef = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU0, Pattern);
+ auto FromProto =
+ FirstDeclMatcher<ClassTemplateDecl>().match(FromTU1, Pattern);
+
+ Decl *ImportedDef = Import(FromDef, Lang_CXX);
+ Decl *ImportedProto = Import(FromProto, Lang_CXX);
+ Decl *ToTU = ImportedDef->getTranslationUnitDecl();
+
+ EXPECT_NE(ImportedDef, ImportedProto);
+ EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 2u);
+ auto ToDef = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+ auto ToProto = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedDef == ToDef);
+ EXPECT_TRUE(ImportedProto == ToProto);
+ ASSERT_TRUE(ToDef->getTemplatedDecl());
+ ASSERT_TRUE(ToProto->getTemplatedDecl());
+ EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+ EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+ EXPECT_EQ(ToProto->getPreviousDecl(), ToDef);
+ EXPECT_EQ(ToProto->getTemplatedDecl()->getPreviousDecl(),
+ ToDef->getTemplatedDecl());
+}
+
+TEST_P(ImportClassTemplates, ImportPrototypeThenDefinition) {
+ Decl *FromTU0 =
+ getTuDecl("template <class T> class X;", Lang_CXX, "input0.cc");
+ Decl *FromTU1 =
+ getTuDecl("template <class T> class X {};", Lang_CXX, "input1.cc");
+ auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+ auto FromProto =
+ FirstDeclMatcher<ClassTemplateDecl>().match(FromTU0, Pattern);
+ auto FromDef = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU1, Pattern);
+
+ Decl *ImportedProto = Import(FromProto, Lang_CXX);
+ Decl *ImportedDef = Import(FromDef, Lang_CXX);
+ Decl *ToTU = ImportedDef->getTranslationUnitDecl();
+
+ EXPECT_NE(ImportedDef, ImportedProto);
+ EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 2u);
+ auto ToProto = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+ auto ToDef = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedDef == ToDef);
+ EXPECT_TRUE(ImportedProto == ToProto);
+ ASSERT_TRUE(ToProto->getTemplatedDecl());
+ ASSERT_TRUE(ToDef->getTemplatedDecl());
+ EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+ EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+ EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
+ EXPECT_EQ(ToDef->getTemplatedDecl()->getPreviousDecl(),
+ ToProto->getTemplatedDecl());
+}
+
+struct ImportFriendClasses : ASTImporterTestBase {};
+
+TEST_P(ImportFriendClasses, ImportOfFriendRecordDoesNotMergeDefinition) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ class A {
+ template <int I> class F {};
+ class X {
+ template <int I> friend class F;
+ };
+ };
+ )",
+ Lang_CXX, "input0.cc");
+
+ auto *FromClass = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("F"), isDefinition()));
+ auto *FromFriendClass = LastDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("F")));
+
+ ASSERT_TRUE(FromClass);
+ ASSERT_TRUE(FromFriendClass);
+ ASSERT_NE(FromClass, FromFriendClass);
+ ASSERT_EQ(FromFriendClass->getDefinition(), FromClass);
+ ASSERT_EQ(FromFriendClass->getPreviousDecl(), FromClass);
+ ASSERT_EQ(FromFriendClass->getDescribedClassTemplate()->getPreviousDecl(),
+ FromClass->getDescribedClassTemplate());
+
+ auto *ToClass = cast<CXXRecordDecl>(Import(FromClass, Lang_CXX));
+ auto *ToFriendClass = cast<CXXRecordDecl>(Import(FromFriendClass, Lang_CXX));
+
+ EXPECT_TRUE(ToClass);
+ EXPECT_TRUE(ToFriendClass);
+ EXPECT_NE(ToClass, ToFriendClass);
+ EXPECT_EQ(ToFriendClass->getDefinition(), ToClass);
+ EXPECT_EQ(ToFriendClass->getPreviousDecl(), ToClass);
+ EXPECT_EQ(ToFriendClass->getDescribedClassTemplate()->getPreviousDecl(),
+ ToClass->getDescribedClassTemplate());
+}
+
+TEST_P(ImportFriendClasses, ImportOfRecursiveFriendClass) {
+ Decl *FromTu = getTuDecl(
+ R"(
+ class declToImport {
+ friend class declToImport;
+ };
+ )",
+ Lang_CXX, "input.cc");
+
+ auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTu, cxxRecordDecl(hasName("declToImport")));
+ auto *ToD = Import(FromD, Lang_CXX);
+ auto Pattern = cxxRecordDecl(has(friendDecl()));
+ ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern));
+ EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern));
+}
+
+TEST_P(ImportFriendClasses, ImportOfRecursiveFriendClassTemplate) {
+ Decl *FromTu = getTuDecl(
+ R"(
+ template<class A> class declToImport {
+ template<class A1> friend class declToImport;
+ };
+ )",
+ Lang_CXX, "input.cc");
+
+ auto *FromD =
+ FirstDeclMatcher<ClassTemplateDecl>().match(FromTu, classTemplateDecl());
+ auto *ToD = Import(FromD, Lang_CXX);
+
+ auto Pattern = classTemplateDecl(
+ has(cxxRecordDecl(has(friendDecl(has(classTemplateDecl()))))));
+ ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern));
+ EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern));
+
+ auto *Class =
+ FirstDeclMatcher<ClassTemplateDecl>().match(ToD, classTemplateDecl());
+ auto *Friend = FirstDeclMatcher<FriendDecl>().match(ToD, friendDecl());
+ EXPECT_NE(Friend->getFriendDecl(), Class);
+ EXPECT_EQ(Friend->getFriendDecl()->getPreviousDecl(), Class);
+}
+
+TEST_P(ImportFriendClasses, ProperPrevDeclForClassTemplateDecls) {
+ auto Pattern = classTemplateSpecializationDecl(hasName("X"));
+
+ ClassTemplateSpecializationDecl *Imported1;
+ {
+ Decl *FromTU = getTuDecl("template<class T> class X;"
+ "struct Y { friend class X<int>; };",
+ Lang_CXX, "input0.cc");
+ auto *FromD = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ FromTU, Pattern);
+
+ Imported1 = cast<ClassTemplateSpecializationDecl>(Import(FromD, Lang_CXX));
+ }
+ ClassTemplateSpecializationDecl *Imported2;
+ {
+ Decl *FromTU = getTuDecl("template<class T> class X;"
+ "template<> class X<int>{};"
+ "struct Z { friend class X<int>; };",
+ Lang_CXX, "input1.cc");
+ auto *FromD = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ FromTU, Pattern);
+
+ Imported2 = cast<ClassTemplateSpecializationDecl>(Import(FromD, Lang_CXX));
+ }
+
+ Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ EXPECT_EQ(DeclCounter<ClassTemplateSpecializationDecl>().match(ToTU, Pattern),
+ 2u);
+ ASSERT_TRUE(Imported2->getPreviousDecl());
+ EXPECT_EQ(Imported2->getPreviousDecl(), Imported1);
+}
+
+TEST_P(ImportFriendClasses, TypeForDeclShouldBeSetInTemplated) {
+ Decl *FromTU0 = getTuDecl(
+ R"(
+ class X {
+ class Y;
+ };
+ class X::Y {
+ template <typename T>
+ friend class F; // The decl context of F is the global namespace.
+ };
+ )",
+ Lang_CXX, "input0.cc");
+ auto *Fwd = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU0, classTemplateDecl(hasName("F")));
+ auto *Imported0 = cast<ClassTemplateDecl>(Import(Fwd, Lang_CXX));
+ Decl *FromTU1 = getTuDecl(
+ R"(
+ template <typename T>
+ class F {};
+ )",
+ Lang_CXX, "input1.cc");
+ auto *Definition = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU1, classTemplateDecl(hasName("F")));
+ auto *Imported1 = cast<ClassTemplateDecl>(Import(Definition, Lang_CXX));
+ EXPECT_EQ(Imported0->getTemplatedDecl()->getTypeForDecl(),
+ Imported1->getTemplatedDecl()->getTypeForDecl());
+}
+
+TEST_P(ImportFriendClasses, DeclsFromFriendsShouldBeInRedeclChains) {
+ Decl *From, *To;
+ std::tie(From, To) =
+ getImportedDecl("class declToImport {};", Lang_CXX,
+ "class Y { friend class declToImport; };", Lang_CXX);
+ auto *Imported = cast<CXXRecordDecl>(To);
+
+ EXPECT_TRUE(Imported->getPreviousDecl());
+}
+
+TEST_P(ImportFriendClasses,
+ ImportOfClassTemplateDefinitionShouldConnectToFwdFriend) {
+ Decl *ToTU = getToTuDecl(
+ R"(
+ class X {
+ class Y;
+ };
+ class X::Y {
+ template <typename T>
+ friend class F; // The decl context of F is the global namespace.
+ };
+ )",
+ Lang_CXX);
+ auto *ToDecl = FirstDeclMatcher<ClassTemplateDecl>().match(
+ ToTU, classTemplateDecl(hasName("F")));
+ Decl *FromTU = getTuDecl(
+ R"(
+ template <typename T>
+ class F {};
+ )",
+ Lang_CXX, "input0.cc");
+ auto *Definition = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU, classTemplateDecl(hasName("F")));
+ auto *ImportedDef = cast<ClassTemplateDecl>(Import(Definition, Lang_CXX));
+ EXPECT_TRUE(ImportedDef->getPreviousDecl());
+ EXPECT_EQ(ToDecl, ImportedDef->getPreviousDecl());
+ EXPECT_EQ(ToDecl->getTemplatedDecl(),
+ ImportedDef->getTemplatedDecl()->getPreviousDecl());
+}
+
+TEST_P(ImportFriendClasses,
+ ImportOfClassTemplateDefinitionAndFwdFriendShouldBeLinked) {
+ Decl *FromTU0 = getTuDecl(
+ R"(
+ class X {
+ class Y;
+ };
+ class X::Y {
+ template <typename T>
+ friend class F; // The decl context of F is the global namespace.
+ };
+ )",
+ Lang_CXX, "input0.cc");
+ auto *Fwd = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU0, classTemplateDecl(hasName("F")));
+ auto *ImportedFwd = cast<ClassTemplateDecl>(Import(Fwd, Lang_CXX));
+ Decl *FromTU1 = getTuDecl(
+ R"(
+ template <typename T>
+ class F {};
+ )",
+ Lang_CXX, "input1.cc");
+ auto *Definition = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU1, classTemplateDecl(hasName("F")));
+ auto *ImportedDef = cast<ClassTemplateDecl>(Import(Definition, Lang_CXX));
+ EXPECT_TRUE(ImportedDef->getPreviousDecl());
+ EXPECT_EQ(ImportedFwd, ImportedDef->getPreviousDecl());
+ EXPECT_EQ(ImportedFwd->getTemplatedDecl(),
+ ImportedDef->getTemplatedDecl()->getPreviousDecl());
+}
+
+TEST_P(ImportFriendClasses, ImportOfClassDefinitionAndFwdFriendShouldBeLinked) {
+ Decl *FromTU0 = getTuDecl(
+ R"(
+ class X {
+ class Y;
+ };
+ class X::Y {
+ friend class F; // The decl context of F is the global namespace.
+ };
+ )",
+ Lang_CXX, "input0.cc");
+ auto *Friend = FirstDeclMatcher<FriendDecl>().match(FromTU0, friendDecl());
+ QualType FT = Friend->getFriendType()->getType();
+ FT = FromTU0->getASTContext().getCanonicalType(FT);
+ auto *Fwd = cast<TagType>(FT)->getDecl();
+ auto *ImportedFwd = Import(Fwd, Lang_CXX);
+ Decl *FromTU1 = getTuDecl(
+ R"(
+ class F {};
+ )",
+ Lang_CXX, "input1.cc");
+ auto *Definition = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU1, cxxRecordDecl(hasName("F")));
+ auto *ImportedDef = Import(Definition, Lang_CXX);
+ EXPECT_TRUE(ImportedDef->getPreviousDecl());
+ EXPECT_EQ(ImportedFwd, ImportedDef->getPreviousDecl());
+}
+
+TEST_P(ASTImporterTestBase, FriendFunInClassTemplate) {
+ auto *Code = R"(
+ template <class T>
+ struct X {
+ friend void foo(){}
+ };
+ )";
+ TranslationUnitDecl *ToTU = getToTuDecl(Code, Lang_CXX);
+ auto *ToFoo = FirstDeclMatcher<FunctionDecl>().match(
+ ToTU, functionDecl(hasName("foo")));
+
+ TranslationUnitDecl *FromTU = getTuDecl(Code, Lang_CXX, "input.cc");
+ auto *FromFoo = FirstDeclMatcher<FunctionDecl>().match(
+ FromTU, functionDecl(hasName("foo")));
+ auto *ImportedFoo = Import(FromFoo, Lang_CXX);
+ EXPECT_EQ(ImportedFoo, ToFoo);
+}
+
struct DeclContextTest : ASTImporterTestBase {};
TEST_P(DeclContextTest, removeDeclOfClassTemplateSpecialization) {
@@ -3410,6 +4029,40 @@ TEST_P(DeclContextTest, removeDeclOfClassTemplateSpecialization) {
EXPECT_FALSE(NS->containsDecl(Spec));
}
+TEST_P(DeclContextTest,
+ removeDeclShouldNotFailEvenIfWeHaveExternalVisibleStorage) {
+ Decl *TU = getTuDecl("extern int A; int A;", Lang_CXX);
+ auto *A0 = FirstDeclMatcher<VarDecl>().match(TU, varDecl(hasName("A")));
+ auto *A1 = LastDeclMatcher<VarDecl>().match(TU, varDecl(hasName("A")));
+
+ // Investigate the list.
+ auto *DC = A0->getDeclContext();
+ ASSERT_TRUE(DC->containsDecl(A0));
+ ASSERT_TRUE(DC->containsDecl(A1));
+
+ // Investigate the lookup table.
+ auto *Map = DC->getLookupPtr();
+ ASSERT_TRUE(Map);
+ auto I = Map->find(A0->getDeclName());
+ ASSERT_NE(I, Map->end());
+ StoredDeclsList &L = I->second;
+ // The lookup table contains the most recent decl of A.
+ ASSERT_NE(L.getAsDecl(), A0);
+ ASSERT_EQ(L.getAsDecl(), A1);
+
+ ASSERT_TRUE(L.getAsDecl());
+ // Simulate the private function DeclContext::reconcileExternalVisibleStorage.
+ // The point here is to have a Vec with only one element, which is not the
+ // one we are going to delete from the DC later.
+ L.setHasExternalDecls();
+ ASSERT_TRUE(L.getAsVector());
+ ASSERT_EQ(1u, L.getAsVector()->size());
+
+ // This asserts in the old implementation.
+ DC->removeDecl(A0);
+ EXPECT_FALSE(DC->containsDecl(A0));
+}
+
struct ImportFunctionTemplateSpecializations : ASTImporterTestBase {};
TEST_P(ImportFunctionTemplateSpecializations,
@@ -3796,6 +4449,416 @@ TEST_P(ASTImporterTestBase, ImportingTypedefShouldImportTheCompleteType) {
EXPECT_FALSE(ImportedD->getUnderlyingType()->isIncompleteType());
}
+struct ASTImporterLookupTableTest : ASTImporterTestBase {};
+
+TEST_P(ASTImporterLookupTableTest, OneDecl) {
+ auto *ToTU = getToTuDecl("int a;", Lang_CXX);
+ auto *D = FirstDeclMatcher<VarDecl>().match(ToTU, varDecl(hasName("a")));
+ ASTImporterLookupTable LT(*ToTU);
+ auto Res = LT.lookup(ToTU, D->getDeclName());
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), D);
+}
+
+static Decl *findInDeclListOfDC(DeclContext *DC, DeclarationName Name) {
+ for (Decl *D : DC->decls()) {
+ if (auto *ND = dyn_cast<NamedDecl>(D))
+ if (ND->getDeclName() == Name)
+ return ND;
+ }
+ return nullptr;
+}
+
+TEST_P(ASTImporterLookupTableTest,
+ FriendWhichIsnotFoundByNormalLookupShouldBeFoundByImporterSpecificLookup) {
+ auto *Code = R"(
+ template <class T>
+ struct X {
+ friend void foo(){}
+ };
+ )";
+ TranslationUnitDecl *ToTU = getToTuDecl(Code, Lang_CXX);
+ auto *X = FirstDeclMatcher<ClassTemplateDecl>().match(
+ ToTU, classTemplateDecl(hasName("X")));
+ auto *Foo = FirstDeclMatcher<FunctionDecl>().match(
+ ToTU, functionDecl(hasName("foo")));
+ DeclContext *FooDC = Foo->getDeclContext();
+ DeclContext *FooLexicalDC = Foo->getLexicalDeclContext();
+ ASSERT_EQ(cast<Decl>(FooLexicalDC), X->getTemplatedDecl());
+ ASSERT_EQ(cast<Decl>(FooDC), ToTU);
+ DeclarationName FooName = Foo->getDeclName();
+
+ // Cannot find in the LookupTable of its DC (TUDecl)
+ SmallVector<NamedDecl *, 2> FoundDecls;
+ FooDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls);
+ EXPECT_EQ(FoundDecls.size(), 0u);
+
+ // Cannot find in the LookupTable of its LexicalDC (X)
+ FooLexicalDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls);
+ EXPECT_EQ(FoundDecls.size(), 0u);
+
+ // Can't find in the list of Decls of the DC.
+ EXPECT_EQ(findInDeclListOfDC(FooDC, FooName), nullptr);
+
+ // Can't find in the list of Decls of the LexicalDC
+ EXPECT_EQ(findInDeclListOfDC(FooLexicalDC, FooName), nullptr);
+
+ // ASTImporter specific lookup finds it.
+ ASTImporterLookupTable LT(*ToTU);
+ auto Res = LT.lookup(FooDC, Foo->getDeclName());
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), Foo);
+}
+
+TEST_P(ASTImporterLookupTableTest,
+ FwdDeclStructShouldBeFoundByImporterSpecificLookup) {
+ TranslationUnitDecl *ToTU =
+ getToTuDecl("struct A { struct Foo *p; };", Lang_C);
+ auto *Foo =
+ FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("Foo")));
+ auto *A =
+ FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("A")));
+ DeclContext *FooDC = Foo->getDeclContext();
+ DeclContext *FooLexicalDC = Foo->getLexicalDeclContext();
+ ASSERT_EQ(cast<Decl>(FooLexicalDC), A);
+ ASSERT_EQ(cast<Decl>(FooDC), ToTU);
+ DeclarationName FooName = Foo->getDeclName();
+
+ // Cannot find in the LookupTable of its DC (TUDecl).
+ SmallVector<NamedDecl *, 2> FoundDecls;
+ FooDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls);
+ EXPECT_EQ(FoundDecls.size(), 0u);
+
+ // Cannot find in the LookupTable of its LexicalDC (A).
+ FooLexicalDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls);
+ EXPECT_EQ(FoundDecls.size(), 0u);
+
+ // Can't find in the list of Decls of the DC.
+ EXPECT_EQ(findInDeclListOfDC(FooDC, FooName), nullptr);
+
+ // Can find in the list of Decls of the LexicalDC.
+ EXPECT_EQ(findInDeclListOfDC(FooLexicalDC, FooName), Foo);
+
+ // ASTImporter specific lookup finds it.
+ ASTImporterLookupTable LT(*ToTU);
+ auto Res = LT.lookup(FooDC, Foo->getDeclName());
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), Foo);
+}
+
+TEST_P(ASTImporterLookupTableTest, LookupFindsNamesInDifferentDC) {
+ TranslationUnitDecl *ToTU =
+ getToTuDecl("int V; struct A { int V; }; struct B { int V; };", Lang_C);
+ DeclarationName VName = FirstDeclMatcher<VarDecl>()
+ .match(ToTU, varDecl(hasName("V")))
+ ->getDeclName();
+ auto *A =
+ FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("A")));
+ auto *B =
+ FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("B")));
+
+ ASTImporterLookupTable LT(*ToTU);
+
+ auto Res = LT.lookup(cast<DeclContext>(A), VName);
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), FirstDeclMatcher<FieldDecl>().match(
+ ToTU, fieldDecl(hasName("V"),
+ hasParent(recordDecl(hasName("A"))))));
+ Res = LT.lookup(cast<DeclContext>(B), VName);
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), FirstDeclMatcher<FieldDecl>().match(
+ ToTU, fieldDecl(hasName("V"),
+ hasParent(recordDecl(hasName("B"))))));
+ Res = LT.lookup(ToTU, VName);
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), FirstDeclMatcher<VarDecl>().match(
+ ToTU, varDecl(hasName("V"),
+ hasParent(translationUnitDecl()))));
+}
+
+TEST_P(ASTImporterLookupTableTest, LookupFindsOverloadedNames) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ void foo();
+ void foo(int);
+ void foo(int, int);
+ )",
+ Lang_CXX);
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto *F0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, functionDecl());
+ auto *F2 = LastDeclMatcher<FunctionDecl>().match(ToTU, functionDecl());
+ DeclarationName Name = F0->getDeclName();
+ auto Res = LT.lookup(ToTU, Name);
+ EXPECT_EQ(Res.size(), 3u);
+ EXPECT_EQ(Res.count(F0), 1u);
+ EXPECT_EQ(Res.count(F2), 1u);
+}
+
+static const RecordDecl * getRecordDeclOfFriend(FriendDecl *FD) {
+ QualType Ty = FD->getFriendType()->getType();
+ QualType NamedTy = cast<ElaboratedType>(Ty)->getNamedType();
+ return cast<RecordType>(NamedTy)->getDecl();
+}
+
+TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendClassDecl) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ class Y { friend class F; };
+ )",
+ Lang_CXX);
+
+ // In this case, the CXXRecordDecl is hidden, the FriendDecl is not a parent.
+ // So we must dig up the underlying CXXRecordDecl.
+ ASTImporterLookupTable LT(*ToTU);
+ auto *FriendD = FirstDeclMatcher<FriendDecl>().match(ToTU, friendDecl());
+ const RecordDecl *RD = getRecordDeclOfFriend(FriendD);
+ auto *Y = FirstDeclMatcher<CXXRecordDecl>().match(
+ ToTU, cxxRecordDecl(hasName("Y")));
+
+ DeclarationName Name = RD->getDeclName();
+ auto Res = LT.lookup(ToTU, Name);
+ EXPECT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), RD);
+
+ Res = LT.lookup(Y, Name);
+ EXPECT_EQ(Res.size(), 0u);
+}
+
+TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendClassTemplateDecl) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ class Y { template <class T> friend class F; };
+ )",
+ Lang_CXX);
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto *F = FirstDeclMatcher<ClassTemplateDecl>().match(
+ ToTU, classTemplateDecl(hasName("F")));
+ DeclarationName Name = F->getDeclName();
+ auto Res = LT.lookup(ToTU, Name);
+ EXPECT_EQ(Res.size(), 2u);
+ EXPECT_EQ(Res.count(F), 1u);
+ EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u);
+}
+
+TEST_P(ASTImporterLookupTableTest, DependentFriendClass) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ template <typename T>
+ class F;
+
+ template <typename T>
+ class Y {
+ friend class F<T>;
+ };
+ )",
+ Lang_CXX);
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto *F = FirstDeclMatcher<ClassTemplateDecl>().match(
+ ToTU, classTemplateDecl(hasName("F")));
+ DeclarationName Name = F->getDeclName();
+ auto Res = LT.lookup(ToTU, Name);
+ EXPECT_EQ(Res.size(), 2u);
+ EXPECT_EQ(Res.count(F), 1u);
+ EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u);
+}
+
+TEST_P(ASTImporterLookupTableTest, FriendClassTemplateSpecialization) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ template <typename T>
+ class F;
+
+ class Y {
+ friend class F<int>;
+ };
+ )",
+ Lang_CXX);
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto *F = FirstDeclMatcher<ClassTemplateDecl>().match(
+ ToTU, classTemplateDecl(hasName("F")));
+ DeclarationName Name = F->getDeclName();
+ auto Res = LT.lookup(ToTU, Name);
+ ASSERT_EQ(Res.size(), 3u);
+ EXPECT_EQ(Res.count(F), 1u);
+ EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u);
+ EXPECT_EQ(Res.count(*F->spec_begin()), 1u);
+}
+
+TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendFunctionDecl) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ class Y { friend void F(); };
+ )",
+ Lang_CXX);
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto *F =
+ FirstDeclMatcher<FunctionDecl>().match(ToTU, functionDecl(hasName("F")));
+ DeclarationName Name = F->getDeclName();
+ auto Res = LT.lookup(ToTU, Name);
+ EXPECT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), F);
+}
+
+TEST_P(ASTImporterLookupTableTest,
+ LookupFindsDeclsInClassTemplateSpecialization) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ template <typename T>
+ struct X {
+ int F;
+ };
+ void foo() {
+ X<char> xc;
+ }
+ )",
+ Lang_CXX);
+
+ ASTImporterLookupTable LT(*ToTU);
+
+ auto *Template = FirstDeclMatcher<ClassTemplateDecl>().match(
+ ToTU, classTemplateDecl(hasName("X")));
+ auto *FieldInTemplate = FirstDeclMatcher<FieldDecl>().match(
+ ToTU,
+ fieldDecl(hasParent(cxxRecordDecl(hasParent(classTemplateDecl())))));
+
+ auto *Spec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ ToTU, classTemplateSpecializationDecl(hasName("X")));
+ FieldDecl *FieldInSpec = *Spec->field_begin();
+ ASSERT_TRUE(FieldInSpec);
+
+ DeclarationName Name = FieldInSpec->getDeclName();
+ auto TemplateDC = cast<DeclContext>(Template->getTemplatedDecl());
+
+ SmallVector<NamedDecl *, 2> FoundDecls;
+ TemplateDC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+ EXPECT_EQ(FoundDecls.size(), 1u);
+ EXPECT_EQ(FoundDecls[0], FieldInTemplate);
+
+ auto Res = LT.lookup(TemplateDC, Name);
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), FieldInTemplate);
+
+ cast<DeclContext>(Spec)->getRedeclContext()->localUncachedLookup(Name,
+ FoundDecls);
+ EXPECT_EQ(FoundDecls.size(), 1u);
+ EXPECT_EQ(FoundDecls[0], FieldInSpec);
+
+ Res = LT.lookup(cast<DeclContext>(Spec), Name);
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), FieldInSpec);
+}
+
+TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendFunctionTemplateDecl) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ class Y { template <class T> friend void F(); };
+ )",
+ Lang_CXX);
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto *F = FirstDeclMatcher<FunctionTemplateDecl>().match(
+ ToTU, functionTemplateDecl(hasName("F")));
+ DeclarationName Name = F->getDeclName();
+ auto Res = LT.lookup(ToTU, Name);
+ EXPECT_EQ(Res.size(), 2u);
+ EXPECT_EQ(Res.count(F), 1u);
+ EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u);
+}
+
+TEST_P(ASTImporterLookupTableTest, MultipleBefriendingClasses) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ struct X;
+ struct A {
+ friend struct X;
+ };
+ struct B {
+ friend struct X;
+ };
+ )",
+ Lang_CXX);
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto *X = FirstDeclMatcher<CXXRecordDecl>().match(
+ ToTU, cxxRecordDecl(hasName("X")));
+ auto *FriendD0 = FirstDeclMatcher<FriendDecl>().match(ToTU, friendDecl());
+ auto *FriendD1 = LastDeclMatcher<FriendDecl>().match(ToTU, friendDecl());
+ const RecordDecl *RD0 = getRecordDeclOfFriend(FriendD0);
+ const RecordDecl *RD1 = getRecordDeclOfFriend(FriendD1);
+ ASSERT_EQ(RD0, RD1);
+ ASSERT_EQ(RD1, X);
+
+ DeclarationName Name = X->getDeclName();
+ auto Res = LT.lookup(ToTU, Name);
+ EXPECT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), X);
+}
+
+TEST_P(ASTImporterLookupTableTest, EnumConstantDecl) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ enum E {
+ A,
+ B
+ };
+ )",
+ Lang_C);
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto *E = FirstDeclMatcher<EnumDecl>().match(ToTU, enumDecl(hasName("E")));
+ auto *A = FirstDeclMatcher<EnumConstantDecl>().match(
+ ToTU, enumConstantDecl(hasName("A")));
+
+ DeclarationName Name = A->getDeclName();
+ // Redecl context is the TU.
+ ASSERT_EQ(E->getRedeclContext(), ToTU);
+
+ SmallVector<NamedDecl *, 2> FoundDecls;
+ // Normal lookup finds in the DC.
+ E->localUncachedLookup(Name, FoundDecls);
+ EXPECT_EQ(FoundDecls.size(), 1u);
+
+ // Normal lookup finds in the Redecl context.
+ ToTU->localUncachedLookup(Name, FoundDecls);
+ EXPECT_EQ(FoundDecls.size(), 1u);
+
+ // Import specific lookup finds in the DC.
+ auto Res = LT.lookup(E, Name);
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), A);
+
+ // Import specific lookup finds in the Redecl context.
+ Res = LT.lookup(ToTU, Name);
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), A);
+}
+
+TEST_P(ASTImporterLookupTableTest, LookupSearchesInTheWholeRedeclChain) {
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ R"(
+ namespace N {
+ int A;
+ }
+ namespace N {
+ }
+ )",
+ Lang_CXX);
+ auto *N1 =
+ LastDeclMatcher<NamespaceDecl>().match(ToTU, namespaceDecl(hasName("N")));
+ auto *A = FirstDeclMatcher<VarDecl>().match(ToTU, varDecl(hasName("A")));
+ DeclarationName Name = A->getDeclName();
+
+ ASTImporterLookupTable LT(*ToTU);
+ auto Res = LT.lookup(N1, Name);
+ ASSERT_EQ(Res.size(), 1u);
+ EXPECT_EQ(*Res.begin(), A);
+}
+
INSTANTIATE_TEST_CASE_P(ParameterizedTests, DeclContextTest,
::testing::Values(ArgVector()), );
@@ -3809,6 +4872,9 @@ auto DefaultTestValuesForRunOptions = ::testing::Values(
ArgVector{"-fms-compatibility"},
ArgVector{"-fdelayed-template-parsing", "-fms-compatibility"});
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterLookupTableTest,
+ DefaultTestValuesForRunOptions, );
+
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportExpr,
DefaultTestValuesForRunOptions, );
@@ -3824,9 +4890,18 @@ INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterTestBase,
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctions,
DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClasses,
+ DefaultTestValuesForRunOptions, );
+
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendFunctions,
DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClassTemplates,
+ DefaultTestValuesForRunOptions, );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendClasses,
+ DefaultTestValuesForRunOptions, );
+
INSTANTIATE_TEST_CASE_P(ParameterizedTests,
ImportFunctionTemplateSpecializations,
DefaultTestValuesForRunOptions, );
diff --git a/unittests/AST/CMakeLists.txt b/unittests/AST/CMakeLists.txt
index 6621ce681b..c416e5b996 100644
--- a/unittests/AST/CMakeLists.txt
+++ b/unittests/AST/CMakeLists.txt
@@ -2,6 +2,10 @@ set(LLVM_LINK_COMPONENTS
Support
)
+if (MSVC)
+ set_source_files_properties(ASTImporterTest.cpp PROPERTIES COMPILE_FLAGS /bigobj)
+endif()
+
add_clang_unittest(ASTTests
ASTContextParentMapTest.cpp
ASTImporterTest.cpp
diff --git a/unittests/AST/StructuralEquivalenceTest.cpp b/unittests/AST/StructuralEquivalenceTest.cpp
index 7ff7736d4c..cd1f01d4bf 100644
--- a/unittests/AST/StructuralEquivalenceTest.cpp
+++ b/unittests/AST/StructuralEquivalenceTest.cpp
@@ -597,6 +597,77 @@ TEST_F(StructuralEquivalenceRecordTest, UnnamedRecordsShouldBeInequivalent) {
EXPECT_FALSE(testStructuralMatch(R0, R1));
}
+TEST_F(StructuralEquivalenceRecordTest, AnonymousRecordsShouldBeInequivalent) {
+ auto t = makeTuDecls(
+ R"(
+ struct X {
+ struct {
+ int a;
+ };
+ struct {
+ int b;
+ };
+ };
+ )",
+ "", Lang_C);
+ auto *TU = get<0>(t);
+ auto *A = FirstDeclMatcher<IndirectFieldDecl>().match(
+ TU, indirectFieldDecl(hasName("a")));
+ auto *FA = cast<FieldDecl>(A->chain().front());
+ RecordDecl *RA = cast<RecordType>(FA->getType().getTypePtr())->getDecl();
+ auto *B = FirstDeclMatcher<IndirectFieldDecl>().match(
+ TU, indirectFieldDecl(hasName("b")));
+ auto *FB = cast<FieldDecl>(B->chain().front());
+ RecordDecl *RB = cast<RecordType>(FB->getType().getTypePtr())->getDecl();
+
+ ASSERT_NE(RA, RB);
+ EXPECT_TRUE(testStructuralMatch(RA, RA));
+ EXPECT_TRUE(testStructuralMatch(RB, RB));
+ EXPECT_FALSE(testStructuralMatch(RA, RB));
+}
+
+TEST_F(StructuralEquivalenceRecordTest,
+ RecordsAreInequivalentIfOrderOfAnonRecordsIsDifferent) {
+ auto t = makeTuDecls(
+ R"(
+ struct X {
+ struct { int a; };
+ struct { int b; };
+ };
+ )",
+ R"(
+ struct X { // The order is reversed.
+ struct { int b; };
+ struct { int a; };
+ };
+ )",
+ Lang_C);
+
+ auto *TU = get<0>(t);
+ auto *A = FirstDeclMatcher<IndirectFieldDecl>().match(
+ TU, indirectFieldDecl(hasName("a")));
+ auto *FA = cast<FieldDecl>(A->chain().front());
+ RecordDecl *RA = cast<RecordType>(FA->getType().getTypePtr())->getDecl();
+
+ auto *TU1 = get<1>(t);
+ auto *A1 = FirstDeclMatcher<IndirectFieldDecl>().match(
+ TU1, indirectFieldDecl(hasName("a")));
+ auto *FA1 = cast<FieldDecl>(A1->chain().front());
+ RecordDecl *RA1 = cast<RecordType>(FA1->getType().getTypePtr())->getDecl();
+
+ RecordDecl *X =
+ FirstDeclMatcher<RecordDecl>().match(TU, recordDecl(hasName("X")));
+ RecordDecl *X1 =
+ FirstDeclMatcher<RecordDecl>().match(TU1, recordDecl(hasName("X")));
+ ASSERT_NE(X, X1);
+ EXPECT_FALSE(testStructuralMatch(X, X1));
+
+ ASSERT_NE(RA, RA1);
+ EXPECT_TRUE(testStructuralMatch(RA, RA));
+ EXPECT_TRUE(testStructuralMatch(RA1, RA1));
+ EXPECT_FALSE(testStructuralMatch(RA1, RA));
+}
+
TEST_F(StructuralEquivalenceRecordTest,
UnnamedRecordsShouldBeInequivalentEvenIfTheSecondIsBeingDefined) {
auto Code =
diff --git a/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
index d1f9495432..fb17d100c5 100644
--- a/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ b/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -2255,6 +2255,18 @@ TEST(IsAssignmentOperator, Basic) {
notMatches("void x() { int a; if(a == 0) return; }", BinAsgmtOperator));
}
+TEST(HasInit, Basic) {
+ EXPECT_TRUE(
+ matches("int x{0};",
+ initListExpr(hasInit(0, expr()))));
+ EXPECT_FALSE(
+ matches("int x{0};",
+ initListExpr(hasInit(1, expr()))));
+ EXPECT_FALSE(
+ matches("int x;",
+ initListExpr(hasInit(0, expr()))));
+}
+
TEST(Matcher, isMain) {
EXPECT_TRUE(
matches("int main() {}", functionDecl(isMain())));
diff --git a/unittests/Analysis/ExprMutationAnalyzerTest.cpp b/unittests/Analysis/ExprMutationAnalyzerTest.cpp
index 9c6bc783b3..68c921e439 100644
--- a/unittests/Analysis/ExprMutationAnalyzerTest.cpp
+++ b/unittests/Analysis/ExprMutationAnalyzerTest.cpp
@@ -11,6 +11,7 @@
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/SmallString.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <cctype>
@@ -32,7 +33,9 @@ using StmtMatcher = internal::Matcher<Stmt>;
std::unique_ptr<ASTUnit>
buildASTFromCodeWithArgs(const Twine &Code,
const std::vector<std::string> &Args) {
- auto AST = tooling::buildASTFromCodeWithArgs(Code, Args);
+ SmallString<1024> CodeStorage;
+ auto AST =
+ tooling::buildASTFromCodeWithArgs(Code.toStringRef(CodeStorage), Args);
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
return AST;
}
diff --git a/unittests/Basic/FileManagerTest.cpp b/unittests/Basic/FileManagerTest.cpp
index c0efaf4fc4..746d9ad5e8 100644
--- a/unittests/Basic/FileManagerTest.cpp
+++ b/unittests/Basic/FileManagerTest.cpp
@@ -111,7 +111,7 @@ TEST_F(FileManagerTest, NoVirtualDirectoryExistsBeforeAVirtualFileIsAdded) {
// FileManager to report "file/directory doesn't exist". This
// avoids the possibility of the result of this test being affected
// by what's in the real file system.
- manager.addStatCache(llvm::make_unique<FakeStatCache>());
+ manager.setStatCache(llvm::make_unique<FakeStatCache>());
EXPECT_EQ(nullptr, manager.getDirectory("virtual/dir/foo"));
EXPECT_EQ(nullptr, manager.getDirectory("virtual/dir"));
@@ -121,7 +121,7 @@ TEST_F(FileManagerTest, NoVirtualDirectoryExistsBeforeAVirtualFileIsAdded) {
// When a virtual file is added, all of its ancestors should be created.
TEST_F(FileManagerTest, getVirtualFileCreatesDirectoryEntriesForAncestors) {
// Fake an empty real file system.
- manager.addStatCache(llvm::make_unique<FakeStatCache>());
+ manager.setStatCache(llvm::make_unique<FakeStatCache>());
manager.getVirtualFile("virtual/dir/bar.h", 100, 0);
EXPECT_EQ(nullptr, manager.getDirectory("virtual/dir/foo"));
@@ -149,7 +149,7 @@ TEST_F(FileManagerTest, getFileReturnsValidFileEntryForExistingRealFile) {
statCache->InjectFile(FileName, 45);
#endif
- manager.addStatCache(std::move(statCache));
+ manager.setStatCache(std::move(statCache));
const FileEntry *file = manager.getFile("/tmp/test");
ASSERT_TRUE(file != nullptr);
@@ -173,7 +173,7 @@ TEST_F(FileManagerTest, getFileReturnsValidFileEntryForExistingRealFile) {
// getFile() returns non-NULL if a virtual file exists at the given path.
TEST_F(FileManagerTest, getFileReturnsValidFileEntryForExistingVirtualFile) {
// Fake an empty real file system.
- manager.addStatCache(llvm::make_unique<FakeStatCache>());
+ manager.setStatCache(llvm::make_unique<FakeStatCache>());
manager.getVirtualFile("virtual/dir/bar.h", 100, 0);
const FileEntry *file = manager.getFile("virtual/dir/bar.h");
@@ -195,7 +195,7 @@ TEST_F(FileManagerTest, getFileReturnsDifferentFileEntriesForDifferentFiles) {
statCache->InjectDirectory(".", 41);
statCache->InjectFile("foo.cpp", 42);
statCache->InjectFile("bar.cpp", 43);
- manager.addStatCache(std::move(statCache));
+ manager.setStatCache(std::move(statCache));
const FileEntry *fileFoo = manager.getFile("foo.cpp");
const FileEntry *fileBar = manager.getFile("bar.cpp");
@@ -213,7 +213,7 @@ TEST_F(FileManagerTest, getFileReturnsNULLForNonexistentFile) {
auto statCache = llvm::make_unique<FakeStatCache>();
statCache->InjectDirectory(".", 41);
statCache->InjectFile("foo.cpp", 42);
- manager.addStatCache(std::move(statCache));
+ manager.setStatCache(std::move(statCache));
// Create a virtual bar.cpp file.
manager.getVirtualFile("bar.cpp", 200, 0);
@@ -260,7 +260,7 @@ TEST_F(FileManagerTest, getFileReturnsSameFileEntryForAliasedRealFiles) {
statCache->InjectDirectory("abc", 41);
statCache->InjectFile("abc/foo.cpp", 42);
statCache->InjectFile("abc/bar.cpp", 42);
- manager.addStatCache(std::move(statCache));
+ manager.setStatCache(std::move(statCache));
EXPECT_EQ(manager.getFile("abc/foo.cpp"), manager.getFile("abc/bar.cpp"));
}
@@ -273,7 +273,7 @@ TEST_F(FileManagerTest, getFileReturnsSameFileEntryForAliasedVirtualFiles) {
statCache->InjectDirectory("abc", 41);
statCache->InjectFile("abc/foo.cpp", 42);
statCache->InjectFile("abc/bar.cpp", 42);
- manager.addStatCache(std::move(statCache));
+ manager.setStatCache(std::move(statCache));
ASSERT_TRUE(manager.getVirtualFile("abc/foo.cpp", 100, 0)->isValid());
ASSERT_TRUE(manager.getVirtualFile("abc/bar.cpp", 200, 0)->isValid());
@@ -281,15 +281,6 @@ TEST_F(FileManagerTest, getFileReturnsSameFileEntryForAliasedVirtualFiles) {
EXPECT_EQ(manager.getFile("abc/foo.cpp"), manager.getFile("abc/bar.cpp"));
}
-TEST_F(FileManagerTest, addRemoveStatCache) {
- manager.addStatCache(llvm::make_unique<FakeStatCache>());
- auto statCacheOwner = llvm::make_unique<FakeStatCache>();
- auto *statCache = statCacheOwner.get();
- manager.addStatCache(std::move(statCacheOwner));
- manager.addStatCache(llvm::make_unique<FakeStatCache>());
- manager.removeStatCache(statCache);
-}
-
// getFile() Should return the same entry as getVirtualFile if the file actually
// is a virtual file, even if the name is not exactly the same (but is after
// normalisation done by the file system, like on Windows). This can be checked
@@ -300,7 +291,7 @@ TEST_F(FileManagerTest, getVirtualFileWithDifferentName) {
statCache->InjectDirectory("c:\\tmp", 42);
statCache->InjectFile("c:\\tmp\\test", 43);
- manager.addStatCache(std::move(statCache));
+ manager.setStatCache(std::move(statCache));
// Inject the virtual file:
const FileEntry *file1 = manager.getVirtualFile("c:\\tmp\\test", 123, 1);
@@ -371,7 +362,7 @@ TEST_F(FileManagerTest, getVirtualFileFillsRealPathName) {
statCache->InjectDirectory("/tmp", 42);
statCache->InjectFile("/tmp/test", 43);
- Manager.addStatCache(std::move(statCache));
+ Manager.setStatCache(std::move(statCache));
// Check for real path.
const FileEntry *file = Manager.getVirtualFile("/tmp/test", 123, 1);
diff --git a/unittests/Driver/DistroTest.cpp b/unittests/Driver/DistroTest.cpp
index 5a23392e8c..bc1863c429 100644
--- a/unittests/Driver/DistroTest.cpp
+++ b/unittests/Driver/DistroTest.cpp
@@ -51,6 +51,7 @@ TEST(DistroTest, DetectUbuntu) {
ASSERT_FALSE(UbuntuTrusty.IsRedhat());
ASSERT_FALSE(UbuntuTrusty.IsOpenSUSE());
ASSERT_FALSE(UbuntuTrusty.IsDebian());
+ ASSERT_FALSE(UbuntuTrusty.IsGentoo());
llvm::vfs::InMemoryFileSystem UbuntuYakketyFileSystem;
UbuntuYakketyFileSystem.addFile("/etc/debian_version", 0,
@@ -80,6 +81,7 @@ TEST(DistroTest, DetectUbuntu) {
ASSERT_FALSE(UbuntuYakkety.IsRedhat());
ASSERT_FALSE(UbuntuYakkety.IsOpenSUSE());
ASSERT_FALSE(UbuntuYakkety.IsDebian());
+ ASSERT_FALSE(UbuntuYakkety.IsGentoo());
}
TEST(DistroTest, DetectRedhat) {
@@ -114,6 +116,7 @@ TEST(DistroTest, DetectRedhat) {
ASSERT_TRUE(Fedora25.IsRedhat());
ASSERT_FALSE(Fedora25.IsOpenSUSE());
ASSERT_FALSE(Fedora25.IsDebian());
+ ASSERT_FALSE(Fedora25.IsGentoo());
llvm::vfs::InMemoryFileSystem CentOS7FileSystem;
CentOS7FileSystem.addFile("/etc/system-release-cpe", 0,
@@ -150,6 +153,7 @@ TEST(DistroTest, DetectRedhat) {
ASSERT_TRUE(CentOS7.IsRedhat());
ASSERT_FALSE(CentOS7.IsOpenSUSE());
ASSERT_FALSE(CentOS7.IsDebian());
+ ASSERT_FALSE(CentOS7.IsGentoo());
}
TEST(DistroTest, DetectOpenSUSE) {
@@ -177,6 +181,7 @@ TEST(DistroTest, DetectOpenSUSE) {
ASSERT_FALSE(OpenSUSELeap421.IsRedhat());
ASSERT_TRUE(OpenSUSELeap421.IsOpenSUSE());
ASSERT_FALSE(OpenSUSELeap421.IsDebian());
+ ASSERT_FALSE(OpenSUSELeap421.IsGentoo());
llvm::vfs::InMemoryFileSystem OpenSUSE132FileSystem;
OpenSUSE132FileSystem.addFile("/etc/SuSE-release", 0,
@@ -202,6 +207,7 @@ TEST(DistroTest, DetectOpenSUSE) {
ASSERT_FALSE(OpenSUSE132.IsRedhat());
ASSERT_TRUE(OpenSUSE132.IsOpenSUSE());
ASSERT_FALSE(OpenSUSE132.IsDebian());
+ ASSERT_FALSE(OpenSUSE132.IsGentoo());
llvm::vfs::InMemoryFileSystem SLES10FileSystem;
SLES10FileSystem.addFile("/etc/SuSE-release", 0,
@@ -218,6 +224,7 @@ TEST(DistroTest, DetectOpenSUSE) {
ASSERT_FALSE(SLES10.IsRedhat());
ASSERT_FALSE(SLES10.IsOpenSUSE());
ASSERT_FALSE(SLES10.IsDebian());
+ ASSERT_FALSE(SLES10.IsGentoo());
}
TEST(DistroTest, DetectDebian) {
@@ -240,6 +247,7 @@ TEST(DistroTest, DetectDebian) {
ASSERT_FALSE(DebianJessie.IsRedhat());
ASSERT_FALSE(DebianJessie.IsOpenSUSE());
ASSERT_TRUE(DebianJessie.IsDebian());
+ ASSERT_FALSE(DebianJessie.IsGentoo());
llvm::vfs::InMemoryFileSystem DebianStretchSidFileSystem;
DebianStretchSidFileSystem.addFile("/etc/debian_version", 0,
@@ -258,6 +266,7 @@ TEST(DistroTest, DetectDebian) {
ASSERT_FALSE(DebianStretchSid.IsRedhat());
ASSERT_FALSE(DebianStretchSid.IsOpenSUSE());
ASSERT_TRUE(DebianStretchSid.IsDebian());
+ ASSERT_FALSE(DebianStretchSid.IsGentoo());
}
TEST(DistroTest, DetectExherbo) {
@@ -279,6 +288,7 @@ TEST(DistroTest, DetectExherbo) {
ASSERT_FALSE(Exherbo.IsRedhat());
ASSERT_FALSE(Exherbo.IsOpenSUSE());
ASSERT_FALSE(Exherbo.IsDebian());
+ ASSERT_FALSE(Exherbo.IsGentoo());
}
TEST(DistroTest, DetectArchLinux) {
@@ -300,6 +310,32 @@ TEST(DistroTest, DetectArchLinux) {
ASSERT_FALSE(ArchLinux.IsRedhat());
ASSERT_FALSE(ArchLinux.IsOpenSUSE());
ASSERT_FALSE(ArchLinux.IsDebian());
+ ASSERT_FALSE(ArchLinux.IsGentoo());
+}
+
+TEST(DistroTest, DetectGentoo) {
+ llvm::vfs::InMemoryFileSystem GentooFileSystem;
+ GentooFileSystem.addFile(
+ "/etc/gentoo-release", 0,
+ llvm::MemoryBuffer::getMemBuffer("Gentoo Base System release 2.6"));
+ GentooFileSystem.addFile(
+ "/etc/os-release", 0,
+ llvm::MemoryBuffer::getMemBuffer(
+ "NAME=Gentoo\n"
+ "ID=gentoo\n"
+ "PRETTY_NAME=\"Gentoo/Linux\"\n"
+ "ANSI_COLOR=\"1;32\"\n"
+ "HOME_URL=\"https://www.gentoo.org/\"\n"
+ "SUPPORT_URL=\"https://www.gentoo.org/support/\"\n"
+ "BUG_REPORT_URL=\"https://bugs.gentoo.org/\"\n"));
+
+ Distro Gentoo{GentooFileSystem};
+ ASSERT_EQ(Distro(Distro::Gentoo), Gentoo);
+ ASSERT_FALSE(Gentoo.IsUbuntu());
+ ASSERT_FALSE(Gentoo.IsRedhat());
+ ASSERT_FALSE(Gentoo.IsOpenSUSE());
+ ASSERT_FALSE(Gentoo.IsDebian());
+ ASSERT_TRUE(Gentoo.IsGentoo());
}
} // end anonymous namespace
diff --git a/unittests/Format/FormatTestJS.cpp b/unittests/Format/FormatTestJS.cpp
index a14724f85e..67b99ba146 100644
--- a/unittests/Format/FormatTestJS.cpp
+++ b/unittests/Format/FormatTestJS.cpp
@@ -600,6 +600,8 @@ TEST_F(FormatTestJS, GoogModules) {
getGoogleJSStyleWithColumns(40));
verifyFormat("var long = goog.require('this.is.really.absurdly.long');",
getGoogleJSStyleWithColumns(40));
+ verifyFormat("const X = goog.requireType('this.is.really.absurdly.long');",
+ getGoogleJSStyleWithColumns(40));
verifyFormat("goog.forwardDeclare('this.is.really.absurdly.long');",
getGoogleJSStyleWithColumns(40));
diff --git a/unittests/Lex/PPCallbacksTest.cpp b/unittests/Lex/PPCallbacksTest.cpp
index d0ff45ec7c..838e033e3d 100644
--- a/unittests/Lex/PPCallbacksTest.cpp
+++ b/unittests/Lex/PPCallbacksTest.cpp
@@ -65,6 +65,29 @@ public:
SrcMgr::CharacteristicKind FileType;
};
+class CondDirectiveCallbacks : public PPCallbacks {
+public:
+ struct Result {
+ SourceRange ConditionRange;
+ ConditionValueKind ConditionValue;
+
+ Result(SourceRange R, ConditionValueKind K)
+ : ConditionRange(R), ConditionValue(K) {}
+ };
+
+ std::vector<Result> Results;
+
+ void If(SourceLocation Loc, SourceRange ConditionRange,
+ ConditionValueKind ConditionValue) override {
+ Results.emplace_back(ConditionRange, ConditionValue);
+ }
+
+ void Elif(SourceLocation Loc, SourceRange ConditionRange,
+ ConditionValueKind ConditionValue, SourceLocation IfLoc) override {
+ Results.emplace_back(ConditionRange, ConditionValue);
+ }
+};
+
// Stub to collect data from PragmaOpenCLExtension callbacks.
class PragmaOpenCLExtensionCallbacks : public PPCallbacks {
public:
@@ -137,6 +160,15 @@ protected:
return StringRef(B, E - B);
}
+ StringRef GetSourceStringToEnd(CharSourceRange Range) {
+ const char *B = SourceMgr.getCharacterData(Range.getBegin());
+ const char *E = SourceMgr.getCharacterData(Range.getEnd());
+
+ return StringRef(
+ B,
+ E - B + Lexer::MeasureTokenLength(Range.getEnd(), SourceMgr, LangOpts));
+ }
+
// Run lexer over SourceText and collect FilenameRange from
// the InclusionDirective callback.
CharSourceRange InclusionDirectiveFilenameRange(const char *SourceText,
@@ -199,6 +231,36 @@ protected:
return Callbacks;
}
+ std::vector<CondDirectiveCallbacks::Result>
+ DirectiveExprRange(StringRef SourceText) {
+ TrivialModuleLoader ModLoader;
+ MemoryBufferCache PCMCache;
+ std::unique_ptr<llvm::MemoryBuffer> Buf =
+ llvm::MemoryBuffer::getMemBuffer(SourceText);
+ SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
+ HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
+ Diags, LangOpts, Target.get());
+ Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts,
+ SourceMgr, PCMCache, HeaderInfo, ModLoader,
+ /*IILookup =*/nullptr,
+ /*OwnsHeaderSearch =*/false);
+ PP.Initialize(*Target);
+ auto *Callbacks = new CondDirectiveCallbacks;
+ PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callbacks));
+
+ // Lex source text.
+ PP.EnterMainSourceFile();
+
+ while (true) {
+ Token Tok;
+ PP.Lex(Tok);
+ if (Tok.is(tok::eof))
+ break;
+ }
+
+ return Callbacks->Results;
+ }
+
PragmaOpenCLExtensionCallbacks::CallbackParameters
PragmaOpenCLExtensionCall(const char *SourceText) {
LangOptions OpenCLLangOpts;
@@ -368,4 +430,17 @@ TEST_F(PPCallbacksTest, OpenCLExtensionPragmaDisabled) {
ASSERT_EQ(ExpectedState, Parameters.State);
}
-} // anonoymous namespace
+TEST_F(PPCallbacksTest, DirectiveExprRanges) {
+ const auto &Results8 =
+ DirectiveExprRange("#define FLOOFY 0\n#if __FILE__ > FLOOFY\n#endif\n");
+ EXPECT_EQ(Results8.size(), 1U);
+ EXPECT_EQ(
+ GetSourceStringToEnd(CharSourceRange(Results8[0].ConditionRange, false)),
+ " __FILE__ > FLOOFY\n#");
+ EXPECT_EQ(
+ Lexer::getSourceText(CharSourceRange(Results8[0].ConditionRange, false),
+ SourceMgr, LangOpts),
+ " __FILE__ > FLOOFY\n");
+}
+
+} // namespace
diff --git a/unittests/Sema/CodeCompleteTest.cpp b/unittests/Sema/CodeCompleteTest.cpp
index 294807c56c..28faa0c1ee 100644
--- a/unittests/Sema/CodeCompleteTest.cpp
+++ b/unittests/Sema/CodeCompleteTest.cpp
@@ -31,6 +31,9 @@ const char TestCCName[] = "test.cc";
struct CompletionContext {
std::vector<std::string> VisitedNamespaces;
std::string PreferredType;
+ // String representation of std::ptrdiff_t on a given platform. This is a hack
+ // to properly account for different configurations of clang.
+ std::string PtrDiffType;
};
class VisitedContextFinder : public CodeCompleteConsumer {
@@ -47,6 +50,8 @@ public:
ResultCtx.VisitedNamespaces =
getVisitedNamespace(Context.getVisitedContexts());
ResultCtx.PreferredType = Context.getPreferredType().getAsString();
+ ResultCtx.PtrDiffType =
+ S.getASTContext().getPointerDiffType().getAsString();
}
CodeCompletionAllocator &getAllocator() override {
@@ -133,11 +138,19 @@ CompletionContext runCodeCompleteOnCode(StringRef AnnotatedCode) {
return runCompletion(P.Code, P.Points.front());
}
-std::vector<std::string> collectPreferredTypes(StringRef AnnotatedCode) {
+std::vector<std::string>
+collectPreferredTypes(StringRef AnnotatedCode,
+ std::string *PtrDiffType = nullptr) {
ParsedAnnotations P = parseAnnotations(AnnotatedCode);
std::vector<std::string> Types;
- for (size_t Point : P.Points)
- Types.push_back(runCompletion(P.Code, Point).PreferredType);
+ for (size_t Point : P.Points) {
+ auto Results = runCompletion(P.Code, Point);
+ if (PtrDiffType) {
+ assert(PtrDiffType->empty() || *PtrDiffType == Results.PtrDiffType);
+ *PtrDiffType = Results.PtrDiffType;
+ }
+ Types.push_back(Results.PreferredType);
+ }
return Types;
}
@@ -213,9 +226,11 @@ TEST(PreferredTypeTest, BinaryExpr) {
ptr += ^10;
ptr -= ^10;
})cpp";
- // Expect the normalized ptrdiff_t type, which is typically long or long long.
- const char *PtrDiff = sizeof(void *) == sizeof(long) ? "long" : "long long";
- EXPECT_THAT(collectPreferredTypes(Code), Each(PtrDiff));
+ {
+ std::string PtrDiff;
+ auto Types = collectPreferredTypes(Code, &PtrDiff);
+ EXPECT_THAT(Types, Each(PtrDiff));
+ }
// Comparison operators.
Code = R"cpp(
diff --git a/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp b/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp
index dcd115e596..568a719e33 100644
--- a/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp
+++ b/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp
@@ -11,9 +11,9 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
-#include "clang/StaticAnalyzer/Core/CheckerRegistry.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
+#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
#include "clang/Tooling/Tooling.h"
#include "gtest/gtest.h"
@@ -21,16 +21,7 @@ namespace clang {
namespace ento {
namespace {
-class CustomChecker : public Checker<check::ASTCodeBody> {
-public:
- void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
- BugReporter &BR) const {
- BR.EmitBasicReport(D, this, "Custom diagnostic", categories::LogicError,
- "Custom diagnostic description",
- PathDiagnosticLocation(D, Mgr.getSourceManager()), {});
- }
-};
-
+template <typename CheckerT>
class TestAction : public ASTFrontendAction {
class DiagConsumer : public PathDiagnosticConsumer {
llvm::raw_ostream &Output;
@@ -59,22 +50,55 @@ public:
Compiler.getAnalyzerOpts()->CheckersControlList = {
{"custom.CustomChecker", true}};
AnalysisConsumer->AddCheckerRegistrationFn([](CheckerRegistry &Registry) {
- Registry.addChecker<CustomChecker>("custom.CustomChecker", "Description");
+ Registry.addChecker<CheckerT>("custom.CustomChecker", "Description", "");
});
return std::move(AnalysisConsumer);
}
};
+template <typename CheckerT>
+bool runCheckerOnCode(const std::string &Code, std::string &Diags) {
+ llvm::raw_string_ostream OS(Diags);
+ return tooling::runToolOnCode(new TestAction<CheckerT>(OS), Code);
+}
+template <typename CheckerT>
+bool runCheckerOnCode(const std::string &Code) {
+ std::string Diags;
+ return runCheckerOnCode<CheckerT>(Code, Diags);
+}
+
+
+class CustomChecker : public Checker<check::ASTCodeBody> {
+public:
+ void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
+ BugReporter &BR) const {
+ BR.EmitBasicReport(D, this, "Custom diagnostic", categories::LogicError,
+ "Custom diagnostic description",
+ PathDiagnosticLocation(D, Mgr.getSourceManager()), {});
+ }
+};
TEST(RegisterCustomCheckers, RegisterChecker) {
std::string Diags;
- {
- llvm::raw_string_ostream OS(Diags);
- EXPECT_TRUE(tooling::runToolOnCode(new TestAction(OS), "void f() {;}"));
- }
+ EXPECT_TRUE(runCheckerOnCode<CustomChecker>("void f() {;}", Diags));
EXPECT_EQ(Diags, "custom.CustomChecker:Custom diagnostic description");
}
+class LocIncDecChecker : public Checker<check::Location> {
+public:
+ void checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
+ CheckerContext &C) const {
+ auto UnaryOp = dyn_cast<UnaryOperator>(S);
+ if (UnaryOp && !IsLoad)
+ EXPECT_FALSE(UnaryOp->isIncrementOp());
+ }
+};
+
+TEST(RegisterCustomCheckers, CheckLocationIncDec) {
+ EXPECT_TRUE(
+ runCheckerOnCode<LocIncDecChecker>("void f() { int *p; (*p)++; }"));
+}
+
}
}
}
diff --git a/unittests/Tooling/CompilationDatabaseTest.cpp b/unittests/Tooling/CompilationDatabaseTest.cpp
index 125ba4896e..949d6a3b73 100644
--- a/unittests/Tooling/CompilationDatabaseTest.cpp
+++ b/unittests/Tooling/CompilationDatabaseTest.cpp
@@ -14,11 +14,15 @@
#include "clang/Tooling/JSONCompilationDatabase.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Support/Path.h"
+#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace clang {
namespace tooling {
+using testing::ElementsAre;
+using testing::EndsWith;
+
static void expectFailure(StringRef JSONDatabase, StringRef Explanation) {
std::string ErrorMessage;
EXPECT_EQ(nullptr,
@@ -467,21 +471,15 @@ TEST(unescapeJsonCommandLine, ParsesSingleQuotedString) {
}
TEST(FixedCompilationDatabase, ReturnsFixedCommandLine) {
- std::vector<std::string> CommandLine;
- CommandLine.push_back("one");
- CommandLine.push_back("two");
- FixedCompilationDatabase Database(".", CommandLine);
+ FixedCompilationDatabase Database(".", /*CommandLine*/ {"one", "two"});
StringRef FileName("source");
std::vector<CompileCommand> Result =
Database.getCompileCommands(FileName);
ASSERT_EQ(1ul, Result.size());
- std::vector<std::string> ExpectedCommandLine(1, "clang-tool");
- ExpectedCommandLine.insert(ExpectedCommandLine.end(),
- CommandLine.begin(), CommandLine.end());
- ExpectedCommandLine.push_back("source");
EXPECT_EQ(".", Result[0].Directory);
EXPECT_EQ(FileName, Result[0].Filename);
- EXPECT_EQ(ExpectedCommandLine, Result[0].CommandLine);
+ EXPECT_THAT(Result[0].CommandLine,
+ ElementsAre(EndsWith("clang-tool"), "one", "two", "source"));
}
TEST(FixedCompilationDatabase, GetAllFiles) {
@@ -537,12 +535,8 @@ TEST(ParseFixedCompilationDatabase, ReturnsArgumentsAfterDoubleDash) {
Database->getCompileCommands("source");
ASSERT_EQ(1ul, Result.size());
ASSERT_EQ(".", Result[0].Directory);
- std::vector<std::string> CommandLine;
- CommandLine.push_back("clang-tool");
- CommandLine.push_back("-DDEF3");
- CommandLine.push_back("-DDEF4");
- CommandLine.push_back("source");
- ASSERT_EQ(CommandLine, Result[0].CommandLine);
+ ASSERT_THAT(Result[0].CommandLine, ElementsAre(EndsWith("clang-tool"),
+ "-DDEF3", "-DDEF4", "source"));
EXPECT_EQ(2, Argc);
}
@@ -558,10 +552,8 @@ TEST(ParseFixedCompilationDatabase, ReturnsEmptyCommandLine) {
Database->getCompileCommands("source");
ASSERT_EQ(1ul, Result.size());
ASSERT_EQ(".", Result[0].Directory);
- std::vector<std::string> CommandLine;
- CommandLine.push_back("clang-tool");
- CommandLine.push_back("source");
- ASSERT_EQ(CommandLine, Result[0].CommandLine);
+ ASSERT_THAT(Result[0].CommandLine,
+ ElementsAre(EndsWith("clang-tool"), "source"));
EXPECT_EQ(2, Argc);
}
@@ -577,12 +569,8 @@ TEST(ParseFixedCompilationDatabase, HandlesPositionalArgs) {
Database->getCompileCommands("source");
ASSERT_EQ(1ul, Result.size());
ASSERT_EQ(".", Result[0].Directory);
- std::vector<std::string> Expected;
- Expected.push_back("clang-tool");
- Expected.push_back("-c");
- Expected.push_back("-DDEF3");
- Expected.push_back("source");
- ASSERT_EQ(Expected, Result[0].CommandLine);
+ ASSERT_THAT(Result[0].CommandLine,
+ ElementsAre(EndsWith("clang-tool"), "-c", "-DDEF3", "source"));
EXPECT_EQ(2, Argc);
}
@@ -599,12 +587,9 @@ TEST(ParseFixedCompilationDatabase, HandlesPositionalArgsSyntaxOnly) {
std::vector<CompileCommand> Result = Database->getCompileCommands("source");
ASSERT_EQ(1ul, Result.size());
ASSERT_EQ(".", Result[0].Directory);
- std::vector<std::string> Expected;
- Expected.push_back("clang-tool");
- Expected.push_back("-fsyntax-only");
- Expected.push_back("-DDEF3");
- Expected.push_back("source");
- ASSERT_EQ(Expected, Result[0].CommandLine);
+ ASSERT_THAT(
+ Result[0].CommandLine,
+ ElementsAre(EndsWith("clang-tool"), "-fsyntax-only", "-DDEF3", "source"));
}
TEST(ParseFixedCompilationDatabase, HandlesArgv0) {
@@ -620,9 +605,8 @@ TEST(ParseFixedCompilationDatabase, HandlesArgv0) {
ASSERT_EQ(1ul, Result.size());
ASSERT_EQ(".", Result[0].Directory);
std::vector<std::string> Expected;
- Expected.push_back("clang-tool");
- Expected.push_back("source");
- ASSERT_EQ(Expected, Result[0].CommandLine);
+ ASSERT_THAT(Result[0].CommandLine,
+ ElementsAre(EndsWith("clang-tool"), "source"));
EXPECT_EQ(2, Argc);
}
diff --git a/unittests/Tooling/RecursiveASTVisitorTests/LambdaExpr.cpp b/unittests/Tooling/RecursiveASTVisitorTests/LambdaExpr.cpp
index 80aeb43528..d3a3eba15d 100644
--- a/unittests/Tooling/RecursiveASTVisitorTests/LambdaExpr.cpp
+++ b/unittests/Tooling/RecursiveASTVisitorTests/LambdaExpr.cpp
@@ -17,25 +17,33 @@ namespace {
class LambdaExprVisitor : public ExpectedLocationVisitor<LambdaExprVisitor> {
public:
bool VisitLambdaExpr(LambdaExpr *Lambda) {
- PendingBodies.push(Lambda);
+ PendingBodies.push(Lambda->getBody());
+ PendingClasses.push(Lambda->getLambdaClass());
Match("", Lambda->getIntroducerRange().getBegin());
return true;
}
- /// For each call to VisitLambdaExpr, we expect a subsequent call (with
- /// proper nesting) to TraverseLambdaBody.
- bool TraverseLambdaBody(LambdaExpr *Lambda) {
- EXPECT_FALSE(PendingBodies.empty());
- EXPECT_EQ(PendingBodies.top(), Lambda);
- PendingBodies.pop();
- return TraverseStmt(Lambda->getBody());
+ /// For each call to VisitLambdaExpr, we expect a subsequent call to visit
+ /// the body (and maybe the lambda class, which is implicit).
+ bool VisitStmt(Stmt *S) {
+ if (!PendingBodies.empty() && S == PendingBodies.top())
+ PendingBodies.pop();
+ return true;
}
- /// Determine whether TraverseLambdaBody has been called for every call to
- /// VisitLambdaExpr.
- bool allBodiesHaveBeenTraversed() const {
- return PendingBodies.empty();
+ bool VisitDecl(Decl *D) {
+ if (!PendingClasses.empty() && D == PendingClasses.top())
+ PendingClasses.pop();
+ return true;
}
+ /// Determine whether parts of lambdas (VisitLambdaExpr) were later traversed.
+ bool allBodiesHaveBeenTraversed() const { return PendingBodies.empty(); }
+ bool allClassesHaveBeenTraversed() const { return PendingClasses.empty(); }
+
+ bool VisitImplicitCode = false;
+ bool shouldVisitImplicitCode() const { return VisitImplicitCode; }
+
private:
- std::stack<LambdaExpr *> PendingBodies;
+ std::stack<Stmt *> PendingBodies;
+ std::stack<Decl *> PendingClasses;
};
TEST(RecursiveASTVisitor, VisitsLambdaExpr) {
@@ -43,13 +51,39 @@ TEST(RecursiveASTVisitor, VisitsLambdaExpr) {
Visitor.ExpectMatch("", 1, 12);
EXPECT_TRUE(Visitor.runOver("void f() { []{ return; }(); }",
LambdaExprVisitor::Lang_CXX11));
+ EXPECT_TRUE(Visitor.allBodiesHaveBeenTraversed());
+ EXPECT_FALSE(Visitor.allClassesHaveBeenTraversed());
}
-TEST(RecursiveASTVisitor, TraverseLambdaBodyCanBeOverridden) {
+TEST(RecursiveASTVisitor, LambdaInLambda) {
LambdaExprVisitor Visitor;
+ Visitor.ExpectMatch("", 1, 12);
+ Visitor.ExpectMatch("", 1, 16);
+ EXPECT_TRUE(Visitor.runOver("void f() { []{ []{ return; }; }(); }",
+ LambdaExprVisitor::Lang_CXX11));
+ EXPECT_TRUE(Visitor.allBodiesHaveBeenTraversed());
+ EXPECT_FALSE(Visitor.allClassesHaveBeenTraversed());
+}
+
+TEST(RecursiveASTVisitor, TopLevelLambda) {
+ LambdaExprVisitor Visitor;
+ Visitor.VisitImplicitCode = true;
+ Visitor.ExpectMatch("", 1, 10);
+ Visitor.ExpectMatch("", 1, 14);
+ EXPECT_TRUE(Visitor.runOver("auto x = []{ [] {}; };",
+ LambdaExprVisitor::Lang_CXX11));
+ EXPECT_TRUE(Visitor.allBodiesHaveBeenTraversed());
+ EXPECT_TRUE(Visitor.allClassesHaveBeenTraversed());
+}
+
+TEST(RecursiveASTVisitor, VisitsLambdaExprAndImplicitClass) {
+ LambdaExprVisitor Visitor;
+ Visitor.VisitImplicitCode = true;
+ Visitor.ExpectMatch("", 1, 12);
EXPECT_TRUE(Visitor.runOver("void f() { []{ return; }(); }",
LambdaExprVisitor::Lang_CXX11));
EXPECT_TRUE(Visitor.allBodiesHaveBeenTraversed());
+ EXPECT_TRUE(Visitor.allClassesHaveBeenTraversed());
}
TEST(RecursiveASTVisitor, VisitsAttributedLambdaExpr) {
diff --git a/unittests/libclang/LibclangTest.cpp b/unittests/libclang/LibclangTest.cpp
index 6fddcb2cbf..b88b88dac3 100644
--- a/unittests/libclang/LibclangTest.cpp
+++ b/unittests/libclang/LibclangTest.cpp
@@ -461,6 +461,48 @@ TEST_F(LibclangParseTest, AllSkippedRanges) {
clang_disposeSourceRangeList(Ranges);
}
+TEST_F(LibclangParseTest, EvaluateChildExpression) {
+ std::string Main = "main.m";
+ WriteFile(Main, "#define kFOO @\"foo\"\n"
+ "void foobar(void) {\n"
+ " {kFOO;}\n"
+ "}\n");
+ ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0, nullptr,
+ 0, TUFlags);
+
+ CXCursor C = clang_getTranslationUnitCursor(ClangTU);
+ clang_visitChildren(
+ C,
+ [](CXCursor cursor, CXCursor parent,
+ CXClientData client_data) -> CXChildVisitResult {
+ if (clang_getCursorKind(cursor) == CXCursor_FunctionDecl) {
+ int numberedStmt = 0;
+ clang_visitChildren(
+ cursor,
+ [](CXCursor cursor, CXCursor parent,
+ CXClientData client_data) -> CXChildVisitResult {
+ int &numberedStmt = *((int *)client_data);
+ if (clang_getCursorKind(cursor) == CXCursor_CompoundStmt) {
+ if (numberedStmt) {
+ CXEvalResult RE = clang_Cursor_Evaluate(cursor);
+ EXPECT_NE(RE, nullptr);
+ EXPECT_EQ(clang_EvalResult_getKind(RE),
+ CXEval_ObjCStrLiteral);
+ clang_EvalResult_dispose(RE);
+ return CXChildVisit_Break;
+ }
+ numberedStmt++;
+ }
+ return CXChildVisit_Recurse;
+ },
+ &numberedStmt);
+ EXPECT_EQ(numberedStmt, 1);
+ }
+ return CXChildVisit_Continue;
+ },
+ nullptr);
+}
+
class LibclangReparseTest : public LibclangParseTest {
public:
void DisplayDiagnostics() {