diff options
author | Jordan Rupprecht <rupprecht@google.com> | 2019-05-14 21:58:59 +0000 |
---|---|---|
committer | Jordan Rupprecht <rupprecht@google.com> | 2019-05-14 21:58:59 +0000 |
commit | b35a2aa71f76a334a9c98c0a3c3995b5d902d2b9 (patch) | |
tree | cdff4a5d1a715d4ad622fd8f190128b54bebe440 /unittests | |
parent | 3748d41833787fcbf59cc5624e8d2b042a8991bc (diff) | |
parent | 741e05796da92b46d4f7bcbee00702ff37df6489 (diff) |
Creating branches/google/stable and tags/google/stable/2019-05-14 from r360103upstream/google/stable
git-svn-id: https://llvm.org/svn/llvm-project/cfe/branches/google/stable@360714 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'unittests')
149 files changed, 6579 insertions, 1735 deletions
diff --git a/unittests/AST/ASTContextParentMapTest.cpp b/unittests/AST/ASTContextParentMapTest.cpp index fb9d517069..14da737500 100644 --- a/unittests/AST/ASTContextParentMapTest.cpp +++ b/unittests/AST/ASTContextParentMapTest.cpp @@ -1,9 +1,8 @@ //===- unittest/AST/ASTContextParentMapTest.cpp - AST parent map test -----===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/unittests/AST/ASTImporterTest.cpp b/unittests/AST/ASTImporterTest.cpp index c6acf573e5..7cac2b5703 100644 --- a/unittests/AST/ASTImporterTest.cpp +++ b/unittests/AST/ASTImporterTest.cpp @@ -1,9 +1,8 @@ //===- unittest/AST/ASTImporterTest.cpp - AST node import test ------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -11,6 +10,10 @@ // //===----------------------------------------------------------------------===// +// Define this to have ::testing::Combine available. +// FIXME: Better solution for this? +#define GTEST_HAS_COMBINE 1 + #include "clang/AST/ASTImporter.h" #include "MatchVerifier.h" #include "clang/AST/ASTContext.h" @@ -41,7 +44,7 @@ createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName, assert(ToAST); ASTContext &ToCtx = ToAST->getASTContext(); auto *OFS = static_cast<llvm::vfs::OverlayFileSystem *>( - ToCtx.getSourceManager().getFileManager().getVirtualFileSystem().get()); + &ToCtx.getSourceManager().getFileManager().getVirtualFileSystem()); auto *MFS = static_cast<llvm::vfs::InMemoryFileSystem *>( OFS->overlays_begin()->get()); MFS->addFile(FileName, 0, std::move(Buffer)); @@ -59,27 +62,35 @@ const StringRef DeclToVerifyID = "declToVerify"; // Common base for the different families of ASTImporter tests that are // parameterized on the compiler options which may result a different AST. E.g. // -fms-compatibility or -fdelayed-template-parsing. -struct ParameterizedTestsFixture : ::testing::TestWithParam<ArgVector> { +class CompilerOptionSpecificTest : public ::testing::Test { +protected: + // Return the extra arguments appended to runtime options at compilation. + virtual ArgVector getExtraArgs() const { return ArgVector(); } // Returns the argument vector used for a specific language option, this set // can be tweaked by the test parameters. ArgVector getArgVectorForLanguage(Language Lang) const { ArgVector Args = getBasicRunOptionsForLanguage(Lang); - ArgVector ExtraArgs = GetParam(); + ArgVector ExtraArgs = getExtraArgs(); for (const auto &Arg : ExtraArgs) { Args.push_back(Arg); } return Args; } - }; +auto DefaultTestValuesForRunOptions = ::testing::Values( + ArgVector(), ArgVector{"-fdelayed-template-parsing"}, + ArgVector{"-fms-compatibility"}, + ArgVector{"-fdelayed-template-parsing", "-fms-compatibility"}); + // Base class for those tests which use the family of `testImport` functions. -class TestImportBase : public ParameterizedTestsFixture { +class TestImportBase : public CompilerOptionSpecificTest, + public ::testing::WithParamInterface<ArgVector> { template <typename NodeType> - NodeType importNode(ASTUnit *From, ASTUnit *To, ASTImporter &Importer, - NodeType Node) { + llvm::Expected<NodeType> importNode(ASTUnit *From, ASTUnit *To, + ASTImporter &Importer, NodeType Node) { ASTContext &ToCtx = To->getASTContext(); // Add 'From' file to virtual file system so importer can 'find' it @@ -89,17 +100,19 @@ class TestImportBase : public ParameterizedTestsFixture { createVirtualFileIfNeeded(To, FromFileName, From->getBufferForFile(FromFileName)); - auto Imported = Importer.Import(Node); + auto Imported = Importer.Import_New(Node); - // This should dump source locations and assert if some source locations - // were not imported. - SmallString<1024> ImportChecker; - llvm::raw_svector_ostream ToNothing(ImportChecker); - ToCtx.getTranslationUnitDecl()->print(ToNothing); + if (Imported) { + // This should dump source locations and assert if some source locations + // were not imported. + SmallString<1024> ImportChecker; + llvm::raw_svector_ostream ToNothing(ImportChecker); + ToCtx.getTranslationUnitDecl()->print(ToNothing); - // This traverses the AST to catch certain bugs like poorly or not - // implemented subtrees. - Imported->dump(ToNothing); + // This traverses the AST to catch certain bugs like poorly or not + // implemented subtrees. + (*Imported)->dump(ToNothing); + } return Imported; } @@ -140,11 +153,16 @@ class TestImportBase : public ParameterizedTestsFixture { EXPECT_TRUE(Verifier.match(ToImport, WrapperMatcher)); auto Imported = importNode(FromAST.get(), ToAST.get(), Importer, ToImport); - if (!Imported) - return testing::AssertionFailure() << "Import failed, nullptr returned!"; - + if (!Imported) { + std::string ErrorText; + handleAllErrors( + Imported.takeError(), + [&ErrorText](const ImportError &Err) { ErrorText = Err.message(); }); + return testing::AssertionFailure() + << "Import failed, error: \"" << ErrorText << "\"!"; + } - return Verifier.match(Imported, WrapperMatcher); + return Verifier.match(*Imported, WrapperMatcher); } template <typename NodeType> @@ -160,6 +178,9 @@ class TestImportBase : public ParameterizedTestsFixture { VerificationMatcher); } +protected: + ArgVector getExtraArgs() const override { return GetParam(); } + public: /// Test how AST node named "declToImport" located in the translation unit @@ -263,7 +284,9 @@ public: EXPECT_TRUE(FoundDecl.size() == 1); const Decl *ToImport = selectFirst<Decl>(DeclToImportID, FoundDecl); auto Imported = importNode(From, To, *ImporterRef, ToImport); - EXPECT_TRUE(Imported); + EXPECT_TRUE(static_cast<bool>(Imported)); + if (!Imported) + llvm::consumeError(Imported.takeError()); } // Find the declaration and import it. @@ -285,11 +308,22 @@ template <typename T> RecordDecl *getRecordDecl(T *D) { // This class provides generic methods to write tests which can check internal // attributes of AST nodes like getPreviousDecl(), isVirtual(), etc. Also, // this fixture makes it possible to import from several "From" contexts. -class ASTImporterTestBase : public ParameterizedTestsFixture { +class ASTImporterTestBase : public CompilerOptionSpecificTest { const char *const InputFileName = "input.cc"; const char *const OutputFileName = "output.cc"; +public: + /// Allocates an ASTImporter (or one of its subclasses). + typedef std::function<ASTImporter *(ASTContext &, FileManager &, ASTContext &, + FileManager &, bool, + ASTImporterLookupTable *)> + ImporterConstructor; + + // The lambda that constructs the ASTImporter we use in this test. + ImporterConstructor Creator; + +private: // Buffer for the To context, must live in the test scope. std::string ToCode; @@ -302,22 +336,32 @@ class ASTImporterTestBase : public ParameterizedTestsFixture { std::unique_ptr<ASTUnit> Unit; TranslationUnitDecl *TUDecl = nullptr; std::unique_ptr<ASTImporter> Importer; - TU(StringRef Code, StringRef FileName, ArgVector Args) + ImporterConstructor Creator; + TU(StringRef Code, StringRef FileName, ArgVector Args, + ImporterConstructor C = ImporterConstructor()) : Code(Code), FileName(FileName), Unit(tooling::buildASTFromCodeWithArgs(this->Code, Args, this->FileName)), - TUDecl(Unit->getASTContext().getTranslationUnitDecl()) { + TUDecl(Unit->getASTContext().getTranslationUnitDecl()), Creator(C) { Unit->enableSourceFileDiagnostics(); + + // If the test doesn't need a specific ASTImporter, we just create a + // normal ASTImporter with it. + if (!Creator) + Creator = [](ASTContext &ToContext, FileManager &ToFileManager, + ASTContext &FromContext, FileManager &FromFileManager, + bool MinimalImport, ASTImporterLookupTable *LookupTable) { + return new ASTImporter(ToContext, ToFileManager, FromContext, + FromFileManager, MinimalImport, LookupTable); + }; } void lazyInitImporter(ASTImporterLookupTable &LookupTable, ASTUnit *ToAST) { assert(ToAST); - if (!Importer) { - Importer.reset( - new ASTImporter(ToAST->getASTContext(), ToAST->getFileManager(), - Unit->getASTContext(), Unit->getFileManager(), - false, &LookupTable)); - } + if (!Importer) + Importer.reset(Creator(ToAST->getASTContext(), ToAST->getFileManager(), + Unit->getASTContext(), Unit->getFileManager(), + false, &LookupTable)); assert(&ToAST->getASTContext() == &Importer->getToContext()); createVirtualFileIfNeeded(ToAST, FileName, Code); } @@ -325,13 +369,23 @@ class ASTImporterTestBase : public ParameterizedTestsFixture { Decl *import(ASTImporterLookupTable &LookupTable, ASTUnit *ToAST, Decl *FromDecl) { lazyInitImporter(LookupTable, ToAST); - return Importer->Import(FromDecl); + if (auto ImportedOrErr = Importer->Import_New(FromDecl)) + return *ImportedOrErr; + else { + llvm::consumeError(ImportedOrErr.takeError()); + return nullptr; + } } QualType import(ASTImporterLookupTable &LookupTable, ASTUnit *ToAST, QualType FromType) { lazyInitImporter(LookupTable, ToAST); - return Importer->Import(FromType); + if (auto ImportedOrErr = Importer->Import_New(FromType)) + return *ImportedOrErr; + else { + llvm::consumeError(ImportedOrErr.takeError()); + return QualType{}; + } } }; @@ -367,7 +421,7 @@ class ASTImporterTestBase : public ParameterizedTestsFixture { // Create a virtual file in the To Ctx which corresponds to the file from // which we want to import the `From` Decl. Without this source locations // will be invalid in the ToCtx. - auto It = std::find_if(FromTUs.begin(), FromTUs.end(), [From](const TU &E) { + auto It = llvm::find_if(FromTUs, [From](const TU &E) { return E.TUDecl == From->getTranslationUnitDecl(); }); assert(It != FromTUs.end()); @@ -391,7 +445,7 @@ public: ArgVector FromArgs = getArgVectorForLanguage(FromLang), ToArgs = getArgVectorForLanguage(ToLang); - FromTUs.emplace_back(FromSrcCode, InputFileName, FromArgs); + FromTUs.emplace_back(FromSrcCode, InputFileName, FromArgs, Creator); TU &FromTU = FromTUs.back(); assert(!ToAST); @@ -421,10 +475,9 @@ public: // name). TranslationUnitDecl *getTuDecl(StringRef SrcCode, Language Lang, StringRef FileName = "input.cc") { - assert( - std::find_if(FromTUs.begin(), FromTUs.end(), [FileName](const TU &E) { - return E.FileName == FileName; - }) == FromTUs.end()); + assert(llvm::find_if(FromTUs, [FileName](const TU &E) { + return E.FileName == FileName; + }) == FromTUs.end()); ArgVector Args = getArgVectorForLanguage(Lang); FromTUs.emplace_back(SrcCode, FileName, Args); @@ -451,6 +504,10 @@ public: return FromTU->import(*LookupTablePtr, ToAST.get(), From); } + template <class DeclT> DeclT *Import(DeclT *From, Language Lang) { + return cast_or_null<DeclT>(Import(cast<Decl>(From), Lang)); + } + QualType ImportType(QualType FromType, Decl *TUDecl, Language ToLang) { lazyInitToAST(ToLang, "", OutputFileName); TU *FromTU = findFromTU(TUDecl); @@ -474,11 +531,18 @@ public: } }; +class ASTImporterOptionSpecificTestBase + : public ASTImporterTestBase, + public ::testing::WithParamInterface<ArgVector> { +protected: + ArgVector getExtraArgs() const override { return GetParam(); } +}; + struct ImportExpr : TestImportBase {}; struct ImportType : TestImportBase {}; struct ImportDecl : TestImportBase {}; -struct CanonicalRedeclChain : ASTImporterTestBase {}; +struct CanonicalRedeclChain : ASTImporterOptionSpecificTestBase {}; TEST_P(CanonicalRedeclChain, ShouldBeConsequentWithMatchers) { Decl *FromTU = getTuDecl("void f();", Lang_CXX); @@ -519,6 +583,74 @@ TEST_P(CanonicalRedeclChain, ShouldBeSameForAllDeclInTheChain) { EXPECT_THAT(RedeclsD1, ::testing::ContainerEq(RedeclsD2)); } +namespace { +struct RedirectingImporter : public ASTImporter { + using ASTImporter::ASTImporter; + +protected: + llvm::Expected<Decl *> ImportImpl(Decl *FromD) override { + auto *ND = dyn_cast<NamedDecl>(FromD); + if (!ND || ND->getName() != "shouldNotBeImported") + return ASTImporter::ImportImpl(FromD); + for (Decl *D : getToContext().getTranslationUnitDecl()->decls()) { + if (auto *ND = dyn_cast<NamedDecl>(D)) + if (ND->getName() == "realDecl") { + RegisterImportedDecl(FromD, ND); + return ND; + } + } + return ASTImporter::ImportImpl(FromD); + } +}; + +} // namespace + +struct RedirectingImporterTest : ASTImporterOptionSpecificTestBase { + RedirectingImporterTest() { + Creator = [](ASTContext &ToContext, FileManager &ToFileManager, + ASTContext &FromContext, FileManager &FromFileManager, + bool MinimalImport, ASTImporterLookupTable *LookupTable) { + return new RedirectingImporter(ToContext, ToFileManager, FromContext, + FromFileManager, MinimalImport, + LookupTable); + }; + } +}; + +// Test that an ASTImporter subclass can intercept an import call. +TEST_P(RedirectingImporterTest, InterceptImport) { + Decl *From, *To; + std::tie(From, To) = + getImportedDecl("class shouldNotBeImported {};", Lang_CXX, + "class realDecl {};", Lang_CXX, "shouldNotBeImported"); + auto *Imported = cast<CXXRecordDecl>(To); + EXPECT_EQ(Imported->getQualifiedNameAsString(), "realDecl"); + + // Make sure our importer prevented the importing of the decl. + auto *ToTU = Imported->getTranslationUnitDecl(); + auto Pattern = functionDecl(hasName("shouldNotBeImported")); + unsigned count = + DeclCounterWithPredicate<CXXRecordDecl>().match(ToTU, Pattern); + EXPECT_EQ(0U, count); +} + +// Test that when we indirectly import a declaration the custom ASTImporter +// is still intercepting the import. +TEST_P(RedirectingImporterTest, InterceptIndirectImport) { + Decl *From, *To; + std::tie(From, To) = + getImportedDecl("class shouldNotBeImported {};" + "class F { shouldNotBeImported f; };", + Lang_CXX, "class realDecl {};", Lang_CXX, "F"); + + // Make sure our ASTImporter prevented the importing of the decl. + auto *ToTU = To->getTranslationUnitDecl(); + auto Pattern = functionDecl(hasName("shouldNotBeImported")); + unsigned count = + DeclCounterWithPredicate<CXXRecordDecl>().match(ToTU, Pattern); + EXPECT_EQ(0U, count); +} + TEST_P(ImportExpr, ImportStringLiteral) { MatchVerifier<Decl> Verifier; testImport( @@ -538,6 +670,17 @@ TEST_P(ImportExpr, ImportStringLiteral) { stringLiteral(hasType(asString("const char [7]")))))); } +TEST_P(ImportExpr, ImportChooseExpr) { + MatchVerifier<Decl> Verifier; + + // This case tests C code that is not condition-dependent and has a true + // condition. + testImport( + "void declToImport() { (void)__builtin_choose_expr(1, 2, 3); }", + Lang_C, "", Lang_C, Verifier, + functionDecl(hasDescendant(chooseExpr()))); +} + TEST_P(ImportExpr, ImportGNUNullExpr) { MatchVerifier<Decl> Verifier; testImport( @@ -1001,7 +1144,7 @@ TEST_P(ImportDecl, ImportRecordDeclInFunc) { has(declStmt(hasSingleDecl(varDecl(hasName("d"))))))))); } -TEST_P(ASTImporterTestBase, ImportRecordTypeInFunc) { +TEST_P(ASTImporterOptionSpecificTestBase, ImportRecordTypeInFunc) { Decl *FromTU = getTuDecl("int declToImport() { " " struct data_t {int a;int b;};" " struct data_t d;" @@ -1016,7 +1159,7 @@ TEST_P(ASTImporterTestBase, ImportRecordTypeInFunc) { EXPECT_FALSE(ToType.isNull()); } -TEST_P(ASTImporterTestBase, ImportRecordDeclInFuncParams) { +TEST_P(ASTImporterOptionSpecificTestBase, ImportRecordDeclInFuncParams) { // This construct is not supported by ASTImporter. Decl *FromTU = getTuDecl( "int declToImport(struct data_t{int a;int b;} ***d){ return 0; }", @@ -1028,7 +1171,7 @@ TEST_P(ASTImporterTestBase, ImportRecordDeclInFuncParams) { EXPECT_EQ(To, nullptr); } -TEST_P(ASTImporterTestBase, ImportRecordDeclInFuncFromMacro) { +TEST_P(ASTImporterOptionSpecificTestBase, ImportRecordDeclInFuncFromMacro) { Decl *FromTU = getTuDecl( "#define NONAME_SIZEOF(type) sizeof(struct{type *dummy;}) \n" "int declToImport(){ return NONAME_SIZEOF(int); }", @@ -1043,7 +1186,8 @@ TEST_P(ASTImporterTestBase, ImportRecordDeclInFuncFromMacro) { hasDescendant(unaryExprOrTypeTraitExpr())))); } -TEST_P(ASTImporterTestBase, ImportRecordDeclInFuncParamsFromMacro) { +TEST_P(ASTImporterOptionSpecificTestBase, + ImportRecordDeclInFuncParamsFromMacro) { // This construct is not supported by ASTImporter. Decl *FromTU = getTuDecl( "#define PAIR_STRUCT(type) struct data_t{type a;type b;} \n" @@ -1195,7 +1339,28 @@ TEST_P(ImportExpr, DependentSizedArrayType) { has(fieldDecl(hasType(dependentSizedArrayType()))))))); } -TEST_P(ASTImporterTestBase, ImportOfTemplatedDeclOfClassTemplateDecl) { +TEST_P(ASTImporterOptionSpecificTestBase, ImportBeginLocOfDeclRefExpr) { + Decl *FromTU = getTuDecl( + "class A { public: static int X; }; void f() { (void)A::X; }", Lang_CXX); + auto From = FirstDeclMatcher<FunctionDecl>().match( + FromTU, functionDecl(hasName("f"))); + ASSERT_TRUE(From); + ASSERT_TRUE( + cast<CStyleCastExpr>(cast<CompoundStmt>(From->getBody())->body_front()) + ->getSubExpr() + ->getBeginLoc() + .isValid()); + FunctionDecl *To = Import(From, Lang_CXX); + ASSERT_TRUE(To); + ASSERT_TRUE( + cast<CStyleCastExpr>(cast<CompoundStmt>(To->getBody())->body_front()) + ->getSubExpr() + ->getBeginLoc() + .isValid()); +} + +TEST_P(ASTImporterOptionSpecificTestBase, + ImportOfTemplatedDeclOfClassTemplateDecl) { Decl *FromTU = getTuDecl("template<class X> struct S{};", Lang_CXX); auto From = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, classTemplateDecl()); @@ -1208,7 +1373,8 @@ TEST_P(ASTImporterTestBase, ImportOfTemplatedDeclOfClassTemplateDecl) { EXPECT_EQ(ToTemplated1, ToTemplated); } -TEST_P(ASTImporterTestBase, ImportOfTemplatedDeclOfFunctionTemplateDecl) { +TEST_P(ASTImporterOptionSpecificTestBase, + ImportOfTemplatedDeclOfFunctionTemplateDecl) { Decl *FromTU = getTuDecl("template<class X> void f(){}", Lang_CXX); auto From = FirstDeclMatcher<FunctionTemplateDecl>().match( FromTU, functionTemplateDecl()); @@ -1221,7 +1387,7 @@ TEST_P(ASTImporterTestBase, ImportOfTemplatedDeclOfFunctionTemplateDecl) { EXPECT_EQ(ToTemplated1, ToTemplated); } -TEST_P(ASTImporterTestBase, +TEST_P(ASTImporterOptionSpecificTestBase, ImportOfTemplatedDeclShouldImportTheClassTemplateDecl) { Decl *FromTU = getTuDecl("template<class X> struct S{};", Lang_CXX); auto FromFT = @@ -1237,7 +1403,7 @@ TEST_P(ASTImporterTestBase, EXPECT_TRUE(ToFT); } -TEST_P(ASTImporterTestBase, +TEST_P(ASTImporterOptionSpecificTestBase, ImportOfTemplatedDeclShouldImportTheFunctionTemplateDecl) { Decl *FromTU = getTuDecl("template<class X> void f(){}", Lang_CXX); auto FromFT = FirstDeclMatcher<FunctionTemplateDecl>().match( @@ -1253,7 +1419,7 @@ TEST_P(ASTImporterTestBase, EXPECT_TRUE(ToFT); } -TEST_P(ASTImporterTestBase, ImportCorrectTemplatedDecl) { +TEST_P(ASTImporterOptionSpecificTestBase, ImportCorrectTemplatedDecl) { auto Code = R"( namespace x { @@ -1284,7 +1450,31 @@ TEST_P(ASTImporterTestBase, ImportCorrectTemplatedDecl) { ASSERT_EQ(ToTemplated1, ToTemplated); } -TEST_P(ASTImporterTestBase, ImportFunctionWithBackReferringParameter) { +TEST_P(ASTImporterOptionSpecificTestBase, ImportChooseExpr) { + // This tests the import of isConditionTrue directly to make sure the importer + // gets it right. + Decl *From, *To; + std::tie(From, To) = getImportedDecl( + "void declToImport() { (void)__builtin_choose_expr(1, 0, 1); }", + Lang_C, "", Lang_C); + + auto ToResults = match(chooseExpr().bind("choose"), To->getASTContext()); + auto FromResults = match(chooseExpr().bind("choose"), From->getASTContext()); + + const ChooseExpr *FromChooseExpr = + selectFirst<ChooseExpr>("choose", FromResults); + ASSERT_TRUE(FromChooseExpr); + + const ChooseExpr *ToChooseExpr = selectFirst<ChooseExpr>("choose", ToResults); + ASSERT_TRUE(ToChooseExpr); + + EXPECT_EQ(FromChooseExpr->isConditionTrue(), ToChooseExpr->isConditionTrue()); + EXPECT_EQ(FromChooseExpr->isConditionDependent(), + ToChooseExpr->isConditionDependent()); +} + +TEST_P(ASTImporterOptionSpecificTestBase, + ImportFunctionWithBackReferringParameter) { Decl *From, *To; std::tie(From, To) = getImportedDecl( R"( @@ -1311,7 +1501,7 @@ TEST_P(ASTImporterTestBase, ImportFunctionWithBackReferringParameter) { EXPECT_TRUE(Verifier.match(To, Matcher)); } -TEST_P(ASTImporterTestBase, +TEST_P(ASTImporterOptionSpecificTestBase, TUshouldNotContainTemplatedDeclOfFunctionTemplates) { Decl *From, *To; std::tie(From, To) = @@ -1337,7 +1527,8 @@ TEST_P(ASTImporterTestBase, EXPECT_TRUE(Check(To)); } -TEST_P(ASTImporterTestBase, TUshouldNotContainTemplatedDeclOfClassTemplates) { +TEST_P(ASTImporterOptionSpecificTestBase, + TUshouldNotContainTemplatedDeclOfClassTemplates) { Decl *From, *To; std::tie(From, To) = getImportedDecl("template <typename T> struct declToImport { T t; };" @@ -1362,7 +1553,8 @@ TEST_P(ASTImporterTestBase, TUshouldNotContainTemplatedDeclOfClassTemplates) { EXPECT_TRUE(Check(To)); } -TEST_P(ASTImporterTestBase, TUshouldNotContainTemplatedDeclOfTypeAlias) { +TEST_P(ASTImporterOptionSpecificTestBase, + TUshouldNotContainTemplatedDeclOfTypeAlias) { Decl *From, *To; std::tie(From, To) = getImportedDecl( @@ -1389,9 +1581,8 @@ TEST_P(ASTImporterTestBase, TUshouldNotContainTemplatedDeclOfTypeAlias) { EXPECT_TRUE(Check(To)); } -TEST_P( - ASTImporterTestBase, - TUshouldNotContainClassTemplateSpecializationOfImplicitInstantiation) { +TEST_P(ASTImporterOptionSpecificTestBase, + TUshouldNotContainClassTemplateSpecializationOfImplicitInstantiation) { Decl *From, *To; std::tie(From, To) = getImportedDecl( @@ -1432,7 +1623,7 @@ AST_MATCHER_P(RecordDecl, hasFieldOrder, std::vector<StringRef>, Order) { return Index == Order.size(); } -TEST_P(ASTImporterTestBase, +TEST_P(ASTImporterOptionSpecificTestBase, TUshouldContainClassTemplateSpecializationOfExplicitInstantiation) { Decl *From, *To; std::tie(From, To) = getImportedDecl( @@ -1459,7 +1650,8 @@ TEST_P(ASTImporterTestBase, EXPECT_TRUE(MatchVerifier<Decl>{}.match(To, Pattern)); } -TEST_P(ASTImporterTestBase, CXXRecordDeclFieldsShouldBeInCorrectOrder) { +TEST_P(ASTImporterOptionSpecificTestBase, + CXXRecordDeclFieldsShouldBeInCorrectOrder) { Decl *From, *To; std::tie(From, To) = getImportedDecl( @@ -1471,7 +1663,7 @@ TEST_P(ASTImporterTestBase, CXXRecordDeclFieldsShouldBeInCorrectOrder) { EXPECT_TRUE(Verifier.match(To, cxxRecordDecl(hasFieldOrder({"a", "b"})))); } -TEST_P(ASTImporterTestBase, +TEST_P(ASTImporterOptionSpecificTestBase, DISABLED_CXXRecordDeclFieldOrderShouldNotDependOnImportOrder) { Decl *From, *To; std::tie(From, To) = getImportedDecl( @@ -1493,7 +1685,7 @@ TEST_P(ASTImporterTestBase, Verifier.match(To, cxxRecordDecl(hasFieldOrder({"a", "b", "c"})))); } -TEST_P(ASTImporterTestBase, ShouldImportImplicitCXXRecordDecl) { +TEST_P(ASTImporterOptionSpecificTestBase, ShouldImportImplicitCXXRecordDecl) { Decl *From, *To; std::tie(From, To) = getImportedDecl( R"( @@ -1509,7 +1701,8 @@ TEST_P(ASTImporterTestBase, ShouldImportImplicitCXXRecordDecl) { EXPECT_TRUE(Verifier.match(To, Matcher)); } -TEST_P(ASTImporterTestBase, ShouldImportImplicitCXXRecordDeclOfClassTemplate) { +TEST_P(ASTImporterOptionSpecificTestBase, + ShouldImportImplicitCXXRecordDeclOfClassTemplate) { Decl *From, *To; std::tie(From, To) = getImportedDecl( R"( @@ -1526,9 +1719,8 @@ TEST_P(ASTImporterTestBase, ShouldImportImplicitCXXRecordDeclOfClassTemplate) { EXPECT_TRUE(Verifier.match(To, Matcher)); } -TEST_P( - ASTImporterTestBase, - ShouldImportImplicitCXXRecordDeclOfClassTemplateSpecializationDecl) { +TEST_P(ASTImporterOptionSpecificTestBase, + ShouldImportImplicitCXXRecordDeclOfClassTemplateSpecializationDecl) { Decl *From, *To; std::tie(From, To) = getImportedDecl( R"( @@ -1548,7 +1740,7 @@ TEST_P( MatchVerifier<Decl>{}.match(To->getTranslationUnitDecl(), Pattern)); } -TEST_P(ASTImporterTestBase, IDNSOrdinary) { +TEST_P(ASTImporterOptionSpecificTestBase, IDNSOrdinary) { Decl *From, *To; std::tie(From, To) = getImportedDecl("void declToImport() {}", Lang_CXX, "", Lang_CXX); @@ -1560,7 +1752,7 @@ TEST_P(ASTImporterTestBase, IDNSOrdinary) { EXPECT_EQ(From->getIdentifierNamespace(), To->getIdentifierNamespace()); } -TEST_P(ASTImporterTestBase, IDNSOfNonmemberOperator) { +TEST_P(ASTImporterOptionSpecificTestBase, IDNSOfNonmemberOperator) { Decl *FromTU = getTuDecl( R"( struct X {}; @@ -1572,7 +1764,7 @@ TEST_P(ASTImporterTestBase, IDNSOfNonmemberOperator) { EXPECT_EQ(From->getIdentifierNamespace(), To->getIdentifierNamespace()); } -TEST_P(ASTImporterTestBase, +TEST_P(ASTImporterOptionSpecificTestBase, ShouldImportMembersOfClassTemplateSpecializationDecl) { Decl *From, *To; std::tie(From, To) = getImportedDecl( @@ -1592,7 +1784,8 @@ TEST_P(ASTImporterTestBase, MatchVerifier<Decl>{}.match(To->getTranslationUnitDecl(), Pattern)); } -TEST_P(ASTImporterTestBase, ImportDefinitionOfClassTemplateAfterFwdDecl) { +TEST_P(ASTImporterOptionSpecificTestBase, + ImportDefinitionOfClassTemplateAfterFwdDecl) { { Decl *FromTU = getTuDecl( R"( @@ -1625,7 +1818,7 @@ TEST_P(ASTImporterTestBase, ImportDefinitionOfClassTemplateAfterFwdDecl) { } } -TEST_P(ASTImporterTestBase, +TEST_P(ASTImporterOptionSpecificTestBase, ImportDefinitionOfClassTemplateIfThereIsAnExistingFwdDeclAndDefinition) { Decl *ToTU = getToTuDecl( R"( @@ -1665,7 +1858,7 @@ TEST_P(ASTImporterTestBase, .match(ToTU, classTemplateDecl())); } -TEST_P(ASTImporterTestBase, +TEST_P(ASTImporterOptionSpecificTestBase, ImportDefinitionOfClassIfThereIsAnExistingFwdDeclAndDefinition) { Decl *ToTU = getToTuDecl( R"( @@ -1708,7 +1901,7 @@ static void CompareSourceRanges(SourceRange Range1, SourceRange Range2, CompareSourceLocs(FullSourceLoc{ Range1.getEnd(), SM1 }, FullSourceLoc{ Range2.getEnd(), SM2 }); } -TEST_P(ASTImporterTestBase, ImportSourceLocs) { +TEST_P(ASTImporterOptionSpecificTestBase, ImportSourceLocs) { Decl *FromTU = getTuDecl( R"( #define MFOO(arg) arg = arg + 1 @@ -1738,7 +1931,7 @@ TEST_P(ASTImporterTestBase, ImportSourceLocs) { FromSM); } -TEST_P(ASTImporterTestBase, ImportNestedMacro) { +TEST_P(ASTImporterOptionSpecificTestBase, ImportNestedMacro) { Decl *FromTU = getTuDecl( R"( #define FUNC_INT void declToImport @@ -1756,9 +1949,8 @@ TEST_P(ASTImporterTestBase, ImportNestedMacro) { } TEST_P( - ASTImporterTestBase, - ImportDefinitionOfClassTemplateSpecIfThereIsAnExistingFwdDeclAndDefinition) -{ + ASTImporterOptionSpecificTestBase, + ImportDefinitionOfClassTemplateSpecIfThereIsAnExistingFwdDeclAndDefinition) { Decl *ToTU = getToTuDecl( R"( template <typename T> @@ -1800,7 +1992,7 @@ TEST_P( .match(ToTU, classTemplateSpecializationDecl())); } -TEST_P(ASTImporterTestBase, ObjectsWithUnnamedStructType) { +TEST_P(ASTImporterOptionSpecificTestBase, ObjectsWithUnnamedStructType) { Decl *FromTU = getTuDecl( R"( struct { int a; int b; } object0 = { 2, 3 }; @@ -1824,7 +2016,7 @@ TEST_P(ASTImporterTestBase, ObjectsWithUnnamedStructType) { EXPECT_NE(To0->getCanonicalDecl(), To1->getCanonicalDecl()); } -TEST_P(ASTImporterTestBase, AnonymousRecords) { +TEST_P(ASTImporterOptionSpecificTestBase, AnonymousRecords) { auto *Code = R"( struct X { @@ -1850,7 +2042,7 @@ TEST_P(ASTImporterTestBase, AnonymousRecords) { DeclCounter<RecordDecl>().match(ToTU, recordDecl(hasName("X")))); } -TEST_P(ASTImporterTestBase, AnonymousRecordsReversed) { +TEST_P(ASTImporterOptionSpecificTestBase, AnonymousRecordsReversed) { Decl *FromTU0 = getTuDecl( R"( struct X { @@ -1883,7 +2075,7 @@ TEST_P(ASTImporterTestBase, AnonymousRecordsReversed) { DeclCounter<RecordDecl>().match(ToTU, recordDecl(hasName("X")))); } -TEST_P(ASTImporterTestBase, ImportDoesUpdateUsedFlag) { +TEST_P(ASTImporterOptionSpecificTestBase, ImportDoesUpdateUsedFlag) { auto Pattern = varDecl(hasName("x")); VarDecl *Imported1; { @@ -1909,7 +2101,7 @@ TEST_P(ASTImporterTestBase, ImportDoesUpdateUsedFlag) { EXPECT_TRUE(Imported2->isUsed(false)); } -TEST_P(ASTImporterTestBase, ImportDoesUpdateUsedFlag2) { +TEST_P(ASTImporterOptionSpecificTestBase, ImportDoesUpdateUsedFlag2) { auto Pattern = varDecl(hasName("x")); VarDecl *ExistingD; { @@ -1927,7 +2119,7 @@ TEST_P(ASTImporterTestBase, ImportDoesUpdateUsedFlag2) { EXPECT_TRUE(ExistingD->isUsed(false)); } -TEST_P(ASTImporterTestBase, ImportDoesUpdateUsedFlag3) { +TEST_P(ASTImporterOptionSpecificTestBase, ImportDoesUpdateUsedFlag3) { auto Pattern = varDecl(hasName("a")); VarDecl *ExistingD; { @@ -1958,7 +2150,7 @@ TEST_P(ASTImporterTestBase, ImportDoesUpdateUsedFlag3) { EXPECT_TRUE(ExistingD->isUsed(false)); } -TEST_P(ASTImporterTestBase, ReimportWithUsedFlag) { +TEST_P(ASTImporterOptionSpecificTestBase, ReimportWithUsedFlag) { auto Pattern = varDecl(hasName("x")); Decl *FromTU = getTuDecl("int x;", Lang_CXX, "input0.cc"); @@ -1975,34 +2167,7 @@ TEST_P(ASTImporterTestBase, ReimportWithUsedFlag) { EXPECT_TRUE(Imported2->isUsed(false)); } -struct ImportFunctions : ASTImporterTestBase {}; - -TEST_P(ImportFunctions, - DefinitionShouldBeImportedAsDefintionWhenThereIsAPrototype) { - Decl *FromTU = getTuDecl("void f(); void f() {}", Lang_CXX); - auto Pattern = functionDecl(hasName("f")); - FunctionDecl *FromD = // Definition - LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern); - - Decl *ImportedD = Import(FromD, Lang_CXX); - Decl *ToTU = ImportedD->getTranslationUnitDecl(); - - EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u); - EXPECT_TRUE(cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody()); -} - -TEST_P(ImportFunctions, DefinitionShouldBeImportedAsADefinition) { - Decl *FromTU = getTuDecl("void f() {}", Lang_CXX); - auto Pattern = functionDecl(hasName("f")); - FunctionDecl *FromD = - FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); - - Decl *ImportedD = Import(FromD, Lang_CXX); - Decl *ToTU = ImportedD->getTranslationUnitDecl(); - - EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u); - EXPECT_TRUE(cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody()); -} +struct ImportFunctions : ASTImporterOptionSpecificTestBase {}; TEST_P(ImportFunctions, ImportPrototypeOfRecursiveFunction) { Decl *FromTU = getTuDecl("void f(); void f() { f(); }", Lang_CXX); @@ -2040,138 +2205,6 @@ TEST_P(ImportFunctions, ImportDefinitionOfRecursiveFunction) { EXPECT_EQ(To1->getPreviousDecl(), To0); } -TEST_P(ImportFunctions, ImportPrototypes) { - auto Pattern = functionDecl(hasName("f")); - - Decl *ImportedD; - { - Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input0.cc"); - auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); - - ImportedD = Import(FromD, Lang_CXX); - } - { - Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input1.cc"); - auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); - Import(FromD, Lang_CXX); - } - - Decl *ToTU = ImportedD->getTranslationUnitDecl(); - - EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u); - auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern); - auto *To1 = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern); - EXPECT_TRUE(ImportedD == To0); - EXPECT_FALSE(To0->doesThisDeclarationHaveABody()); - EXPECT_FALSE(To1->doesThisDeclarationHaveABody()); - EXPECT_EQ(To1->getPreviousDecl(), To0); -} - -TEST_P(ImportFunctions, ImportDefinitions) { - auto Pattern = functionDecl(hasName("f")); - - Decl *ImportedD; - { - Decl *FromTU = getTuDecl("void f(){}", Lang_CXX, "input0.cc"); - auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); - ImportedD = Import(FromD, Lang_CXX); - } - { - Decl *FromTU = getTuDecl("void f(){};", Lang_CXX, "input1.cc"); - auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); - Import(FromD, Lang_CXX); - } - - Decl *ToTU = ImportedD->getTranslationUnitDecl(); - - EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u); - auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern); - EXPECT_TRUE(ImportedD == To0); - EXPECT_TRUE(To0->doesThisDeclarationHaveABody()); -} - -TEST_P(ImportFunctions, ImportDefinitionThenPrototype) { - auto Pattern = functionDecl(hasName("f")); - - Decl *ImportedD; - { - Decl *FromTU = getTuDecl("void f(){}", Lang_CXX, "input0.cc"); - auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); - ImportedD = Import(FromD, Lang_CXX); - } - { - Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input1.cc"); - auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); - Import(FromD, Lang_CXX); - } - - Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); - - EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u); - auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern); - auto *To1 = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern); - EXPECT_TRUE(ImportedD == To0); - EXPECT_TRUE(To0->doesThisDeclarationHaveABody()); - EXPECT_FALSE(To1->doesThisDeclarationHaveABody()); - EXPECT_EQ(To1->getPreviousDecl(), To0); -} - -TEST_P(ImportFunctions, ImportPrototypeThenDefinition) { - auto Pattern = functionDecl(hasName("f")); - - { - Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input0.cc"); - FunctionDecl *FromD = - FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); - - Import(FromD, Lang_CXX); - } - { - Decl *FromTU = getTuDecl("void f(){}", Lang_CXX, "input1.cc"); - FunctionDecl *FromD = - FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); - Import(FromD, Lang_CXX); - } - - Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); - ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u); - FunctionDecl *ProtoD = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern); - EXPECT_FALSE(ProtoD->doesThisDeclarationHaveABody()); - FunctionDecl *DefinitionD = - LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern); - EXPECT_TRUE(DefinitionD->doesThisDeclarationHaveABody()); - EXPECT_EQ(DefinitionD->getPreviousDecl(), ProtoD); -} - -TEST_P(ImportFunctions, ImportPrototypeThenProtoAndDefinition) { - auto Pattern = functionDecl(hasName("f")); - - { - Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input0.cc"); - auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); - Import(FromD, Lang_CXX); - } - { - Decl *FromTU = getTuDecl("void f(); void f(){}", Lang_CXX, "input1.cc"); - auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); - Import(FromD, Lang_CXX); - } - - Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); - - ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 3u); - FunctionDecl *ProtoD = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern); - EXPECT_FALSE(ProtoD->doesThisDeclarationHaveABody()); - - FunctionDecl *DefinitionD = - LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern); - EXPECT_TRUE(DefinitionD->doesThisDeclarationHaveABody()); - - EXPECT_TRUE(DefinitionD->getPreviousDecl()); - EXPECT_FALSE(DefinitionD->getPreviousDecl()->doesThisDeclarationHaveABody()); - EXPECT_EQ(DefinitionD->getPreviousDecl()->getPreviousDecl(), ProtoD); -} - TEST_P(ImportFunctions, OverriddenMethodsShouldBeImported) { auto Code = R"( @@ -2233,6 +2266,521 @@ TEST_P(ImportFunctions, }).match(ToTU, functionDecl())); } +TEST_P(ImportFunctions, ImportOverriddenMethodTwice) { + auto Code = + R"( + struct B { virtual void f(); }; + struct D:B { void f(); }; + )"; + auto BFP = + cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B")))); + auto DFP = + cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D")))); + + Decl *FromTU0 = getTuDecl(Code, Lang_CXX); + auto *DF = FirstDeclMatcher<CXXMethodDecl>().match(FromTU0, DFP); + Import(DF, Lang_CXX); + + Decl *FromTU1 = getTuDecl(Code, Lang_CXX, "input1.cc"); + auto *BF = FirstDeclMatcher<CXXMethodDecl>().match(FromTU1, BFP); + Import(BF, Lang_CXX); + + auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + + EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFP), 1u); + EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFP), 1u); +} + +TEST_P(ImportFunctions, ImportOverriddenMethodTwiceDefinitionFirst) { + auto CodeWithoutDef = + R"( + struct B { virtual void f(); }; + struct D:B { void f(); }; + )"; + auto CodeWithDef = + R"( + struct B { virtual void f(){}; }; + struct D:B { void f(){}; }; + )"; + auto BFP = + cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B")))); + auto DFP = + cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D")))); + auto BFDefP = cxxMethodDecl( + hasName("f"), hasParent(cxxRecordDecl(hasName("B"))), isDefinition()); + auto DFDefP = cxxMethodDecl( + hasName("f"), hasParent(cxxRecordDecl(hasName("D"))), isDefinition()); + auto FDefAllP = cxxMethodDecl(hasName("f"), isDefinition()); + + { + Decl *FromTU = getTuDecl(CodeWithDef, Lang_CXX, "input0.cc"); + auto *FromD = FirstDeclMatcher<CXXMethodDecl>().match(FromTU, DFP); + Import(FromD, Lang_CXX); + } + { + Decl *FromTU = getTuDecl(CodeWithoutDef, Lang_CXX, "input1.cc"); + auto *FromB = FirstDeclMatcher<CXXMethodDecl>().match(FromTU, BFP); + Import(FromB, Lang_CXX); + } + + auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + + EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFP), 1u); + EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFP), 1u); + EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFDefP), 1u); + EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFDefP), 1u); + EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, FDefAllP), 2u); +} + +TEST_P(ImportFunctions, ImportOverriddenMethodTwiceOutOfClassDef) { + auto Code = + R"( + struct B { virtual void f(); }; + struct D:B { void f(); }; + void B::f(){}; + )"; + + auto BFP = + cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B")))); + auto BFDefP = cxxMethodDecl( + hasName("f"), hasParent(cxxRecordDecl(hasName("B"))), isDefinition()); + auto DFP = cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D"))), + unless(isDefinition())); + + Decl *FromTU0 = getTuDecl(Code, Lang_CXX); + auto *D = FirstDeclMatcher<CXXMethodDecl>().match(FromTU0, DFP); + Import(D, Lang_CXX); + + Decl *FromTU1 = getTuDecl(Code, Lang_CXX, "input1.cc"); + auto *B = FirstDeclMatcher<CXXMethodDecl>().match(FromTU1, BFP); + Import(B, Lang_CXX); + + auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + + EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFP), 1u); + EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFDefP), 0u); + + auto *ToB = FirstDeclMatcher<CXXRecordDecl>().match( + ToTU, cxxRecordDecl(hasName("B"))); + auto *ToBFInClass = FirstDeclMatcher<CXXMethodDecl>().match(ToTU, BFP); + auto *ToBFOutOfClass = FirstDeclMatcher<CXXMethodDecl>().match( + ToTU, cxxMethodDecl(hasName("f"), isDefinition())); + + // The definition should be out-of-class. + EXPECT_NE(ToBFInClass, ToBFOutOfClass); + EXPECT_NE(ToBFInClass->getLexicalDeclContext(), + ToBFOutOfClass->getLexicalDeclContext()); + EXPECT_EQ(ToBFOutOfClass->getDeclContext(), ToB); + EXPECT_EQ(ToBFOutOfClass->getLexicalDeclContext(), ToTU); + + // Check that the redecl chain is intact. + EXPECT_EQ(ToBFOutOfClass->getPreviousDecl(), ToBFInClass); +} + +TEST_P(ImportFunctions, + ImportOverriddenMethodTwiceOutOfClassDefInSeparateCode) { + auto CodeTU0 = + R"( + struct B { virtual void f(); }; + struct D:B { void f(); }; + )"; + auto CodeTU1 = + R"( + struct B { virtual void f(); }; + struct D:B { void f(); }; + void B::f(){} + void D::f(){} + void foo(B &b, D &d) { b.f(); d.f(); } + )"; + + auto BFP = + cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B")))); + auto BFDefP = cxxMethodDecl( + hasName("f"), hasParent(cxxRecordDecl(hasName("B"))), isDefinition()); + auto DFP = + cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D")))); + auto DFDefP = cxxMethodDecl( + hasName("f"), hasParent(cxxRecordDecl(hasName("D"))), isDefinition()); + auto FooDef = functionDecl(hasName("foo")); + + { + Decl *FromTU0 = getTuDecl(CodeTU0, Lang_CXX, "input0.cc"); + auto *D = FirstDeclMatcher<CXXMethodDecl>().match(FromTU0, DFP); + Import(D, Lang_CXX); + } + + { + Decl *FromTU1 = getTuDecl(CodeTU1, Lang_CXX, "input1.cc"); + auto *Foo = FirstDeclMatcher<FunctionDecl>().match(FromTU1, FooDef); + Import(Foo, Lang_CXX); + } + + auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + + EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFP), 1u); + EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFP), 1u); + EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFDefP), 0u); + EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFDefP), 0u); + + auto *ToB = FirstDeclMatcher<CXXRecordDecl>().match( + ToTU, cxxRecordDecl(hasName("B"))); + auto *ToD = FirstDeclMatcher<CXXRecordDecl>().match( + ToTU, cxxRecordDecl(hasName("D"))); + auto *ToBFInClass = FirstDeclMatcher<CXXMethodDecl>().match(ToTU, BFP); + auto *ToBFOutOfClass = FirstDeclMatcher<CXXMethodDecl>().match( + ToTU, cxxMethodDecl(hasName("f"), isDefinition())); + auto *ToDFInClass = FirstDeclMatcher<CXXMethodDecl>().match(ToTU, DFP); + auto *ToDFOutOfClass = LastDeclMatcher<CXXMethodDecl>().match( + ToTU, cxxMethodDecl(hasName("f"), isDefinition())); + + // The definition should be out-of-class. + EXPECT_NE(ToBFInClass, ToBFOutOfClass); + EXPECT_NE(ToBFInClass->getLexicalDeclContext(), + ToBFOutOfClass->getLexicalDeclContext()); + EXPECT_EQ(ToBFOutOfClass->getDeclContext(), ToB); + EXPECT_EQ(ToBFOutOfClass->getLexicalDeclContext(), ToTU); + + EXPECT_NE(ToDFInClass, ToDFOutOfClass); + EXPECT_NE(ToDFInClass->getLexicalDeclContext(), + ToDFOutOfClass->getLexicalDeclContext()); + EXPECT_EQ(ToDFOutOfClass->getDeclContext(), ToD); + EXPECT_EQ(ToDFOutOfClass->getLexicalDeclContext(), ToTU); + + // Check that the redecl chain is intact. + EXPECT_EQ(ToBFOutOfClass->getPreviousDecl(), ToBFInClass); + EXPECT_EQ(ToDFOutOfClass->getPreviousDecl(), ToDFInClass); +} + +//FIXME Move these tests to a separate test file. +namespace TypeAndValueParameterizedTests { + +// Type parameters for type-parameterized test fixtures. +struct GetFunPattern { + using DeclTy = FunctionDecl; + BindableMatcher<Decl> operator()() { return functionDecl(hasName("f")); } +}; +struct GetVarPattern { + using DeclTy = VarDecl; + BindableMatcher<Decl> operator()() { return varDecl(hasName("v")); } +}; + +// Values for the value-parameterized test fixtures. +// FunctionDecl: +auto *ExternF = "void f();"; +auto *StaticF = "static void f();"; +auto *AnonF = "namespace { void f(); }"; +// VarDecl: +auto *ExternV = "extern int v;"; +auto *StaticV = "static int v;"; +auto *AnonV = "namespace { extern int v; }"; + +// First value in tuple: Compile options. +// Second value in tuple: Source code to be used in the test. +using ImportVisibilityChainParams = + ::testing::WithParamInterface<std::tuple<ArgVector, const char *>>; +// Fixture to test the redecl chain of Decls with the same visibility. Gtest +// makes it possible to have either value-parameterized or type-parameterized +// fixtures. However, we cannot have both value- and type-parameterized test +// fixtures. This is a value-parameterized test fixture in the gtest sense. We +// intend to mimic gtest's type-parameters via the PatternFactory template +// parameter. We manually instantiate the different tests with the each types. +template <typename PatternFactory> +class ImportVisibilityChain + : public ASTImporterTestBase, public ImportVisibilityChainParams { +protected: + using DeclTy = typename PatternFactory::DeclTy; + ArgVector getExtraArgs() const override { return std::get<0>(GetParam()); } + std::string getCode() const { return std::get<1>(GetParam()); } + BindableMatcher<Decl> getPattern() const { return PatternFactory()(); } + + // Type-parameterized test. + void TypedTest_ImportChain() { + std::string Code = getCode() + getCode(); + auto Pattern = getPattern(); + + TranslationUnitDecl *FromTu = getTuDecl(Code, Lang_CXX, "input0.cc"); + + auto *FromF0 = FirstDeclMatcher<DeclTy>().match(FromTu, Pattern); + auto *FromF1 = LastDeclMatcher<DeclTy>().match(FromTu, Pattern); + + auto *ToF0 = Import(FromF0, Lang_CXX); + auto *ToF1 = Import(FromF1, Lang_CXX); + + EXPECT_TRUE(ToF0); + ASSERT_TRUE(ToF1); + EXPECT_NE(ToF0, ToF1); + EXPECT_EQ(ToF1->getPreviousDecl(), ToF0); + } +}; + +// Manual instantiation of the fixture with each type. +using ImportFunctionsVisibilityChain = ImportVisibilityChain<GetFunPattern>; +using ImportVariablesVisibilityChain = ImportVisibilityChain<GetVarPattern>; +// Value-parameterized test for the first type. +TEST_P(ImportFunctionsVisibilityChain, ImportChain) { + TypedTest_ImportChain(); +} +// Value-parameterized test for the second type. +TEST_P(ImportVariablesVisibilityChain, ImportChain) { + TypedTest_ImportChain(); +} + +// Automatic instantiation of the value-parameterized tests. +INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctionsVisibilityChain, + ::testing::Combine( + DefaultTestValuesForRunOptions, + ::testing::Values(ExternF, StaticF, AnonF)), ); +INSTANTIATE_TEST_CASE_P( + ParameterizedTests, ImportVariablesVisibilityChain, + ::testing::Combine( + DefaultTestValuesForRunOptions, + // There is no point to instantiate with StaticV, because in C++ we can + // forward declare a variable only with the 'extern' keyword. + // Consequently, each fwd declared variable has external linkage. This + // is different in the C language where any declaration without an + // initializer is a tentative definition, subsequent definitions may be + // provided but they must have the same linkage. See also the test + // ImportVariableChainInC which test for this special C Lang case. + ::testing::Values(ExternV, AnonV)), ); + +// First value in tuple: Compile options. +// Second value in tuple: Tuple with informations for the test. +// Code for first import (or initial code), code to import, whether the `f` +// functions are expected to be linked in a declaration chain. +// One value of this tuple is combined with every value of compile options. +// The test can have a single tuple as parameter only. +using ImportVisibilityParams = ::testing::WithParamInterface< + std::tuple<ArgVector, std::tuple<const char *, const char *, bool>>>; + +template <typename PatternFactory> +class ImportVisibility + : public ASTImporterTestBase, + public ImportVisibilityParams { +protected: + using DeclTy = typename PatternFactory::DeclTy; + ArgVector getExtraArgs() const override { return std::get<0>(GetParam()); } + std::string getCode0() const { return std::get<0>(std::get<1>(GetParam())); } + std::string getCode1() const { return std::get<1>(std::get<1>(GetParam())); } + bool shouldBeLinked() const { return std::get<2>(std::get<1>(GetParam())); } + BindableMatcher<Decl> getPattern() const { return PatternFactory()(); } + + void TypedTest_ImportAfter() { + TranslationUnitDecl *ToTu = getToTuDecl(getCode0(), Lang_CXX); + TranslationUnitDecl *FromTu = getTuDecl(getCode1(), Lang_CXX, "input1.cc"); + + auto *ToF0 = FirstDeclMatcher<DeclTy>().match(ToTu, getPattern()); + auto *FromF1 = FirstDeclMatcher<DeclTy>().match(FromTu, getPattern()); + + auto *ToF1 = Import(FromF1, Lang_CXX); + + ASSERT_TRUE(ToF0); + ASSERT_TRUE(ToF1); + EXPECT_NE(ToF0, ToF1); + + if (shouldBeLinked()) + EXPECT_EQ(ToF1->getPreviousDecl(), ToF0); + else + EXPECT_FALSE(ToF1->getPreviousDecl()); + } + + void TypedTest_ImportAfterImport() { + TranslationUnitDecl *FromTu0 = getTuDecl(getCode0(), Lang_CXX, "input0.cc"); + TranslationUnitDecl *FromTu1 = getTuDecl(getCode1(), Lang_CXX, "input1.cc"); + auto *FromF0 = + FirstDeclMatcher<DeclTy>().match(FromTu0, getPattern()); + auto *FromF1 = + FirstDeclMatcher<DeclTy>().match(FromTu1, getPattern()); + auto *ToF0 = Import(FromF0, Lang_CXX); + auto *ToF1 = Import(FromF1, Lang_CXX); + ASSERT_TRUE(ToF0); + ASSERT_TRUE(ToF1); + EXPECT_NE(ToF0, ToF1); + if (shouldBeLinked()) + EXPECT_EQ(ToF1->getPreviousDecl(), ToF0); + else + EXPECT_FALSE(ToF1->getPreviousDecl()); + } +}; +using ImportFunctionsVisibility = ImportVisibility<GetFunPattern>; +using ImportVariablesVisibility = ImportVisibility<GetVarPattern>; + +// FunctionDecl. +TEST_P(ImportFunctionsVisibility, ImportAfter) { + TypedTest_ImportAfter(); +} +TEST_P(ImportFunctionsVisibility, ImportAfterImport) { + TypedTest_ImportAfterImport(); +} +// VarDecl. +TEST_P(ImportVariablesVisibility, ImportAfter) { + TypedTest_ImportAfter(); +} +TEST_P(ImportVariablesVisibility, ImportAfterImport) { + TypedTest_ImportAfterImport(); +} + +bool ExpectLink = true; +bool ExpectNotLink = false; + +INSTANTIATE_TEST_CASE_P( + ParameterizedTests, ImportFunctionsVisibility, + ::testing::Combine( + DefaultTestValuesForRunOptions, + ::testing::Values(std::make_tuple(ExternF, ExternF, ExpectLink), + std::make_tuple(ExternF, StaticF, ExpectNotLink), + std::make_tuple(ExternF, AnonF, ExpectNotLink), + std::make_tuple(StaticF, ExternF, ExpectNotLink), + std::make_tuple(StaticF, StaticF, ExpectNotLink), + std::make_tuple(StaticF, AnonF, ExpectNotLink), + std::make_tuple(AnonF, ExternF, ExpectNotLink), + std::make_tuple(AnonF, StaticF, ExpectNotLink), + std::make_tuple(AnonF, AnonF, ExpectNotLink))), ); +INSTANTIATE_TEST_CASE_P( + ParameterizedTests, ImportVariablesVisibility, + ::testing::Combine( + DefaultTestValuesForRunOptions, + ::testing::Values(std::make_tuple(ExternV, ExternV, ExpectLink), + std::make_tuple(ExternV, StaticV, ExpectNotLink), + std::make_tuple(ExternV, AnonV, ExpectNotLink), + std::make_tuple(StaticV, ExternV, ExpectNotLink), + std::make_tuple(StaticV, StaticV, ExpectNotLink), + std::make_tuple(StaticV, AnonV, ExpectNotLink), + std::make_tuple(AnonV, ExternV, ExpectNotLink), + std::make_tuple(AnonV, StaticV, ExpectNotLink), + std::make_tuple(AnonV, AnonV, ExpectNotLink))), ); + +} // namespace TypeAndValueParameterizedTests + +TEST_P(ASTImporterOptionSpecificTestBase, ImportVariableChainInC) { + std::string Code = "static int v; static int v = 0;"; + auto Pattern = varDecl(hasName("v")); + + TranslationUnitDecl *FromTu = getTuDecl(Code, Lang_C, "input0.c"); + + auto *From0 = FirstDeclMatcher<VarDecl>().match(FromTu, Pattern); + auto *From1 = LastDeclMatcher<VarDecl>().match(FromTu, Pattern); + + auto *To0 = Import(From0, Lang_C); + auto *To1 = Import(From1, Lang_C); + + EXPECT_TRUE(To0); + ASSERT_TRUE(To1); + EXPECT_NE(To0, To1); + EXPECT_EQ(To1->getPreviousDecl(), To0); +} + +TEST_P(ImportFunctions, ImportFromDifferentScopedAnonNamespace) { + TranslationUnitDecl *FromTu = getTuDecl( + "namespace NS0 { namespace { void f(); } }" + "namespace NS1 { namespace { void f(); } }", + Lang_CXX, "input0.cc"); + auto Pattern = functionDecl(hasName("f")); + + auto *FromF0 = FirstDeclMatcher<FunctionDecl>().match(FromTu, Pattern); + auto *FromF1 = LastDeclMatcher<FunctionDecl>().match(FromTu, Pattern); + + auto *ToF0 = Import(FromF0, Lang_CXX); + auto *ToF1 = Import(FromF1, Lang_CXX); + + EXPECT_TRUE(ToF0); + ASSERT_TRUE(ToF1); + EXPECT_NE(ToF0, ToF1); + EXPECT_FALSE(ToF1->getPreviousDecl()); +} + +TEST_P(ImportFunctions, ImportFunctionFromUnnamedNamespace) { + { + Decl *FromTU = getTuDecl("namespace { void f() {} } void g0() { f(); }", + Lang_CXX, "input0.cc"); + auto *FromD = FirstDeclMatcher<FunctionDecl>().match( + FromTU, functionDecl(hasName("g0"))); + + Import(FromD, Lang_CXX); + } + { + Decl *FromTU = + getTuDecl("namespace { void f() { int a; } } void g1() { f(); }", + Lang_CXX, "input1.cc"); + auto *FromD = FirstDeclMatcher<FunctionDecl>().match( + FromTU, functionDecl(hasName("g1"))); + Import(FromD, Lang_CXX); + } + + Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, functionDecl(hasName("f"))), + 2u); +} + +TEST_P(ImportFunctions, ImportImplicitFunctionsInLambda) { + Decl *FromTU = getTuDecl( + R"( + void foo() { + (void)[]() { ; }; + } + )", + Lang_CXX11); + auto *FromD = FirstDeclMatcher<FunctionDecl>().match( + FromTU, functionDecl(hasName("foo"))); + auto *ToD = Import(FromD, Lang_CXX); + EXPECT_TRUE(ToD); + CXXRecordDecl *LambdaRec = + cast<LambdaExpr>(cast<CStyleCastExpr>( + *cast<CompoundStmt>(ToD->getBody())->body_begin()) + ->getSubExpr()) + ->getLambdaClass(); + EXPECT_TRUE(LambdaRec->getDestructor()); +} + +TEST_P(ImportFunctions, + CallExprOfMemberFunctionTemplateWithExplicitTemplateArgs) { + Decl *FromTU = getTuDecl( + R"( + struct X { + template <typename T> + void foo(){} + }; + void f() { + X x; + x.foo<int>(); + } + )", + Lang_CXX); + auto *FromD = FirstDeclMatcher<FunctionDecl>().match( + FromTU, functionDecl(hasName("f"))); + auto *ToD = Import(FromD, Lang_CXX); + EXPECT_TRUE(ToD); + EXPECT_TRUE(MatchVerifier<FunctionDecl>().match( + ToD, functionDecl(hasName("f"), hasDescendant(declRefExpr())))); +} + +TEST_P(ImportFunctions, + DependentCallExprOfMemberFunctionTemplateWithExplicitTemplateArgs) { + Decl *FromTU = getTuDecl( + R"( + struct X { + template <typename T> + void foo(){} + }; + template <typename T> + void f() { + X x; + x.foo<T>(); + } + void g() { + f<int>(); + } + )", + Lang_CXX); + auto *FromD = FirstDeclMatcher<FunctionDecl>().match( + FromTU, functionDecl(hasName("g"))); + auto *ToD = Import(FromD, Lang_CXX); + EXPECT_TRUE(ToD); + Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + EXPECT_TRUE(MatchVerifier<TranslationUnitDecl>().match( + ToTU, translationUnitDecl(hasDescendant( + functionDecl(hasName("f"), hasDescendant(declRefExpr())))))); +} + struct ImportFriendFunctions : ImportFunctions {}; TEST_P(ImportFriendFunctions, ImportFriendFunctionRedeclChainProto) { @@ -2701,7 +3249,7 @@ TEST_P(ImportExpr, UnresolvedMemberExpr) { compoundStmt(has(callExpr(has(unresolvedMemberExpr()))))))))); } -class ImportImplicitMethods : public ASTImporterTestBase { +class ImportImplicitMethods : public ASTImporterOptionSpecificTestBase { public: static constexpr auto DefaultCode = R"( struct A { int x; }; @@ -2813,7 +3361,7 @@ TEST_P(ImportImplicitMethods, DoNotImportOtherMethod) { testNoImportOf(cxxMethodDecl(hasName("f")), Code); } -TEST_P(ASTImporterTestBase, ImportOfEquivalentRecord) { +TEST_P(ASTImporterOptionSpecificTestBase, ImportOfEquivalentRecord) { Decl *ToR1; { Decl *FromTU = getTuDecl( @@ -2837,7 +3385,7 @@ TEST_P(ASTImporterTestBase, ImportOfEquivalentRecord) { EXPECT_EQ(ToR1, ToR2); } -TEST_P(ASTImporterTestBase, ImportOfNonEquivalentRecord) { +TEST_P(ASTImporterOptionSpecificTestBase, ImportOfNonEquivalentRecord) { Decl *ToR1; { Decl *FromTU = getTuDecl( @@ -2857,7 +3405,7 @@ TEST_P(ASTImporterTestBase, ImportOfNonEquivalentRecord) { EXPECT_NE(ToR1, ToR2); } -TEST_P(ASTImporterTestBase, ImportOfEquivalentField) { +TEST_P(ASTImporterOptionSpecificTestBase, ImportOfEquivalentField) { Decl *ToF1; { Decl *FromTU = getTuDecl( @@ -2877,7 +3425,7 @@ TEST_P(ASTImporterTestBase, ImportOfEquivalentField) { EXPECT_EQ(ToF1, ToF2); } -TEST_P(ASTImporterTestBase, ImportOfNonEquivalentField) { +TEST_P(ASTImporterOptionSpecificTestBase, ImportOfNonEquivalentField) { Decl *ToF1; { Decl *FromTU = getTuDecl( @@ -2897,7 +3445,7 @@ TEST_P(ASTImporterTestBase, ImportOfNonEquivalentField) { EXPECT_NE(ToF1, ToF2); } -TEST_P(ASTImporterTestBase, ImportOfEquivalentMethod) { +TEST_P(ASTImporterOptionSpecificTestBase, ImportOfEquivalentMethod) { Decl *ToM1; { Decl *FromTU = getTuDecl( @@ -2917,7 +3465,7 @@ TEST_P(ASTImporterTestBase, ImportOfEquivalentMethod) { EXPECT_EQ(ToM1, ToM2); } -TEST_P(ASTImporterTestBase, ImportOfNonEquivalentMethod) { +TEST_P(ASTImporterOptionSpecificTestBase, ImportOfNonEquivalentMethod) { Decl *ToM1; { Decl *FromTU = getTuDecl( @@ -2939,7 +3487,8 @@ TEST_P(ASTImporterTestBase, ImportOfNonEquivalentMethod) { EXPECT_NE(ToM1, ToM2); } -TEST_P(ASTImporterTestBase, ImportUnnamedStructsWithRecursingField) { +TEST_P(ASTImporterOptionSpecificTestBase, + ImportUnnamedStructsWithRecursingField) { Decl *FromTU = getTuDecl( R"( struct A { @@ -2971,7 +3520,7 @@ TEST_P(ASTImporterTestBase, ImportUnnamedStructsWithRecursingField) { R1, recordDecl(has(fieldDecl(hasName("next")))))); } -TEST_P(ASTImporterTestBase, ImportUnnamedFieldsInCorrectOrder) { +TEST_P(ASTImporterOptionSpecificTestBase, ImportUnnamedFieldsInCorrectOrder) { Decl *FromTU = getTuDecl( R"( void f(int X, int Y, bool Z) { @@ -3006,7 +3555,8 @@ TEST_P(ASTImporterTestBase, ImportUnnamedFieldsInCorrectOrder) { EXPECT_EQ(FromIndex, 3u); } -TEST_P(ASTImporterTestBase, MergeFieldDeclsOfClassTemplateSpecialization) { +TEST_P(ASTImporterOptionSpecificTestBase, + MergeFieldDeclsOfClassTemplateSpecialization) { std::string ClassTemplate = R"( template <typename T> @@ -3051,7 +3601,8 @@ TEST_P(ASTImporterTestBase, MergeFieldDeclsOfClassTemplateSpecialization) { EXPECT_TRUE(ToField->getInClassInitializer()); } -TEST_P(ASTImporterTestBase, MergeFunctionOfClassTemplateSpecialization) { +TEST_P(ASTImporterOptionSpecificTestBase, + MergeFunctionOfClassTemplateSpecialization) { std::string ClassTemplate = R"( template <typename T> @@ -3092,7 +3643,7 @@ TEST_P(ASTImporterTestBase, MergeFunctionOfClassTemplateSpecialization) { EXPECT_TRUE(ToFun->hasBody()); } -TEST_P(ASTImporterTestBase, +TEST_P(ASTImporterOptionSpecificTestBase, ODRViolationOfClassTemplateSpecializationsShouldBeReported) { std::string ClassTemplate = R"( @@ -3131,15 +3682,14 @@ TEST_P(ASTImporterTestBase, // The second specialization is different from the first, thus it violates // ODR, consequently we expect to keep the first specialization only, which is // already in the "To" context. - EXPECT_TRUE(ImportedSpec); - auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match( - ToTU, classTemplateSpecializationDecl(hasName("X"))); - EXPECT_EQ(ImportedSpec, ToSpec); - EXPECT_EQ(1u, DeclCounter<ClassTemplateSpecializationDecl>().match( - ToTU, classTemplateSpecializationDecl())); + EXPECT_FALSE(ImportedSpec); + EXPECT_EQ(1u, + DeclCounter<ClassTemplateSpecializationDecl>().match( + ToTU, classTemplateSpecializationDecl(hasName("X")))); } -TEST_P(ASTImporterTestBase, MergeCtorOfClassTemplateSpecialization) { +TEST_P(ASTImporterOptionSpecificTestBase, + MergeCtorOfClassTemplateSpecialization) { std::string ClassTemplate = R"( template <typename T> @@ -3180,7 +3730,7 @@ TEST_P(ASTImporterTestBase, MergeCtorOfClassTemplateSpecialization) { EXPECT_TRUE(ToCtor->hasBody()); } -TEST_P(ASTImporterTestBase, +TEST_P(ASTImporterOptionSpecificTestBase, ClassTemplatePartialSpecializationsShouldNotBeDuplicated) { auto Code = R"( @@ -3207,7 +3757,8 @@ TEST_P(ASTImporterTestBase, ToTU, classTemplatePartialSpecializationDecl())); } -TEST_P(ASTImporterTestBase, ClassTemplateSpecializationsShouldNotBeDuplicated) { +TEST_P(ASTImporterOptionSpecificTestBase, + ClassTemplateSpecializationsShouldNotBeDuplicated) { auto Code = R"( // primary template @@ -3231,7 +3782,8 @@ TEST_P(ASTImporterTestBase, ClassTemplateSpecializationsShouldNotBeDuplicated) { ToTU, classTemplateSpecializationDecl())); } -TEST_P(ASTImporterTestBase, ClassTemplateFullAndPartialSpecsShouldNotBeMixed) { +TEST_P(ASTImporterOptionSpecificTestBase, + ClassTemplateFullAndPartialSpecsShouldNotBeMixed) { std::string PrimaryTemplate = R"( template<class T1, class T2, int I> @@ -3263,7 +3815,8 @@ TEST_P(ASTImporterTestBase, ClassTemplateFullAndPartialSpecsShouldNotBeMixed) { unless(classTemplatePartialSpecializationDecl())))); } -TEST_P(ASTImporterTestBase, InitListExprValueKindShouldBeImported) { +TEST_P(ASTImporterOptionSpecificTestBase, + InitListExprValueKindShouldBeImported) { Decl *TU = getTuDecl( R"( const int &init(); @@ -3282,7 +3835,7 @@ TEST_P(ASTImporterTestBase, InitListExprValueKindShouldBeImported) { EXPECT_TRUE(ToInitExpr->isGLValue()); } -struct ImportVariables : ASTImporterTestBase {}; +struct ImportVariables : ASTImporterOptionSpecificTestBase {}; TEST_P(ImportVariables, ImportOfOneDeclBringsInTheWholeChain) { Decl *FromTU = getTuDecl( @@ -3370,155 +3923,7 @@ 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); -} +struct ImportClasses : ASTImporterOptionSpecificTestBase {}; TEST_P(ImportClasses, ImportDefinitionWhenProtoIsInNestedToContext) { Decl *ToTU = getToTuDecl("struct A { struct X *Xp; };", Lang_C); @@ -3578,171 +3983,576 @@ TEST_P(ImportClasses, ImportNestedPrototypeThenDefinition) { EXPECT_EQ(ToDef->getPreviousDecl(), ToProto); } -struct ImportClassTemplates : ASTImporterTestBase {}; +// FIXME put these structs and the tests rely on them into their own separate +// test file! +struct Function { + using DeclTy = FunctionDecl; + static constexpr auto *Prototype = "void X();"; + static constexpr auto *Definition = "void X() {}"; + BindableMatcher<Decl> getPattern() { + return functionDecl(hasName("X"), unless(isImplicit())); + } +}; -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); +struct Class { + using DeclTy = CXXRecordDecl; + static constexpr auto *Prototype = "class X;"; + static constexpr auto *Definition = "class X {};"; + BindableMatcher<Decl> getPattern() { + return cxxRecordDecl(hasName("X"), unless(isImplicit())); + } +}; - Decl *ImportedD = Import(FromD, Lang_CXX); - Decl *ToTU = ImportedD->getTranslationUnitDecl(); +struct Variable { + using DeclTy = VarDecl; + static constexpr auto *Prototype = "extern int X;"; + static constexpr auto *Definition = "int X;"; + BindableMatcher<Decl> getPattern() { + return varDecl(hasName("X")); + } +}; - 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()); -} +struct FunctionTemplate { + using DeclTy = FunctionTemplateDecl; + static constexpr auto *Prototype = "template <class T> void X();"; + static constexpr auto *Definition = + R"( + template <class T> void X() {}; + // Explicit instantiation is a must because of -fdelayed-template-parsing: + template void X<int>(); + )"; + BindableMatcher<Decl> getPattern() { + return functionTemplateDecl(hasName("X"), unless(isImplicit())); + } +}; -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()); -} +struct ClassTemplate { + using DeclTy = ClassTemplateDecl; + static constexpr auto *Prototype = "template <class T> class X;"; + static constexpr auto *Definition = "template <class T> class X {};"; + BindableMatcher<Decl> getPattern() { + return classTemplateDecl(hasName("X"), unless(isImplicit())); + } +}; -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); +struct FunctionTemplateSpec { + using DeclTy = FunctionDecl; + static constexpr auto *Prototype = + R"( + // Proto of the primary template. + template <class T> + void X(); + // Proto of the specialization. + template <> + void X<int>(); + )"; + static constexpr auto *Definition = + R"( + // Proto of the primary template. + template <class T> + void X(); + // Specialization and definition. + template <> + void X<int>() {} + )"; + BindableMatcher<Decl> getPattern() { + return functionDecl(hasName("X"), isExplicitTemplateSpecialization()); + } +}; - Decl *ImportedD = Import(FromD, Lang_CXX); - Decl *ToTU = ImportedD->getTranslationUnitDecl(); +struct ClassTemplateSpec { + using DeclTy = ClassTemplateSpecializationDecl; + static constexpr auto *Prototype = + R"( + template <class T> class X; + template <> class X<int>; + )"; + static constexpr auto *Definition = + R"( + template <class T> class X; + template <> class X<int> {}; + )"; + BindableMatcher<Decl> getPattern() { + return classTemplateSpecializationDecl(hasName("X"), unless(isImplicit())); + } +}; - 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); +template <typename TypeParam> +struct RedeclChain : ASTImporterOptionSpecificTestBase { + + using DeclTy = typename TypeParam::DeclTy; + std::string getPrototype() { return TypeParam::Prototype; } + std::string getDefinition() { return TypeParam::Definition; } + BindableMatcher<Decl> getPattern() const { return TypeParam().getPattern(); } + + void CheckPreviousDecl(Decl *Prev, Decl *Current) { + ASSERT_NE(Prev, Current); + ASSERT_EQ(&Prev->getASTContext(), &Current->getASTContext()); + EXPECT_EQ(Prev->getCanonicalDecl(), Current->getCanonicalDecl()); + + // Templates. + if (auto *PrevT = dyn_cast<TemplateDecl>(Prev)) { + EXPECT_EQ(Current->getPreviousDecl(), Prev); + auto *CurrentT = cast<TemplateDecl>(Current); + ASSERT_TRUE(PrevT->getTemplatedDecl()); + ASSERT_TRUE(CurrentT->getTemplatedDecl()); + EXPECT_EQ(CurrentT->getTemplatedDecl()->getPreviousDecl(), + PrevT->getTemplatedDecl()); + return; + } - Decl *ImportedDef = Import(FromDef, Lang_CXX); - Decl *ImportedProto = Import(FromProto, Lang_CXX); - Decl *ToTU = ImportedDef->getTranslationUnitDecl(); + // Specializations. + if (auto *PrevF = dyn_cast<FunctionDecl>(Prev)) { + if (PrevF->getTemplatedKind() == + FunctionDecl::TK_FunctionTemplateSpecialization) { + // There may be a hidden fwd spec decl before a spec decl. + // In that case the previous visible decl can be reached through that + // invisible one. + EXPECT_THAT(Prev, testing::AnyOf( + Current->getPreviousDecl(), + Current->getPreviousDecl()->getPreviousDecl())); + auto *ToTU = Prev->getTranslationUnitDecl(); + auto *TemplateD = FirstDeclMatcher<FunctionTemplateDecl>().match( + ToTU, functionTemplateDecl()); + auto *FirstSpecD = *(TemplateD->spec_begin()); + EXPECT_EQ(FirstSpecD->getCanonicalDecl(), PrevF->getCanonicalDecl()); + return; + } + } - 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(); + // The rest: Classes, Functions, etc. + EXPECT_EQ(Current->getPreviousDecl(), Prev); + } - 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()); -} + void + TypedTest_PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition() { + Decl *FromTU = getTuDecl(getPrototype(), Lang_CXX); + auto *FromD = FirstDeclMatcher<DeclTy>().match(FromTU, getPattern()); + ASSERT_FALSE(FromD->isThisDeclarationADefinition()); + + Decl *ImportedD = Import(FromD, Lang_CXX); + Decl *ToTU = ImportedD->getTranslationUnitDecl(); + + EXPECT_EQ(DeclCounter<DeclTy>().match(ToTU, getPattern()), 1u); + auto *ToD = LastDeclMatcher<DeclTy>().match(ToTU, getPattern()); + EXPECT_TRUE(ImportedD == ToD); + EXPECT_FALSE(ToD->isThisDeclarationADefinition()); + if (auto *ToT = dyn_cast<TemplateDecl>(ToD)) { + EXPECT_TRUE(ToT->getTemplatedDecl()); + } + } + + void TypedTest_DefinitionShouldBeImportedAsADefinition() { + Decl *FromTU = getTuDecl(getDefinition(), Lang_CXX); + auto *FromD = FirstDeclMatcher<DeclTy>().match(FromTU, getPattern()); + ASSERT_TRUE(FromD->isThisDeclarationADefinition()); + + Decl *ImportedD = Import(FromD, Lang_CXX); + Decl *ToTU = ImportedD->getTranslationUnitDecl(); + + EXPECT_EQ(DeclCounter<DeclTy>().match(ToTU, getPattern()), 1u); + auto *ToD = LastDeclMatcher<DeclTy>().match(ToTU, getPattern()); + EXPECT_TRUE(ToD->isThisDeclarationADefinition()); + if (auto *ToT = dyn_cast<TemplateDecl>(ToD)) { + EXPECT_TRUE(ToT->getTemplatedDecl()); + } + } + + void TypedTest_ImportPrototypeAfterImportedPrototype() { + Decl *FromTU = getTuDecl( + getPrototype() + getPrototype(), Lang_CXX); + auto *From0 = + FirstDeclMatcher<DeclTy>().match(FromTU, getPattern()); + auto *From1 = LastDeclMatcher<DeclTy>().match(FromTU, getPattern()); + ASSERT_FALSE(From0->isThisDeclarationADefinition()); + ASSERT_FALSE(From1->isThisDeclarationADefinition()); + + Decl *Imported0 = Import(From0, Lang_CXX); + Decl *Imported1 = Import(From1, Lang_CXX); + Decl *ToTU = Imported0->getTranslationUnitDecl(); + + EXPECT_EQ(DeclCounter<DeclTy>().match(ToTU, getPattern()), 2u); + auto *To0 = FirstDeclMatcher<DeclTy>().match(ToTU, getPattern()); + auto *To1 = LastDeclMatcher<DeclTy>().match(ToTU, getPattern()); + EXPECT_TRUE(Imported0 == To0); + EXPECT_TRUE(Imported1 == To1); + EXPECT_FALSE(To0->isThisDeclarationADefinition()); + EXPECT_FALSE(To1->isThisDeclarationADefinition()); + + CheckPreviousDecl(To0, To1); + } + + void TypedTest_ImportDefinitionAfterImportedPrototype() { + Decl *FromTU = getTuDecl( + getPrototype() + getDefinition(), Lang_CXX); + auto *FromProto = FirstDeclMatcher<DeclTy>().match(FromTU, getPattern()); + auto *FromDef = LastDeclMatcher<DeclTy>().match(FromTU, getPattern()); + ASSERT_FALSE(FromProto->isThisDeclarationADefinition()); + ASSERT_TRUE(FromDef->isThisDeclarationADefinition()); + + Decl *ImportedProto = Import(FromProto, Lang_CXX); + Decl *ImportedDef = Import(FromDef, Lang_CXX); + Decl *ToTU = ImportedProto->getTranslationUnitDecl(); + + EXPECT_EQ(DeclCounter<DeclTy>().match(ToTU, getPattern()), 2u); + auto *ToProto = FirstDeclMatcher<DeclTy>().match(ToTU, getPattern()); + auto *ToDef = LastDeclMatcher<DeclTy>().match(ToTU, getPattern()); + EXPECT_TRUE(ImportedProto == ToProto); + EXPECT_TRUE(ImportedDef == ToDef); + EXPECT_FALSE(ToProto->isThisDeclarationADefinition()); + EXPECT_TRUE(ToDef->isThisDeclarationADefinition()); + + CheckPreviousDecl(ToProto, ToDef); + } + + void TypedTest_ImportPrototypeAfterImportedDefinition() { + Decl *FromTU = getTuDecl( + getDefinition() + getPrototype(), Lang_CXX); + auto *FromDef = FirstDeclMatcher<DeclTy>().match(FromTU, getPattern()); + auto *FromProto = LastDeclMatcher<DeclTy>().match(FromTU, getPattern()); + ASSERT_TRUE(FromDef->isThisDeclarationADefinition()); + ASSERT_FALSE(FromProto->isThisDeclarationADefinition()); + + Decl *ImportedDef = Import(FromDef, Lang_CXX); + Decl *ImportedProto = Import(FromProto, Lang_CXX); + Decl *ToTU = ImportedDef->getTranslationUnitDecl(); + + EXPECT_EQ(DeclCounter<DeclTy>().match(ToTU, getPattern()), 2u); + auto *ToDef = FirstDeclMatcher<DeclTy>().match(ToTU, getPattern()); + auto *ToProto = LastDeclMatcher<DeclTy>().match(ToTU, getPattern()); + EXPECT_TRUE(ImportedDef == ToDef); + EXPECT_TRUE(ImportedProto == ToProto); + EXPECT_TRUE(ToDef->isThisDeclarationADefinition()); + EXPECT_FALSE(ToProto->isThisDeclarationADefinition()); + + CheckPreviousDecl(ToDef, ToProto); + } + + void TypedTest_ImportPrototypes() { + Decl *FromTU0 = getTuDecl(getPrototype(), Lang_CXX, "input0.cc"); + Decl *FromTU1 = getTuDecl(getPrototype(), Lang_CXX, "input1.cc"); + auto *From0 = FirstDeclMatcher<DeclTy>().match(FromTU0, getPattern()); + auto *From1 = FirstDeclMatcher<DeclTy>().match(FromTU1, getPattern()); + ASSERT_FALSE(From0->isThisDeclarationADefinition()); + ASSERT_FALSE(From1->isThisDeclarationADefinition()); + + Decl *Imported0 = Import(From0, Lang_CXX); + Decl *Imported1 = Import(From1, Lang_CXX); + Decl *ToTU = Imported0->getTranslationUnitDecl(); + + EXPECT_EQ(DeclCounter<DeclTy>().match(ToTU, getPattern()), 2u); + auto *To0 = FirstDeclMatcher<DeclTy>().match(ToTU, getPattern()); + auto *To1 = LastDeclMatcher<DeclTy>().match(ToTU, getPattern()); + EXPECT_TRUE(Imported0 == To0); + EXPECT_TRUE(Imported1 == To1); + EXPECT_FALSE(To0->isThisDeclarationADefinition()); + EXPECT_FALSE(To1->isThisDeclarationADefinition()); + + CheckPreviousDecl(To0, To1); + } -struct ImportFriendClasses : ASTImporterTestBase {}; + void TypedTest_ImportDefinitions() { + Decl *FromTU0 = getTuDecl(getDefinition(), Lang_CXX, "input0.cc"); + Decl *FromTU1 = getTuDecl(getDefinition(), Lang_CXX, "input1.cc"); + auto *From0 = FirstDeclMatcher<DeclTy>().match(FromTU0, getPattern()); + auto *From1 = FirstDeclMatcher<DeclTy>().match(FromTU1, getPattern()); + ASSERT_TRUE(From0->isThisDeclarationADefinition()); + ASSERT_TRUE(From1->isThisDeclarationADefinition()); + + Decl *Imported0 = Import(From0, Lang_CXX); + Decl *Imported1 = Import(From1, Lang_CXX); + Decl *ToTU = Imported0->getTranslationUnitDecl(); + + EXPECT_EQ(Imported0, Imported1); + EXPECT_EQ(DeclCounter<DeclTy>().match(ToTU, getPattern()), 1u); + auto *To0 = FirstDeclMatcher<DeclTy>().match(ToTU, getPattern()); + EXPECT_TRUE(Imported0 == To0); + EXPECT_TRUE(To0->isThisDeclarationADefinition()); + if (auto *ToT0 = dyn_cast<TemplateDecl>(To0)) { + EXPECT_TRUE(ToT0->getTemplatedDecl()); + } + } + + void TypedTest_ImportDefinitionThenPrototype() { + Decl *FromTUDef = getTuDecl(getDefinition(), Lang_CXX, "input0.cc"); + Decl *FromTUProto = getTuDecl(getPrototype(), Lang_CXX, "input1.cc"); + auto *FromDef = FirstDeclMatcher<DeclTy>().match(FromTUDef, getPattern()); + auto *FromProto = + FirstDeclMatcher<DeclTy>().match(FromTUProto, getPattern()); + ASSERT_TRUE(FromDef->isThisDeclarationADefinition()); + ASSERT_FALSE(FromProto->isThisDeclarationADefinition()); + + Decl *ImportedDef = Import(FromDef, Lang_CXX); + Decl *ImportedProto = Import(FromProto, Lang_CXX); + Decl *ToTU = ImportedDef->getTranslationUnitDecl(); + + EXPECT_NE(ImportedDef, ImportedProto); + EXPECT_EQ(DeclCounter<DeclTy>().match(ToTU, getPattern()), 2u); + auto *ToDef = FirstDeclMatcher<DeclTy>().match(ToTU, getPattern()); + auto *ToProto = LastDeclMatcher<DeclTy>().match(ToTU, getPattern()); + EXPECT_TRUE(ImportedDef == ToDef); + EXPECT_TRUE(ImportedProto == ToProto); + EXPECT_TRUE(ToDef->isThisDeclarationADefinition()); + EXPECT_FALSE(ToProto->isThisDeclarationADefinition()); + + CheckPreviousDecl(ToDef, ToProto); + } + + void TypedTest_ImportPrototypeThenDefinition() { + Decl *FromTUProto = getTuDecl(getPrototype(), Lang_CXX, "input0.cc"); + Decl *FromTUDef = getTuDecl(getDefinition(), Lang_CXX, "input1.cc"); + auto *FromProto = + FirstDeclMatcher<DeclTy>().match(FromTUProto, getPattern()); + auto *FromDef = FirstDeclMatcher<DeclTy>().match(FromTUDef, getPattern()); + ASSERT_TRUE(FromDef->isThisDeclarationADefinition()); + ASSERT_FALSE(FromProto->isThisDeclarationADefinition()); + + Decl *ImportedProto = Import(FromProto, Lang_CXX); + Decl *ImportedDef = Import(FromDef, Lang_CXX); + Decl *ToTU = ImportedDef->getTranslationUnitDecl(); + + EXPECT_NE(ImportedDef, ImportedProto); + EXPECT_EQ(DeclCounter<DeclTy>().match(ToTU, getPattern()), 2u); + auto *ToProto = FirstDeclMatcher<DeclTy>().match(ToTU, getPattern()); + auto *ToDef = LastDeclMatcher<DeclTy>().match(ToTU, getPattern()); + EXPECT_TRUE(ImportedDef == ToDef); + EXPECT_TRUE(ImportedProto == ToProto); + EXPECT_TRUE(ToDef->isThisDeclarationADefinition()); + EXPECT_FALSE(ToProto->isThisDeclarationADefinition()); + + CheckPreviousDecl(ToProto, ToDef); + } + + void TypedTest_WholeRedeclChainIsImportedAtOnce() { + Decl *FromTU = getTuDecl(getPrototype() + getDefinition(), Lang_CXX); + auto *FromD = // Definition + LastDeclMatcher<DeclTy>().match(FromTU, getPattern()); + ASSERT_TRUE(FromD->isThisDeclarationADefinition()); + + Decl *ImportedD = Import(FromD, Lang_CXX); + Decl *ToTU = ImportedD->getTranslationUnitDecl(); + + // The whole redecl chain is imported at once. + EXPECT_EQ(DeclCounter<DeclTy>().match(ToTU, getPattern()), 2u); + EXPECT_TRUE(cast<DeclTy>(ImportedD)->isThisDeclarationADefinition()); + } + + void TypedTest_ImportPrototypeThenProtoAndDefinition() { + { + Decl *FromTU = getTuDecl(getPrototype(), Lang_CXX, "input0.cc"); + auto *FromD = FirstDeclMatcher<DeclTy>().match(FromTU, getPattern()); + Import(FromD, Lang_CXX); + } + { + Decl *FromTU = + getTuDecl(getPrototype() + getDefinition(), Lang_CXX, "input1.cc"); + auto *FromD = FirstDeclMatcher<DeclTy>().match(FromTU, getPattern()); + Import(FromD, Lang_CXX); + } + + Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + + ASSERT_EQ(DeclCounter<DeclTy>().match(ToTU, getPattern()), 3u); + DeclTy *ProtoD = FirstDeclMatcher<DeclTy>().match(ToTU, getPattern()); + EXPECT_FALSE(ProtoD->isThisDeclarationADefinition()); + + DeclTy *DefinitionD = LastDeclMatcher<DeclTy>().match(ToTU, getPattern()); + EXPECT_TRUE(DefinitionD->isThisDeclarationADefinition()); + + EXPECT_TRUE(DefinitionD->getPreviousDecl()); + EXPECT_FALSE( + DefinitionD->getPreviousDecl()->isThisDeclarationADefinition()); + + CheckPreviousDecl(ProtoD, DefinitionD->getPreviousDecl()); + } +}; + +#define ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(BaseTemplate, TypeParam, \ + NamePrefix, TestCase) \ + using BaseTemplate##TypeParam = BaseTemplate<TypeParam>; \ + TEST_P(BaseTemplate##TypeParam, NamePrefix##TestCase) { \ + TypedTest_##TestCase(); \ + } + +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE( + RedeclChain, Function, , + PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE( + RedeclChain, Class, , + PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE( + RedeclChain, Variable, , + PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE( + RedeclChain, FunctionTemplate, , + PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE( + RedeclChain, ClassTemplate, , + PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE( + RedeclChain, FunctionTemplateSpec, , + PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE( + RedeclChain, ClassTemplateSpec, , + PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition) + +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE( + RedeclChain, Function, , DefinitionShouldBeImportedAsADefinition) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE( + RedeclChain, Class, , DefinitionShouldBeImportedAsADefinition) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE( + RedeclChain, Variable, , DefinitionShouldBeImportedAsADefinition) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE( + RedeclChain, FunctionTemplate, , + DefinitionShouldBeImportedAsADefinition) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE( + RedeclChain, ClassTemplate, , DefinitionShouldBeImportedAsADefinition) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE( + RedeclChain, FunctionTemplateSpec, , + DefinitionShouldBeImportedAsADefinition) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE( + RedeclChain, ClassTemplateSpec, , DefinitionShouldBeImportedAsADefinition) + +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Function, , + ImportPrototypeAfterImportedPrototype) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Class, , + ImportPrototypeAfterImportedPrototype) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Variable, , + ImportPrototypeAfterImportedPrototype) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplate, , + ImportPrototypeAfterImportedPrototype) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplate, , + ImportPrototypeAfterImportedPrototype) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, , + ImportPrototypeAfterImportedPrototype) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplateSpec, , + ImportPrototypeAfterImportedPrototype) + +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Function, , + ImportDefinitionAfterImportedPrototype) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Class, , + ImportDefinitionAfterImportedPrototype) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Variable, , + ImportDefinitionAfterImportedPrototype) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplate, , + ImportDefinitionAfterImportedPrototype) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplate, , + ImportDefinitionAfterImportedPrototype) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, , + ImportDefinitionAfterImportedPrototype) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplateSpec, , + ImportDefinitionAfterImportedPrototype) + +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Function, , + ImportPrototypeAfterImportedDefinition) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Class, , + ImportPrototypeAfterImportedDefinition) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Variable, , + ImportPrototypeAfterImportedDefinition) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplate, , + ImportPrototypeAfterImportedDefinition) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplate, , + ImportPrototypeAfterImportedDefinition) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, , + ImportPrototypeAfterImportedDefinition) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplateSpec, , + ImportPrototypeAfterImportedDefinition) + +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Function, , + ImportPrototypes) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Class, , ImportPrototypes) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Variable, , + ImportPrototypes) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplate, , + ImportPrototypes) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplate, , + ImportPrototypes) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplateSpec, , + ImportPrototypes) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, , + ImportPrototypes) + +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Function, , + ImportDefinitions) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Class, , + ImportDefinitions) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Variable, , + ImportDefinitions) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplate, , + ImportDefinitions) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplate, , + ImportDefinitions) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplateSpec, , + ImportDefinitions) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, , + ImportDefinitions) + +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Function, , + ImportDefinitionThenPrototype) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Class, , + ImportDefinitionThenPrototype) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Variable, , + ImportDefinitionThenPrototype) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplate, , + ImportDefinitionThenPrototype) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplate, , + ImportDefinitionThenPrototype) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, , + ImportDefinitionThenPrototype) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplateSpec, , + ImportDefinitionThenPrototype) + +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Function, , + ImportPrototypeThenDefinition) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Class, , + ImportPrototypeThenDefinition) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Variable, , + ImportPrototypeThenDefinition) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplate, , + ImportPrototypeThenDefinition) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplate, , + ImportPrototypeThenDefinition) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, , + ImportPrototypeThenDefinition) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, ClassTemplateSpec, , + ImportPrototypeThenDefinition) + +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Function, , + WholeRedeclChainIsImportedAtOnce) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Variable, , + WholeRedeclChainIsImportedAtOnce) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplate, , + WholeRedeclChainIsImportedAtOnce) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, , + WholeRedeclChainIsImportedAtOnce) + +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Function, , + ImportPrototypeThenProtoAndDefinition) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, Variable, , + ImportPrototypeThenProtoAndDefinition) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplate, , + ImportPrototypeThenProtoAndDefinition) +ASTIMPORTER_INSTANTIATE_TYPED_TEST_CASE(RedeclChain, FunctionTemplateSpec, , + ImportPrototypeThenProtoAndDefinition) + +INSTANTIATE_TEST_CASE_P(ParameterizedTests, RedeclChainFunction, + DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P(ParameterizedTests, RedeclChainClass, + DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P(ParameterizedTests, RedeclChainVariable, + DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P(ParameterizedTests, RedeclChainFunctionTemplate, + DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P(ParameterizedTests, RedeclChainClassTemplate, + DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P(ParameterizedTests, RedeclChainFunctionTemplateSpec, + DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P(ParameterizedTests, RedeclChainClassTemplateSpec, + DefaultTestValuesForRunOptions, ); + + + +struct ImportFriendClasses : ASTImporterOptionSpecificTestBase {}; TEST_P(ImportFriendClasses, ImportOfFriendRecordDoesNotMergeDefinition) { Decl *FromTU = getTuDecl( @@ -3982,7 +4792,7 @@ TEST_P(ImportFriendClasses, ImportOfClassDefinitionAndFwdFriendShouldBeLinked) { EXPECT_EQ(ImportedFwd, ImportedDef->getPreviousDecl()); } -TEST_P(ASTImporterTestBase, FriendFunInClassTemplate) { +TEST_P(ASTImporterOptionSpecificTestBase, FriendFunInClassTemplate) { auto *Code = R"( template <class T> struct X { @@ -4000,7 +4810,7 @@ TEST_P(ASTImporterTestBase, FriendFunInClassTemplate) { EXPECT_EQ(ImportedFoo, ToFoo); } -struct DeclContextTest : ASTImporterTestBase {}; +struct DeclContextTest : ASTImporterOptionSpecificTestBase {}; TEST_P(DeclContextTest, removeDeclOfClassTemplateSpecialization) { Decl *TU = getTuDecl( @@ -4063,7 +4873,8 @@ TEST_P(DeclContextTest, EXPECT_FALSE(DC->containsDecl(A0)); } -struct ImportFunctionTemplateSpecializations : ASTImporterTestBase {}; +struct ImportFunctionTemplateSpecializations + : ASTImporterOptionSpecificTestBase {}; TEST_P(ImportFunctionTemplateSpecializations, TUshouldNotContainFunctionTemplateImplicitInstantiation) { @@ -4200,185 +5011,7 @@ TEST_P(ImportFunctionTemplateSpecializations, DeclCounter<FunctionDecl>().match(ToTU, functionDecl(hasName("f")))); } -TEST_P(ImportFunctionTemplateSpecializations, - ImportPrototypes) { - auto Pattern = functionDecl(hasName("f"), isExplicitTemplateSpecialization()); - auto Code = - R"( - // Proto of the primary template. - template <class T> - void f(); - // Proto of the specialization. - template <> - void f<int>(); - )"; - - Decl *ImportedD; - { - Decl *FromTU = getTuDecl(Code, Lang_CXX, "input0.cc"); - auto *FromD = LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern); - - ImportedD = Import(FromD, Lang_CXX); - } - { - Decl *FromTU = getTuDecl(Code, Lang_CXX, "input1.cc"); - auto *FromD = LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern); - Import(FromD, Lang_CXX); - } - - Decl *ToTU = ImportedD->getTranslationUnitDecl(); - - EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u); - auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern); - auto *To1 = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern); - EXPECT_TRUE(ImportedD == To0); - EXPECT_TRUE(ImportedD != To1); - EXPECT_FALSE(To0->doesThisDeclarationHaveABody()); - EXPECT_FALSE(To1->doesThisDeclarationHaveABody()); - // Check that they are part of the same redecl chain. - EXPECT_EQ(To1->getCanonicalDecl(), To0->getCanonicalDecl()); -} - -TEST_P(ImportFunctionTemplateSpecializations, ImportDefinitions) { - auto Pattern = functionDecl(hasName("f"), isExplicitTemplateSpecialization()); - auto Code = - R"( - // Proto of the primary template. - template <class T> - void f(); - // Specialization and definition. - template <> - void f<int>() {} - )"; - - Decl *ImportedD; - { - Decl *FromTU = getTuDecl(Code, Lang_CXX, "input0.cc"); - auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); - ImportedD = Import(FromD, Lang_CXX); - } - { - Decl *FromTU = getTuDecl(Code, Lang_CXX, "input1.cc"); - auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); - Import(FromD, Lang_CXX); - } - - Decl *ToTU = ImportedD->getTranslationUnitDecl(); - - EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u); - auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern); - EXPECT_TRUE(ImportedD == To0); - EXPECT_TRUE(To0->doesThisDeclarationHaveABody()); - - auto *TemplateD = FirstDeclMatcher<FunctionTemplateDecl>().match( - ToTU, functionTemplateDecl()); - auto *FirstSpecD = *(TemplateD->spec_begin()); - EXPECT_EQ(FirstSpecD->getCanonicalDecl(), To0->getCanonicalDecl()); -} - -TEST_P(ImportFunctionTemplateSpecializations, PrototypeThenPrototype) { - auto Pattern = functionDecl(hasName("f"), isExplicitTemplateSpecialization()); - auto Code = - R"( - // Proto of the primary template. - template <class T> - void f(); - // Specialization proto. - template <> - void f<int>(); - // Specialization proto. - template <> - void f<int>(); - )"; - - Decl *ImportedD; - { - Decl *FromTU = getTuDecl(Code, Lang_CXX, "input0.cc"); - auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); - ImportedD = Import(FromD, Lang_CXX); - } - - Decl *ToTU = ImportedD->getTranslationUnitDecl(); - - EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u); - auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern); - auto *To1 = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern); - EXPECT_TRUE(ImportedD == To0); - EXPECT_TRUE(ImportedD != To1); - EXPECT_FALSE(To0->doesThisDeclarationHaveABody()); - EXPECT_FALSE(To1->doesThisDeclarationHaveABody()); - EXPECT_EQ(To1->getPreviousDecl(), To0); -} - -TEST_P(ImportFunctionTemplateSpecializations, PrototypeThenDefinition) { - auto Pattern = functionDecl(hasName("f"), isExplicitTemplateSpecialization()); - auto Code = - R"( - // Proto of the primary template. - template <class T> - void f(); - // Specialization proto. - template <> - void f<int>(); - // Specialization definition. - template <> - void f<int>() {} - )"; - - Decl *ImportedD; - { - Decl *FromTU = getTuDecl(Code, Lang_CXX, "input0.cc"); - auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); - ImportedD = Import(FromD, Lang_CXX); - } - - Decl *ToTU = ImportedD->getTranslationUnitDecl(); - - EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u); - auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern); - auto *To1 = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern); - EXPECT_TRUE(ImportedD == To0); - EXPECT_TRUE(ImportedD != To1); - EXPECT_FALSE(To0->doesThisDeclarationHaveABody()); - EXPECT_TRUE(To1->doesThisDeclarationHaveABody()); - EXPECT_EQ(To1->getPreviousDecl(), To0); -} - -TEST_P(ImportFunctionTemplateSpecializations, DefinitionThenPrototype) { - auto Pattern = functionDecl(hasName("f"), isExplicitTemplateSpecialization()); - auto Code = - R"( - // Proto of the primary template. - template <class T> - void f(); - // Specialization definition. - template <> - void f<int>() {} - // Specialization proto. - template <> - void f<int>(); - )"; - - Decl *ImportedD; - { - Decl *FromTU = getTuDecl(Code, Lang_CXX, "input0.cc"); - auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); - ImportedD = Import(FromD, Lang_CXX); - } - - Decl *ToTU = ImportedD->getTranslationUnitDecl(); - - EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u); - auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern); - auto *To1 = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern); - EXPECT_TRUE(ImportedD == To0); - EXPECT_TRUE(ImportedD != To1); - EXPECT_TRUE(To0->doesThisDeclarationHaveABody()); - EXPECT_FALSE(To1->doesThisDeclarationHaveABody()); - EXPECT_EQ(To1->getPreviousDecl(), To0); -} - -TEST_P(ASTImporterTestBase, +TEST_P(ASTImporterOptionSpecificTestBase, ImportShouldNotReportFalseODRErrorWhenRecordIsBeingDefined) { { Decl *FromTU = getTuDecl( @@ -4417,7 +5050,8 @@ TEST_P(ASTImporterTestBase, } } -TEST_P(ASTImporterTestBase, ImportingTypedefShouldImportTheCompleteType) { +TEST_P(ASTImporterOptionSpecificTestBase, + ImportingTypedefShouldImportTheCompleteType) { // We already have an incomplete underlying type in the "To" context. auto Code = R"( @@ -4449,7 +5083,7 @@ TEST_P(ASTImporterTestBase, ImportingTypedefShouldImportTheCompleteType) { EXPECT_FALSE(ImportedD->getUnderlyingType()->isIncompleteType()); } -struct ASTImporterLookupTableTest : ASTImporterTestBase {}; +struct ASTImporterLookupTableTest : ASTImporterOptionSpecificTestBase {}; TEST_P(ASTImporterLookupTableTest, OneDecl) { auto *ToTU = getToTuDecl("int a;", Lang_CXX); @@ -4595,6 +5229,66 @@ TEST_P(ASTImporterLookupTableTest, LookupFindsOverloadedNames) { EXPECT_EQ(Res.count(F2), 1u); } +TEST_P(ASTImporterLookupTableTest, + DifferentOperatorsShouldHaveDifferentResultSet) { + TranslationUnitDecl *ToTU = getToTuDecl( + R"( + struct X{}; + void operator+(X, X); + void operator-(X, X); + )", + Lang_CXX); + + ASTImporterLookupTable LT(*ToTU); + auto *FPlus = FirstDeclMatcher<FunctionDecl>().match( + ToTU, functionDecl(hasOverloadedOperatorName("+"))); + auto *FMinus = FirstDeclMatcher<FunctionDecl>().match( + ToTU, functionDecl(hasOverloadedOperatorName("-"))); + DeclarationName NamePlus = FPlus->getDeclName(); + auto ResPlus = LT.lookup(ToTU, NamePlus); + EXPECT_EQ(ResPlus.size(), 1u); + EXPECT_EQ(ResPlus.count(FPlus), 1u); + EXPECT_EQ(ResPlus.count(FMinus), 0u); + DeclarationName NameMinus = FMinus->getDeclName(); + auto ResMinus = LT.lookup(ToTU, NameMinus); + EXPECT_EQ(ResMinus.size(), 1u); + EXPECT_EQ(ResMinus.count(FMinus), 1u); + EXPECT_EQ(ResMinus.count(FPlus), 0u); + EXPECT_NE(*ResMinus.begin(), *ResPlus.begin()); +} + +TEST_P(ASTImporterLookupTableTest, LookupDeclNamesFromDifferentTUs) { + TranslationUnitDecl *ToTU = getToTuDecl( + R"( + struct X {}; + void operator+(X, X); + )", + Lang_CXX); + auto *ToPlus = FirstDeclMatcher<FunctionDecl>().match( + ToTU, functionDecl(hasOverloadedOperatorName("+"))); + + Decl *FromTU = getTuDecl( + R"( + struct X {}; + void operator+(X, X); + )", + Lang_CXX); + auto *FromPlus = FirstDeclMatcher<FunctionDecl>().match( + FromTU, functionDecl(hasOverloadedOperatorName("+"))); + + // FromPlus have a different TU, thus its DeclarationName is different too. + ASSERT_NE(ToPlus->getDeclName(), FromPlus->getDeclName()); + + ASTImporterLookupTable LT(*ToTU); + auto Res = LT.lookup(ToTU, ToPlus->getDeclName()); + ASSERT_EQ(Res.size(), 1u); + EXPECT_EQ(*Res.begin(), ToPlus); + + // FromPlus have a different TU, thus its DeclarationName is different too. + Res = LT.lookup(ToTU, FromPlus->getDeclName()); + ASSERT_EQ(Res.size(), 0u); +} + static const RecordDecl * getRecordDeclOfFriend(FriendDecl *FD) { QualType Ty = FD->getFriendType()->getType(); QualType NamedTy = cast<ElaboratedType>(Ty)->getNamedType(); @@ -4866,11 +5560,71 @@ INSTANTIATE_TEST_CASE_P( ParameterizedTests, CanonicalRedeclChain, ::testing::Values(ArgVector()),); -auto DefaultTestValuesForRunOptions = ::testing::Values( - ArgVector(), - ArgVector{"-fdelayed-template-parsing"}, - ArgVector{"-fms-compatibility"}, - ArgVector{"-fdelayed-template-parsing", "-fms-compatibility"}); +// FIXME This test is disabled currently, upcoming patches will make it +// possible to enable. +TEST_P(ASTImporterOptionSpecificTestBase, + DISABLED_RedeclChainShouldBeCorrectAmongstNamespaces) { + Decl *FromTU = getTuDecl( + R"( + namespace NS { + struct X; + struct Y { + static const int I = 3; + }; + } + namespace NS { + struct X { // <--- To be imported + void method(int i = Y::I) {} + int f; + }; + } + )", + Lang_CXX); + auto *FromFwd = FirstDeclMatcher<CXXRecordDecl>().match( + FromTU, cxxRecordDecl(hasName("X"), unless(isImplicit()))); + auto *FromDef = LastDeclMatcher<CXXRecordDecl>().match( + FromTU, + cxxRecordDecl(hasName("X"), isDefinition(), unless(isImplicit()))); + ASSERT_NE(FromFwd, FromDef); + ASSERT_FALSE(FromFwd->isThisDeclarationADefinition()); + ASSERT_TRUE(FromDef->isThisDeclarationADefinition()); + ASSERT_EQ(FromFwd->getCanonicalDecl(), FromDef->getCanonicalDecl()); + + auto *ToDef = cast_or_null<CXXRecordDecl>(Import(FromDef, Lang_CXX)); + auto *ToFwd = cast_or_null<CXXRecordDecl>(Import(FromFwd, Lang_CXX)); + EXPECT_NE(ToFwd, ToDef); + EXPECT_FALSE(ToFwd->isThisDeclarationADefinition()); + EXPECT_TRUE(ToDef->isThisDeclarationADefinition()); + EXPECT_EQ(ToFwd->getCanonicalDecl(), ToDef->getCanonicalDecl()); + auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + // We expect no (ODR) warning during the import. + EXPECT_EQ(0u, ToTU->getASTContext().getDiagnostics().getNumWarnings()); +} + +struct ImportFriendFunctionTemplates : ASTImporterOptionSpecificTestBase {}; + +TEST_P(ImportFriendFunctionTemplates, LookupShouldFindPreviousFriend) { + Decl *ToTU = getToTuDecl( + R"( + class X { + template <typename T> friend void foo(); + }; + )", + Lang_CXX); + auto *Friend = FirstDeclMatcher<FunctionTemplateDecl>().match( + ToTU, functionTemplateDecl(hasName("foo"))); + + Decl *FromTU = getTuDecl( + R"( + template <typename T> void foo(); + )", + Lang_CXX); + auto *FromFoo = FirstDeclMatcher<FunctionTemplateDecl>().match( + FromTU, functionTemplateDecl(hasName("foo"))); + auto *Imported = Import(FromFoo, Lang_CXX); + + EXPECT_EQ(Imported->getPreviousDecl(), Friend); +} INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterLookupTableTest, DefaultTestValuesForRunOptions, ); @@ -4884,19 +5638,22 @@ INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportType, INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportDecl, DefaultTestValuesForRunOptions, ); -INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterTestBase, +INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterOptionSpecificTestBase, + DefaultTestValuesForRunOptions, ); + +INSTANTIATE_TEST_CASE_P(ParameterizedTests, RedirectingImporterTest, DefaultTestValuesForRunOptions, ); INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctions, DefaultTestValuesForRunOptions, ); -INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClasses, +INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendFunctionTemplates, DefaultTestValuesForRunOptions, ); -INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendFunctions, +INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClasses, DefaultTestValuesForRunOptions, ); -INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClassTemplates, +INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendFunctions, DefaultTestValuesForRunOptions, ); INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendClasses, diff --git a/unittests/AST/ASTPrint.h b/unittests/AST/ASTPrint.h new file mode 100644 index 0000000000..c3b6b84231 --- /dev/null +++ b/unittests/AST/ASTPrint.h @@ -0,0 +1,92 @@ +//===- unittests/AST/ASTPrint.h ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Helpers to simplify testing of printing of AST constructs provided in the/ +// form of the source code. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/ADT/SmallString.h" +#include "gtest/gtest.h" + +namespace clang { + +using PolicyAdjusterType = + Optional<llvm::function_ref<void(PrintingPolicy &Policy)>>; + +static void PrintStmt(raw_ostream &Out, const ASTContext *Context, + const Stmt *S, PolicyAdjusterType PolicyAdjuster) { + assert(S != nullptr && "Expected non-null Stmt"); + PrintingPolicy Policy = Context->getPrintingPolicy(); + if (PolicyAdjuster) + (*PolicyAdjuster)(Policy); + S->printPretty(Out, /*Helper*/ nullptr, Policy); +} + +class PrintMatch : public ast_matchers::MatchFinder::MatchCallback { + SmallString<1024> Printed; + unsigned NumFoundStmts; + PolicyAdjusterType PolicyAdjuster; + +public: + PrintMatch(PolicyAdjusterType PolicyAdjuster) + : NumFoundStmts(0), PolicyAdjuster(PolicyAdjuster) {} + + void run(const ast_matchers::MatchFinder::MatchResult &Result) override { + const Stmt *S = Result.Nodes.getNodeAs<Stmt>("id"); + if (!S) + return; + NumFoundStmts++; + if (NumFoundStmts > 1) + return; + + llvm::raw_svector_ostream Out(Printed); + PrintStmt(Out, Result.Context, S, PolicyAdjuster); + } + + StringRef getPrinted() const { return Printed; } + + unsigned getNumFoundStmts() const { return NumFoundStmts; } +}; + +template <typename T> +::testing::AssertionResult +PrintedStmtMatches(StringRef Code, const std::vector<std::string> &Args, + const T &NodeMatch, StringRef ExpectedPrinted, + PolicyAdjusterType PolicyAdjuster = None) { + + PrintMatch Printer(PolicyAdjuster); + ast_matchers::MatchFinder Finder; + Finder.addMatcher(NodeMatch, &Printer); + std::unique_ptr<tooling::FrontendActionFactory> Factory( + tooling::newFrontendActionFactory(&Finder)); + + if (!tooling::runToolOnCodeWithArgs(Factory->create(), Code, Args)) + return testing::AssertionFailure() + << "Parsing error in \"" << Code.str() << "\""; + + if (Printer.getNumFoundStmts() == 0) + return testing::AssertionFailure() << "Matcher didn't find any statements"; + + if (Printer.getNumFoundStmts() > 1) + return testing::AssertionFailure() + << "Matcher should match only one statement (found " + << Printer.getNumFoundStmts() << ")"; + + if (Printer.getPrinted() != ExpectedPrinted) + return ::testing::AssertionFailure() + << "Expected \"" << ExpectedPrinted.str() << "\", got \"" + << Printer.getPrinted().str() << "\""; + + return ::testing::AssertionSuccess(); +} + +} // namespace clang diff --git a/unittests/AST/ASTTypeTraitsTest.cpp b/unittests/AST/ASTTypeTraitsTest.cpp index 722c468f30..2313f9f6df 100644 --- a/unittests/AST/ASTTypeTraitsTest.cpp +++ b/unittests/AST/ASTTypeTraitsTest.cpp @@ -1,9 +1,8 @@ //===- unittest/AST/ASTTypeTraits.cpp - AST type traits unit tests ------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===--------------------------------------------------------------------===// diff --git a/unittests/AST/ASTVectorTest.cpp b/unittests/AST/ASTVectorTest.cpp index 359d2f4232..f5b208ab16 100644 --- a/unittests/AST/ASTVectorTest.cpp +++ b/unittests/AST/ASTVectorTest.cpp @@ -1,9 +1,8 @@ //===- unittests/AST/DeclTest.cpp --- Declaration tests -------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/unittests/AST/CMakeLists.txt b/unittests/AST/CMakeLists.txt index c416e5b996..25ebd37ef3 100644 --- a/unittests/AST/CMakeLists.txt +++ b/unittests/AST/CMakeLists.txt @@ -21,6 +21,7 @@ add_clang_unittest(ASTTests ExternalASTSourceTest.cpp Language.cpp NamedDeclPrinterTest.cpp + OMPStructuredBlockTest.cpp SourceLocationTest.cpp StmtPrinterTest.cpp StructuralEquivalenceTest.cpp diff --git a/unittests/AST/CommentLexer.cpp b/unittests/AST/CommentLexer.cpp index f96d6cd15f..1883050658 100644 --- a/unittests/AST/CommentLexer.cpp +++ b/unittests/AST/CommentLexer.cpp @@ -1,9 +1,8 @@ //===- unittests/AST/CommentLexer.cpp ------ Comment lexer tests ----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/AST/CommentParser.cpp b/unittests/AST/CommentParser.cpp index a185f73971..d1f732cb5f 100644 --- a/unittests/AST/CommentParser.cpp +++ b/unittests/AST/CommentParser.cpp @@ -1,9 +1,8 @@ //===- unittests/AST/CommentParser.cpp ------ Comment parser tests --------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/AST/CommentTextTest.cpp b/unittests/AST/CommentTextTest.cpp index 5fb779535f..3de6758e45 100644 --- a/unittests/AST/CommentTextTest.cpp +++ b/unittests/AST/CommentTextTest.cpp @@ -1,9 +1,8 @@ //===- unittest/AST/CommentTextTest.cpp - Comment text extraction test ----===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/unittests/AST/DataCollectionTest.cpp b/unittests/AST/DataCollectionTest.cpp index e8ebd16217..b732a445d9 100644 --- a/unittests/AST/DataCollectionTest.cpp +++ b/unittests/AST/DataCollectionTest.cpp @@ -1,9 +1,8 @@ //===- unittests/AST/DataCollectionTest.cpp -------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/unittests/AST/DeclMatcher.h b/unittests/AST/DeclMatcher.h index 602f8dff07..a7698aab76 100644 --- a/unittests/AST/DeclMatcher.h +++ b/unittests/AST/DeclMatcher.h @@ -1,9 +1,8 @@ //===- unittest/AST/DeclMatcher.h - AST unit test support ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/AST/DeclPrinterTest.cpp b/unittests/AST/DeclPrinterTest.cpp index 4cf8bce20e..c003e361ef 100644 --- a/unittests/AST/DeclPrinterTest.cpp +++ b/unittests/AST/DeclPrinterTest.cpp @@ -1,9 +1,8 @@ //===- unittests/AST/DeclPrinterTest.cpp --- Declaration printer tests ----===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/unittests/AST/DeclTest.cpp b/unittests/AST/DeclTest.cpp index 87aeef47c6..6691952b2f 100644 --- a/unittests/AST/DeclTest.cpp +++ b/unittests/AST/DeclTest.cpp @@ -1,9 +1,8 @@ //===- unittests/AST/DeclTest.cpp --- Declaration tests -------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/unittests/AST/EvaluateAsRValueTest.cpp b/unittests/AST/EvaluateAsRValueTest.cpp index 820edbc7c3..e737507abd 100644 --- a/unittests/AST/EvaluateAsRValueTest.cpp +++ b/unittests/AST/EvaluateAsRValueTest.cpp @@ -1,9 +1,8 @@ //===- unittests/AST/EvaluateAsRValueTest.cpp -----------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/unittests/AST/ExternalASTSourceTest.cpp b/unittests/AST/ExternalASTSourceTest.cpp index 513ff5b99f..3a0fe01ec0 100644 --- a/unittests/AST/ExternalASTSourceTest.cpp +++ b/unittests/AST/ExternalASTSourceTest.cpp @@ -1,9 +1,8 @@ //===- unittest/AST/ExternalASTSourceTest.cpp -----------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/unittests/AST/Language.cpp b/unittests/AST/Language.cpp index 5d1664019c..210014b3cd 100644 --- a/unittests/AST/Language.cpp +++ b/unittests/AST/Language.cpp @@ -1,9 +1,8 @@ //===------ unittest/AST/Language.cpp - AST unit test support -------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/unittests/AST/Language.h b/unittests/AST/Language.h index 0eb2fb2417..cd19fb7b09 100644 --- a/unittests/AST/Language.h +++ b/unittests/AST/Language.h @@ -1,9 +1,8 @@ //===------ unittest/AST/Language.h - AST unit test support ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/unittests/AST/MatchVerifier.h b/unittests/AST/MatchVerifier.h index 3e94d539f7..1d1bf57db1 100644 --- a/unittests/AST/MatchVerifier.h +++ b/unittests/AST/MatchVerifier.h @@ -1,9 +1,8 @@ //===- unittest/AST/MatchVerifier.h - AST unit test support ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/unittests/AST/NamedDeclPrinterTest.cpp b/unittests/AST/NamedDeclPrinterTest.cpp index 5715a341d8..a50626517f 100644 --- a/unittests/AST/NamedDeclPrinterTest.cpp +++ b/unittests/AST/NamedDeclPrinterTest.cpp @@ -1,9 +1,8 @@ //===- unittests/AST/NamedDeclPrinterTest.cpp --- NamedDecl printer tests -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -116,6 +115,18 @@ PrintedWrittenNamedDeclCXX11Matches(StringRef Code, StringRef DeclName, "input.cc"); } +::testing::AssertionResult +PrintedWrittenPropertyDeclObjCMatches(StringRef Code, StringRef DeclName, + StringRef ExpectedPrinted) { + std::vector<std::string> Args{"-std=c++11", "-xobjective-c++"}; + return PrintedNamedDeclMatches(Code, + Args, + /*SuppressUnwrittenScope*/ true, + objcPropertyDecl(hasName(DeclName)).bind("id"), + ExpectedPrinted, + "input.m"); +} + } // unnamed namespace TEST(NamedDeclPrinter, TestNamespace1) { @@ -180,3 +191,35 @@ TEST(NamedDeclPrinter, TestLinkageInNamespace) { "A", "X::A")); } + +TEST(NamedDeclPrinter, TestObjCClassExtension) { + const char *Code = +R"( + @interface Obj + @end + + @interface Obj () + @property(nonatomic) int property; + @end +)"; + ASSERT_TRUE(PrintedWrittenPropertyDeclObjCMatches( + Code, + "property", + "Obj::property")); +} + +TEST(NamedDeclPrinter, TestObjCClassExtensionWithGetter) { + const char *Code = +R"( + @interface Obj + @end + + @interface Obj () + @property(nonatomic, getter=myPropertyGetter) int property; + @end +)"; + ASSERT_TRUE(PrintedWrittenPropertyDeclObjCMatches( + Code, + "property", + "Obj::property")); +} diff --git a/unittests/AST/OMPStructuredBlockTest.cpp b/unittests/AST/OMPStructuredBlockTest.cpp new file mode 100644 index 0000000000..f4a3fad4a1 --- /dev/null +++ b/unittests/AST/OMPStructuredBlockTest.cpp @@ -0,0 +1,540 @@ +//===- unittests/AST/OMPStructuredBlockTest.cpp ---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Fine-grained tests for IsOMPStructuredBlock bit of Stmt. +// +//===----------------------------------------------------------------------===// + +#include "ASTPrint.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/StmtOpenMP.h" +#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" + +using namespace clang; +using namespace ast_matchers; +using namespace tooling; + +namespace { + +const ast_matchers::internal::VariadicDynCastAllOfMatcher< + OMPExecutableDirective, OMPTargetDirective> + ompTargetDirective; + +StatementMatcher OMPInnermostStructuredBlockMatcher() { + return stmt(isOMPStructuredBlock(), + unless(hasDescendant(stmt(isOMPStructuredBlock())))) + .bind("id"); +} + +StatementMatcher OMPStandaloneDirectiveMatcher() { + return stmt(ompExecutableDirective(isStandaloneDirective())).bind("id"); +} + +template <typename T> +::testing::AssertionResult +PrintedOMPStmtMatches(StringRef Code, const T &NodeMatch, + StringRef ExpectedPrinted, + PolicyAdjusterType PolicyAdjuster = None) { + std::vector<std::string> Args = { + "-fopenmp=libomp", + }; + return PrintedStmtMatches(Code, Args, NodeMatch, ExpectedPrinted, + PolicyAdjuster); +} + +static testing::AssertionResult NoMatches(StringRef Code, + const StatementMatcher &StmtMatch) { + PrintMatch Printer((PolicyAdjusterType())); + MatchFinder Finder; + Finder.addMatcher(StmtMatch, &Printer); + std::unique_ptr<FrontendActionFactory> Factory( + newFrontendActionFactory(&Finder)); + if (!runToolOnCode(Factory->create(), Code)) + return testing::AssertionFailure() + << "Parsing error in \"" << Code.str() << "\""; + if (Printer.getNumFoundStmts() == 0) + return testing::AssertionSuccess(); + return testing::AssertionFailure() + << "Matcher should match only zero statements (found " + << Printer.getNumFoundStmts() << ")"; +} + +} // unnamed namespace + +TEST(OMPStructuredBlock, TestAtomic) { + const char *Source = + R"( +void test(int i) { +#pragma omp atomic +++i; +})"; + ASSERT_TRUE(PrintedOMPStmtMatches( + Source, OMPInnermostStructuredBlockMatcher(), "++i")); +} + +TEST(OMPStructuredBlock, TestBarrier) { + const char *Source = + R"( +void test() { +#pragma omp barrier +})"; + ASSERT_TRUE(PrintedOMPStmtMatches(Source, OMPStandaloneDirectiveMatcher(), + "#pragma omp barrier\n")); + ASSERT_TRUE(NoMatches(Source, OMPInnermostStructuredBlockMatcher())); +} + +TEST(OMPStructuredBlock, TestCancel) { + const char *Source = + R"( +void test() { +#pragma omp parallel +{ + #pragma omp cancel parallel +} +})"; + const char *Expected = R"({ + #pragma omp cancel parallel +} +)"; + ASSERT_TRUE(PrintedOMPStmtMatches( + Source, OMPInnermostStructuredBlockMatcher(), Expected)); + ASSERT_TRUE(PrintedOMPStmtMatches(Source, OMPStandaloneDirectiveMatcher(), + "#pragma omp cancel parallel\n")); +} + +TEST(OMPStructuredBlock, TestCancellationPoint) { + const char *Source = + R"( +void test() { +#pragma omp parallel +{ + #pragma omp cancellation point parallel +} +})"; + const char *Expected = R"({ + #pragma omp cancellation point parallel +} +)"; + ASSERT_TRUE(PrintedOMPStmtMatches( + Source, OMPInnermostStructuredBlockMatcher(), Expected)); + ASSERT_TRUE( + PrintedOMPStmtMatches(Source, OMPStandaloneDirectiveMatcher(), + "#pragma omp cancellation point parallel\n")); +} + +TEST(OMPStructuredBlock, TestCritical) { + const char *Source = + R"( +void test() { +#pragma omp critical +; +})"; + ASSERT_TRUE(PrintedOMPStmtMatches( + Source, OMPInnermostStructuredBlockMatcher(), ";\n")); +} + +//----------------------------------------------------------------------------// +// Loop tests +//----------------------------------------------------------------------------// + +class OMPStructuredBlockLoop : public ::testing::TestWithParam<const char *> {}; + +TEST_P(OMPStructuredBlockLoop, TestDirective0) { + const std::string Source = + R"( +void test(int x) { +#pragma omp )" + + std::string(GetParam()) + R"( +for (int i = 0; i < x; i++) +; +})"; + ASSERT_TRUE(PrintedOMPStmtMatches( + Source, OMPInnermostStructuredBlockMatcher(), ";\n")); +} + +TEST_P(OMPStructuredBlockLoop, TestDirective1) { + const std::string Source = + R"( +void test(int x, int y) { +#pragma omp )" + + std::string(GetParam()) + R"( +for (int i = 0; i < x; i++) +for (int i = 0; i < y; i++) +; +})"; + ASSERT_TRUE(PrintedOMPStmtMatches(Source, + OMPInnermostStructuredBlockMatcher(), + "for (int i = 0; i < y; i++)\n ;\n")); +} + +TEST_P(OMPStructuredBlockLoop, TestDirectiveCollapse1) { + const std::string Source = + R"( +void test(int x, int y) { +#pragma omp )" + + std::string(GetParam()) + R"( collapse(1) +for (int i = 0; i < x; i++) +for (int i = 0; i < y; i++) +; +})"; + ASSERT_TRUE(PrintedOMPStmtMatches(Source, + OMPInnermostStructuredBlockMatcher(), + "for (int i = 0; i < y; i++)\n ;\n")); +} + +TEST_P(OMPStructuredBlockLoop, TestDirectiveCollapse2) { + const std::string Source = + R"( +void test(int x, int y) { +#pragma omp )" + + std::string(GetParam()) + R"( collapse(2) +for (int i = 0; i < x; i++) +for (int i = 0; i < y; i++) +; +})"; + ASSERT_TRUE(PrintedOMPStmtMatches( + Source, OMPInnermostStructuredBlockMatcher(), ";\n")); +} + +TEST_P(OMPStructuredBlockLoop, TestDirectiveCollapse22) { + const std::string Source = + R"( +void test(int x, int y, int z) { +#pragma omp )" + + std::string(GetParam()) + R"( collapse(2) +for (int i = 0; i < x; i++) +for (int i = 0; i < y; i++) +for (int i = 0; i < z; i++) +; +})"; + ASSERT_TRUE(PrintedOMPStmtMatches(Source, + OMPInnermostStructuredBlockMatcher(), + "for (int i = 0; i < z; i++)\n ;\n")); +} + +INSTANTIATE_TEST_CASE_P( + OMPStructuredBlockLoopDirectives, OMPStructuredBlockLoop, + ::testing::Values("simd", "for", "for simd", "parallel for", + "parallel for simd", "target parallel for", "taskloop", + "taskloop simd", "distribute", "distribute parallel for", + "distribute parallel for simd", "distribute simd", + "target parallel for simd", "target simd", + "target\n#pragma omp teams distribute", + "target\n#pragma omp teams distribute simd", + "target\n#pragma omp teams distribute parallel for simd", + "target\n#pragma omp teams distribute parallel for", + "target teams distribute", + "target teams distribute parallel for", + "target teams distribute parallel for simd", + "target teams distribute simd"), ); + +//----------------------------------------------------------------------------// +// End Loop tests +//----------------------------------------------------------------------------// + +TEST(OMPStructuredBlock, TestFlush) { + const char *Source = + R"( +void test() { +#pragma omp flush +})"; + ASSERT_TRUE(PrintedOMPStmtMatches(Source, OMPStandaloneDirectiveMatcher(), + "#pragma omp flush\n")); + ASSERT_TRUE(NoMatches(Source, OMPInnermostStructuredBlockMatcher())); +} + +TEST(OMPStructuredBlock, TestMaster) { + const char *Source = + R"( +void test() { +#pragma omp master +; +})"; + ASSERT_TRUE(PrintedOMPStmtMatches( + Source, OMPInnermostStructuredBlockMatcher(), ";\n")); +} + +TEST(OMPStructuredBlock, TestOrdered0) { + const char *Source = + R"( +void test() { +#pragma omp ordered +; +})"; + ASSERT_TRUE(PrintedOMPStmtMatches( + Source, OMPInnermostStructuredBlockMatcher(), ";\n")); +} + +TEST(OMPStructuredBlock, TestOrdered1) { + const char *Source = + R"( +void test(int x) { +#pragma omp for ordered +for (int i = 0; i < x; i++) +; +})"; + ASSERT_TRUE(PrintedOMPStmtMatches( + Source, OMPInnermostStructuredBlockMatcher(), ";\n")); +} + +TEST(OMPStructuredBlock, TestOrdered2) { + const char *Source = + R"( +void test(int x) { +#pragma omp for ordered(1) +for (int i = 0; i < x; i++) { +#pragma omp ordered depend(source) +} +})"; + ASSERT_TRUE( + PrintedOMPStmtMatches(Source, OMPInnermostStructuredBlockMatcher(), + "{\n #pragma omp ordered depend(source)\n}\n")); + ASSERT_TRUE(PrintedOMPStmtMatches(Source, OMPStandaloneDirectiveMatcher(), + "#pragma omp ordered depend(source)\n")); +} + +TEST(OMPStructuredBlock, DISABLED_TestParallelMaster0XFAIL) { + const char *Source = + R"( +void test() { +#pragma omp parallel master +; +})"; + ASSERT_TRUE(PrintedOMPStmtMatches( + Source, OMPInnermostStructuredBlockMatcher(), ";\n")); +} + +TEST(OMPStructuredBlock, DISABLED_TestParallelMaster1XFAIL) { + const char *Source = + R"( +void test() { +#pragma omp parallel master +{ ; } +})"; + ASSERT_TRUE(PrintedOMPStmtMatches( + Source, OMPInnermostStructuredBlockMatcher(), "{\n ;\n}\n")); +} + +TEST(OMPStructuredBlock, TestParallelSections) { + const char *Source = + R"( +void test() { +#pragma omp parallel sections +{ ; } +})"; + ASSERT_TRUE(PrintedOMPStmtMatches( + Source, OMPInnermostStructuredBlockMatcher(), "{\n ;\n}\n")); +} + +TEST(OMPStructuredBlock, TestParallelDirective) { + const char *Source = + R"( +void test() { +#pragma omp parallel +; +})"; + ASSERT_TRUE(PrintedOMPStmtMatches( + Source, OMPInnermostStructuredBlockMatcher(), ";\n")); +} + +const ast_matchers::internal::VariadicDynCastAllOfMatcher< + OMPExecutableDirective, OMPSectionsDirective> + ompSectionsDirective; + +const ast_matchers::internal::VariadicDynCastAllOfMatcher< + OMPExecutableDirective, OMPSectionDirective> + ompSectionDirective; + +StatementMatcher OMPSectionsDirectiveMatcher() { + return stmt( + isOMPStructuredBlock(), + hasAncestor(ompExecutableDirective(ompSectionsDirective())), + unless(hasAncestor(ompExecutableDirective(ompSectionDirective())))) + .bind("id"); +} + +TEST(OMPStructuredBlock, TestSectionDirective) { + const char *Source = + R"( +void test() { +#pragma omp sections +{ +#pragma omp section +; +} +})"; + ASSERT_TRUE(PrintedOMPStmtMatches(Source, OMPSectionsDirectiveMatcher(), + "{\n" + " #pragma omp section\n" + " ;\n" + "}\n")); + ASSERT_TRUE(PrintedOMPStmtMatches( + Source, OMPInnermostStructuredBlockMatcher(), ";\n")); +} + +TEST(OMPStructuredBlock, TestSections) { + const char *Source = + R"( +void test() { +#pragma omp sections +{ ; } +})"; + ASSERT_TRUE(PrintedOMPStmtMatches( + Source, OMPInnermostStructuredBlockMatcher(), "{\n ;\n}\n")); +} + +TEST(OMPStructuredBlock, TestSingleDirective) { + const char *Source = + R"( +void test() { +#pragma omp single +; +})"; + ASSERT_TRUE(PrintedOMPStmtMatches( + Source, OMPInnermostStructuredBlockMatcher(), ";\n")); +} + +TEST(OMPStructuredBlock, TesTargetDataDirective) { + const char *Source = + R"( +void test(int x) { +#pragma omp target data map(x) +; +})"; + ASSERT_TRUE(PrintedOMPStmtMatches( + Source, OMPInnermostStructuredBlockMatcher(), ";\n")); +} + +TEST(OMPStructuredBlock, TesTargetEnterDataDirective) { + const char *Source = + R"( +void test(int x) { +#pragma omp target enter data map(to : x) +})"; + ASSERT_TRUE( + PrintedOMPStmtMatches(Source, OMPStandaloneDirectiveMatcher(), + "#pragma omp target enter data map(to: x)\n")); + ASSERT_TRUE(NoMatches(Source, OMPInnermostStructuredBlockMatcher())); +} + +TEST(OMPStructuredBlock, TesTargetExitDataDirective) { + const char *Source = + R"( +void test(int x) { +#pragma omp target exit data map(from : x) +})"; + ASSERT_TRUE( + PrintedOMPStmtMatches(Source, OMPStandaloneDirectiveMatcher(), + "#pragma omp target exit data map(from: x)\n")); + ASSERT_TRUE(NoMatches(Source, OMPInnermostStructuredBlockMatcher())); +} + +TEST(OMPStructuredBlock, TestTargetParallelDirective) { + const char *Source = + R"( +void test() { +#pragma omp target parallel +; +})"; + ASSERT_TRUE(PrintedOMPStmtMatches( + Source, OMPInnermostStructuredBlockMatcher(), ";\n")); +} + +TEST(OMPStructuredBlock, TestTargetTeams) { + const char *Source = + R"( +void test() { +#pragma omp target teams +; +})"; + ASSERT_TRUE(PrintedOMPStmtMatches( + Source, OMPInnermostStructuredBlockMatcher(), ";\n")); +} + +TEST(OMPStructuredBlock, TestTargetUpdateDirective) { + const char *Source = + R"( +void test(int x) { +#pragma omp target update to(x) +})"; + ASSERT_TRUE(PrintedOMPStmtMatches(Source, OMPStandaloneDirectiveMatcher(), + "#pragma omp target update to(x)\n")); + ASSERT_TRUE(NoMatches(Source, OMPInnermostStructuredBlockMatcher())); +} + +TEST(OMPStructuredBlock, TestTarget) { + const char *Source = + R"( +void test() { +#pragma omp target +; +})"; + ASSERT_TRUE(PrintedOMPStmtMatches( + Source, OMPInnermostStructuredBlockMatcher(), ";\n")); +} + +TEST(OMPStructuredBlock, TestTask) { + const char *Source = + R"( +void test() { +#pragma omp task +; +})"; + ASSERT_TRUE(PrintedOMPStmtMatches( + Source, OMPInnermostStructuredBlockMatcher(), ";\n")); +} + +TEST(OMPStructuredBlock, TestTaskgroup) { + const char *Source = + R"( +void test() { +#pragma omp taskgroup +; +})"; + ASSERT_TRUE(PrintedOMPStmtMatches( + Source, OMPInnermostStructuredBlockMatcher(), ";\n")); +} + +TEST(OMPStructuredBlock, TestTaskwaitDirective) { + const char *Source = + R"( +void test() { +#pragma omp taskwait +})"; + ASSERT_TRUE(PrintedOMPStmtMatches(Source, OMPStandaloneDirectiveMatcher(), + "#pragma omp taskwait\n")); + ASSERT_TRUE(NoMatches(Source, OMPInnermostStructuredBlockMatcher())); +} + +TEST(OMPStructuredBlock, TestTaskyieldDirective) { + const char *Source = + R"( +void test() { +#pragma omp taskyield +})"; + ASSERT_TRUE(PrintedOMPStmtMatches(Source, OMPStandaloneDirectiveMatcher(), + "#pragma omp taskyield\n")); + ASSERT_TRUE(NoMatches(Source, OMPInnermostStructuredBlockMatcher())); +} + +TEST(OMPStructuredBlock, TestTeams) { + const char *Source = + R"( +void test() { +#pragma omp target +#pragma omp teams +; +})"; + ASSERT_TRUE(PrintedOMPStmtMatches( + Source, OMPInnermostStructuredBlockMatcher(), ";\n")); +} diff --git a/unittests/AST/SourceLocationTest.cpp b/unittests/AST/SourceLocationTest.cpp index 5f69c54043..6b4dddc385 100644 --- a/unittests/AST/SourceLocationTest.cpp +++ b/unittests/AST/SourceLocationTest.cpp @@ -1,9 +1,8 @@ //===- unittest/AST/SourceLocationTest.cpp - AST source loc unit tests ----===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/unittests/AST/StmtPrinterTest.cpp b/unittests/AST/StmtPrinterTest.cpp index 40da6ca6bb..0d383d547a 100644 --- a/unittests/AST/StmtPrinterTest.cpp +++ b/unittests/AST/StmtPrinterTest.cpp @@ -1,9 +1,8 @@ //===- unittests/AST/StmtPrinterTest.cpp --- Statement printer tests ------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -19,6 +18,7 @@ // //===----------------------------------------------------------------------===// +#include "ASTPrint.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Tooling/Tooling.h" @@ -31,81 +31,6 @@ using namespace tooling; namespace { -using PolicyAdjusterType = - Optional<llvm::function_ref<void(PrintingPolicy &Policy)>>; - -void PrintStmt(raw_ostream &Out, const ASTContext *Context, const Stmt *S, - PolicyAdjusterType PolicyAdjuster) { - assert(S != nullptr && "Expected non-null Stmt"); - PrintingPolicy Policy = Context->getPrintingPolicy(); - if (PolicyAdjuster) - (*PolicyAdjuster)(Policy); - S->printPretty(Out, /*Helper*/ nullptr, Policy); -} - -class PrintMatch : public MatchFinder::MatchCallback { - SmallString<1024> Printed; - unsigned NumFoundStmts; - PolicyAdjusterType PolicyAdjuster; - -public: - PrintMatch(PolicyAdjusterType PolicyAdjuster) - : NumFoundStmts(0), PolicyAdjuster(PolicyAdjuster) {} - - void run(const MatchFinder::MatchResult &Result) override { - const Stmt *S = Result.Nodes.getNodeAs<Stmt>("id"); - if (!S) - return; - NumFoundStmts++; - if (NumFoundStmts > 1) - return; - - llvm::raw_svector_ostream Out(Printed); - PrintStmt(Out, Result.Context, S, PolicyAdjuster); - } - - StringRef getPrinted() const { - return Printed; - } - - unsigned getNumFoundStmts() const { - return NumFoundStmts; - } -}; - -template <typename T> -::testing::AssertionResult -PrintedStmtMatches(StringRef Code, const std::vector<std::string> &Args, - const T &NodeMatch, StringRef ExpectedPrinted, - PolicyAdjusterType PolicyAdjuster = None) { - - PrintMatch Printer(PolicyAdjuster); - MatchFinder Finder; - Finder.addMatcher(NodeMatch, &Printer); - std::unique_ptr<FrontendActionFactory> Factory( - newFrontendActionFactory(&Finder)); - - if (!runToolOnCodeWithArgs(Factory->create(), Code, Args)) - return testing::AssertionFailure() - << "Parsing error in \"" << Code.str() << "\""; - - if (Printer.getNumFoundStmts() == 0) - return testing::AssertionFailure() - << "Matcher didn't find any statements"; - - if (Printer.getNumFoundStmts() > 1) - return testing::AssertionFailure() - << "Matcher should match only one statement " - "(found " << Printer.getNumFoundStmts() << ")"; - - if (Printer.getPrinted() != ExpectedPrinted) - return ::testing::AssertionFailure() - << "Expected \"" << ExpectedPrinted.str() << "\", " - "got \"" << Printer.getPrinted().str() << "\""; - - return ::testing::AssertionSuccess(); -} - enum class StdVer { CXX98, CXX11, CXX14, CXX17, CXX2a }; DeclarationMatcher FunctionBodyMatcher(StringRef ContainingFunction) { @@ -232,6 +157,43 @@ TEST(StmtPrinter, TestCXXConversionDeclExplicit) { // WRONG; Should be: (a & b).operator void *() } +TEST(StmtPrinter, TestCXXLamda) { + ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11, + "void A() {" + " auto l = [] { };" + "}", + lambdaExpr(anything()).bind("id"), + "[] {\n" + "}")); + + ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX11, + "void A() {" + " int a = 0, b = 1;" + " auto l = [a,b](int c, float d) { };" + "}", + lambdaExpr(anything()).bind("id"), + "[a, b](int c, float d) {\n" + "}")); + + ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX14, + "void A() {" + " auto l = [](auto a, int b, auto c, int, auto) { };" + "}", + lambdaExpr(anything()).bind("id"), + "[](auto a, int b, auto c, int, auto) {\n" + "}")); + + ASSERT_TRUE(PrintedStmtCXXMatches(StdVer::CXX2a, + "void A() {" + " auto l = []<typename T1, class T2, int I," + " template<class, typename> class T3>" + " (int a, auto, int, auto d) { };" + "}", + lambdaExpr(anything()).bind("id"), + "[]<typename T1, class T2, int I, template <class, typename> class T3>(int a, auto, int, auto d) {\n" + "}")); +} + TEST(StmtPrinter, TestNoImplicitBases) { const char *CPPSource = R"( class A { diff --git a/unittests/AST/StructuralEquivalenceTest.cpp b/unittests/AST/StructuralEquivalenceTest.cpp index cd1f01d4bf..211b9539cf 100644 --- a/unittests/AST/StructuralEquivalenceTest.cpp +++ b/unittests/AST/StructuralEquivalenceTest.cpp @@ -230,6 +230,33 @@ TEST_F(StructuralEquivalenceFunctionTest, TemplateVsNonTemplate) { EXPECT_FALSE(testStructuralMatch(t)); } +TEST_F(StructuralEquivalenceFunctionTest, DifferentOperators) { + auto t = makeDecls<FunctionDecl>( + "struct X{}; bool operator<(X, X);", + "struct X{}; bool operator==(X, X);", Lang_CXX, + functionDecl(hasOverloadedOperatorName("<")), + functionDecl(hasOverloadedOperatorName("=="))); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, SameOperators) { + auto t = makeDecls<FunctionDecl>( + "struct X{}; bool operator<(X, X);", + "struct X{}; bool operator<(X, X);", Lang_CXX, + functionDecl(hasOverloadedOperatorName("<")), + functionDecl(hasOverloadedOperatorName("<"))); + EXPECT_TRUE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, CtorVsDtor) { + auto t = makeDecls<FunctionDecl>( + "struct X{ X(); };", + "struct X{ ~X(); };", Lang_CXX, + cxxConstructorDecl(), + cxxDestructorDecl()); + EXPECT_FALSE(testStructuralMatch(t)); +} + TEST_F(StructuralEquivalenceFunctionTest, ParamConstWithRef) { auto t = makeNamedDecls("void foo(int&);", "void foo(const int&);", Lang_CXX); @@ -370,6 +397,38 @@ TEST_F(StructuralEquivalenceFunctionTest, NameInParenWithConst) { EXPECT_FALSE(testStructuralMatch(t)); } +TEST_F(StructuralEquivalenceFunctionTest, FunctionsWithDifferentNoreturnAttr) { + auto t = makeNamedDecls( + "__attribute__((noreturn)) void foo();", + " void foo();", + Lang_C); + EXPECT_TRUE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, + FunctionsWithDifferentCallingConventions) { + // These attributes may not be available on certain platforms. + if (llvm::Triple(llvm::sys::getDefaultTargetTriple()).getArch() != + llvm::Triple::x86_64) + return; + auto t = makeNamedDecls( + "__attribute__((preserve_all)) void foo();", + "__attribute__((ms_abi)) void foo();", + Lang_C); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, FunctionsWithDifferentSavedRegsAttr) { + if (llvm::Triple(llvm::sys::getDefaultTargetTriple()).getArch() != + llvm::Triple::x86_64) + return; + auto t = makeNamedDecls( + "__attribute__((no_caller_saved_registers)) void foo();", + " void foo();", + Lang_C); + EXPECT_FALSE(testStructuralMatch(t)); +} + struct StructuralEquivalenceCXXMethodTest : StructuralEquivalenceTest { }; @@ -774,6 +833,25 @@ TEST_F(StructuralEquivalenceEnumTest, EnumsWithDifferentBody) { EXPECT_FALSE(testStructuralMatch(t)); } +struct StructuralEquivalenceTemplateTest : StructuralEquivalenceTest {}; + +TEST_F(StructuralEquivalenceTemplateTest, ExactlySameTemplates) { + auto t = makeNamedDecls("template <class T> struct foo;", + "template <class T> struct foo;", Lang_CXX); + EXPECT_TRUE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceTemplateTest, DifferentTemplateArgName) { + auto t = makeNamedDecls("template <class T> struct foo;", + "template <class U> struct foo;", Lang_CXX); + EXPECT_TRUE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceTemplateTest, DifferentTemplateArgKind) { + auto t = makeNamedDecls("template <class T> struct foo;", + "template <int T> struct foo;", Lang_CXX); + EXPECT_FALSE(testStructuralMatch(t)); +} } // end namespace ast_matchers } // end namespace clang diff --git a/unittests/ASTMatchers/ASTMatchersInternalTest.cpp b/unittests/ASTMatchers/ASTMatchersInternalTest.cpp index 288fce08a8..dc031256cf 100644 --- a/unittests/ASTMatchers/ASTMatchersInternalTest.cpp +++ b/unittests/ASTMatchers/ASTMatchersInternalTest.cpp @@ -1,9 +1,8 @@ // unittests/ASTMatchers/ASTMatchersInternalTest.cpp - AST matcher unit tests // // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp index fb17d100c5..01b168da2b 100644 --- a/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ b/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp @@ -1,9 +1,8 @@ // unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp - AST matcher unit tests// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -2032,6 +2031,57 @@ TEST(NS, Anonymous) { EXPECT_TRUE(matches("namespace {}", namespaceDecl(isAnonymous()))); } +TEST(DeclarationMatcher, InStdNamespace) { + EXPECT_TRUE(notMatches("class vector {};" + "namespace foo {" + " class vector {};" + "}" + "namespace foo {" + " namespace std {" + " class vector {};" + " }" + "}", + cxxRecordDecl(hasName("vector"), isInStdNamespace()))); + + EXPECT_TRUE(matches("namespace std {" + " class vector {};" + "}", + cxxRecordDecl(hasName("vector"), isInStdNamespace()))); + EXPECT_TRUE(matches("namespace std {" + " inline namespace __1 {" + " class vector {};" + " }" + "}", + cxxRecordDecl(hasName("vector"), isInStdNamespace()))); + EXPECT_TRUE(notMatches("namespace std {" + " inline namespace __1 {" + " inline namespace __fs {" + " namespace filesystem {" + " inline namespace v1 {" + " class path {};" + " }" + " }" + " }" + " }" + "}", + cxxRecordDecl(hasName("path"), isInStdNamespace()))); + EXPECT_TRUE( + matches("namespace std {" + " inline namespace __1 {" + " inline namespace __fs {" + " namespace filesystem {" + " inline namespace v1 {" + " class path {};" + " }" + " }" + " }" + " }" + "}", + cxxRecordDecl(hasName("path"), + hasAncestor(namespaceDecl(hasName("filesystem"), + isInStdNamespace()))))); +} + TEST(EqualsBoundNodeMatcher, QualType) { EXPECT_TRUE(matches( "int i = 1;", varDecl(hasType(qualType().bind("type")), @@ -2275,5 +2325,238 @@ TEST(Matcher, isMain) { notMatches("int main2() {}", functionDecl(isMain()))); } +TEST(OMPExecutableDirective, isStandaloneDirective) { + auto Matcher = ompExecutableDirective(isStandaloneDirective()); + + const std::string Source0 = R"( +void x() { +#pragma omp parallel +; +})"; + EXPECT_TRUE(notMatchesWithOpenMP(Source0, Matcher)); + + const std::string Source1 = R"( +void x() { +#pragma omp taskyield +})"; + EXPECT_TRUE(matchesWithOpenMP(Source1, Matcher)); +} + +TEST(Stmt, isOMPStructuredBlock) { + const std::string Source0 = R"( +void x() { +#pragma omp parallel +; +})"; + EXPECT_TRUE( + matchesWithOpenMP(Source0, stmt(nullStmt(), isOMPStructuredBlock()))); + + const std::string Source1 = R"( +void x() { +#pragma omp parallel +{;} +})"; + EXPECT_TRUE( + notMatchesWithOpenMP(Source1, stmt(nullStmt(), isOMPStructuredBlock()))); + EXPECT_TRUE( + matchesWithOpenMP(Source1, stmt(compoundStmt(), isOMPStructuredBlock()))); +} + +TEST(OMPExecutableDirective, hasStructuredBlock) { + const std::string Source0 = R"( +void x() { +#pragma omp parallel +; +})"; + EXPECT_TRUE(matchesWithOpenMP( + Source0, ompExecutableDirective(hasStructuredBlock(nullStmt())))); + + const std::string Source1 = R"( +void x() { +#pragma omp parallel +{;} +})"; + EXPECT_TRUE(notMatchesWithOpenMP( + Source1, ompExecutableDirective(hasStructuredBlock(nullStmt())))); + EXPECT_TRUE(matchesWithOpenMP( + Source1, ompExecutableDirective(hasStructuredBlock(compoundStmt())))); + + const std::string Source2 = R"( +void x() { +#pragma omp taskyield +{;} +})"; + EXPECT_TRUE(notMatchesWithOpenMP( + Source2, ompExecutableDirective(hasStructuredBlock(anything())))); +} + +TEST(OMPExecutableDirective, hasClause) { + auto Matcher = ompExecutableDirective(hasAnyClause(anything())); + + const std::string Source0 = R"( +void x() { +; +})"; + EXPECT_TRUE(notMatchesWithOpenMP(Source0, Matcher)); + + const std::string Source1 = R"( +void x() { +#pragma omp parallel +; +})"; + EXPECT_TRUE(notMatchesWithOpenMP(Source1, Matcher)); + + const std::string Source2 = R"( +void x() { +#pragma omp parallel default(none) +; +})"; + EXPECT_TRUE(matchesWithOpenMP(Source2, Matcher)); + + const std::string Source3 = R"( +void x() { +#pragma omp parallel default(shared) +; +})"; + EXPECT_TRUE(matchesWithOpenMP(Source3, Matcher)); + + const std::string Source4 = R"( +void x(int x) { +#pragma omp parallel num_threads(x) +; +})"; + EXPECT_TRUE(matchesWithOpenMP(Source4, Matcher)); +} + +TEST(OMPDefaultClause, isNoneKind) { + auto Matcher = + ompExecutableDirective(hasAnyClause(ompDefaultClause(isNoneKind()))); + + const std::string Source0 = R"( +void x() { +; +})"; + EXPECT_TRUE(notMatchesWithOpenMP(Source0, Matcher)); + + const std::string Source1 = R"( +void x() { +#pragma omp parallel +; +})"; + EXPECT_TRUE(notMatchesWithOpenMP(Source1, Matcher)); + + const std::string Source2 = R"( +void x() { +#pragma omp parallel default(none) +; +})"; + EXPECT_TRUE(matchesWithOpenMP(Source2, Matcher)); + + const std::string Source3 = R"( +void x() { +#pragma omp parallel default(shared) +; +})"; + EXPECT_TRUE(notMatchesWithOpenMP(Source3, Matcher)); + + const std::string Source4 = R"( +void x(int x) { +#pragma omp parallel num_threads(x) +; +})"; + EXPECT_TRUE(notMatchesWithOpenMP(Source4, Matcher)); +} + +TEST(OMPDefaultClause, isSharedKind) { + auto Matcher = + ompExecutableDirective(hasAnyClause(ompDefaultClause(isSharedKind()))); + + const std::string Source0 = R"( +void x() { +; +})"; + EXPECT_TRUE(notMatchesWithOpenMP(Source0, Matcher)); + + const std::string Source1 = R"( +void x() { +#pragma omp parallel +; +})"; + EXPECT_TRUE(notMatchesWithOpenMP(Source1, Matcher)); + + const std::string Source2 = R"( +void x() { +#pragma omp parallel default(shared) +; +})"; + EXPECT_TRUE(matchesWithOpenMP(Source2, Matcher)); + + const std::string Source3 = R"( +void x() { +#pragma omp parallel default(none) +; +})"; + EXPECT_TRUE(notMatchesWithOpenMP(Source3, Matcher)); + + const std::string Source4 = R"( +void x(int x) { +#pragma omp parallel num_threads(x) +; +})"; + EXPECT_TRUE(notMatchesWithOpenMP(Source4, Matcher)); +} + +TEST(OMPExecutableDirective, isAllowedToContainClauseKind) { + auto Matcher = + ompExecutableDirective(isAllowedToContainClauseKind(OMPC_default)); + + const std::string Source0 = R"( +void x() { +; +})"; + EXPECT_TRUE(notMatchesWithOpenMP(Source0, Matcher)); + + const std::string Source1 = R"( +void x() { +#pragma omp parallel +; +})"; + EXPECT_TRUE(matchesWithOpenMP(Source1, Matcher)); + + const std::string Source2 = R"( +void x() { +#pragma omp parallel default(none) +; +})"; + EXPECT_TRUE(matchesWithOpenMP(Source2, Matcher)); + + const std::string Source3 = R"( +void x() { +#pragma omp parallel default(shared) +; +})"; + EXPECT_TRUE(matchesWithOpenMP(Source3, Matcher)); + + const std::string Source4 = R"( +void x(int x) { +#pragma omp parallel num_threads(x) +; +})"; + EXPECT_TRUE(matchesWithOpenMP(Source4, Matcher)); + + const std::string Source5 = R"( +void x() { +#pragma omp taskyield +})"; + EXPECT_TRUE(notMatchesWithOpenMP(Source5, Matcher)); + + const std::string Source6 = R"( +void x() { +#pragma omp task +; +})"; + EXPECT_TRUE(matchesWithOpenMP(Source6, Matcher)); +} + } // namespace ast_matchers } // namespace clang diff --git a/unittests/ASTMatchers/ASTMatchersNodeTest.cpp b/unittests/ASTMatchers/ASTMatchersNodeTest.cpp index 1bd4e09e77..16e682aeff 100644 --- a/unittests/ASTMatchers/ASTMatchersNodeTest.cpp +++ b/unittests/ASTMatchers/ASTMatchersNodeTest.cpp @@ -1,9 +1,8 @@ //== unittests/ASTMatchers/ASTMatchersNodeTest.cpp - AST matcher unit tests ==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -755,6 +754,11 @@ TEST(Matcher, NullPtrLiteral) { EXPECT_TRUE(matches("int* i = nullptr;", cxxNullPtrLiteralExpr())); } +TEST(Matcher, ChooseExpr) { + EXPECT_TRUE(matchesC("void f() { (void)__builtin_choose_expr(1, 2, 3); }", + chooseExpr())); +} + TEST(Matcher, GNUNullExpr) { EXPECT_TRUE(matches("int* i = __null;", gnuNullExpr())); } @@ -1761,5 +1765,67 @@ TEST(ObjCAutoreleaseMatcher, AutoreleasePool) { EXPECT_FALSE(matchesObjC(ObjCStringNoPool, autoreleasePoolStmt())); } +TEST(OMPExecutableDirective, Matches) { + auto Matcher = stmt(ompExecutableDirective()); + + const std::string Source0 = R"( +void x() { +#pragma omp parallel +; +})"; + EXPECT_TRUE(matchesWithOpenMP(Source0, Matcher)); + + const std::string Source1 = R"( +void x() { +#pragma omp taskyield +; +})"; + EXPECT_TRUE(matchesWithOpenMP(Source1, Matcher)); + + const std::string Source2 = R"( +void x() { +; +})"; + EXPECT_TRUE(notMatchesWithOpenMP(Source2, Matcher)); +} + +TEST(OMPDefaultClause, Matches) { + auto Matcher = ompExecutableDirective(hasAnyClause(ompDefaultClause())); + + const std::string Source0 = R"( +void x() { +; +})"; + EXPECT_TRUE(notMatchesWithOpenMP(Source0, Matcher)); + + const std::string Source1 = R"( +void x() { +#pragma omp parallel +; +})"; + EXPECT_TRUE(notMatchesWithOpenMP(Source1, Matcher)); + + const std::string Source2 = R"( +void x() { +#pragma omp parallel default(none) +; +})"; + EXPECT_TRUE(matchesWithOpenMP(Source2, Matcher)); + + const std::string Source3 = R"( +void x() { +#pragma omp parallel default(shared) +; +})"; + EXPECT_TRUE(matchesWithOpenMP(Source3, Matcher)); + + const std::string Source4 = R"( +void x(int x) { +#pragma omp parallel num_threads(x) +; +})"; + EXPECT_TRUE(notMatchesWithOpenMP(Source4, Matcher)); +} + } // namespace ast_matchers } // namespace clang diff --git a/unittests/ASTMatchers/ASTMatchersTest.h b/unittests/ASTMatchers/ASTMatchersTest.h index 504668872f..78c551f806 100644 --- a/unittests/ASTMatchers/ASTMatchersTest.h +++ b/unittests/ASTMatchers/ASTMatchersTest.h @@ -1,9 +1,8 @@ //===- unittest/Tooling/ASTMatchersTest.h - Matcher tests helpers ------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -184,7 +183,9 @@ testing::AssertionResult matchesConditionallyWithCuda( "typedef struct cudaStream *cudaStream_t;" "int cudaConfigureCall(dim3 gridSize, dim3 blockSize," " size_t sharedSize = 0," - " cudaStream_t stream = 0);"; + " cudaStream_t stream = 0);" + "extern \"C\" unsigned __cudaPushCallConfiguration(" + " dim3 gridDim, dim3 blockDim, size_t sharedMem = 0, void *stream = 0);"; bool Found = false, DynamicFound = false; MatchFinder Finder; @@ -234,6 +235,18 @@ testing::AssertionResult notMatchesWithCuda(const std::string &Code, } template <typename T> +testing::AssertionResult matchesWithOpenMP(const std::string &Code, + const T &AMatcher) { + return matchesConditionally(Code, AMatcher, true, "-fopenmp=libomp"); +} + +template <typename T> +testing::AssertionResult notMatchesWithOpenMP(const std::string &Code, + const T &AMatcher) { + return matchesConditionally(Code, AMatcher, false, "-fopenmp=libomp"); +} + +template <typename T> testing::AssertionResult matchAndVerifyResultConditionally(const std::string &Code, const T &AMatcher, std::unique_ptr<BoundNodesCallback> FindResultVerifier, diff --git a/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp index 5f6ecc0d0b..dafc8c52e9 100644 --- a/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ b/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -1,9 +1,8 @@ //= unittests/ASTMatchers/ASTMatchersTraversalTest.cpp - matchers unit tests =// // -// The LLVM Compiler Infrastructure -//` -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -455,6 +454,20 @@ TEST(Matcher, HasReceiver) { objcMessageExpr(hasReceiver(declRefExpr(to(varDecl(hasName("x")))))))); } +TEST(Matcher, isClassMessage) { + EXPECT_TRUE(matchesObjC( + "@interface NSString +(NSString *) stringWithFormat; @end " + "void f() { [NSString stringWithFormat]; }", + objcMessageExpr(isClassMessage()))); + + EXPECT_FALSE(matchesObjC( + "@interface NSString @end " + "void f(NSString *x) {" + "[x containsString];" + "}", + objcMessageExpr(isClassMessage()))); +} + TEST(Matcher, isInstanceMessage) { EXPECT_TRUE(matchesObjC( "@interface NSString @end " @@ -470,6 +483,138 @@ TEST(Matcher, isInstanceMessage) { } +TEST(Matcher, isClassMethod) { + EXPECT_TRUE(matchesObjC( + "@interface Bar + (void)bar; @end", + objcMethodDecl(isClassMethod()))); + + EXPECT_TRUE(matchesObjC( + "@interface Bar @end" + "@implementation Bar + (void)bar {} @end", + objcMethodDecl(isClassMethod()))); + + EXPECT_FALSE(matchesObjC( + "@interface Foo - (void)foo; @end", + objcMethodDecl(isClassMethod()))); + + EXPECT_FALSE(matchesObjC( + "@interface Foo @end " + "@implementation Foo - (void)foo {} @end", + objcMethodDecl(isClassMethod()))); +} + +TEST(Matcher, isInstanceMethod) { + EXPECT_TRUE(matchesObjC( + "@interface Foo - (void)foo; @end", + objcMethodDecl(isInstanceMethod()))); + + EXPECT_TRUE(matchesObjC( + "@interface Foo @end " + "@implementation Foo - (void)foo {} @end", + objcMethodDecl(isInstanceMethod()))); + + EXPECT_FALSE(matchesObjC( + "@interface Bar + (void)bar; @end", + objcMethodDecl(isInstanceMethod()))); + + EXPECT_FALSE(matchesObjC( + "@interface Bar @end" + "@implementation Bar + (void)bar {} @end", + objcMethodDecl(isInstanceMethod()))); +} + +TEST(MatcherCXXMemberCallExpr, On) { + auto Snippet1 = R"cc( + struct Y { + void m(); + }; + void z(Y y) { y.m(); } + )cc"; + auto Snippet2 = R"cc( + struct Y { + void m(); + }; + struct X : public Y {}; + void z(X x) { x.m(); } + )cc"; + auto MatchesY = cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("Y"))))); + EXPECT_TRUE(matches(Snippet1, MatchesY)); + EXPECT_TRUE(notMatches(Snippet2, MatchesY)); + + auto MatchesX = cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("X"))))); + EXPECT_TRUE(matches(Snippet2, MatchesX)); + + // Parens are ignored. + auto Snippet3 = R"cc( + struct Y { + void m(); + }; + Y g(); + void z(Y y) { (g()).m(); } + )cc"; + auto MatchesCall = cxxMemberCallExpr(on(callExpr())); + EXPECT_TRUE(matches(Snippet3, MatchesCall)); +} + +TEST(MatcherCXXMemberCallExpr, OnImplicitObjectArgument) { + auto Snippet1 = R"cc( + struct Y { + void m(); + }; + void z(Y y) { y.m(); } + )cc"; + auto Snippet2 = R"cc( + struct Y { + void m(); + }; + struct X : public Y {}; + void z(X x) { x.m(); } + )cc"; + auto MatchesY = cxxMemberCallExpr( + onImplicitObjectArgument(hasType(cxxRecordDecl(hasName("Y"))))); + EXPECT_TRUE(matches(Snippet1, MatchesY)); + EXPECT_TRUE(matches(Snippet2, MatchesY)); + + auto MatchesX = cxxMemberCallExpr( + onImplicitObjectArgument(hasType(cxxRecordDecl(hasName("X"))))); + EXPECT_TRUE(notMatches(Snippet2, MatchesX)); + + // Parens are not ignored. + auto Snippet3 = R"cc( + struct Y { + void m(); + }; + Y g(); + void z(Y y) { (g()).m(); } + )cc"; + auto MatchesCall = cxxMemberCallExpr(onImplicitObjectArgument(callExpr())); + EXPECT_TRUE(notMatches(Snippet3, MatchesCall)); +} + +TEST(Matcher, HasObjectExpr) { + auto Snippet1 = R"cc( + struct X { + int m; + int f(X x) { return x.m; } + }; + )cc"; + auto Snippet2 = R"cc( + struct X { + int m; + int f(X x) { return m; } + }; + )cc"; + auto MatchesX = + memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X"))))); + EXPECT_TRUE(matches(Snippet1, MatchesX)); + EXPECT_TRUE(notMatches(Snippet2, MatchesX)); + + auto MatchesXPointer = memberExpr( + hasObjectExpression(hasType(pointsTo(cxxRecordDecl(hasName("X")))))); + EXPECT_TRUE(notMatches(Snippet1, MatchesXPointer)); + EXPECT_TRUE(matches(Snippet2, MatchesXPointer)); +} + TEST(ForEachArgumentWithParam, ReportsNoFalsePositives) { StatementMatcher ArgumentY = declRefExpr(to(varDecl(hasName("y")))).bind("arg"); diff --git a/unittests/ASTMatchers/Dynamic/ParserTest.cpp b/unittests/ASTMatchers/Dynamic/ParserTest.cpp index 9e891069c8..aba094ca69 100644 --- a/unittests/ASTMatchers/Dynamic/ParserTest.cpp +++ b/unittests/ASTMatchers/Dynamic/ParserTest.cpp @@ -1,9 +1,8 @@ //===- unittest/ASTMatchers/Dynamic/ParserTest.cpp - Parser unit tests -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===-------------------------------------------------------------------===// diff --git a/unittests/ASTMatchers/Dynamic/RegistryTest.cpp b/unittests/ASTMatchers/Dynamic/RegistryTest.cpp index 1ca394d8d8..cf016a120b 100644 --- a/unittests/ASTMatchers/Dynamic/RegistryTest.cpp +++ b/unittests/ASTMatchers/Dynamic/RegistryTest.cpp @@ -1,9 +1,8 @@ //===- unittest/ASTMatchers/Dynamic/RegistryTest.cpp - Registry unit tests -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===-----------------------------------------------------------------------===// diff --git a/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp b/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp index 7d3a07028a..c08d7fc3ff 100644 --- a/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp +++ b/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp @@ -1,9 +1,8 @@ //===- unittest/ASTMatchers/Dynamic/VariantValueTest.cpp - VariantValue unit tests -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===-----------------------------------------------------------------------------===// diff --git a/unittests/Analysis/CFGTest.cpp b/unittests/Analysis/CFGTest.cpp index 768705f46f..2c2522d262 100644 --- a/unittests/Analysis/CFGTest.cpp +++ b/unittests/Analysis/CFGTest.cpp @@ -1,9 +1,8 @@ //===- unittests/Analysis/CFGTest.cpp - CFG tests -------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -18,27 +17,41 @@ namespace clang { namespace analysis { namespace { -enum BuildResult { - ToolFailed, - ToolRan, - SawFunctionBody, - BuiltCFG, +class BuildResult { +public: + enum Status { + ToolFailed, + ToolRan, + SawFunctionBody, + BuiltCFG, + }; + + BuildResult(Status S, std::unique_ptr<CFG> Cfg = nullptr) + : S(S), Cfg(std::move(Cfg)) {} + + Status getStatus() const { return S; } + CFG *getCFG() const { return Cfg.get(); } + +private: + Status S; + std::unique_ptr<CFG> Cfg; }; class CFGCallback : public ast_matchers::MatchFinder::MatchCallback { public: - BuildResult TheBuildResult = ToolRan; + BuildResult TheBuildResult = BuildResult::ToolRan; void run(const ast_matchers::MatchFinder::MatchResult &Result) override { const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func"); Stmt *Body = Func->getBody(); if (!Body) return; - TheBuildResult = SawFunctionBody; + TheBuildResult = BuildResult::SawFunctionBody; CFG::BuildOptions Options; Options.AddImplicitDtors = true; - if (CFG::buildCFG(nullptr, Body, Result.Context, Options)) - TheBuildResult = BuiltCFG; + if (std::unique_ptr<CFG> Cfg = + CFG::buildCFG(nullptr, Body, Result.Context, Options)) + TheBuildResult = {BuildResult::BuiltCFG, std::move(Cfg)}; } }; @@ -51,8 +64,8 @@ BuildResult BuildCFG(const char *Code) { tooling::newFrontendActionFactory(&Finder)); std::vector<std::string> Args = {"-std=c++11", "-fno-delayed-template-parsing"}; if (!tooling::runToolOnCodeWithArgs(Factory->create(), Code, Args)) - return ToolFailed; - return Callback.TheBuildResult; + return BuildResult::ToolFailed; + return std::move(Callback.TheBuildResult); } // Constructing a CFG for a range-based for over a dependent type fails (but @@ -64,7 +77,7 @@ TEST(CFG, RangeBasedForOverDependentType) { " for (const Foo *TheFoo : Range) {\n" " }\n" "}\n"; - EXPECT_EQ(SawFunctionBody, BuildCFG(Code)); + EXPECT_EQ(BuildResult::SawFunctionBody, BuildCFG(Code).getStatus()); } // Constructing a CFG containing a delete expression on a dependent type should @@ -74,7 +87,7 @@ TEST(CFG, DeleteExpressionOnDependentType) { "void f(T t) {\n" " delete t;\n" "}\n"; - EXPECT_EQ(BuiltCFG, BuildCFG(Code)); + EXPECT_EQ(BuildResult::BuiltCFG, BuildCFG(Code).getStatus()); } // Constructing a CFG on a function template with a variable of incomplete type @@ -84,7 +97,24 @@ TEST(CFG, VariableOfIncompleteType) { " class Undefined;\n" " Undefined u;\n" "}\n"; - EXPECT_EQ(BuiltCFG, BuildCFG(Code)); + EXPECT_EQ(BuildResult::BuiltCFG, BuildCFG(Code).getStatus()); +} + +TEST(CFG, IsLinear) { + auto expectLinear = [](bool IsLinear, const char *Code) { + BuildResult B = BuildCFG(Code); + EXPECT_EQ(BuildResult::BuiltCFG, B.getStatus()); + EXPECT_EQ(IsLinear, B.getCFG()->isLinear()); + }; + + expectLinear(true, "void foo() {}"); + expectLinear(true, "void foo() { if (true) return; }"); + expectLinear(true, "void foo() { if constexpr (false); }"); + expectLinear(false, "void foo(bool coin) { if (coin) return; }"); + expectLinear(false, "void foo() { for(;;); }"); + expectLinear(false, "void foo() { do {} while (true); }"); + expectLinear(true, "void foo() { do {} while (false); }"); + expectLinear(true, "void foo() { foo(); }"); // Recursion is not our problem. } } // namespace diff --git a/unittests/Analysis/CloneDetectionTest.cpp b/unittests/Analysis/CloneDetectionTest.cpp index 965a4bc308..03b63c4004 100644 --- a/unittests/Analysis/CloneDetectionTest.cpp +++ b/unittests/Analysis/CloneDetectionTest.cpp @@ -1,9 +1,8 @@ //===- unittests/Analysis/CloneDetectionTest.cpp - Clone detection tests --===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Analysis/ExprMutationAnalyzerTest.cpp b/unittests/Analysis/ExprMutationAnalyzerTest.cpp index 68c921e439..2c22a5cf9e 100644 --- a/unittests/Analysis/ExprMutationAnalyzerTest.cpp +++ b/unittests/Analysis/ExprMutationAnalyzerTest.cpp @@ -1,9 +1,8 @@ //===---------- ExprMutationAnalyzerTest.cpp ------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -882,6 +881,137 @@ TEST(ExprMutationAnalyzerTest, CastToConstRef) { EXPECT_FALSE(isMutated(Results, AST.get())); } +TEST(ExprMutationAnalyzerTest, CommaExprWithAnAssigment) { + const auto AST = + buildASTFromCodeWithArgs("void f() { int x; int y; (x, y) = 5; }", + {"-Wno-unused-value"}); + const auto Results = + match(withEnclosingCompound(declRefTo("y")), AST->getASTContext()); + EXPECT_TRUE(isMutated(Results, AST.get())); +} + +TEST(ExprMutationAnalyzerTest, CommaExprWithDecOp) { + const auto AST = + buildASTFromCodeWithArgs("void f() { int x; int y; (x, y)++; }", + {"-Wno-unused-value"}); + const auto Results = + match(withEnclosingCompound(declRefTo("y")), AST->getASTContext()); + EXPECT_TRUE(isMutated(Results, AST.get())); +} + +TEST(ExprMutationAnalyzerTest, CommaExprWithNonConstMemberCall) { + const auto AST = + buildASTFromCodeWithArgs("class A { public: int mem; void f() { mem ++; } };" + "void fn() { A o1, o2; (o1, o2).f(); }", + {"-Wno-unused-value"}); + const auto Results = + match(withEnclosingCompound(declRefTo("o2")), AST->getASTContext()); + EXPECT_TRUE(isMutated(Results, AST.get())); +} + +TEST(ExprMutationAnalyzerTest, CommaExprWithConstMemberCall) { + const auto AST = + buildASTFromCodeWithArgs("class A { public: int mem; void f() const { } };" + "void fn() { A o1, o2; (o1, o2).f(); }", + {"-Wno-unused-value"}); + const auto Results = + match(withEnclosingCompound(declRefTo("o2")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); +} + +TEST(ExprMutationAnalyzerTest, CommaExprWithCallExpr) { + const auto AST = + buildASTFromCodeWithArgs("class A { public: int mem; void f(A &O1) {} };" + "void fn() { A o1, o2; o2.f((o2, o1)); }", + {"-Wno-unused-value"}); + const auto Results = + match(withEnclosingCompound(declRefTo("o1")), AST->getASTContext()); + EXPECT_TRUE(isMutated(Results, AST.get())); +} + +TEST(ExprMutationAnalyzerTest, CommaExprWithCallUnresolved) { + auto AST = buildASTFromCodeWithArgs( + "template <class T> struct S;" + "template <class T> void f() { S<T> s; int x, y; s.mf((y, x)); }", + {"-fno-delayed-template-parsing", "-Wno-unused-value"}); + auto Results = + match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_TRUE(isMutated(Results, AST.get())); + + AST = buildASTFromCodeWithArgs( + "template <class T> void f(T t) { int x, y; g(t, (y, x)); }", + {"-fno-delayed-template-parsing", "-Wno-unused-value"}); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_TRUE(isMutated(Results, AST.get())); +} + +TEST(ExprMutationAnalyzerTest, CommaExprParmRef) { + const auto AST = + buildASTFromCodeWithArgs("class A { public: int mem;};" + "extern void fn(A &o1);" + "void fn2 () { A o1, o2; fn((o2, o1)); } ", + {"-Wno-unused-value"}); + const auto Results = + match(withEnclosingCompound(declRefTo("o1")), AST->getASTContext()); + EXPECT_TRUE(isMutated(Results, AST.get())); +} + +TEST(ExprMutationAnalyzerTest, CommaExprWithAmpersandOp) { + const auto AST = + buildASTFromCodeWithArgs("class A { public: int mem;};" + "void fn () { A o1, o2;" + "void *addr = &(o2, o1); } ", + {"-Wno-unused-value"}); + const auto Results = + match(withEnclosingCompound(declRefTo("o1")), AST->getASTContext()); + EXPECT_TRUE(isMutated(Results, AST.get())); +} + +TEST(ExprMutationAnalyzerTest, CommaExprAsReturnAsValue) { + auto AST = buildASTFromCodeWithArgs("int f() { int x, y; return (x, y); }", + {"-Wno-unused-value"}); + auto Results = + match(withEnclosingCompound(declRefTo("y")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); +} + +TEST(ExprMutationAnalyzerTest, CommaEpxrAsReturnAsNonConstRef) { + const auto AST = + buildASTFromCodeWithArgs("int& f() { int x, y; return (y, x); }", + {"-Wno-unused-value"}); + const auto Results = + match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_TRUE(isMutated(Results, AST.get())); +} + +TEST(ExprMutationAnalyzerTest, CommaExprAsArrayToPointerDecay) { + const auto AST = + buildASTFromCodeWithArgs("void g(int*); " + "void f() { int x[2], y[2]; g((y, x)); }", + {"-Wno-unused-value"}); + const auto Results = + match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_TRUE(isMutated(Results, AST.get())); +} + +TEST(ExprMutationAnalyzerTest, CommaExprAsUniquePtr) { + const std::string UniquePtrDef = + "template <class T> struct UniquePtr {" + " UniquePtr();" + " UniquePtr(const UniquePtr&) = delete;" + " T& operator*() const;" + " T* operator->() const;" + "};"; + const auto AST = buildASTFromCodeWithArgs( + UniquePtrDef + "template <class T> void f() " + "{ UniquePtr<T> x; UniquePtr<T> y;" + " (y, x)->mf(); }", + {"-fno-delayed-template-parsing", "-Wno-unused-value"}); + const auto Results = + match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_TRUE(isMutated(Results, AST.get())); +} + TEST(ExprMutationAnalyzerTest, LambdaDefaultCaptureByValue) { const auto AST = buildASTFromCode("void f() { int x; [=]() { x; }; }"); const auto Results = @@ -1109,4 +1239,23 @@ TEST(ExprMutationAnalyzerTest, UniquePtr) { EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x->mf()")); } +TEST(ExprMutationAnalyzerTest, ReproduceFailureMinimal) { + const std::string Reproducer = + "namespace std {" + "template <class T> T forward(T & A) { return static_cast<T&&>(A); }" + "template <class T> struct __bind {" + " T f;" + " template <class V> __bind(T v, V &&) : f(forward(v)) {}" + "};" + "}" + "void f() {" + " int x = 42;" + " auto Lambda = [] {};" + " std::__bind<decltype(Lambda)>(Lambda, x);" + "}"; + auto AST11 = buildASTFromCodeWithArgs(Reproducer, {"-std=c++11"}); + auto Results11 = + match(withEnclosingCompound(declRefTo("x")), AST11->getASTContext()); + EXPECT_FALSE(isMutated(Results11, AST11.get())); +} } // namespace clang diff --git a/unittests/Basic/CMakeLists.txt b/unittests/Basic/CMakeLists.txt index 537f3ba5fc..d883c362e2 100644 --- a/unittests/Basic/CMakeLists.txt +++ b/unittests/Basic/CMakeLists.txt @@ -7,7 +7,6 @@ add_clang_unittest(BasicTests DiagnosticTest.cpp FileManagerTest.cpp FixedPointTest.cpp - MemoryBufferCacheTest.cpp SourceManagerTest.cpp ) diff --git a/unittests/Basic/CharInfoTest.cpp b/unittests/Basic/CharInfoTest.cpp index 7a9d17fce6..4f84bebec3 100644 --- a/unittests/Basic/CharInfoTest.cpp +++ b/unittests/Basic/CharInfoTest.cpp @@ -1,9 +1,8 @@ //===- unittests/Basic/CharInfoTest.cpp -- ASCII classification tests -----===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Basic/DiagnosticTest.cpp b/unittests/Basic/DiagnosticTest.cpp index 3068e1c340..ffb750bdaa 100644 --- a/unittests/Basic/DiagnosticTest.cpp +++ b/unittests/Basic/DiagnosticTest.cpp @@ -1,9 +1,8 @@ //===- unittests/Basic/DiagnosticTest.cpp -- Diagnostic engine tests ------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -47,13 +46,13 @@ TEST(DiagnosticTest, suppressAndTrap) { EXPECT_FALSE(Diags.hasUnrecoverableErrorOccurred()); } -// Check that SuppressAfterFatalError works as intended -TEST(DiagnosticTest, suppressAfterFatalError) { - for (unsigned Suppress = 0; Suppress != 2; ++Suppress) { +// Check that FatalsAsError works as intended +TEST(DiagnosticTest, fatalsAsError) { + for (unsigned FatalsAsError = 0; FatalsAsError != 2; ++FatalsAsError) { DiagnosticsEngine Diags(new DiagnosticIDs(), new DiagnosticOptions, new IgnoringDiagConsumer()); - Diags.setSuppressAfterFatalError(Suppress); + Diags.setFatalsAsError(FatalsAsError); // Diag that would set UnrecoverableErrorOccurred and ErrorOccurred. Diags.Report(diag::err_cannot_open_file) << "file" << "error"; @@ -63,16 +62,15 @@ TEST(DiagnosticTest, suppressAfterFatalError) { Diags.Report(diag::warn_mt_message) << "warning"; EXPECT_TRUE(Diags.hasErrorOccurred()); - EXPECT_TRUE(Diags.hasFatalErrorOccurred()); + EXPECT_EQ(Diags.hasFatalErrorOccurred(), FatalsAsError ? 0u : 1u); EXPECT_TRUE(Diags.hasUncompilableErrorOccurred()); EXPECT_TRUE(Diags.hasUnrecoverableErrorOccurred()); // The warning should be emitted and counted only if we're not suppressing // after fatal errors. - EXPECT_EQ(Diags.getNumWarnings(), Suppress ? 0u : 1u); + EXPECT_EQ(Diags.getNumWarnings(), FatalsAsError); } } - TEST(DiagnosticTest, diagnosticError) { DiagnosticsEngine Diags(new DiagnosticIDs(), new DiagnosticOptions, new IgnoringDiagConsumer()); diff --git a/unittests/Basic/FileManagerTest.cpp b/unittests/Basic/FileManagerTest.cpp index 746d9ad5e8..19e2180d3f 100644 --- a/unittests/Basic/FileManagerTest.cpp +++ b/unittests/Basic/FileManagerTest.cpp @@ -1,9 +1,8 @@ //===- unittests/Basic/FileMangerTest.cpp ------------ FileManger tests ---===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -27,7 +26,7 @@ class FakeStatCache : public FileSystemStatCache { private: // Maps a file/directory path to its desired stat result. Anything // not in this map is considered to not exist in the file system. - llvm::StringMap<FileData, llvm::BumpPtrAllocator> StatCalls; + llvm::StringMap<llvm::vfs::Status, llvm::BumpPtrAllocator> StatCalls; void InjectFileOrDirectory(const char *Path, ino_t INode, bool IsFile) { #ifndef _WIN32 @@ -36,15 +35,14 @@ private: Path = NormalizedPath.c_str(); #endif - FileData Data; - Data.Name = Path; - Data.Size = 0; - Data.ModTime = 0; - Data.UniqueID = llvm::sys::fs::UniqueID(1, INode); - Data.IsDirectory = !IsFile; - Data.IsNamedPipe = false; - Data.InPCH = false; - StatCalls[Path] = Data; + auto fileType = IsFile ? + llvm::sys::fs::file_type::regular_file : + llvm::sys::fs::file_type::directory_file; + llvm::vfs::Status Status(Path, llvm::sys::fs::UniqueID(1, INode), + /*MTime*/{}, /*User*/0, /*Group*/0, + /*Size*/0, fileType, + llvm::sys::fs::perms::all_all); + StatCalls[Path] = Status; } public: @@ -59,9 +57,10 @@ public: } // Implement FileSystemStatCache::getStat(). - LookupResult getStat(StringRef Path, FileData &Data, bool isFile, - std::unique_ptr<llvm::vfs::File> *F, - llvm::vfs::FileSystem &FS) override { + std::error_code getStat(StringRef Path, llvm::vfs::Status &Status, + bool isFile, + std::unique_ptr<llvm::vfs::File> *F, + llvm::vfs::FileSystem &FS) override { #ifndef _WIN32 SmallString<128> NormalizedPath(Path); llvm::sys::path::native(NormalizedPath); @@ -69,11 +68,11 @@ public: #endif if (StatCalls.count(Path) != 0) { - Data = StatCalls[Path]; - return CacheExists; + Status = StatCalls[Path]; + return std::error_code(); } - return CacheMissing; // This means the file/directory doesn't exist. + return std::make_error_code(std::errc::no_such_file_or_directory); } }; @@ -222,33 +221,6 @@ TEST_F(FileManagerTest, getFileReturnsNULLForNonexistentFile) { EXPECT_EQ(nullptr, file); } -// When calling getFile(OpenFile=false); getFile(OpenFile=true) the file is -// opened for the second call. -TEST_F(FileManagerTest, getFileDefersOpen) { - llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS( - new llvm::vfs::InMemoryFileSystem()); - FS->addFile("/tmp/test", 0, llvm::MemoryBuffer::getMemBufferCopy("test")); - FS->addFile("/tmp/testv", 0, llvm::MemoryBuffer::getMemBufferCopy("testv")); - FileManager manager(options, FS); - - const FileEntry *file = manager.getFile("/tmp/test", /*OpenFile=*/false); - ASSERT_TRUE(file != nullptr); - ASSERT_TRUE(file->isValid()); - // "real path name" reveals whether the file was actually opened. - EXPECT_FALSE(file->isOpenForTests()); - - file = manager.getFile("/tmp/test", /*OpenFile=*/true); - ASSERT_TRUE(file != nullptr); - ASSERT_TRUE(file->isValid()); - EXPECT_TRUE(file->isOpenForTests()); - - // However we should never try to open a file previously opened as virtual. - ASSERT_TRUE(manager.getVirtualFile("/tmp/testv", 5, 0)); - ASSERT_TRUE(manager.getFile("/tmp/testv", /*OpenFile=*/false)); - file = manager.getFile("/tmp/testv", /*OpenFile=*/true); - EXPECT_FALSE(file->isOpenForTests()); -} - // The following tests apply to Unix-like system only. #ifndef _WIN32 @@ -374,4 +346,37 @@ TEST_F(FileManagerTest, getVirtualFileFillsRealPathName) { EXPECT_EQ(file->tryGetRealPathName(), ExpectedResult); } +TEST_F(FileManagerTest, getFileDontOpenRealPath) { + SmallString<64> CustomWorkingDir; +#ifdef _WIN32 + CustomWorkingDir = "C:/"; +#else + CustomWorkingDir = "/"; +#endif + + auto FS = IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem>( + new llvm::vfs::InMemoryFileSystem); + // setCurrentworkingdirectory must finish without error. + ASSERT_TRUE(!FS->setCurrentWorkingDirectory(CustomWorkingDir)); + + FileSystemOptions Opts; + FileManager Manager(Opts, FS); + + // Inject fake files into the file system. + auto statCache = llvm::make_unique<FakeStatCache>(); + statCache->InjectDirectory("/tmp", 42); + statCache->InjectFile("/tmp/test", 43); + + Manager.setStatCache(std::move(statCache)); + + // Check for real path. + const FileEntry *file = Manager.getFile("/tmp/test", /*OpenFile=*/false); + ASSERT_TRUE(file != nullptr); + ASSERT_TRUE(file->isValid()); + SmallString<64> ExpectedResult = CustomWorkingDir; + + llvm::sys::path::append(ExpectedResult, "tmp", "test"); + EXPECT_EQ(file->tryGetRealPathName(), ExpectedResult); +} + } // anonymous namespace diff --git a/unittests/Basic/FixedPointTest.cpp b/unittests/Basic/FixedPointTest.cpp index 8e184a7af8..5d991c0720 100644 --- a/unittests/Basic/FixedPointTest.cpp +++ b/unittests/Basic/FixedPointTest.cpp @@ -1,9 +1,8 @@ //===- unittests/Basic/FixedPointTest.cpp -- fixed point number tests -----===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Basic/MemoryBufferCacheTest.cpp b/unittests/Basic/MemoryBufferCacheTest.cpp deleted file mode 100644 index 99178f8150..0000000000 --- a/unittests/Basic/MemoryBufferCacheTest.cpp +++ /dev/null @@ -1,94 +0,0 @@ -//===- MemoryBufferCacheTest.cpp - MemoryBufferCache tests ----------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "clang/Basic/MemoryBufferCache.h" -#include "llvm/Support/MemoryBuffer.h" -#include "gtest/gtest.h" - -using namespace llvm; -using namespace clang; - -namespace { - -std::unique_ptr<MemoryBuffer> getBuffer(int I) { - SmallVector<char, 8> Bytes; - raw_svector_ostream(Bytes) << "data:" << I; - return MemoryBuffer::getMemBuffer(StringRef(Bytes.data(), Bytes.size()), "", - /* RequiresNullTerminator = */ false); -} - -TEST(MemoryBufferCacheTest, addBuffer) { - auto B1 = getBuffer(1); - auto B2 = getBuffer(2); - auto B3 = getBuffer(3); - auto *RawB1 = B1.get(); - auto *RawB2 = B2.get(); - auto *RawB3 = B3.get(); - - // Add a few buffers. - MemoryBufferCache Cache; - EXPECT_EQ(RawB1, &Cache.addBuffer("1", std::move(B1))); - EXPECT_EQ(RawB2, &Cache.addBuffer("2", std::move(B2))); - EXPECT_EQ(RawB3, &Cache.addBuffer("3", std::move(B3))); - EXPECT_EQ(RawB1, Cache.lookupBuffer("1")); - EXPECT_EQ(RawB2, Cache.lookupBuffer("2")); - EXPECT_EQ(RawB3, Cache.lookupBuffer("3")); - EXPECT_FALSE(Cache.isBufferFinal("1")); - EXPECT_FALSE(Cache.isBufferFinal("2")); - EXPECT_FALSE(Cache.isBufferFinal("3")); - - // Remove the middle buffer. - EXPECT_FALSE(Cache.tryToRemoveBuffer("2")); - EXPECT_EQ(nullptr, Cache.lookupBuffer("2")); - EXPECT_FALSE(Cache.isBufferFinal("2")); - - // Replace the middle buffer. - B2 = getBuffer(2); - RawB2 = B2.get(); - EXPECT_EQ(RawB2, &Cache.addBuffer("2", std::move(B2))); - - // Check that nothing is final. - EXPECT_FALSE(Cache.isBufferFinal("1")); - EXPECT_FALSE(Cache.isBufferFinal("2")); - EXPECT_FALSE(Cache.isBufferFinal("3")); -} - -TEST(MemoryBufferCacheTest, finalizeCurrentBuffers) { - // Add a buffer. - MemoryBufferCache Cache; - auto B1 = getBuffer(1); - auto *RawB1 = B1.get(); - Cache.addBuffer("1", std::move(B1)); - ASSERT_FALSE(Cache.isBufferFinal("1")); - - // Finalize it. - Cache.finalizeCurrentBuffers(); - EXPECT_TRUE(Cache.isBufferFinal("1")); - EXPECT_TRUE(Cache.tryToRemoveBuffer("1")); - EXPECT_EQ(RawB1, Cache.lookupBuffer("1")); - EXPECT_TRUE(Cache.isBufferFinal("1")); - - // Repeat. - auto B2 = getBuffer(2); - auto *RawB2 = B2.get(); - Cache.addBuffer("2", std::move(B2)); - EXPECT_FALSE(Cache.isBufferFinal("2")); - - Cache.finalizeCurrentBuffers(); - EXPECT_TRUE(Cache.isBufferFinal("1")); - EXPECT_TRUE(Cache.isBufferFinal("2")); - EXPECT_TRUE(Cache.tryToRemoveBuffer("1")); - EXPECT_TRUE(Cache.tryToRemoveBuffer("2")); - EXPECT_EQ(RawB1, Cache.lookupBuffer("1")); - EXPECT_EQ(RawB2, Cache.lookupBuffer("2")); - EXPECT_TRUE(Cache.isBufferFinal("1")); - EXPECT_TRUE(Cache.isBufferFinal("2")); -} - -} // namespace diff --git a/unittests/Basic/SourceManagerTest.cpp b/unittests/Basic/SourceManagerTest.cpp index b548bf5752..ff8a364736 100644 --- a/unittests/Basic/SourceManagerTest.cpp +++ b/unittests/Basic/SourceManagerTest.cpp @@ -1,9 +1,8 @@ //===- unittests/Basic/SourceManagerTest.cpp ------ SourceManager tests ---===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -12,7 +11,6 @@ #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/LangOptions.h" -#include "clang/Basic/MemoryBufferCache.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/TargetOptions.h" #include "clang/Lex/HeaderSearch.h" @@ -61,11 +59,10 @@ TEST_F(SourceManagerTest, isBeforeInTranslationUnit) { SourceMgr.setMainFileID(mainFileID); TrivialModuleLoader ModLoader; - MemoryBufferCache PCMCache; HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr, Diags, LangOpts, &*Target); Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts, - SourceMgr, PCMCache, HeaderInfo, ModLoader, + SourceMgr, HeaderInfo, ModLoader, /*IILookup =*/nullptr, /*OwnsHeaderSearch =*/false); PP.Initialize(*Target); @@ -230,11 +227,10 @@ TEST_F(SourceManagerTest, getMacroArgExpandedLocation) { SourceMgr.overrideFileContents(headerFile, std::move(HeaderBuf)); TrivialModuleLoader ModLoader; - MemoryBufferCache PCMCache; HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr, Diags, LangOpts, &*Target); Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts, - SourceMgr, PCMCache, HeaderInfo, ModLoader, + SourceMgr, HeaderInfo, ModLoader, /*IILookup =*/nullptr, /*OwnsHeaderSearch =*/false); PP.Initialize(*Target); @@ -349,11 +345,10 @@ TEST_F(SourceManagerTest, isBeforeInTranslationUnitWithMacroInInclude) { SourceMgr.overrideFileContents(headerFile, std::move(HeaderBuf)); TrivialModuleLoader ModLoader; - MemoryBufferCache PCMCache; HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr, Diags, LangOpts, &*Target); Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts, - SourceMgr, PCMCache, HeaderInfo, ModLoader, + SourceMgr, HeaderInfo, ModLoader, /*IILookup =*/nullptr, /*OwnsHeaderSearch =*/false); PP.Initialize(*Target); diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 6eff59986c..4c8a3a840d 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -32,3 +32,4 @@ if(NOT WIN32 AND CLANG_TOOL_LIBCLANG_BUILD) endif() add_subdirectory(Rename) add_subdirectory(Index) +add_subdirectory(Serialization) diff --git a/unittests/CodeGen/BufferSourceTest.cpp b/unittests/CodeGen/BufferSourceTest.cpp index 1934e66138..c1c2bf818c 100644 --- a/unittests/CodeGen/BufferSourceTest.cpp +++ b/unittests/CodeGen/BufferSourceTest.cpp @@ -1,9 +1,8 @@ //===- unittests/CodeGen/BufferSourceTest.cpp - MemoryBuffer source tests -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/CodeGen/CodeGenExternalTest.cpp b/unittests/CodeGen/CodeGenExternalTest.cpp index bcec3eab06..8dff45c8a0 100644 --- a/unittests/CodeGen/CodeGenExternalTest.cpp +++ b/unittests/CodeGen/CodeGenExternalTest.cpp @@ -1,9 +1,8 @@ //===- unittests/CodeGen/CodeGenExternalTest.cpp - test external CodeGen -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/CodeGen/IRMatchers.h b/unittests/CodeGen/IRMatchers.h index 5150ca40fb..9cc2a31777 100644 --- a/unittests/CodeGen/IRMatchers.h +++ b/unittests/CodeGen/IRMatchers.h @@ -1,9 +1,8 @@ //=== unittests/CodeGen/IRMatchers.h - Match on the LLVM IR -----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// \file diff --git a/unittests/CodeGen/IncrementalProcessingTest.cpp b/unittests/CodeGen/IncrementalProcessingTest.cpp index 40b814bf31..045ed9bbc7 100644 --- a/unittests/CodeGen/IncrementalProcessingTest.cpp +++ b/unittests/CodeGen/IncrementalProcessingTest.cpp @@ -1,9 +1,8 @@ //=== unittests/CodeGen/IncrementalProcessingTest.cpp - IncrementalCodeGen ===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/CodeGen/TBAAMetadataTest.cpp b/unittests/CodeGen/TBAAMetadataTest.cpp index 7514160e6e..6535fe27b3 100644 --- a/unittests/CodeGen/TBAAMetadataTest.cpp +++ b/unittests/CodeGen/TBAAMetadataTest.cpp @@ -1,9 +1,8 @@ //=== unittests/CodeGen/TBAAMetadataTest.cpp - Checks metadata generation -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/CrossTU/CrossTranslationUnitTest.cpp b/unittests/CrossTU/CrossTranslationUnitTest.cpp index dd82743ac6..43e0e75c31 100644 --- a/unittests/CrossTU/CrossTranslationUnitTest.cpp +++ b/unittests/CrossTU/CrossTranslationUnitTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/CrossTranslationUnitTest.cpp - Tooling unit tests -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Driver/DistroTest.cpp b/unittests/Driver/DistroTest.cpp index bc1863c429..d0c86d1c54 100644 --- a/unittests/Driver/DistroTest.cpp +++ b/unittests/Driver/DistroTest.cpp @@ -1,9 +1,8 @@ //===- unittests/Driver/DistroTest.cpp --- ToolChains tests ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/unittests/Driver/ModuleCacheTest.cpp b/unittests/Driver/ModuleCacheTest.cpp index 7340889796..db3395f4ab 100644 --- a/unittests/Driver/ModuleCacheTest.cpp +++ b/unittests/Driver/ModuleCacheTest.cpp @@ -1,9 +1,8 @@ //===- unittests/Driver/ModuleCacheTest.cpp -------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/unittests/Driver/MultilibTest.cpp b/unittests/Driver/MultilibTest.cpp index c5e8e0970d..0731c81d9f 100644 --- a/unittests/Driver/MultilibTest.cpp +++ b/unittests/Driver/MultilibTest.cpp @@ -1,9 +1,8 @@ //===- unittests/Driver/MultilibTest.cpp --- Multilib tests ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -350,3 +349,27 @@ TEST(MultilibTest, SetCombineWith) { Latte.combineWith(Milk); ASSERT_EQ(Latte.size(), (unsigned)2); } + +TEST(MultilibTest, SetPriority) { + MultilibSet MS; + MS.push_back(Multilib("foo", {}, {}, 1).flag("+foo")); + MS.push_back(Multilib("bar", {}, {}, 2).flag("+bar")); + + Multilib::flags_list Flags1; + Flags1.push_back("+foo"); + Flags1.push_back("-bar"); + Multilib Selection1; + ASSERT_TRUE(MS.select(Flags1, Selection1)) + << "Flag set was {\"+foo\"}, but selection not found"; + ASSERT_TRUE(Selection1.gccSuffix() == "/foo") + << "Selection picked " << Selection1 << " which was not expected"; + + Multilib::flags_list Flags2; + Flags2.push_back("+foo"); + Flags2.push_back("+bar"); + Multilib Selection2; + ASSERT_TRUE(MS.select(Flags2, Selection2)) + << "Flag set was {\"+bar\"}, but selection not found"; + ASSERT_TRUE(Selection2.gccSuffix() == "/bar") + << "Selection picked " << Selection2 << " which was not expected"; +} diff --git a/unittests/Driver/ToolChainTest.cpp b/unittests/Driver/ToolChainTest.cpp index f1181072a7..80938c83f8 100644 --- a/unittests/Driver/ToolChainTest.cpp +++ b/unittests/Driver/ToolChainTest.cpp @@ -1,9 +1,8 @@ //===- unittests/Driver/ToolChainTest.cpp --- ToolChain tests -------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/unittests/Format/CMakeLists.txt b/unittests/Format/CMakeLists.txt index 015c25ee6b..bf02de9705 100644 --- a/unittests/Format/CMakeLists.txt +++ b/unittests/Format/CMakeLists.txt @@ -6,6 +6,7 @@ add_clang_unittest(FormatTests CleanupTest.cpp FormatTest.cpp FormatTestComments.cpp + FormatTestCSharp.cpp FormatTestJS.cpp FormatTestJava.cpp FormatTestObjC.cpp diff --git a/unittests/Format/CleanupTest.cpp b/unittests/Format/CleanupTest.cpp index f4a36d8e1f..0628c38a2b 100644 --- a/unittests/Format/CleanupTest.cpp +++ b/unittests/Format/CleanupTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Format/CleanupTest.cpp - Code cleanup unit tests ----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -421,8 +420,10 @@ TEST_F(CleanUpReplacementsTest, InsertMultipleNewHeadersAndSortLLVM) { TEST_F(CleanUpReplacementsTest, InsertMultipleNewHeadersAndSortGoogle) { std::string Code = "\nint x;"; std::string Expected = "\n#include \"fix.h\"\n" + "\n" "#include <list>\n" "#include <vector>\n" + "\n" "#include \"a.h\"\n" "#include \"b.h\"\n" "#include \"c.h\"\n" diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index c05fceb476..31f40b1670 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Format/FormatTest.cpp - Formatting unit tests -------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -121,6 +120,15 @@ TEST_F(FormatTest, MessUp) { EXPECT_EQ("a\n#b c d\ne", test::messUp("a\n#b\\\nc\\\nd\ne")); } +TEST_F(FormatTest, DefaultLLVMStyleIsCpp) { + EXPECT_EQ(FormatStyle::LK_Cpp, getLLVMStyle().Language); +} + +TEST_F(FormatTest, LLVMStyleOverride) { + EXPECT_EQ(FormatStyle::LK_Proto, + getLLVMStyle(FormatStyle::LK_Proto).Language); +} + //===----------------------------------------------------------------------===// // Basic function tests. //===----------------------------------------------------------------------===// @@ -431,7 +439,8 @@ TEST_F(FormatTest, FormatIfWithoutCompoundStatement) { FormatStyle AllowsMergedIf = getLLVMStyle(); AllowsMergedIf.AlignEscapedNewlines = FormatStyle::ENAS_Left; - AllowsMergedIf.AllowShortIfStatementsOnASingleLine = true; + AllowsMergedIf.AllowShortIfStatementsOnASingleLine = + FormatStyle::SIS_WithoutElse; verifyFormat("if (a)\n" " // comment\n" " f();", @@ -479,6 +488,41 @@ TEST_F(FormatTest, FormatIfWithoutCompoundStatement) { verifyFormat("if (a)\n return;", AllowsMergedIf); } +TEST_F(FormatTest, FormatIfWithoutCompoundStatementButElseWith) { + FormatStyle AllowsMergedIf = getLLVMStyle(); + AllowsMergedIf.AlignEscapedNewlines = FormatStyle::ENAS_Left; + AllowsMergedIf.AllowShortIfStatementsOnASingleLine = + FormatStyle::SIS_WithoutElse; + verifyFormat("if (a)\n" + " f();\n" + "else {\n" + " g();\n" + "}", + AllowsMergedIf); + verifyFormat("if (a)\n" + " f();\n" + "else\n" + " g();\n", + AllowsMergedIf); + + AllowsMergedIf.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_Always; + + verifyFormat("if (a) f();\n" + "else {\n" + " g();\n" + "}", + AllowsMergedIf); + verifyFormat("if (a) f();\n" + "else {\n" + " if (a) f();\n" + " else {\n" + " g();\n" + " }\n" + " g();\n" + "}", + AllowsMergedIf); +} + TEST_F(FormatTest, FormatLoopsWithoutCompoundStatement) { FormatStyle AllowsMergedLoops = getLLVMStyle(); AllowsMergedLoops.AllowShortLoopsOnASingleLine = true; @@ -507,7 +551,8 @@ TEST_F(FormatTest, FormatShortBracedStatements) { AllowSimpleBracedStatements.ColumnLimit = 40; AllowSimpleBracedStatements.AllowShortBlocksOnASingleLine = true; - AllowSimpleBracedStatements.AllowShortIfStatementsOnASingleLine = true; + AllowSimpleBracedStatements.AllowShortIfStatementsOnASingleLine = + FormatStyle::SIS_WithoutElse; AllowSimpleBracedStatements.AllowShortLoopsOnASingleLine = true; AllowSimpleBracedStatements.BreakBeforeBraces = FormatStyle::BS_Custom; @@ -555,7 +600,8 @@ TEST_F(FormatTest, FormatShortBracedStatements) { "};", AllowSimpleBracedStatements); - AllowSimpleBracedStatements.AllowShortIfStatementsOnASingleLine = false; + AllowSimpleBracedStatements.AllowShortIfStatementsOnASingleLine = + FormatStyle::SIS_Never; verifyFormat("if (true) {}", AllowSimpleBracedStatements); verifyFormat("if (true) {\n" " f();\n" @@ -580,7 +626,8 @@ TEST_F(FormatTest, FormatShortBracedStatements) { "}", AllowSimpleBracedStatements); - AllowSimpleBracedStatements.AllowShortIfStatementsOnASingleLine = true; + AllowSimpleBracedStatements.AllowShortIfStatementsOnASingleLine = + FormatStyle::SIS_WithoutElse; AllowSimpleBracedStatements.AllowShortLoopsOnASingleLine = true; AllowSimpleBracedStatements.BraceWrapping.AfterControlStatement = true; @@ -617,7 +664,8 @@ TEST_F(FormatTest, FormatShortBracedStatements) { "}", AllowSimpleBracedStatements); - AllowSimpleBracedStatements.AllowShortIfStatementsOnASingleLine = false; + AllowSimpleBracedStatements.AllowShortIfStatementsOnASingleLine = + FormatStyle::SIS_Never; verifyFormat("if (true) {}", AllowSimpleBracedStatements); verifyFormat("if (true)\n" "{\n" @@ -651,7 +699,7 @@ TEST_F(FormatTest, FormatShortBracedStatements) { TEST_F(FormatTest, ShortBlocksInMacrosDontMergeWithCodeAfterMacro) { FormatStyle Style = getLLVMStyleWithColumns(60); Style.AllowShortBlocksOnASingleLine = true; - Style.AllowShortIfStatementsOnASingleLine = true; + Style.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_WithoutElse; Style.BreakBeforeBraces = FormatStyle::BS_Allman; EXPECT_EQ("#define A \\\n" " if (HANDLEwernufrnuLwrmviferuvnierv) \\\n" @@ -1069,6 +1117,7 @@ TEST_F(FormatTest, FormatsSwitchStatement) { Style.IndentCaseLabels = true; Style.AllowShortBlocksOnASingleLine = false; Style.BreakBeforeBraces = FormatStyle::BS_Custom; + Style.BraceWrapping.AfterCaseLabel = true; Style.BraceWrapping.AfterControlStatement = true; EXPECT_EQ("switch (n)\n" "{\n" @@ -1090,6 +1139,27 @@ TEST_F(FormatTest, FormatsSwitchStatement) { " }\n" "}", Style)); + Style.BraceWrapping.AfterCaseLabel = false; + EXPECT_EQ("switch (n)\n" + "{\n" + " case 0: {\n" + " return false;\n" + " }\n" + " default: {\n" + " return true;\n" + " }\n" + "}", + format("switch (n) {\n" + " case 0:\n" + " {\n" + " return false;\n" + " }\n" + " default:\n" + " {\n" + " return true;\n" + " }\n" + "}", + Style)); } TEST_F(FormatTest, CaseRanges) { @@ -1243,6 +1313,7 @@ TEST_F(FormatTest, ShortCaseLabels) { Style)); Style.AllowShortCaseLabelsOnASingleLine = true; Style.BreakBeforeBraces = FormatStyle::BS_Custom; + Style.BraceWrapping.AfterCaseLabel = true; Style.BraceWrapping.AfterControlStatement = true; EXPECT_EQ("switch (n)\n" "{\n" @@ -2397,6 +2468,12 @@ TEST_F(FormatTest, HashInMacroDefinition) { TEST_F(FormatTest, RespectWhitespaceInMacroDefinitions) { EXPECT_EQ("#define A (x)", format("#define A (x)")); EXPECT_EQ("#define A(x)", format("#define A(x)")); + + FormatStyle Style = getLLVMStyle(); + Style.SpaceBeforeParens = FormatStyle::SBPO_Never; + verifyFormat("#define true ((foo)1)", Style); + Style.SpaceBeforeParens = FormatStyle::SBPO_Always; + verifyFormat("#define false((foo)0)", Style); } TEST_F(FormatTest, EmptyLinesInMacroDefinitions) { @@ -2507,6 +2584,12 @@ TEST_F(FormatTest, MacrosWithoutTrailingSemicolon) { verifyFormat("VISIT_GL_CALL(GenBuffers, void, (GLsizei n, GLuint* buffers), " "(n, buffers))\n", getChromiumStyle(FormatStyle::LK_Cpp)); + + // See PR41483 + EXPECT_EQ("/**/ FOO(a)\n" + "FOO(b)", + format("/**/ FOO(a)\n" + "FOO(b)")); } TEST_F(FormatTest, MacroCallsWithoutTrailingSemicolon) { @@ -2945,22 +3028,25 @@ TEST_F(FormatTest, IndentPreprocessorDirectives) { EXPECT_EQ(Expected, format(ToFormat, Style)); EXPECT_EQ(Expected, format(Expected, Style)); } - // Test with tabs. - Style.UseTab = FormatStyle::UT_Always; - Style.IndentWidth = 8; - Style.TabWidth = 8; - verifyFormat("#ifdef _WIN32\n" - "#\tdefine A 0\n" - "#\tifdef VAR2\n" - "#\t\tdefine B 1\n" - "#\t\tinclude <someheader.h>\n" - "#\t\tdefine MACRO \\\n" - "\t\t\tsome_very_long_func_aaaaaaaaaa();\n" - "#\tendif\n" - "#else\n" - "#\tdefine A 1\n" - "#endif", - Style); + // Test AfterHash with tabs. + { + FormatStyle Tabbed = Style; + Tabbed.UseTab = FormatStyle::UT_Always; + Tabbed.IndentWidth = 8; + Tabbed.TabWidth = 8; + verifyFormat("#ifdef _WIN32\n" + "#\tdefine A 0\n" + "#\tifdef VAR2\n" + "#\t\tdefine B 1\n" + "#\t\tinclude <someheader.h>\n" + "#\t\tdefine MACRO \\\n" + "\t\t\tsome_very_long_func_aaaaaaaaaa();\n" + "#\tendif\n" + "#else\n" + "#\tdefine A 1\n" + "#endif", + Tabbed); + } // Regression test: Multiline-macro inside include guards. verifyFormat("#ifndef HEADER_H\n" @@ -2970,6 +3056,102 @@ TEST_F(FormatTest, IndentPreprocessorDirectives) { " int j;\n" "#endif // HEADER_H", getLLVMStyleWithColumns(20)); + + Style.IndentPPDirectives = FormatStyle::PPDIS_BeforeHash; + // Basic before hash indent tests + verifyFormat("#ifdef _WIN32\n" + " #define A 0\n" + " #ifdef VAR2\n" + " #define B 1\n" + " #include <someheader.h>\n" + " #define MACRO \\\n" + " some_very_long_func_aaaaaaaaaa();\n" + " #endif\n" + "#else\n" + " #define A 1\n" + "#endif", + Style); + verifyFormat("#if A\n" + " #define MACRO \\\n" + " void a(int x) { \\\n" + " b(); \\\n" + " c(); \\\n" + " d(); \\\n" + " e(); \\\n" + " f(); \\\n" + " }\n" + "#endif", + Style); + // Keep comments aligned with indented directives. These + // tests cannot use verifyFormat because messUp manipulates leading + // whitespace. + { + const char *Expected = "void f() {\n" + "// Aligned to preprocessor.\n" + "#if 1\n" + " // Aligned to code.\n" + " int a;\n" + " #if 1\n" + " // Aligned to preprocessor.\n" + " #define A 0\n" + " // Aligned to code.\n" + " int b;\n" + " #endif\n" + "#endif\n" + "}"; + const char *ToFormat = "void f() {\n" + "// Aligned to preprocessor.\n" + "#if 1\n" + "// Aligned to code.\n" + "int a;\n" + "#if 1\n" + "// Aligned to preprocessor.\n" + "#define A 0\n" + "// Aligned to code.\n" + "int b;\n" + "#endif\n" + "#endif\n" + "}"; + EXPECT_EQ(Expected, format(ToFormat, Style)); + EXPECT_EQ(Expected, format(Expected, Style)); + } + { + const char *Expected = "void f() {\n" + "/* Aligned to preprocessor. */\n" + "#if 1\n" + " /* Aligned to code. */\n" + " int a;\n" + " #if 1\n" + " /* Aligned to preprocessor. */\n" + " #define A 0\n" + " /* Aligned to code. */\n" + " int b;\n" + " #endif\n" + "#endif\n" + "}"; + const char *ToFormat = "void f() {\n" + "/* Aligned to preprocessor. */\n" + "#if 1\n" + "/* Aligned to code. */\n" + "int a;\n" + "#if 1\n" + "/* Aligned to preprocessor. */\n" + "#define A 0\n" + "/* Aligned to code. */\n" + "int b;\n" + "#endif\n" + "#endif\n" + "}"; + EXPECT_EQ(Expected, format(ToFormat, Style)); + EXPECT_EQ(Expected, format(Expected, Style)); + } + + // Test single comment before preprocessor + verifyFormat("// Comment\n" + "\n" + "#if 1\n" + "#endif", + Style); } TEST_F(FormatTest, FormatHashIfNotAtStartOfLine) { @@ -3149,7 +3331,7 @@ TEST_F(FormatTest, GraciouslyHandleIncorrectPreprocessorConditions) { TEST_F(FormatTest, FormatsJoinedLinesOnSubsequentRuns) { FormatStyle SingleLine = getLLVMStyle(); - SingleLine.AllowShortIfStatementsOnASingleLine = true; + SingleLine.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_WithoutElse; verifyFormat("#if 0\n" "#elif 1\n" "#endif\n" @@ -3809,6 +3991,191 @@ TEST_F(FormatTest, ConstructorInitializers) { " aaaa(aaaa) {}")); } +TEST_F(FormatTest, AllowAllConstructorInitializersOnNextLine) { + FormatStyle Style = getLLVMStyle(); + Style.BreakConstructorInitializers = FormatStyle::BCIS_BeforeComma; + Style.ColumnLimit = 60; + Style.ConstructorInitializerAllOnOneLineOrOnePerLine = true; + Style.AllowAllConstructorInitializersOnNextLine = true; + Style.BinPackParameters = false; + + for (int i = 0; i < 4; ++i) { + // Test all combinations of parameters that should not have an effect. + Style.AllowAllParametersOfDeclarationOnNextLine = i & 1; + Style.AllowAllArgumentsOnNextLine = i & 2; + + Style.AllowAllConstructorInitializersOnNextLine = true; + Style.BreakConstructorInitializers = FormatStyle::BCIS_BeforeComma; + verifyFormat("Constructor()\n" + " : aaaaaaaaaaaaaaaaaaaa(a), bbbbbbbbbbbbbbbbbbbbb(b) {}", + Style); + verifyFormat("Constructor() : a(a), b(b) {}", Style); + + Style.AllowAllConstructorInitializersOnNextLine = false; + verifyFormat("Constructor()\n" + " : aaaaaaaaaaaaaaaaaaaa(a)\n" + " , bbbbbbbbbbbbbbbbbbbbb(b) {}", + Style); + verifyFormat("Constructor() : a(a), b(b) {}", Style); + + Style.BreakConstructorInitializers = FormatStyle::BCIS_BeforeColon; + Style.AllowAllConstructorInitializersOnNextLine = true; + verifyFormat("Constructor()\n" + " : aaaaaaaaaaaaaaaaaaaa(a), bbbbbbbbbbbbbbbbbbbbb(b) {}", + Style); + + Style.AllowAllConstructorInitializersOnNextLine = false; + verifyFormat("Constructor()\n" + " : aaaaaaaaaaaaaaaaaaaa(a),\n" + " bbbbbbbbbbbbbbbbbbbbb(b) {}", + Style); + + Style.BreakConstructorInitializers = FormatStyle::BCIS_AfterColon; + Style.AllowAllConstructorInitializersOnNextLine = true; + verifyFormat("Constructor() :\n" + " aaaaaaaaaaaaaaaaaa(a), bbbbbbbbbbbbbbbbbbbbb(b) {}", + Style); + + Style.AllowAllConstructorInitializersOnNextLine = false; + verifyFormat("Constructor() :\n" + " aaaaaaaaaaaaaaaaaa(a),\n" + " bbbbbbbbbbbbbbbbbbbbb(b) {}", + Style); + } + + // Test interactions between AllowAllParametersOfDeclarationOnNextLine and + // AllowAllConstructorInitializersOnNextLine in all + // BreakConstructorInitializers modes + Style.BreakConstructorInitializers = FormatStyle::BCIS_BeforeComma; + Style.AllowAllParametersOfDeclarationOnNextLine = true; + Style.AllowAllConstructorInitializersOnNextLine = false; + verifyFormat("SomeClassWithALongName::Constructor(\n" + " int aaaaaaaaaaaaaaaaaaaaaaaa, int bbbbbbbbbbbbb)\n" + " : aaaaaaaaaaaaaaaaaaaa(a)\n" + " , bbbbbbbbbbbbbbbbbbbbb(b) {}", + Style); + + Style.AllowAllConstructorInitializersOnNextLine = true; + verifyFormat("SomeClassWithALongName::Constructor(\n" + " int aaaaaaaaaaaaaaaaaaaaaaaa,\n" + " int bbbbbbbbbbbbb,\n" + " int cccccccccccccccc)\n" + " : aaaaaaaaaaaaaaaaaaaa(a), bbbbbbbbbbbbbbbbbbbbb(b) {}", + Style); + + Style.AllowAllParametersOfDeclarationOnNextLine = false; + Style.AllowAllConstructorInitializersOnNextLine = false; + verifyFormat("SomeClassWithALongName::Constructor(\n" + " int aaaaaaaaaaaaaaaaaaaaaaaa,\n" + " int bbbbbbbbbbbbb)\n" + " : aaaaaaaaaaaaaaaaaaaa(a)\n" + " , bbbbbbbbbbbbbbbbbbbbb(b) {}", + Style); + + Style.BreakConstructorInitializers = FormatStyle::BCIS_BeforeColon; + + Style.AllowAllParametersOfDeclarationOnNextLine = true; + verifyFormat("SomeClassWithALongName::Constructor(\n" + " int aaaaaaaaaaaaaaaaaaaaaaaa, int bbbbbbbbbbbbb)\n" + " : aaaaaaaaaaaaaaaaaaaa(a),\n" + " bbbbbbbbbbbbbbbbbbbbb(b) {}", + Style); + + Style.AllowAllConstructorInitializersOnNextLine = true; + verifyFormat("SomeClassWithALongName::Constructor(\n" + " int aaaaaaaaaaaaaaaaaaaaaaaa,\n" + " int bbbbbbbbbbbbb,\n" + " int cccccccccccccccc)\n" + " : aaaaaaaaaaaaaaaaaaaa(a), bbbbbbbbbbbbbbbbbbbbb(b) {}", + Style); + + Style.AllowAllParametersOfDeclarationOnNextLine = false; + Style.AllowAllConstructorInitializersOnNextLine = false; + verifyFormat("SomeClassWithALongName::Constructor(\n" + " int aaaaaaaaaaaaaaaaaaaaaaaa,\n" + " int bbbbbbbbbbbbb)\n" + " : aaaaaaaaaaaaaaaaaaaa(a),\n" + " bbbbbbbbbbbbbbbbbbbbb(b) {}", + Style); + + Style.BreakConstructorInitializers = FormatStyle::BCIS_AfterColon; + Style.AllowAllParametersOfDeclarationOnNextLine = true; + verifyFormat("SomeClassWithALongName::Constructor(\n" + " int aaaaaaaaaaaaaaaaaaaaaaaa, int bbbbbbbbbbbbb) :\n" + " aaaaaaaaaaaaaaaaaaaa(a),\n" + " bbbbbbbbbbbbbbbbbbbbb(b) {}", + Style); + + Style.AllowAllConstructorInitializersOnNextLine = true; + verifyFormat("SomeClassWithALongName::Constructor(\n" + " int aaaaaaaaaaaaaaaaaaaaaaaa,\n" + " int bbbbbbbbbbbbb,\n" + " int cccccccccccccccc) :\n" + " aaaaaaaaaaaaaaaaaaaa(a), bbbbbbbbbbbbbbbbbbbbb(b) {}", + Style); + + Style.AllowAllParametersOfDeclarationOnNextLine = false; + Style.AllowAllConstructorInitializersOnNextLine = false; + verifyFormat("SomeClassWithALongName::Constructor(\n" + " int aaaaaaaaaaaaaaaaaaaaaaaa,\n" + " int bbbbbbbbbbbbb) :\n" + " aaaaaaaaaaaaaaaaaaaa(a),\n" + " bbbbbbbbbbbbbbbbbbbbb(b) {}", + Style); +} + +TEST_F(FormatTest, AllowAllArgumentsOnNextLine) { + FormatStyle Style = getLLVMStyle(); + Style.ColumnLimit = 60; + Style.BinPackArguments = false; + for (int i = 0; i < 4; ++i) { + // Test all combinations of parameters that should not have an effect. + Style.AllowAllParametersOfDeclarationOnNextLine = i & 1; + Style.AllowAllConstructorInitializersOnNextLine = i & 2; + + Style.AllowAllArgumentsOnNextLine = true; + verifyFormat("void foo() {\n" + " FunctionCallWithReallyLongName(\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbb);\n" + "}", + Style); + Style.AllowAllArgumentsOnNextLine = false; + verifyFormat("void foo() {\n" + " FunctionCallWithReallyLongName(\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" + " bbbbbbbbbbbb);\n" + "}", + Style); + + Style.AllowAllArgumentsOnNextLine = true; + verifyFormat("void foo() {\n" + " auto VariableWithReallyLongName = {\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbb};\n" + "}", + Style); + Style.AllowAllArgumentsOnNextLine = false; + verifyFormat("void foo() {\n" + " auto VariableWithReallyLongName = {\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" + " bbbbbbbbbbbb};\n" + "}", + Style); + } + + // This parameter should not affect declarations. + Style.BinPackParameters = false; + Style.AllowAllArgumentsOnNextLine = false; + Style.AllowAllParametersOfDeclarationOnNextLine = true; + verifyFormat("void FunctionCallWithReallyLongName(\n" + " int aaaaaaaaaaaaaaaaaaaaaaa, int bbbbbbbbbbbb);", + Style); + Style.AllowAllParametersOfDeclarationOnNextLine = false; + verifyFormat("void FunctionCallWithReallyLongName(\n" + " int aaaaaaaaaaaaaaaaaaaaaaa,\n" + " int bbbbbbbbbbbb);", + Style); +} + TEST_F(FormatTest, BreakConstructorInitializersAfterColon) { FormatStyle Style = getLLVMStyle(); Style.BreakConstructorInitializers = FormatStyle::BCIS_AfterColon; @@ -3826,17 +4193,23 @@ TEST_F(FormatTest, BreakConstructorInitializersAfterColon) { verifyFormat("template <typename T>\n" "Constructor() : Initializer(FitsOnTheLine) {}", getStyleWithColumns(Style, 50)); + Style.ConstructorInitializerAllOnOneLineOrOnePerLine = true; + verifyFormat( + "SomeClass::Constructor() :\n" + " aaaaaaaaaaaaa(aaaaaaaaaaaaaa), aaaaaaaaaaaaaaa(aaaaaaaaaaaa) {}", + Style); + Style.ConstructorInitializerAllOnOneLineOrOnePerLine = false; verifyFormat( "SomeClass::Constructor() :\n" " aaaaaaaaaaaaa(aaaaaaaaaaaaaa), aaaaaaaaaaaaaaa(aaaaaaaaaaaa) {}", - Style); + Style); verifyFormat( "SomeClass::Constructor() :\n" " aaaaaaaaaaaaa(aaaaaaaaaaaaaa), aaaaaaaaaaaaa(aaaaaaaaaaaaaa),\n" " aaaaaaaaaaaaa(aaaaaaaaaaaaaa) {}", - Style); + Style); verifyFormat( "SomeClass::Constructor() :\n" " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa),\n" @@ -3882,7 +4255,7 @@ TEST_F(FormatTest, BreakConstructorInitializersAfterColon) { FormatStyle OnePerLine = Style; OnePerLine.ConstructorInitializerAllOnOneLineOrOnePerLine = true; - OnePerLine.AllowAllParametersOfDeclarationOnNextLine = false; + OnePerLine.AllowAllConstructorInitializersOnNextLine = false; verifyFormat("SomeClass::Constructor() :\n" " aaaaaaaaaaaaa(aaaaaaaaaaaaaa),\n" " aaaaaaaaaaaaa(aaaaaaaaaaaaaa),\n" @@ -4184,6 +4557,18 @@ TEST_F(FormatTest, BreaksFunctionDeclarations) { Style); } +TEST_F(FormatTest, DontBreakBeforeQualifiedOperator) { + // Regression test for https://bugs.llvm.org/show_bug.cgi?id=40516: + // Prefer keeping `::` followed by `operator` together. + EXPECT_EQ("const aaaa::bbbbbbb &\n" + "ccccccccc::operator++() {\n" + " stuff();\n" + "}", + format("const aaaa::bbbbbbb\n" + "&ccccccccc::operator++() { stuff(); }", + getLLVMStyleWithColumns(40))); +} + TEST_F(FormatTest, TrailingReturnType) { verifyFormat("auto foo() -> int;\n"); verifyFormat("struct S {\n" @@ -5359,6 +5744,62 @@ TEST_F(FormatTest, ReturnTypeBreakingStyle) { "}\n" "template <class T> T *f(T &c);\n", // No break here. Style); + verifyFormat("int\n" + "foo(A<bool> a)\n" + "{\n" + " return a;\n" + "}\n", + Style); + verifyFormat("int\n" + "foo(A<8> a)\n" + "{\n" + " return a;\n" + "}\n", + Style); + verifyFormat("int\n" + "foo(A<B<bool>, 8> a)\n" + "{\n" + " return a;\n" + "}\n", + Style); + verifyFormat("int\n" + "foo(A<B<8>, bool> a)\n" + "{\n" + " return a;\n" + "}\n", + Style); + verifyFormat("int\n" + "foo(A<B<bool>, bool> a)\n" + "{\n" + " return a;\n" + "}\n", + Style); + verifyFormat("int\n" + "foo(A<B<8>, 8> a)\n" + "{\n" + " return a;\n" + "}\n", + Style); + + Style = getGNUStyle(); + + // Test for comments at the end of function declarations. + verifyFormat("void\n" + "foo (int a, /*abc*/ int b) // def\n" + "{\n" + "}\n", + Style); + + verifyFormat("void\n" + "foo (int a, /* abc */ int b) /* def */\n" + "{\n" + "}\n", + Style); + + // Definitions that should not break after return type + verifyFormat("void foo (int a, int b); // def\n", Style); + verifyFormat("void foo (int a, int b); /* def */\n", Style); + verifyFormat("void foo (int a, int b);\n", Style); } TEST_F(FormatTest, AlwaysBreakBeforeMultilineStrings) { @@ -7980,7 +8421,8 @@ TEST_F(FormatTest, FormatHashIfExpressions) { TEST_F(FormatTest, MergeHandlingInTheFaceOfPreprocessorDirectives) { FormatStyle AllowsMergedIf = getGoogleStyle(); - AllowsMergedIf.AllowShortIfStatementsOnASingleLine = true; + AllowsMergedIf.AllowShortIfStatementsOnASingleLine = + FormatStyle::SIS_WithoutElse; verifyFormat("void f() { f(); }\n#error E", AllowsMergedIf); verifyFormat("if (true) return 42;\n#error E", AllowsMergedIf); verifyFormat("if (true)\n#error E\n return 42;", AllowsMergedIf); @@ -8740,6 +9182,9 @@ TEST_F(FormatTest, ConfigurableUseOfTab) { "\t\t parameter2); \\\n" "\t}", Tab); + verifyFormat("int a;\t // x\n" + "int bbbbbbbb; // x\n", + Tab); Tab.TabWidth = 4; Tab.IndentWidth = 8; @@ -9224,6 +9669,7 @@ TEST_F(FormatTest, ConfigurableSpaceBeforeParens) { verifyFormat("typedef void (*cb)(int);", NoSpace); verifyFormat("T A::operator()();", NoSpace); verifyFormat("X A::operator++(T);", NoSpace); + verifyFormat("auto lambda = []() { return 0; };", NoSpace); FormatStyle Space = getLLVMStyle(); Space.SpaceBeforeParens = FormatStyle::SBPO_Always; @@ -9271,6 +9717,72 @@ TEST_F(FormatTest, ConfigurableSpaceBeforeParens) { verifyFormat("typedef void (*cb) (int);", Space); verifyFormat("T A::operator() ();", Space); verifyFormat("X A::operator++ (T);", Space); + verifyFormat("auto lambda = [] () { return 0; };", Space); + verifyFormat("int x = int (y);", Space); + + FormatStyle SomeSpace = getLLVMStyle(); + SomeSpace.SpaceBeforeParens = FormatStyle::SBPO_NonEmptyParentheses; + + verifyFormat("[]() -> float {}", SomeSpace); + verifyFormat("[] (auto foo) {}", SomeSpace); + verifyFormat("[foo]() -> int {}", SomeSpace); + verifyFormat("int f();", SomeSpace); + verifyFormat("void f (int a, T b) {\n" + " while (true)\n" + " continue;\n" + "}", + SomeSpace); + verifyFormat("if (true)\n" + " f();\n" + "else if (true)\n" + " f();", + SomeSpace); + verifyFormat("do {\n" + " do_something();\n" + "} while (something());", + SomeSpace); + verifyFormat("switch (x) {\n" + "default:\n" + " break;\n" + "}", + SomeSpace); + verifyFormat("A::A() : a (1) {}", SomeSpace); + verifyFormat("void f() __attribute__ ((asdf));", SomeSpace); + verifyFormat("*(&a + 1);\n" + "&((&a)[1]);\n" + "a[(b + c) * d];\n" + "(((a + 1) * 2) + 3) * 4;", + SomeSpace); + verifyFormat("#define A(x) x", SomeSpace); + verifyFormat("#define A (x) x", SomeSpace); + verifyFormat("#if defined(x)\n" + "#endif", + SomeSpace); + verifyFormat("auto i = std::make_unique<int> (5);", SomeSpace); + verifyFormat("size_t x = sizeof (x);", SomeSpace); + verifyFormat("auto f (int x) -> decltype (x);", SomeSpace); + verifyFormat("int f (T x) noexcept (x.create());", SomeSpace); + verifyFormat("alignas (128) char a[128];", SomeSpace); + verifyFormat("size_t x = alignof (MyType);", SomeSpace); + verifyFormat("static_assert (sizeof (char) == 1, \"Impossible!\");", + SomeSpace); + verifyFormat("int f() throw (Deprecated);", SomeSpace); + verifyFormat("typedef void (*cb) (int);", SomeSpace); + verifyFormat("T A::operator()();", SomeSpace); + verifyFormat("X A::operator++ (T);", SomeSpace); + verifyFormat("int x = int (y);", SomeSpace); + verifyFormat("auto lambda = []() { return 0; };", SomeSpace); +} + +TEST_F(FormatTest, SpaceAfterLogicalNot) { + FormatStyle Spaces = getLLVMStyle(); + Spaces.SpaceAfterLogicalNot = true; + + verifyFormat("bool x = ! y", Spaces); + verifyFormat("if (! isFailure())", Spaces); + verifyFormat("if (! (a && b))", Spaces); + verifyFormat("\"Error!\"", Spaces); + verifyFormat("! ! x", Spaces); } TEST_F(FormatTest, ConfigurableSpacesInParentheses) { @@ -10069,6 +10581,13 @@ TEST_F(FormatTest, AlignConsecutiveDeclarations) { " unsigned c;\n" "}", Alignment); + + // See PR37175 + FormatStyle Style = getMozillaStyle(); + Style.AlignConsecutiveDeclarations = true; + EXPECT_EQ("DECOR1 /**/ int8_t /**/ DECOR2 /**/\n" + "foo(int a);", + format("DECOR1 /**/ int8_t /**/ DECOR2 /**/ foo (int a);", Style)); } TEST_F(FormatTest, LinuxBraceBreaking) { @@ -10400,7 +10919,8 @@ TEST_F(FormatTest, AllmanBraceBreaking) { AllmanBraceStyle.ColumnLimit = 80; FormatStyle BreakBeforeBraceShortIfs = AllmanBraceStyle; - BreakBeforeBraceShortIfs.AllowShortIfStatementsOnASingleLine = true; + BreakBeforeBraceShortIfs.AllowShortIfStatementsOnASingleLine = + FormatStyle::SIS_WithoutElse; BreakBeforeBraceShortIfs.AllowShortLoopsOnASingleLine = true; verifyFormat("void f(bool b)\n" "{\n" @@ -10630,6 +11150,24 @@ TEST_F(FormatTest, OptimizeBreakPenaltyVsExcess) { FormatStyle Style = getLLVMStyle(); Style.ColumnLimit = 20; + // See PR41213 + EXPECT_EQ("/*\n" + " *\t9012345\n" + " * /8901\n" + " */", + format("/*\n" + " *\t9012345 /8901\n" + " */", + Style)); + EXPECT_EQ("/*\n" + " *345678\n" + " *\t/8901\n" + " */", + format("/*\n" + " *345678\t/8901\n" + " */", + Style)); + verifyFormat("int a; // the\n" " // comment", Style); EXPECT_EQ("int a; /* first line\n" @@ -10859,10 +11397,11 @@ TEST_F(FormatTest, ParsesConfigurationBools) { CHECK_PARSE_BOOL(AlignTrailingComments); CHECK_PARSE_BOOL(AlignConsecutiveAssignments); CHECK_PARSE_BOOL(AlignConsecutiveDeclarations); + CHECK_PARSE_BOOL(AllowAllArgumentsOnNextLine); + CHECK_PARSE_BOOL(AllowAllConstructorInitializersOnNextLine); CHECK_PARSE_BOOL(AllowAllParametersOfDeclarationOnNextLine); CHECK_PARSE_BOOL(AllowShortBlocksOnASingleLine); CHECK_PARSE_BOOL(AllowShortCaseLabelsOnASingleLine); - CHECK_PARSE_BOOL(AllowShortIfStatementsOnASingleLine); CHECK_PARSE_BOOL(AllowShortLoopsOnASingleLine); CHECK_PARSE_BOOL(BinPackArguments); CHECK_PARSE_BOOL(BinPackParameters); @@ -10891,12 +11430,14 @@ TEST_F(FormatTest, ParsesConfigurationBools) { CHECK_PARSE_BOOL(SpacesInCStyleCastParentheses); CHECK_PARSE_BOOL(SpaceAfterCStyleCast); CHECK_PARSE_BOOL(SpaceAfterTemplateKeyword); + CHECK_PARSE_BOOL(SpaceAfterLogicalNot); CHECK_PARSE_BOOL(SpaceBeforeAssignmentOperators); CHECK_PARSE_BOOL(SpaceBeforeCpp11BracedList); CHECK_PARSE_BOOL(SpaceBeforeCtorInitializerColon); CHECK_PARSE_BOOL(SpaceBeforeInheritanceColon); CHECK_PARSE_BOOL(SpaceBeforeRangeBasedForLoopColon); + CHECK_PARSE_NESTED_BOOL(BraceWrapping, AfterCaseLabel); CHECK_PARSE_NESTED_BOOL(BraceWrapping, AfterClass); CHECK_PARSE_NESTED_BOOL(BraceWrapping, AfterControlStatement); CHECK_PARSE_NESTED_BOOL(BraceWrapping, AfterEnum); @@ -11055,6 +11596,8 @@ TEST_F(FormatTest, ParsesConfiguration) { FormatStyle::SBPO_Always); CHECK_PARSE("SpaceBeforeParens: ControlStatements", SpaceBeforeParens, FormatStyle::SBPO_ControlStatements); + CHECK_PARSE("SpaceBeforeParens: NonEmptyParentheses", SpaceBeforeParens, + FormatStyle::SBPO_NonEmptyParentheses); // For backward compatibility: CHECK_PARSE("SpaceAfterControlStatementKeyword: false", SpaceBeforeParens, FormatStyle::SBPO_Never); @@ -11125,6 +11668,20 @@ TEST_F(FormatTest, ParsesConfiguration) { CHECK_PARSE("NamespaceIndentation: All", NamespaceIndentation, FormatStyle::NI_All); + Style.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_Always; + CHECK_PARSE("AllowShortIfStatementsOnASingleLine: Never", + AllowShortIfStatementsOnASingleLine, FormatStyle::SIS_Never); + CHECK_PARSE("AllowShortIfStatementsOnASingleLine: WithoutElse", + AllowShortIfStatementsOnASingleLine, + FormatStyle::SIS_WithoutElse); + CHECK_PARSE("AllowShortIfStatementsOnASingleLine: Always", + AllowShortIfStatementsOnASingleLine, FormatStyle::SIS_Always); + CHECK_PARSE("AllowShortIfStatementsOnASingleLine: false", + AllowShortIfStatementsOnASingleLine, FormatStyle::SIS_Never); + CHECK_PARSE("AllowShortIfStatementsOnASingleLine: true", + AllowShortIfStatementsOnASingleLine, + FormatStyle::SIS_WithoutElse); + // FIXME: This is required because parsing a configuration simply overwrites // the first N elements of the list instead of resetting it. Style.ForEachMacros.clear(); @@ -11482,6 +12039,13 @@ TEST_F(FormatTest, ConstructorInitializerIndentWidth) { "bool smaller = 1 < bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb(\n" " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);", Style); + + Style.BreakConstructorInitializers = FormatStyle::BCIS_AfterColon; + verifyFormat( + "SomeClass::Constructor() :\n" + "aaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaa),\n" + "aaaaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaa) {}", + Style); } TEST_F(FormatTest, BreakConstructorInitializersBeforeComma) { @@ -11726,6 +12290,8 @@ TEST_F(FormatTest, FormatsWithWebKitStyle) { TEST_F(FormatTest, FormatsLambdas) { verifyFormat("int c = [b]() mutable { return [&b] { return b++; }(); }();\n"); + verifyFormat( + "int c = [b]() mutable noexcept { return [&b] { return b++; }(); }();\n"); verifyFormat("int c = [&] { [=] { return b++; }(); }();\n"); verifyFormat("int c = [&, &a, a] { [=, c, &d] { return b++; }(); }();\n"); verifyFormat("int c = [&a, &a, a] { [=, a, b, &c] { return b++; }(); }();\n"); @@ -11819,6 +12385,111 @@ TEST_F(FormatTest, FormatsLambdas) { verifyGoogleFormat("auto a = [&b, c](D* d) -> D& {};"); verifyGoogleFormat("auto a = [&b, c](D* d) -> const D* {};"); verifyFormat("[a, a]() -> a<1> {};"); + verifyFormat("[]() -> foo<5 + 2> { return {}; };"); + verifyFormat("[]() -> foo<5 - 2> { return {}; };"); + verifyFormat("[]() -> foo<5 / 2> { return {}; };"); + verifyFormat("[]() -> foo<5 * 2> { return {}; };"); + verifyFormat("[]() -> foo<5 % 2> { return {}; };"); + verifyFormat("[]() -> foo<5 << 2> { return {}; };"); + verifyFormat("[]() -> foo<!5> { return {}; };"); + verifyFormat("[]() -> foo<~5> { return {}; };"); + verifyFormat("[]() -> foo<5 | 2> { return {}; };"); + verifyFormat("[]() -> foo<5 || 2> { return {}; };"); + verifyFormat("[]() -> foo<5 & 2> { return {}; };"); + verifyFormat("[]() -> foo<5 && 2> { return {}; };"); + verifyFormat("[]() -> foo<5 == 2> { return {}; };"); + verifyFormat("[]() -> foo<5 != 2> { return {}; };"); + verifyFormat("[]() -> foo<5 >= 2> { return {}; };"); + verifyFormat("[]() -> foo<5 <= 2> { return {}; };"); + verifyFormat("[]() -> foo<5 < 2> { return {}; };"); + verifyFormat("[]() -> foo<2 ? 1 : 0> { return {}; };"); + verifyFormat("namespace bar {\n" + "// broken:\n" + "auto foo{[]() -> foo<5 + 2> { return {}; }};\n" + "} // namespace bar"); + verifyFormat("namespace bar {\n" + "// broken:\n" + "auto foo{[]() -> foo<5 - 2> { return {}; }};\n" + "} // namespace bar"); + verifyFormat("namespace bar {\n" + "// broken:\n" + "auto foo{[]() -> foo<5 / 2> { return {}; }};\n" + "} // namespace bar"); + verifyFormat("namespace bar {\n" + "// broken:\n" + "auto foo{[]() -> foo<5 * 2> { return {}; }};\n" + "} // namespace bar"); + verifyFormat("namespace bar {\n" + "// broken:\n" + "auto foo{[]() -> foo<5 % 2> { return {}; }};\n" + "} // namespace bar"); + verifyFormat("namespace bar {\n" + "// broken:\n" + "auto foo{[]() -> foo<5 << 2> { return {}; }};\n" + "} // namespace bar"); + verifyFormat("namespace bar {\n" + "// broken:\n" + "auto foo{[]() -> foo<!5> { return {}; }};\n" + "} // namespace bar"); + verifyFormat("namespace bar {\n" + "// broken:\n" + "auto foo{[]() -> foo<~5> { return {}; }};\n" + "} // namespace bar"); + verifyFormat("namespace bar {\n" + "// broken:\n" + "auto foo{[]() -> foo<5 | 2> { return {}; }};\n" + "} // namespace bar"); + verifyFormat("namespace bar {\n" + "// broken:\n" + "auto foo{[]() -> foo<5 || 2> { return {}; }};\n" + "} // namespace bar"); + verifyFormat("namespace bar {\n" + "// broken:\n" + "auto foo{[]() -> foo<5 & 2> { return {}; }};\n" + "} // namespace bar"); + verifyFormat("namespace bar {\n" + "// broken:\n" + "auto foo{[]() -> foo<5 && 2> { return {}; }};\n" + "} // namespace bar"); + verifyFormat("namespace bar {\n" + "// broken:\n" + "auto foo{[]() -> foo<5 == 2> { return {}; }};\n" + "} // namespace bar"); + verifyFormat("namespace bar {\n" + "// broken:\n" + "auto foo{[]() -> foo<5 != 2> { return {}; }};\n" + "} // namespace bar"); + verifyFormat("namespace bar {\n" + "// broken:\n" + "auto foo{[]() -> foo<5 >= 2> { return {}; }};\n" + "} // namespace bar"); + verifyFormat("namespace bar {\n" + "// broken:\n" + "auto foo{[]() -> foo<5 <= 2> { return {}; }};\n" + "} // namespace bar"); + verifyFormat("namespace bar {\n" + "// broken:\n" + "auto foo{[]() -> foo<5 < 2> { return {}; }};\n" + "} // namespace bar"); + verifyFormat("namespace bar {\n" + "// broken:\n" + "auto foo{[]() -> foo<2 ? 1 : 0> { return {}; }};\n" + "} // namespace bar"); + verifyFormat("[]() -> a<1> {};"); + verifyFormat("[]() -> a<1> { ; };"); + verifyFormat("[]() -> a<1> { ; }();"); + verifyFormat("[a, a]() -> a<true> {};"); + verifyFormat("[]() -> a<true> {};"); + verifyFormat("[]() -> a<true> { ; };"); + verifyFormat("[]() -> a<true> { ; }();"); + verifyFormat("[a, a]() -> a<false> {};"); + verifyFormat("[]() -> a<false> {};"); + verifyFormat("[]() -> a<false> { ; };"); + verifyFormat("[]() -> a<false> { ; }();"); + verifyFormat("auto foo{[]() -> foo<false> { ; }};"); + verifyFormat("namespace bar {\n" + "auto foo{[]() -> foo<false> { ; }};\n" + "} // namespace bar"); verifyFormat("auto aaaaaaaa = [](int i, // break for some reason\n" " int j) -> int {\n" " return ffffffffffffffffffffffffffffffffffffffffffff(i * j);\n" @@ -11953,6 +12624,43 @@ TEST_F(FormatTest, FormatsLambdas) { " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa> {\n" " //\n" " });"); + + FormatStyle DoNotMerge = getLLVMStyle(); + DoNotMerge.AllowShortLambdasOnASingleLine = FormatStyle::SLS_None; + verifyFormat("auto c = []() {\n" + " return b;\n" + "};", + "auto c = []() { return b; };", DoNotMerge); + verifyFormat("auto c = []() {\n" + "};", + " auto c = []() {};", DoNotMerge); + + FormatStyle MergeEmptyOnly = getLLVMStyle(); + MergeEmptyOnly.AllowShortLambdasOnASingleLine = FormatStyle::SLS_Empty; + verifyFormat("auto c = []() {\n" + " return b;\n" + "};", + "auto c = []() {\n" + " return b;\n" + " };", + MergeEmptyOnly); + verifyFormat("auto c = []() {};", + "auto c = []() {\n" + "};", + MergeEmptyOnly); + + FormatStyle MergeInline = getLLVMStyle(); + MergeInline.AllowShortLambdasOnASingleLine = FormatStyle::SLS_Inline; + verifyFormat("auto c = []() {\n" + " return b;\n" + "};", + "auto c = []() { return b; };", MergeInline); + verifyFormat("function([]() { return b; })", "function([]() { return b; })", + MergeInline); + verifyFormat("function([]() { return b; }, a)", + "function([]() { return b; }, a)", MergeInline); + verifyFormat("function(a, []() { return b; })", + "function(a, []() { return b; })", MergeInline); } TEST_F(FormatTest, EmptyLinesInLambdas) { @@ -12180,6 +12888,12 @@ TEST_F(FormatTest, SupportsCRLF) { "should not introduce\r\n" "an extra carriage return\r\n" "*/\r\n")); + EXPECT_EQ("/*\r\n" + "\r\n" + "*/", + format("/*\r\n" + " \r\r\r\n" + "*/")); } TEST_F(FormatTest, MunchSemicolonAfterBlocks) { @@ -12207,6 +12921,22 @@ TEST_F(FormatTest, ConfigurableContinuationIndentWidth) { format("int i = longFunction(arg);", SixIndent)); } +TEST_F(FormatTest, WrappedClosingParenthesisIndent) { + FormatStyle Style = getLLVMStyle(); + verifyFormat("int Foo::getter(\n" + " //\n" + ") const {\n" + " return foo;\n" + "}", + Style); + verifyFormat("void Foo::setter(\n" + " //\n" + ") {\n" + " foo = 1;\n" + "}", + Style); +} + TEST_F(FormatTest, SpacesInAngles) { FormatStyle Spaces = getLLVMStyle(); Spaces.SpacesInAngles = true; @@ -12527,6 +13257,11 @@ TEST(FormatStyle, GetStyleOfFile) { auto Style7 = getStyle("file", "/d/.clang-format", "LLVM", "", &FS); ASSERT_FALSE((bool)Style7); llvm::consumeError(Style7.takeError()); + + // Test 8: inferred per-language defaults apply. + auto StyleTd = getStyle("file", "x.td", "llvm", "", &FS); + ASSERT_TRUE((bool)StyleTd); + ASSERT_EQ(*StyleTd, getLLVMStyle(FormatStyle::LK_TableGen)); } TEST_F(ReplacementTest, FormatCodeAfterReplacements) { @@ -12734,6 +13469,9 @@ TEST_F(FormatTest, GuessLanguageWithCpp11AttributeSpecifiers) { guessLanguage("foo.h", "[[using gsl: suppress(\"type\")]];")); EXPECT_EQ( FormatStyle::LK_Cpp, + guessLanguage("foo.h", "for (auto &&[endpoint, stream] : streams_)")); + EXPECT_EQ( + FormatStyle::LK_Cpp, guessLanguage("foo.h", "[[clang::callable_when(\"unconsumed\", \"unknown\")]]")); EXPECT_EQ(FormatStyle::LK_Cpp, guessLanguage("foo.h", "[[foo::bar, ...]]")); diff --git a/unittests/Format/FormatTestCSharp.cpp b/unittests/Format/FormatTestCSharp.cpp new file mode 100644 index 0000000000..801adb28bd --- /dev/null +++ b/unittests/Format/FormatTestCSharp.cpp @@ -0,0 +1,184 @@ +//===- unittest/Format/FormatTestCSharp.cpp - Formatting tests for CSharp -===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "FormatTestUtils.h" +#include "clang/Format/Format.h" +#include "llvm/Support/Debug.h" +#include "gtest/gtest.h" + +#define DEBUG_TYPE "format-test" + +namespace clang { +namespace format { + +class FormatTestCSharp : public ::testing::Test { +protected: + static std::string format(llvm::StringRef Code, unsigned Offset, + unsigned Length, const FormatStyle &Style) { + LLVM_DEBUG(llvm::errs() << "---\n"); + LLVM_DEBUG(llvm::errs() << Code << "\n\n"); + std::vector<tooling::Range> Ranges(1, tooling::Range(Offset, Length)); + tooling::Replacements Replaces = reformat(Style, Code, Ranges); + auto Result = applyAllReplacements(Code, Replaces); + EXPECT_TRUE(static_cast<bool>(Result)); + LLVM_DEBUG(llvm::errs() << "\n" << *Result << "\n\n"); + return *Result; + } + + static std::string + format(llvm::StringRef Code, + const FormatStyle &Style = getGoogleStyle(FormatStyle::LK_CSharp)) { + return format(Code, 0, Code.size(), Style); + } + + static FormatStyle getStyleWithColumns(unsigned ColumnLimit) { + FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); + Style.ColumnLimit = ColumnLimit; + return Style; + } + + static void verifyFormat( + llvm::StringRef Code, + const FormatStyle &Style = getGoogleStyle(FormatStyle::LK_CSharp)) { + EXPECT_EQ(Code.str(), format(Code, Style)) << "Expected code is not stable"; + EXPECT_EQ(Code.str(), format(test::messUp(Code), Style)); + } +}; + +TEST_F(FormatTestCSharp, CSharpClass) { + verifyFormat("public class SomeClass {\n" + " void f() {}\n" + " int g() { return 0; }\n" + " void h() {\n" + " while (true) f();\n" + " for (;;) f();\n" + " if (true) f();\n" + " }\n" + "}"); +} + +TEST_F(FormatTestCSharp, AccessModifiers) { + verifyFormat("public String toString() {}"); + verifyFormat("private String toString() {}"); + verifyFormat("protected String toString() {}"); + verifyFormat("internal String toString() {}"); + + verifyFormat("public override String toString() {}"); + verifyFormat("private override String toString() {}"); + verifyFormat("protected override String toString() {}"); + verifyFormat("internal override String toString() {}"); + + verifyFormat("internal static String toString() {}"); +} + +TEST_F(FormatTestCSharp, NoStringLiteralBreaks) { + verifyFormat("foo(" + "\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaa\");"); +} + +TEST_F(FormatTestCSharp, CSharpVerbatiumStringLiterals) { + verifyFormat("foo(@\"aaaaaaaa\\abc\\aaaa\");"); + // @"ABC\" + ToString("B") - handle embedded \ in literal string at + // the end + // + /* + * After removal of Lexer change we are currently not able + * To handle these cases + verifyFormat("string s = @\"ABC\\\" + ToString(\"B\");"); + verifyFormat("string s = @\"ABC\"\"DEF\"\"GHI\""); + verifyFormat("string s = @\"ABC\"\"DEF\"\"\""); + verifyFormat("string s = @\"ABC\"\"DEF\"\"\" + abc"); + */ +} + +TEST_F(FormatTestCSharp, CSharpInterpolatedStringLiterals) { + verifyFormat("foo($\"aaaaaaaa{aaa}aaaa\");"); + verifyFormat("foo($\"aaaa{A}\");"); + verifyFormat( + "foo($\"aaaa{A}" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\");"); + verifyFormat("Name = $\"{firstName} {lastName}\";"); + + // $"ABC\" + ToString("B") - handle embedded \ in literal string at + // the end + verifyFormat("string s = $\"A{abc}BC\" + ToString(\"B\");"); + verifyFormat("$\"{domain}\\\\{user}\""); + verifyFormat( + "var verbatimInterpolated = $@\"C:\\Users\\{userName}\\Documents\\\";"); +} + +TEST_F(FormatTestCSharp, CSharpFatArrows) { + verifyFormat("Task serverTask = Task.Run(async() => {"); + verifyFormat("public override string ToString() => \"{Name}\\{Age}\";"); +} + +TEST_F(FormatTestCSharp, CSharpNullConditional) { + verifyFormat( + "public Person(string firstName, string lastName, int? age=null)"); + + verifyFormat("switch(args?.Length)"); + + verifyFormat("public static void Main(string[] args) { string dirPath " + "= args?[0]; }"); +} + +TEST_F(FormatTestCSharp, Attributes) { + verifyFormat("[STAThread]\n" + "static void\n" + "Main(string[] args) {}"); + + verifyFormat("[TestMethod]\n" + "private class Test {}"); + + verifyFormat("[TestMethod]\n" + "protected class Test {}"); + + verifyFormat("[TestMethod]\n" + "internal class Test {}"); + + verifyFormat("[TestMethod]\n" + "class Test {}"); + + verifyFormat("[TestMethod]\n" + "[DeploymentItem(\"Test.txt\")]\n" + "public class Test {}"); + + verifyFormat("[System.AttributeUsage(System.AttributeTargets.Method)]\n" + "[System.Runtime.InteropServices.ComVisible(true)]\n" + "public sealed class STAThreadAttribute : Attribute {}"); + + verifyFormat("[Verb(\"start\", HelpText = \"Starts the server listening on " + "provided port\")]\n" + "class Test {}"); + + verifyFormat("[TestMethod]\n" + "public string Host {\n set;\n get;\n}"); + + verifyFormat("[TestMethod(\"start\", HelpText = \"Starts the server " + "listening on provided host\")]\n" + "public string Host {\n set;\n get;\n}"); +} + +TEST_F(FormatTestCSharp, CSharpRegions) { + verifyFormat("#region aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaa " + "aaaaaaaaaaaaaaa long region"); +} + +TEST_F(FormatTestCSharp, CSharpKeyWordEscaping) { + verifyFormat("public enum var { none, @string, bool, @enum }"); +} + +TEST_F(FormatTestCSharp, CSharpNullCoalescing) { + verifyFormat("var test = ABC ?? DEF"); + verifyFormat("string myname = name ?? \"ABC\";"); + verifyFormat("return _name ?? \"DEF\";"); +} + +} // namespace format +} // end namespace clang diff --git a/unittests/Format/FormatTestComments.cpp b/unittests/Format/FormatTestComments.cpp index 9f43677b70..6dbc364fd2 100644 --- a/unittests/Format/FormatTestComments.cpp +++ b/unittests/Format/FormatTestComments.cpp @@ -1,9 +1,8 @@ //===- unittest/Format/FormatTestComments.cpp - Formatting unit tests -----===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Format/FormatTestJS.cpp b/unittests/Format/FormatTestJS.cpp index 67b99ba146..b332f1bd97 100644 --- a/unittests/Format/FormatTestJS.cpp +++ b/unittests/Format/FormatTestJS.cpp @@ -1,9 +1,8 @@ //===- unittest/Format/FormatTestJS.cpp - Formatting unit tests for JS ----===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -1963,6 +1962,12 @@ TEST_F(FormatTestJS, NestedTemplateStrings) { TEST_F(FormatTestJS, TaggedTemplateStrings) { verifyFormat("var x = html`<ul>`;"); verifyFormat("yield `hello`;"); + verifyFormat("var f = {\n" + " param: longTagName`This is a ${\n" + " 'really'} long line`\n" + "};", + "var f = {param: longTagName`This is a ${'really'} long line`};", + getGoogleJSStyleWithColumns(40)); } TEST_F(FormatTestJS, CastSyntax) { @@ -2329,5 +2334,27 @@ TEST_F(FormatTestJS, ConditionalTypes) { " never) extends((k: infer I) => void) ? I : never;"); } -} // end namespace tooling +TEST_F(FormatTestJS, SupportPrivateFieldsAndMethods) { + verifyFormat("class Example {\n" + " pub = 1;\n" + " #priv = 2;\n" + " static pub2 = 'foo';\n" + " static #priv2 = 'bar';\n" + " method() {\n" + " this.#priv = 5;\n" + " }\n" + " static staticMethod() {\n" + " switch (this.#priv) {\n" + " case '1':\n" + " #priv = 3;\n" + " break;\n" + " }\n" + " }\n" + " #privateMethod() {\n" + " this.#privateMethod(); // infinite loop\n" + " }\n" + " static #staticPrivateMethod() {}\n"); +} + +} // namespace format } // end namespace clang diff --git a/unittests/Format/FormatTestJava.cpp b/unittests/Format/FormatTestJava.cpp index f12d7fba50..a4936e0e1c 100644 --- a/unittests/Format/FormatTestJava.cpp +++ b/unittests/Format/FormatTestJava.cpp @@ -1,9 +1,8 @@ //===- unittest/Format/FormatTestJava.cpp - Formatting tests for Java -----===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Format/FormatTestObjC.cpp b/unittests/Format/FormatTestObjC.cpp index a417b6710d..b859d92a89 100644 --- a/unittests/Format/FormatTestObjC.cpp +++ b/unittests/Format/FormatTestObjC.cpp @@ -1,9 +1,8 @@ //===- unittest/Format/FormatTestObjC.cpp - Formatting unit tests----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -166,6 +165,20 @@ TEST(FormatTestObjCStyle, DetectsObjCInHeaders) { EXPECT_EQ(FormatStyle::LK_ObjC, Style->Language); } +TEST(FormatTestObjCStyle, AvoidDetectingDesignatedInitializersAsObjCInHeaders) { + auto Style = getStyle("LLVM", "a.h", "none", + "static const char *names[] = {[0] = \"foo\",\n" + "[kBar] = \"bar\"};"); + ASSERT_TRUE((bool)Style); + EXPECT_EQ(FormatStyle::LK_Cpp, Style->Language); + + Style = getStyle("LLVM", "a.h", "none", + "static const char *names[] = {[0] EQ \"foo\",\n" + "[kBar] EQ \"bar\"};"); + ASSERT_TRUE((bool)Style); + EXPECT_EQ(FormatStyle::LK_Cpp, Style->Language); +} + TEST_F(FormatTestObjC, FormatObjCTryCatch) { verifyFormat("@try {\n" " f();\n" @@ -598,6 +611,7 @@ TEST_F(FormatTestObjC, FormatObjCMethodDeclarations) { TEST_F(FormatTestObjC, FormatObjCMethodExpr) { verifyFormat("[foo bar:baz];"); + verifyFormat("[foo bar]->baz;"); verifyFormat("return [foo bar:baz];"); verifyFormat("return (a)[foo bar:baz];"); verifyFormat("f([foo bar:baz]);"); @@ -1315,6 +1329,58 @@ TEST_F(FormatTestObjC, AlwaysBreakBeforeMultilineStrings) { " @\"fffff\"];"); } +TEST_F(FormatTestObjC, DisambiguatesCallsFromCppLambdas) { + verifyFormat("x = ([a foo:bar] && b->c == 'd');"); + verifyFormat("x = ([a foo:bar] + b->c == 'd');"); + verifyFormat("x = ([a foo:bar] + !b->c == 'd');"); + verifyFormat("x = ([a foo:bar] + ~b->c == 'd');"); + verifyFormat("x = ([a foo:bar] - b->c == 'd');"); + verifyFormat("x = ([a foo:bar] / b->c == 'd');"); + verifyFormat("x = ([a foo:bar] % b->c == 'd');"); + verifyFormat("x = ([a foo:bar] | b->c == 'd');"); + verifyFormat("x = ([a foo:bar] || b->c == 'd');"); + verifyFormat("x = ([a foo:bar] && b->c == 'd');"); + verifyFormat("x = ([a foo:bar] == b->c == 'd');"); + verifyFormat("x = ([a foo:bar] != b->c == 'd');"); + verifyFormat("x = ([a foo:bar] <= b->c == 'd');"); + verifyFormat("x = ([a foo:bar] >= b->c == 'd');"); + verifyFormat("x = ([a foo:bar] << b->c == 'd');"); + verifyFormat("x = ([a foo:bar] ? b->c == 'd' : 'e');"); + // FIXME: The following are wrongly classified as C++ lambda expressions. + // For example this code: + // x = ([a foo:bar] & b->c == 'd'); + // is formatted as: + // x = ([a foo:bar] & b -> c == 'd'); + // verifyFormat("x = ([a foo:bar] & b->c == 'd');"); + // verifyFormat("x = ([a foo:bar] > b->c == 'd');"); + // verifyFormat("x = ([a foo:bar] < b->c == 'd');"); + // verifyFormat("x = ([a foo:bar] >> b->c == 'd');"); +} + +TEST_F(FormatTestObjC, DisambiguatesCallsFromStructuredBindings) { + verifyFormat("int f() {\n" + " if (a && [f arg])\n" + " return 0;\n" + "}"); + verifyFormat("int f() {\n" + " if (a & [f arg])\n" + " return 0;\n" + "}"); + verifyFormat("int f() {\n" + " for (auto &[elem] : list)\n" + " return 0;\n" + "}"); + verifyFormat("int f() {\n" + " for (auto &&[elem] : list)\n" + " return 0;\n" + "}"); + verifyFormat( + "int f() {\n" + " for (auto /**/ const /**/ volatile /**/ && /**/ [elem] : list)\n" + " return 0;\n" + "}"); +} + } // end namespace } // end namespace format } // end namespace clang diff --git a/unittests/Format/FormatTestProto.cpp b/unittests/Format/FormatTestProto.cpp index 70ef2d2f13..ac042316bc 100644 --- a/unittests/Format/FormatTestProto.cpp +++ b/unittests/Format/FormatTestProto.cpp @@ -1,9 +1,8 @@ //===- unittest/Format/FormatTestProto.cpp --------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -108,6 +107,12 @@ TEST_F(FormatTestProto, FormatsEnums) { "};"); } +TEST_F(FormatTestProto, EnumAsFieldName) { + verifyFormat("message SomeMessage {\n" + " required int32 enum = 1;\n" + "}"); +} + TEST_F(FormatTestProto, UnderstandsReturns) { verifyFormat("rpc Search(SearchRequest) returns (SearchResponse);"); } @@ -188,6 +193,10 @@ TEST_F(FormatTestProto, DoesntWrapFileOptions) { "\"some.really.long.package.that.exceeds.the.column.limit\";")); } +TEST_F(FormatTestProto, TrailingCommentAfterFileOption) { + verifyFormat("option java_package = \"foo.pkg\"; // comment\n"); +} + TEST_F(FormatTestProto, FormatsOptions) { verifyFormat("option (MyProto.options) = {\n" " field_a: OK\n" diff --git a/unittests/Format/FormatTestRawStrings.cpp b/unittests/Format/FormatTestRawStrings.cpp index 2a8a43dc95..dc2f6b5180 100644 --- a/unittests/Format/FormatTestRawStrings.cpp +++ b/unittests/Format/FormatTestRawStrings.cpp @@ -1,9 +1,8 @@ //===- unittest/Format/FormatTestRawStrings.cpp - Formatting unit tests ---===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -982,6 +981,20 @@ int f() { })test", Style)); } +TEST_F(FormatTestRawStrings, IndentsLastParamAfterNewline) { + FormatStyle Style = getRawStringPbStyleWithColumns(60); + expect_eq(R"test( +fffffffffffffffffffff("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + R"pb( + b: c + )pb");)test", + format(R"test( +fffffffffffffffffffff("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + R"pb( + b: c + )pb");)test", + Style)); +} } // end namespace } // end namespace format } // end namespace clang diff --git a/unittests/Format/FormatTestSelective.cpp b/unittests/Format/FormatTestSelective.cpp index 36d9089c60..f031a3dee5 100644 --- a/unittests/Format/FormatTestSelective.cpp +++ b/unittests/Format/FormatTestSelective.cpp @@ -1,9 +1,8 @@ //===- unittest/Format/FormatTestSelective.cpp - Formatting unit tests ----===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -99,7 +98,7 @@ TEST_F(FormatTestSelective, ReformatsMovedLines) { } TEST_F(FormatTestSelective, FormatsIfWithoutCompoundStatement) { - Style.AllowShortIfStatementsOnASingleLine = true; + Style.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_WithoutElse; EXPECT_EQ("if (a) return;", format("if(a)\nreturn;", 7, 1)); EXPECT_EQ("if (a) return; // comment", format("if(a)\nreturn; // comment", 20, 1)); diff --git a/unittests/Format/FormatTestTableGen.cpp b/unittests/Format/FormatTestTableGen.cpp index 820ea783cc..06029bd8c7 100644 --- a/unittests/Format/FormatTestTableGen.cpp +++ b/unittests/Format/FormatTestTableGen.cpp @@ -1,9 +1,8 @@ //===- unittest/Format/FormatTestTableGen.cpp -----------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -52,5 +51,9 @@ TEST_F(FormatTestTableGen, FormatStringBreak) { " \"very long help string\">;\n"); } +TEST_F(FormatTestTableGen, NoSpacesInSquareBracketLists) { + verifyFormat("def flag : Flag<[\"-\", \"--\"], \"foo\">;\n"); +} + } // namespace format } // end namespace clang diff --git a/unittests/Format/FormatTestTextProto.cpp b/unittests/Format/FormatTestTextProto.cpp index 44431e4dc6..dba81fcd3a 100644 --- a/unittests/Format/FormatTestTextProto.cpp +++ b/unittests/Format/FormatTestTextProto.cpp @@ -1,9 +1,8 @@ //===- unittest/Format/FormatTestTextProto.cpp ----------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Format/FormatTestUtils.h b/unittests/Format/FormatTestUtils.h index d82d84ebed..fb75070db1 100644 --- a/unittests/Format/FormatTestUtils.h +++ b/unittests/Format/FormatTestUtils.h @@ -1,9 +1,8 @@ //===- unittest/Format/FormatTestUtils.h - Formatting unit tests ----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/unittests/Format/NamespaceEndCommentsFixerTest.cpp b/unittests/Format/NamespaceEndCommentsFixerTest.cpp index ee083b8ad1..5091b1d9de 100644 --- a/unittests/Format/NamespaceEndCommentsFixerTest.cpp +++ b/unittests/Format/NamespaceEndCommentsFixerTest.cpp @@ -1,9 +1,8 @@ //===- NamespaceEndCommentsFixerTest.cpp - Formatting unit tests ----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Format/SortImportsTestJS.cpp b/unittests/Format/SortImportsTestJS.cpp index 91be0313cf..72c79ac718 100644 --- a/unittests/Format/SortImportsTestJS.cpp +++ b/unittests/Format/SortImportsTestJS.cpp @@ -1,9 +1,8 @@ //===- unittest/Format/SortImportsTestJS.cpp - JS import sort unit tests --===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Format/SortImportsTestJava.cpp b/unittests/Format/SortImportsTestJava.cpp index 3bcf809d96..d2826a2107 100644 --- a/unittests/Format/SortImportsTestJava.cpp +++ b/unittests/Format/SortImportsTestJava.cpp @@ -262,6 +262,29 @@ TEST_F(SortImportsTestJava, NoNewlineAtEnd) { "import org.a;")); } +TEST_F(SortImportsTestJava, ImportNamedFunction) { + EXPECT_EQ("import X;\n" + "class C {\n" + " void m() {\n" + " importFile();\n" + " }\n" + "}\n", + sort("import X;\n" + "class C {\n" + " void m() {\n" + " importFile();\n" + " }\n" + "}\n")); +} + +TEST_F(SortImportsTestJava, NoReplacementsForValidImports) { + // Identical #includes have led to a failure with an unstable sort. + std::string Code = "import org.a;\n" + "import org.b;\n"; + EXPECT_TRUE( + sortIncludes(FmtStyle, Code, GetCodeRange(Code), "input.java").empty()); +} + } // end namespace } // end namespace format } // end namespace clang diff --git a/unittests/Format/SortIncludesTest.cpp b/unittests/Format/SortIncludesTest.cpp index dde8800378..c00d3cb747 100644 --- a/unittests/Format/SortIncludesTest.cpp +++ b/unittests/Format/SortIncludesTest.cpp @@ -1,14 +1,14 @@ //===- unittest/Format/SortIncludesTest.cpp - Include sort unit tests -----===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "FormatTestUtils.h" #include "clang/Format/Format.h" +#include "llvm/ADT/None.h" #include "llvm/Support/Debug.h" #include "gtest/gtest.h" @@ -25,9 +25,11 @@ protected: } std::string sort(StringRef Code, std::vector<tooling::Range> Ranges, - StringRef FileName = "input.cc") { + StringRef FileName = "input.cc", + unsigned ExpectedNumRanges = 1) { auto Replaces = sortIncludes(FmtStyle, Code, Ranges, FileName); Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges); + EXPECT_EQ(ExpectedNumRanges, Replaces.size()); auto Sorted = applyAllReplacements(Code, Replaces); EXPECT_TRUE(static_cast<bool>(Sorted)); auto Result = applyAllReplacements( @@ -36,8 +38,10 @@ protected: return *Result; } - std::string sort(StringRef Code, StringRef FileName = "input.cpp") { - return sort(Code, GetCodeRange(Code), FileName); + std::string sort(StringRef Code, + StringRef FileName = "input.cpp", + unsigned ExpectedNumRanges = 1) { + return sort(Code, GetCodeRange(Code), FileName, ExpectedNumRanges); } unsigned newCursor(llvm::StringRef Code, unsigned Cursor) { @@ -118,6 +122,43 @@ TEST_F(SortIncludesTest, SupportClangFormatOff) { "// clang-format on\n")); } +TEST_F(SortIncludesTest, SupportClangFormatOffCStyle) { + EXPECT_EQ("#include <a>\n" + "#include <b>\n" + "#include <c>\n" + "/* clang-format off */\n" + "#include <b>\n" + "#include <a>\n" + "#include <c>\n" + "/* clang-format on */\n", + sort("#include <b>\n" + "#include <a>\n" + "#include <c>\n" + "/* clang-format off */\n" + "#include <b>\n" + "#include <a>\n" + "#include <c>\n" + "/* clang-format on */\n")); + + // Not really turning it off + EXPECT_EQ("#include <a>\n" + "#include <b>\n" + "#include <c>\n" + "/* clang-format offically */\n" + "#include <a>\n" + "#include <b>\n" + "#include <c>\n" + "/* clang-format onwards */\n", + sort("#include <b>\n" + "#include <a>\n" + "#include <c>\n" + "/* clang-format offically */\n" + "#include <b>\n" + "#include <a>\n" + "#include <c>\n" + "/* clang-format onwards */\n", "input.h", 2)); +} + TEST_F(SortIncludesTest, IncludeSortingCanBeDisabled) { FmtStyle.SortIncludes = false; EXPECT_EQ("#include \"a.h\"\n" @@ -125,7 +166,8 @@ TEST_F(SortIncludesTest, IncludeSortingCanBeDisabled) { "#include \"b.h\"\n", sort("#include \"a.h\"\n" "#include \"c.h\"\n" - "#include \"b.h\"\n")); + "#include \"b.h\"\n", + "input.h", 0)); } TEST_F(SortIncludesTest, MixIncludeAndImport) { @@ -178,7 +220,7 @@ TEST_F(SortIncludesTest, SortsLocallyInEachBlock) { sort("#include \"a.h\"\n" "#include \"c.h\"\n" "\n" - "#include \"b.h\"\n")); + "#include \"b.h\"\n", "input.h", 0)); } TEST_F(SortIncludesTest, SortsAllBlocksWhenMerging) { @@ -226,9 +268,13 @@ TEST_F(SortIncludesTest, CommentsAlwaysSeparateGroups) { TEST_F(SortIncludesTest, HandlesAngledIncludesAsSeparateBlocks) { EXPECT_EQ("#include \"a.h\"\n" "#include \"c.h\"\n" + "#include <array>\n" "#include <b.h>\n" - "#include <d.h>\n", - sort("#include <d.h>\n" + "#include <d.h>\n" + "#include <vector>\n", + sort("#include <vector>\n" + "#include <d.h>\n" + "#include <array>\n" "#include <b.h>\n" "#include \"c.h\"\n" "#include \"a.h\"\n")); @@ -236,9 +282,15 @@ TEST_F(SortIncludesTest, HandlesAngledIncludesAsSeparateBlocks) { FmtStyle = getGoogleStyle(FormatStyle::LK_Cpp); EXPECT_EQ("#include <b.h>\n" "#include <d.h>\n" + "\n" + "#include <array>\n" + "#include <vector>\n" + "\n" "#include \"a.h\"\n" "#include \"c.h\"\n", - sort("#include <d.h>\n" + sort("#include <vector>\n" + "#include <d.h>\n" + "#include <array>\n" "#include <b.h>\n" "#include \"c.h\"\n" "#include \"a.h\"\n")); @@ -412,7 +464,7 @@ TEST_F(SortIncludesTest, NegativePriorities) { sort("#include \"important_os_header.h\"\n" "#include \"c_main.h\"\n" "#include \"a_other.h\"\n", - "c_main.cc")); + "c_main.cc", 0)); } TEST_F(SortIncludesTest, PriorityGroupsAreSeparatedWhenRegroupping) { @@ -440,7 +492,7 @@ TEST_F(SortIncludesTest, PriorityGroupsAreSeparatedWhenRegroupping) { "#include \"c_main.h\"\n" "\n" "#include \"a_other.h\"\n", - "c_main.cc")); + "c_main.cc", 0)); } TEST_F(SortIncludesTest, CalculatesCorrectCursorPosition) { @@ -588,7 +640,29 @@ TEST_F(SortIncludesTest, DoNotSortLikelyXml) { sort("<!--;\n" "#include <b>\n" "#include <a>\n" - "-->")); + "-->", "input.h", 0)); +} + +TEST_F(SortIncludesTest, DoNotOutputReplacementsForSortedBlocksWithRegrouping) { + Style.IncludeBlocks = Style.IBS_Regroup; + std::string Code = R"( +#include "b.h" + +#include <a.h> +)"; + EXPECT_EQ(Code, sort(Code, "input.h", 0)); +} + + +TEST_F(SortIncludesTest, DoNotRegroupGroupsInGoogleObjCStyle) { + FmtStyle = getGoogleStyle(FormatStyle::LK_ObjC); + + EXPECT_EQ("#include <a.h>\n" + "#include <b.h>\n" + "#include \"a.h\"", + sort("#include <b.h>\n" + "#include <a.h>\n" + "#include \"a.h\"")); } } // end namespace diff --git a/unittests/Format/UsingDeclarationsSorterTest.cpp b/unittests/Format/UsingDeclarationsSorterTest.cpp index 2ba6520e05..0f517d0a61 100644 --- a/unittests/Format/UsingDeclarationsSorterTest.cpp +++ b/unittests/Format/UsingDeclarationsSorterTest.cpp @@ -1,9 +1,8 @@ //===- UsingDeclarationsSorterTest.cpp - Formatting unit tests ------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Frontend/ASTUnitTest.cpp b/unittests/Frontend/ASTUnitTest.cpp index c60004e40b..3228dfbe6e 100644 --- a/unittests/Frontend/ASTUnitTest.cpp +++ b/unittests/Frontend/ASTUnitTest.cpp @@ -1,9 +1,8 @@ //===- unittests/Frontend/ASTUnitTest.cpp - ASTUnit tests -----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Frontend/CodeGenActionTest.cpp b/unittests/Frontend/CodeGenActionTest.cpp index d90c2bce2f..7576c91966 100644 --- a/unittests/Frontend/CodeGenActionTest.cpp +++ b/unittests/Frontend/CodeGenActionTest.cpp @@ -1,9 +1,8 @@ //===- unittests/Frontend/CodeGenActionTest.cpp --- FrontendAction tests --===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/unittests/Frontend/CompilerInstanceTest.cpp b/unittests/Frontend/CompilerInstanceTest.cpp index b2d9f8bcf0..1c3803289b 100644 --- a/unittests/Frontend/CompilerInstanceTest.cpp +++ b/unittests/Frontend/CompilerInstanceTest.cpp @@ -1,9 +1,8 @@ //===- unittests/Frontend/CompilerInstanceTest.cpp - CI tests -------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Frontend/FrontendActionTest.cpp b/unittests/Frontend/FrontendActionTest.cpp index ce0144538d..20356c6d83 100644 --- a/unittests/Frontend/FrontendActionTest.cpp +++ b/unittests/Frontend/FrontendActionTest.cpp @@ -1,24 +1,25 @@ //===- unittests/Frontend/FrontendActionTest.cpp - FrontendAction tests ---===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// +#include "clang/Frontend/FrontendAction.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" -#include "clang/Frontend/FrontendAction.h" #include "clang/Frontend/FrontendActions.h" #include "clang/Lex/Preprocessor.h" #include "clang/Lex/PreprocessorOptions.h" #include "clang/Sema/Sema.h" +#include "clang/Serialization/InMemoryModuleCache.h" #include "llvm/ADT/Triple.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/ToolOutputFile.h" #include "gtest/gtest.h" using namespace llvm; @@ -254,4 +255,40 @@ TEST(ASTFrontendAction, ExternalSemaSource) { EXPECT_EQ("This is a note", TDC->Note.str().str()); } +TEST(GeneratePCHFrontendAction, CacheGeneratedPCH) { + // Create a temporary file for writing out the PCH that will be cleaned up. + int PCHFD; + llvm::SmallString<128> PCHFilename; + ASSERT_FALSE( + llvm::sys::fs::createTemporaryFile("test.h", "pch", PCHFD, PCHFilename)); + llvm::ToolOutputFile PCHFile(PCHFilename, PCHFD); + + for (bool ShouldCache : {false, true}) { + auto Invocation = std::make_shared<CompilerInvocation>(); + Invocation->getLangOpts()->CacheGeneratedPCH = ShouldCache; + Invocation->getPreprocessorOpts().addRemappedFile( + "test.h", + MemoryBuffer::getMemBuffer("int foo(void) { return 1; }\n").release()); + Invocation->getFrontendOpts().Inputs.push_back( + FrontendInputFile("test.h", InputKind::C)); + Invocation->getFrontendOpts().OutputFile = StringRef(PCHFilename); + Invocation->getFrontendOpts().ProgramAction = frontend::GeneratePCH; + Invocation->getTargetOpts().Triple = "x86_64-apple-darwin19.0.0"; + CompilerInstance Compiler; + Compiler.setInvocation(std::move(Invocation)); + Compiler.createDiagnostics(); + + GeneratePCHAction TestAction; + ASSERT_TRUE(Compiler.ExecuteAction(TestAction)); + + // Check whether the PCH was cached. + if (ShouldCache) + EXPECT_EQ(InMemoryModuleCache::Final, + Compiler.getModuleCache().getPCMState(PCHFilename)); + else + EXPECT_EQ(InMemoryModuleCache::Unknown, + Compiler.getModuleCache().getPCMState(PCHFilename)); + } +} + } // anonymous namespace diff --git a/unittests/Frontend/OutputStreamTest.cpp b/unittests/Frontend/OutputStreamTest.cpp index ff036500d8..1ac875ffb3 100644 --- a/unittests/Frontend/OutputStreamTest.cpp +++ b/unittests/Frontend/OutputStreamTest.cpp @@ -1,9 +1,8 @@ //===- unittests/Frontend/OutputStreamTest.cpp --- FrontendAction tests --===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Frontend/PCHPreambleTest.cpp b/unittests/Frontend/PCHPreambleTest.cpp index 162a281b04..70567405f1 100644 --- a/unittests/Frontend/PCHPreambleTest.cpp +++ b/unittests/Frontend/PCHPreambleTest.cpp @@ -1,9 +1,8 @@ //====-- unittests/Frontend/PCHPreambleTest.cpp - FrontendAction tests ---====// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Frontend/ParsedSourceLocationTest.cpp b/unittests/Frontend/ParsedSourceLocationTest.cpp index 0cbdc7e1d5..1539005acd 100644 --- a/unittests/Frontend/ParsedSourceLocationTest.cpp +++ b/unittests/Frontend/ParsedSourceLocationTest.cpp @@ -1,9 +1,8 @@ //===- unittests/Frontend/ParsedSourceLocationTest.cpp - ------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Index/IndexTests.cpp b/unittests/Index/IndexTests.cpp index 2d4463d833..bbd5db3d39 100644 --- a/unittests/Index/IndexTests.cpp +++ b/unittests/Index/IndexTests.cpp @@ -1,14 +1,16 @@ //===--- IndexTests.cpp - Test indexing actions -----------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendAction.h" #include "clang/Index/IndexDataConsumer.h" @@ -24,40 +26,86 @@ namespace clang { namespace index { +namespace { +struct Position { + size_t Line = 0; + size_t Column = 0; + + Position(size_t Line = 0, size_t Column = 0) : Line(Line), Column(Column) {} + + static Position fromSourceLocation(SourceLocation Loc, + const SourceManager &SM) { + FileID FID; + unsigned Offset; + std::tie(FID, Offset) = SM.getDecomposedSpellingLoc(Loc); + Position P; + P.Line = SM.getLineNumber(FID, Offset); + P.Column = SM.getColumnNumber(FID, Offset); + return P; + } +}; + +bool operator==(const Position &LHS, const Position &RHS) { + return std::tie(LHS.Line, LHS.Column) == std::tie(RHS.Line, RHS.Column); +} + +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Position &Pos) { + return OS << Pos.Line << ':' << Pos.Column; +} struct TestSymbol { std::string QName; + Position WrittenPos; + Position DeclPos; + SymbolInfo SymInfo; + SymbolRoleSet Roles; // FIXME: add more information. }; llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const TestSymbol &S) { - return OS << S.QName; + return OS << S.QName << '[' << S.WrittenPos << ']' << '@' << S.DeclPos << '(' + << static_cast<unsigned>(S.SymInfo.Kind) << ')'; } -namespace { class Indexer : public IndexDataConsumer { public: + void initialize(ASTContext &Ctx) override { + AST = &Ctx; + IndexDataConsumer::initialize(Ctx); + } + bool handleDeclOccurence(const Decl *D, SymbolRoleSet Roles, - ArrayRef<SymbolRelation>, SourceLocation, + ArrayRef<SymbolRelation>, SourceLocation Loc, ASTNodeInfo) override { const auto *ND = llvm::dyn_cast<NamedDecl>(D); if (!ND) return true; TestSymbol S; + S.SymInfo = getSymbolInfo(D); S.QName = ND->getQualifiedNameAsString(); + S.WrittenPos = Position::fromSourceLocation(Loc, AST->getSourceManager()); + S.DeclPos = + Position::fromSourceLocation(D->getLocation(), AST->getSourceManager()); + S.Roles = Roles; Symbols.push_back(std::move(S)); return true; } - bool handleMacroOccurence(const IdentifierInfo *Name, const MacroInfo *, - SymbolRoleSet, SourceLocation) override { + bool handleMacroOccurence(const IdentifierInfo *Name, const MacroInfo *MI, + SymbolRoleSet Roles, SourceLocation Loc) override { TestSymbol S; + S.SymInfo = getSymbolInfoForMacro(*MI); S.QName = Name->getName(); + S.WrittenPos = Position::fromSourceLocation(Loc, AST->getSourceManager()); + S.DeclPos = Position::fromSourceLocation(MI->getDefinitionLoc(), + AST->getSourceManager()); + S.Roles = Roles; Symbols.push_back(std::move(S)); return true; } std::vector<TestSymbol> Symbols; + const ASTContext *AST = nullptr; }; class IndexAction : public ASTFrontendAction { @@ -94,11 +142,16 @@ private: IndexingOptions Opts; }; +using testing::AllOf; using testing::Contains; using testing::Not; using testing::UnorderedElementsAre; MATCHER_P(QName, Name, "") { return arg.QName == Name; } +MATCHER_P(WrittenAt, Pos, "") { return arg.WrittenPos == Pos; } +MATCHER_P(DeclAt, Pos, "") { return arg.DeclPos == Pos; } +MATCHER_P(Kind, SymKind, "") { return arg.SymInfo.Kind == SymKind; } +MATCHER_P(HasRole, Role, "") { return arg.Roles & static_cast<unsigned>(Role); } TEST(IndexTest, Simple) { auto Index = std::make_shared<Indexer>(); @@ -120,6 +173,125 @@ TEST(IndexTest, IndexPreprocessorMacros) { EXPECT_THAT(Index->Symbols, UnorderedElementsAre()); } +TEST(IndexTest, IndexParametersInDecls) { + std::string Code = "void foo(int bar);"; + auto Index = std::make_shared<Indexer>(); + IndexingOptions Opts; + Opts.IndexFunctionLocals = true; + Opts.IndexParametersInDeclarations = true; + tooling::runToolOnCode(new IndexAction(Index, Opts), Code); + EXPECT_THAT(Index->Symbols, Contains(QName("bar"))); + + Opts.IndexParametersInDeclarations = false; + Index->Symbols.clear(); + tooling::runToolOnCode(new IndexAction(Index, Opts), Code); + EXPECT_THAT(Index->Symbols, Not(Contains(QName("bar")))); +} + +TEST(IndexTest, IndexExplicitTemplateInstantiation) { + std::string Code = R"cpp( + template <typename T> + struct Foo { void bar() {} }; + template <> + struct Foo<int> { void bar() {} }; + void foo() { + Foo<char> abc; + Foo<int> b; + } + )cpp"; + auto Index = std::make_shared<Indexer>(); + IndexingOptions Opts; + tooling::runToolOnCode(new IndexAction(Index, Opts), Code); + EXPECT_THAT(Index->Symbols, + AllOf(Contains(AllOf(QName("Foo"), WrittenAt(Position(8, 7)), + DeclAt(Position(5, 12)))), + Contains(AllOf(QName("Foo"), WrittenAt(Position(7, 7)), + DeclAt(Position(3, 12)))))); +} + +TEST(IndexTest, IndexTemplateInstantiationPartial) { + std::string Code = R"cpp( + template <typename T1, typename T2> + struct Foo { void bar() {} }; + template <typename T> + struct Foo<T, int> { void bar() {} }; + void foo() { + Foo<char, char> abc; + Foo<int, int> b; + } + )cpp"; + auto Index = std::make_shared<Indexer>(); + IndexingOptions Opts; + tooling::runToolOnCode(new IndexAction(Index, Opts), Code); + EXPECT_THAT(Index->Symbols, + Contains(AllOf(QName("Foo"), WrittenAt(Position(8, 7)), + DeclAt(Position(5, 12))))); +} + +TEST(IndexTest, IndexTypeParmDecls) { + std::string Code = R"cpp( + template <typename T, int I, template<typename> class C, typename NoRef> + struct Foo { + T t = I; + C<int> x; + }; + )cpp"; + auto Index = std::make_shared<Indexer>(); + IndexingOptions Opts; + tooling::runToolOnCode(new IndexAction(Index, Opts), Code); + EXPECT_THAT(Index->Symbols, AllOf(Not(Contains(QName("Foo::T"))), + Not(Contains(QName("Foo::I"))), + Not(Contains(QName("Foo::C"))), + Not(Contains(QName("Foo::NoRef"))))); + + Opts.IndexTemplateParameters = true; + Index->Symbols.clear(); + tooling::runToolOnCode(new IndexAction(Index, Opts), Code); + EXPECT_THAT(Index->Symbols, + AllOf(Contains(QName("Foo::T")), Contains(QName("Foo::I")), + Contains(QName("Foo::C")), Contains(QName("Foo::NoRef")))); +} + +TEST(IndexTest, UsingDecls) { + std::string Code = R"cpp( + void foo(int bar); + namespace std { + using ::foo; + } + )cpp"; + auto Index = std::make_shared<Indexer>(); + IndexingOptions Opts; + tooling::runToolOnCode(new IndexAction(Index, Opts), Code); + EXPECT_THAT(Index->Symbols, + Contains(AllOf(QName("std::foo"), Kind(SymbolKind::Using)))); +} + +TEST(IndexTest, Constructors) { + std::string Code = R"cpp( + struct Foo { + Foo(int); + ~Foo(); + }; + )cpp"; + auto Index = std::make_shared<Indexer>(); + IndexingOptions Opts; + tooling::runToolOnCode(new IndexAction(Index, Opts), Code); + EXPECT_THAT( + Index->Symbols, + UnorderedElementsAre( + AllOf(QName("Foo"), Kind(SymbolKind::Struct), + WrittenAt(Position(2, 12))), + AllOf(QName("Foo::Foo"), Kind(SymbolKind::Constructor), + WrittenAt(Position(3, 7))), + AllOf(QName("Foo"), Kind(SymbolKind::Struct), + HasRole(SymbolRole::NameReference), WrittenAt(Position(3, 7))), + AllOf(QName("Foo::~Foo"), Kind(SymbolKind::Destructor), + WrittenAt(Position(4, 7))), + AllOf(QName("Foo"), Kind(SymbolKind::Struct), + HasRole(SymbolRole::NameReference), + WrittenAt(Position(4, 8))))); +} + } // namespace } // namespace index } // namespace clang diff --git a/unittests/Lex/HeaderMapTest.cpp b/unittests/Lex/HeaderMapTest.cpp index d16efe82c1..c18ce79ef5 100644 --- a/unittests/Lex/HeaderMapTest.cpp +++ b/unittests/Lex/HeaderMapTest.cpp @@ -1,9 +1,8 @@ //===- unittests/Lex/HeaderMapTest.cpp - HeaderMap tests ----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===--------------------------------------------------------------===// diff --git a/unittests/Lex/HeaderSearchTest.cpp b/unittests/Lex/HeaderSearchTest.cpp index 060135bc73..5bcdd9efd1 100644 --- a/unittests/Lex/HeaderSearchTest.cpp +++ b/unittests/Lex/HeaderSearchTest.cpp @@ -1,9 +1,8 @@ //===- unittests/Lex/HeaderSearchTest.cpp ------ HeaderSearch tests -------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -12,12 +11,12 @@ #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/LangOptions.h" -#include "clang/Basic/MemoryBufferCache.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/TargetOptions.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/HeaderSearchOptions.h" +#include "clang/Serialization/InMemoryModuleCache.h" #include "gtest/gtest.h" namespace clang { @@ -92,5 +91,21 @@ TEST_F(HeaderSearchTest, Dots) { "z"); } +#ifdef _WIN32 +TEST_F(HeaderSearchTest, BackSlash) { + addSearchDir("C:\\x\\y\\"); + EXPECT_EQ(Search.suggestPathToFileForDiagnostics("C:\\x\\y\\z\\t", + /*WorkingDir=*/""), + "z/t"); +} +#endif + +TEST_F(HeaderSearchTest, DotDotsWithAbsPath) { + addSearchDir("/x/../y/"); + EXPECT_EQ(Search.suggestPathToFileForDiagnostics("/y/z", + /*WorkingDir=*/""), + "z"); +} + } // namespace } // namespace clang diff --git a/unittests/Lex/LexerTest.cpp b/unittests/Lex/LexerTest.cpp index c913062a7a..7b14f56201 100644 --- a/unittests/Lex/LexerTest.cpp +++ b/unittests/Lex/LexerTest.cpp @@ -1,9 +1,8 @@ //===- unittests/Lex/LexerTest.cpp ------ Lexer tests ---------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -12,7 +11,6 @@ #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/LangOptions.h" -#include "clang/Basic/MemoryBufferCache.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/TargetOptions.h" @@ -49,12 +47,11 @@ protected: llvm::MemoryBuffer::getMemBuffer(Source); SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf))); - MemoryBufferCache PCMCache; HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr, Diags, LangOpts, Target.get()); std::unique_ptr<Preprocessor> PP = llvm::make_unique<Preprocessor>( std::make_shared<PreprocessorOptions>(), Diags, LangOpts, SourceMgr, - PCMCache, HeaderInfo, ModLoader, + HeaderInfo, ModLoader, /*IILookup =*/nullptr, /*OwnsHeaderSearch =*/false); PP->Initialize(*Target); @@ -516,4 +513,23 @@ TEST_F(LexerTest, StringizingRasString) { EXPECT_EQ(String6, R"(a\\\n\n\n \\\\b)"); } +TEST_F(LexerTest, CharRangeOffByOne) { + std::vector<Token> toks = Lex(R"(#define MOO 1 + void foo() { MOO; })"); + const Token &moo = toks[5]; + + EXPECT_EQ(getSourceText(moo, moo), "MOO"); + + SourceRange R{moo.getLocation(), moo.getLocation()}; + + EXPECT_TRUE( + Lexer::isAtStartOfMacroExpansion(R.getBegin(), SourceMgr, LangOpts)); + EXPECT_TRUE( + Lexer::isAtEndOfMacroExpansion(R.getEnd(), SourceMgr, LangOpts)); + + CharSourceRange CR = Lexer::getAsCharRange(R, SourceMgr, LangOpts); + + EXPECT_EQ(Lexer::getSourceText(CR, SourceMgr, LangOpts), "MOO"); // Was "MO". +} + } // anonymous namespace diff --git a/unittests/Lex/PPCallbacksTest.cpp b/unittests/Lex/PPCallbacksTest.cpp index 838e033e3d..91765960c3 100644 --- a/unittests/Lex/PPCallbacksTest.cpp +++ b/unittests/Lex/PPCallbacksTest.cpp @@ -1,9 +1,8 @@ //===- unittests/Lex/PPCallbacksTest.cpp - PPCallbacks tests ------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===--------------------------------------------------------------===// @@ -14,7 +13,6 @@ #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/LangOptions.h" -#include "clang/Basic/MemoryBufferCache.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/TargetOptions.h" @@ -179,14 +177,13 @@ protected: SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf))); TrivialModuleLoader ModLoader; - MemoryBufferCache PCMCache; HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr, Diags, LangOpts, Target.get()); AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader); Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts, - SourceMgr, PCMCache, HeaderInfo, ModLoader, + SourceMgr, HeaderInfo, ModLoader, /*IILookup =*/nullptr, /*OwnsHeaderSearch =*/false); return InclusionDirectiveCallback(PP)->FilenameRange; @@ -199,14 +196,13 @@ protected: SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf))); TrivialModuleLoader ModLoader; - MemoryBufferCache PCMCache; HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr, Diags, LangOpts, Target.get()); AddFakeHeader(HeaderInfo, HeaderPath, SystemHeader); Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts, - SourceMgr, PCMCache, HeaderInfo, ModLoader, + SourceMgr, HeaderInfo, ModLoader, /*IILookup =*/nullptr, /*OwnsHeaderSearch =*/false); return InclusionDirectiveCallback(PP)->FileType; @@ -234,14 +230,13 @@ protected: 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, + SourceMgr, HeaderInfo, ModLoader, /*IILookup =*/nullptr, /*OwnsHeaderSearch =*/false); PP.Initialize(*Target); @@ -271,12 +266,11 @@ protected: SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(SourceBuf))); TrivialModuleLoader ModLoader; - MemoryBufferCache PCMCache; HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr, Diags, OpenCLLangOpts, Target.get()); Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, - OpenCLLangOpts, SourceMgr, PCMCache, HeaderInfo, ModLoader, + OpenCLLangOpts, SourceMgr, HeaderInfo, ModLoader, /*IILookup =*/nullptr, /*OwnsHeaderSearch =*/false); PP.Initialize(*Target); @@ -431,16 +425,69 @@ TEST_F(PPCallbacksTest, OpenCLExtensionPragmaDisabled) { } TEST_F(PPCallbacksTest, DirectiveExprRanges) { + const auto &Results1 = DirectiveExprRange("#if FLUZZY_FLOOF\n#endif\n"); + EXPECT_EQ(Results1.size(), 1U); + EXPECT_EQ( + GetSourceStringToEnd(CharSourceRange(Results1[0].ConditionRange, false)), + "FLUZZY_FLOOF"); + + const auto &Results2 = DirectiveExprRange("#if 1 + 4 < 7\n#endif\n"); + EXPECT_EQ(Results2.size(), 1U); + EXPECT_EQ( + GetSourceStringToEnd(CharSourceRange(Results2[0].ConditionRange, false)), + "1 + 4 < 7"); + + const auto &Results3 = DirectiveExprRange("#if 1 + \\\n 2\n#endif\n"); + EXPECT_EQ(Results3.size(), 1U); + EXPECT_EQ( + GetSourceStringToEnd(CharSourceRange(Results3[0].ConditionRange, false)), + "1 + \\\n 2"); + + const auto &Results4 = DirectiveExprRange("#if 0\n#elif FLOOFY\n#endif\n"); + EXPECT_EQ(Results4.size(), 2U); + EXPECT_EQ( + GetSourceStringToEnd(CharSourceRange(Results4[0].ConditionRange, false)), + "0"); + EXPECT_EQ( + GetSourceStringToEnd(CharSourceRange(Results4[1].ConditionRange, false)), + "FLOOFY"); + + const auto &Results5 = DirectiveExprRange("#if 1\n#elif FLOOFY\n#endif\n"); + EXPECT_EQ(Results5.size(), 2U); + EXPECT_EQ( + GetSourceStringToEnd(CharSourceRange(Results5[0].ConditionRange, false)), + "1"); + EXPECT_EQ( + GetSourceStringToEnd(CharSourceRange(Results5[1].ConditionRange, false)), + "FLOOFY"); + + const auto &Results6 = + DirectiveExprRange("#if defined(FLUZZY_FLOOF)\n#endif\n"); + EXPECT_EQ(Results6.size(), 1U); + EXPECT_EQ( + GetSourceStringToEnd(CharSourceRange(Results6[0].ConditionRange, false)), + "defined(FLUZZY_FLOOF)"); + + const auto &Results7 = + DirectiveExprRange("#if 1\n#elif defined(FLOOFY)\n#endif\n"); + EXPECT_EQ(Results7.size(), 2U); + EXPECT_EQ( + GetSourceStringToEnd(CharSourceRange(Results7[0].ConditionRange, false)), + "1"); + EXPECT_EQ( + GetSourceStringToEnd(CharSourceRange(Results7[1].ConditionRange, false)), + "defined(FLOOFY)"); + 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#"); + "__FILE__ > FLOOFY"); EXPECT_EQ( Lexer::getSourceText(CharSourceRange(Results8[0].ConditionRange, false), SourceMgr, LangOpts), - " __FILE__ > FLOOFY\n"); + "__FILE__ > FLOOFY"); } } // namespace diff --git a/unittests/Lex/PPConditionalDirectiveRecordTest.cpp b/unittests/Lex/PPConditionalDirectiveRecordTest.cpp index f7b6f717a1..ba75639578 100644 --- a/unittests/Lex/PPConditionalDirectiveRecordTest.cpp +++ b/unittests/Lex/PPConditionalDirectiveRecordTest.cpp @@ -1,9 +1,8 @@ //===- unittests/Lex/PPConditionalDirectiveRecordTest.cpp-PP directive tests =// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -12,7 +11,6 @@ #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/LangOptions.h" -#include "clang/Basic/MemoryBufferCache.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/TargetOptions.h" @@ -76,11 +74,10 @@ TEST_F(PPConditionalDirectiveRecordTest, PPRecAPI) { SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf))); TrivialModuleLoader ModLoader; - MemoryBufferCache PCMCache; HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr, Diags, LangOpts, Target.get()); Preprocessor PP(std::make_shared<PreprocessorOptions>(), Diags, LangOpts, - SourceMgr, PCMCache, HeaderInfo, ModLoader, + SourceMgr, HeaderInfo, ModLoader, /*IILookup =*/nullptr, /*OwnsHeaderSearch =*/false); PP.Initialize(*Target); diff --git a/unittests/Rename/ClangRenameTest.h b/unittests/Rename/ClangRenameTest.h index 13906d15bc..9dfa6d9c90 100644 --- a/unittests/Rename/ClangRenameTest.h +++ b/unittests/Rename/ClangRenameTest.h @@ -1,9 +1,8 @@ //===-- ClangRenameTests.cpp - clang-rename unit tests --------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Rename/RenameAliasTest.cpp b/unittests/Rename/RenameAliasTest.cpp index 59becaef68..ad9ce65ac8 100644 --- a/unittests/Rename/RenameAliasTest.cpp +++ b/unittests/Rename/RenameAliasTest.cpp @@ -1,9 +1,8 @@ //===-- RenameAliasTest.cpp - unit tests for renaming alias ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Rename/RenameClassTest.cpp b/unittests/Rename/RenameClassTest.cpp index 5845d63412..04a9138f74 100644 --- a/unittests/Rename/RenameClassTest.cpp +++ b/unittests/Rename/RenameClassTest.cpp @@ -1,9 +1,8 @@ //===-- RenameClassTest.cpp - unit tests for renaming classes -------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Rename/RenameFunctionTest.cpp b/unittests/Rename/RenameFunctionTest.cpp index b27bbe273a..1c9b112232 100644 --- a/unittests/Rename/RenameFunctionTest.cpp +++ b/unittests/Rename/RenameFunctionTest.cpp @@ -1,9 +1,8 @@ //===-- RenameFunctionTest.cpp - unit tests for renaming functions --------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Rename/RenameMemberTest.cpp b/unittests/Rename/RenameMemberTest.cpp index fb8d5580fb..c9192c638a 100644 --- a/unittests/Rename/RenameMemberTest.cpp +++ b/unittests/Rename/RenameMemberTest.cpp @@ -1,9 +1,8 @@ //===-- ClangMemberTests.cpp - unit tests for renaming class members ------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Rewrite/RewriteBufferTest.cpp b/unittests/Rewrite/RewriteBufferTest.cpp index e3b7d1fb88..eb8d986c4d 100644 --- a/unittests/Rewrite/RewriteBufferTest.cpp +++ b/unittests/Rewrite/RewriteBufferTest.cpp @@ -1,9 +1,8 @@ //===- unittests/Rewrite/RewriteBufferTest.cpp - RewriteBuffer tests ------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Sema/CMakeLists.txt b/unittests/Sema/CMakeLists.txt index 78601046dc..00ffa65864 100644 --- a/unittests/Sema/CMakeLists.txt +++ b/unittests/Sema/CMakeLists.txt @@ -16,4 +16,5 @@ target_link_libraries(SemaTests clangSema clangSerialization clangTooling + LLVMTestingSupport ) diff --git a/unittests/Sema/CodeCompleteTest.cpp b/unittests/Sema/CodeCompleteTest.cpp index 28faa0c1ee..4e1068f4a3 100644 --- a/unittests/Sema/CodeCompleteTest.cpp +++ b/unittests/Sema/CodeCompleteTest.cpp @@ -1,9 +1,8 @@ //=== unittests/Sema/CodeCompleteTest.cpp - Code Complete tests ==============// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -14,6 +13,7 @@ #include "clang/Sema/Sema.h" #include "clang/Sema/SemaDiagnostic.h" #include "clang/Tooling/Tooling.h" +#include "llvm/Testing/Support/Annotations.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include <cstddef> @@ -39,9 +39,7 @@ struct CompletionContext { class VisitedContextFinder : public CodeCompleteConsumer { public: VisitedContextFinder(CompletionContext &ResultCtx) - : CodeCompleteConsumer(/*CodeCompleteOpts=*/{}, - /*CodeCompleteConsumer*/ false), - ResultCtx(ResultCtx), + : CodeCompleteConsumer(/*CodeCompleteOpts=*/{}), ResultCtx(ResultCtx), CCTUInfo(std::make_shared<GlobalCodeCompletionAllocator>()) {} void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context, @@ -110,41 +108,18 @@ CompletionContext runCompletion(StringRef Code, size_t Offset) { return ResultCtx; } -struct ParsedAnnotations { - std::vector<size_t> Points; - std::string Code; -}; - -ParsedAnnotations parseAnnotations(StringRef AnnotatedCode) { - ParsedAnnotations R; - while (!AnnotatedCode.empty()) { - size_t NextPoint = AnnotatedCode.find('^'); - if (NextPoint == StringRef::npos) { - R.Code += AnnotatedCode; - AnnotatedCode = ""; - break; - } - R.Code += AnnotatedCode.substr(0, NextPoint); - R.Points.push_back(R.Code.size()); - - AnnotatedCode = AnnotatedCode.substr(NextPoint + 1); - } - return R; -} - CompletionContext runCodeCompleteOnCode(StringRef AnnotatedCode) { - ParsedAnnotations P = parseAnnotations(AnnotatedCode); - assert(P.Points.size() == 1 && "expected exactly one annotation point"); - return runCompletion(P.Code, P.Points.front()); + llvm::Annotations A(AnnotatedCode); + return runCompletion(A.code(), A.point()); } std::vector<std::string> collectPreferredTypes(StringRef AnnotatedCode, std::string *PtrDiffType = nullptr) { - ParsedAnnotations P = parseAnnotations(AnnotatedCode); + llvm::Annotations A(AnnotatedCode); std::vector<std::string> Types; - for (size_t Point : P.Points) { - auto Results = runCompletion(P.Code, Point); + for (size_t Point : A.points()) { + auto Results = runCompletion(A.code(), Point); if (PtrDiffType) { assert(PtrDiffType->empty() || *PtrDiffType == Results.PtrDiffType); *PtrDiffType = Results.PtrDiffType; @@ -174,12 +149,16 @@ TEST(SemaCodeCompleteTest, VisitedNSForValidQualifiedId) { "foo::(anonymous)")); } -TEST(SemaCodeCompleteTest, VisitedNSForInvalideQualifiedId) { +TEST(SemaCodeCompleteTest, VisitedNSForInvalidQualifiedId) { auto VisitedNS = runCodeCompleteOnCode(R"cpp( - namespace ns { foo::^ } + namespace na {} + namespace ns1 { + using namespace na; + foo::^ + } )cpp") .VisitedNamespaces; - EXPECT_TRUE(VisitedNS.empty()); + EXPECT_THAT(VisitedNS, UnorderedElementsAre("ns1", "na")); } TEST(SemaCodeCompleteTest, VisitedNSWithoutQualifier) { @@ -340,4 +319,140 @@ TEST(PreferredTypeTest, BinaryExpr) { EXPECT_THAT(collectPreferredTypes(Code), Each("NULL TYPE")); } +TEST(PreferredTypeTest, Members) { + StringRef Code = R"cpp( + struct vector { + int *begin(); + vector clone(); + }; + + void test(int *a) { + a = ^vector().^clone().^begin(); + } + )cpp"; + EXPECT_THAT(collectPreferredTypes(Code), Each("int *")); +} + +TEST(PreferredTypeTest, Conditions) { + StringRef Code = R"cpp( + struct vector { + bool empty(); + }; + + void test() { + if (^vector().^empty()) {} + while (^vector().^empty()) {} + for (; ^vector().^empty();) {} + } + )cpp"; + EXPECT_THAT(collectPreferredTypes(Code), Each("_Bool")); +} + +TEST(PreferredTypeTest, InitAndAssignment) { + StringRef Code = R"cpp( + struct vector { + int* begin(); + }; + + void test() { + const int* x = ^vector().^begin(); + x = ^vector().^begin(); + + if (const int* y = ^vector().^begin()) {} + } + )cpp"; + EXPECT_THAT(collectPreferredTypes(Code), Each("const int *")); +} + +TEST(PreferredTypeTest, UnaryExprs) { + StringRef Code = R"cpp( + void test(long long a) { + a = +^a; + a = -^a + a = ++^a; + a = --^a; + } + )cpp"; + EXPECT_THAT(collectPreferredTypes(Code), Each("long long")); + + Code = R"cpp( + void test(int a, int *ptr) { + !^a; + !^ptr; + !!!^a; + + a = !^a; + a = !^ptr; + a = !!!^a; + } + )cpp"; + EXPECT_THAT(collectPreferredTypes(Code), Each("_Bool")); + + Code = R"cpp( + void test(int a) { + const int* x = &^a; + } + )cpp"; + EXPECT_THAT(collectPreferredTypes(Code), Each("const int")); + + Code = R"cpp( + void test(int *a) { + int x = *^a; + int &r = *^a; + } + )cpp"; + EXPECT_THAT(collectPreferredTypes(Code), Each("int *")); + + Code = R"cpp( + void test(int a) { + *^a; + &^a; + } + + )cpp"; +} + +TEST(PreferredTypeTest, ParenExpr) { + StringRef Code = R"cpp( + const int *i = ^(^(^(^10))); + )cpp"; + EXPECT_THAT(collectPreferredTypes(Code), Each("const int *")); +} + +TEST(PreferredTypeTest, FunctionArguments) { + StringRef Code = R"cpp( + void foo(const int*); + + void bar(const int*); + void bar(const int*, int b); + + struct vector { + const int *data(); + }; + void test() { + foo(^(^(^(^vec^tor^().^da^ta^())))); + bar(^(^(^(^vec^tor^().^da^ta^())))); + } + )cpp"; + EXPECT_THAT(collectPreferredTypes(Code), Each("const int *")); + + Code = R"cpp( + void bar(int, volatile double *); + void bar(int, volatile double *, int, int); + + struct vector { + double *data(); + }; + + struct class_members { + void bar(int, volatile double *); + void bar(int, volatile double *, int, int); + }; + void test() { + bar(10, ^(^(^(^vec^tor^().^da^ta^())))); + class_members().bar(10, ^(^(^(^vec^tor^().^da^ta^())))); + } + )cpp"; + EXPECT_THAT(collectPreferredTypes(Code), Each("volatile double *")); +} } // namespace diff --git a/unittests/Sema/ExternalSemaSourceTest.cpp b/unittests/Sema/ExternalSemaSourceTest.cpp index d2cdd633fa..c591ccbb73 100644 --- a/unittests/Sema/ExternalSemaSourceTest.cpp +++ b/unittests/Sema/ExternalSemaSourceTest.cpp @@ -1,9 +1,8 @@ //=== unittests/Sema/ExternalSemaSourceTest.cpp - ExternalSemaSource tests ===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Serialization/CMakeLists.txt b/unittests/Serialization/CMakeLists.txt new file mode 100644 index 0000000000..c7ec9a4f07 --- /dev/null +++ b/unittests/Serialization/CMakeLists.txt @@ -0,0 +1,17 @@ +set(LLVM_LINK_COMPONENTS + BitReader + Support + ) + +add_clang_unittest(SerializationTests + InMemoryModuleCacheTest.cpp + ) + +target_link_libraries(SerializationTests + PRIVATE + clangAST + clangBasic + clangLex + clangSema + clangSerialization + ) diff --git a/unittests/Serialization/InMemoryModuleCacheTest.cpp b/unittests/Serialization/InMemoryModuleCacheTest.cpp new file mode 100644 index 0000000000..ed5e1538eb --- /dev/null +++ b/unittests/Serialization/InMemoryModuleCacheTest.cpp @@ -0,0 +1,119 @@ +//===- InMemoryModuleCacheTest.cpp - InMemoryModuleCache tests ------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Serialization/InMemoryModuleCache.h" +#include "llvm/Support/MemoryBuffer.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace clang; + +namespace { + +std::unique_ptr<MemoryBuffer> getBuffer(int I) { + SmallVector<char, 8> Bytes; + raw_svector_ostream(Bytes) << "data:" << I; + return MemoryBuffer::getMemBuffer(StringRef(Bytes.data(), Bytes.size()), "", + /* RequiresNullTerminator = */ false); +} + +TEST(InMemoryModuleCacheTest, initialState) { + InMemoryModuleCache Cache; + EXPECT_EQ(InMemoryModuleCache::Unknown, Cache.getPCMState("B")); + EXPECT_FALSE(Cache.isPCMFinal("B")); + EXPECT_FALSE(Cache.shouldBuildPCM("B")); + +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + EXPECT_DEATH(Cache.tryToDropPCM("B"), "PCM to remove is unknown"); + EXPECT_DEATH(Cache.finalizePCM("B"), "PCM to finalize is unknown"); +#endif +} + +TEST(InMemoryModuleCacheTest, addPCM) { + auto B = getBuffer(1); + auto *RawB = B.get(); + + InMemoryModuleCache Cache; + EXPECT_EQ(RawB, &Cache.addPCM("B", std::move(B))); + EXPECT_EQ(InMemoryModuleCache::Tentative, Cache.getPCMState("B")); + EXPECT_EQ(RawB, Cache.lookupPCM("B")); + EXPECT_FALSE(Cache.isPCMFinal("B")); + EXPECT_FALSE(Cache.shouldBuildPCM("B")); + +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + EXPECT_DEATH(Cache.addPCM("B", getBuffer(2)), "Already has a PCM"); + EXPECT_DEATH(Cache.addBuiltPCM("B", getBuffer(2)), + "Trying to override tentative PCM"); +#endif +} + +TEST(InMemoryModuleCacheTest, addBuiltPCM) { + auto B = getBuffer(1); + auto *RawB = B.get(); + + InMemoryModuleCache Cache; + EXPECT_EQ(RawB, &Cache.addBuiltPCM("B", std::move(B))); + EXPECT_EQ(InMemoryModuleCache::Final, Cache.getPCMState("B")); + EXPECT_EQ(RawB, Cache.lookupPCM("B")); + EXPECT_TRUE(Cache.isPCMFinal("B")); + EXPECT_FALSE(Cache.shouldBuildPCM("B")); + +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + EXPECT_DEATH(Cache.addPCM("B", getBuffer(2)), "Already has a PCM"); + EXPECT_DEATH(Cache.addBuiltPCM("B", getBuffer(2)), + "Trying to override finalized PCM"); +#endif +} + +TEST(InMemoryModuleCacheTest, tryToDropPCM) { + auto B1 = getBuffer(1); + auto B2 = getBuffer(2); + auto *RawB1 = B1.get(); + auto *RawB2 = B2.get(); + ASSERT_NE(RawB1, RawB2); + + InMemoryModuleCache Cache; + EXPECT_EQ(InMemoryModuleCache::Unknown, Cache.getPCMState("B")); + EXPECT_EQ(RawB1, &Cache.addPCM("B", std::move(B1))); + EXPECT_FALSE(Cache.tryToDropPCM("B")); + EXPECT_EQ(nullptr, Cache.lookupPCM("B")); + EXPECT_EQ(InMemoryModuleCache::ToBuild, Cache.getPCMState("B")); + EXPECT_FALSE(Cache.isPCMFinal("B")); + EXPECT_TRUE(Cache.shouldBuildPCM("B")); + +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + EXPECT_DEATH(Cache.addPCM("B", getBuffer(2)), "Already has a PCM"); + EXPECT_DEATH(Cache.tryToDropPCM("B"), + "PCM to remove is scheduled to be built"); + EXPECT_DEATH(Cache.finalizePCM("B"), "Trying to finalize a dropped PCM"); +#endif + + // Add a new one. + EXPECT_EQ(RawB2, &Cache.addBuiltPCM("B", std::move(B2))); + EXPECT_TRUE(Cache.isPCMFinal("B")); + + // Can try to drop again, but this should error and do nothing. + EXPECT_TRUE(Cache.tryToDropPCM("B")); + EXPECT_EQ(RawB2, Cache.lookupPCM("B")); +} + +TEST(InMemoryModuleCacheTest, finalizePCM) { + auto B = getBuffer(1); + auto *RawB = B.get(); + + InMemoryModuleCache Cache; + EXPECT_EQ(InMemoryModuleCache::Unknown, Cache.getPCMState("B")); + EXPECT_EQ(RawB, &Cache.addPCM("B", std::move(B))); + + // Call finalize. + Cache.finalizePCM("B"); + EXPECT_EQ(InMemoryModuleCache::Final, Cache.getPCMState("B")); + EXPECT_TRUE(Cache.isPCMFinal("B")); +} + +} // namespace diff --git a/unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp b/unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp index de41874bde..0fb0c04b97 100644 --- a/unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp +++ b/unittests/StaticAnalyzer/AnalyzerOptionsTest.cpp @@ -1,9 +1,8 @@ //===- unittest/StaticAnalyzer/AnalyzerOptionsTest.cpp - SA Options test --===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -49,28 +48,28 @@ TEST(StaticAnalyzerOptions, SearchInParentPackageTests) { } }; - // Checker one has Option specified as true. It should read true regardless of - // search mode. + // CheckerTwo one has Option specified as true. It should read true regardless + // of search mode. CheckerOneMock CheckerOne; - EXPECT_TRUE(Opts.getCheckerBooleanOption("Option", false, &CheckerOne)); + EXPECT_TRUE(Opts.getCheckerBooleanOption(&CheckerOne, "Option", false)); // The package option is overridden with a checker option. - EXPECT_TRUE(Opts.getCheckerBooleanOption("Option", false, &CheckerOne, + EXPECT_TRUE(Opts.getCheckerBooleanOption(&CheckerOne, "Option", false, true)); // The Outer package option is overridden by the Inner package option. No // package option is specified. - EXPECT_TRUE(Opts.getCheckerBooleanOption("Option2", false, &CheckerOne, + EXPECT_TRUE(Opts.getCheckerBooleanOption(&CheckerOne, "Option2", false, true)); // No package option is specified and search in packages is turned off. The // default value should be returned. - EXPECT_FALSE(Opts.getCheckerBooleanOption("Option2", false, &CheckerOne)); - EXPECT_TRUE(Opts.getCheckerBooleanOption("Option2", true, &CheckerOne)); + EXPECT_FALSE(Opts.getCheckerBooleanOption(&CheckerOne, "Option2", false)); + EXPECT_TRUE(Opts.getCheckerBooleanOption(&CheckerOne, "Option2", true)); // Checker true has no option specified. It should get the default value when // search in parents turned off and false when search in parents turned on. CheckerTwoMock CheckerTwo; - EXPECT_FALSE(Opts.getCheckerBooleanOption("Option", false, &CheckerTwo)); - EXPECT_TRUE(Opts.getCheckerBooleanOption("Option", true, &CheckerTwo)); - EXPECT_FALSE(Opts.getCheckerBooleanOption("Option", true, &CheckerTwo, true)); + EXPECT_FALSE(Opts.getCheckerBooleanOption(&CheckerTwo, "Option", false)); + EXPECT_TRUE(Opts.getCheckerBooleanOption(&CheckerTwo, "Option", true)); + EXPECT_FALSE(Opts.getCheckerBooleanOption(&CheckerTwo, "Option", true, true)); } TEST(StaticAnalyzerOptions, StringOptions) { @@ -85,9 +84,17 @@ TEST(StaticAnalyzerOptions, StringOptions) { CheckerOneMock CheckerOne; EXPECT_TRUE("StringValue" == - Opts.getCheckerStringOption("Option", "DefaultValue", &CheckerOne)); + Opts.getCheckerStringOption(&CheckerOne, "Option", "DefaultValue")); EXPECT_TRUE("DefaultValue" == - Opts.getCheckerStringOption("Option2", "DefaultValue", &CheckerOne)); + Opts.getCheckerStringOption(&CheckerOne, "Option2", "DefaultValue")); +} + +TEST(StaticAnalyzerOptions, SubCheckerOptions) { + AnalyzerOptions Opts; + Opts.Config["Outer.Inner.CheckerOne:Option"] = "StringValue"; + EXPECT_TRUE("StringValue" == Opts.getCheckerStringOption( + "Outer.Inner.CheckerOne", "Option", "DefaultValue")); } + } // end namespace ento } // end namespace clang diff --git a/unittests/StaticAnalyzer/CMakeLists.txt b/unittests/StaticAnalyzer/CMakeLists.txt index 3036dec167..5348a0a2bc 100644 --- a/unittests/StaticAnalyzer/CMakeLists.txt +++ b/unittests/StaticAnalyzer/CMakeLists.txt @@ -4,13 +4,18 @@ set(LLVM_LINK_COMPONENTS add_clang_unittest(StaticAnalysisTests AnalyzerOptionsTest.cpp + StoreTest.cpp RegisterCustomCheckersTest.cpp + SymbolReaperTest.cpp ) target_link_libraries(StaticAnalysisTests PRIVATE clangBasic clangAnalysis + clangAST + clangASTMatchers + clangCrossTU clangFrontend clangSerialization clangStaticAnalyzerCore diff --git a/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp b/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp index 568a719e33..d8988a0ee3 100644 --- a/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp +++ b/unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp @@ -1,9 +1,8 @@ //===- unittests/StaticAnalyzer/RegisterCustomCheckersTest.cpp ------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -89,8 +88,9 @@ public: void checkLocation(SVal Loc, bool IsLoad, const Stmt *S, CheckerContext &C) const { auto UnaryOp = dyn_cast<UnaryOperator>(S); - if (UnaryOp && !IsLoad) + if (UnaryOp && !IsLoad) { EXPECT_FALSE(UnaryOp->isIncrementOp()); + } } }; diff --git a/unittests/StaticAnalyzer/Reusables.h b/unittests/StaticAnalyzer/Reusables.h new file mode 100644 index 0000000000..06aed884f6 --- /dev/null +++ b/unittests/StaticAnalyzer/Reusables.h @@ -0,0 +1,63 @@ +//===- unittests/StaticAnalyzer/Reusables.h -------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_UNITTESTS_STATICANALYZER_REUSABLES_H +#define LLVM_CLANG_UNITTESTS_STATICANALYZER_REUSABLES_H + +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/CrossTU/CrossTranslationUnit.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" + +namespace clang { +namespace ento { + +// Find a declaration in the current AST by name. +template <typename T> +const T *findDeclByName(const Decl *Where, StringRef Name) { + using namespace ast_matchers; + auto Matcher = decl(hasDescendant(namedDecl(hasName(Name)).bind("d"))); + auto Matches = match(Matcher, *Where, Where->getASTContext()); + assert(Matches.size() == 1 && "Ambiguous name!"); + const T *Node = selectFirst<T>("d", Matches); + assert(Node && "Name not found!"); + return Node; +} + +// A re-usable consumer that constructs ExprEngine out of CompilerInvocation. +class ExprEngineConsumer : public ASTConsumer { +protected: + CompilerInstance &C; + +private: + // We need to construct all of these in order to construct ExprEngine. + CheckerManager ChkMgr; + cross_tu::CrossTranslationUnitContext CTU; + PathDiagnosticConsumers Consumers; + AnalysisManager AMgr; + SetOfConstDecls VisitedCallees; + FunctionSummariesTy FS; + +protected: + ExprEngine Eng; + +public: + ExprEngineConsumer(CompilerInstance &C) + : C(C), ChkMgr(C.getASTContext(), *C.getAnalyzerOpts()), CTU(C), + Consumers(), + AMgr(C.getASTContext(), C.getDiagnostics(), Consumers, + CreateRegionStoreManager, CreateRangeConstraintManager, &ChkMgr, + *C.getAnalyzerOpts()), + VisitedCallees(), FS(), + Eng(CTU, AMgr, &VisitedCallees, &FS, ExprEngine::Inline_Regular) {} +}; + +} // namespace ento +} // namespace clang + +#endif diff --git a/unittests/StaticAnalyzer/StoreTest.cpp b/unittests/StaticAnalyzer/StoreTest.cpp new file mode 100644 index 0000000000..ab8c781e32 --- /dev/null +++ b/unittests/StaticAnalyzer/StoreTest.cpp @@ -0,0 +1,105 @@ +//===- unittests/StaticAnalyzer/StoreTest.cpp -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Reusables.h" + +#include "clang/Tooling/Tooling.h" +#include "gtest/gtest.h" + +namespace clang { +namespace ento { +namespace { + +// Test that we can put a value into an int-type variable and load it +// back from that variable. Test what happens if default bindings are used. +class VariableBindConsumer : public ExprEngineConsumer { + void performTest(const Decl *D) { + StoreManager &StMgr = Eng.getStoreManager(); + SValBuilder &SVB = Eng.getSValBuilder(); + MemRegionManager &MRMgr = StMgr.getRegionManager(); + const ASTContext &ACtx = Eng.getContext(); + + const auto *VDX0 = findDeclByName<VarDecl>(D, "x0"); + const auto *VDY0 = findDeclByName<VarDecl>(D, "y0"); + const auto *VDZ0 = findDeclByName<VarDecl>(D, "z0"); + const auto *VDX1 = findDeclByName<VarDecl>(D, "x1"); + const auto *VDY1 = findDeclByName<VarDecl>(D, "y1"); + assert(VDX0 && VDY0 && VDZ0 && VDX1 && VDY1); + + const StackFrameContext *SFC = + Eng.getAnalysisDeclContextManager().getStackFrame(D); + + Loc LX0 = loc::MemRegionVal(MRMgr.getVarRegion(VDX0, SFC)); + Loc LY0 = loc::MemRegionVal(MRMgr.getVarRegion(VDY0, SFC)); + Loc LZ0 = loc::MemRegionVal(MRMgr.getVarRegion(VDZ0, SFC)); + Loc LX1 = loc::MemRegionVal(MRMgr.getVarRegion(VDX1, SFC)); + Loc LY1 = loc::MemRegionVal(MRMgr.getVarRegion(VDY1, SFC)); + + Store StInit = StMgr.getInitialStore(SFC).getStore(); + SVal Zero = SVB.makeZeroVal(ACtx.IntTy); + SVal One = SVB.makeIntVal(1, ACtx.IntTy); + SVal NarrowZero = SVB.makeZeroVal(ACtx.CharTy); + + // Bind(Zero) + Store StX0 = + StMgr.Bind(StInit, LX0, Zero).getStore(); + ASSERT_EQ(Zero, StMgr.getBinding(StX0, LX0, ACtx.IntTy)); + + // BindDefaultInitial(Zero) + Store StY0 = + StMgr.BindDefaultInitial(StInit, LY0.getAsRegion(), Zero).getStore(); + ASSERT_EQ(Zero, StMgr.getBinding(StY0, LY0, ACtx.IntTy)); + ASSERT_EQ(Zero, *StMgr.getDefaultBinding(StY0, LY0.getAsRegion())); + + // BindDefaultZero() + Store StZ0 = + StMgr.BindDefaultZero(StInit, LZ0.getAsRegion()).getStore(); + // BindDefaultZero wipes the region with '0 S8b', not with out Zero. + // Direct load, however, does give us back the object of the type + // that we specify for loading. + ASSERT_EQ(Zero, StMgr.getBinding(StZ0, LZ0, ACtx.IntTy)); + ASSERT_EQ(NarrowZero, *StMgr.getDefaultBinding(StZ0, LZ0.getAsRegion())); + + // Bind(One) + Store StX1 = + StMgr.Bind(StInit, LX1, One).getStore(); + ASSERT_EQ(One, StMgr.getBinding(StX1, LX1, ACtx.IntTy)); + + // BindDefaultInitial(One) + Store StY1 = + StMgr.BindDefaultInitial(StInit, LY1.getAsRegion(), One).getStore(); + ASSERT_EQ(One, StMgr.getBinding(StY1, LY1, ACtx.IntTy)); + ASSERT_EQ(One, *StMgr.getDefaultBinding(StY1, LY1.getAsRegion())); + } + +public: + VariableBindConsumer(CompilerInstance &C) : ExprEngineConsumer(C) {} + + bool HandleTopLevelDecl(DeclGroupRef DG) override { + for (const auto *D : DG) + performTest(D); + return true; + } +}; + +class VariableBindAction : public ASTFrontendAction { +public: + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, + StringRef File) override { + return llvm::make_unique<VariableBindConsumer>(Compiler); + } +}; + +TEST(Store, VariableBind) { + EXPECT_TRUE(tooling::runToolOnCode( + new VariableBindAction, "void foo() { int x0, y0, z0, x1, y1; }")); +} + +} // namespace +} // namespace ento +} // namespace clang diff --git a/unittests/StaticAnalyzer/SymbolReaperTest.cpp b/unittests/StaticAnalyzer/SymbolReaperTest.cpp new file mode 100644 index 0000000000..5d9af3196d --- /dev/null +++ b/unittests/StaticAnalyzer/SymbolReaperTest.cpp @@ -0,0 +1,70 @@ +//===- unittests/StaticAnalyzer/SymbolReaperTest.cpp ----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Reusables.h" + +#include "clang/Tooling/Tooling.h" +#include "gtest/gtest.h" + +namespace clang { +namespace ento { +namespace { + +class SuperRegionLivenessConsumer : public ExprEngineConsumer { + void performTest(const Decl *D) { + const auto *FD = findDeclByName<FieldDecl>(D, "x"); + const auto *VD = findDeclByName<VarDecl>(D, "s"); + assert(FD && VD); + + // The variable must belong to a stack frame, + // otherwise SymbolReaper would think it's a global. + const StackFrameContext *SFC = + Eng.getAnalysisDeclContextManager().getStackFrame(D); + + // Create regions for 's' and 's.x'. + const VarRegion *VR = Eng.getRegionManager().getVarRegion(VD, SFC); + const FieldRegion *FR = Eng.getRegionManager().getFieldRegion(FD, VR); + + // Pass a null location context to the SymbolReaper so that + // it was thinking that the variable is dead. + SymbolReaper SymReaper((StackFrameContext *)nullptr, (Stmt *)nullptr, + Eng.getSymbolManager(), Eng.getStoreManager()); + + SymReaper.markLive(FR); + EXPECT_TRUE(SymReaper.isLiveRegion(VR)); + } + +public: + SuperRegionLivenessConsumer(CompilerInstance &C) : ExprEngineConsumer(C) {} + ~SuperRegionLivenessConsumer() override {} + + bool HandleTopLevelDecl(DeclGroupRef DG) override { + for (const auto *D : DG) + performTest(D); + return true; + } +}; + +class SuperRegionLivenessAction : public ASTFrontendAction { +public: + SuperRegionLivenessAction() {} + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, + StringRef File) override { + return llvm::make_unique<SuperRegionLivenessConsumer>(Compiler); + } +}; + +// Test that marking s.x as live would also make s live. +TEST(SymbolReaper, SuperRegionLiveness) { + EXPECT_TRUE(tooling::runToolOnCode(new SuperRegionLivenessAction, + "void foo() { struct S { int x; } s; }")); +} + +} // namespace +} // namespace ento +} // namespace clang diff --git a/unittests/Tooling/ASTSelectionTest.cpp b/unittests/Tooling/ASTSelectionTest.cpp index 2f5df8f430..7ad5148213 100644 --- a/unittests/Tooling/ASTSelectionTest.cpp +++ b/unittests/Tooling/ASTSelectionTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/ASTSelectionTest.cpp ------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/CMakeLists.txt b/unittests/Tooling/CMakeLists.txt index 7619c7fb23..994b88b95b 100644 --- a/unittests/Tooling/CMakeLists.txt +++ b/unittests/Tooling/CMakeLists.txt @@ -49,7 +49,10 @@ add_clang_unittest(ToolingTests RefactoringTest.cpp ReplacementsYamlTest.cpp RewriterTest.cpp + SourceCodeTest.cpp + StencilTest.cpp ToolingTest.cpp + TransformerTest.cpp ) target_link_libraries(ToolingTests diff --git a/unittests/Tooling/CastExprTest.cpp b/unittests/Tooling/CastExprTest.cpp index 5310c21254..a9e78d2155 100644 --- a/unittests/Tooling/CastExprTest.cpp +++ b/unittests/Tooling/CastExprTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/CastExprTest.cpp ----------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/CommentHandlerTest.cpp b/unittests/Tooling/CommentHandlerTest.cpp index 9c3abdc4b1..5ceed95b98 100644 --- a/unittests/Tooling/CommentHandlerTest.cpp +++ b/unittests/Tooling/CommentHandlerTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/CommentHandlerTest.cpp -----------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/CompilationDatabaseTest.cpp b/unittests/Tooling/CompilationDatabaseTest.cpp index 949d6a3b73..4e27df71d8 100644 --- a/unittests/Tooling/CompilationDatabaseTest.cpp +++ b/unittests/Tooling/CompilationDatabaseTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/CompilationDatabaseTest.cpp -----------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -89,12 +88,17 @@ TEST(JSONCompilationDatabase, GetAllFiles) { expected_files.push_back(PathStorage.str()); llvm::sys::path::native("//net/dir/file2", PathStorage); expected_files.push_back(PathStorage.str()); + llvm::sys::path::native("//net/file1", PathStorage); + expected_files.push_back(PathStorage.str()); EXPECT_EQ(expected_files, getAllFiles("[{\"directory\":\"//net/dir\"," "\"command\":\"command\"," "\"file\":\"file1\"}," " {\"directory\":\"//net/dir\"," "\"command\":\"command\"," + "\"file\":\"../file1\"}," + " {\"directory\":\"//net/dir\"," + "\"command\":\"command\"," "\"file\":\"file2\"}]", ErrorMessage, JSONCommandLineSyntax::Gnu)) << ErrorMessage; @@ -669,6 +673,27 @@ protected: return llvm::join(Results[0].CommandLine, " "); } + // Parse the file whose command was used out of the Heuristic string. + std::string getProxy(llvm::StringRef F) { + auto Results = + inferMissingCompileCommands(llvm::make_unique<MemCDB>(Entries)) + ->getCompileCommands(path(F)); + if (Results.empty()) + return "none"; + StringRef Proxy = Results.front().Heuristic; + if (!Proxy.consume_front("inferred from ")) + return ""; + // We have a proxy file, convert back to a unix relative path. + // This is a bit messy, but we do need to test these strings somehow... + llvm::SmallString<32> TempDir; + llvm::sys::path::system_temp_directory(false, TempDir); + Proxy.consume_front(TempDir); + Proxy.consume_front(llvm::sys::path::get_separator()); + llvm::SmallString<32> Result = Proxy; + llvm::sys::path::native(Result, llvm::sys::path::Style::posix); + return Result.str(); + } + MemCDB::EntryMap Entries; }; @@ -678,18 +703,16 @@ TEST_F(InterpolateTest, Nearby) { add("an/other/foo.cpp"); // great: dir and name both match (prefix or full, case insensitive) - EXPECT_EQ(getCommand("dir/f.cpp"), "clang -D dir/foo.cpp"); - EXPECT_EQ(getCommand("dir/FOO.cpp"), "clang -D dir/foo.cpp"); + EXPECT_EQ(getProxy("dir/f.cpp"), "dir/foo.cpp"); + EXPECT_EQ(getProxy("dir/FOO.cpp"), "dir/foo.cpp"); // no name match. prefer matching dir, break ties by alpha - EXPECT_EQ(getCommand("dir/a.cpp"), "clang -D dir/bar.cpp"); + EXPECT_EQ(getProxy("dir/a.cpp"), "dir/bar.cpp"); // an exact name match beats one segment of directory match - EXPECT_EQ(getCommand("some/other/bar.h"), - "clang -D dir/bar.cpp -x c++-header"); + EXPECT_EQ(getProxy("some/other/bar.h"), "dir/bar.cpp"); // two segments of directory match beat a prefix name match - EXPECT_EQ(getCommand("an/other/b.cpp"), "clang -D an/other/foo.cpp"); + EXPECT_EQ(getProxy("an/other/b.cpp"), "an/other/foo.cpp"); // if nothing matches at all, we still get the closest alpha match - EXPECT_EQ(getCommand("below/some/obscure/path.cpp"), - "clang -D an/other/foo.cpp"); + EXPECT_EQ(getProxy("below/some/obscure/path.cpp"), "an/other/foo.cpp"); } TEST_F(InterpolateTest, Language) { @@ -723,7 +746,7 @@ TEST_F(InterpolateTest, Case) { add("FOO/BAR/BAZ/SHOUT.cc"); add("foo/bar/baz/quiet.cc"); // Case mismatches are completely ignored, so we choose the name match. - EXPECT_EQ(getCommand("foo/bar/baz/shout.C"), "clang -D FOO/BAR/BAZ/SHOUT.cc"); + EXPECT_EQ(getProxy("foo/bar/baz/shout.C"), "FOO/BAR/BAZ/SHOUT.cc"); } TEST_F(InterpolateTest, Aliasing) { diff --git a/unittests/Tooling/DiagnosticsYamlTest.cpp b/unittests/Tooling/DiagnosticsYamlTest.cpp index f4de53fad2..aaba258911 100644 --- a/unittests/Tooling/DiagnosticsYamlTest.cpp +++ b/unittests/Tooling/DiagnosticsYamlTest.cpp @@ -1,9 +1,8 @@ //===- unittests/Tooling/DiagnosticsYamlTest.cpp - Serialization tests ---===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -21,11 +20,13 @@ using namespace clang::tooling; using clang::tooling::Diagnostic; static DiagnosticMessage makeMessage(const std::string &Message, int FileOffset, - const std::string &FilePath) { + const std::string &FilePath, + const StringMap<Replacements> &Fix) { DiagnosticMessage DiagMessage; DiagMessage.Message = Message; DiagMessage.FileOffset = FileOffset; DiagMessage.FilePath = FilePath; + DiagMessage.Fix = Fix; return DiagMessage; } @@ -33,10 +34,52 @@ static Diagnostic makeDiagnostic(StringRef DiagnosticName, const std::string &Message, int FileOffset, const std::string &FilePath, const StringMap<Replacements> &Fix) { - return Diagnostic(DiagnosticName, makeMessage(Message, FileOffset, FilePath), - Fix, {}, Diagnostic::Warning, "path/to/build/directory"); + return Diagnostic(DiagnosticName, + makeMessage(Message, FileOffset, FilePath, Fix), {}, + Diagnostic::Warning, "path/to/build/directory"); } +static const char *YAMLContent = + "---\n" + "MainSourceFile: 'path/to/source.cpp'\n" + "Diagnostics: \n" + " - DiagnosticName: 'diagnostic#1\'\n" + " DiagnosticMessage: \n" + " Message: 'message #1'\n" + " FilePath: 'path/to/source.cpp'\n" + " FileOffset: 55\n" + " Replacements: \n" + " - FilePath: 'path/to/source.cpp'\n" + " Offset: 100\n" + " Length: 12\n" + " ReplacementText: 'replacement #1'\n" + " - DiagnosticName: 'diagnostic#2'\n" + " DiagnosticMessage: \n" + " Message: 'message #2'\n" + " FilePath: 'path/to/header.h'\n" + " FileOffset: 60\n" + " Replacements: \n" + " - FilePath: 'path/to/header.h'\n" + " Offset: 62\n" + " Length: 2\n" + " ReplacementText: 'replacement #2'\n" + " - DiagnosticName: 'diagnostic#3'\n" + " DiagnosticMessage: \n" + " Message: 'message #3'\n" + " FilePath: 'path/to/source2.cpp'\n" + " FileOffset: 72\n" + " Replacements: []\n" + " Notes: \n" + " - Message: Note1\n" + " FilePath: 'path/to/note1.cpp'\n" + " FileOffset: 88\n" + " Replacements: []\n" + " - Message: Note2\n" + " FilePath: 'path/to/note2.cpp'\n" + " FileOffset: 99\n" + " Replacements: []\n" + "...\n"; + TEST(DiagnosticsYamlTest, serializesDiagnostics) { TranslationUnitDiagnostics TUD; TUD.MainSourceFile = "path/to/source.cpp"; @@ -56,9 +99,9 @@ TEST(DiagnosticsYamlTest, serializesDiagnostics) { TUD.Diagnostics.push_back(makeDiagnostic("diagnostic#3", "message #3", 72, "path/to/source2.cpp", {})); TUD.Diagnostics.back().Notes.push_back( - makeMessage("Note1", 88, "path/to/note1.cpp")); + makeMessage("Note1", 88, "path/to/note1.cpp", {})); TUD.Diagnostics.back().Notes.push_back( - makeMessage("Note2", 99, "path/to/note2.cpp")); + makeMessage("Note2", 99, "path/to/note2.cpp", {})); std::string YamlContent; raw_string_ostream YamlContentStream(YamlContent); @@ -66,80 +109,12 @@ TEST(DiagnosticsYamlTest, serializesDiagnostics) { yaml::Output YAML(YamlContentStream); YAML << TUD; - EXPECT_EQ("---\n" - "MainSourceFile: 'path/to/source.cpp'\n" - "Diagnostics: \n" - " - DiagnosticName: 'diagnostic#1\'\n" - " Message: 'message #1'\n" - " FileOffset: 55\n" - " FilePath: 'path/to/source.cpp'\n" - " Replacements: \n" - " - FilePath: 'path/to/source.cpp'\n" - " Offset: 100\n" - " Length: 12\n" - " ReplacementText: 'replacement #1'\n" - " - DiagnosticName: 'diagnostic#2'\n" - " Message: 'message #2'\n" - " FileOffset: 60\n" - " FilePath: 'path/to/header.h'\n" - " Replacements: \n" - " - FilePath: 'path/to/header.h'\n" - " Offset: 62\n" - " Length: 2\n" - " ReplacementText: 'replacement #2'\n" - " - DiagnosticName: 'diagnostic#3'\n" - " Message: 'message #3'\n" - " FileOffset: 72\n" - " FilePath: 'path/to/source2.cpp'\n" - " Notes: \n" - " - Message: Note1\n" - " FilePath: 'path/to/note1.cpp'\n" - " FileOffset: 88\n" - " - Message: Note2\n" - " FilePath: 'path/to/note2.cpp'\n" - " FileOffset: 99\n" - " Replacements: []\n" - "...\n", - YamlContentStream.str()); + EXPECT_EQ(YAMLContent, YamlContentStream.str()); } TEST(DiagnosticsYamlTest, deserializesDiagnostics) { - std::string YamlContent = "---\n" - "MainSourceFile: path/to/source.cpp\n" - "Diagnostics: \n" - " - DiagnosticName: 'diagnostic#1'\n" - " Message: 'message #1'\n" - " FileOffset: 55\n" - " FilePath: path/to/source.cpp\n" - " Replacements: \n" - " - FilePath: path/to/source.cpp\n" - " Offset: 100\n" - " Length: 12\n" - " ReplacementText: 'replacement #1'\n" - " - DiagnosticName: 'diagnostic#2'\n" - " Message: 'message #2'\n" - " FileOffset: 60\n" - " FilePath: path/to/header.h\n" - " Replacements: \n" - " - FilePath: path/to/header.h\n" - " Offset: 62\n" - " Length: 2\n" - " ReplacementText: 'replacement #2'\n" - " - DiagnosticName: 'diagnostic#3'\n" - " Message: 'message #3'\n" - " FileOffset: 98\n" - " FilePath: path/to/source.cpp\n" - " Notes:\n" - " - Message: Note1\n" - " FilePath: 'path/to/note1.cpp'\n" - " FileOffset: 66\n" - " - Message: Note2\n" - " FilePath: 'path/to/note2.cpp'\n" - " FileOffset: 77\n" - " Replacements: []\n" - "...\n"; TranslationUnitDiagnostics TUDActual; - yaml::Input YAML(YamlContent); + yaml::Input YAML(YAMLContent); YAML >> TUDActual; ASSERT_FALSE(YAML.error()); @@ -161,7 +136,7 @@ TEST(DiagnosticsYamlTest, deserializesDiagnostics) { EXPECT_EQ("message #1", D1.Message.Message); EXPECT_EQ(55u, D1.Message.FileOffset); EXPECT_EQ("path/to/source.cpp", D1.Message.FilePath); - std::vector<Replacement> Fixes1 = getFixes(D1.Fix); + std::vector<Replacement> Fixes1 = getFixes(D1.Message.Fix); ASSERT_EQ(1u, Fixes1.size()); EXPECT_EQ("path/to/source.cpp", Fixes1[0].getFilePath()); EXPECT_EQ(100u, Fixes1[0].getOffset()); @@ -173,7 +148,7 @@ TEST(DiagnosticsYamlTest, deserializesDiagnostics) { EXPECT_EQ("message #2", D2.Message.Message); EXPECT_EQ(60u, D2.Message.FileOffset); EXPECT_EQ("path/to/header.h", D2.Message.FilePath); - std::vector<Replacement> Fixes2 = getFixes(D2.Fix); + std::vector<Replacement> Fixes2 = getFixes(D2.Message.Fix); ASSERT_EQ(1u, Fixes2.size()); EXPECT_EQ("path/to/header.h", Fixes2[0].getFilePath()); EXPECT_EQ(62u, Fixes2[0].getOffset()); @@ -183,15 +158,15 @@ TEST(DiagnosticsYamlTest, deserializesDiagnostics) { Diagnostic D3 = TUDActual.Diagnostics[2]; EXPECT_EQ("diagnostic#3", D3.DiagnosticName); EXPECT_EQ("message #3", D3.Message.Message); - EXPECT_EQ(98u, D3.Message.FileOffset); - EXPECT_EQ("path/to/source.cpp", D3.Message.FilePath); + EXPECT_EQ(72u, D3.Message.FileOffset); + EXPECT_EQ("path/to/source2.cpp", D3.Message.FilePath); EXPECT_EQ(2u, D3.Notes.size()); EXPECT_EQ("Note1", D3.Notes[0].Message); - EXPECT_EQ(66u, D3.Notes[0].FileOffset); + EXPECT_EQ(88u, D3.Notes[0].FileOffset); EXPECT_EQ("path/to/note1.cpp", D3.Notes[0].FilePath); EXPECT_EQ("Note2", D3.Notes[1].Message); - EXPECT_EQ(77u, D3.Notes[1].FileOffset); + EXPECT_EQ(99u, D3.Notes[1].FileOffset); EXPECT_EQ("path/to/note2.cpp", D3.Notes[1].FilePath); - std::vector<Replacement> Fixes3 = getFixes(D3.Fix); + std::vector<Replacement> Fixes3 = getFixes(D3.Message.Fix); EXPECT_TRUE(Fixes3.empty()); } diff --git a/unittests/Tooling/ExecutionTest.cpp b/unittests/Tooling/ExecutionTest.cpp index 785ec7c2bc..31d5fe5c42 100644 --- a/unittests/Tooling/ExecutionTest.cpp +++ b/unittests/Tooling/ExecutionTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/ExecutionTest.cpp - Tool execution tests. --------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/FixItTest.cpp b/unittests/Tooling/FixItTest.cpp index 365180e67f..ec9801d345 100644 --- a/unittests/Tooling/FixItTest.cpp +++ b/unittests/Tooling/FixItTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/FixitTest.cpp ------------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/HeaderIncludesTest.cpp b/unittests/Tooling/HeaderIncludesTest.cpp index ff68f75a6e..635d7ebb1e 100644 --- a/unittests/Tooling/HeaderIncludesTest.cpp +++ b/unittests/Tooling/HeaderIncludesTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/CleanupTest.cpp - Include insertion/deletion tests ===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -15,9 +14,6 @@ #include "gtest/gtest.h" -using clang::tooling::ReplacementTest; -using clang::tooling::toReplacements; - namespace clang { namespace tooling { namespace { @@ -316,6 +312,17 @@ TEST_F(HeaderIncludesTest, RealHeaderGuardAfterComments) { EXPECT_EQ(Expected, insert(Code, "<vector>")); } +TEST_F(HeaderIncludesTest, PragmaOnce) { + std::string Code = "// comment \n" + "#pragma once\n" + "int x;\n"; + std::string Expected = "// comment \n" + "#pragma once\n" + "#include <vector>\n" + "int x;\n"; + EXPECT_EQ(Expected, insert(Code, "<vector>")); +} + TEST_F(HeaderIncludesTest, IfNDefWithNoDefine) { std::string Code = "// comment \n" "#ifndef X\n" diff --git a/unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp b/unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp index 7387e9c44d..38079f706f 100644 --- a/unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp +++ b/unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp -------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/LookupTest.cpp b/unittests/Tooling/LookupTest.cpp index a08b2b418f..372cbbf62b 100644 --- a/unittests/Tooling/LookupTest.cpp +++ b/unittests/Tooling/LookupTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/LookupTest.cpp ------------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -45,8 +44,8 @@ TEST(LookupTest, replaceNestedFunctionName) { const auto *Callee = cast<DeclRefExpr>(Expr->getCallee()->IgnoreImplicit()); const ValueDecl *FD = Callee->getDecl(); return tooling::replaceNestedName( - Callee->getQualifier(), Visitor.DeclStack.back()->getDeclContext(), FD, - ReplacementString); + Callee->getQualifier(), Callee->getLocation(), + Visitor.DeclStack.back()->getDeclContext(), FD, ReplacementString); }; Visitor.OnCall = [&](CallExpr *Expr) { @@ -130,20 +129,38 @@ TEST(LookupTest, replaceNestedFunctionName) { // If the shortest name is ambiguous, we need to add more qualifiers. Visitor.OnCall = [&](CallExpr *Expr) { - EXPECT_EQ("::a::y::bar", replaceCallExpr(Expr, "::a::y::bar")); + EXPECT_EQ("a::y::bar", replaceCallExpr(Expr, "::a::y::bar")); }; Visitor.runOver(R"( namespace a { - namespace b { - namespace x { void foo() {} } - namespace y { void foo() {} } - } + namespace b { + namespace x { void foo() {} } + namespace y { void foo() {} } + } } namespace a { - namespace b { - void f() { x::foo(); } + namespace b { + void f() { x::foo(); } + } + })"); + + Visitor.OnCall = [&](CallExpr *Expr) { + // y::bar would be ambiguous due to "a::b::y". + EXPECT_EQ("::y::bar", replaceCallExpr(Expr, "::y::bar")); + }; + Visitor.runOver(R"( + namespace a { + namespace b { + void foo() {} + namespace y { } + } } + + namespace a { + namespace b { + void f() { foo(); } + } })"); Visitor.OnCall = [&](CallExpr *Expr) { @@ -164,12 +181,12 @@ TEST(LookupTest, replaceNestedFunctionName) { TEST(LookupTest, replaceNestedClassName) { GetDeclsVisitor Visitor; - auto replaceRecordTypeLoc = [&](RecordTypeLoc Loc, + auto replaceRecordTypeLoc = [&](RecordTypeLoc TLoc, StringRef ReplacementString) { - const auto *FD = cast<CXXRecordDecl>(Loc.getDecl()); + const auto *FD = cast<CXXRecordDecl>(TLoc.getDecl()); return tooling::replaceNestedName( - nullptr, Visitor.DeclStack.back()->getDeclContext(), FD, - ReplacementString); + nullptr, TLoc.getBeginLoc(), Visitor.DeclStack.back()->getDeclContext(), + FD, ReplacementString); }; Visitor.OnRecordTypeLoc = [&](RecordTypeLoc Type) { @@ -194,6 +211,41 @@ TEST(LookupTest, replaceNestedClassName) { }; Visitor.runOver("namespace a { namespace b { class Foo {}; } }\n" "namespace c { using a::b::Foo; Foo f();; }\n"); + + // Rename TypeLoc `x::y::Old` to new name `x::Foo` at [0] and check that the + // type is replaced with "Foo" instead of "x::Foo". Although there is a symbol + // `x::y::Foo` in c.cc [1], it should not make "Foo" at [0] ambiguous because + // it's not visible at [0]. + Visitor.OnRecordTypeLoc = [&](RecordTypeLoc Type) { + if (Type.getDecl()->getQualifiedNameAsString() == "x::y::Old") { + EXPECT_EQ("Foo", replaceRecordTypeLoc(Type, "::x::Foo")); + } + }; + Visitor.runOver(R"( + // a.h + namespace x { + namespace y { + class Old {}; + class Other {}; + } + } + + // b.h + namespace x { + namespace y { + // This is to be renamed to x::Foo + // The expected replacement is "Foo". + Old f; // [0]. + } + } + + // c.cc + namespace x { + namespace y { + using Foo = ::x::y::Other; // [1] + } + } + )"); } } // end anonymous namespace diff --git a/unittests/Tooling/QualTypeNamesTest.cpp b/unittests/Tooling/QualTypeNamesTest.cpp index b4c56f7bd5..b6c3029778 100644 --- a/unittests/Tooling/QualTypeNamesTest.cpp +++ b/unittests/Tooling/QualTypeNamesTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/QualTypeNameTest.cpp ------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -195,6 +194,7 @@ TEST(QualTypeNameTest, getFullyQualifiedName) { GlobalNsPrefix.ExpectedQualTypeNames["ZVal"] = "::A::B::Y::Z"; GlobalNsPrefix.ExpectedQualTypeNames["GlobalZVal"] = "::Z"; GlobalNsPrefix.ExpectedQualTypeNames["CheckK"] = "D::aStruct"; + GlobalNsPrefix.ExpectedQualTypeNames["YZMPtr"] = "::A::B::X ::A::B::Y::Z::*"; GlobalNsPrefix.runOver( "namespace A {\n" " namespace B {\n" @@ -206,8 +206,9 @@ TEST(QualTypeNameTest, getFullyQualifiedName) { " template <typename T>\n" " using Alias = CCC<T>;\n" " Alias<int> IntAliasVal;\n" - " struct Y { struct Z {}; };\n" + " struct Y { struct Z { X YZIPtr; }; };\n" " Y::Z ZVal;\n" + " X Y::Z::*YZMPtr;\n" " }\n" "}\n" "struct Z {};\n" diff --git a/unittests/Tooling/RecursiveASTVisitorTestDeclVisitor.cpp b/unittests/Tooling/RecursiveASTVisitorTestDeclVisitor.cpp index e91873c406..e207f03971 100644 --- a/unittests/Tooling/RecursiveASTVisitorTestDeclVisitor.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTestDeclVisitor.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTestDeclVisitor.cpp ------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTestPostOrderVisitor.cpp b/unittests/Tooling/RecursiveASTVisitorTestPostOrderVisitor.cpp index 2e7b398c3d..965bb3d7b7 100644 --- a/unittests/Tooling/RecursiveASTVisitorTestPostOrderVisitor.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTestPostOrderVisitor.cpp @@ -1,9 +1,8 @@ //===- unittests/Tooling/RecursiveASTVisitorPostOrderASTVisitor.cpp -------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/unittests/Tooling/RecursiveASTVisitorTestTypeLocVisitor.cpp b/unittests/Tooling/RecursiveASTVisitorTestTypeLocVisitor.cpp index dc2adaf4da..299e1b022a 100644 --- a/unittests/Tooling/RecursiveASTVisitorTestTypeLocVisitor.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTestTypeLocVisitor.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTestTypeLocVisitor.cpp ---------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/Attr.cpp b/unittests/Tooling/RecursiveASTVisitorTests/Attr.cpp index 33163c30e5..022ef8b832 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/Attr.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/Attr.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/Attr.cpp -----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/CXXBoolLiteralExpr.cpp b/unittests/Tooling/RecursiveASTVisitorTests/CXXBoolLiteralExpr.cpp index ca2e4a4691..1fb192dcda 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/CXXBoolLiteralExpr.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/CXXBoolLiteralExpr.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/CXXBoolLiteralExpr.cpp ---===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/CXXMemberCall.cpp b/unittests/Tooling/RecursiveASTVisitorTests/CXXMemberCall.cpp index a83e55137a..c7b31e06e0 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/CXXMemberCall.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/CXXMemberCall.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/CXXMemberCall.cpp --------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/CXXOperatorCallExprTraverser.cpp b/unittests/Tooling/RecursiveASTVisitorTests/CXXOperatorCallExprTraverser.cpp index 414b0f0174..91de8d17c9 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/CXXOperatorCallExprTraverser.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/CXXOperatorCallExprTraverser.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/CXXOperatorCallExprTraverser.cpp -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/Class.cpp b/unittests/Tooling/RecursiveASTVisitorTests/Class.cpp index 666c924d1a..3ea5abd46a 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/Class.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/Class.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/Class.cpp ----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/ConstructExpr.cpp b/unittests/Tooling/RecursiveASTVisitorTests/ConstructExpr.cpp index f775a31e59..b4f4f54dc7 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/ConstructExpr.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/ConstructExpr.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/ConstructExpr.cpp --------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/DeclRefExpr.cpp b/unittests/Tooling/RecursiveASTVisitorTests/DeclRefExpr.cpp index cd0e4260a8..adc972e1c3 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/DeclRefExpr.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/DeclRefExpr.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/DeclRefExpr.cpp ----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/ImplicitCtor.cpp b/unittests/Tooling/RecursiveASTVisitorTests/ImplicitCtor.cpp index c2194ab292..27999e5ef8 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/ImplicitCtor.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/ImplicitCtor.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/ImplicitCtor.cpp ---------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPostOrder.cpp b/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPostOrder.cpp index 396f25de5c..80d9c98735 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPostOrder.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPostOrder.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/InitListExprPostOrder.cpp -==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPostOrderNoQueue.cpp b/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPostOrderNoQueue.cpp index 587f84bb43..a15f4c83c5 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPostOrderNoQueue.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPostOrderNoQueue.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/InitListExprPostOrderNoQueue.cpp -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPreOrder.cpp b/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPreOrder.cpp index 01f6e19029..401ae6b15d 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPreOrder.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPreOrder.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/InitListExprPreOrder.cpp -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPreOrderNoQueue.cpp b/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPreOrderNoQueue.cpp index d48b5a89c8..1dafeef7cd 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPreOrderNoQueue.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPreOrderNoQueue.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/InitListExprPreOrderNoQueue.cpp -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/IntegerLiteral.cpp b/unittests/Tooling/RecursiveASTVisitorTests/IntegerLiteral.cpp index 218f7e0c2d..3fc3cb1a99 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/IntegerLiteral.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/IntegerLiteral.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/IntegerLiteral.cpp -------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/LambdaDefaultCapture.cpp b/unittests/Tooling/RecursiveASTVisitorTests/LambdaDefaultCapture.cpp index c3f8e4f419..b1d6d593e7 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/LambdaDefaultCapture.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/LambdaDefaultCapture.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/LambdaDefaultCapture.cpp -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/LambdaExpr.cpp b/unittests/Tooling/RecursiveASTVisitorTests/LambdaExpr.cpp index d3a3eba15d..560cdf95c0 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/LambdaExpr.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/LambdaExpr.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/LambdaExpr.cpp -----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/LambdaTemplateParams.cpp b/unittests/Tooling/RecursiveASTVisitorTests/LambdaTemplateParams.cpp new file mode 100644 index 0000000000..d0e4fb733e --- /dev/null +++ b/unittests/Tooling/RecursiveASTVisitorTests/LambdaTemplateParams.cpp @@ -0,0 +1,53 @@ +//===- unittest/Tooling/RecursiveASTVisitorTests/LambdaTemplateParams.cpp -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "TestVisitor.h" + +using namespace clang; + +namespace { + +// Matches (optional) explicit template parameters. +class LambdaTemplateParametersVisitor + : public ExpectedLocationVisitor<LambdaTemplateParametersVisitor> { +public: + bool shouldVisitImplicitCode() const { return false; } + + bool VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) { + EXPECT_FALSE(D->isImplicit()); + Match(D->getName(), D->getLocStart()); + return true; + } + + bool VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) { + EXPECT_FALSE(D->isImplicit()); + Match(D->getName(), D->getLocStart()); + return true; + } + + bool VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) { + EXPECT_FALSE(D->isImplicit()); + Match(D->getName(), D->getLocStart()); + return true; + } +}; + +TEST(RecursiveASTVisitor, VisitsLambdaExplicitTemplateParameters) { + LambdaTemplateParametersVisitor Visitor; + Visitor.ExpectMatch("T", 2, 15); + Visitor.ExpectMatch("I", 2, 24); + Visitor.ExpectMatch("TT", 2, 31); + EXPECT_TRUE(Visitor.runOver( + "void f() { \n" + " auto l = []<class T, int I, template<class> class TT>(auto p) { }; \n" + "}", + LambdaTemplateParametersVisitor::Lang_CXX2a)); +} + +} // end anonymous namespace diff --git a/unittests/Tooling/RecursiveASTVisitorTests/NestedNameSpecifiers.cpp b/unittests/Tooling/RecursiveASTVisitorTests/NestedNameSpecifiers.cpp index 23afda6e87..868a3988c7 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/NestedNameSpecifiers.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/NestedNameSpecifiers.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/NestedNameSpecifiers.cpp -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/ParenExpr.cpp b/unittests/Tooling/RecursiveASTVisitorTests/ParenExpr.cpp index b6a5a18e01..c316f98f40 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/ParenExpr.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/ParenExpr.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/ParenExpr.cpp ------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/TemplateArgumentLocTraverser.cpp b/unittests/Tooling/RecursiveASTVisitorTests/TemplateArgumentLocTraverser.cpp index 0c9cbf2eb4..ae427a02bc 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/TemplateArgumentLocTraverser.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/TemplateArgumentLocTraverser.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/TemplateArgumentLocTraverser.cpp -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/TraversalScope.cpp b/unittests/Tooling/RecursiveASTVisitorTests/TraversalScope.cpp index 72f6c644b3..c05be7f2e3 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/TraversalScope.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/TraversalScope.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/TraversalScope.cpp -------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RefactoringActionRulesTest.cpp b/unittests/Tooling/RefactoringActionRulesTest.cpp index acacfa05b4..7daef33337 100644 --- a/unittests/Tooling/RefactoringActionRulesTest.cpp +++ b/unittests/Tooling/RefactoringActionRulesTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RefactoringTestActionRulesTest.cpp ----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RefactoringCallbacksTest.cpp b/unittests/Tooling/RefactoringCallbacksTest.cpp index e226522a70..1663581d65 100644 --- a/unittests/Tooling/RefactoringCallbacksTest.cpp +++ b/unittests/Tooling/RefactoringCallbacksTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RefactoringCallbacksTest.cpp ----------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RefactoringTest.cpp b/unittests/Tooling/RefactoringTest.cpp index d618c0fb07..ed111b7878 100644 --- a/unittests/Tooling/RefactoringTest.cpp +++ b/unittests/Tooling/RefactoringTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RefactoringTest.cpp - Refactoring unit tests ------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/ReplacementTest.h b/unittests/Tooling/ReplacementTest.h index b6fe5c79b7..b97e0e7f2f 100644 --- a/unittests/Tooling/ReplacementTest.h +++ b/unittests/Tooling/ReplacementTest.h @@ -1,9 +1,8 @@ //===- unittest/Tooling/ReplacementTest.h - Replacements related test------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/unittests/Tooling/ReplacementsYamlTest.cpp b/unittests/Tooling/ReplacementsYamlTest.cpp index 2e5a87a931..ab9f6c9b5d 100644 --- a/unittests/Tooling/ReplacementsYamlTest.cpp +++ b/unittests/Tooling/ReplacementsYamlTest.cpp @@ -1,9 +1,8 @@ //===- unittests/Tooling/ReplacementsYamlTest.cpp - Serialization tests ---===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/unittests/Tooling/RewriterTest.cpp b/unittests/Tooling/RewriterTest.cpp index 4305d421e1..e744940783 100644 --- a/unittests/Tooling/RewriterTest.cpp +++ b/unittests/Tooling/RewriterTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RewriterTest.cpp ----------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RewriterTestContext.h b/unittests/Tooling/RewriterTestContext.h index 9e66484158..ba919d6472 100644 --- a/unittests/Tooling/RewriterTestContext.h +++ b/unittests/Tooling/RewriterTestContext.h @@ -1,9 +1,8 @@ //===--- RewriterTestContext.h ----------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/unittests/Tooling/SourceCodeTest.cpp b/unittests/Tooling/SourceCodeTest.cpp new file mode 100644 index 0000000000..258947a1a7 --- /dev/null +++ b/unittests/Tooling/SourceCodeTest.cpp @@ -0,0 +1,97 @@ +//===- unittest/Tooling/SourceCodeTest.cpp --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "TestVisitor.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Tooling/Refactoring/SourceCode.h" + +using namespace clang; + +using tooling::getText; +using tooling::getExtendedText; + +namespace { + +struct CallsVisitor : TestVisitor<CallsVisitor> { + bool VisitCallExpr(CallExpr *Expr) { + OnCall(Expr, Context); + return true; + } + + std::function<void(CallExpr *, ASTContext *Context)> OnCall; +}; + +TEST(SourceCodeTest, getText) { + CallsVisitor Visitor; + + Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { + EXPECT_EQ("foo(x, y)", getText(*CE, *Context)); + }; + Visitor.runOver("void foo(int x, int y) { foo(x, y); }"); + + Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { + EXPECT_EQ("APPLY(foo, x, y)", getText(*CE, *Context)); + }; + Visitor.runOver("#define APPLY(f, x, y) f(x, y)\n" + "void foo(int x, int y) { APPLY(foo, x, y); }"); +} + +TEST(SourceCodeTest, getTextWithMacro) { + CallsVisitor Visitor; + + Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { + EXPECT_EQ("F OO", getText(*CE, *Context)); + Expr *P0 = CE->getArg(0); + Expr *P1 = CE->getArg(1); + EXPECT_EQ("", getText(*P0, *Context)); + EXPECT_EQ("", getText(*P1, *Context)); + }; + Visitor.runOver("#define F foo(\n" + "#define OO x, y)\n" + "void foo(int x, int y) { F OO ; }"); + + Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { + EXPECT_EQ("", getText(*CE, *Context)); + Expr *P0 = CE->getArg(0); + Expr *P1 = CE->getArg(1); + EXPECT_EQ("x", getText(*P0, *Context)); + EXPECT_EQ("y", getText(*P1, *Context)); + }; + Visitor.runOver("#define FOO(x, y) (void)x; (void)y; foo(x, y);\n" + "void foo(int x, int y) { FOO(x,y) }"); +} + +TEST(SourceCodeTest, getExtendedText) { + CallsVisitor Visitor; + + Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { + EXPECT_EQ("foo(x, y);", + getExtendedText(*CE, tok::TokenKind::semi, *Context)); + + Expr *P0 = CE->getArg(0); + Expr *P1 = CE->getArg(1); + EXPECT_EQ("x", getExtendedText(*P0, tok::TokenKind::semi, *Context)); + EXPECT_EQ("x,", getExtendedText(*P0, tok::TokenKind::comma, *Context)); + EXPECT_EQ("y", getExtendedText(*P1, tok::TokenKind::semi, *Context)); + }; + Visitor.runOver("void foo(int x, int y) { foo(x, y); }"); + Visitor.runOver("void foo(int x, int y) { if (true) foo(x, y); }"); + Visitor.runOver("int foo(int x, int y) { if (true) return 3 + foo(x, y); }"); + Visitor.runOver("void foo(int x, int y) { for (foo(x, y);;) ++x; }"); + Visitor.runOver( + "bool foo(int x, int y) { for (;foo(x, y);) x = 1; return true; }"); + + Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { + EXPECT_EQ("foo()", getExtendedText(*CE, tok::TokenKind::semi, *Context)); + }; + Visitor.runOver("bool foo() { if (foo()) return true; return false; }"); + Visitor.runOver("void foo() { int x; for (;; foo()) ++x; }"); + Visitor.runOver("int foo() { return foo() + 3; }"); +} + +} // end anonymous namespace diff --git a/unittests/Tooling/StencilTest.cpp b/unittests/Tooling/StencilTest.cpp new file mode 100644 index 0000000000..ffdca0562e --- /dev/null +++ b/unittests/Tooling/StencilTest.cpp @@ -0,0 +1,223 @@ +//===- unittest/Tooling/StencilTest.cpp -----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactoring/Stencil.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Tooling/FixIt.h" +#include "clang/Tooling/Tooling.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace clang; +using namespace tooling; +using namespace ast_matchers; + +namespace { +using ::testing::AllOf; +using ::testing::Eq; +using ::testing::HasSubstr; +using MatchResult = MatchFinder::MatchResult; +using tooling::stencil::node; +using tooling::stencil::sNode; +using tooling::stencil::text; + +// In tests, we can't directly match on llvm::Expected since its accessors +// mutate the object. So, we collapse it to an Optional. +static llvm::Optional<std::string> toOptional(llvm::Expected<std::string> V) { + if (V) + return *V; + ADD_FAILURE() << "Losing error in conversion to IsSomething: " + << llvm::toString(V.takeError()); + return llvm::None; +} + +// A very simple matcher for llvm::Optional values. +MATCHER_P(IsSomething, ValueMatcher, "") { + if (!arg) + return false; + return ::testing::ExplainMatchResult(ValueMatcher, *arg, result_listener); +} + +// Create a valid translation-unit from a statement. +static std::string wrapSnippet(llvm::Twine StatementCode) { + return ("auto stencil_test_snippet = []{" + StatementCode + "};").str(); +} + +static DeclarationMatcher wrapMatcher(const StatementMatcher &Matcher) { + return varDecl(hasName("stencil_test_snippet"), + hasDescendant(compoundStmt(hasAnySubstatement(Matcher)))); +} + +struct TestMatch { + // The AST unit from which `result` is built. We bundle it because it backs + // the result. Users are not expected to access it. + std::unique_ptr<ASTUnit> AstUnit; + // The result to use in the test. References `ast_unit`. + MatchResult Result; +}; + +// Matches `Matcher` against the statement `StatementCode` and returns the +// result. Handles putting the statement inside a function and modifying the +// matcher correspondingly. `Matcher` should match `StatementCode` exactly -- +// that is, produce exactly one match. +static llvm::Optional<TestMatch> matchStmt(llvm::Twine StatementCode, + StatementMatcher Matcher) { + auto AstUnit = buildASTFromCode(wrapSnippet(StatementCode)); + if (AstUnit == nullptr) { + ADD_FAILURE() << "AST construction failed"; + return llvm::None; + } + ASTContext &Context = AstUnit->getASTContext(); + auto Matches = ast_matchers::match(wrapMatcher(Matcher), Context); + // We expect a single, exact match for the statement. + if (Matches.size() != 1) { + ADD_FAILURE() << "Wrong number of matches: " << Matches.size(); + return llvm::None; + } + return TestMatch{std::move(AstUnit), MatchResult(Matches[0], &Context)}; +} + +class StencilTest : public ::testing::Test { +protected: + // Verifies that the given stencil fails when evaluated on a valid match + // result. Binds a statement to "stmt", a (non-member) ctor-initializer to + // "init", an expression to "expr" and a (nameless) declaration to "decl". + void testError(const Stencil &Stencil, + ::testing::Matcher<std::string> Matcher) { + const std::string Snippet = R"cc( + struct A {}; + class F : public A { + public: + F(int) {} + }; + F(1); + )cc"; + auto StmtMatch = matchStmt( + Snippet, + stmt(hasDescendant( + cxxConstructExpr( + hasDeclaration(decl(hasDescendant(cxxCtorInitializer( + isBaseInitializer()) + .bind("init"))) + .bind("decl"))) + .bind("expr"))) + .bind("stmt")); + ASSERT_TRUE(StmtMatch); + if (auto ResultOrErr = Stencil.eval(StmtMatch->Result)) { + ADD_FAILURE() << "Expected failure but succeeded: " << *ResultOrErr; + } else { + auto Err = llvm::handleErrors(ResultOrErr.takeError(), + [&Matcher](const llvm::StringError &Err) { + EXPECT_THAT(Err.getMessage(), Matcher); + }); + if (Err) { + ADD_FAILURE() << "Unhandled error: " << llvm::toString(std::move(Err)); + } + } + } + + // Tests failures caused by references to unbound nodes. `unbound_id` is the + // id that will cause the failure. + void testUnboundNodeError(const Stencil &Stencil, llvm::StringRef UnboundId) { + testError(Stencil, AllOf(HasSubstr(UnboundId), HasSubstr("not bound"))); + } +}; + +TEST_F(StencilTest, SingleStatement) { + StringRef Condition("C"), Then("T"), Else("E"); + const std::string Snippet = R"cc( + if (true) + return 1; + else + return 0; + )cc"; + auto StmtMatch = matchStmt( + Snippet, ifStmt(hasCondition(expr().bind(Condition)), + hasThen(stmt().bind(Then)), hasElse(stmt().bind(Else)))); + ASSERT_TRUE(StmtMatch); + // Invert the if-then-else. + auto Stencil = Stencil::cat("if (!", node(Condition), ") ", sNode(Else), + " else ", sNode(Then)); + EXPECT_THAT(toOptional(Stencil.eval(StmtMatch->Result)), + IsSomething(Eq("if (!true) return 0; else return 1;"))); +} + +TEST_F(StencilTest, SingleStatementCallOperator) { + StringRef Condition("C"), Then("T"), Else("E"); + const std::string Snippet = R"cc( + if (true) + return 1; + else + return 0; + )cc"; + auto StmtMatch = matchStmt( + Snippet, ifStmt(hasCondition(expr().bind(Condition)), + hasThen(stmt().bind(Then)), hasElse(stmt().bind(Else)))); + ASSERT_TRUE(StmtMatch); + // Invert the if-then-else. + Stencil S = Stencil::cat("if (!", node(Condition), ") ", sNode(Else), + " else ", sNode(Then)); + EXPECT_THAT(toOptional(S(StmtMatch->Result)), + IsSomething(Eq("if (!true) return 0; else return 1;"))); +} + +TEST_F(StencilTest, UnboundNode) { + const std::string Snippet = R"cc( + if (true) + return 1; + else + return 0; + )cc"; + auto StmtMatch = matchStmt(Snippet, ifStmt(hasCondition(stmt().bind("a1")), + hasThen(stmt().bind("a2")))); + ASSERT_TRUE(StmtMatch); + auto Stencil = Stencil::cat("if(!", sNode("a1"), ") ", node("UNBOUND"), ";"); + auto ResultOrErr = Stencil.eval(StmtMatch->Result); + EXPECT_TRUE(llvm::errorToBool(ResultOrErr.takeError())) + << "Expected unbound node, got " << *ResultOrErr; +} + +// Tests that a stencil with a single parameter (`Id`) evaluates to the expected +// string, when `Id` is bound to the expression-statement in `Snippet`. +void testExpr(StringRef Id, StringRef Snippet, const Stencil &Stencil, + StringRef Expected) { + auto StmtMatch = matchStmt(Snippet, expr().bind(Id)); + ASSERT_TRUE(StmtMatch); + EXPECT_THAT(toOptional(Stencil.eval(StmtMatch->Result)), + IsSomething(Expected)); +} + +TEST_F(StencilTest, NodeOp) { + StringRef Id = "id"; + testExpr(Id, "3;", Stencil::cat(node(Id)), "3"); +} + +TEST_F(StencilTest, SNodeOp) { + StringRef Id = "id"; + testExpr(Id, "3;", Stencil::cat(sNode(Id)), "3;"); +} + +TEST(StencilEqualityTest, Equality) { + using stencil::dPrint; + auto Lhs = Stencil::cat("foo", node("node"), dPrint("dprint_id")); + auto Rhs = Lhs; + EXPECT_EQ(Lhs, Rhs); +} + +TEST(StencilEqualityTest, InEqualityDifferentOrdering) { + auto Lhs = Stencil::cat("foo", node("node")); + auto Rhs = Stencil::cat(node("node"), "foo"); + EXPECT_NE(Lhs, Rhs); +} + +TEST(StencilEqualityTest, InEqualityDifferentSizes) { + auto Lhs = Stencil::cat("foo", node("node"), "bar", "baz"); + auto Rhs = Stencil::cat("foo", node("node"), "bar"); + EXPECT_NE(Lhs, Rhs); +} +} // namespace diff --git a/unittests/Tooling/TestVisitor.h b/unittests/Tooling/TestVisitor.h index 1a22ae737b..ff90a77a69 100644 --- a/unittests/Tooling/TestVisitor.h +++ b/unittests/Tooling/TestVisitor.h @@ -1,9 +1,8 @@ //===--- TestVisitor.h ------------------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// diff --git a/unittests/Tooling/ToolingTest.cpp b/unittests/Tooling/ToolingTest.cpp index 186463f80a..34f68a6aeb 100644 --- a/unittests/Tooling/ToolingTest.cpp +++ b/unittests/Tooling/ToolingTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/ToolingTest.cpp - Tooling unit tests --------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// @@ -383,7 +382,7 @@ TEST(ClangToolTest, ArgumentAdjusters) { ArgumentsAdjuster CheckSyntaxOnlyAdjuster = [&Found, &Ran](const CommandLineArguments &Args, StringRef /*unused*/) { Ran = true; - if (std::find(Args.begin(), Args.end(), "-fsyntax-only") != Args.end()) + if (llvm::is_contained(Args, "-fsyntax-only")) Found = true; return Args; }; @@ -441,8 +440,7 @@ TEST(ClangToolTest, StripDependencyFileAdjuster) { Tool.run(Action.get()); auto HasFlag = [&FinalArgs](const std::string &Flag) { - return std::find(FinalArgs.begin(), FinalArgs.end(), Flag) != - FinalArgs.end(); + return llvm::find(FinalArgs, Flag) != FinalArgs.end(); }; EXPECT_FALSE(HasFlag("-MD")); EXPECT_FALSE(HasFlag("-MMD")); @@ -450,6 +448,36 @@ TEST(ClangToolTest, StripDependencyFileAdjuster) { EXPECT_TRUE(HasFlag("-w")); } +// Check getClangStripPluginsAdjuster strips plugin related args. +TEST(ClangToolTest, StripPluginsAdjuster) { + FixedCompilationDatabase Compilations( + "/", {"-Xclang", "-add-plugin", "-Xclang", "random-plugin"}); + + ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); + Tool.mapVirtualFile("/a.cc", "void a() {}"); + + std::unique_ptr<FrontendActionFactory> Action( + newFrontendActionFactory<SyntaxOnlyAction>()); + + CommandLineArguments FinalArgs; + ArgumentsAdjuster CheckFlagsAdjuster = + [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) { + FinalArgs = Args; + return Args; + }; + Tool.clearArgumentsAdjusters(); + Tool.appendArgumentsAdjuster(getStripPluginsAdjuster()); + Tool.appendArgumentsAdjuster(CheckFlagsAdjuster); + Tool.run(Action.get()); + + auto HasFlag = [&FinalArgs](const std::string &Flag) { + return llvm::find(FinalArgs, Flag) != FinalArgs.end(); + }; + EXPECT_FALSE(HasFlag("-Xclang")); + EXPECT_FALSE(HasFlag("-add-plugin")); + EXPECT_FALSE(HasFlag("-random-plugin")); +} + namespace { /// Find a target name such that looking for it in TargetRegistry by that name /// returns the same target. We expect that there is at least one target diff --git a/unittests/Tooling/TransformerTest.cpp b/unittests/Tooling/TransformerTest.cpp new file mode 100644 index 0000000000..e07d9b7029 --- /dev/null +++ b/unittests/Tooling/TransformerTest.cpp @@ -0,0 +1,462 @@ +//===- unittest/Tooling/TransformerTest.cpp -------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactoring/Transformer.h" + +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace clang; +using namespace tooling; +using namespace ast_matchers; + +namespace { +using ::testing::IsEmpty; + +constexpr char KHeaderContents[] = R"cc( + struct string { + string(const char*); + char* c_str(); + int size(); + }; + int strlen(const char*); + + namespace proto { + struct PCFProto { + int foo(); + }; + struct ProtoCommandLineFlag : PCFProto { + PCFProto& GetProto(); + }; + } // namespace proto + class Logger {}; + void operator<<(Logger& l, string msg); + Logger& log(int level); +)cc"; + +static ast_matchers::internal::Matcher<clang::QualType> +isOrPointsTo(const clang::ast_matchers::DeclarationMatcher &TypeMatcher) { + return anyOf(hasDeclaration(TypeMatcher), pointsTo(TypeMatcher)); +} + +static std::string format(StringRef Code) { + const std::vector<Range> Ranges(1, Range(0, Code.size())); + auto Style = format::getLLVMStyle(); + const auto Replacements = format::reformat(Style, Code, Ranges); + auto Formatted = applyAllReplacements(Code, Replacements); + if (!Formatted) { + ADD_FAILURE() << "Could not format code: " + << llvm::toString(Formatted.takeError()); + return std::string(); + } + return *Formatted; +} + +static void compareSnippets(StringRef Expected, + const llvm::Optional<std::string> &MaybeActual) { + ASSERT_TRUE(MaybeActual) << "Rewrite failed. Expecting: " << Expected; + auto Actual = *MaybeActual; + std::string HL = "#include \"header.h\"\n"; + auto I = Actual.find(HL); + if (I != std::string::npos) + Actual.erase(I, HL.size()); + EXPECT_EQ(format(Expected), format(Actual)); +} + +// FIXME: consider separating this class into its own file(s). +class ClangRefactoringTestBase : public testing::Test { +protected: + void appendToHeader(StringRef S) { FileContents[0].second += S; } + + void addFile(StringRef Filename, StringRef Content) { + FileContents.emplace_back(Filename, Content); + } + + llvm::Optional<std::string> rewrite(StringRef Input) { + std::string Code = ("#include \"header.h\"\n" + Input).str(); + auto Factory = newFrontendActionFactory(&MatchFinder); + if (!runToolOnCodeWithArgs( + Factory->create(), Code, std::vector<std::string>(), "input.cc", + "clang-tool", std::make_shared<PCHContainerOperations>(), + FileContents)) { + llvm::errs() << "Running tool failed.\n"; + return None; + } + if (ErrorCount != 0) { + llvm::errs() << "Generating changes failed.\n"; + return None; + } + auto ChangedCode = + applyAtomicChanges("input.cc", Code, Changes, ApplyChangesSpec()); + if (!ChangedCode) { + llvm::errs() << "Applying changes failed: " + << llvm::toString(ChangedCode.takeError()) << "\n"; + return None; + } + return *ChangedCode; + } + + Transformer::ChangeConsumer consumer() { + return [this](Expected<AtomicChange> C) { + if (C) { + Changes.push_back(std::move(*C)); + } else { + consumeError(C.takeError()); + ++ErrorCount; + } + }; + } + + void testRule(RewriteRule Rule, StringRef Input, StringRef Expected) { + Transformer T(std::move(Rule), consumer()); + T.registerMatchers(&MatchFinder); + compareSnippets(Expected, rewrite(Input)); + } + + clang::ast_matchers::MatchFinder MatchFinder; + // Records whether any errors occurred in individual changes. + int ErrorCount = 0; + AtomicChanges Changes; + +private: + FileContentMappings FileContents = {{"header.h", ""}}; +}; + +class TransformerTest : public ClangRefactoringTestBase { +protected: + TransformerTest() { appendToHeader(KHeaderContents); } +}; + +// Given string s, change strlen($s.c_str()) to $s.size(). +static RewriteRule ruleStrlenSize() { + StringRef StringExpr = "strexpr"; + auto StringType = namedDecl(hasAnyName("::basic_string", "::string")); + auto R = makeRule( + callExpr(callee(functionDecl(hasName("strlen"))), + hasArgument(0, cxxMemberCallExpr( + on(expr(hasType(isOrPointsTo(StringType))) + .bind(StringExpr)), + callee(cxxMethodDecl(hasName("c_str")))))), + change<clang::Expr>("REPLACED")); + R.Explanation = text("Use size() method directly on string."); + return R; +} + +TEST_F(TransformerTest, StrlenSize) { + std::string Input = "int f(string s) { return strlen(s.c_str()); }"; + std::string Expected = "int f(string s) { return REPLACED; }"; + testRule(ruleStrlenSize(), Input, Expected); +} + +// Tests that no change is applied when a match is not expected. +TEST_F(TransformerTest, NoMatch) { + std::string Input = "int f(string s) { return s.size(); }"; + testRule(ruleStrlenSize(), Input, Input); +} + +// Tests that expressions in macro arguments are rewritten (when applicable). +TEST_F(TransformerTest, StrlenSizeMacro) { + std::string Input = R"cc( +#define ID(e) e + int f(string s) { return ID(strlen(s.c_str())); })cc"; + std::string Expected = R"cc( +#define ID(e) e + int f(string s) { return ID(REPLACED); })cc"; + testRule(ruleStrlenSize(), Input, Expected); +} + +// Tests replacing an expression. +TEST_F(TransformerTest, Flag) { + StringRef Flag = "flag"; + RewriteRule Rule = makeRule( + cxxMemberCallExpr(on(expr(hasType(cxxRecordDecl( + hasName("proto::ProtoCommandLineFlag")))) + .bind(Flag)), + unless(callee(cxxMethodDecl(hasName("GetProto"))))), + change<clang::Expr>(Flag, "EXPR")); + + std::string Input = R"cc( + proto::ProtoCommandLineFlag flag; + int x = flag.foo(); + int y = flag.GetProto().foo(); + )cc"; + std::string Expected = R"cc( + proto::ProtoCommandLineFlag flag; + int x = EXPR.foo(); + int y = flag.GetProto().foo(); + )cc"; + + testRule(std::move(Rule), Input, Expected); +} + +TEST_F(TransformerTest, NodePartNameNamedDecl) { + StringRef Fun = "fun"; + RewriteRule Rule = + makeRule(functionDecl(hasName("bad")).bind(Fun), + change<clang::FunctionDecl>(Fun, NodePart::Name, "good")); + + std::string Input = R"cc( + int bad(int x); + int bad(int x) { return x * x; } + )cc"; + std::string Expected = R"cc( + int good(int x); + int good(int x) { return x * x; } + )cc"; + + testRule(Rule, Input, Expected); +} + +TEST_F(TransformerTest, NodePartNameDeclRef) { + std::string Input = R"cc( + template <typename T> + T bad(T x) { + return x; + } + int neutral(int x) { return bad<int>(x) * x; } + )cc"; + std::string Expected = R"cc( + template <typename T> + T bad(T x) { + return x; + } + int neutral(int x) { return good<int>(x) * x; } + )cc"; + + StringRef Ref = "ref"; + testRule(makeRule(declRefExpr(to(functionDecl(hasName("bad")))).bind(Ref), + change<clang::Expr>(Ref, NodePart::Name, "good")), + Input, Expected); +} + +TEST_F(TransformerTest, NodePartNameDeclRefFailure) { + std::string Input = R"cc( + struct Y { + int operator*(); + }; + int neutral(int x) { + Y y; + int (Y::*ptr)() = &Y::operator*; + return *y + x; + } + )cc"; + + StringRef Ref = "ref"; + Transformer T(makeRule(declRefExpr(to(functionDecl())).bind(Ref), + change<clang::Expr>(Ref, NodePart::Name, "good")), + consumer()); + T.registerMatchers(&MatchFinder); + EXPECT_FALSE(rewrite(Input)); +} + +TEST_F(TransformerTest, NodePartMember) { + StringRef E = "expr"; + RewriteRule Rule = makeRule(memberExpr(member(hasName("bad"))).bind(E), + change<clang::Expr>(E, NodePart::Member, "good")); + + std::string Input = R"cc( + struct S { + int bad; + }; + int g() { + S s; + return s.bad; + } + )cc"; + std::string Expected = R"cc( + struct S { + int bad; + }; + int g() { + S s; + return s.good; + } + )cc"; + + testRule(Rule, Input, Expected); +} + +TEST_F(TransformerTest, NodePartMemberQualified) { + std::string Input = R"cc( + struct S { + int bad; + int good; + }; + struct T : public S { + int bad; + }; + int g() { + T t; + return t.S::bad; + } + )cc"; + std::string Expected = R"cc( + struct S { + int bad; + int good; + }; + struct T : public S { + int bad; + }; + int g() { + T t; + return t.S::good; + } + )cc"; + + StringRef E = "expr"; + testRule(makeRule(memberExpr().bind(E), + change<clang::Expr>(E, NodePart::Member, "good")), + Input, Expected); +} + +TEST_F(TransformerTest, NodePartMemberMultiToken) { + std::string Input = R"cc( + struct Y { + int operator*(); + int good(); + template <typename T> void foo(T t); + }; + int neutral(int x) { + Y y; + y.template foo<int>(3); + return y.operator *(); + } + )cc"; + std::string Expected = R"cc( + struct Y { + int operator*(); + int good(); + template <typename T> void foo(T t); + }; + int neutral(int x) { + Y y; + y.template good<int>(3); + return y.good(); + } + )cc"; + + StringRef MemExpr = "member"; + testRule(makeRule(memberExpr().bind(MemExpr), + change<clang::Expr>(MemExpr, NodePart::Member, "good")), + Input, Expected); +} + +TEST_F(TransformerTest, MultiChange) { + std::string Input = R"cc( + void foo() { + if (10 > 1.0) + log(1) << "oh no!"; + else + log(0) << "ok"; + } + )cc"; + std::string Expected = R"( + void foo() { + if (true) { /* then */ } + else { /* else */ } + } + )"; + + StringRef C = "C", T = "T", E = "E"; + testRule(makeRule(ifStmt(hasCondition(expr().bind(C)), + hasThen(stmt().bind(T)), hasElse(stmt().bind(E))), + {change<Expr>(C, "true"), change<Stmt>(T, "{ /* then */ }"), + change<Stmt>(E, "{ /* else */ }")}), + Input, Expected); +} + +// +// Negative tests (where we expect no transformation to occur). +// + +// Tests for a conflict in edits from a single match for a rule. +TEST_F(TransformerTest, TextGeneratorFailure) { + std::string Input = "int conflictOneRule() { return 3 + 7; }"; + // Try to change the whole binary-operator expression AND one its operands: + StringRef O = "O"; + auto AlwaysFail = [](const ast_matchers::MatchFinder::MatchResult &) + -> llvm::Expected<std::string> { + return llvm::createStringError(llvm::errc::invalid_argument, "ERROR"); + }; + Transformer T(makeRule(binaryOperator().bind(O), change<Expr>(O, AlwaysFail)), + consumer()); + T.registerMatchers(&MatchFinder); + EXPECT_FALSE(rewrite(Input)); + EXPECT_THAT(Changes, IsEmpty()); + EXPECT_EQ(ErrorCount, 1); +} + +// Tests for a conflict in edits from a single match for a rule. +TEST_F(TransformerTest, OverlappingEditsInRule) { + std::string Input = "int conflictOneRule() { return 3 + 7; }"; + // Try to change the whole binary-operator expression AND one its operands: + StringRef O = "O", L = "L"; + Transformer T( + makeRule(binaryOperator(hasLHS(expr().bind(L))).bind(O), + {change<Expr>(O, "DELETE_OP"), change<Expr>(L, "DELETE_LHS")}), + consumer()); + T.registerMatchers(&MatchFinder); + EXPECT_FALSE(rewrite(Input)); + EXPECT_THAT(Changes, IsEmpty()); + EXPECT_EQ(ErrorCount, 1); +} + +// Tests for a conflict in edits across multiple matches (of the same rule). +TEST_F(TransformerTest, OverlappingEditsMultipleMatches) { + std::string Input = "int conflictOneRule() { return -7; }"; + // Try to change the whole binary-operator expression AND one its operands: + StringRef E = "E"; + Transformer T(makeRule(expr().bind(E), change<Expr>(E, "DELETE_EXPR")), + consumer()); + T.registerMatchers(&MatchFinder); + // The rewrite process fails because the changes conflict with each other... + EXPECT_FALSE(rewrite(Input)); + // ... but two changes were produced. + EXPECT_EQ(Changes.size(), 2u); + EXPECT_EQ(ErrorCount, 0); +} + +TEST_F(TransformerTest, ErrorOccurredMatchSkipped) { + // Syntax error in the function body: + std::string Input = "void errorOccurred() { 3 }"; + Transformer T(makeRule(functionDecl(hasName("errorOccurred")), + change<Decl>("DELETED;")), + consumer()); + T.registerMatchers(&MatchFinder); + // The rewrite process itself fails... + EXPECT_FALSE(rewrite(Input)); + // ... and no changes or errors are produced in the process. + EXPECT_THAT(Changes, IsEmpty()); + EXPECT_EQ(ErrorCount, 0); +} + +TEST_F(TransformerTest, NoTransformationInMacro) { + std::string Input = R"cc( +#define MACRO(str) strlen((str).c_str()) + int f(string s) { return MACRO(s); })cc"; + testRule(ruleStrlenSize(), Input, Input); +} + +// This test handles the corner case where a macro called within another macro +// expands to matching code, but the matched code is an argument to the nested +// macro. A simple check of isMacroArgExpansion() vs. isMacroBodyExpansion() +// will get this wrong, and transform the code. This test verifies that no such +// transformation occurs. +TEST_F(TransformerTest, NoTransformationInNestedMacro) { + std::string Input = R"cc( +#define NESTED(e) e +#define MACRO(str) NESTED(strlen((str).c_str())) + int f(string s) { return MACRO(s); })cc"; + testRule(ruleStrlenSize(), Input, Input); +} +} // namespace diff --git a/unittests/libclang/LibclangTest.cpp b/unittests/libclang/LibclangTest.cpp index b88b88dac3..b3ad5c705b 100644 --- a/unittests/libclang/LibclangTest.cpp +++ b/unittests/libclang/LibclangTest.cpp @@ -1,9 +1,8 @@ //===- unittests/libclang/LibclangTest.cpp --- libclang tests -------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// |