| //===- 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Tests for the correct import of AST nodes from one AST context to another. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/ADT/StringMap.h" |
| |
| #include "clang/AST/DeclContextInternals.h" |
| |
| #include "ASTImporterFixtures.h" |
| #include "MatchVerifier.h" |
| |
| namespace clang { |
| namespace ast_matchers { |
| |
| using internal::Matcher; |
| using internal::BindableMatcher; |
| using llvm::StringMap; |
| |
| // Base class for those tests which use the family of `testImport` functions. |
| class TestImportBase : public CompilerOptionSpecificTest, |
| public ::testing::WithParamInterface<ArgVector> { |
| |
| template <typename NodeType> |
| llvm::Expected<NodeType> importNode(ASTUnit *From, ASTUnit *To, |
| ASTImporter &Importer, NodeType Node) { |
| ASTContext &ToCtx = To->getASTContext(); |
| |
| // Add 'From' file to virtual file system so importer can 'find' it |
| // 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); |
| |
| if (Imported) { |
| // This should dump source locations and assert if some source locations |
| // were not imported. |
| SmallString<1024> ImportChecker; |
| llvm::raw_svector_ostream ToNothing(ImportChecker); |
| ToCtx.getTranslationUnitDecl()->print(ToNothing); |
| |
| // This traverses the AST to catch certain bugs like poorly or not |
| // implemented subtrees. |
| (*Imported)->dump(ToNothing); |
| } |
| |
| return Imported; |
| } |
| |
| template <typename NodeType> |
| testing::AssertionResult |
| testImport(const std::string &FromCode, const ArgVector &FromArgs, |
| const std::string &ToCode, const ArgVector &ToArgs, |
| MatchVerifier<NodeType> &Verifier, |
| const BindableMatcher<NodeType> &SearchMatcher, |
| const BindableMatcher<NodeType> &VerificationMatcher) { |
| const char *const InputFileName = "input.cc"; |
| const char *const OutputFileName = "output.cc"; |
| |
| std::unique_ptr<ASTUnit> 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<NodeType>(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<NodeType> WrapperMatcher(VerificationMatcher); |
| EXPECT_TRUE(Verifier.match(ToImport, WrapperMatcher)); |
| |
| auto Imported = importNode(FromAST.get(), ToAST.get(), Importer, ToImport); |
| if (!Imported) { |
| std::string ErrorText; |
| handleAllErrors( |
| Imported.takeError(), |
| [&ErrorText](const ImportError &Err) { ErrorText = Err.message(); }); |
| return testing::AssertionFailure() |
| << "Import failed, error: \"" << ErrorText << "\"!"; |
| } |
| |
| return Verifier.match(*Imported, WrapperMatcher); |
| } |
| |
| template <typename NodeType> |
| testing::AssertionResult |
| testImport(const std::string &FromCode, const ArgVector &FromArgs, |
| const std::string &ToCode, const ArgVector &ToArgs, |
| MatchVerifier<NodeType> &Verifier, |
| const BindableMatcher<NodeType> &VerificationMatcher) { |
| return testImport( |
| FromCode, FromArgs, ToCode, ToArgs, Verifier, |
| translationUnitDecl( |
| has(namedDecl(hasName(DeclToImportID)).bind(DeclToImportID))), |
| VerificationMatcher); |
| } |
| |
| protected: |
| ArgVector getExtraArgs() const override { return GetParam(); } |
| |
| 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 <typename NodeType, typename MatcherType> |
| void testImport(const std::string &FromCode, Language FromLang, |
| const std::string &ToCode, Language ToLang, |
| MatchVerifier<NodeType> &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<Decl> 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<ASTUnit>; |
| using AllASTUnits = StringMap<SingleASTUnit>; |
| |
| struct CodeEntry { |
| std::string CodeSample; |
| Language Lang; |
| }; |
| |
| using CodeFiles = StringMap<CodeEntry>; |
| |
| /// 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<ImportAction> &ImportActions, |
| StringRef FileForFinalCheck, |
| BindableMatcher<Decl> FinalSelectPredicate, |
| BindableMatcher<Decl> VerificationMatcher) { |
| AllASTUnits AllASTs; |
| using ImporterKey = std::pair<const ASTUnit *, const ASTUnit *>; |
| llvm::DenseMap<ImporterKey, std::unique_ptr<ASTImporter>> 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<ASTImporter> &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<Decl>(DeclToImportID, FoundDecl); |
| auto Imported = importNode(From, To, *ImporterRef, ToImport); |
| EXPECT_TRUE(static_cast<bool>(Imported)); |
| if (!Imported) |
| llvm::consumeError(Imported.takeError()); |
| } |
| |
| // Find the declaration and import it. |
| auto FoundDecl = match(FinalSelectPredicate.bind(DeclToVerifyID), |
| AllASTs[FileForFinalCheck]->getASTContext()); |
| EXPECT_TRUE(FoundDecl.size() == 1); |
| const Decl *ToVerify = selectFirst<Decl>(DeclToVerifyID, FoundDecl); |
| MatchVerifier<Decl> Verifier; |
| EXPECT_TRUE( |
| Verifier.match(ToVerify, BindableMatcher<Decl>(VerificationMatcher))); |
| } |
| }; |
| |
| template <typename T> RecordDecl *getRecordDecl(T *D) { |
| auto *ET = cast<ElaboratedType>(D->getType().getTypePtr()); |
| return cast<RecordType>(ET->getNamedType().getTypePtr())->getDecl(); |
| } |
| |
| struct ImportExpr : TestImportBase {}; |
| struct ImportType : TestImportBase {}; |
| struct ImportDecl : TestImportBase {}; |
| |
| struct CanonicalRedeclChain : ASTImporterOptionSpecificTestBase {}; |
| |
| TEST_P(CanonicalRedeclChain, ShouldBeConsequentWithMatchers) { |
| Decl *FromTU = getTuDecl("void f();", Lang_CXX); |
| auto Pattern = functionDecl(hasName("f")); |
| auto *D0 = FirstDeclMatcher<FunctionDecl>().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<FunctionDecl>().match(FromTU, Pattern); |
| auto *D2 = LastDeclMatcher<FunctionDecl>().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<FunctionDecl>().match(FromTU, Pattern); |
| auto *D2 = LastDeclMatcher<FunctionDecl>().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)); |
| } |
| |
| namespace { |
| struct RedirectingImporter : public ASTImporter { |
| using ASTImporter::ASTImporter; |
| |
| protected: |
| llvm::Expected<Decl *> ImportImpl(Decl *FromD) override { |
| auto *ND = dyn_cast<NamedDecl>(FromD); |
| if (!ND || ND->getName() != "shouldNotBeImported") |
| return ASTImporter::ImportImpl(FromD); |
| for (Decl *D : getToContext().getTranslationUnitDecl()->decls()) { |
| if (auto *ND = dyn_cast<NamedDecl>(D)) |
| if (ND->getName() == "realDecl") { |
| RegisterImportedDecl(FromD, ND); |
| return ND; |
| } |
| } |
| return ASTImporter::ImportImpl(FromD); |
| } |
| }; |
| |
| } // namespace |
| |
| struct RedirectingImporterTest : ASTImporterOptionSpecificTestBase { |
| RedirectingImporterTest() { |
| Creator = [](ASTContext &ToContext, FileManager &ToFileManager, |
| ASTContext &FromContext, FileManager &FromFileManager, |
| bool MinimalImport, |
| const std::shared_ptr<ASTImporterSharedState> &SharedState) { |
| return new RedirectingImporter(ToContext, ToFileManager, FromContext, |
| FromFileManager, MinimalImport, |
| SharedState); |
| }; |
| } |
| }; |
| |
| // Test that an ASTImporter subclass can intercept an import call. |
| TEST_P(RedirectingImporterTest, InterceptImport) { |
| Decl *From, *To; |
| std::tie(From, To) = |
| getImportedDecl("class shouldNotBeImported {};", Lang_CXX, |
| "class realDecl {};", Lang_CXX, "shouldNotBeImported"); |
| auto *Imported = cast<CXXRecordDecl>(To); |
| EXPECT_EQ(Imported->getQualifiedNameAsString(), "realDecl"); |
| |
| // Make sure our importer prevented the importing of the decl. |
| auto *ToTU = Imported->getTranslationUnitDecl(); |
| auto Pattern = functionDecl(hasName("shouldNotBeImported")); |
| unsigned count = |
| DeclCounterWithPredicate<CXXRecordDecl>().match(ToTU, Pattern); |
| EXPECT_EQ(0U, count); |
| } |
| |
| // Test that when we indirectly import a declaration the custom ASTImporter |
| // is still intercepting the import. |
| TEST_P(RedirectingImporterTest, InterceptIndirectImport) { |
| Decl *From, *To; |
| std::tie(From, To) = |
| getImportedDecl("class shouldNotBeImported {};" |
| "class F { shouldNotBeImported f; };", |
| Lang_CXX, "class realDecl {};", Lang_CXX, "F"); |
| |
| // Make sure our ASTImporter prevented the importing of the decl. |
| auto *ToTU = To->getTranslationUnitDecl(); |
| auto Pattern = functionDecl(hasName("shouldNotBeImported")); |
| unsigned count = |
| DeclCounterWithPredicate<CXXRecordDecl>().match(ToTU, Pattern); |
| EXPECT_EQ(0U, count); |
| } |
| |
| struct ImportPath : ASTImporterOptionSpecificTestBase { |
| Decl *FromTU; |
| FunctionDecl *D0, *D1, *D2; |
| ImportPath() { |
| FromTU = getTuDecl("void f(); void f(); void f();", Lang_CXX); |
| auto Pattern = functionDecl(hasName("f")); |
| D0 = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); |
| D2 = LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern); |
| D1 = D2->getPreviousDecl(); |
| } |
| }; |
| |
| TEST_P(ImportPath, Push) { |
| ASTImporter::ImportPathTy path; |
| path.push(D0); |
| EXPECT_FALSE(path.hasCycleAtBack()); |
| } |
| |
| TEST_P(ImportPath, SmallCycle) { |
| ASTImporter::ImportPathTy path; |
| path.push(D0); |
| path.push(D0); |
| EXPECT_TRUE(path.hasCycleAtBack()); |
| path.pop(); |
| EXPECT_FALSE(path.hasCycleAtBack()); |
| path.push(D0); |
| EXPECT_TRUE(path.hasCycleAtBack()); |
| } |
| |
| TEST_P(ImportPath, GetSmallCycle) { |
| ASTImporter::ImportPathTy path; |
| path.push(D0); |
| path.push(D0); |
| EXPECT_TRUE(path.hasCycleAtBack()); |
| std::array<Decl* ,2> Res; |
| int i = 0; |
| for (Decl *Di : path.getCycleAtBack()) { |
| Res[i++] = Di; |
| } |
| ASSERT_EQ(i, 2); |
| EXPECT_EQ(Res[0], D0); |
| EXPECT_EQ(Res[1], D0); |
| } |
| |
| TEST_P(ImportPath, GetCycle) { |
| ASTImporter::ImportPathTy path; |
| path.push(D0); |
| path.push(D1); |
| path.push(D2); |
| path.push(D0); |
| EXPECT_TRUE(path.hasCycleAtBack()); |
| std::array<Decl* ,4> Res; |
| int i = 0; |
| for (Decl *Di : path.getCycleAtBack()) { |
| Res[i++] = Di; |
| } |
| ASSERT_EQ(i, 4); |
| EXPECT_EQ(Res[0], D0); |
| EXPECT_EQ(Res[1], D2); |
| EXPECT_EQ(Res[2], D1); |
| EXPECT_EQ(Res[3], D0); |
| } |
| |
| TEST_P(ImportPath, CycleAfterCycle) { |
| ASTImporter::ImportPathTy path; |
| path.push(D0); |
| path.push(D1); |
| path.push(D0); |
| path.push(D1); |
| path.push(D2); |
| path.push(D0); |
| EXPECT_TRUE(path.hasCycleAtBack()); |
| std::array<Decl* ,4> Res; |
| int i = 0; |
| for (Decl *Di : path.getCycleAtBack()) { |
| Res[i++] = Di; |
| } |
| ASSERT_EQ(i, 4); |
| EXPECT_EQ(Res[0], D0); |
| EXPECT_EQ(Res[1], D2); |
| EXPECT_EQ(Res[2], D1); |
| EXPECT_EQ(Res[3], D0); |
| |
| path.pop(); |
| path.pop(); |
| path.pop(); |
| EXPECT_TRUE(path.hasCycleAtBack()); |
| i = 0; |
| for (Decl *Di : path.getCycleAtBack()) { |
| Res[i++] = Di; |
| } |
| ASSERT_EQ(i, 3); |
| EXPECT_EQ(Res[0], D0); |
| EXPECT_EQ(Res[1], D1); |
| EXPECT_EQ(Res[2], D0); |
| |
| path.pop(); |
| EXPECT_FALSE(path.hasCycleAtBack()); |
| } |
| |
| TEST_P(ImportExpr, ImportStringLiteral) { |
| MatchVerifier<Decl> 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, ImportChooseExpr) { |
| MatchVerifier<Decl> Verifier; |
| |
| // This case tests C code that is not condition-dependent and has a true |
| // condition. |
| testImport( |
| "void declToImport() { (void)__builtin_choose_expr(1, 2, 3); }", |
| Lang_C, "", Lang_C, Verifier, |
| functionDecl(hasDescendant(chooseExpr()))); |
| } |
| |
| TEST_P(ImportExpr, ImportGNUNullExpr) { |
| MatchVerifier<Decl> Verifier; |
| testImport( |
| "void declToImport() { (void)__null; }", |
| Lang_CXX, "", Lang_CXX, Verifier, |
| functionDecl(hasDescendant(gnuNullExpr(hasType(isInteger()))))); |
| } |
| |
| TEST_P(ImportExpr, ImportCXXNullPtrLiteralExpr) { |
| MatchVerifier<Decl> Verifier; |
| testImport( |
| "void declToImport() { (void)nullptr; }", |
| Lang_CXX11, "", Lang_CXX11, Verifier, |
| functionDecl(hasDescendant(cxxNullPtrLiteralExpr()))); |
| } |
| |
| |
| TEST_P(ImportExpr, ImportFloatinglLiteralExpr) { |
| MatchVerifier<Decl> 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, ImportImaginaryLiteralExpr) { |
| MatchVerifier<Decl> Verifier; |
| testImport( |
| "void declToImport() { (void)1.0i; }", |
| Lang_CXX14, "", Lang_CXX14, Verifier, |
| functionDecl(hasDescendant(imaginaryLiteral()))); |
| } |
| |
| TEST_P(ImportExpr, ImportCompoundLiteralExpr) { |
| MatchVerifier<Decl> 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<Decl> 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<Decl> 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<Decl> 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<NamedDecl>, InnerMatcher) { |
| const NamedDecl *Template = Node.getTemplatedDecl(); |
| return Template && InnerMatcher.matches(*Template, Finder, Builder); |
| } |
| |
| TEST_P(ImportExpr, ImportParenListExpr) { |
| MatchVerifier<Decl> Verifier; |
| testImport( |
| "template<typename T> class dummy { void f() { dummy X(*this); } };" |
| "typedef dummy<int> declToImport;" |
| "template class dummy<int>;", |
| 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<Decl> 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<Decl> Verifier; |
| 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<Decl> 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<Decl> 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<Decl> 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), |
| hasDescendant(floatLiteral(equals(1.0))), |
| hasDescendant(integerLiteral(equals(2))))), |
| has(designatedInitExpr( |
| designatorCountIs(2), |
| hasDescendant(floatLiteral(equals(2.0))), |
| hasDescendant(integerLiteral(equals(2))))), |
| has(designatedInitExpr( |
| designatorCountIs(2), |
| hasDescendant(floatLiteral(equals(1.0))), |
| hasDescendant(integerLiteral(equals(0))))))))); |
| } |
| |
| TEST_P(ImportExpr, ImportPredefinedExpr) { |
| MatchVerifier<Decl> 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<Decl> 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<Expr, VAArgExpr> vaArgExpr; |
| |
| TEST_P(ImportExpr, ImportVAArgExpr) { |
| MatchVerifier<Decl> 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<Decl> 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<Decl> Verifier; |
| testImport( |
| "void declToImport() { typedef _Atomic(int) a_int; }", |
| Lang_CXX11, "", Lang_CXX11, Verifier, |
| functionDecl(hasDescendant(typedefDecl(has(atomicType()))))); |
| } |
| |
| TEST_P(ImportDecl, ImportFunctionTemplateDecl) { |
| MatchVerifier<Decl> Verifier; |
| testImport( |
| "template <typename T> void declToImport() { };", |
| Lang_CXX, "", Lang_CXX, Verifier, |
| functionTemplateDecl()); |
| } |
| |
| TEST_P(ImportExpr, ImportCXXDependentScopeMemberExpr) { |
| MatchVerifier<Decl> Verifier; |
| testImport( |
| "template <typename T> struct C { T t; };" |
| "template <typename T> void declToImport() {" |
| " C<T> d;" |
| " (void)d.t;" |
| "}" |
| "void instantiate() { declToImport<int>(); }", |
| Lang_CXX, "", Lang_CXX, Verifier, |
| functionTemplateDecl(hasDescendant( |
| cStyleCastExpr(has(cxxDependentScopeMemberExpr()))))); |
| testImport( |
| "template <typename T> struct C { T t; };" |
| "template <typename T> void declToImport() {" |
| " C<T> d;" |
| " (void)(&d)->t;" |
| "}" |
| "void instantiate() { declToImport<int>(); }", |
| Lang_CXX, "", Lang_CXX, Verifier, |
| functionTemplateDecl(hasDescendant( |
| cStyleCastExpr(has(cxxDependentScopeMemberExpr()))))); |
| } |
| |
| TEST_P(ImportType, ImportTypeAliasTemplate) { |
| MatchVerifier<Decl> Verifier; |
| testImport( |
| "template <int K>" |
| "struct dummy { static const int i = K; };" |
| "template <int K> using dummy2 = dummy<K>;" |
| "int declToImport() { return dummy2<3>::i; }", |
| Lang_CXX11, "", Lang_CXX11, Verifier, |
| functionDecl( |
| hasDescendant(implicitCastExpr(has(declRefExpr()))), |
| unless(hasAncestor(translationUnitDecl(has(typeAliasDecl())))))); |
| } |
| |
| const internal::VariadicDynCastAllOfMatcher<Decl, VarTemplateSpecializationDecl> |
| varTemplateSpecializationDecl; |
| |
| TEST_P(ImportDecl, ImportVarTemplate) { |
| MatchVerifier<Decl> Verifier; |
| testImport( |
| "template <typename T>" |
| "T pi = T(3.1415926535897932385L);" |
| "void declToImport() { (void)pi<int>; }", |
| Lang_CXX14, "", Lang_CXX14, Verifier, |
| functionDecl( |
| hasDescendant(declRefExpr(to(varTemplateSpecializationDecl()))), |
| unless(hasAncestor(translationUnitDecl(has(varDecl( |
| hasName("pi"), unless(varTemplateSpecializationDecl())))))))); |
| } |
| |
| TEST_P(ImportType, ImportPackExpansion) { |
| MatchVerifier<Decl> Verifier; |
| testImport( |
| "template <typename... Args>" |
| "struct dummy {" |
| " dummy(Args... args) {}" |
| " static const int i = 4;" |
| "};" |
| "int declToImport() { return dummy<int>::i; }", |
| Lang_CXX11, "", Lang_CXX11, Verifier, |
| functionDecl(hasDescendant( |
| returnStmt(has(implicitCastExpr(has(declRefExpr()))))))); |
| } |
| |
| const internal::VariadicDynCastAllOfMatcher<Type, |
| DependentTemplateSpecializationType> |
| dependentTemplateSpecializationType; |
| |
| TEST_P(ImportType, ImportDependentTemplateSpecialization) { |
| MatchVerifier<Decl> Verifier; |
| testImport( |
| "template<typename T>" |
| "struct A;" |
| "template<typename T>" |
| "struct declToImport {" |
| " typename A<T>::template B<T> a;" |
| "};", |
| Lang_CXX, "", Lang_CXX, Verifier, |
| classTemplateDecl(has(cxxRecordDecl(has( |
| fieldDecl(hasType(dependentTemplateSpecializationType()))))))); |
| } |
| |
| const internal::VariadicDynCastAllOfMatcher<Stmt, SizeOfPackExpr> |
| sizeOfPackExpr; |
| |
| TEST_P(ImportExpr, ImportSizeOfPackExpr) { |
| MatchVerifier<Decl> Verifier; |
| testImport( |
| "template <typename... Ts>" |
| "void declToImport() {" |
| " const int i = sizeof...(Ts);" |
| "};" |
| "void g() { declToImport<int>(); }", |
| Lang_CXX11, "", Lang_CXX11, Verifier, |
| functionTemplateDecl(hasDescendant(sizeOfPackExpr()))); |
| testImport( |
| "template <typename... Ts>" |
| "using X = int[sizeof...(Ts)];" |
| "template <typename... Us>" |
| "struct Y {" |
| " X<Us..., int, double, int, Us...> f;" |
| "};" |
| "Y<float, int> 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<Stmt, TypeTraitExpr> typeTraitExpr; |
| |
| TEST_P(ImportExpr, ImportTypeTraitExpr) { |
| MatchVerifier<Decl> 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<Stmt, CXXTypeidExpr> cxxTypeidExpr; |
| |
| TEST_P(ImportExpr, ImportCXXTypeidExpr) { |
| MatchVerifier<Decl> 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<Decl> Verifier; |
| testImport( |
| "template<typename T> struct declToImport {" |
| " void m() { (void)__is_pod(T); }" |
| "};" |
| "void f() { declToImport<int>().m(); }", |
| Lang_CXX11, "", Lang_CXX11, Verifier, |
| classTemplateDecl(has(cxxRecordDecl(has( |
| functionDecl(hasDescendant( |
| typeTraitExpr(hasType(booleanType()))))))))); |
| } |
| |
| TEST_P(ImportDecl, ImportRecordDeclInFunc) { |
| MatchVerifier<Decl> 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(ASTImporterOptionSpecificTestBase, 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<VarDecl>().match(FromTU, varDecl(hasName("d"))); |
| ASSERT_TRUE(FromVar); |
| auto ToType = |
| ImportType(FromVar->getType().getCanonicalType(), FromVar, Lang_C); |
| EXPECT_FALSE(ToType.isNull()); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ImportRecordDeclInFuncParams) { |
| // This construct is not supported by ASTImporter. |
| Decl *FromTU = getTuDecl( |
| "int declToImport(struct data_t{int a;int b;} ***d){ return 0; }", |
| Lang_C, "input.c"); |
| auto *From = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("declToImport"))); |
| ASSERT_TRUE(From); |
| auto *To = Import(From, Lang_C); |
| EXPECT_EQ(To, nullptr); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ImportRecordDeclInFuncFromMacro) { |
| Decl *FromTU = getTuDecl( |
| "#define NONAME_SIZEOF(type) sizeof(struct{type *dummy;}) \n" |
| "int declToImport(){ return NONAME_SIZEOF(int); }", |
| Lang_C, "input.c"); |
| auto *From = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("declToImport"))); |
| ASSERT_TRUE(From); |
| auto *To = Import(From, Lang_C); |
| ASSERT_TRUE(To); |
| EXPECT_TRUE(MatchVerifier<FunctionDecl>().match( |
| To, functionDecl(hasName("declToImport"), |
| hasDescendant(unaryExprOrTypeTraitExpr())))); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| ImportRecordDeclInFuncParamsFromMacro) { |
| // This construct is not supported by ASTImporter. |
| Decl *FromTU = getTuDecl( |
| "#define PAIR_STRUCT(type) struct data_t{type a;type b;} \n" |
| "int declToImport(PAIR_STRUCT(int) ***d){ return 0; }", |
| Lang_C, "input.c"); |
| auto *From = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("declToImport"))); |
| ASSERT_TRUE(From); |
| auto *To = Import(From, Lang_C); |
| EXPECT_EQ(To, nullptr); |
| } |
| |
| const internal::VariadicDynCastAllOfMatcher<Expr, CXXPseudoDestructorExpr> |
| cxxPseudoDestructorExpr; |
| |
| TEST_P(ImportExpr, ImportCXXPseudoDestructorExpr) { |
| MatchVerifier<Decl> 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<Decl> 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<Decl, |
| UsingShadowDecl> usingShadowDecl; |
| |
| TEST_P(ImportDecl, ImportUsingShadowDecl) { |
| MatchVerifier<Decl> Verifier; |
| testImport( |
| "namespace foo { int bar; }" |
| "namespace declToImport { using foo::bar; }", |
| Lang_CXX, "", Lang_CXX, Verifier, |
| namespaceDecl(has(usingShadowDecl()))); |
| } |
| |
| TEST_P(ImportExpr, ImportUnresolvedLookupExpr) { |
| MatchVerifier<Decl> Verifier; |
| testImport( |
| "template<typename T> int foo();" |
| "template <typename T> void declToImport() {" |
| " (void)::foo<T>;" |
| " (void)::template foo<T>;" |
| "}" |
| "void instantiate() { declToImport<int>(); }", |
| Lang_CXX, "", Lang_CXX, Verifier, |
| functionTemplateDecl(hasDescendant(unresolvedLookupExpr()))); |
| } |
| |
| TEST_P(ImportExpr, ImportCXXUnresolvedConstructExpr) { |
| MatchVerifier<Decl> Verifier; |
| testImport( |
| "template <typename T> struct C { T t; };" |
| "template <typename T> void declToImport() {" |
| " C<T> d;" |
| " d.t = T();" |
| "}" |
| "void instantiate() { declToImport<int>(); }", |
| Lang_CXX, "", Lang_CXX, Verifier, |
| functionTemplateDecl(hasDescendant( |
| binaryOperator(has(cxxUnresolvedConstructExpr()))))); |
| testImport( |
| "template <typename T> struct C { T t; };" |
| "template <typename T> void declToImport() {" |
| " C<T> d;" |
| " (&d)->t = T();" |
| "}" |
| "void instantiate() { declToImport<int>(); }", |
| 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<Decl> Verifier; |
| testImport( |
| "template <typename T> void declToImport() { T a = 1; }" |
| "void instantiate() { declToImport<int>(); }", |
| Lang_CXX, "", Lang_CXX, Verifier, |
| functionTemplateDecl(hasAncestor(translationUnitDecl( |
| unless(has(functionDecl(hasName("declToImport")))))))); |
| testImport( |
| "template <typename T> struct declToImport { T t; };" |
| "void instantiate() { declToImport<int>(); }", |
| Lang_CXX, "", Lang_CXX, Verifier, |
| classTemplateDecl(hasAncestor(translationUnitDecl( |
| unless(has(cxxRecordDecl(hasName("declToImport")))))))); |
| } |
| |
| TEST_P(ImportDecl, ImportClassTemplatePartialSpecialization) { |
| MatchVerifier<Decl> Verifier; |
| auto Code = |
| R"s( |
| struct declToImport { |
| template <typename T0> struct X; |
| template <typename T0> struct X<T0 *> {}; |
| }; |
| )s"; |
| testImport(Code, Lang_CXX, "", Lang_CXX, Verifier, |
| recordDecl(has(classTemplateDecl()), |
| has(classTemplateSpecializationDecl()))); |
| } |
| |
| TEST_P(ImportExpr, CXXOperatorCallExpr) { |
| MatchVerifier<Decl> Verifier; |
| testImport( |
| "class declToImport {" |
| " void f() { *this = declToImport(); }" |
| "};", |
| Lang_CXX, "", Lang_CXX, Verifier, |
| cxxRecordDecl(has(cxxMethodDecl(hasDescendant( |
| cxxOperatorCallExpr()))))); |
| } |
| |
| TEST_P(ImportExpr, DependentSizedArrayType) { |
| MatchVerifier<Decl> Verifier; |
| testImport( |
| "template<typename T, int Size> class declToImport {" |
| " T data[Size];" |
| "};", |
| Lang_CXX, "", Lang_CXX, Verifier, |
| classTemplateDecl(has(cxxRecordDecl( |
| has(fieldDecl(hasType(dependentSizedArrayType()))))))); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ImportBeginLocOfDeclRefExpr) { |
| Decl *FromTU = getTuDecl( |
| "class A { public: static int X; }; void f() { (void)A::X; }", Lang_CXX); |
| auto From = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("f"))); |
| ASSERT_TRUE(From); |
| ASSERT_TRUE( |
| cast<CStyleCastExpr>(cast<CompoundStmt>(From->getBody())->body_front()) |
| ->getSubExpr() |
| ->getBeginLoc() |
| .isValid()); |
| FunctionDecl *To = Import(From, Lang_CXX); |
| ASSERT_TRUE(To); |
| ASSERT_TRUE( |
| cast<CStyleCastExpr>(cast<CompoundStmt>(To->getBody())->body_front()) |
| ->getSubExpr() |
| ->getBeginLoc() |
| .isValid()); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| ImportOfTemplatedDeclOfClassTemplateDecl) { |
| Decl *FromTU = getTuDecl("template<class X> struct S{};", Lang_CXX); |
| auto From = |
| FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, classTemplateDecl()); |
| ASSERT_TRUE(From); |
| auto To = cast<ClassTemplateDecl>(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(ASTImporterOptionSpecificTestBase, |
| ImportOfTemplatedDeclOfFunctionTemplateDecl) { |
| Decl *FromTU = getTuDecl("template<class X> void f(){}", Lang_CXX); |
| auto From = FirstDeclMatcher<FunctionTemplateDecl>().match( |
| FromTU, functionTemplateDecl()); |
| ASSERT_TRUE(From); |
| auto To = cast<FunctionTemplateDecl>(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(ASTImporterOptionSpecificTestBase, |
| ImportOfTemplatedDeclShouldImportTheClassTemplateDecl) { |
| Decl *FromTU = getTuDecl("template<class X> struct S{};", Lang_CXX); |
| auto FromFT = |
| FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, classTemplateDecl()); |
| ASSERT_TRUE(FromFT); |
| |
| auto ToTemplated = |
| cast<CXXRecordDecl>(Import(FromFT->getTemplatedDecl(), Lang_CXX)); |
| EXPECT_TRUE(ToTemplated); |
| auto ToTU = ToTemplated->getTranslationUnitDecl(); |
| auto ToFT = |
| FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, classTemplateDecl()); |
| EXPECT_TRUE(ToFT); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| ImportOfTemplatedDeclShouldImportTheFunctionTemplateDecl) { |
| Decl *FromTU = getTuDecl("template<class X> void f(){}", Lang_CXX); |
| auto FromFT = FirstDeclMatcher<FunctionTemplateDecl>().match( |
| FromTU, functionTemplateDecl()); |
| ASSERT_TRUE(FromFT); |
| |
| auto ToTemplated = |
| cast<FunctionDecl>(Import(FromFT->getTemplatedDecl(), Lang_CXX)); |
| EXPECT_TRUE(ToTemplated); |
| auto ToTU = ToTemplated->getTranslationUnitDecl(); |
| auto ToFT = FirstDeclMatcher<FunctionTemplateDecl>().match( |
| ToTU, functionTemplateDecl()); |
| EXPECT_TRUE(ToFT); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ImportCorrectTemplatedDecl) { |
| auto Code = |
| R"( |
| namespace x { |
| template<class X> struct S1{}; |
| template<class X> struct S2{}; |
| template<class X> struct S3{}; |
| } |
| )"; |
| Decl *FromTU = getTuDecl(Code, Lang_CXX); |
| auto FromNs = |
| FirstDeclMatcher<NamespaceDecl>().match(FromTU, namespaceDecl()); |
| auto ToNs = cast<NamespaceDecl>(Import(FromNs, Lang_CXX)); |
| ASSERT_TRUE(ToNs); |
| auto From = |
| FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, |
| classTemplateDecl( |
| hasName("S2"))); |
| auto To = |
| FirstDeclMatcher<ClassTemplateDecl>().match(ToNs, |
| classTemplateDecl( |
| hasName("S2"))); |
| ASSERT_TRUE(From); |
| ASSERT_TRUE(To); |
| auto ToTemplated = To->getTemplatedDecl(); |
| auto ToTemplated1 = |
| cast<CXXRecordDecl>(Import(From->getTemplatedDecl(), Lang_CXX)); |
| EXPECT_TRUE(ToTemplated1); |
| ASSERT_EQ(ToTemplated1, ToTemplated); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ImportChooseExpr) { |
| // This tests the import of isConditionTrue directly to make sure the importer |
| // gets it right. |
| Decl *From, *To; |
| std::tie(From, To) = getImportedDecl( |
| "void declToImport() { (void)__builtin_choose_expr(1, 0, 1); }", |
| Lang_C, "", Lang_C); |
| |
| auto ToResults = match(chooseExpr().bind("choose"), To->getASTContext()); |
| auto FromResults = match(chooseExpr().bind("choose"), From->getASTContext()); |
| |
| const ChooseExpr *FromChooseExpr = |
| selectFirst<ChooseExpr>("choose", FromResults); |
| ASSERT_TRUE(FromChooseExpr); |
| |
| const ChooseExpr *ToChooseExpr = selectFirst<ChooseExpr>("choose", ToResults); |
| ASSERT_TRUE(ToChooseExpr); |
| |
| EXPECT_EQ(FromChooseExpr->isConditionTrue(), ToChooseExpr->isConditionTrue()); |
| EXPECT_EQ(FromChooseExpr->isConditionDependent(), |
| ToChooseExpr->isConditionDependent()); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| ImportFunctionWithBackReferringParameter) { |
| Decl *From, *To; |
| std::tie(From, To) = getImportedDecl( |
| R"( |
| template <typename T> struct X {}; |
| |
| void declToImport(int y, X<int> &x) {} |
| |
| template <> struct X<int> { |
| void g() { |
| X<int> x; |
| declToImport(0, x); |
| } |
| }; |
| )", |
| Lang_CXX, "", Lang_CXX); |
| |
| MatchVerifier<Decl> Verifier; |
| auto Matcher = functionDecl(hasName("declToImport"), |
| parameterCountIs(2), |
| hasParameter(0, hasName("y")), |
| hasParameter(1, hasName("x")), |
| hasParameter(1, hasType(asString("X<int> &")))); |
| ASSERT_TRUE(Verifier.match(From, Matcher)); |
| EXPECT_TRUE(Verifier.match(To, Matcher)); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| TUshouldNotContainTemplatedDeclOfFunctionTemplates) { |
| Decl *From, *To; |
| std::tie(From, To) = |
| getImportedDecl("template <typename T> void declToImport() { T a = 1; }" |
| "void instantiate() { declToImport<int>(); }", |
| Lang_CXX, "", Lang_CXX); |
| |
| auto Check = [](Decl *D) -> bool { |
| auto TU = D->getTranslationUnitDecl(); |
| for (auto Child : TU->decls()) { |
| if (auto *FD = dyn_cast<FunctionDecl>(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(ASTImporterOptionSpecificTestBase, |
| TUshouldNotContainTemplatedDeclOfClassTemplates) { |
| Decl *From, *To; |
| std::tie(From, To) = |
| getImportedDecl("template <typename T> struct declToImport { T t; };" |
| "void instantiate() { declToImport<int>(); }", |
| Lang_CXX, "", Lang_CXX); |
| |
| auto Check = [](Decl *D) -> bool { |
| auto TU = D->getTranslationUnitDecl(); |
| for (auto Child : TU->decls()) { |
| if (auto *RD = dyn_cast<CXXRecordDecl>(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(ASTImporterOptionSpecificTestBase, |
| TUshouldNotContainTemplatedDeclOfTypeAlias) { |
| Decl *From, *To; |
| std::tie(From, To) = |
| getImportedDecl( |
| "template <typename T> struct X {};" |
| "template <typename T> using declToImport = X<T>;" |
| "void instantiate() { declToImport<int> a; }", |
| Lang_CXX11, "", Lang_CXX11); |
| |
| auto Check = [](Decl *D) -> bool { |
| auto TU = D->getTranslationUnitDecl(); |
| for (auto Child : TU->decls()) { |
| if (auto *AD = dyn_cast<TypeAliasDecl>(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(ASTImporterOptionSpecificTestBase, |
| TUshouldNotContainClassTemplateSpecializationOfImplicitInstantiation) { |
| |
| Decl *From, *To; |
| std::tie(From, To) = getImportedDecl( |
| R"( |
| template<class T> |
| class Base {}; |
| class declToImport : public Base<declToImport> {}; |
| )", |
| Lang_CXX, "", Lang_CXX); |
| |
| // Check that the ClassTemplateSpecializationDecl is NOT the child of the TU. |
| auto Pattern = |
| translationUnitDecl(unless(has(classTemplateSpecializationDecl()))); |
| ASSERT_TRUE( |
| MatchVerifier<Decl>{}.match(From->getTranslationUnitDecl(), Pattern)); |
| EXPECT_TRUE( |
| MatchVerifier<Decl>{}.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<Decl>{}.match(From->getTranslationUnitDecl(), Pattern)); |
| EXPECT_TRUE( |
| MatchVerifier<Decl>{}.match(To->getTranslationUnitDecl(), Pattern)); |
| } |
| |
| AST_MATCHER_P(RecordDecl, hasFieldOrder, std::vector<StringRef>, Order) { |
| size_t Index = 0; |
| for (Decl *D : Node.decls()) { |
| if (isa<FieldDecl>(D) || isa<IndirectFieldDecl>(D)) { |
| auto *ND = cast<NamedDecl>(D); |
| if (Index == Order.size()) |
| return false; |
| if (ND->getName() != Order[Index]) |
| return false; |
| ++Index; |
| } |
| } |
| return Index == Order.size(); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| TUshouldContainClassTemplateSpecializationOfExplicitInstantiation) { |
| Decl *From, *To; |
| std::tie(From, To) = getImportedDecl( |
| R"( |
| namespace NS { |
| template<class T> |
| class X {}; |
| template class X<int>; |
| } |
| )", |
| 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<Decl>{}.match(From, Pattern)); |
| EXPECT_TRUE(MatchVerifier<Decl>{}.match(To, Pattern)); |
| |
| // Check that the ClassTemplateSpecializationDecl is the child of the |
| // NamespaceDecl. |
| Pattern = namespaceDecl(has(classTemplateSpecializationDecl(hasName("X")))); |
| ASSERT_TRUE(MatchVerifier<Decl>{}.match(From, Pattern)); |
| EXPECT_TRUE(MatchVerifier<Decl>{}.match(To, Pattern)); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| CXXRecordDeclFieldsShouldBeInCorrectOrder) { |
| Decl *From, *To; |
| std::tie(From, To) = |
| getImportedDecl( |
| "struct declToImport { int a; int b; };", |
| Lang_CXX11, "", Lang_CXX11); |
| |
| MatchVerifier<Decl> Verifier; |
| ASSERT_TRUE(Verifier.match(From, cxxRecordDecl(hasFieldOrder({"a", "b"})))); |
| EXPECT_TRUE(Verifier.match(To, cxxRecordDecl(hasFieldOrder({"a", "b"})))); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| 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<Decl> Verifier; |
| ASSERT_TRUE( |
| Verifier.match(From, cxxRecordDecl(hasFieldOrder({"a", "b", "c"})))); |
| EXPECT_TRUE( |
| Verifier.match(To, cxxRecordDecl(hasFieldOrder({"a", "b", "c"})))); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| CXXRecordDeclFieldAndIndirectFieldOrder) { |
| Decl *From, *To; |
| std::tie(From, To) = getImportedDecl( |
| // First field is "a", then the field for unnamed union, then "b" and "c" |
| // from it (indirect fields), then "d". |
| R"s( |
| struct declToImport { |
| int a = d; |
| union { |
| int b; |
| int c; |
| }; |
| int d; |
| }; |
| )s", |
| Lang_CXX11, "", Lang_CXX11); |
| |
| MatchVerifier<Decl> Verifier; |
| ASSERT_TRUE(Verifier.match( |
| From, cxxRecordDecl(hasFieldOrder({"a", "", "b", "c", "d"})))); |
| EXPECT_TRUE(Verifier.match( |
| To, cxxRecordDecl(hasFieldOrder({"a", "", "b", "c", "d"})))); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ShouldImportImplicitCXXRecordDecl) { |
| Decl *From, *To; |
| std::tie(From, To) = getImportedDecl( |
| R"( |
| struct declToImport { |
| }; |
| )", |
| Lang_CXX, "", Lang_CXX); |
| |
| MatchVerifier<Decl> Verifier; |
| // Match the implicit Decl. |
| auto Matcher = cxxRecordDecl(has(cxxRecordDecl())); |
| ASSERT_TRUE(Verifier.match(From, Matcher)); |
| EXPECT_TRUE(Verifier.match(To, Matcher)); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| ShouldImportImplicitCXXRecordDeclOfClassTemplate) { |
| Decl *From, *To; |
| std::tie(From, To) = getImportedDecl( |
| R"( |
| template <typename U> |
| struct declToImport { |
| }; |
| )", |
| Lang_CXX, "", Lang_CXX); |
| |
| MatchVerifier<Decl> 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(ASTImporterOptionSpecificTestBase, |
| ShouldImportImplicitCXXRecordDeclOfClassTemplateSpecializationDecl) { |
| Decl *From, *To; |
| std::tie(From, To) = getImportedDecl( |
| R"( |
| template<class T> |
| class Base {}; |
| class declToImport : public Base<declToImport> {}; |
| )", |
| Lang_CXX, "", Lang_CXX); |
| |
| auto hasImplicitClass = has(cxxRecordDecl()); |
| auto Pattern = translationUnitDecl(has(classTemplateDecl( |
| hasName("Base"), |
| has(classTemplateSpecializationDecl(hasImplicitClass))))); |
| ASSERT_TRUE( |
| MatchVerifier<Decl>{}.match(From->getTranslationUnitDecl(), Pattern)); |
| EXPECT_TRUE( |
| MatchVerifier<Decl>{}.match(To->getTranslationUnitDecl(), Pattern)); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, IDNSOrdinary) { |
| Decl *From, *To; |
| std::tie(From, To) = |
| getImportedDecl("void declToImport() {}", Lang_CXX, "", Lang_CXX); |
| |
| MatchVerifier<Decl> Verifier; |
| auto Matcher = functionDecl(); |
| ASSERT_TRUE(Verifier.match(From, Matcher)); |
| EXPECT_TRUE(Verifier.match(To, Matcher)); |
| EXPECT_EQ(From->getIdentifierNamespace(), To->getIdentifierNamespace()); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, IDNSOfNonmemberOperator) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| struct X {}; |
| void operator<<(int, X); |
| )", |
| Lang_CXX); |
| Decl *From = LastDeclMatcher<Decl>{}.match(FromTU, functionDecl()); |
| const Decl *To = Import(From, Lang_CXX); |
| EXPECT_EQ(From->getIdentifierNamespace(), To->getIdentifierNamespace()); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| ShouldImportMembersOfClassTemplateSpecializationDecl) { |
| Decl *From, *To; |
| std::tie(From, To) = getImportedDecl( |
| R"( |
| template<class T> |
| class Base { int a; }; |
| class declToImport : Base<declToImport> {}; |
| )", |
| Lang_CXX, "", Lang_CXX); |
| |
| auto Pattern = translationUnitDecl(has(classTemplateDecl( |
| hasName("Base"), |
| has(classTemplateSpecializationDecl(has(fieldDecl(hasName("a")))))))); |
| ASSERT_TRUE( |
| MatchVerifier<Decl>{}.match(From->getTranslationUnitDecl(), Pattern)); |
| EXPECT_TRUE( |
| MatchVerifier<Decl>{}.match(To->getTranslationUnitDecl(), Pattern)); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| ImportDefinitionOfClassTemplateAfterFwdDecl) { |
| { |
| Decl *FromTU = getTuDecl( |
| R"( |
| template <typename T> |
| struct B; |
| )", |
| Lang_CXX, "input0.cc"); |
| auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match( |
| FromTU, classTemplateDecl(hasName("B"))); |
| |
| Import(FromD, Lang_CXX); |
| } |
| |
| { |
| Decl *FromTU = getTuDecl( |
| R"( |
| template <typename T> |
| struct B { |
| void f(); |
| }; |
| )", |
| Lang_CXX, "input1.cc"); |
| FunctionDecl *FromD = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("f"))); |
| Import(FromD, Lang_CXX); |
| auto *FromCTD = FirstDeclMatcher<ClassTemplateDecl>().match( |
| FromTU, classTemplateDecl(hasName("B"))); |
| auto *ToCTD = cast<ClassTemplateDecl>(Import(FromCTD, Lang_CXX)); |
| EXPECT_TRUE(ToCTD->isThisDeclarationADefinition()); |
| } |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| ImportDefinitionOfClassTemplateIfThereIsAnExistingFwdDeclAndDefinition) { |
| Decl *ToTU = getToTuDecl( |
| R"( |
| template <typename T> |
| struct B { |
| void f(); |
| }; |
| |
| template <typename T> |
| struct B; |
| )", |
| Lang_CXX); |
| ASSERT_EQ(1u, DeclCounterWithPredicate<ClassTemplateDecl>( |
| [](const ClassTemplateDecl *T) { |
| return T->isThisDeclarationADefinition(); |
| }) |
| .match(ToTU, classTemplateDecl())); |
| |
| Decl *FromTU = getTuDecl( |
| R"( |
| template <typename T> |
| struct B { |
| void f(); |
| }; |
| )", |
| Lang_CXX, "input1.cc"); |
| ClassTemplateDecl *FromD = FirstDeclMatcher<ClassTemplateDecl>().match( |
| FromTU, classTemplateDecl(hasName("B"))); |
| |
| Import(FromD, Lang_CXX); |
| |
| // We should have only one definition. |
| EXPECT_EQ(1u, DeclCounterWithPredicate<ClassTemplateDecl>( |
| [](const ClassTemplateDecl *T) { |
| return T->isThisDeclarationADefinition(); |
| }) |
| .match(ToTU, classTemplateDecl())); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| ImportDefinitionOfClassIfThereIsAnExistingFwdDeclAndDefinition) { |
| Decl *ToTU = getToTuDecl( |
| R"( |
| struct B { |
| void f(); |
| }; |
| |
| struct B; |
| )", |
| Lang_CXX); |
| ASSERT_EQ(2u, DeclCounter<CXXRecordDecl>().match( |
| ToTU, cxxRecordDecl(unless(isImplicit())))); |
| |
| Decl *FromTU = getTuDecl( |
| R"( |
| struct B { |
| void f(); |
| }; |
| )", |
| Lang_CXX, "input1.cc"); |
| auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match( |
| FromTU, cxxRecordDecl(hasName("B"))); |
| |
| Import(FromD, Lang_CXX); |
| |
| EXPECT_EQ(2u, DeclCounter<CXXRecordDecl>().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(ASTImporterOptionSpecificTestBase, ImportSourceLocs) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| #define MFOO(arg) arg = arg + 1 |
| |
| void foo() { |
| int a = 5; |
| MFOO(a); |
| } |
| )", |
| Lang_CXX); |
| auto FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl()); |
| auto ToD = Import(FromD, Lang_CXX); |
| |
| auto ToLHS = LastDeclMatcher<DeclRefExpr>().match(ToD, declRefExpr()); |
| auto FromLHS = LastDeclMatcher<DeclRefExpr>().match(FromTU, declRefExpr()); |
| auto ToRHS = LastDeclMatcher<IntegerLiteral>().match(ToD, integerLiteral()); |
| auto FromRHS = |
| LastDeclMatcher<IntegerLiteral>().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(ASTImporterOptionSpecificTestBase, ImportNestedMacro) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| #define FUNC_INT void declToImport |
| #define FUNC FUNC_INT |
| FUNC(int a); |
| )", |
| Lang_CXX); |
| auto FromD = FirstDeclMatcher<FunctionDecl>().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( |
| ASTImporterOptionSpecificTestBase, |
| ImportDefinitionOfClassTemplateSpecIfThereIsAnExistingFwdDeclAndDefinition) { |
| Decl *ToTU = getToTuDecl( |
| R"( |
| template <typename T> |
| struct B; |
| |
| template <> |
| struct B<int> {}; |
| |
| template <> |
| struct B<int>; |
| )", |
| Lang_CXX); |
| // We should have only one definition. |
| ASSERT_EQ(1u, DeclCounterWithPredicate<ClassTemplateSpecializationDecl>( |
| [](const ClassTemplateSpecializationDecl *T) { |
| return T->isThisDeclarationADefinition(); |
| }) |
| .match(ToTU, classTemplateSpecializationDecl())); |
| |
| Decl *FromTU = getTuDecl( |
| R"( |
| template <typename T> |
| struct B; |
| |
| template <> |
| struct B<int> {}; |
| )", |
| Lang_CXX, "input1.cc"); |
| auto *FromD = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match( |
| FromTU, classTemplateSpecializationDecl(hasName("B"))); |
| |
| Import(FromD, Lang_CXX); |
| |
| // We should have only one definition. |
| EXPECT_EQ(1u, DeclCounterWithPredicate<ClassTemplateSpecializationDecl>( |
| [](const ClassTemplateSpecializationDecl *T) { |
| return T->isThisDeclarationADefinition(); |
| }) |
| .match(ToTU, classTemplateSpecializationDecl())); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, 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<VarDecl>().match(FromTU, varDecl(hasName("object0"))); |
| auto *From0 = getRecordDecl(Obj0); |
| auto *Obj1 = |
| FirstDeclMatcher<VarDecl>().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(ASTImporterOptionSpecificTestBase, AnonymousRecords) { |
| auto *Code = |
| R"( |
| struct X { |
| struct { int a; }; |
| struct { int b; }; |
| }; |
| )"; |
| Decl *FromTU0 = getTuDecl(Code, Lang_C, "input0.c"); |
| |
| Decl *FromTU1 = getTuDecl(Code, Lang_C, "input1.c"); |
| |
| auto *X0 = |
| FirstDeclMatcher<RecordDecl>().match(FromTU0, recordDecl(hasName("X"))); |
| auto *X1 = |
| FirstDeclMatcher<RecordDecl>().match(FromTU1, recordDecl(hasName("X"))); |
| Import(X0, Lang_C); |
| Import(X1, Lang_C); |
| |
| auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| // We expect no (ODR) warning during the import. |
| EXPECT_EQ(0u, ToTU->getASTContext().getDiagnostics().getNumWarnings()); |
| EXPECT_EQ(1u, |
| DeclCounter<RecordDecl>().match(ToTU, recordDecl(hasName("X")))); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, AnonymousRecordsReversed) { |
| Decl *FromTU0 = getTuDecl( |
| R"( |
| struct X { |
| struct { int a; }; |
| struct { int b; }; |
| }; |
| )", |
| Lang_C, "input0.c"); |
| |
| Decl *FromTU1 = getTuDecl( |
| R"( |
| struct X { // reversed order |
| struct { int b; }; |
| struct { int a; }; |
| }; |
| )", |
| Lang_C, "input1.c"); |
| |
| auto *X0 = |
| FirstDeclMatcher<RecordDecl>().match(FromTU0, recordDecl(hasName("X"))); |
| auto *X1 = |
| FirstDeclMatcher<RecordDecl>().match(FromTU1, recordDecl(hasName("X"))); |
| Import(X0, Lang_C); |
| Import(X1, Lang_C); |
| |
| auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| // We expect one (ODR) warning during the import. |
| EXPECT_EQ(1u, ToTU->getASTContext().getDiagnostics().getNumWarnings()); |
| EXPECT_EQ(1u, |
| DeclCounter<RecordDecl>().match(ToTU, recordDecl(hasName("X")))); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ImportDoesUpdateUsedFlag) { |
| auto Pattern = varDecl(hasName("x")); |
| VarDecl *Imported1; |
| { |
| Decl *FromTU = getTuDecl("extern int x;", Lang_CXX, "input0.cc"); |
| auto *FromD = FirstDeclMatcher<VarDecl>().match(FromTU, Pattern); |
| Imported1 = cast<VarDecl>(Import(FromD, Lang_CXX)); |
| } |
| VarDecl *Imported2; |
| { |
| Decl *FromTU = getTuDecl("int x;", Lang_CXX, "input1.cc"); |
| auto *FromD = FirstDeclMatcher<VarDecl>().match(FromTU, Pattern); |
| Imported2 = cast<VarDecl>(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<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("f"))); |
| Import(FromD, Lang_CXX); |
| } |
| EXPECT_TRUE(Imported2->isUsed(false)); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ImportDoesUpdateUsedFlag2) { |
| auto Pattern = varDecl(hasName("x")); |
| VarDecl *ExistingD; |
| { |
| Decl *ToTU = getToTuDecl("int x = 1;", Lang_CXX); |
| ExistingD = FirstDeclMatcher<VarDecl>().match(ToTU, Pattern); |
| } |
| EXPECT_FALSE(ExistingD->isUsed(false)); |
| { |
| Decl *FromTU = getTuDecl( |
| "int x = 1; int f() { return x; }", Lang_CXX, "input1.cc"); |
| auto *FromD = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("f"))); |
| Import(FromD, Lang_CXX); |
| } |
| EXPECT_TRUE(ExistingD->isUsed(false)); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ImportDoesUpdateUsedFlag3) { |
| auto Pattern = varDecl(hasName("a")); |
| VarDecl *ExistingD; |
| { |
| Decl *ToTU = getToTuDecl( |
| R"( |
| struct A { |
| static const int a = 1; |
| }; |
| )", Lang_CXX); |
| ExistingD = FirstDeclMatcher<VarDecl>().match(ToTU, Pattern); |
| } |
| EXPECT_FALSE(ExistingD->isUsed(false)); |
| { |
| Decl *FromTU = getTuDecl( |
| R"( |
| struct A { |
| static const int a = 1; |
| }; |
| const int *f() { return &A::a; } // requires storage, |
| // thus used flag will be set |
| )", Lang_CXX, "input1.cc"); |
| auto *FromFunD = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("f"))); |
| auto *FromD = FirstDeclMatcher<VarDecl>().match(FromTU, Pattern); |
| ASSERT_TRUE(FromD->isUsed(false)); |
| Import(FromFunD, Lang_CXX); |
| } |
| EXPECT_TRUE(ExistingD->isUsed(false)); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ReimportWithUsedFlag) { |
| auto Pattern = varDecl(hasName("x")); |
| |
| Decl *FromTU = getTuDecl("int x;", Lang_CXX, "input0.cc"); |
| auto *FromD = FirstDeclMatcher<VarDecl>().match(FromTU, Pattern); |
| |
| auto *Imported1 = cast<VarDecl>(Import(FromD, Lang_CXX)); |
| |
| ASSERT_FALSE(Imported1->isUsed(false)); |
| |
| FromD->setIsUsed(); |
| auto *Imported2 = cast<VarDecl>(Import(FromD, Lang_CXX)); |
| |
| EXPECT_EQ(Imported1, Imported2); |
| EXPECT_TRUE(Imported2->isUsed(false)); |
| } |
| |
| struct ImportFunctions : ASTImporterOptionSpecificTestBase {}; |
| |
| TEST_P(ImportFunctions, ImportPrototypeOfRecursiveFunction) { |
| Decl *FromTU = getTuDecl("void f(); void f() { f(); }", Lang_CXX); |
| auto Pattern = functionDecl(hasName("f")); |
| auto *From = |
| FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); // Proto |
| |
| Decl *ImportedD = Import(From, Lang_CXX); |
| Decl *ToTU = ImportedD->getTranslationUnitDecl(); |
| |
| EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u); |
| auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern); |
| auto *To1 = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern); |
| EXPECT_TRUE(ImportedD == To0); |
| EXPECT_FALSE(To0->doesThisDeclarationHaveABody()); |
| EXPECT_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<FunctionDecl>().match(FromTU, Pattern); // Def |
| |
| Decl *ImportedD = Import(From, Lang_CXX); |
| Decl *ToTU = ImportedD->getTranslationUnitDecl(); |
| |
| EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u); |
| auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern); |
| auto *To1 = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern); |
| EXPECT_TRUE(ImportedD == To1); |
| EXPECT_FALSE(To0->doesThisDeclarationHaveABody()); |
| EXPECT_TRUE(To1->doesThisDeclarationHaveABody()); |
| EXPECT_EQ(To1->getPreviousDecl(), To0); |
| } |
| |
| 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<CXXMethodDecl>().match(FromTU, Pattern); |
| |
| ASSERT_EQ(Proto->size_overridden_methods(), 1u); |
| CXXMethodDecl *To = cast<CXXMethodDecl>(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<CXXMethodDecl>().match(FromTU, Pattern); |
| CXXMethodDecl *Def = LastDeclMatcher<CXXMethodDecl>().match(FromTU, Pattern); |
| |
| ASSERT_TRUE(Proto->isVirtual()); |
| ASSERT_TRUE(Def->isVirtual()); |
| CXXMethodDecl *To = cast<CXXMethodDecl>(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<FunctionDecl>([](const FunctionDecl *FD) { |
| return FD->doesThisDeclarationHaveABody(); |
| }).match(ToTU, functionDecl())); |
| |
| Decl *FromTU = getTuDecl("void f() {}", Lang_CXX, "input0.cc"); |
| auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl()); |
| |
| Import(FromD, Lang_CXX); |
| |
| EXPECT_EQ(1u, |
| DeclCounterWithPredicate<FunctionDecl>([](const FunctionDecl *FD) { |
| return FD->doesThisDeclarationHaveABody(); |
| }).match(ToTU, functionDecl())); |
| } |
| |
| TEST_P(ImportFunctions, ImportOverriddenMethodTwice) { |
| auto Code = |
| R"( |
| struct B { virtual void f(); }; |
| struct D:B { void f(); }; |
| )"; |
| auto BFP = |
| cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B")))); |
| auto DFP = |
| cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D")))); |
| |
| Decl *FromTU0 = getTuDecl(Code, Lang_CXX); |
| auto *DF = FirstDeclMatcher<CXXMethodDecl>().match(FromTU0, DFP); |
| Import(DF, Lang_CXX); |
| |
| Decl *FromTU1 = getTuDecl(Code, Lang_CXX, "input1.cc"); |
| auto *BF = FirstDeclMatcher<CXXMethodDecl>().match(FromTU1, BFP); |
| Import(BF, Lang_CXX); |
| |
| auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| |
| EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFP), 1u); |
| EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFP), 1u); |
| } |
| |
| TEST_P(ImportFunctions, ImportOverriddenMethodTwiceDefinitionFirst) { |
| auto CodeWithoutDef = |
| R"( |
| struct B { virtual void f(); }; |
| struct D:B { void f(); }; |
| )"; |
| auto CodeWithDef = |
| R"( |
| struct B { virtual void f(){}; }; |
| struct D:B { void f(){}; }; |
| )"; |
| auto BFP = |
| cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B")))); |
| auto DFP = |
| cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D")))); |
| auto BFDefP = cxxMethodDecl( |
| hasName("f"), hasParent(cxxRecordDecl(hasName("B"))), isDefinition()); |
| auto DFDefP = cxxMethodDecl( |
| hasName("f"), hasParent(cxxRecordDecl(hasName("D"))), isDefinition()); |
| auto FDefAllP = cxxMethodDecl(hasName("f"), isDefinition()); |
| |
| { |
| Decl *FromTU = getTuDecl(CodeWithDef, Lang_CXX, "input0.cc"); |
| auto *FromD = FirstDeclMatcher<CXXMethodDecl>().match(FromTU, DFP); |
| Import(FromD, Lang_CXX); |
| } |
| { |
| Decl *FromTU = getTuDecl(CodeWithoutDef, Lang_CXX, "input1.cc"); |
| auto *FromB = FirstDeclMatcher<CXXMethodDecl>().match(FromTU, BFP); |
| Import(FromB, Lang_CXX); |
| } |
| |
| auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| |
| EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFP), 1u); |
| EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFP), 1u); |
| EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFDefP), 1u); |
| EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFDefP), 1u); |
| EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, FDefAllP), 2u); |
| } |
| |
| TEST_P(ImportFunctions, ImportOverriddenMethodTwiceOutOfClassDef) { |
| auto Code = |
| R"( |
| struct B { virtual void f(); }; |
| struct D:B { void f(); }; |
| void B::f(){}; |
| )"; |
| |
| auto BFP = |
| cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B")))); |
| auto BFDefP = cxxMethodDecl( |
| hasName("f"), hasParent(cxxRecordDecl(hasName("B"))), isDefinition()); |
| auto DFP = cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D"))), |
| unless(isDefinition())); |
| |
| Decl *FromTU0 = getTuDecl(Code, Lang_CXX); |
| auto *D = FirstDeclMatcher<CXXMethodDecl>().match(FromTU0, DFP); |
| Import(D, Lang_CXX); |
| |
| Decl *FromTU1 = getTuDecl(Code, Lang_CXX, "input1.cc"); |
| auto *B = FirstDeclMatcher<CXXMethodDecl>().match(FromTU1, BFP); |
| Import(B, Lang_CXX); |
| |
| auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| |
| EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFP), 1u); |
| EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFDefP), 0u); |
| |
| auto *ToB = FirstDeclMatcher<CXXRecordDecl>().match( |
| ToTU, cxxRecordDecl(hasName("B"))); |
| auto *ToBFInClass = FirstDeclMatcher<CXXMethodDecl>().match(ToTU, BFP); |
| auto *ToBFOutOfClass = FirstDeclMatcher<CXXMethodDecl>().match( |
| ToTU, cxxMethodDecl(hasName("f"), isDefinition())); |
| |
| // The definition should be out-of-class. |
| EXPECT_NE(ToBFInClass, ToBFOutOfClass); |
| EXPECT_NE(ToBFInClass->getLexicalDeclContext(), |
| ToBFOutOfClass->getLexicalDeclContext()); |
| EXPECT_EQ(ToBFOutOfClass->getDeclContext(), ToB); |
| EXPECT_EQ(ToBFOutOfClass->getLexicalDeclContext(), ToTU); |
| |
| // Check that the redecl chain is intact. |
| EXPECT_EQ(ToBFOutOfClass->getPreviousDecl(), ToBFInClass); |
| } |
| |
| TEST_P(ImportFunctions, |
| ImportOverriddenMethodTwiceOutOfClassDefInSeparateCode) { |
| auto CodeTU0 = |
| R"( |
| struct B { virtual void f(); }; |
| struct D:B { void f(); }; |
| )"; |
| auto CodeTU1 = |
| R"( |
| struct B { virtual void f(); }; |
| struct D:B { void f(); }; |
| void B::f(){} |
| void D::f(){} |
| void foo(B &b, D &d) { b.f(); d.f(); } |
| )"; |
| |
| auto BFP = |
| cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B")))); |
| auto BFDefP = cxxMethodDecl( |
| hasName("f"), hasParent(cxxRecordDecl(hasName("B"))), isDefinition()); |
| auto DFP = |
| cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D")))); |
| auto DFDefP = cxxMethodDecl( |
| hasName("f"), hasParent(cxxRecordDecl(hasName("D"))), isDefinition()); |
| auto FooDef = functionDecl(hasName("foo")); |
| |
| { |
| Decl *FromTU0 = getTuDecl(CodeTU0, Lang_CXX, "input0.cc"); |
| auto *D = FirstDeclMatcher<CXXMethodDecl>().match(FromTU0, DFP); |
| Import(D, Lang_CXX); |
| } |
| |
| { |
| Decl *FromTU1 = getTuDecl(CodeTU1, Lang_CXX, "input1.cc"); |
| auto *Foo = FirstDeclMatcher<FunctionDecl>().match(FromTU1, FooDef); |
| Import(Foo, Lang_CXX); |
| } |
| |
| auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| |
| EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFP), 1u); |
| EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFP), 1u); |
| EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFDefP), 0u); |
| EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFDefP), 0u); |
| |
| auto *ToB = FirstDeclMatcher<CXXRecordDecl>().match( |
| ToTU, cxxRecordDecl(hasName("B"))); |
| auto *ToD = FirstDeclMatcher<CXXRecordDecl>().match( |
| ToTU, cxxRecordDecl(hasName("D"))); |
| auto *ToBFInClass = FirstDeclMatcher<CXXMethodDecl>().match(ToTU, BFP); |
| auto *ToBFOutOfClass = FirstDeclMatcher<CXXMethodDecl>().match( |
| ToTU, cxxMethodDecl(hasName("f"), isDefinition())); |
| auto *ToDFInClass = FirstDeclMatcher<CXXMethodDecl>().match(ToTU, DFP); |
| auto *ToDFOutOfClass = LastDeclMatcher<CXXMethodDecl>().match( |
| ToTU, cxxMethodDecl(hasName("f"), isDefinition())); |
| |
| // The definition should be out-of-class. |
| EXPECT_NE(ToBFInClass, ToBFOutOfClass); |
| EXPECT_NE(ToBFInClass->getLexicalDeclContext(), |
| ToBFOutOfClass->getLexicalDeclContext()); |
| EXPECT_EQ(ToBFOutOfClass->getDeclContext(), ToB); |
| EXPECT_EQ(ToBFOutOfClass->getLexicalDeclContext(), ToTU); |
| |
| EXPECT_NE(ToDFInClass, ToDFOutOfClass); |
| EXPECT_NE(ToDFInClass->getLexicalDeclContext(), |
| ToDFOutOfClass->getLexicalDeclContext()); |
| EXPECT_EQ(ToDFOutOfClass->getDeclContext(), ToD); |
| EXPECT_EQ(ToDFOutOfClass->getLexicalDeclContext(), ToTU); |
| |
| // Check that the redecl chain is intact. |
| EXPECT_EQ(ToBFOutOfClass->getPreviousDecl(), ToBFInClass); |
| EXPECT_EQ(ToDFOutOfClass->getPreviousDecl(), ToDFInClass); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ImportVariableChainInC) { |
| std::string Code = "static int v; static int v = 0;"; |
| auto Pattern = varDecl(hasName("v")); |
| |
| TranslationUnitDecl *FromTu = getTuDecl(Code, Lang_C, "input0.c"); |
| |
| auto *From0 = FirstDeclMatcher<VarDecl>().match(FromTu, Pattern); |
| auto *From1 = LastDeclMatcher<VarDecl>().match(FromTu, Pattern); |
| |
| auto *To0 = Import(From0, Lang_C); |
| auto *To1 = Import(From1, Lang_C); |
| |
| EXPECT_TRUE(To0); |
| ASSERT_TRUE(To1); |
| EXPECT_NE(To0, To1); |
| EXPECT_EQ(To1->getPreviousDecl(), To0); |
| } |
| |
| TEST_P(ImportFunctions, ImportFromDifferentScopedAnonNamespace) { |
| TranslationUnitDecl *FromTu = getTuDecl( |
| "namespace NS0 { namespace { void f(); } }" |
| "namespace NS1 { namespace { void f(); } }", |
| Lang_CXX, "input0.cc"); |
| auto Pattern = functionDecl(hasName("f")); |
| |
| auto *FromF0 = FirstDeclMatcher<FunctionDecl>().match(FromTu, Pattern); |
| auto *FromF1 = LastDeclMatcher<FunctionDecl>().match(FromTu, Pattern); |
| |
| auto *ToF0 = Import(FromF0, Lang_CXX); |
| auto *ToF1 = Import(FromF1, Lang_CXX); |
| |
| EXPECT_TRUE(ToF0); |
| ASSERT_TRUE(ToF1); |
| EXPECT_NE(ToF0, ToF1); |
| EXPECT_FALSE(ToF1->getPreviousDecl()); |
| } |
| |
| TEST_P(ImportFunctions, ImportFunctionFromUnnamedNamespace) { |
| { |
| Decl *FromTU = getTuDecl("namespace { void f() {} } void g0() { f(); }", |
| Lang_CXX, "input0.cc"); |
| auto *FromD = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("g0"))); |
| |
| Import(FromD, Lang_CXX); |
| } |
| { |
| Decl *FromTU = |
| getTuDecl("namespace { void f() { int a; } } void g1() { f(); }", |
| Lang_CXX, "input1.cc"); |
| auto *FromD = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("g1"))); |
| Import(FromD, Lang_CXX); |
| } |
| |
| Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, functionDecl(hasName("f"))), |
| 2u); |
| } |
| |
| TEST_P(ImportFunctions, ImportImplicitFunctionsInLambda) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| void foo() { |
| (void)[]() { ; }; |
| } |
| )", |
| Lang_CXX11); |
| auto *FromD = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("foo"))); |
| auto *ToD = Import(FromD, Lang_CXX); |
| EXPECT_TRUE(ToD); |
| CXXRecordDecl *LambdaRec = |
| cast<LambdaExpr>(cast<CStyleCastExpr>( |
| *cast<CompoundStmt>(ToD->getBody())->body_begin()) |
| ->getSubExpr()) |
| ->getLambdaClass(); |
| EXPECT_TRUE(LambdaRec->getDestructor()); |
| } |
| |
| TEST_P(ImportFunctions, |
| CallExprOfMemberFunctionTemplateWithExplicitTemplateArgs) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| struct X { |
| template <typename T> |
| void foo(){} |
| }; |
| void f() { |
| X x; |
| x.foo<int>(); |
| } |
| )", |
| Lang_CXX); |
| auto *FromD = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("f"))); |
| auto *ToD = Import(FromD, Lang_CXX); |
| EXPECT_TRUE(ToD); |
| EXPECT_TRUE(MatchVerifier<FunctionDecl>().match( |
| ToD, functionDecl(hasName("f"), hasDescendant(declRefExpr())))); |
| } |
| |
| TEST_P(ImportFunctions, |
| DependentCallExprOfMemberFunctionTemplateWithExplicitTemplateArgs) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| struct X { |
| template <typename T> |
| void foo(){} |
| }; |
| template <typename T> |
| void f() { |
| X x; |
| x.foo<T>(); |
| } |
| void g() { |
| f<int>(); |
| } |
| )", |
| Lang_CXX); |
| auto *FromD = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("g"))); |
| auto *ToD = Import(FromD, Lang_CXX); |
| EXPECT_TRUE(ToD); |
| Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| EXPECT_TRUE(MatchVerifier<TranslationUnitDecl>().match( |
| ToTU, translationUnitDecl(hasDescendant( |
| functionDecl(hasName("f"), hasDescendant(declRefExpr())))))); |
| } |
| |
| struct ImportFunctionTemplates : ASTImporterOptionSpecificTestBase {}; |
| |
| TEST_P(ImportFunctionTemplates, ImportFunctionTemplateInRecordDeclTwice) { |
| auto Code = |
| R"( |
| class X { |
| template <class T> |
| void f(T t); |
| }; |
| )"; |
| Decl *FromTU1 = getTuDecl(Code, Lang_CXX, "input1.cc"); |
| auto *FromD1 = FirstDeclMatcher<FunctionTemplateDecl>().match( |
| FromTU1, functionTemplateDecl(hasName("f"))); |
| auto *ToD1 = Import(FromD1, Lang_CXX); |
| Decl *FromTU2 = getTuDecl(Code, Lang_CXX, "input2.cc"); |
| auto *FromD2 = FirstDeclMatcher<FunctionTemplateDecl>().match( |
| FromTU2, functionTemplateDecl(hasName("f"))); |
| auto *ToD2 = Import(FromD2, Lang_CXX); |
| EXPECT_EQ(ToD1, ToD2); |
| } |
| |
| TEST_P(ImportFunctionTemplates, |
| ImportFunctionTemplateWithDefInRecordDeclTwice) { |
| auto Code = |
| R"( |
| class X { |
| template <class T> |
| void f(T t); |
| }; |
| template <class T> |
| void X::f(T t) {}; |
| )"; |
| Decl *FromTU1 = getTuDecl(Code, Lang_CXX, "input1.cc"); |
| auto *FromD1 = FirstDeclMatcher<FunctionTemplateDecl>().match( |
| FromTU1, functionTemplateDecl(hasName("f"))); |
| auto *ToD1 = Import(FromD1, Lang_CXX); |
| Decl *FromTU2 = getTuDecl(Code, Lang_CXX, "input2.cc"); |
| auto *FromD2 = FirstDeclMatcher<FunctionTemplateDecl>().match( |
| FromTU2, functionTemplateDecl(hasName("f"))); |
| auto *ToD2 = Import(FromD2, Lang_CXX); |
| EXPECT_EQ(ToD1, ToD2); |
| } |
| |
| TEST_P(ImportFunctionTemplates, |
| ImportFunctionWhenThereIsAFunTemplateWithSameName) { |
| getToTuDecl( |
| R"( |
| template <typename T> |
| void foo(T) {} |
| void foo(); |
| )", |
| Lang_CXX); |
| Decl *FromTU = getTuDecl("void foo();", Lang_CXX); |
| auto *FromD = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("foo"))); |
| auto *ImportedD = Import(FromD, Lang_CXX); |
| EXPECT_TRUE(ImportedD); |
| } |
| |
| TEST_P(ImportFunctionTemplates, |
| ImportConstructorWhenThereIsAFunTemplateWithSameName) { |
| auto Code = |
| R"( |
| struct Foo { |
| template <typename T> |
| Foo(T) {} |
| Foo(); |
| }; |
| )"; |
| getToTuDecl(Code, Lang_CXX); |
| Decl *FromTU = getTuDecl(Code, Lang_CXX); |
| auto *FromD = |
| LastDeclMatcher<CXXConstructorDecl>().match(FromTU, cxxConstructorDecl()); |
| auto *ImportedD = Import(FromD, Lang_CXX); |
| EXPECT_TRUE(ImportedD); |
| } |
| |
| TEST_P(ImportFunctionTemplates, |
| ImportOperatorWhenThereIsAFunTemplateWithSameName) { |
| getToTuDecl( |
| R"( |
| template <typename T> |
| void operator<(T,T) {} |
| struct X{}; |
| void operator<(X, X); |
| )", |
| Lang_CXX); |
| Decl *FromTU = getTuDecl( |
| R"( |
| struct X{}; |
| void operator<(X, X); |
| )", |
| Lang_CXX); |
| auto *FromD = LastDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasOverloadedOperatorName("<"))); |
| auto *ImportedD = Import(FromD, Lang_CXX); |
| EXPECT_TRUE(ImportedD); |
| } |
| |
| 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<FunctionDecl>().match(FromTU, Pattern); |
| |
| auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX)); |
| Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u); |
| EXPECT_FALSE(ImportedD->doesThisDeclarationHaveABody()); |
| auto *ToFD = LastDeclMatcher<FunctionDecl>().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<FunctionDecl>().match(FromTU, Pattern); |
| |
| auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX)); |
| Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u); |
| EXPECT_FALSE(ImportedD->doesThisDeclarationHaveABody()); |
| auto *ToFD = LastDeclMatcher<FunctionDecl>().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<FunctionDecl>().match(FromTU, Pattern); |
| |
| auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX)); |
| Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u); |
| EXPECT_TRUE(ImportedD->doesThisDeclarationHaveABody()); |
| auto *ToFD = LastDeclMatcher<FunctionDecl>().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<FunctionDecl>().match(FromTU, Pattern); |
| |
| auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX)); |
| Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u); |
| EXPECT_FALSE(ImportedD->doesThisDeclarationHaveABody()); |
| auto *ToFD = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern); |
| EXPECT_TRUE(ToFD->doesThisDeclarationHaveABody()); |
| EXPECT_EQ(ToFD->getPreviousDecl(), ImportedD); |
| } |
| |
| TEST_P(ImportFriendFunctions, 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<FunctionDecl>().match(FromTU, Pattern); |
| |
| auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX)); |
| Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u); |
| EXPECT_TRUE(ImportedD->doesThisDeclarationHaveABody()); |
| auto *InClassFD = cast<FunctionDecl>(FirstDeclMatcher<FriendDecl>() |
| .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()); |
| } |
| |
| TEST_P(ImportFriendFunctions, |
| 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<FunctionDecl>().match(FromTU, Pattern); |
| |
| auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX)); |
| Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u); |
| EXPECT_FALSE(ImportedD->doesThisDeclarationHaveABody()); |
| auto *OutOfClassFD = FirstDeclMatcher<FunctionDecl>().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<FunctionDecl>().match(FromTU, Pattern); |
| ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX)); |
| } |
| FunctionDecl *ImportedD1; |
| { |
| Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input1.cc"); |
| auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); |
| ImportedD1 = cast<FunctionDecl>(Import(FromD, Lang_CXX)); |
| } |
| |
| Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u); |
| EXPECT_TRUE(ImportedD->doesThisDeclarationHaveABody()); |
| EXPECT_FALSE(ImportedD1->doesThisDeclarationHaveABody()); |
| EXPECT_EQ(ImportedD1->getPreviousDecl(), ImportedD); |
| } |
| |
| TEST_P(ImportFriendFunctions, Lookup) { |
| auto FunctionPattern = functionDecl(hasName("f")); |
| auto ClassPattern = cxxRecordDecl(hasName("X")); |
| |
| TranslationUnitDecl *FromTU = |
| getTuDecl("struct X { friend void f(); };", Lang_CXX, "input0.cc"); |
| auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, FunctionPattern); |
| ASSERT_TRUE(FromD->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); |
| ASSERT_FALSE(FromD->isInIdentifierNamespace(Decl::IDNS_Ordinary)); |
| { |
| auto FromName = FromD->getDeclName(); |
| auto *Class = FirstDeclMatcher<CXXRecordDecl>().match(FromTU, ClassPattern); |
| auto LookupRes = Class->noload_lookup(FromName); |
| ASSERT_EQ(LookupRes.size(), 0u); |
| LookupRes = FromTU->noload_lookup(FromName); |
| ASSERT_EQ(LookupRes.size(), 1u); |
| } |
| |
| auto *ToD = cast<FunctionDecl>(Import(FromD, Lang_CXX)); |
| auto ToName = ToD->getDeclName(); |
| |
| TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| auto *Class = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, ClassPattern); |
| auto LookupRes = Class->noload_lookup(ToName); |
| EXPECT_EQ(LookupRes.size(), 0u); |
| LookupRes = ToTU->noload_lookup(ToName); |
| EXPECT_EQ(LookupRes.size(), 1u); |
| |
| EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, FunctionPattern), 1u); |
| auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, FunctionPattern); |
| EXPECT_TRUE(To0->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); |
| EXPECT_FALSE(To0->isInIdentifierNamespace(Decl::IDNS_Ordinary)); |
| } |
| |
| TEST_P(ImportFriendFunctions, DISABLED_LookupWithProtoAfter) { |
| auto FunctionPattern = functionDecl(hasName("f")); |
| auto ClassPattern = cxxRecordDecl(hasName("X")); |
| |
| TranslationUnitDecl *FromTU = getTuDecl( |
| "struct X { friend void f(); };" |
| // This proto decl makes f available to normal |
| // lookup, otherwise it is hidden. |
| // Normal C++ lookup (implemented in |
| // `clang::Sema::CppLookupName()` and in `LookupDirect()`) |
| // returns the found `NamedDecl` only if the set IDNS is matched |
| "void f();", |
| Lang_CXX, "input0.cc"); |
| auto *FromFriend = |
| FirstDeclMatcher<FunctionDecl>().match(FromTU, FunctionPattern); |
| auto *FromNormal = |
| LastDeclMatcher<FunctionDecl>().match(FromTU, |