Index: unittests/AST/ASTImporterFixtures.h =================================================================== --- unittests/AST/ASTImporterFixtures.h +++ unittests/AST/ASTImporterFixtures.h @@ -0,0 +1,176 @@ +//===- unittest/AST/ASTImporterFixtures.h - AST unit test support ---------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +/// \file +/// Fixture classes for testing the ASTImporter. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_UNITTESTS_AST_IMPORTER_FIXTURES_H +#define LLVM_CLANG_UNITTESTS_AST_IMPORTER_FIXTURES_H + +#include "gmock/gmock.h" + +#include "clang/AST/ASTImporter.h" +#include "clang/AST/ASTImporterLookupTable.h" +#include "clang/Frontend/ASTUnit.h" + +#include "DeclMatcher.h" +#include "Language.h" + +namespace clang { + +class ASTImporter; +class ASTImporterLookupTable; +class ASTUnit; + +namespace ast_matchers { + +const StringRef DeclToImportID = "declToImport"; +const StringRef DeclToVerifyID = "declToVerify"; + +// Creates a virtual file and assigns that to the context of given AST. If the +// file already exists then the file will not be created again as a duplicate. +void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName, + std::unique_ptr &&Buffer); + +void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName, + StringRef Code); + +// 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. +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 = getExtraArgs(); + for (const auto &Arg : ExtraArgs) { + Args.push_back(Arg); + } + return Args; + } +}; + +const auto DefaultTestValuesForRunOptions = ::testing::Values( + ArgVector(), ArgVector{"-fdelayed-template-parsing"}, + ArgVector{"-fms-compatibility"}, + ArgVector{"-fdelayed-template-parsing", "-fms-compatibility"}); + +// 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 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 + 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; + + // Represents a "From" translation unit and holds an importer object which we + // use to import from this translation unit. + struct TU { + // Buffer for the context, must live in the test scope. + std::string Code; + std::string FileName; + std::unique_ptr Unit; + TranslationUnitDecl *TUDecl = nullptr; + std::unique_ptr Importer; + ImporterConstructor Creator; + + TU(StringRef Code, StringRef FileName, ArgVector Args, + ImporterConstructor C = ImporterConstructor()); + ~TU(); + + void lazyInitImporter(ASTImporterLookupTable &LookupTable, ASTUnit *ToAST); + Decl *import(ASTImporterLookupTable &LookupTable, ASTUnit *ToAST, + Decl *FromDecl); + QualType import(ASTImporterLookupTable &LookupTable, ASTUnit *ToAST, + QualType FromType); + }; + + // We may have several From contexts and related translation units. In each + // AST, the buffers for the source are handled via references and are set + // during the creation of the AST. These references must point to a valid + // buffer until the AST is alive. Thus, we must use a list in order to avoid + // moving of the stored objects because that would mean breaking the + // references in the AST. By using a vector a move could happen when the + // vector is expanding, with the list we won't have these issues. + std::list FromTUs; + + // Initialize the lookup table if not initialized already. + void lazyInitLookupTable(TranslationUnitDecl *ToTU); + + void lazyInitToAST(Language ToLang, StringRef ToSrcCode, StringRef FileName); + TU *findFromTU(Decl *From); + +protected: + std::unique_ptr LookupTablePtr; + +public: + // We may have several From context but only one To context. + std::unique_ptr ToAST; + + // Creates an AST both for the From and To source code and imports the Decl + // of the identifier into the To context. + // Must not be called more than once within the same test. + std::tuple + getImportedDecl(StringRef FromSrcCode, Language FromLang, StringRef ToSrcCode, + Language ToLang, StringRef Identifier = DeclToImportID); + + // Creates a TU decl for the given source code which can be used as a From + // context. May be called several times in a given test (with different file + // name). + TranslationUnitDecl *getTuDecl(StringRef SrcCode, Language Lang, + StringRef FileName = "input.cc"); + + // Creates the To context with the given source code and returns the TU decl. + TranslationUnitDecl *getToTuDecl(StringRef ToSrcCode, Language ToLang); + + // Import the given Decl into the ToCtx. + // May be called several times in a given test. + // The different instances of the param From may have different ASTContext. + Decl *Import(Decl *From, Language ToLang); + + template DeclT *Import(DeclT *From, Language Lang) { + return cast_or_null(Import(cast(From), Lang)); + } + + QualType ImportType(QualType FromType, Decl *TUDecl, Language ToLang); + + ~ASTImporterTestBase(); +}; + +class ASTImporterOptionSpecificTestBase + : public ASTImporterTestBase, + public ::testing::WithParamInterface { +protected: + ArgVector getExtraArgs() const override { return GetParam(); } +}; + +} // end namespace ast_matchers +} // end namespace clang + +#endif Index: unittests/AST/ASTImporterFixtures.cpp =================================================================== --- unittests/AST/ASTImporterFixtures.cpp +++ unittests/AST/ASTImporterFixtures.cpp @@ -0,0 +1,210 @@ +//===- unittest/AST/ASTImporterFixtures.cpp - AST unit test support -------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +/// \file +/// Implementation of fixture classes for testing the ASTImporter. +// +//===----------------------------------------------------------------------===// + +#include "ASTImporterFixtures.h" + +#include "clang/AST/ASTImporter.h" +#include "clang/AST/ASTImporterLookupTable.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Tooling/Tooling.h" + +namespace clang { +namespace ast_matchers { + +void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName, + std::unique_ptr &&Buffer) { + assert(ToAST); + ASTContext &ToCtx = ToAST->getASTContext(); + auto *OFS = static_cast( + &ToCtx.getSourceManager().getFileManager().getVirtualFileSystem()); + auto *MFS = static_cast( + OFS->overlays_begin()->get()); + MFS->addFile(FileName, 0, std::move(Buffer)); +} + +void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName, + StringRef Code) { + return createVirtualFileIfNeeded(ToAST, FileName, + llvm::MemoryBuffer::getMemBuffer(Code)); +} + +ASTImporterTestBase::TU::TU(StringRef Code, StringRef FileName, ArgVector Args, + ImporterConstructor C) + : Code(Code), FileName(FileName), + Unit(tooling::buildASTFromCodeWithArgs(this->Code, Args, this->FileName)), + 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); + }; +} + +ASTImporterTestBase::TU::~TU() {} + +void ASTImporterTestBase::TU::lazyInitImporter( + ASTImporterLookupTable &LookupTable, ASTUnit *ToAST) { + assert(ToAST); + if (!Importer) + Importer.reset(Creator(ToAST->getASTContext(), ToAST->getFileManager(), + Unit->getASTContext(), Unit->getFileManager(), false, + &LookupTable)); + assert(&ToAST->getASTContext() == &Importer->getToContext()); + createVirtualFileIfNeeded(ToAST, FileName, Code); +} + +Decl *ASTImporterTestBase::TU::import(ASTImporterLookupTable &LookupTable, + ASTUnit *ToAST, Decl *FromDecl) { + lazyInitImporter(LookupTable, ToAST); + if (auto ImportedOrErr = Importer->Import_New(FromDecl)) + return *ImportedOrErr; + else { + llvm::consumeError(ImportedOrErr.takeError()); + return nullptr; + } +} + +QualType ASTImporterTestBase::TU::import(ASTImporterLookupTable &LookupTable, + ASTUnit *ToAST, QualType FromType) { + lazyInitImporter(LookupTable, ToAST); + if (auto ImportedOrErr = Importer->Import_New(FromType)) + return *ImportedOrErr; + else { + llvm::consumeError(ImportedOrErr.takeError()); + return QualType{}; + } +} + +void ASTImporterTestBase::lazyInitLookupTable(TranslationUnitDecl *ToTU) { + assert(ToTU); + if (!LookupTablePtr) + LookupTablePtr = llvm::make_unique(*ToTU); +} + +void ASTImporterTestBase::lazyInitToAST(Language ToLang, StringRef ToSrcCode, + StringRef FileName) { + if (ToAST) + return; + ArgVector ToArgs = getArgVectorForLanguage(ToLang); + // Source code must be a valid live buffer through the tests lifetime. + ToCode = ToSrcCode; + // Build the AST from an empty file. + ToAST = tooling::buildASTFromCodeWithArgs(ToCode, ToArgs, FileName); + ToAST->enableSourceFileDiagnostics(); + lazyInitLookupTable(ToAST->getASTContext().getTranslationUnitDecl()); +} + +ASTImporterTestBase::TU *ASTImporterTestBase::findFromTU(Decl *From) { + // 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 = llvm::find_if(FromTUs, [From](const TU &E) { + return E.TUDecl == From->getTranslationUnitDecl(); + }); + assert(It != FromTUs.end()); + return &*It; +} + +std::tuple +ASTImporterTestBase::getImportedDecl(StringRef FromSrcCode, Language FromLang, + StringRef ToSrcCode, Language ToLang, + StringRef Identifier) { + ArgVector FromArgs = getArgVectorForLanguage(FromLang), + ToArgs = getArgVectorForLanguage(ToLang); + + FromTUs.emplace_back(FromSrcCode, InputFileName, FromArgs, Creator); + TU &FromTU = FromTUs.back(); + + assert(!ToAST); + lazyInitToAST(ToLang, ToSrcCode, OutputFileName); + + ASTContext &FromCtx = FromTU.Unit->getASTContext(); + + IdentifierInfo *ImportedII = &FromCtx.Idents.get(Identifier); + assert(ImportedII && "Declaration with the given identifier " + "should be specified in test!"); + DeclarationName ImportDeclName(ImportedII); + SmallVector FoundDecls; + FromCtx.getTranslationUnitDecl()->localUncachedLookup(ImportDeclName, + FoundDecls); + + assert(FoundDecls.size() == 1); + + Decl *Imported = + FromTU.import(*LookupTablePtr, ToAST.get(), FoundDecls.front()); + + assert(Imported); + return std::make_tuple(*FoundDecls.begin(), Imported); +} + +TranslationUnitDecl *ASTImporterTestBase::getTuDecl(StringRef SrcCode, + Language Lang, + StringRef FileName) { + 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); + TU &Tu = FromTUs.back(); + + return Tu.TUDecl; +} + +TranslationUnitDecl *ASTImporterTestBase::getToTuDecl(StringRef ToSrcCode, + Language ToLang) { + ArgVector ToArgs = getArgVectorForLanguage(ToLang); + assert(!ToAST); + lazyInitToAST(ToLang, ToSrcCode, OutputFileName); + return ToAST->getASTContext().getTranslationUnitDecl(); +} + +Decl *ASTImporterTestBase::Import(Decl *From, Language ToLang) { + lazyInitToAST(ToLang, "", OutputFileName); + TU *FromTU = findFromTU(From); + assert(LookupTablePtr); + return FromTU->import(*LookupTablePtr, ToAST.get(), From); +} + +QualType ASTImporterTestBase::ImportType(QualType FromType, Decl *TUDecl, + Language ToLang) { + lazyInitToAST(ToLang, "", OutputFileName); + TU *FromTU = findFromTU(TUDecl); + assert(LookupTablePtr); + return FromTU->import(*LookupTablePtr, ToAST.get(), FromType); +} + +ASTImporterTestBase::~ASTImporterTestBase() { + if (!::testing::Test::HasFailure()) + return; + + for (auto &Tu : FromTUs) { + assert(Tu.Unit); + llvm::errs() << "FromAST:\n"; + Tu.Unit->getASTContext().getTranslationUnitDecl()->dump(); + llvm::errs() << "\n"; + } + if (ToAST) { + llvm::errs() << "ToAST:\n"; + ToAST->getASTContext().getTranslationUnitDecl()->dump(); + } +} + +} // end namespace ast_matchers +} // end namespace clang Index: unittests/AST/ASTImporterGenericRedeclTest.cpp =================================================================== --- unittests/AST/ASTImporterGenericRedeclTest.cpp +++ unittests/AST/ASTImporterGenericRedeclTest.cpp @@ -0,0 +1,577 @@ +//===- unittest/AST/ASTImporterTest.cpp - AST node import test ------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Type-parameterized tests for the correct import of redecl chains. +// +//===----------------------------------------------------------------------===// + +#include "ASTImporterFixtures.h" + +namespace clang { +namespace ast_matchers { + +using internal::BindableMatcher; + +struct Function { + using DeclTy = FunctionDecl; + static constexpr auto *Prototype = "void X();"; + static constexpr auto *Definition = "void X() {}"; + BindableMatcher getPattern() { + return functionDecl(hasName("X"), unless(isImplicit())); + } +}; + +struct Class { + using DeclTy = CXXRecordDecl; + static constexpr auto *Prototype = "class X;"; + static constexpr auto *Definition = "class X {};"; + BindableMatcher getPattern() { + return cxxRecordDecl(hasName("X"), unless(isImplicit())); + } +}; + +struct Variable { + using DeclTy = VarDecl; + static constexpr auto *Prototype = "extern int X;"; + static constexpr auto *Definition = "int X;"; + BindableMatcher getPattern() { return varDecl(hasName("X")); } +}; + +struct FunctionTemplate { + using DeclTy = FunctionTemplateDecl; + static constexpr auto *Prototype = "template void X();"; + static constexpr auto *Definition = + R"( + template void X() {}; + // Explicit instantiation is a must because of -fdelayed-template-parsing: + template void X(); + )"; + BindableMatcher getPattern() { + return functionTemplateDecl(hasName("X"), unless(isImplicit())); + } +}; + +struct ClassTemplate { + using DeclTy = ClassTemplateDecl; + static constexpr auto *Prototype = "template class X;"; + static constexpr auto *Definition = "template class X {};"; + BindableMatcher getPattern() { + return classTemplateDecl(hasName("X"), unless(isImplicit())); + } +}; + +struct FunctionTemplateSpec { + using DeclTy = FunctionDecl; + static constexpr auto *Prototype = + R"( + // Proto of the primary template. + template + void X(); + // Proto of the specialization. + template <> + void X(); + )"; + static constexpr auto *Definition = + R"( + // Proto of the primary template. + template + void X(); + // Specialization and definition. + template <> + void X() {} + )"; + BindableMatcher getPattern() { + return functionDecl(hasName("X"), isExplicitTemplateSpecialization()); + } +}; + +struct ClassTemplateSpec { + using DeclTy = ClassTemplateSpecializationDecl; + static constexpr auto *Prototype = + R"( + template class X; + template <> class X; + )"; + static constexpr auto *Definition = + R"( + template class X; + template <> class X {}; + )"; + BindableMatcher getPattern() { + return classTemplateSpecializationDecl(hasName("X"), unless(isImplicit())); + } +}; + +template +struct RedeclChain : ASTImporterOptionSpecificTestBase { + + using DeclTy = typename TypeParam::DeclTy; + std::string getPrototype() { return TypeParam::Prototype; } + std::string getDefinition() { return TypeParam::Definition; } + BindableMatcher 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(Prev)) { + EXPECT_EQ(Current->getPreviousDecl(), Prev); + auto *CurrentT = cast(Current); + ASSERT_TRUE(PrevT->getTemplatedDecl()); + ASSERT_TRUE(CurrentT->getTemplatedDecl()); + EXPECT_EQ(CurrentT->getTemplatedDecl()->getPreviousDecl(), + PrevT->getTemplatedDecl()); + return; + } + + // Specializations. + if (auto *PrevF = dyn_cast(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().match( + ToTU, functionTemplateDecl()); + auto *FirstSpecD = *(TemplateD->spec_begin()); + EXPECT_EQ(FirstSpecD->getCanonicalDecl(), PrevF->getCanonicalDecl()); + return; + } + } + + // The rest: Classes, Functions, etc. + EXPECT_EQ(Current->getPreviousDecl(), Prev); + } + + void + TypedTest_PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition() { + Decl *FromTU = getTuDecl(getPrototype(), Lang_CXX); + auto *FromD = FirstDeclMatcher().match(FromTU, getPattern()); + ASSERT_FALSE(FromD->isThisDeclarationADefinition()); + + Decl *ImportedD = Import(FromD, Lang_CXX); + Decl *ToTU = ImportedD->getTranslationUnitDecl(); + + EXPECT_EQ(DeclCounter().match(ToTU, getPattern()), 1u); + auto *ToD = LastDeclMatcher().match(ToTU, getPattern()); + EXPECT_TRUE(ImportedD == ToD); + EXPECT_FALSE(ToD->isThisDeclarationADefinition()); + if (auto *ToT = dyn_cast(ToD)) { + EXPECT_TRUE(ToT->getTemplatedDecl()); + } + } + + void TypedTest_DefinitionShouldBeImportedAsADefinition() { + Decl *FromTU = getTuDecl(getDefinition(), Lang_CXX); + auto *FromD = FirstDeclMatcher().match(FromTU, getPattern()); + ASSERT_TRUE(FromD->isThisDeclarationADefinition()); + + Decl *ImportedD = Import(FromD, Lang_CXX); + Decl *ToTU = ImportedD->getTranslationUnitDecl(); + + EXPECT_EQ(DeclCounter().match(ToTU, getPattern()), 1u); + auto *ToD = LastDeclMatcher().match(ToTU, getPattern()); + EXPECT_TRUE(ToD->isThisDeclarationADefinition()); + if (auto *ToT = dyn_cast(ToD)) { + EXPECT_TRUE(ToT->getTemplatedDecl()); + } + } + + void TypedTest_ImportPrototypeAfterImportedPrototype() { + Decl *FromTU = getTuDecl(getPrototype() + getPrototype(), Lang_CXX); + auto *From0 = FirstDeclMatcher().match(FromTU, getPattern()); + auto *From1 = LastDeclMatcher().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().match(ToTU, getPattern()), 2u); + auto *To0 = FirstDeclMatcher().match(ToTU, getPattern()); + auto *To1 = LastDeclMatcher().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().match(FromTU, getPattern()); + auto *FromDef = LastDeclMatcher().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().match(ToTU, getPattern()), 2u); + auto *ToProto = FirstDeclMatcher().match(ToTU, getPattern()); + auto *ToDef = LastDeclMatcher().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().match(FromTU, getPattern()); + auto *FromProto = LastDeclMatcher().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().match(ToTU, getPattern()), 2u); + auto *ToDef = FirstDeclMatcher().match(ToTU, getPattern()); + auto *ToProto = LastDeclMatcher().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().match(FromTU0, getPattern()); + auto *From1 = FirstDeclMatcher().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().match(ToTU, getPattern()), 2u); + auto *To0 = FirstDeclMatcher().match(ToTU, getPattern()); + auto *To1 = LastDeclMatcher().match(ToTU, getPattern()); + EXPECT_TRUE(Imported0 == To0); + EXPECT_TRUE(Imported1 == To1); + EXPECT_FALSE(To0->isThisDeclarationADefinition()); + EXPECT_FALSE(To1->isThisDeclarationADefinition()); + + CheckPreviousDecl(To0, To1); + } + + void TypedTest_ImportDefinitions() { + Decl *FromTU0 = getTuDecl(getDefinition(), Lang_CXX, "input0.cc"); + Decl *FromTU1 = getTuDecl(getDefinition(), Lang_CXX, "input1.cc"); + auto *From0 = FirstDeclMatcher().match(FromTU0, getPattern()); + auto *From1 = FirstDeclMatcher().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().match(ToTU, getPattern()), 1u); + auto *To0 = FirstDeclMatcher().match(ToTU, getPattern()); + EXPECT_TRUE(Imported0 == To0); + EXPECT_TRUE(To0->isThisDeclarationADefinition()); + if (auto *ToT0 = dyn_cast(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().match(FromTUDef, getPattern()); + auto *FromProto = + FirstDeclMatcher().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().match(ToTU, getPattern()), 2u); + auto *ToDef = FirstDeclMatcher().match(ToTU, getPattern()); + auto *ToProto = LastDeclMatcher().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().match(FromTUProto, getPattern()); + auto *FromDef = FirstDeclMatcher().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().match(ToTU, getPattern()), 2u); + auto *ToProto = FirstDeclMatcher().match(ToTU, getPattern()); + auto *ToDef = LastDeclMatcher().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().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().match(ToTU, getPattern()), 2u); + EXPECT_TRUE(cast(ImportedD)->isThisDeclarationADefinition()); + } + + void TypedTest_ImportPrototypeThenProtoAndDefinition() { + { + Decl *FromTU = getTuDecl(getPrototype(), Lang_CXX, "input0.cc"); + auto *FromD = FirstDeclMatcher().match(FromTU, getPattern()); + Import(FromD, Lang_CXX); + } + { + Decl *FromTU = + getTuDecl(getPrototype() + getDefinition(), Lang_CXX, "input1.cc"); + auto *FromD = FirstDeclMatcher().match(FromTU, getPattern()); + Import(FromD, Lang_CXX); + } + + Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + + ASSERT_EQ(DeclCounter().match(ToTU, getPattern()), 3u); + DeclTy *ProtoD = FirstDeclMatcher().match(ToTU, getPattern()); + EXPECT_FALSE(ProtoD->isThisDeclarationADefinition()); + + DeclTy *DefinitionD = LastDeclMatcher().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; \ + 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, ); + +} // end namespace ast_matchers +} // end namespace clang Index: unittests/AST/ASTImporterTest.cpp =================================================================== --- unittests/AST/ASTImporterTest.cpp +++ unittests/AST/ASTImporterTest.cpp @@ -10,24 +10,12 @@ // //===----------------------------------------------------------------------===// -// Define this to have ::testing::Combine available. -// FIXME: Better solution for this? -#define GTEST_HAS_COMBINE 1 +#include "llvm/ADT/StringMap.h" -#include "clang/AST/ASTImporter.h" -#include "MatchVerifier.h" -#include "clang/AST/ASTContext.h" #include "clang/AST/DeclContextInternals.h" -#include "clang/AST/ASTImporter.h" -#include "clang/AST/ASTImporterLookupTable.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/ASTMatchers/ASTMatchers.h" -#include "clang/Tooling/Tooling.h" - -#include "DeclMatcher.h" -#include "Language.h" -#include "gmock/gmock.h" -#include "llvm/ADT/StringMap.h" + +#include "ASTImporterFixtures.h" +#include "MatchVerifier.h" namespace clang { namespace ast_matchers { @@ -36,54 +24,6 @@ using internal::BindableMatcher; using llvm::StringMap; -// Creates a virtual file and assigns that to the context of given AST. If the -// file already exists then the file will not be created again as a duplicate. -static void -createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName, - std::unique_ptr &&Buffer) { - assert(ToAST); - ASTContext &ToCtx = ToAST->getASTContext(); - auto *OFS = static_cast( - &ToCtx.getSourceManager().getFileManager().getVirtualFileSystem()); - auto *MFS = static_cast( - OFS->overlays_begin()->get()); - MFS->addFile(FileName, 0, std::move(Buffer)); -} - -static void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName, - StringRef Code) { - return createVirtualFileIfNeeded(ToAST, FileName, - llvm::MemoryBuffer::getMemBuffer(Code)); -} - -const StringRef DeclToImportID = "declToImport"; -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. -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 = 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 CompilerOptionSpecificTest, public ::testing::WithParamInterface { @@ -305,239 +245,6 @@ return cast(ET->getNamedType().getTypePtr())->getDecl(); } -// 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 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 - 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; - - // Represents a "From" translation unit and holds an importer object which we - // use to import from this translation unit. - struct TU { - // Buffer for the context, must live in the test scope. - std::string Code; - std::string FileName; - std::unique_ptr Unit; - TranslationUnitDecl *TUDecl = nullptr; - std::unique_ptr Importer; - 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()), 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(Creator(ToAST->getASTContext(), ToAST->getFileManager(), - Unit->getASTContext(), Unit->getFileManager(), - false, &LookupTable)); - assert(&ToAST->getASTContext() == &Importer->getToContext()); - createVirtualFileIfNeeded(ToAST, FileName, Code); - } - - Decl *import(ASTImporterLookupTable &LookupTable, ASTUnit *ToAST, - Decl *FromDecl) { - lazyInitImporter(LookupTable, ToAST); - 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); - if (auto ImportedOrErr = Importer->Import_New(FromType)) - return *ImportedOrErr; - else { - llvm::consumeError(ImportedOrErr.takeError()); - return QualType{}; - } - } - }; - - // We may have several From contexts and related translation units. In each - // AST, the buffers for the source are handled via references and are set - // during the creation of the AST. These references must point to a valid - // buffer until the AST is alive. Thus, we must use a list in order to avoid - // moving of the stored objects because that would mean breaking the - // references in the AST. By using a vector a move could happen when the - // vector is expanding, with the list we won't have these issues. - std::list FromTUs; - - // Initialize the lookup table if not initialized already. - void lazyInitLookupTable(TranslationUnitDecl *ToTU) { - assert(ToTU); - if (!LookupTablePtr) - LookupTablePtr = llvm::make_unique(*ToTU); - } - - void lazyInitToAST(Language ToLang, StringRef ToSrcCode, StringRef FileName) { - if (ToAST) - return; - ArgVector ToArgs = getArgVectorForLanguage(ToLang); - // Source code must be a valid live buffer through the tests lifetime. - ToCode = ToSrcCode; - // Build the AST from an empty file. - ToAST = tooling::buildASTFromCodeWithArgs(ToCode, ToArgs, FileName); - ToAST->enableSourceFileDiagnostics(); - lazyInitLookupTable(ToAST->getASTContext().getTranslationUnitDecl()); - } - - TU *findFromTU(Decl *From) { - // 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 = llvm::find_if(FromTUs, [From](const TU &E) { - return E.TUDecl == From->getTranslationUnitDecl(); - }); - assert(It != FromTUs.end()); - return &*It; - } - -protected: - - std::unique_ptr LookupTablePtr; - -public: - // We may have several From context but only one To context. - std::unique_ptr ToAST; - - // Creates an AST both for the From and To source code and imports the Decl - // of the identifier into the To context. - // Must not be called more than once within the same test. - std::tuple - getImportedDecl(StringRef FromSrcCode, Language FromLang, StringRef ToSrcCode, - Language ToLang, StringRef Identifier = DeclToImportID) { - ArgVector FromArgs = getArgVectorForLanguage(FromLang), - ToArgs = getArgVectorForLanguage(ToLang); - - FromTUs.emplace_back(FromSrcCode, InputFileName, FromArgs, Creator); - TU &FromTU = FromTUs.back(); - - assert(!ToAST); - lazyInitToAST(ToLang, ToSrcCode, OutputFileName); - - ASTContext &FromCtx = FromTU.Unit->getASTContext(); - - IdentifierInfo *ImportedII = &FromCtx.Idents.get(Identifier); - assert(ImportedII && "Declaration with the given identifier " - "should be specified in test!"); - DeclarationName ImportDeclName(ImportedII); - SmallVector FoundDecls; - FromCtx.getTranslationUnitDecl()->localUncachedLookup(ImportDeclName, - FoundDecls); - - assert(FoundDecls.size() == 1); - - Decl *Imported = - FromTU.import(*LookupTablePtr, ToAST.get(), FoundDecls.front()); - - assert(Imported); - return std::make_tuple(*FoundDecls.begin(), Imported); - } - - // Creates a TU decl for the given source code which can be used as a From - // context. May be called several times in a given test (with different file - // name). - TranslationUnitDecl *getTuDecl(StringRef SrcCode, Language Lang, - StringRef FileName = "input.cc") { - 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); - TU &Tu = FromTUs.back(); - - return Tu.TUDecl; - } - - // Creates the To context with the given source code and returns the TU decl. - TranslationUnitDecl *getToTuDecl(StringRef ToSrcCode, Language ToLang) { - ArgVector ToArgs = getArgVectorForLanguage(ToLang); - assert(!ToAST); - lazyInitToAST(ToLang, ToSrcCode, OutputFileName); - return ToAST->getASTContext().getTranslationUnitDecl(); - } - - // Import the given Decl into the ToCtx. - // May be called several times in a given test. - // The different instances of the param From may have different ASTContext. - Decl *Import(Decl *From, Language ToLang) { - lazyInitToAST(ToLang, "", OutputFileName); - TU *FromTU = findFromTU(From); - assert(LookupTablePtr); - return FromTU->import(*LookupTablePtr, ToAST.get(), From); - } - - template DeclT *Import(DeclT *From, Language Lang) { - return cast_or_null(Import(cast(From), Lang)); - } - - QualType ImportType(QualType FromType, Decl *TUDecl, Language ToLang) { - lazyInitToAST(ToLang, "", OutputFileName); - TU *FromTU = findFromTU(TUDecl); - assert(LookupTablePtr); - return FromTU->import(*LookupTablePtr, ToAST.get(), FromType); - } - - ~ASTImporterTestBase() { - if (!::testing::Test::HasFailure()) return; - - for (auto &Tu : FromTUs) { - assert(Tu.Unit); - llvm::errs() << "FromAST:\n"; - Tu.Unit->getASTContext().getTranslationUnitDecl()->dump(); - llvm::errs() << "\n"; - } - if (ToAST) { - llvm::errs() << "ToAST:\n"; - ToAST->getASTContext().getTranslationUnitDecl()->dump(); - } - } -}; - -class ASTImporterOptionSpecificTestBase - : public ASTImporterTestBase, - public ::testing::WithParamInterface { -protected: - ArgVector getExtraArgs() const override { return GetParam(); } -}; - struct ImportExpr : TestImportBase {}; struct ImportType : TestImportBase {}; struct ImportDecl : TestImportBase {}; @@ -2451,206 +2158,6 @@ 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 operator()() { return functionDecl(hasName("f")); } -}; -struct GetVarPattern { - using DeclTy = VarDecl; - BindableMatcher 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>; -// 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 -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 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().match(FromTu, Pattern); - auto *FromF1 = LastDeclMatcher().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; -using ImportVariablesVisibilityChain = ImportVisibilityChain; -// 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>>; - -template -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 getPattern() const { return PatternFactory()(); } - - void TypedTest_ImportAfter() { - TranslationUnitDecl *ToTu = getToTuDecl(getCode0(), Lang_CXX); - TranslationUnitDecl *FromTu = getTuDecl(getCode1(), Lang_CXX, "input1.cc"); - - auto *ToF0 = FirstDeclMatcher().match(ToTu, getPattern()); - auto *FromF1 = FirstDeclMatcher().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().match(FromTu0, getPattern()); - auto *FromF1 = - FirstDeclMatcher().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; -using ImportVariablesVisibility = ImportVisibility; - -// 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")); @@ -3983,574 +3490,6 @@ EXPECT_EQ(ToDef->getPreviousDecl(), ToProto); } -// 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 getPattern() { - return functionDecl(hasName("X"), unless(isImplicit())); - } -}; - -struct Class { - using DeclTy = CXXRecordDecl; - static constexpr auto *Prototype = "class X;"; - static constexpr auto *Definition = "class X {};"; - BindableMatcher getPattern() { - return cxxRecordDecl(hasName("X"), unless(isImplicit())); - } -}; - -struct Variable { - using DeclTy = VarDecl; - static constexpr auto *Prototype = "extern int X;"; - static constexpr auto *Definition = "int X;"; - BindableMatcher getPattern() { - return varDecl(hasName("X")); - } -}; - -struct FunctionTemplate { - using DeclTy = FunctionTemplateDecl; - static constexpr auto *Prototype = "template void X();"; - static constexpr auto *Definition = - R"( - template void X() {}; - // Explicit instantiation is a must because of -fdelayed-template-parsing: - template void X(); - )"; - BindableMatcher getPattern() { - return functionTemplateDecl(hasName("X"), unless(isImplicit())); - } -}; - -struct ClassTemplate { - using DeclTy = ClassTemplateDecl; - static constexpr auto *Prototype = "template class X;"; - static constexpr auto *Definition = "template class X {};"; - BindableMatcher getPattern() { - return classTemplateDecl(hasName("X"), unless(isImplicit())); - } -}; - -struct FunctionTemplateSpec { - using DeclTy = FunctionDecl; - static constexpr auto *Prototype = - R"( - // Proto of the primary template. - template - void X(); - // Proto of the specialization. - template <> - void X(); - )"; - static constexpr auto *Definition = - R"( - // Proto of the primary template. - template - void X(); - // Specialization and definition. - template <> - void X() {} - )"; - BindableMatcher getPattern() { - return functionDecl(hasName("X"), isExplicitTemplateSpecialization()); - } -}; - -struct ClassTemplateSpec { - using DeclTy = ClassTemplateSpecializationDecl; - static constexpr auto *Prototype = - R"( - template class X; - template <> class X; - )"; - static constexpr auto *Definition = - R"( - template class X; - template <> class X {}; - )"; - BindableMatcher getPattern() { - return classTemplateSpecializationDecl(hasName("X"), unless(isImplicit())); - } -}; - -template -struct RedeclChain : ASTImporterOptionSpecificTestBase { - - using DeclTy = typename TypeParam::DeclTy; - std::string getPrototype() { return TypeParam::Prototype; } - std::string getDefinition() { return TypeParam::Definition; } - BindableMatcher 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(Prev)) { - EXPECT_EQ(Current->getPreviousDecl(), Prev); - auto *CurrentT = cast(Current); - ASSERT_TRUE(PrevT->getTemplatedDecl()); - ASSERT_TRUE(CurrentT->getTemplatedDecl()); - EXPECT_EQ(CurrentT->getTemplatedDecl()->getPreviousDecl(), - PrevT->getTemplatedDecl()); - return; - } - - // Specializations. - if (auto *PrevF = dyn_cast(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().match( - ToTU, functionTemplateDecl()); - auto *FirstSpecD = *(TemplateD->spec_begin()); - EXPECT_EQ(FirstSpecD->getCanonicalDecl(), PrevF->getCanonicalDecl()); - return; - } - } - - // The rest: Classes, Functions, etc. - EXPECT_EQ(Current->getPreviousDecl(), Prev); - } - - void - TypedTest_PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition() { - Decl *FromTU = getTuDecl(getPrototype(), Lang_CXX); - auto *FromD = FirstDeclMatcher().match(FromTU, getPattern()); - ASSERT_FALSE(FromD->isThisDeclarationADefinition()); - - Decl *ImportedD = Import(FromD, Lang_CXX); - Decl *ToTU = ImportedD->getTranslationUnitDecl(); - - EXPECT_EQ(DeclCounter().match(ToTU, getPattern()), 1u); - auto *ToD = LastDeclMatcher().match(ToTU, getPattern()); - EXPECT_TRUE(ImportedD == ToD); - EXPECT_FALSE(ToD->isThisDeclarationADefinition()); - if (auto *ToT = dyn_cast(ToD)) { - EXPECT_TRUE(ToT->getTemplatedDecl()); - } - } - - void TypedTest_DefinitionShouldBeImportedAsADefinition() { - Decl *FromTU = getTuDecl(getDefinition(), Lang_CXX); - auto *FromD = FirstDeclMatcher().match(FromTU, getPattern()); - ASSERT_TRUE(FromD->isThisDeclarationADefinition()); - - Decl *ImportedD = Import(FromD, Lang_CXX); - Decl *ToTU = ImportedD->getTranslationUnitDecl(); - - EXPECT_EQ(DeclCounter().match(ToTU, getPattern()), 1u); - auto *ToD = LastDeclMatcher().match(ToTU, getPattern()); - EXPECT_TRUE(ToD->isThisDeclarationADefinition()); - if (auto *ToT = dyn_cast(ToD)) { - EXPECT_TRUE(ToT->getTemplatedDecl()); - } - } - - void TypedTest_ImportPrototypeAfterImportedPrototype() { - Decl *FromTU = getTuDecl( - getPrototype() + getPrototype(), Lang_CXX); - auto *From0 = - FirstDeclMatcher().match(FromTU, getPattern()); - auto *From1 = LastDeclMatcher().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().match(ToTU, getPattern()), 2u); - auto *To0 = FirstDeclMatcher().match(ToTU, getPattern()); - auto *To1 = LastDeclMatcher().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().match(FromTU, getPattern()); - auto *FromDef = LastDeclMatcher().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().match(ToTU, getPattern()), 2u); - auto *ToProto = FirstDeclMatcher().match(ToTU, getPattern()); - auto *ToDef = LastDeclMatcher().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().match(FromTU, getPattern()); - auto *FromProto = LastDeclMatcher().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().match(ToTU, getPattern()), 2u); - auto *ToDef = FirstDeclMatcher().match(ToTU, getPattern()); - auto *ToProto = LastDeclMatcher().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().match(FromTU0, getPattern()); - auto *From1 = FirstDeclMatcher().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().match(ToTU, getPattern()), 2u); - auto *To0 = FirstDeclMatcher().match(ToTU, getPattern()); - auto *To1 = LastDeclMatcher().match(ToTU, getPattern()); - EXPECT_TRUE(Imported0 == To0); - EXPECT_TRUE(Imported1 == To1); - EXPECT_FALSE(To0->isThisDeclarationADefinition()); - EXPECT_FALSE(To1->isThisDeclarationADefinition()); - - CheckPreviousDecl(To0, To1); - } - - void TypedTest_ImportDefinitions() { - Decl *FromTU0 = getTuDecl(getDefinition(), Lang_CXX, "input0.cc"); - Decl *FromTU1 = getTuDecl(getDefinition(), Lang_CXX, "input1.cc"); - auto *From0 = FirstDeclMatcher().match(FromTU0, getPattern()); - auto *From1 = FirstDeclMatcher().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().match(ToTU, getPattern()), 1u); - auto *To0 = FirstDeclMatcher().match(ToTU, getPattern()); - EXPECT_TRUE(Imported0 == To0); - EXPECT_TRUE(To0->isThisDeclarationADefinition()); - if (auto *ToT0 = dyn_cast(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().match(FromTUDef, getPattern()); - auto *FromProto = - FirstDeclMatcher().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().match(ToTU, getPattern()), 2u); - auto *ToDef = FirstDeclMatcher().match(ToTU, getPattern()); - auto *ToProto = LastDeclMatcher().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().match(FromTUProto, getPattern()); - auto *FromDef = FirstDeclMatcher().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().match(ToTU, getPattern()), 2u); - auto *ToProto = FirstDeclMatcher().match(ToTU, getPattern()); - auto *ToDef = LastDeclMatcher().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().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().match(ToTU, getPattern()), 2u); - EXPECT_TRUE(cast(ImportedD)->isThisDeclarationADefinition()); - } - - void TypedTest_ImportPrototypeThenProtoAndDefinition() { - { - Decl *FromTU = getTuDecl(getPrototype(), Lang_CXX, "input0.cc"); - auto *FromD = FirstDeclMatcher().match(FromTU, getPattern()); - Import(FromD, Lang_CXX); - } - { - Decl *FromTU = - getTuDecl(getPrototype() + getDefinition(), Lang_CXX, "input1.cc"); - auto *FromD = FirstDeclMatcher().match(FromTU, getPattern()); - Import(FromD, Lang_CXX); - } - - Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); - - ASSERT_EQ(DeclCounter().match(ToTU, getPattern()), 3u); - DeclTy *ProtoD = FirstDeclMatcher().match(ToTU, getPattern()); - EXPECT_FALSE(ProtoD->isThisDeclarationADefinition()); - - DeclTy *DefinitionD = LastDeclMatcher().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; \ - 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 {}; Index: unittests/AST/ASTImporterVisibilityTest.cpp =================================================================== --- unittests/AST/ASTImporterVisibilityTest.cpp +++ unittests/AST/ASTImporterVisibilityTest.cpp @@ -0,0 +1,219 @@ +//===- unittest/AST/ASTImporterTest.cpp - AST node import test ------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Type-parameterized tests for the correct import of Decls with different +// visibility. +// +//===----------------------------------------------------------------------===// + +// Define this to have ::testing::Combine available. +// FIXME: Better solution for this? +#define GTEST_HAS_COMBINE 1 + +#include "ASTImporterFixtures.h" + +namespace clang { +namespace ast_matchers { + +using internal::BindableMatcher; + +// Type parameters for type-parameterized test fixtures. +struct GetFunPattern { + using DeclTy = FunctionDecl; + BindableMatcher operator()() { return functionDecl(hasName("f")); } +}; +struct GetVarPattern { + using DeclTy = VarDecl; + BindableMatcher operator()() { return varDecl(hasName("v")); } +}; + +// Values for the value-parameterized test fixtures. +// FunctionDecl: +const auto *ExternF = "void f();"; +const auto *StaticF = "static void f();"; +const auto *AnonF = "namespace { void f(); }"; +// VarDecl: +const auto *ExternV = "extern int v;"; +const auto *StaticV = "static int v;"; +const 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>; +// 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 +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 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 *FromD0 = FirstDeclMatcher().match(FromTu, Pattern); + auto *FromD1 = LastDeclMatcher().match(FromTu, Pattern); + + auto *ToD0 = Import(FromD0, Lang_CXX); + auto *ToD1 = Import(FromD1, Lang_CXX); + + EXPECT_TRUE(ToD0); + ASSERT_TRUE(ToD1); + EXPECT_NE(ToD0, ToD1); + EXPECT_EQ(ToD1->getPreviousDecl(), ToD0); + } +}; + +// Manual instantiation of the fixture with each type. +using ImportFunctionsVisibilityChain = ImportVisibilityChain; +using ImportVariablesVisibilityChain = ImportVisibilityChain; +// 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>>; + +template +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 getPattern() const { return PatternFactory()(); } + + void TypedTest_ImportAfter() { + TranslationUnitDecl *ToTu = getToTuDecl(getCode0(), Lang_CXX); + TranslationUnitDecl *FromTu = getTuDecl(getCode1(), Lang_CXX, "input1.cc"); + + auto *ToD0 = FirstDeclMatcher().match(ToTu, getPattern()); + auto *FromD1 = FirstDeclMatcher().match(FromTu, getPattern()); + + auto *ToD1 = Import(FromD1, Lang_CXX); + + ASSERT_TRUE(ToD0); + ASSERT_TRUE(ToD1); + EXPECT_NE(ToD0, ToD1); + + if (shouldBeLinked()) + EXPECT_EQ(ToD1->getPreviousDecl(), ToD0); + else + EXPECT_FALSE(ToD1->getPreviousDecl()); + } + + void TypedTest_ImportAfterImport() { + TranslationUnitDecl *FromTu0 = getTuDecl(getCode0(), Lang_CXX, "input0.cc"); + TranslationUnitDecl *FromTu1 = getTuDecl(getCode1(), Lang_CXX, "input1.cc"); + auto *FromD0 = FirstDeclMatcher().match(FromTu0, getPattern()); + auto *FromD1 = FirstDeclMatcher().match(FromTu1, getPattern()); + auto *ToD0 = Import(FromD0, Lang_CXX); + auto *ToD1 = Import(FromD1, Lang_CXX); + ASSERT_TRUE(ToD0); + ASSERT_TRUE(ToD1); + EXPECT_NE(ToD0, ToD1); + if (shouldBeLinked()) + EXPECT_EQ(ToD1->getPreviousDecl(), ToD0); + else + EXPECT_FALSE(ToD1->getPreviousDecl()); + } +}; +using ImportFunctionsVisibility = ImportVisibility; +using ImportVariablesVisibility = ImportVisibility; + +// 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(); +} + +const bool ExpectLink = true; +const 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))), ); + +} // end namespace ast_matchers +} // end namespace clang Index: unittests/AST/CMakeLists.txt =================================================================== --- unittests/AST/CMakeLists.txt +++ unittests/AST/CMakeLists.txt @@ -8,7 +8,10 @@ add_clang_unittest(ASTTests ASTContextParentMapTest.cpp + ASTImporterFixtures.cpp ASTImporterTest.cpp + ASTImporterGenericRedeclTest.cpp + ASTImporterVisibilityTest.cpp ASTTypeTraitsTest.cpp ASTVectorTest.cpp CommentLexer.cpp