diff options
Diffstat (limited to 'unittests/AST/ASTImporterTest.cpp')
-rw-r--r-- | unittests/AST/ASTImporterTest.cpp | 2287 |
1 files changed, 1522 insertions, 765 deletions
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, |