diff options
Diffstat (limited to 'unittests')
-rw-r--r-- | unittests/AST/ASTContextParentMapTest.cpp | 11 | ||||
-rw-r--r-- | unittests/AST/ASTImporterTest.cpp | 1301 | ||||
-rw-r--r-- | unittests/AST/CMakeLists.txt | 4 | ||||
-rw-r--r-- | unittests/AST/StructuralEquivalenceTest.cpp | 71 | ||||
-rw-r--r-- | unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp | 12 | ||||
-rw-r--r-- | unittests/Analysis/ExprMutationAnalyzerTest.cpp | 5 | ||||
-rw-r--r-- | unittests/Basic/FileManagerTest.cpp | 29 | ||||
-rw-r--r-- | unittests/Driver/DistroTest.cpp | 36 | ||||
-rw-r--r-- | unittests/Format/FormatTestJS.cpp | 2 | ||||
-rw-r--r-- | unittests/Lex/PPCallbacksTest.cpp | 77 | ||||
-rw-r--r-- | unittests/Sema/CodeCompleteTest.cpp | 27 | ||||
-rw-r--r-- | unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp | 56 | ||||
-rw-r--r-- | unittests/Tooling/CompilationDatabaseTest.cpp | 52 | ||||
-rw-r--r-- | unittests/Tooling/RecursiveASTVisitorTests/LambdaExpr.cpp | 62 | ||||
-rw-r--r-- | unittests/libclang/LibclangTest.cpp | 42 |
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() { |