//===- 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. // //===----------------------------------------------------------------------===// // // Tests for the correct import of AST nodes from one AST context to another. // //===----------------------------------------------------------------------===// #include "MatchVerifier.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTImporter.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" namespace clang { namespace ast_matchers { using internal::Matcher; 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().get()); 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. struct ParameterizedTestsFixture : ::testing::TestWithParam { // 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(); for (const auto &Arg : ExtraArgs) { Args.push_back(Arg); } return Args; } }; // Base class for those tests which use the family of `testImport` functions. class TestImportBase : public ParameterizedTestsFixture { template 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 // while importing SourceLocations. It is safe to add same file multiple // times - it just isn't replaced. StringRef FromFileName = From->getMainFileName(); createVirtualFileIfNeeded(To, FromFileName, From->getBufferForFile(FromFileName)); auto Imported = Importer.Import(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); // This traverses the AST to catch certain bugs like poorly or not // implemented subtrees. Imported->dump(ToNothing); return Imported; } template testing::AssertionResult testImport(const std::string &FromCode, const ArgVector &FromArgs, const std::string &ToCode, const ArgVector &ToArgs, MatchVerifier &Verifier, const BindableMatcher &SearchMatcher, const BindableMatcher &VerificationMatcher) { const char *const InputFileName = "input.cc"; const char *const OutputFileName = "output.cc"; std::unique_ptr FromAST = tooling::buildASTFromCodeWithArgs( FromCode, FromArgs, InputFileName), ToAST = tooling::buildASTFromCodeWithArgs( ToCode, ToArgs, OutputFileName); ASTContext &FromCtx = FromAST->getASTContext(), &ToCtx = ToAST->getASTContext(); ASTImporter Importer(ToCtx, ToAST->getFileManager(), FromCtx, FromAST->getFileManager(), false); auto FoundNodes = match(SearchMatcher, FromCtx); if (FoundNodes.size() != 1) return testing::AssertionFailure() << "Multiple potential nodes were found!"; auto ToImport = selectFirst(DeclToImportID, FoundNodes); if (!ToImport) return testing::AssertionFailure() << "Node type mismatch!"; // Sanity check: the node being imported should match in the same way as // the result node. BindableMatcher WrapperMatcher(VerificationMatcher); EXPECT_TRUE(Verifier.match(ToImport, WrapperMatcher)); auto Imported = importNode(FromAST.get(), ToAST.get(), Importer, ToImport); if (!Imported) return testing::AssertionFailure() << "Import failed, nullptr returned!"; return Verifier.match(Imported, WrapperMatcher); } template testing::AssertionResult testImport(const std::string &FromCode, const ArgVector &FromArgs, const std::string &ToCode, const ArgVector &ToArgs, MatchVerifier &Verifier, const BindableMatcher &VerificationMatcher) { return testImport( FromCode, FromArgs, ToCode, ToArgs, Verifier, translationUnitDecl( has(namedDecl(hasName(DeclToImportID)).bind(DeclToImportID))), VerificationMatcher); } public: /// Test how AST node named "declToImport" located in the translation unit /// of "FromCode" virtual file is imported to "ToCode" virtual file. /// The verification is done by running AMatcher over the imported node. template void testImport(const std::string &FromCode, Language FromLang, const std::string &ToCode, Language ToLang, MatchVerifier &Verifier, const MatcherType &AMatcher) { ArgVector FromArgs = getArgVectorForLanguage(FromLang), ToArgs = getArgVectorForLanguage(ToLang); EXPECT_TRUE( testImport(FromCode, FromArgs, ToCode, ToArgs, Verifier, AMatcher)); } struct ImportAction { StringRef FromFilename; StringRef ToFilename; // FIXME: Generalize this to support other node kinds. BindableMatcher ImportPredicate; ImportAction(StringRef FromFilename, StringRef ToFilename, DeclarationMatcher ImportPredicate) : FromFilename(FromFilename), ToFilename(ToFilename), ImportPredicate(ImportPredicate) {} ImportAction(StringRef FromFilename, StringRef ToFilename, const std::string &DeclName) : FromFilename(FromFilename), ToFilename(ToFilename), ImportPredicate(namedDecl(hasName(DeclName))) {} }; using SingleASTUnit = std::unique_ptr; using AllASTUnits = StringMap; struct CodeEntry { std::string CodeSample; Language Lang; }; using CodeFiles = StringMap; /// Builds an ASTUnit for one potential compile options set. SingleASTUnit createASTUnit(StringRef FileName, const CodeEntry &CE) const { ArgVector Args = getArgVectorForLanguage(CE.Lang); auto AST = tooling::buildASTFromCodeWithArgs(CE.CodeSample, Args, FileName); EXPECT_TRUE(AST.get()); return AST; } /// Test an arbitrary sequence of imports for a set of given in-memory files. /// The verification is done by running VerificationMatcher against a /// specified AST node inside of one of given files. /// \param CodeSamples Map whose key is the file name and the value is the /// file content. /// \param ImportActions Sequence of imports. Each import in sequence /// specifies "from file" and "to file" and a matcher that is used for /// searching a declaration for import in "from file". /// \param FileForFinalCheck Name of virtual file for which the final check is /// applied. /// \param FinalSelectPredicate Matcher that specifies the AST node in the /// FileForFinalCheck for which the verification will be done. /// \param VerificationMatcher Matcher that will be used for verification /// after all imports in sequence are done. void testImportSequence(const CodeFiles &CodeSamples, const std::vector &ImportActions, StringRef FileForFinalCheck, BindableMatcher FinalSelectPredicate, BindableMatcher VerificationMatcher) { AllASTUnits AllASTs; using ImporterKey = std::pair; llvm::DenseMap> Importers; auto GenASTsIfNeeded = [this, &AllASTs, &CodeSamples](StringRef Filename) { if (!AllASTs.count(Filename)) { auto Found = CodeSamples.find(Filename); assert(Found != CodeSamples.end() && "Wrong file for import!"); AllASTs[Filename] = createASTUnit(Filename, Found->getValue()); } }; for (const ImportAction &Action : ImportActions) { StringRef FromFile = Action.FromFilename, ToFile = Action.ToFilename; GenASTsIfNeeded(FromFile); GenASTsIfNeeded(ToFile); ASTUnit *From = AllASTs[FromFile].get(); ASTUnit *To = AllASTs[ToFile].get(); // Create a new importer if needed. std::unique_ptr &ImporterRef = Importers[{From, To}]; if (!ImporterRef) ImporterRef.reset(new ASTImporter( To->getASTContext(), To->getFileManager(), From->getASTContext(), From->getFileManager(), false)); // Find the declaration and import it. auto FoundDecl = match(Action.ImportPredicate.bind(DeclToImportID), From->getASTContext()); EXPECT_TRUE(FoundDecl.size() == 1); const Decl *ToImport = selectFirst(DeclToImportID, FoundDecl); auto Imported = importNode(From, To, *ImporterRef, ToImport); EXPECT_TRUE(Imported); } // Find the declaration and import it. auto FoundDecl = match(FinalSelectPredicate.bind(DeclToVerifyID), AllASTs[FileForFinalCheck]->getASTContext()); EXPECT_TRUE(FoundDecl.size() == 1); const Decl *ToVerify = selectFirst(DeclToVerifyID, FoundDecl); MatchVerifier Verifier; EXPECT_TRUE( Verifier.match(ToVerify, BindableMatcher(VerificationMatcher))); } }; template RecordDecl *getRecordDecl(T *D) { auto *ET = cast(D->getType().getTypePtr()); 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 ParameterizedTestsFixture { const char *const InputFileName = "input.cc"; const char *const OutputFileName = "output.cc"; // 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; TU(StringRef Code, StringRef FileName, ArgVector Args) : Code(Code), FileName(FileName), Unit(tooling::buildASTFromCodeWithArgs(this->Code, Args, this->FileName)), TUDecl(Unit->getASTContext().getTranslationUnitDecl()) { Unit->enableSourceFileDiagnostics(); } void lazyInitImporter(ASTUnit *ToAST) { assert(ToAST); if (!Importer) { Importer.reset(new ASTImporter( ToAST->getASTContext(), ToAST->getFileManager(), Unit->getASTContext(), Unit->getFileManager(), false)); } assert(&ToAST->getASTContext() == &Importer->getToContext()); createVirtualFileIfNeeded(ToAST, FileName, Code); } Decl *import(ASTUnit *ToAST, Decl *FromDecl) { lazyInitImporter(ToAST); return Importer->Import(FromDecl); } QualType import(ASTUnit *ToAST, QualType FromType) { lazyInitImporter(ToAST); return Importer->Import(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; void lazyInitToAST(Language ToLang) { if (ToAST) return; ArgVector ToArgs = getArgVectorForLanguage(ToLang); // Build the AST from an empty file. ToAST = tooling::buildASTFromCodeWithArgs(/*Code=*/"", ToArgs, "empty.cc"); ToAST->enableSourceFileDiagnostics(); } 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 = std::find_if(FromTUs.begin(), FromTUs.end(), [From](const TU &E) { return E.TUDecl == From->getTranslationUnitDecl(); }); assert(It != FromTUs.end()); return &*It; } 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); TU &FromTU = FromTUs.back(); ToCode = ToSrcCode; assert(!ToAST); ToAST = tooling::buildASTFromCodeWithArgs(ToCode, ToArgs, OutputFileName); ToAST->enableSourceFileDiagnostics(); ASTContext &FromCtx = FromTU.Unit->getASTContext(); createVirtualFileIfNeeded(ToAST.get(), InputFileName, FromTU.Code); IdentifierInfo *ImportedII = &FromCtx.Idents.get(Identifier); assert(ImportedII && "Declaration with the given identifier " "should be specified in test!"); DeclarationName ImportDeclName(ImportedII); SmallVector FoundDecls; FromCtx.getTranslationUnitDecl()->localUncachedLookup(ImportDeclName, FoundDecls); assert(FoundDecls.size() == 1); Decl *Imported = FromTU.import(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( std::find_if(FromTUs.begin(), FromTUs.end(), [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); ToCode = ToSrcCode; assert(!ToAST); ToAST = tooling::buildASTFromCodeWithArgs(ToCode, ToArgs, OutputFileName); ToAST->enableSourceFileDiagnostics(); 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); TU *FromTU = findFromTU(From); return FromTU->import(ToAST.get(), From); } QualType ImportType(QualType FromType, Decl *TUDecl, Language ToLang) { lazyInitToAST(ToLang); TU *FromTU = findFromTU(TUDecl); return FromTU->import(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(); } } }; struct ImportExpr : TestImportBase {}; struct ImportType : TestImportBase {}; struct ImportDecl : TestImportBase {}; struct CanonicalRedeclChain : ASTImporterTestBase {}; TEST_P(CanonicalRedeclChain, ShouldBeConsequentWithMatchers) { Decl *FromTU = getTuDecl("void f();", Lang_CXX); auto Pattern = functionDecl(hasName("f")); auto *D0 = FirstDeclMatcher().match(FromTU, Pattern); auto Redecls = getCanonicalForwardRedeclChain(D0); ASSERT_EQ(Redecls.size(), 1u); EXPECT_EQ(D0, Redecls[0]); } TEST_P(CanonicalRedeclChain, ShouldBeConsequentWithMatchers2) { Decl *FromTU = getTuDecl("void f(); void f(); void f();", Lang_CXX); auto Pattern = functionDecl(hasName("f")); auto *D0 = FirstDeclMatcher().match(FromTU, Pattern); auto *D2 = LastDeclMatcher().match(FromTU, Pattern); FunctionDecl *D1 = D2->getPreviousDecl(); auto Redecls = getCanonicalForwardRedeclChain(D0); ASSERT_EQ(Redecls.size(), 3u); EXPECT_EQ(D0, Redecls[0]); EXPECT_EQ(D1, Redecls[1]); EXPECT_EQ(D2, Redecls[2]); } TEST_P(CanonicalRedeclChain, ShouldBeSameForAllDeclInTheChain) { Decl *FromTU = getTuDecl("void f(); void f(); void f();", Lang_CXX); auto Pattern = functionDecl(hasName("f")); auto *D0 = FirstDeclMatcher().match(FromTU, Pattern); auto *D2 = LastDeclMatcher().match(FromTU, Pattern); FunctionDecl *D1 = D2->getPreviousDecl(); auto RedeclsD0 = getCanonicalForwardRedeclChain(D0); auto RedeclsD1 = getCanonicalForwardRedeclChain(D1); auto RedeclsD2 = getCanonicalForwardRedeclChain(D2); EXPECT_THAT(RedeclsD0, ::testing::ContainerEq(RedeclsD1)); EXPECT_THAT(RedeclsD1, ::testing::ContainerEq(RedeclsD2)); } TEST_P(ImportExpr, ImportStringLiteral) { MatchVerifier Verifier; testImport( "void declToImport() { (void)\"foo\"; }", Lang_CXX, "", Lang_CXX, Verifier, functionDecl(hasDescendant( stringLiteral(hasType(asString("const char [4]")))))); testImport( "void declToImport() { (void)L\"foo\"; }", Lang_CXX, "", Lang_CXX, Verifier, functionDecl(hasDescendant( stringLiteral(hasType(asString("const wchar_t [4]")))))); testImport( "void declToImport() { (void) \"foo\" \"bar\"; }", Lang_CXX, "", Lang_CXX, Verifier, functionDecl(hasDescendant( stringLiteral(hasType(asString("const char [7]")))))); } TEST_P(ImportExpr, ImportGNUNullExpr) { MatchVerifier Verifier; testImport( "void declToImport() { (void)__null; }", Lang_CXX, "", Lang_CXX, Verifier, functionDecl(hasDescendant(gnuNullExpr(hasType(isInteger()))))); } TEST_P(ImportExpr, ImportCXXNullPtrLiteralExpr) { MatchVerifier Verifier; testImport( "void declToImport() { (void)nullptr; }", Lang_CXX11, "", Lang_CXX11, Verifier, functionDecl(hasDescendant(cxxNullPtrLiteralExpr()))); } TEST_P(ImportExpr, ImportFloatinglLiteralExpr) { MatchVerifier Verifier; testImport( "void declToImport() { (void)1.0; }", Lang_C, "", Lang_C, Verifier, functionDecl(hasDescendant( floatLiteral(equals(1.0), hasType(asString("double")))))); testImport( "void declToImport() { (void)1.0e-5f; }", Lang_C, "", Lang_C, Verifier, functionDecl(hasDescendant( floatLiteral(equals(1.0e-5f), hasType(asString("float")))))); } TEST_P(ImportExpr, ImportCompoundLiteralExpr) { MatchVerifier Verifier; testImport( "void declToImport() {" " struct s { int x; long y; unsigned z; }; " " (void)(struct s){ 42, 0L, 1U }; }", Lang_CXX, "", Lang_CXX, Verifier, functionDecl(hasDescendant( compoundLiteralExpr( hasType(asString("struct s")), has(initListExpr( hasType(asString("struct s")), has(integerLiteral( equals(42), hasType(asString("int")))), has(integerLiteral( equals(0), hasType(asString("long")))), has(integerLiteral( equals(1), hasType(asString("unsigned int")))))))))); } TEST_P(ImportExpr, ImportCXXThisExpr) { MatchVerifier Verifier; testImport( "class declToImport { void f() { (void)this; } };", Lang_CXX, "", Lang_CXX, Verifier, cxxRecordDecl( hasMethod( hasDescendant( cxxThisExpr( hasType( asString("class declToImport *"))))))); } TEST_P(ImportExpr, ImportAtomicExpr) { MatchVerifier Verifier; testImport( "void declToImport() { int *ptr; __atomic_load_n(ptr, 1); }", Lang_C, "", Lang_C, Verifier, functionDecl(hasDescendant( atomicExpr( has(ignoringParenImpCasts( declRefExpr(hasDeclaration(varDecl(hasName("ptr"))), hasType(asString("int *"))))), has(integerLiteral(equals(1), hasType(asString("int")))))))); } TEST_P(ImportExpr, ImportLabelDeclAndAddrLabelExpr) { MatchVerifier Verifier; testImport( "void declToImport() { loop: goto loop; (void)&&loop; }", Lang_C, "", Lang_C, Verifier, functionDecl( hasDescendant( labelStmt(hasDeclaration(labelDecl(hasName("loop"))))), hasDescendant( addrLabelExpr(hasDeclaration(labelDecl(hasName("loop"))))))); } AST_MATCHER_P(TemplateDecl, hasTemplateDecl, internal::Matcher, InnerMatcher) { const NamedDecl *Template = Node.getTemplatedDecl(); return Template && InnerMatcher.matches(*Template, Finder, Builder); } TEST_P(ImportExpr, ImportParenListExpr) { MatchVerifier Verifier; testImport( "template class dummy { void f() { dummy X(*this); } };" "typedef dummy declToImport;" "template class dummy;", Lang_CXX, "", Lang_CXX, Verifier, typedefDecl(hasType(templateSpecializationType( hasDeclaration(classTemplateSpecializationDecl(hasSpecializedTemplate( classTemplateDecl(hasTemplateDecl(cxxRecordDecl(hasMethod(allOf( hasName("f"), hasBody(compoundStmt(has(declStmt(hasSingleDecl( varDecl(hasInitializer(parenListExpr(has(unaryOperator( hasOperatorName("*"), hasUnaryOperand(cxxThisExpr()))))))))))))))))))))))); } TEST_P(ImportExpr, ImportSwitch) { MatchVerifier Verifier; testImport( "void declToImport() { int b; switch (b) { case 1: break; } }", Lang_C, "", Lang_C, Verifier, functionDecl(hasDescendant( switchStmt(has(compoundStmt(has(caseStmt()))))))); } TEST_P(ImportExpr, ImportStmtExpr) { MatchVerifier Verifier; // NOTE: has() ignores implicit casts, using hasDescendant() to match it testImport( "void declToImport() { int b; int a = b ?: 1; int C = ({int X=4; X;}); }", Lang_C, "", Lang_C, Verifier, functionDecl(hasDescendant( varDecl( hasName("C"), hasType(asString("int")), hasInitializer( stmtExpr( hasAnySubstatement(declStmt(hasSingleDecl( varDecl( hasName("X"), hasType(asString("int")), hasInitializer( integerLiteral(equals(4))))))), hasDescendant( implicitCastExpr()))))))); } TEST_P(ImportExpr, ImportConditionalOperator) { MatchVerifier Verifier; testImport( "void declToImport() { (void)(true ? 1 : -5); }", Lang_CXX, "", Lang_CXX, Verifier, functionDecl(hasDescendant( conditionalOperator( hasCondition(cxxBoolLiteral(equals(true))), hasTrueExpression(integerLiteral(equals(1))), hasFalseExpression( unaryOperator(hasUnaryOperand(integerLiteral(equals(5)))))) ))); } TEST_P(ImportExpr, ImportBinaryConditionalOperator) { MatchVerifier Verifier; testImport( "void declToImport() { (void)(1 ?: -5); }", Lang_CXX, "", Lang_CXX, Verifier, functionDecl(hasDescendant( binaryConditionalOperator( hasCondition( implicitCastExpr( hasSourceExpression(opaqueValueExpr( hasSourceExpression(integerLiteral(equals(1))))), hasType(booleanType()))), hasTrueExpression( opaqueValueExpr( hasSourceExpression(integerLiteral(equals(1))))), hasFalseExpression( unaryOperator( hasOperatorName("-"), hasUnaryOperand(integerLiteral(equals(5))))))))); } TEST_P(ImportExpr, ImportDesignatedInitExpr) { MatchVerifier Verifier; testImport( "void declToImport() {" " struct point { double x; double y; };" " struct point ptarray[10] = " "{ [2].y = 1.0, [2].x = 2.0, [0].x = 1.0 }; }", Lang_C, "", Lang_C, Verifier, functionDecl(hasDescendant( initListExpr( has(designatedInitExpr( designatorCountIs(2), has(floatLiteral(equals(1.0))), has(integerLiteral(equals(2))))), has(designatedInitExpr( designatorCountIs(2), has(floatLiteral(equals(2.0))), has(integerLiteral(equals(2))))), has(designatedInitExpr( designatorCountIs(2), has(floatLiteral(equals(1.0))), has(integerLiteral(equals(0))))))))); } TEST_P(ImportExpr, ImportPredefinedExpr) { MatchVerifier Verifier; // __func__ expands as StringLiteral("declToImport") testImport( "void declToImport() { (void)__func__; }", Lang_CXX, "", Lang_CXX, Verifier, functionDecl(hasDescendant( predefinedExpr( hasType( asString("const char [13]")), has(stringLiteral(hasType( asString("const char [13]")))))))); } TEST_P(ImportExpr, ImportInitListExpr) { MatchVerifier Verifier; testImport( "void declToImport() {" " struct point { double x; double y; };" " point ptarray[10] = { [2].y = 1.0, [2].x = 2.0," " [0].x = 1.0 }; }", Lang_CXX, "", Lang_CXX, Verifier, functionDecl(hasDescendant( initListExpr( has( cxxConstructExpr( requiresZeroInitialization())), has( initListExpr( hasType(asString("struct point")), has(floatLiteral(equals(1.0))), has(implicitValueInitExpr( hasType(asString("double")))))), has( initListExpr( hasType(asString("struct point")), has(floatLiteral(equals(2.0))), has(floatLiteral(equals(1.0))))))))); } const internal::VariadicDynCastAllOfMatcher vaArgExpr; TEST_P(ImportExpr, ImportVAArgExpr) { MatchVerifier Verifier; testImport( "void declToImport(__builtin_va_list list, ...) {" " (void)__builtin_va_arg(list, int); }", Lang_CXX, "", Lang_CXX, Verifier, functionDecl(hasDescendant( cStyleCastExpr(hasSourceExpression(vaArgExpr()))))); } TEST_P(ImportExpr, CXXTemporaryObjectExpr) { MatchVerifier Verifier; testImport( "struct C {};" "void declToImport() { C c = C(); }", Lang_CXX, "", Lang_CXX, Verifier, functionDecl(hasDescendant( exprWithCleanups(has(cxxConstructExpr( has(materializeTemporaryExpr(has(implicitCastExpr( has(cxxTemporaryObjectExpr()))))))))))); } TEST_P(ImportType, ImportAtomicType) { MatchVerifier Verifier; testImport( "void declToImport() { typedef _Atomic(int) a_int; }", Lang_CXX11, "", Lang_CXX11, Verifier, functionDecl(hasDescendant(typedefDecl(has(atomicType()))))); } TEST_P(ImportDecl, ImportFunctionTemplateDecl) { MatchVerifier Verifier; testImport( "template void declToImport() { };", Lang_CXX, "", Lang_CXX, Verifier, functionTemplateDecl()); } const internal::VariadicDynCastAllOfMatcher cxxDependentScopeMemberExpr; TEST_P(ImportExpr, ImportCXXDependentScopeMemberExpr) { MatchVerifier Verifier; testImport( "template struct C { T t; };" "template void declToImport() {" " C d;" " (void)d.t;" "}" "void instantiate() { declToImport(); }", Lang_CXX, "", Lang_CXX, Verifier, functionTemplateDecl(hasDescendant( cStyleCastExpr(has(cxxDependentScopeMemberExpr()))))); testImport( "template struct C { T t; };" "template void declToImport() {" " C d;" " (void)(&d)->t;" "}" "void instantiate() { declToImport(); }", Lang_CXX, "", Lang_CXX, Verifier, functionTemplateDecl(hasDescendant( cStyleCastExpr(has(cxxDependentScopeMemberExpr()))))); } TEST_P(ImportType, ImportTypeAliasTemplate) { MatchVerifier Verifier; testImport( "template " "struct dummy { static const int i = K; };" "template using dummy2 = dummy;" "int declToImport() { return dummy2<3>::i; }", Lang_CXX11, "", Lang_CXX11, Verifier, functionDecl( hasDescendant(implicitCastExpr(has(declRefExpr()))), unless(hasAncestor(translationUnitDecl(has(typeAliasDecl())))))); } const internal::VariadicDynCastAllOfMatcher varTemplateSpecializationDecl; TEST_P(ImportDecl, ImportVarTemplate) { MatchVerifier Verifier; testImport( "template " "T pi = T(3.1415926535897932385L);" "void declToImport() { (void)pi; }", Lang_CXX14, "", Lang_CXX14, Verifier, functionDecl( hasDescendant(declRefExpr(to(varTemplateSpecializationDecl()))), unless(hasAncestor(translationUnitDecl(has(varDecl( hasName("pi"), unless(varTemplateSpecializationDecl())))))))); } TEST_P(ImportType, ImportPackExpansion) { MatchVerifier Verifier; testImport( "template " "struct dummy {" " dummy(Args... args) {}" " static const int i = 4;" "};" "int declToImport() { return dummy::i; }", Lang_CXX11, "", Lang_CXX11, Verifier, functionDecl(hasDescendant( returnStmt(has(implicitCastExpr(has(declRefExpr()))))))); } const internal::VariadicDynCastAllOfMatcher dependentTemplateSpecializationType; TEST_P(ImportType, ImportDependentTemplateSpecialization) { MatchVerifier Verifier; testImport( "template" "struct A;" "template" "struct declToImport {" " typename A::template B a;" "};", Lang_CXX, "", Lang_CXX, Verifier, classTemplateDecl(has(cxxRecordDecl(has( fieldDecl(hasType(dependentTemplateSpecializationType()))))))); } const internal::VariadicDynCastAllOfMatcher sizeOfPackExpr; TEST_P(ImportExpr, ImportSizeOfPackExpr) { MatchVerifier Verifier; testImport( "template " "void declToImport() {" " const int i = sizeof...(Ts);" "};" "void g() { declToImport(); }", Lang_CXX11, "", Lang_CXX11, Verifier, functionTemplateDecl(hasDescendant(sizeOfPackExpr()))); testImport( "template " "using X = int[sizeof...(Ts)];" "template " "struct Y {" " X f;" "};" "Y declToImport;", Lang_CXX11, "", Lang_CXX11, Verifier, varDecl(hasType(classTemplateSpecializationDecl(has(fieldDecl(hasType( hasUnqualifiedDesugaredType(constantArrayType(hasSize(7)))))))))); } /// \brief Matches __builtin_types_compatible_p: /// GNU extension to check equivalent types /// Given /// \code /// __builtin_types_compatible_p(int, int) /// \endcode // will generate TypeTraitExpr <...> 'int' const internal::VariadicDynCastAllOfMatcher typeTraitExpr; TEST_P(ImportExpr, ImportTypeTraitExpr) { MatchVerifier Verifier; testImport( "void declToImport() { " " (void)__builtin_types_compatible_p(int, int);" "}", Lang_C, "", Lang_C, Verifier, functionDecl(hasDescendant(typeTraitExpr(hasType(asString("int")))))); } const internal::VariadicDynCastAllOfMatcher cxxTypeidExpr; TEST_P(ImportExpr, ImportCXXTypeidExpr) { MatchVerifier Verifier; testImport( "namespace std { class type_info {}; }" "void declToImport() {" " int x;" " auto a = typeid(int); auto b = typeid(x);" "}", Lang_CXX11, "", Lang_CXX11, Verifier, functionDecl( hasDescendant(varDecl( hasName("a"), hasInitializer(hasDescendant(cxxTypeidExpr())))), hasDescendant(varDecl( hasName("b"), hasInitializer(hasDescendant(cxxTypeidExpr())))))); } TEST_P(ImportExpr, ImportTypeTraitExprValDep) { MatchVerifier Verifier; testImport( "template struct declToImport {" " void m() { (void)__is_pod(T); }" "};" "void f() { declToImport().m(); }", Lang_CXX11, "", Lang_CXX11, Verifier, classTemplateDecl(has(cxxRecordDecl(has( functionDecl(hasDescendant( typeTraitExpr(hasType(booleanType()))))))))); } TEST_P(ImportDecl, ImportRecordDeclInFunc) { MatchVerifier Verifier; testImport("int declToImport() { " " struct data_t {int a;int b;};" " struct data_t d;" " return 0;" "}", Lang_C, "", Lang_C, Verifier, functionDecl(hasBody(compoundStmt( has(declStmt(hasSingleDecl(varDecl(hasName("d"))))))))); } TEST_P(ASTImporterTestBase, ImportRecordTypeInFunc) { Decl *FromTU = getTuDecl("int declToImport() { " " struct data_t {int a;int b;};" " struct data_t d;" " return 0;" "}", Lang_C, "input.c"); auto FromVar = FirstDeclMatcher().match(FromTU, varDecl(hasName("d"))); ASSERT_TRUE(FromVar); auto ToType = ImportType(FromVar->getType().getCanonicalType(), FromVar, Lang_C); EXPECT_FALSE(ToType.isNull()); } TEST_P(ASTImporterTestBase, ImportRecordDeclInFuncParams) { // This construct is not supported by ASTImporter. Decl *FromTU = getTuDecl("int declToImport(struct data_t{int a;int b;} *d){ return 0; }", Lang_C, "input.c"); auto From = FirstDeclMatcher().match(FromTU, functionDecl()); ASSERT_TRUE(From); auto To = Import(From, Lang_C); EXPECT_EQ(To, nullptr); } const internal::VariadicDynCastAllOfMatcher cxxPseudoDestructorExpr; TEST_P(ImportExpr, ImportCXXPseudoDestructorExpr) { MatchVerifier Verifier; testImport( "typedef int T;" "void declToImport(int *p) {" " T t;" " p->T::~T();" "}", Lang_CXX, "", Lang_CXX, Verifier, functionDecl(hasDescendant( callExpr(has(cxxPseudoDestructorExpr()))))); } TEST_P(ImportDecl, ImportUsingDecl) { MatchVerifier Verifier; testImport( "namespace foo { int bar; }" "void declToImport() { using foo::bar; }", Lang_CXX, "", Lang_CXX, Verifier, functionDecl(hasDescendant(usingDecl()))); } /// \brief Matches shadow declarations introduced into a scope by a /// (resolved) using declaration. /// /// Given /// \code /// namespace n { int f; } /// namespace declToImport { using n::f; } /// \endcode /// usingShadowDecl() /// matches \code f \endcode const internal::VariadicDynCastAllOfMatcher usingShadowDecl; TEST_P(ImportDecl, ImportUsingShadowDecl) { MatchVerifier Verifier; testImport( "namespace foo { int bar; }" "namespace declToImport { using foo::bar; }", Lang_CXX, "", Lang_CXX, Verifier, namespaceDecl(has(usingShadowDecl()))); } TEST_P(ImportExpr, ImportUnresolvedLookupExpr) { MatchVerifier Verifier; testImport( "template int foo();" "template void declToImport() {" " (void)::foo;" " (void)::template foo;" "}" "void instantiate() { declToImport(); }", Lang_CXX, "", Lang_CXX, Verifier, functionTemplateDecl(hasDescendant(unresolvedLookupExpr()))); } TEST_P(ImportExpr, ImportCXXUnresolvedConstructExpr) { MatchVerifier Verifier; testImport( "template struct C { T t; };" "template void declToImport() {" " C d;" " d.t = T();" "}" "void instantiate() { declToImport(); }", Lang_CXX, "", Lang_CXX, Verifier, functionTemplateDecl(hasDescendant( binaryOperator(has(cxxUnresolvedConstructExpr()))))); testImport( "template struct C { T t; };" "template void declToImport() {" " C d;" " (&d)->t = T();" "}" "void instantiate() { declToImport(); }", Lang_CXX, "", Lang_CXX, Verifier, functionTemplateDecl(hasDescendant( binaryOperator(has(cxxUnresolvedConstructExpr()))))); } /// Check that function "declToImport()" (which is the templated function /// for corresponding FunctionTemplateDecl) is not added into DeclContext. /// Same for class template declarations. TEST_P(ImportDecl, ImportTemplatedDeclForTemplate) { MatchVerifier Verifier; testImport( "template void declToImport() { T a = 1; }" "void instantiate() { declToImport(); }", Lang_CXX, "", Lang_CXX, Verifier, functionTemplateDecl(hasAncestor(translationUnitDecl( unless(has(functionDecl(hasName("declToImport")))))))); testImport( "template struct declToImport { T t; };" "void instantiate() { declToImport(); }", Lang_CXX, "", Lang_CXX, Verifier, classTemplateDecl(hasAncestor(translationUnitDecl( unless(has(cxxRecordDecl(hasName("declToImport")))))))); } TEST_P(ImportDecl, ImportClassTemplatePartialSpecialization) { MatchVerifier Verifier; auto Code = R"s( struct declToImport { template struct X; template struct X {}; }; )s"; testImport(Code, Lang_CXX, "", Lang_CXX, Verifier, recordDecl(has(classTemplateDecl()), has(classTemplateSpecializationDecl()))); } TEST_P(ImportExpr, CXXOperatorCallExpr) { MatchVerifier Verifier; testImport( "class declToImport {" " void f() { *this = declToImport(); }" "};", Lang_CXX, "", Lang_CXX, Verifier, cxxRecordDecl(has(cxxMethodDecl(hasDescendant( cxxOperatorCallExpr()))))); } TEST_P(ImportExpr, DependentSizedArrayType) { MatchVerifier Verifier; testImport( "template class declToImport {" " T data[Size];" "};", Lang_CXX, "", Lang_CXX, Verifier, classTemplateDecl(has(cxxRecordDecl( has(fieldDecl(hasType(dependentSizedArrayType()))))))); } TEST_P(ASTImporterTestBase, ImportOfTemplatedDeclOfClassTemplateDecl) { Decl *FromTU = getTuDecl("template struct S{};", Lang_CXX); auto From = FirstDeclMatcher().match(FromTU, classTemplateDecl()); ASSERT_TRUE(From); auto To = cast(Import(From, Lang_CXX)); ASSERT_TRUE(To); Decl *ToTemplated = To->getTemplatedDecl(); Decl *ToTemplated1 = Import(From->getTemplatedDecl(), Lang_CXX); EXPECT_TRUE(ToTemplated1); EXPECT_EQ(ToTemplated1, ToTemplated); } TEST_P(ASTImporterTestBase, ImportOfTemplatedDeclOfFunctionTemplateDecl) { Decl *FromTU = getTuDecl("template void f(){}", Lang_CXX); auto From = FirstDeclMatcher().match( FromTU, functionTemplateDecl()); ASSERT_TRUE(From); auto To = cast(Import(From, Lang_CXX)); ASSERT_TRUE(To); Decl *ToTemplated = To->getTemplatedDecl(); Decl *ToTemplated1 = Import(From->getTemplatedDecl(), Lang_CXX); EXPECT_TRUE(ToTemplated1); EXPECT_EQ(ToTemplated1, ToTemplated); } TEST_P(ASTImporterTestBase, ImportOfTemplatedDeclShouldImportTheClassTemplateDecl) { Decl *FromTU = getTuDecl("template struct S{};", Lang_CXX); auto FromFT = FirstDeclMatcher().match(FromTU, classTemplateDecl()); ASSERT_TRUE(FromFT); auto ToTemplated = cast(Import(FromFT->getTemplatedDecl(), Lang_CXX)); EXPECT_TRUE(ToTemplated); auto ToTU = ToTemplated->getTranslationUnitDecl(); auto ToFT = FirstDeclMatcher().match(ToTU, classTemplateDecl()); EXPECT_TRUE(ToFT); } TEST_P(ASTImporterTestBase, ImportOfTemplatedDeclShouldImportTheFunctionTemplateDecl) { Decl *FromTU = getTuDecl("template void f(){}", Lang_CXX); auto FromFT = FirstDeclMatcher().match( FromTU, functionTemplateDecl()); ASSERT_TRUE(FromFT); auto ToTemplated = cast(Import(FromFT->getTemplatedDecl(), Lang_CXX)); EXPECT_TRUE(ToTemplated); auto ToTU = ToTemplated->getTranslationUnitDecl(); auto ToFT = FirstDeclMatcher().match( ToTU, functionTemplateDecl()); EXPECT_TRUE(ToFT); } TEST_P(ASTImporterTestBase, ImportCorrectTemplatedDecl) { auto Code = R"( namespace x { template struct S1{}; template struct S2{}; template struct S3{}; } )"; Decl *FromTU = getTuDecl(Code, Lang_CXX); auto FromNs = FirstDeclMatcher().match(FromTU, namespaceDecl()); auto ToNs = cast(Import(FromNs, Lang_CXX)); ASSERT_TRUE(ToNs); auto From = FirstDeclMatcher().match(FromTU, classTemplateDecl( hasName("S2"))); auto To = FirstDeclMatcher().match(ToNs, classTemplateDecl( hasName("S2"))); ASSERT_TRUE(From); ASSERT_TRUE(To); auto ToTemplated = To->getTemplatedDecl(); auto ToTemplated1 = cast(Import(From->getTemplatedDecl(), Lang_CXX)); EXPECT_TRUE(ToTemplated1); ASSERT_EQ(ToTemplated1, ToTemplated); } TEST_P(ASTImporterTestBase, ImportFunctionWithBackReferringParameter) { Decl *From, *To; std::tie(From, To) = getImportedDecl( R"( template struct X {}; void declToImport(int y, X &x) {} template <> struct X { void g() { X x; declToImport(0, x); } }; )", Lang_CXX, "", Lang_CXX); MatchVerifier Verifier; auto Matcher = functionDecl(hasName("declToImport"), parameterCountIs(2), hasParameter(0, hasName("y")), hasParameter(1, hasName("x")), hasParameter(1, hasType(asString("X &")))); ASSERT_TRUE(Verifier.match(From, Matcher)); EXPECT_TRUE(Verifier.match(To, Matcher)); } TEST_P(ASTImporterTestBase, TUshouldNotContainTemplatedDeclOfFunctionTemplates) { Decl *From, *To; std::tie(From, To) = getImportedDecl("template void declToImport() { T a = 1; }" "void instantiate() { declToImport(); }", Lang_CXX, "", Lang_CXX); auto Check = [](Decl *D) -> bool { auto TU = D->getTranslationUnitDecl(); for (auto Child : TU->decls()) { if (auto *FD = dyn_cast(Child)) { if (FD->getNameAsString() == "declToImport") { GTEST_NONFATAL_FAILURE_( "TU should not contain any FunctionDecl with name declToImport"); return false; } } } return true; }; ASSERT_TRUE(Check(From)); EXPECT_TRUE(Check(To)); } TEST_P(ASTImporterTestBase, TUshouldNotContainTemplatedDeclOfClassTemplates) { Decl *From, *To; std::tie(From, To) = getImportedDecl("template struct declToImport { T t; };" "void instantiate() { declToImport(); }", Lang_CXX, "", Lang_CXX); auto Check = [](Decl *D) -> bool { auto TU = D->getTranslationUnitDecl(); for (auto Child : TU->decls()) { if (auto *RD = dyn_cast(Child)) { if (RD->getNameAsString() == "declToImport") { GTEST_NONFATAL_FAILURE_( "TU should not contain any CXXRecordDecl with name declToImport"); return false; } } } return true; }; ASSERT_TRUE(Check(From)); EXPECT_TRUE(Check(To)); } TEST_P(ASTImporterTestBase, TUshouldNotContainTemplatedDeclOfTypeAlias) { Decl *From, *To; std::tie(From, To) = getImportedDecl( "template struct X {};" "template using declToImport = X;" "void instantiate() { declToImport a; }", Lang_CXX11, "", Lang_CXX11); auto Check = [](Decl *D) -> bool { auto TU = D->getTranslationUnitDecl(); for (auto Child : TU->decls()) { if (auto *AD = dyn_cast(Child)) { if (AD->getNameAsString() == "declToImport") { GTEST_NONFATAL_FAILURE_( "TU should not contain any TypeAliasDecl with name declToImport"); return false; } } } return true; }; ASSERT_TRUE(Check(From)); EXPECT_TRUE(Check(To)); } TEST_P( ASTImporterTestBase, TUshouldNotContainClassTemplateSpecializationOfImplicitInstantiation) { Decl *From, *To; std::tie(From, To) = getImportedDecl( R"( template class Base {}; class declToImport : public Base {}; )", Lang_CXX, "", Lang_CXX); // Check that the ClassTemplateSpecializationDecl is NOT the child of the TU. auto Pattern = translationUnitDecl(unless(has(classTemplateSpecializationDecl()))); ASSERT_TRUE( MatchVerifier{}.match(From->getTranslationUnitDecl(), Pattern)); EXPECT_TRUE( MatchVerifier{}.match(To->getTranslationUnitDecl(), Pattern)); // Check that the ClassTemplateSpecializationDecl is the child of the // ClassTemplateDecl. Pattern = translationUnitDecl(has(classTemplateDecl( hasName("Base"), has(classTemplateSpecializationDecl())))); ASSERT_TRUE( MatchVerifier{}.match(From->getTranslationUnitDecl(), Pattern)); EXPECT_TRUE( MatchVerifier{}.match(To->getTranslationUnitDecl(), Pattern)); } AST_MATCHER_P(RecordDecl, hasFieldOrder, std::vector, Order) { size_t Index = 0; for (FieldDecl *Field : Node.fields()) { if (Index == Order.size()) return false; if (Field->getName() != Order[Index]) return false; ++Index; } return Index == Order.size(); } TEST_P(ASTImporterTestBase, TUshouldContainClassTemplateSpecializationOfExplicitInstantiation) { Decl *From, *To; std::tie(From, To) = getImportedDecl( R"( namespace NS { template class X {}; template class X; } )", Lang_CXX, "", Lang_CXX, "NS"); // Check that the ClassTemplateSpecializationDecl is NOT the child of the // ClassTemplateDecl. auto Pattern = namespaceDecl(has(classTemplateDecl( hasName("X"), unless(has(classTemplateSpecializationDecl()))))); ASSERT_TRUE(MatchVerifier{}.match(From, Pattern)); EXPECT_TRUE(MatchVerifier{}.match(To, Pattern)); // Check that the ClassTemplateSpecializationDecl is the child of the // NamespaceDecl. Pattern = namespaceDecl(has(classTemplateSpecializationDecl(hasName("X")))); ASSERT_TRUE(MatchVerifier{}.match(From, Pattern)); EXPECT_TRUE(MatchVerifier{}.match(To, Pattern)); } TEST_P(ASTImporterTestBase, CXXRecordDeclFieldsShouldBeInCorrectOrder) { Decl *From, *To; std::tie(From, To) = getImportedDecl( "struct declToImport { int a; int b; };", Lang_CXX11, "", Lang_CXX11); MatchVerifier Verifier; ASSERT_TRUE(Verifier.match(From, cxxRecordDecl(hasFieldOrder({"a", "b"})))); EXPECT_TRUE(Verifier.match(To, cxxRecordDecl(hasFieldOrder({"a", "b"})))); } TEST_P(ASTImporterTestBase, DISABLED_CXXRecordDeclFieldOrderShouldNotDependOnImportOrder) { Decl *From, *To; std::tie(From, To) = getImportedDecl( // The original recursive algorithm of ASTImporter first imports 'c' then // 'b' and lastly 'a'. Therefore we must restore the order somehow. R"s( struct declToImport { int a = c + b; int b = 1; int c = 2; }; )s", Lang_CXX11, "", Lang_CXX11); MatchVerifier Verifier; ASSERT_TRUE( Verifier.match(From, cxxRecordDecl(hasFieldOrder({"a", "b", "c"})))); EXPECT_TRUE( Verifier.match(To, cxxRecordDecl(hasFieldOrder({"a", "b", "c"})))); } TEST_P(ASTImporterTestBase, ShouldImportImplicitCXXRecordDecl) { Decl *From, *To; std::tie(From, To) = getImportedDecl( R"( struct declToImport { }; )", Lang_CXX, "", Lang_CXX); MatchVerifier Verifier; // Match the implicit Decl. auto Matcher = cxxRecordDecl(has(cxxRecordDecl())); ASSERT_TRUE(Verifier.match(From, Matcher)); EXPECT_TRUE(Verifier.match(To, Matcher)); } TEST_P(ASTImporterTestBase, ShouldImportImplicitCXXRecordDeclOfClassTemplate) { Decl *From, *To; std::tie(From, To) = getImportedDecl( R"( template struct declToImport { }; )", Lang_CXX, "", Lang_CXX); MatchVerifier Verifier; // Match the implicit Decl. auto Matcher = classTemplateDecl(has(cxxRecordDecl(has(cxxRecordDecl())))); ASSERT_TRUE(Verifier.match(From, Matcher)); EXPECT_TRUE(Verifier.match(To, Matcher)); } TEST_P( ASTImporterTestBase, ShouldImportImplicitCXXRecordDeclOfClassTemplateSpecializationDecl) { Decl *From, *To; std::tie(From, To) = getImportedDecl( R"( template class Base {}; class declToImport : public Base {}; )", Lang_CXX, "", Lang_CXX); auto hasImplicitClass = has(cxxRecordDecl()); auto Pattern = translationUnitDecl(has(classTemplateDecl( hasName("Base"), has(classTemplateSpecializationDecl(hasImplicitClass))))); ASSERT_TRUE( MatchVerifier{}.match(From->getTranslationUnitDecl(), Pattern)); EXPECT_TRUE( MatchVerifier{}.match(To->getTranslationUnitDecl(), Pattern)); } TEST_P(ASTImporterTestBase, IDNSOrdinary) { Decl *From, *To; std::tie(From, To) = getImportedDecl("void declToImport() {}", Lang_CXX, "", Lang_CXX); MatchVerifier Verifier; auto Matcher = functionDecl(); ASSERT_TRUE(Verifier.match(From, Matcher)); EXPECT_TRUE(Verifier.match(To, Matcher)); EXPECT_EQ(From->getIdentifierNamespace(), To->getIdentifierNamespace()); } TEST_P(ASTImporterTestBase, IDNSOfNonmemberOperator) { Decl *FromTU = getTuDecl( R"( struct X {}; void operator<<(int, X); )", Lang_CXX); Decl *From = LastDeclMatcher{}.match(FromTU, functionDecl()); const Decl *To = Import(From, Lang_CXX); EXPECT_EQ(From->getIdentifierNamespace(), To->getIdentifierNamespace()); } TEST_P(ASTImporterTestBase, ShouldImportMembersOfClassTemplateSpecializationDecl) { Decl *From, *To; std::tie(From, To) = getImportedDecl( R"( template class Base { int a; }; class declToImport : Base {}; )", Lang_CXX, "", Lang_CXX); auto Pattern = translationUnitDecl(has(classTemplateDecl( hasName("Base"), has(classTemplateSpecializationDecl(has(fieldDecl(hasName("a")))))))); ASSERT_TRUE( MatchVerifier{}.match(From->getTranslationUnitDecl(), Pattern)); EXPECT_TRUE( MatchVerifier{}.match(To->getTranslationUnitDecl(), Pattern)); } TEST_P(ASTImporterTestBase, ImportDefinitionOfClassTemplateAfterFwdDecl) { { Decl *FromTU = getTuDecl( R"( template struct B; )", Lang_CXX, "input0.cc"); auto *FromD = FirstDeclMatcher().match( FromTU, classTemplateDecl(hasName("B"))); Import(FromD, Lang_CXX); } { Decl *FromTU = getTuDecl( R"( template struct B { void f(); }; )", Lang_CXX, "input1.cc"); FunctionDecl *FromD = FirstDeclMatcher().match( FromTU, functionDecl(hasName("f"))); Import(FromD, Lang_CXX); auto *FromCTD = FirstDeclMatcher().match( FromTU, classTemplateDecl(hasName("B"))); auto *ToCTD = cast(Import(FromCTD, Lang_CXX)); EXPECT_TRUE(ToCTD->isThisDeclarationADefinition()); } } TEST_P(ASTImporterTestBase, ImportDefinitionOfClassTemplateIfThereIsAnExistingFwdDeclAndDefinition) { Decl *ToTU = getToTuDecl( R"( template struct B { void f(); }; template struct B; )", Lang_CXX); ASSERT_EQ(1u, DeclCounterWithPredicate( [](const ClassTemplateDecl *T) { return T->isThisDeclarationADefinition(); }) .match(ToTU, classTemplateDecl())); Decl *FromTU = getTuDecl( R"( template struct B { void f(); }; )", Lang_CXX, "input1.cc"); ClassTemplateDecl *FromD = FirstDeclMatcher().match( FromTU, classTemplateDecl(hasName("B"))); Import(FromD, Lang_CXX); // We should have only one definition. EXPECT_EQ(1u, DeclCounterWithPredicate( [](const ClassTemplateDecl *T) { return T->isThisDeclarationADefinition(); }) .match(ToTU, classTemplateDecl())); } TEST_P(ASTImporterTestBase, ImportDefinitionOfClassIfThereIsAnExistingFwdDeclAndDefinition) { Decl *ToTU = getToTuDecl( R"( struct B { void f(); }; struct B; )", Lang_CXX); ASSERT_EQ(2u, DeclCounter().match( ToTU, cxxRecordDecl(unless(isImplicit())))); Decl *FromTU = getTuDecl( R"( struct B { void f(); }; )", Lang_CXX, "input1.cc"); auto *FromD = FirstDeclMatcher().match( FromTU, cxxRecordDecl(hasName("B"))); Import(FromD, Lang_CXX); EXPECT_EQ(2u, DeclCounter().match( ToTU, cxxRecordDecl(unless(isImplicit())))); } static void CompareSourceLocs(FullSourceLoc Loc1, FullSourceLoc Loc2) { EXPECT_EQ(Loc1.getExpansionLineNumber(), Loc2.getExpansionLineNumber()); EXPECT_EQ(Loc1.getExpansionColumnNumber(), Loc2.getExpansionColumnNumber()); EXPECT_EQ(Loc1.getSpellingLineNumber(), Loc2.getSpellingLineNumber()); EXPECT_EQ(Loc1.getSpellingColumnNumber(), Loc2.getSpellingColumnNumber()); } static void CompareSourceRanges(SourceRange Range1, SourceRange Range2, SourceManager &SM1, SourceManager &SM2) { CompareSourceLocs(FullSourceLoc{ Range1.getBegin(), SM1 }, FullSourceLoc{ Range2.getBegin(), SM2 }); CompareSourceLocs(FullSourceLoc{ Range1.getEnd(), SM1 }, FullSourceLoc{ Range2.getEnd(), SM2 }); } TEST_P(ASTImporterTestBase, ImportSourceLocs) { Decl *FromTU = getTuDecl( R"( #define MFOO(arg) arg = arg + 1 void foo() { int a = 5; MFOO(a); } )", Lang_CXX); auto FromD = FirstDeclMatcher().match(FromTU, functionDecl()); auto ToD = Import(FromD, Lang_CXX); auto ToLHS = LastDeclMatcher().match(ToD, declRefExpr()); auto FromLHS = LastDeclMatcher().match(FromTU, declRefExpr()); auto ToRHS = LastDeclMatcher().match(ToD, integerLiteral()); auto FromRHS = LastDeclMatcher().match(FromTU, integerLiteral()); SourceManager &ToSM = ToAST->getASTContext().getSourceManager(); SourceManager &FromSM = FromD->getASTContext().getSourceManager(); CompareSourceRanges(ToD->getSourceRange(), FromD->getSourceRange(), ToSM, FromSM); CompareSourceRanges(ToLHS->getSourceRange(), FromLHS->getSourceRange(), ToSM, FromSM); CompareSourceRanges(ToRHS->getSourceRange(), FromRHS->getSourceRange(), ToSM, FromSM); } TEST_P(ASTImporterTestBase, ImportNestedMacro) { Decl *FromTU = getTuDecl( R"( #define FUNC_INT void declToImport #define FUNC FUNC_INT FUNC(int a); )", Lang_CXX); auto FromD = FirstDeclMatcher().match(FromTU, functionDecl()); auto ToD = Import(FromD, Lang_CXX); SourceManager &ToSM = ToAST->getASTContext().getSourceManager(); SourceManager &FromSM = FromD->getASTContext().getSourceManager(); CompareSourceRanges(ToD->getSourceRange(), FromD->getSourceRange(), ToSM, FromSM); } TEST_P( ASTImporterTestBase, ImportDefinitionOfClassTemplateSpecIfThereIsAnExistingFwdDeclAndDefinition) { Decl *ToTU = getToTuDecl( R"( template struct B; template <> struct B {}; template <> struct B; )", Lang_CXX); // We should have only one definition. ASSERT_EQ(1u, DeclCounterWithPredicate( [](const ClassTemplateSpecializationDecl *T) { return T->isThisDeclarationADefinition(); }) .match(ToTU, classTemplateSpecializationDecl())); Decl *FromTU = getTuDecl( R"( template struct B; template <> struct B {}; )", Lang_CXX, "input1.cc"); auto *FromD = FirstDeclMatcher().match( FromTU, classTemplateSpecializationDecl(hasName("B"))); Import(FromD, Lang_CXX); // We should have only one definition. EXPECT_EQ(1u, DeclCounterWithPredicate( [](const ClassTemplateSpecializationDecl *T) { return T->isThisDeclarationADefinition(); }) .match(ToTU, classTemplateSpecializationDecl())); } TEST_P(ASTImporterTestBase, ObjectsWithUnnamedStructType) { Decl *FromTU = getTuDecl( R"( struct { int a; int b; } object0 = { 2, 3 }; struct { int x; int y; int z; } object1; )", Lang_CXX, "input0.cc"); auto *Obj0 = FirstDeclMatcher().match(FromTU, varDecl(hasName("object0"))); auto *From0 = getRecordDecl(Obj0); auto *Obj1 = FirstDeclMatcher().match(FromTU, varDecl(hasName("object1"))); auto *From1 = getRecordDecl(Obj1); auto *To0 = Import(From0, Lang_CXX); auto *To1 = Import(From1, Lang_CXX); EXPECT_TRUE(To0); EXPECT_TRUE(To1); EXPECT_NE(To0, To1); EXPECT_NE(To0->getCanonicalDecl(), To1->getCanonicalDecl()); } TEST_P(ASTImporterTestBase, ImportDoesUpdateUsedFlag) { auto Pattern = varDecl(hasName("x")); VarDecl *Imported1; { Decl *FromTU = getTuDecl("extern int x;", Lang_CXX, "input0.cc"); auto *FromD = FirstDeclMatcher().match(FromTU, Pattern); Imported1 = cast(Import(FromD, Lang_CXX)); } VarDecl *Imported2; { Decl *FromTU = getTuDecl("int x;", Lang_CXX, "input1.cc"); auto *FromD = FirstDeclMatcher().match(FromTU, Pattern); Imported2 = cast(Import(FromD, Lang_CXX)); } EXPECT_EQ(Imported1->getCanonicalDecl(), Imported2->getCanonicalDecl()); EXPECT_FALSE(Imported2->isUsed(false)); { Decl *FromTU = getTuDecl("extern int x; int f() { return x; }", Lang_CXX, "input2.cc"); auto *FromD = FirstDeclMatcher().match(FromTU, functionDecl()); Import(FromD, Lang_CXX); } EXPECT_TRUE(Imported2->isUsed(false)); } TEST_P(ASTImporterTestBase, ReimportWithUsedFlag) { auto Pattern = varDecl(hasName("x")); Decl *FromTU = getTuDecl("int x;", Lang_CXX, "input0.cc"); auto *FromD = FirstDeclMatcher().match(FromTU, Pattern); auto *Imported1 = cast(Import(FromD, Lang_CXX)); ASSERT_FALSE(Imported1->isUsed(false)); FromD->setIsUsed(); auto *Imported2 = cast(Import(FromD, Lang_CXX)); EXPECT_EQ(Imported1, Imported2); 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().match(FromTU, Pattern); Decl *ImportedD = Import(FromD, Lang_CXX); Decl *ToTU = ImportedD->getTranslationUnitDecl(); EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 2u); EXPECT_TRUE(cast(ImportedD)->doesThisDeclarationHaveABody()); } TEST_P(ImportFunctions, DefinitionShouldBeImportedAsADefinition) { Decl *FromTU = getTuDecl("void f() {}", Lang_CXX); auto Pattern = functionDecl(hasName("f")); FunctionDecl *FromD = FirstDeclMatcher().match(FromTU, Pattern); Decl *ImportedD = Import(FromD, Lang_CXX); Decl *ToTU = ImportedD->getTranslationUnitDecl(); EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 1u); EXPECT_TRUE(cast(ImportedD)->doesThisDeclarationHaveABody()); } TEST_P(ImportFunctions, ImportPrototypeOfRecursiveFunction) { Decl *FromTU = getTuDecl("void f(); void f() { f(); }", Lang_CXX); auto Pattern = functionDecl(hasName("f")); auto *From = FirstDeclMatcher().match(FromTU, Pattern); // Proto Decl *ImportedD = Import(From, Lang_CXX); Decl *ToTU = ImportedD->getTranslationUnitDecl(); EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 2u); auto *To0 = FirstDeclMatcher().match(ToTU, Pattern); auto *To1 = LastDeclMatcher().match(ToTU, Pattern); EXPECT_TRUE(ImportedD == To0); EXPECT_FALSE(To0->doesThisDeclarationHaveABody()); EXPECT_TRUE(To1->doesThisDeclarationHaveABody()); EXPECT_EQ(To1->getPreviousDecl(), To0); } TEST_P(ImportFunctions, ImportDefinitionOfRecursiveFunction) { Decl *FromTU = getTuDecl("void f(); void f() { f(); }", Lang_CXX); auto Pattern = functionDecl(hasName("f")); auto *From = LastDeclMatcher().match(FromTU, Pattern); // Def Decl *ImportedD = Import(From, Lang_CXX); Decl *ToTU = ImportedD->getTranslationUnitDecl(); EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 2u); auto *To0 = FirstDeclMatcher().match(ToTU, Pattern); auto *To1 = LastDeclMatcher().match(ToTU, Pattern); EXPECT_TRUE(ImportedD == To1); EXPECT_FALSE(To0->doesThisDeclarationHaveABody()); EXPECT_TRUE(To1->doesThisDeclarationHaveABody()); 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().match(FromTU, Pattern); ImportedD = Import(FromD, Lang_CXX); } { Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input1.cc"); auto *FromD = FirstDeclMatcher().match(FromTU, Pattern); Import(FromD, Lang_CXX); } Decl *ToTU = ImportedD->getTranslationUnitDecl(); EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 2u); auto *To0 = FirstDeclMatcher().match(ToTU, Pattern); auto *To1 = LastDeclMatcher().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().match(FromTU, Pattern); ImportedD = Import(FromD, Lang_CXX); } { Decl *FromTU = getTuDecl("void f(){};", Lang_CXX, "input1.cc"); auto *FromD = FirstDeclMatcher().match(FromTU, Pattern); Import(FromD, Lang_CXX); } Decl *ToTU = ImportedD->getTranslationUnitDecl(); EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 1u); auto *To0 = FirstDeclMatcher().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().match(FromTU, Pattern); ImportedD = Import(FromD, Lang_CXX); } { Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input1.cc"); auto *FromD = FirstDeclMatcher().match(FromTU, Pattern); Import(FromD, Lang_CXX); } Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 2u); auto *To0 = FirstDeclMatcher().match(ToTU, Pattern); auto *To1 = LastDeclMatcher().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().match(FromTU, Pattern); Import(FromD, Lang_CXX); } { Decl *FromTU = getTuDecl("void f(){}", Lang_CXX, "input1.cc"); FunctionDecl *FromD = FirstDeclMatcher().match(FromTU, Pattern); Import(FromD, Lang_CXX); } Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); ASSERT_EQ(DeclCounter().match(ToTU, Pattern), 2u); FunctionDecl *ProtoD = FirstDeclMatcher().match(ToTU, Pattern); EXPECT_FALSE(ProtoD->doesThisDeclarationHaveABody()); FunctionDecl *DefinitionD = LastDeclMatcher().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().match(FromTU, Pattern); Import(FromD, Lang_CXX); } { Decl *FromTU = getTuDecl("void f(); void f(){}", Lang_CXX, "input1.cc"); auto *FromD = FirstDeclMatcher().match(FromTU, Pattern); Import(FromD, Lang_CXX); } Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); ASSERT_EQ(DeclCounter().match(ToTU, Pattern), 3u); FunctionDecl *ProtoD = FirstDeclMatcher().match(ToTU, Pattern); EXPECT_FALSE(ProtoD->doesThisDeclarationHaveABody()); FunctionDecl *DefinitionD = LastDeclMatcher().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"( struct B { virtual void f(); }; void B::f() {} struct D : B { void f(); }; )"; auto Pattern = cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D")))); Decl *FromTU = getTuDecl(Code, Lang_CXX); CXXMethodDecl *Proto = FirstDeclMatcher().match(FromTU, Pattern); ASSERT_EQ(Proto->size_overridden_methods(), 1u); CXXMethodDecl *To = cast(Import(Proto, Lang_CXX)); EXPECT_EQ(To->size_overridden_methods(), 1u); } TEST_P(ImportFunctions, VirtualFlagShouldBePreservedWhenImportingPrototype) { auto Code = R"( struct B { virtual void f(); }; void B::f() {} )"; auto Pattern = cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B")))); Decl *FromTU = getTuDecl(Code, Lang_CXX); CXXMethodDecl *Proto = FirstDeclMatcher().match(FromTU, Pattern); CXXMethodDecl *Def = LastDeclMatcher().match(FromTU, Pattern); ASSERT_TRUE(Proto->isVirtual()); ASSERT_TRUE(Def->isVirtual()); CXXMethodDecl *To = cast(Import(Proto, Lang_CXX)); EXPECT_TRUE(To->isVirtual()); } TEST_P(ImportFunctions, ImportDefinitionIfThereIsAnExistingDefinitionAndFwdDecl) { Decl *ToTU = getToTuDecl( R"( void f() {} void f(); )", Lang_CXX); ASSERT_EQ(1u, DeclCounterWithPredicate([](const FunctionDecl *FD) { return FD->doesThisDeclarationHaveABody(); }).match(ToTU, functionDecl())); Decl *FromTU = getTuDecl("void f() {}", Lang_CXX, "input0.cc"); auto *FromD = FirstDeclMatcher().match(FromTU, functionDecl()); Import(FromD, Lang_CXX); EXPECT_EQ(1u, DeclCounterWithPredicate([](const FunctionDecl *FD) { return FD->doesThisDeclarationHaveABody(); }).match(ToTU, functionDecl())); } struct ImportFriendFunctions : ImportFunctions {}; TEST_P(ImportFriendFunctions, ImportFriendFunctionRedeclChainProto) { auto Pattern = functionDecl(hasName("f")); Decl *FromTU = getTuDecl("struct X { friend void f(); };" "void f();", Lang_CXX, "input0.cc"); auto *FromD = FirstDeclMatcher().match(FromTU, Pattern); auto *ImportedD = cast(Import(FromD, Lang_CXX)); Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); ASSERT_EQ(DeclCounter().match(ToTU, Pattern), 2u); EXPECT_FALSE(ImportedD->doesThisDeclarationHaveABody()); auto *ToFD = LastDeclMatcher().match(ToTU, Pattern); EXPECT_FALSE(ToFD->doesThisDeclarationHaveABody()); EXPECT_EQ(ToFD->getPreviousDecl(), ImportedD); } TEST_P(ImportFriendFunctions, ImportFriendFunctionRedeclChainProto_OutOfClassProtoFirst) { auto Pattern = functionDecl(hasName("f")); Decl *FromTU = getTuDecl("void f();" "struct X { friend void f(); };", Lang_CXX, "input0.cc"); auto FromD = FirstDeclMatcher().match(FromTU, Pattern); auto *ImportedD = cast(Import(FromD, Lang_CXX)); Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); ASSERT_EQ(DeclCounter().match(ToTU, Pattern), 2u); EXPECT_FALSE(ImportedD->doesThisDeclarationHaveABody()); auto *ToFD = LastDeclMatcher().match(ToTU, Pattern); EXPECT_FALSE(ToFD->doesThisDeclarationHaveABody()); EXPECT_EQ(ToFD->getPreviousDecl(), ImportedD); } TEST_P(ImportFriendFunctions, ImportFriendFunctionRedeclChainDef) { auto Pattern = functionDecl(hasName("f")); Decl *FromTU = getTuDecl("struct X { friend void f(){} };" "void f();", Lang_CXX, "input0.cc"); auto *FromD = FirstDeclMatcher().match(FromTU, Pattern); auto *ImportedD = cast(Import(FromD, Lang_CXX)); Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); ASSERT_EQ(DeclCounter().match(ToTU, Pattern), 2u); EXPECT_TRUE(ImportedD->doesThisDeclarationHaveABody()); auto *ToFD = LastDeclMatcher().match(ToTU, Pattern); EXPECT_FALSE(ToFD->doesThisDeclarationHaveABody()); EXPECT_EQ(ToFD->getPreviousDecl(), ImportedD); } TEST_P(ImportFriendFunctions, ImportFriendFunctionRedeclChainDef_OutOfClassDef) { auto Pattern = functionDecl(hasName("f")); Decl *FromTU = getTuDecl("struct X { friend void f(); };" "void f(){}", Lang_CXX, "input0.cc"); auto *FromD = FirstDeclMatcher().match(FromTU, Pattern); auto *ImportedD = cast(Import(FromD, Lang_CXX)); Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); ASSERT_EQ(DeclCounter().match(ToTU, Pattern), 2u); EXPECT_FALSE(ImportedD->doesThisDeclarationHaveABody()); auto *ToFD = LastDeclMatcher().match(ToTU, Pattern); EXPECT_TRUE(ToFD->doesThisDeclarationHaveABody()); EXPECT_EQ(ToFD->getPreviousDecl(), ImportedD); } // Disabled temporarily, because the new structural equivalence check // (https://reviews.llvm.org/D48628) breaks it. // PreviousDecl is not set because there is no structural match. // FIXME Enable! TEST_P(ImportFriendFunctions, DISABLED_ImportFriendFunctionRedeclChainDefWithClass) { auto Pattern = functionDecl(hasName("f")); Decl *FromTU = getTuDecl( R"( class X; void f(X *x){} class X{ friend void f(X *x); }; )", Lang_CXX, "input0.cc"); auto *FromD = FirstDeclMatcher().match(FromTU, Pattern); auto *ImportedD = cast(Import(FromD, Lang_CXX)); Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); ASSERT_EQ(DeclCounter().match(ToTU, Pattern), 2u); EXPECT_TRUE(ImportedD->doesThisDeclarationHaveABody()); auto *InClassFD = cast(FirstDeclMatcher() .match(ToTU, friendDecl()) ->getFriendDecl()); EXPECT_FALSE(InClassFD->doesThisDeclarationHaveABody()); EXPECT_EQ(InClassFD->getPreviousDecl(), ImportedD); // The parameters must refer the same type EXPECT_EQ((*InClassFD->param_begin())->getOriginalType(), (*ImportedD->param_begin())->getOriginalType()); } // Disabled temporarily, because the new structural equivalence check // (https://reviews.llvm.org/D48628) breaks it. // PreviousDecl is not set because there is no structural match. // FIXME Enable! TEST_P(ImportFriendFunctions, DISABLED_ImportFriendFunctionRedeclChainDefWithClass_ImportTheProto) { auto Pattern = functionDecl(hasName("f")); Decl *FromTU = getTuDecl( R"( class X; void f(X *x){} class X{ friend void f(X *x); }; )", Lang_CXX, "input0.cc"); auto *FromD = LastDeclMatcher().match(FromTU, Pattern); auto *ImportedD = cast(Import(FromD, Lang_CXX)); Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); ASSERT_EQ(DeclCounter().match(ToTU, Pattern), 2u); EXPECT_FALSE(ImportedD->doesThisDeclarationHaveABody()); auto *OutOfClassFD = FirstDeclMatcher().match( ToTU, functionDecl(unless(hasParent(friendDecl())))); EXPECT_TRUE(OutOfClassFD->doesThisDeclarationHaveABody()); EXPECT_EQ(ImportedD->getPreviousDecl(), OutOfClassFD); // The parameters must refer the same type EXPECT_EQ((*OutOfClassFD->param_begin())->getOriginalType(), (*ImportedD->param_begin())->getOriginalType()); } TEST_P(ImportFriendFunctions, ImportFriendFunctionFromMultipleTU) { auto Pattern = functionDecl(hasName("f")); FunctionDecl *ImportedD; { Decl *FromTU = getTuDecl("struct X { friend void f(){} };", Lang_CXX, "input0.cc"); auto *FromD = FirstDeclMatcher().match(FromTU, Pattern); ImportedD = cast(Import(FromD, Lang_CXX)); } FunctionDecl *ImportedD1; { Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input1.cc"); auto *FromD = FirstDeclMatcher().match(FromTU, Pattern); ImportedD1 = cast(Import(FromD, Lang_CXX)); } Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); ASSERT_EQ(DeclCounter().match(ToTU, Pattern), 2u); EXPECT_TRUE(ImportedD->doesThisDeclarationHaveABody()); EXPECT_FALSE(ImportedD1->doesThisDeclarationHaveABody()); EXPECT_EQ(ImportedD1->getPreviousDecl(), ImportedD); } AST_MATCHER_P(TagDecl, hasTypedefForAnonDecl, Matcher, InnerMatcher) { if (auto *Typedef = Node.getTypedefNameForAnonDecl()) return InnerMatcher.matches(*Typedef, Finder, Builder); return false; } TEST_P(ImportDecl, ImportEnumSequential) { CodeFiles Samples{{"main.c", {"void foo();" "void moo();" "int main() { foo(); moo(); }", Lang_C}}, {"foo.c", {"typedef enum { THING_VALUE } thing_t;" "void conflict(thing_t type);" "void foo() { (void)THING_VALUE; }" "void conflict(thing_t type) {}", Lang_C}}, {"moo.c", {"typedef enum { THING_VALUE } thing_t;" "void conflict(thing_t type);" "void moo() { conflict(THING_VALUE); }", Lang_C}}}; auto VerificationMatcher = enumDecl(has(enumConstantDecl(hasName("THING_VALUE"))), hasTypedefForAnonDecl(hasName("thing_t"))); ImportAction ImportFoo{"foo.c", "main.c", functionDecl(hasName("foo"))}, ImportMoo{"moo.c", "main.c", functionDecl(hasName("moo"))}; testImportSequence( Samples, {ImportFoo, ImportMoo}, // "foo", them "moo". // Just check that there is only one enum decl in the result AST. "main.c", enumDecl(), VerificationMatcher); // For different import order, result should be the same. testImportSequence( Samples, {ImportMoo, ImportFoo}, // "moo", them "foo". // Check that there is only one enum decl in the result AST. "main.c", enumDecl(), VerificationMatcher); } const internal::VariadicDynCastAllOfMatcher dependentScopeDeclRefExpr; TEST_P(ImportExpr, DependentScopeDeclRefExpr) { MatchVerifier Verifier; testImport("template struct S { static T foo; };" "template void declToImport() {" " (void) S::foo;" "}" "void instantiate() { declToImport(); }" "template T S::foo;", Lang_CXX11, "", Lang_CXX11, Verifier, functionTemplateDecl(has(functionDecl(has(compoundStmt( has(cStyleCastExpr(has(dependentScopeDeclRefExpr()))))))))); testImport("template struct S {" "template static void foo(){};" "};" "template void declToImport() {" " S::template foo();" "}" "void instantiate() { declToImport(); }", Lang_CXX11, "", Lang_CXX11, Verifier, functionTemplateDecl(has(functionDecl(has(compoundStmt( has(callExpr(has(dependentScopeDeclRefExpr()))))))))); } const internal::VariadicDynCastAllOfMatcher dependentNameType; TEST_P(ImportExpr, DependentNameType) { MatchVerifier Verifier; testImport("template struct declToImport {" " typedef typename T::type dependent_name;" "};", Lang_CXX11, "", Lang_CXX11, Verifier, classTemplateDecl(has( cxxRecordDecl(has(typedefDecl(has(dependentNameType()))))))); } const internal::VariadicDynCastAllOfMatcher unresolvedMemberExpr; TEST_P(ImportExpr, UnresolvedMemberExpr) { MatchVerifier Verifier; testImport("struct S { template void mem(); };" "template void declToImport() {" " S s;" " s.mem();" "}" "void instantiate() { declToImport(); }", Lang_CXX11, "", Lang_CXX11, Verifier, functionTemplateDecl(has(functionDecl(has( compoundStmt(has(callExpr(has(unresolvedMemberExpr()))))))))); } class ImportImplicitMethods : public ASTImporterTestBase { public: static constexpr auto DefaultCode = R"( struct A { int x; }; void f() { A a; A a1(a); A a2(A{}); a = a1; a = A{}; a.~A(); })"; template void testImportOf( const MatcherType &MethodMatcher, const char *Code = DefaultCode) { test(MethodMatcher, Code, /*ExpectedCount=*/1u); } template void testNoImportOf( const MatcherType &MethodMatcher, const char *Code = DefaultCode) { test(MethodMatcher, Code, /*ExpectedCount=*/0u); } private: template void test(const MatcherType &MethodMatcher, const char *Code, unsigned int ExpectedCount) { auto ClassMatcher = cxxRecordDecl(unless(isImplicit())); Decl *ToTU = getToTuDecl(Code, Lang_CXX11); auto *ToClass = FirstDeclMatcher().match( ToTU, ClassMatcher); ASSERT_EQ(DeclCounter().match(ToClass, MethodMatcher), 1u); { CXXMethodDecl *Method = FirstDeclMatcher().match(ToClass, MethodMatcher); ToClass->removeDecl(Method); } ASSERT_EQ(DeclCounter().match(ToClass, MethodMatcher), 0u); Decl *ImportedClass = nullptr; { Decl *FromTU = getTuDecl(Code, Lang_CXX11, "input1.cc"); auto *FromClass = FirstDeclMatcher().match( FromTU, ClassMatcher); ImportedClass = Import(FromClass, Lang_CXX11); } EXPECT_EQ(ToClass, ImportedClass); EXPECT_EQ(DeclCounter().match(ToClass, MethodMatcher), ExpectedCount); } }; TEST_P(ImportImplicitMethods, DefaultConstructor) { testImportOf(cxxConstructorDecl(isDefaultConstructor())); } TEST_P(ImportImplicitMethods, CopyConstructor) { testImportOf(cxxConstructorDecl(isCopyConstructor())); } TEST_P(ImportImplicitMethods, MoveConstructor) { testImportOf(cxxConstructorDecl(isMoveConstructor())); } TEST_P(ImportImplicitMethods, Destructor) { testImportOf(cxxDestructorDecl()); } TEST_P(ImportImplicitMethods, CopyAssignment) { testImportOf(cxxMethodDecl(isCopyAssignmentOperator())); } TEST_P(ImportImplicitMethods, MoveAssignment) { testImportOf(cxxMethodDecl(isMoveAssignmentOperator())); } TEST_P(ImportImplicitMethods, DoNotImportUserProvided) { auto Code = R"( struct A { A() { int x; } }; )"; testNoImportOf(cxxConstructorDecl(isDefaultConstructor()), Code); } TEST_P(ImportImplicitMethods, DoNotImportDefault) { auto Code = R"( struct A { A() = default; }; )"; testNoImportOf(cxxConstructorDecl(isDefaultConstructor()), Code); } TEST_P(ImportImplicitMethods, DoNotImportDeleted) { auto Code = R"( struct A { A() = delete; }; )"; testNoImportOf(cxxConstructorDecl(isDefaultConstructor()), Code); } TEST_P(ImportImplicitMethods, DoNotImportOtherMethod) { auto Code = R"( struct A { void f() { } }; )"; testNoImportOf(cxxMethodDecl(hasName("f")), Code); } TEST_P(ASTImporterTestBase, ImportOfEquivalentRecord) { Decl *ToR1; { Decl *FromTU = getTuDecl( "struct A { };", Lang_CXX, "input0.cc"); auto *FromR = FirstDeclMatcher().match( FromTU, cxxRecordDecl(hasName("A"))); ToR1 = Import(FromR, Lang_CXX); } Decl *ToR2; { Decl *FromTU = getTuDecl( "struct A { };", Lang_CXX, "input1.cc"); auto *FromR = FirstDeclMatcher().match( FromTU, cxxRecordDecl(hasName("A"))); ToR2 = Import(FromR, Lang_CXX); } EXPECT_EQ(ToR1, ToR2); } TEST_P(ASTImporterTestBase, ImportOfNonEquivalentRecord) { Decl *ToR1; { Decl *FromTU = getTuDecl( "struct A { int x; };", Lang_CXX, "input0.cc"); auto *FromR = FirstDeclMatcher().match( FromTU, cxxRecordDecl(hasName("A"))); ToR1 = Import(FromR, Lang_CXX); } Decl *ToR2; { Decl *FromTU = getTuDecl( "struct A { unsigned x; };", Lang_CXX, "input1.cc"); auto *FromR = FirstDeclMatcher().match( FromTU, cxxRecordDecl(hasName("A"))); ToR2 = Import(FromR, Lang_CXX); } EXPECT_NE(ToR1, ToR2); } TEST_P(ASTImporterTestBase, ImportOfEquivalentField) { Decl *ToF1; { Decl *FromTU = getTuDecl( "struct A { int x; };", Lang_CXX, "input0.cc"); auto *FromF = FirstDeclMatcher().match( FromTU, fieldDecl(hasName("x"))); ToF1 = Import(FromF, Lang_CXX); } Decl *ToF2; { Decl *FromTU = getTuDecl( "struct A { int x; };", Lang_CXX, "input1.cc"); auto *FromF = FirstDeclMatcher().match( FromTU, fieldDecl(hasName("x"))); ToF2 = Import(FromF, Lang_CXX); } EXPECT_EQ(ToF1, ToF2); } TEST_P(ASTImporterTestBase, ImportOfNonEquivalentField) { Decl *ToF1; { Decl *FromTU = getTuDecl( "struct A { int x; };", Lang_CXX, "input0.cc"); auto *FromF = FirstDeclMatcher().match( FromTU, fieldDecl(hasName("x"))); ToF1 = Import(FromF, Lang_CXX); } Decl *ToF2; { Decl *FromTU = getTuDecl( "struct A { unsigned x; };", Lang_CXX, "input1.cc"); auto *FromF = FirstDeclMatcher().match( FromTU, fieldDecl(hasName("x"))); ToF2 = Import(FromF, Lang_CXX); } EXPECT_NE(ToF1, ToF2); } TEST_P(ASTImporterTestBase, ImportOfEquivalentMethod) { Decl *ToM1; { Decl *FromTU = getTuDecl( "struct A { void x(); }; void A::x() { }", Lang_CXX, "input0.cc"); auto *FromM = FirstDeclMatcher().match( FromTU, functionDecl(hasName("x"), isDefinition())); ToM1 = Import(FromM, Lang_CXX); } Decl *ToM2; { Decl *FromTU = getTuDecl( "struct A { void x(); }; void A::x() { }", Lang_CXX, "input1.cc"); auto *FromM = FirstDeclMatcher().match( FromTU, functionDecl(hasName("x"), isDefinition())); ToM2 = Import(FromM, Lang_CXX); } EXPECT_EQ(ToM1, ToM2); } TEST_P(ASTImporterTestBase, ImportOfNonEquivalentMethod) { Decl *ToM1; { Decl *FromTU = getTuDecl( "struct A { void x(); }; void A::x() { }", Lang_CXX, "input0.cc"); auto *FromM = FirstDeclMatcher().match( FromTU, functionDecl(hasName("x"), isDefinition())); ToM1 = Import(FromM, Lang_CXX); } Decl *ToM2; { Decl *FromTU = getTuDecl( "struct A { void x() const; }; void A::x() const { }", Lang_CXX, "input1.cc"); auto *FromM = FirstDeclMatcher().match( FromTU, functionDecl(hasName("x"), isDefinition())); ToM2 = Import(FromM, Lang_CXX); } EXPECT_NE(ToM1, ToM2); } TEST_P(ASTImporterTestBase, ImportUnnamedStructsWithRecursingField) { Decl *FromTU = getTuDecl( R"( struct A { struct { struct A *next; } entry0; struct { struct A *next; } entry1; }; )", Lang_C, "input0.cc"); auto *From = FirstDeclMatcher().match(FromTU, recordDecl(hasName("A"))); Import(From, Lang_C); auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); auto *Entry0 = FirstDeclMatcher().match(ToTU, fieldDecl(hasName("entry0"))); auto *Entry1 = FirstDeclMatcher().match(ToTU, fieldDecl(hasName("entry1"))); auto *R0 = getRecordDecl(Entry0); auto *R1 = getRecordDecl(Entry1); EXPECT_NE(R0, R1); EXPECT_TRUE(MatchVerifier().match( R0, recordDecl(has(fieldDecl(hasName("next")))))); EXPECT_TRUE(MatchVerifier().match( R1, recordDecl(has(fieldDecl(hasName("next")))))); } struct DeclContextTest : ASTImporterTestBase {}; TEST_P(DeclContextTest, removeDeclOfClassTemplateSpecialization) { Decl *TU = getTuDecl( R"( namespace NS { template struct S {}; template struct S; inline namespace INS { template struct S {}; template struct S; } } )", Lang_CXX11, "input0.cc"); auto *NS = FirstDeclMatcher().match( TU, namespaceDecl()); auto *Spec = FirstDeclMatcher().match( TU, classTemplateSpecializationDecl()); ASSERT_TRUE(NS->containsDecl(Spec)); NS->removeDecl(Spec); EXPECT_FALSE(NS->containsDecl(Spec)); } struct ImportFunctionTemplateSpecializations : ASTImporterTestBase {}; TEST_P(ImportFunctionTemplateSpecializations, TUshouldNotContainFunctionTemplateImplicitInstantiation) { Decl *FromTU = getTuDecl( R"( template int f() { return 0; } void foo() { f(); } )", Lang_CXX, "input0.cc"); // Check that the function template instantiation is NOT the child of the TU. auto Pattern = translationUnitDecl( unless(has(functionDecl(hasName("f"), isTemplateInstantiation())))); ASSERT_TRUE(MatchVerifier{}.match(FromTU, Pattern)); auto *Foo = FirstDeclMatcher().match( FromTU, functionDecl(hasName("foo"))); ASSERT_TRUE(Import(Foo, Lang_CXX)); auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); EXPECT_TRUE(MatchVerifier{}.match(ToTU, Pattern)); } TEST_P(ImportFunctionTemplateSpecializations, TUshouldNotContainFunctionTemplateExplicitInstantiation) { Decl *FromTU = getTuDecl( R"( template int f() { return 0; } template int f(); )", Lang_CXX, "input0.cc"); // Check that the function template instantiation is NOT the child of the TU. auto Instantiation = functionDecl(hasName("f"), isTemplateInstantiation()); auto Pattern = translationUnitDecl(unless(has(Instantiation))); ASSERT_TRUE(MatchVerifier{}.match(FromTU, Pattern)); ASSERT_TRUE( Import(FirstDeclMatcher().match(FromTU, Instantiation), Lang_CXX)); auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); EXPECT_TRUE(MatchVerifier{}.match(ToTU, Pattern)); } TEST_P(ImportFunctionTemplateSpecializations, TUshouldContainFunctionTemplateSpecialization) { Decl *FromTU = getTuDecl( R"( template int f() { return 0; } template <> int f() { return 4; } )", Lang_CXX, "input0.cc"); // Check that the function template specialization is the child of the TU. auto Specialization = functionDecl(hasName("f"), isExplicitTemplateSpecialization()); auto Pattern = translationUnitDecl(has(Specialization)); ASSERT_TRUE(MatchVerifier{}.match(FromTU, Pattern)); ASSERT_TRUE( Import(FirstDeclMatcher().match(FromTU, Specialization), Lang_CXX)); auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); EXPECT_TRUE(MatchVerifier{}.match(ToTU, Pattern)); } TEST_P(ImportFunctionTemplateSpecializations, FunctionTemplateSpecializationRedeclChain) { Decl *FromTU = getTuDecl( R"( template int f() { return 0; } template <> int f() { return 4; } )", Lang_CXX, "input0.cc"); auto Spec = functionDecl(hasName("f"), isExplicitTemplateSpecialization(), hasParent(translationUnitDecl())); auto *FromSpecD = FirstDeclMatcher().match(FromTU, Spec); { auto *TU = FromTU; auto *SpecD = FromSpecD; auto *TemplateD = FirstDeclMatcher().match( TU, functionTemplateDecl()); auto *FirstSpecD = *(TemplateD->spec_begin()); ASSERT_EQ(SpecD, FirstSpecD); ASSERT_TRUE(SpecD->getPreviousDecl()); ASSERT_FALSE(cast(SpecD->getPreviousDecl()) ->doesThisDeclarationHaveABody()); } ASSERT_TRUE(Import(FromSpecD, Lang_CXX)); { auto *TU = ToAST->getASTContext().getTranslationUnitDecl(); auto *SpecD = FirstDeclMatcher().match(TU, Spec); auto *TemplateD = FirstDeclMatcher().match( TU, functionTemplateDecl()); auto *FirstSpecD = *(TemplateD->spec_begin()); EXPECT_EQ(SpecD, FirstSpecD); ASSERT_TRUE(SpecD->getPreviousDecl()); EXPECT_FALSE(cast(SpecD->getPreviousDecl()) ->doesThisDeclarationHaveABody()); } } TEST_P(ImportFunctionTemplateSpecializations, MatchNumberOfFunctionTemplateSpecializations) { Decl *FromTU = getTuDecl( R"( template constexpr int f() { return 0; } template <> constexpr int f() { return 4; } void foo() { static_assert(f() == 0, ""); static_assert(f() == 4, ""); } )", Lang_CXX11, "input0.cc"); auto *FromD = FirstDeclMatcher().match( FromTU, functionDecl(hasName("foo"))); Import(FromD, Lang_CXX11); auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); EXPECT_EQ( DeclCounter().match(FromTU, functionDecl(hasName("f"))), DeclCounter().match(ToTU, functionDecl(hasName("f")))); } TEST_P(ImportFunctionTemplateSpecializations, ImportPrototypes) { auto Pattern = functionDecl(hasName("f"), isExplicitTemplateSpecialization()); auto Code = R"( // Proto of the primary template. template void f(); // Proto of the specialization. template <> void f(); )"; Decl *ImportedD; { Decl *FromTU = getTuDecl(Code, Lang_CXX, "input0.cc"); auto *FromD = LastDeclMatcher().match(FromTU, Pattern); ImportedD = Import(FromD, Lang_CXX); } { Decl *FromTU = getTuDecl(Code, Lang_CXX, "input1.cc"); auto *FromD = LastDeclMatcher().match(FromTU, Pattern); Import(FromD, Lang_CXX); } Decl *ToTU = ImportedD->getTranslationUnitDecl(); EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 2u); auto *To0 = FirstDeclMatcher().match(ToTU, Pattern); auto *To1 = LastDeclMatcher().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 void f(); // Specialization and definition. template <> void f() {} )"; Decl *ImportedD; { Decl *FromTU = getTuDecl(Code, Lang_CXX, "input0.cc"); auto *FromD = FirstDeclMatcher().match(FromTU, Pattern); ImportedD = Import(FromD, Lang_CXX); } { Decl *FromTU = getTuDecl(Code, Lang_CXX, "input1.cc"); auto *FromD = FirstDeclMatcher().match(FromTU, Pattern); Import(FromD, Lang_CXX); } Decl *ToTU = ImportedD->getTranslationUnitDecl(); EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 1u); auto *To0 = FirstDeclMatcher().match(ToTU, Pattern); EXPECT_TRUE(ImportedD == To0); EXPECT_TRUE(To0->doesThisDeclarationHaveABody()); auto *TemplateD = FirstDeclMatcher().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 void f(); // Specialization proto. template <> void f(); // Specialization proto. template <> void f(); )"; Decl *ImportedD; { Decl *FromTU = getTuDecl(Code, Lang_CXX, "input0.cc"); auto *FromD = FirstDeclMatcher().match(FromTU, Pattern); ImportedD = Import(FromD, Lang_CXX); } Decl *ToTU = ImportedD->getTranslationUnitDecl(); EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 2u); auto *To0 = FirstDeclMatcher().match(ToTU, Pattern); auto *To1 = LastDeclMatcher().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 void f(); // Specialization proto. template <> void f(); // Specialization definition. template <> void f() {} )"; Decl *ImportedD; { Decl *FromTU = getTuDecl(Code, Lang_CXX, "input0.cc"); auto *FromD = FirstDeclMatcher().match(FromTU, Pattern); ImportedD = Import(FromD, Lang_CXX); } Decl *ToTU = ImportedD->getTranslationUnitDecl(); EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 2u); auto *To0 = FirstDeclMatcher().match(ToTU, Pattern); auto *To1 = LastDeclMatcher().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 void f(); // Specialization definition. template <> void f() {} // Specialization proto. template <> void f(); )"; Decl *ImportedD; { Decl *FromTU = getTuDecl(Code, Lang_CXX, "input0.cc"); auto *FromD = FirstDeclMatcher().match(FromTU, Pattern); ImportedD = Import(FromD, Lang_CXX); } Decl *ToTU = ImportedD->getTranslationUnitDecl(); EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 2u); auto *To0 = FirstDeclMatcher().match(ToTU, Pattern); auto *To1 = LastDeclMatcher().match(ToTU, Pattern); EXPECT_TRUE(ImportedD == To0); EXPECT_TRUE(ImportedD != To1); EXPECT_TRUE(To0->doesThisDeclarationHaveABody()); EXPECT_FALSE(To1->doesThisDeclarationHaveABody()); EXPECT_EQ(To1->getPreviousDecl(), To0); } INSTANTIATE_TEST_CASE_P(ParameterizedTests, DeclContextTest, ::testing::Values(ArgVector()), ); 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"}); INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportExpr, DefaultTestValuesForRunOptions, ); INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportType, DefaultTestValuesForRunOptions, ); INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportDecl, DefaultTestValuesForRunOptions, ); INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterTestBase, DefaultTestValuesForRunOptions, ); INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctions, DefaultTestValuesForRunOptions, ); INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendFunctions, DefaultTestValuesForRunOptions, ); INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctionTemplateSpecializations, DefaultTestValuesForRunOptions, ); } // end namespace ast_matchers } // end namespace clang