| //===- 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 "clang/ASTMatchers/ASTMatchers.h" |
| #include "llvm/ADT/StringMap.h" |
| #include "llvm/Support/SmallVectorMemoryBuffer.h" |
| |
| #include "clang/AST/DeclContextInternals.h" |
| #include "gtest/gtest.h" |
| |
| #include "ASTImporterFixtures.h" |
| |
| namespace clang { |
| namespace ast_matchers { |
| |
| using internal::Matcher; |
| using internal::BindableMatcher; |
| using llvm::StringMap; |
| |
| static const RecordDecl *getRecordDeclOfFriend(FriendDecl *FD) { |
| QualType Ty = FD->getFriendType()->getType().getCanonicalType(); |
| return cast<RecordType>(Ty)->getDecl(); |
| } |
| |
| struct ImportExpr : TestImportBase {}; |
| struct ImportType : TestImportBase {}; |
| struct ImportDecl : TestImportBase {}; |
| struct ImportFixedPointExpr : ImportExpr {}; |
| |
| struct CanonicalRedeclChain : ASTImporterOptionSpecificTestBase {}; |
| |
| TEST_P(CanonicalRedeclChain, ShouldBeConsequentWithMatchers) { |
| Decl *FromTU = getTuDecl("void f();", Lang_CXX03); |
| 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_CXX03); |
| 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_CXX03); |
| 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_CXX03, |
| "class realDecl {};", Lang_CXX03, "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_CXX03, "class realDecl {};", Lang_CXX03, "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_CXX03); |
| 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()); |
| } |
| |
| const internal::VariadicDynCastAllOfMatcher<Stmt, SourceLocExpr> sourceLocExpr; |
| |
| AST_MATCHER_P(SourceLocExpr, hasBuiltinStr, StringRef, Str) { |
| return Node.getBuiltinStr() == Str; |
| } |
| |
| TEST_P(ImportExpr, ImportSourceLocExpr) { |
| MatchVerifier<Decl> Verifier; |
| testImport("void declToImport() { (void)__builtin_FILE(); }", Lang_CXX03, "", |
| Lang_CXX03, Verifier, |
| functionDecl(hasDescendant( |
| sourceLocExpr(hasBuiltinStr("__builtin_FILE"))))); |
| testImport("void declToImport() { (void)__builtin_COLUMN(); }", Lang_CXX03, |
| "", Lang_CXX03, Verifier, |
| functionDecl(hasDescendant( |
| sourceLocExpr(hasBuiltinStr("__builtin_COLUMN"))))); |
| } |
| |
| TEST_P(ImportExpr, ImportStringLiteral) { |
| MatchVerifier<Decl> Verifier; |
| testImport("void declToImport() { (void)\"foo\"; }", Lang_CXX03, "", |
| Lang_CXX03, Verifier, |
| functionDecl(hasDescendant( |
| stringLiteral(hasType(asString("const char [4]")))))); |
| testImport("void declToImport() { (void)L\"foo\"; }", Lang_CXX03, "", |
| Lang_CXX03, Verifier, |
| functionDecl(hasDescendant( |
| stringLiteral(hasType(asString("const wchar_t [4]")))))); |
| testImport("void declToImport() { (void) \"foo\" \"bar\"; }", Lang_CXX03, "", |
| Lang_CXX03, 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_C99, "", Lang_C99, Verifier, |
| functionDecl(hasDescendant(chooseExpr()))); |
| } |
| |
| TEST_P(ImportExpr, ImportGNUNullExpr) { |
| MatchVerifier<Decl> Verifier; |
| testImport("void declToImport() { (void)__null; }", Lang_CXX03, "", |
| Lang_CXX03, Verifier, |
| functionDecl(hasDescendant(gnuNullExpr(hasType(isInteger()))))); |
| } |
| |
| TEST_P(ImportExpr, ImportGenericSelectionExpr) { |
| MatchVerifier<Decl> Verifier; |
| |
| testImport( |
| "void declToImport() { int x; (void)_Generic(x, int: 0, float: 1); }", |
| Lang_C99, "", Lang_C99, Verifier, |
| functionDecl(hasDescendant(genericSelectionExpr()))); |
| } |
| |
| 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_C99, "", Lang_C99, |
| Verifier, |
| functionDecl(hasDescendant( |
| floatLiteral(equals(1.0), hasType(asString("double")))))); |
| testImport("void declToImport() { (void)1.0e-5f; }", Lang_C99, "", Lang_C99, |
| Verifier, |
| functionDecl(hasDescendant( |
| floatLiteral(equals(1.0e-5f), hasType(asString("float")))))); |
| } |
| |
| TEST_P(ImportFixedPointExpr, ImportFixedPointerLiteralExpr) { |
| MatchVerifier<Decl> Verifier; |
| testImport("void declToImport() { (void)1.0k; }", Lang_C99, "", Lang_C99, |
| Verifier, functionDecl(hasDescendant(fixedPointLiteral()))); |
| testImport("void declToImport() { (void)0.75r; }", Lang_C99, "", Lang_C99, |
| Verifier, functionDecl(hasDescendant(fixedPointLiteral()))); |
| } |
| |
| 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_CXX03, "", Lang_CXX03, 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_CXX03, "", |
| Lang_CXX03, 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_C99, "", Lang_C99, 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_C99, |
| "", Lang_C99, 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_CXX03, "", Lang_CXX03, 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_C99, "", Lang_C99, 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_C99, "", Lang_C99, Verifier, |
| traverse(TK_AsIs, |
| 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_CXX03, "", |
| Lang_CXX03, 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_CXX03, "", Lang_CXX03, |
| Verifier, |
| traverse(TK_AsIs, |
| 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_C99, "", Lang_C99, 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_CXX03, "", |
| Lang_CXX03, 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_CXX03, "", Lang_CXX03, 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_CXX03, "", Lang_CXX03, Verifier, |
| functionDecl(hasDescendant( |
| cStyleCastExpr(hasSourceExpression(vaArgExpr()))))); |
| } |
| |
| TEST_P(ImportExpr, CXXTemporaryObjectExpr) { |
| MatchVerifier<Decl> Verifier; |
| testImport( |
| "struct C {};" |
| "void declToImport() { C c = C(); }", |
| Lang_CXX03, "", Lang_CXX03, Verifier, |
| traverse(TK_AsIs, |
| 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_CXX03, "", |
| Lang_CXX03, 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_CXX03, "", Lang_CXX03, 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_CXX03, "", Lang_CXX03, 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, |
| traverse(TK_AsIs, |
| 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, |
| traverse(TK_AsIs, 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_CXX03, "", Lang_CXX03, Verifier, |
| classTemplateDecl(has(cxxRecordDecl(has( |
| fieldDecl(hasType(dependentTemplateSpecializationType()))))))); |
| } |
| |
| TEST_P(ImportType, ImportDeducedTemplateSpecialization) { |
| MatchVerifier<Decl> Verifier; |
| testImport("template <typename T>" |
| "class C { public: C(T); };" |
| "C declToImport(123);", |
| Lang_CXX17, "", Lang_CXX17, Verifier, |
| varDecl(hasType(deducedTemplateSpecializationType()))); |
| } |
| |
| 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)))))))))); |
| } |
| |
| const internal::VariadicDynCastAllOfMatcher<Stmt, CXXFoldExpr> cxxFoldExpr; |
| |
| AST_MATCHER_P(CXXFoldExpr, hasOperator, BinaryOperatorKind, Op) { |
| return Node.getOperator() == Op; |
| } |
| AST_MATCHER(CXXFoldExpr, hasInit) { return Node.getInit(); } |
| AST_MATCHER(CXXFoldExpr, isRightFold) { return Node.isRightFold(); } |
| AST_MATCHER(CXXFoldExpr, isLeftFold) { return Node.isLeftFold(); } |
| |
| TEST_P(ImportExpr, ImportCXXFoldExpr) { |
| auto Match1 = |
| cxxFoldExpr(hasOperator(BO_Add), isLeftFold(), unless(hasInit())); |
| auto Match2 = cxxFoldExpr(hasOperator(BO_Sub), isLeftFold(), hasInit()); |
| auto Match3 = |
| cxxFoldExpr(hasOperator(BO_Mul), isRightFold(), unless(hasInit())); |
| auto Match4 = cxxFoldExpr(hasOperator(BO_Div), isRightFold(), hasInit()); |
| |
| MatchVerifier<Decl> Verifier; |
| testImport("template <typename... Ts>" |
| "void declToImport(Ts... args) {" |
| " const int i1 = (... + args);" |
| " const int i2 = (1 - ... - args);" |
| " const int i3 = (args * ...);" |
| " const int i4 = (args / ... / 1);" |
| "};" |
| "void g() { declToImport(1, 2, 3, 4, 5); }", |
| Lang_CXX17, "", Lang_CXX17, Verifier, |
| functionTemplateDecl(hasDescendant(Match1), hasDescendant(Match2), |
| hasDescendant(Match3), |
| hasDescendant(Match4))); |
| } |
| |
| /// \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_C99, "", Lang_C99, 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, |
| traverse( |
| TK_AsIs, |
| 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_C99, "", Lang_C99, Verifier, |
| functionDecl(hasBody(compoundStmt( |
| has(declStmt(hasSingleDecl(varDecl(hasName("d"))))))))); |
| } |
| |
| TEST_P(ImportDecl, ImportedVarDeclPreservesThreadLocalStorage) { |
| MatchVerifier<Decl> Verifier; |
| testImport("thread_local int declToImport;", Lang_CXX11, "", Lang_CXX11, |
| Verifier, varDecl(hasThreadStorageDuration())); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ImportRecordTypeInFunc) { |
| Decl *FromTU = getTuDecl("int declToImport() { " |
| " struct data_t {int a;int b;};" |
| " struct data_t d;" |
| " return 0;" |
| "}", |
| Lang_C99, "input.c"); |
| auto *FromVar = |
| FirstDeclMatcher<VarDecl>().match(FromTU, varDecl(hasName("d"))); |
| ASSERT_TRUE(FromVar); |
| auto ToType = |
| ImportType(FromVar->getType().getCanonicalType(), FromVar, Lang_C99); |
| 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_C99, "input.c"); |
| auto *From = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("declToImport"))); |
| ASSERT_TRUE(From); |
| auto *To = Import(From, Lang_C99); |
| 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_C99, "input.c"); |
| auto *From = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("declToImport"))); |
| ASSERT_TRUE(From); |
| auto *To = Import(From, Lang_C99); |
| 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_C99, "input.c"); |
| auto *From = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("declToImport"))); |
| ASSERT_TRUE(From); |
| auto *To = Import(From, Lang_C99); |
| 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_CXX03, "", Lang_CXX03, Verifier, |
| functionDecl(hasDescendant(callExpr(has(cxxPseudoDestructorExpr()))))); |
| } |
| |
| TEST_P(ImportDecl, ImportUsingDecl) { |
| MatchVerifier<Decl> Verifier; |
| testImport("namespace foo { int bar; }" |
| "void declToImport() { using foo::bar; }", |
| Lang_CXX03, "", Lang_CXX03, 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_CXX03, "", Lang_CXX03, 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_CXX03, "", Lang_CXX03, 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_CXX03, "", Lang_CXX03, 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_CXX03, "", Lang_CXX03, 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_CXX03, "", Lang_CXX03, Verifier, |
| functionTemplateDecl(hasAncestor(translationUnitDecl( |
| unless(has(functionDecl(hasName("declToImport")))))))); |
| testImport("template <typename T> struct declToImport { T t; };" |
| "void instantiate() { declToImport<int>(); }", |
| Lang_CXX03, "", Lang_CXX03, 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_CXX03, "", Lang_CXX03, Verifier, |
| recordDecl(has(classTemplateDecl()), |
| has(classTemplateSpecializationDecl()))); |
| } |
| |
| TEST_P(ImportExpr, CXXOperatorCallExpr) { |
| MatchVerifier<Decl> Verifier; |
| testImport( |
| "class declToImport {" |
| " void f() { *this = declToImport(); }" |
| "};", |
| Lang_CXX03, "", Lang_CXX03, 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_CXX03, "", Lang_CXX03, Verifier, |
| classTemplateDecl(has(cxxRecordDecl( |
| has(fieldDecl(hasType(dependentSizedArrayType()))))))); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, TemplateTypeParmDeclNoDefaultArg) { |
| Decl *FromTU = getTuDecl("template<typename T> struct X {};", Lang_CXX03); |
| auto From = FirstDeclMatcher<TemplateTypeParmDecl>().match( |
| FromTU, templateTypeParmDecl(hasName("T"))); |
| TemplateTypeParmDecl *To = Import(From, Lang_CXX03); |
| ASSERT_FALSE(To->hasDefaultArgument()); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, TemplateTypeParmDeclDefaultArg) { |
| Decl *FromTU = |
| getTuDecl("template<typename T = int> struct X {};", Lang_CXX03); |
| auto From = FirstDeclMatcher<TemplateTypeParmDecl>().match( |
| FromTU, templateTypeParmDecl(hasName("T"))); |
| TemplateTypeParmDecl *To = Import(From, Lang_CXX03); |
| ASSERT_TRUE(To->hasDefaultArgument()); |
| QualType ToArg = To->getDefaultArgument(); |
| ASSERT_EQ(ToArg, QualType(To->getASTContext().IntTy)); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ImportBeginLocOfDeclRefExpr) { |
| Decl *FromTU = |
| getTuDecl("class A { public: static int X; }; void f() { (void)A::X; }", |
| Lang_CXX03); |
| 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_CXX03); |
| ASSERT_TRUE(To); |
| ASSERT_TRUE( |
| cast<CStyleCastExpr>(cast<CompoundStmt>(To->getBody())->body_front()) |
| ->getSubExpr() |
| ->getBeginLoc() |
| .isValid()); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| TemplateTemplateParmDeclNoDefaultArg) { |
| Decl *FromTU = getTuDecl(R"( |
| template<template<typename> typename TT> struct Y {}; |
| )", |
| Lang_CXX17); |
| auto From = FirstDeclMatcher<TemplateTemplateParmDecl>().match( |
| FromTU, templateTemplateParmDecl(hasName("TT"))); |
| TemplateTemplateParmDecl *To = Import(From, Lang_CXX17); |
| ASSERT_FALSE(To->hasDefaultArgument()); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, TemplateTemplateParmDeclDefaultArg) { |
| Decl *FromTU = getTuDecl(R"( |
| template<typename T> struct X {}; |
| template<template<typename> typename TT = X> struct Y {}; |
| )", |
| Lang_CXX17); |
| auto From = FirstDeclMatcher<TemplateTemplateParmDecl>().match( |
| FromTU, templateTemplateParmDecl(hasName("TT"))); |
| TemplateTemplateParmDecl *To = Import(From, Lang_CXX17); |
| ASSERT_TRUE(To->hasDefaultArgument()); |
| const TemplateArgument &ToDefaultArg = To->getDefaultArgument().getArgument(); |
| ASSERT_TRUE(To->isTemplateDecl()); |
| TemplateDecl *ToTemplate = ToDefaultArg.getAsTemplate().getAsTemplateDecl(); |
| |
| // Find the default argument template 'X' in the AST and compare it against |
| // the default argument we got. |
| auto ToExpectedDecl = FirstDeclMatcher<ClassTemplateDecl>().match( |
| To->getTranslationUnitDecl(), classTemplateDecl(hasName("X"))); |
| ASSERT_EQ(ToTemplate, ToExpectedDecl); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, NonTypeTemplateParmDeclNoDefaultArg) { |
| Decl *FromTU = getTuDecl("template<int N> struct X {};", Lang_CXX03); |
| auto From = FirstDeclMatcher<NonTypeTemplateParmDecl>().match( |
| FromTU, nonTypeTemplateParmDecl(hasName("N"))); |
| NonTypeTemplateParmDecl *To = Import(From, Lang_CXX03); |
| ASSERT_FALSE(To->hasDefaultArgument()); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, NonTypeTemplateParmDeclDefaultArg) { |
| Decl *FromTU = getTuDecl("template<int S = 1> struct X {};", Lang_CXX03); |
| auto From = FirstDeclMatcher<NonTypeTemplateParmDecl>().match( |
| FromTU, nonTypeTemplateParmDecl(hasName("S"))); |
| NonTypeTemplateParmDecl *To = Import(From, Lang_CXX03); |
| ASSERT_TRUE(To->hasDefaultArgument()); |
| Stmt *ToArg = To->getDefaultArgument(); |
| ASSERT_TRUE(isa<ConstantExpr>(ToArg)); |
| ToArg = *ToArg->child_begin(); |
| ASSERT_TRUE(isa<IntegerLiteral>(ToArg)); |
| ASSERT_EQ(cast<IntegerLiteral>(ToArg)->getValue().getLimitedValue(), 1U); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| ImportOfTemplatedDeclOfClassTemplateDecl) { |
| Decl *FromTU = getTuDecl("template<class X> struct S{};", Lang_CXX03); |
| auto From = |
| FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, classTemplateDecl()); |
| ASSERT_TRUE(From); |
| auto To = cast<ClassTemplateDecl>(Import(From, Lang_CXX03)); |
| ASSERT_TRUE(To); |
| Decl *ToTemplated = To->getTemplatedDecl(); |
| Decl *ToTemplated1 = Import(From->getTemplatedDecl(), Lang_CXX03); |
| EXPECT_TRUE(ToTemplated1); |
| EXPECT_EQ(ToTemplated1, ToTemplated); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| ImportOfTemplatedDeclOfFunctionTemplateDecl) { |
| Decl *FromTU = getTuDecl("template<class X> void f(){}", Lang_CXX03); |
| auto From = FirstDeclMatcher<FunctionTemplateDecl>().match( |
| FromTU, functionTemplateDecl()); |
| ASSERT_TRUE(From); |
| auto To = cast<FunctionTemplateDecl>(Import(From, Lang_CXX03)); |
| ASSERT_TRUE(To); |
| Decl *ToTemplated = To->getTemplatedDecl(); |
| Decl *ToTemplated1 = Import(From->getTemplatedDecl(), Lang_CXX03); |
| EXPECT_TRUE(ToTemplated1); |
| EXPECT_EQ(ToTemplated1, ToTemplated); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| ImportOfTemplatedDeclShouldImportTheClassTemplateDecl) { |
| Decl *FromTU = getTuDecl("template<class X> struct S{};", Lang_CXX03); |
| auto FromFT = |
| FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, classTemplateDecl()); |
| ASSERT_TRUE(FromFT); |
| |
| auto ToTemplated = |
| cast<CXXRecordDecl>(Import(FromFT->getTemplatedDecl(), Lang_CXX03)); |
| 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_CXX03); |
| auto FromFT = FirstDeclMatcher<FunctionTemplateDecl>().match( |
| FromTU, functionTemplateDecl()); |
| ASSERT_TRUE(FromFT); |
| |
| auto ToTemplated = |
| cast<FunctionDecl>(Import(FromFT->getTemplatedDecl(), Lang_CXX03)); |
| 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_CXX03); |
| auto FromNs = |
| FirstDeclMatcher<NamespaceDecl>().match(FromTU, namespaceDecl()); |
| auto ToNs = cast<NamespaceDecl>(Import(FromNs, Lang_CXX03)); |
| 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_CXX03)); |
| 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_C99, |
| "", Lang_C99); |
| |
| 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, ImportGenericSelectionExpr) { |
| Decl *From, *To; |
| std::tie(From, To) = getImportedDecl( |
| R"( |
| int declToImport() { |
| int x; |
| return _Generic(x, int: 0, default: 1); |
| } |
| )", |
| Lang_C99, "", Lang_C99); |
| |
| auto ToResults = |
| match(genericSelectionExpr().bind("expr"), To->getASTContext()); |
| auto FromResults = |
| match(genericSelectionExpr().bind("expr"), From->getASTContext()); |
| |
| const GenericSelectionExpr *FromGenericSelectionExpr = |
| selectFirst<GenericSelectionExpr>("expr", FromResults); |
| ASSERT_TRUE(FromGenericSelectionExpr); |
| |
| const GenericSelectionExpr *ToGenericSelectionExpr = |
| selectFirst<GenericSelectionExpr>("expr", ToResults); |
| ASSERT_TRUE(ToGenericSelectionExpr); |
| |
| EXPECT_EQ(FromGenericSelectionExpr->isResultDependent(), |
| ToGenericSelectionExpr->isResultDependent()); |
| EXPECT_EQ(FromGenericSelectionExpr->getResultIndex(), |
| ToGenericSelectionExpr->getResultIndex()); |
| } |
| |
| 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_CXX03, "", Lang_CXX03); |
| |
| 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_CXX03, "", Lang_CXX03); |
| |
| 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_CXX03, "", Lang_CXX03); |
| |
| 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_CXX03, "", Lang_CXX03); |
| |
| // 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_CXX03, "", Lang_CXX03, "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_CXX03, "", Lang_CXX03); |
| |
| 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_CXX03, "", Lang_CXX03); |
| |
| 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_CXX03, "", Lang_CXX03); |
| |
| 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_CXX03, "", Lang_CXX03); |
| |
| 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_CXX03); |
| Decl *From = LastDeclMatcher<Decl>{}.match(FromTU, functionDecl()); |
| const Decl *To = Import(From, Lang_CXX03); |
| 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_CXX03, "", Lang_CXX03); |
| |
| 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_CXX03, "input0.cc"); |
| auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match( |
| FromTU, classTemplateDecl(hasName("B"))); |
| |
| Import(FromD, Lang_CXX03); |
| } |
| |
| { |
| Decl *FromTU = getTuDecl( |
| R"( |
| template <typename T> |
| struct B { |
| void f(); |
| }; |
| )", |
| Lang_CXX03, "input1.cc"); |
| FunctionDecl *FromD = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("f"))); |
| Import(FromD, Lang_CXX03); |
| auto *FromCTD = FirstDeclMatcher<ClassTemplateDecl>().match( |
| FromTU, classTemplateDecl(hasName("B"))); |
| auto *ToCTD = cast<ClassTemplateDecl>(Import(FromCTD, Lang_CXX03)); |
| 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_CXX03); |
| 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_CXX03, "input1.cc"); |
| ClassTemplateDecl *FromD = FirstDeclMatcher<ClassTemplateDecl>().match( |
| FromTU, classTemplateDecl(hasName("B"))); |
| |
| Import(FromD, Lang_CXX03); |
| |
| // 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_CXX03); |
| ASSERT_EQ(2u, DeclCounter<CXXRecordDecl>().match( |
| ToTU, cxxRecordDecl(unless(isImplicit())))); |
| |
| Decl *FromTU = getTuDecl( |
| R"( |
| struct B { |
| void f(); |
| }; |
| )", |
| Lang_CXX03, "input1.cc"); |
| auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match( |
| FromTU, cxxRecordDecl(hasName("B"))); |
| |
| Import(FromD, Lang_CXX03); |
| |
| 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_CXX03); |
| auto FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl()); |
| auto ToD = Import(FromD, Lang_CXX03); |
| |
| 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_CXX03); |
| auto FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl()); |
| auto ToD = Import(FromD, Lang_CXX03); |
| |
| 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_CXX03); |
| // 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_CXX03, "input1.cc"); |
| auto *FromD = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match( |
| FromTU, classTemplateSpecializationDecl(hasName("B"))); |
| |
| Import(FromD, Lang_CXX03); |
| |
| // 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_CXX03, "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_CXX03); |
| auto *To1 = Import(From1, Lang_CXX03); |
| |
| 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_C99, "input0.c"); |
| |
| Decl *FromTU1 = getTuDecl(Code, Lang_C99, "input1.c"); |
| |
| auto *X0 = |
| FirstDeclMatcher<RecordDecl>().match(FromTU0, recordDecl(hasName("X"))); |
| auto *X1 = |
| FirstDeclMatcher<RecordDecl>().match(FromTU1, recordDecl(hasName("X"))); |
| Import(X0, Lang_C99); |
| Import(X1, Lang_C99); |
| |
| 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_C99, "input0.c"); |
| |
| Decl *FromTU1 = getTuDecl( |
| R"( |
| struct X { // reversed order |
| struct { int b; }; |
| struct { int a; }; |
| }; |
| )", |
| Lang_C99, "input1.c"); |
| |
| auto *X0 = |
| FirstDeclMatcher<RecordDecl>().match(FromTU0, recordDecl(hasName("X"))); |
| auto *X1 = |
| FirstDeclMatcher<RecordDecl>().match(FromTU1, recordDecl(hasName("X"))); |
| Import(X0, Lang_C99); |
| Import(X1, Lang_C99); |
| |
| 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_CXX03, "input0.cc"); |
| auto *FromD = FirstDeclMatcher<VarDecl>().match(FromTU, Pattern); |
| Imported1 = cast<VarDecl>(Import(FromD, Lang_CXX03)); |
| } |
| VarDecl *Imported2; |
| { |
| Decl *FromTU = getTuDecl("int x;", Lang_CXX03, "input1.cc"); |
| auto *FromD = FirstDeclMatcher<VarDecl>().match(FromTU, Pattern); |
| Imported2 = cast<VarDecl>(Import(FromD, Lang_CXX03)); |
| } |
| EXPECT_EQ(Imported1->getCanonicalDecl(), Imported2->getCanonicalDecl()); |
| EXPECT_FALSE(Imported2->isUsed(false)); |
| { |
| Decl *FromTU = getTuDecl("extern int x; int f() { return x; }", Lang_CXX03, |
| "input2.cc"); |
| auto *FromD = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("f"))); |
| Import(FromD, Lang_CXX03); |
| } |
| EXPECT_TRUE(Imported2->isUsed(false)); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ImportDoesUpdateUsedFlag2) { |
| auto Pattern = varDecl(hasName("x")); |
| VarDecl *ExistingD; |
| { |
| Decl *ToTU = getToTuDecl("int x = 1;", Lang_CXX03); |
| ExistingD = FirstDeclMatcher<VarDecl>().match(ToTU, Pattern); |
| } |
| EXPECT_FALSE(ExistingD->isUsed(false)); |
| { |
| Decl *FromTU = |
| getTuDecl("int x = 1; int f() { return x; }", Lang_CXX03, "input1.cc"); |
| auto *FromD = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("f"))); |
| Import(FromD, Lang_CXX03); |
| } |
| 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_CXX03); |
| 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_CXX03, "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_CXX03); |
| } |
| EXPECT_TRUE(ExistingD->isUsed(false)); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ReimportWithUsedFlag) { |
| auto Pattern = varDecl(hasName("x")); |
| |
| Decl *FromTU = getTuDecl("int x;", Lang_CXX03, "input0.cc"); |
| auto *FromD = FirstDeclMatcher<VarDecl>().match(FromTU, Pattern); |
| |
| auto *Imported1 = cast<VarDecl>(Import(FromD, Lang_CXX03)); |
| |
| ASSERT_FALSE(Imported1->isUsed(false)); |
| |
| FromD->setIsUsed(); |
| auto *Imported2 = cast<VarDecl>(Import(FromD, Lang_CXX03)); |
| |
| 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_CXX03); |
| auto Pattern = functionDecl(hasName("f")); |
| auto *From = |
| FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); // Proto |
| |
| Decl *ImportedD = Import(From, Lang_CXX03); |
| 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_CXX03); |
| auto Pattern = functionDecl(hasName("f")); |
| auto *From = |
| LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern); // Def |
| |
| Decl *ImportedD = Import(From, Lang_CXX03); |
| 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_CXX03); |
| CXXMethodDecl *Proto = |
| FirstDeclMatcher<CXXMethodDecl>().match(FromTU, Pattern); |
| |
| ASSERT_EQ(Proto->size_overridden_methods(), 1u); |
| CXXMethodDecl *To = cast<CXXMethodDecl>(Import(Proto, Lang_CXX03)); |
| 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_CXX03); |
| 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_CXX03)); |
| EXPECT_TRUE(To->isVirtual()); |
| } |
| |
| TEST_P(ImportFunctions, |
| ImportDefinitionIfThereIsAnExistingDefinitionAndFwdDecl) { |
| Decl *ToTU = getToTuDecl( |
| R"( |
| void f() {} |
| void f(); |
| )", |
| Lang_CXX03); |
| ASSERT_EQ(1u, |
| DeclCounterWithPredicate<FunctionDecl>([](const FunctionDecl *FD) { |
| return FD->doesThisDeclarationHaveABody(); |
| }).match(ToTU, functionDecl())); |
| |
| Decl *FromTU = getTuDecl("void f() {}", Lang_CXX03, "input0.cc"); |
| auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl()); |
| |
| Import(FromD, Lang_CXX03); |
| |
| 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_CXX03); |
| auto *DF = FirstDeclMatcher<CXXMethodDecl>().match(FromTU0, DFP); |
| Import(DF, Lang_CXX03); |
| |
| Decl *FromTU1 = getTuDecl(Code, Lang_CXX03, "input1.cc"); |
| auto *BF = FirstDeclMatcher<CXXMethodDecl>().match(FromTU1, BFP); |
| Import(BF, Lang_CXX03); |
| |
| 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_CXX03, "input0.cc"); |
| auto *FromD = FirstDeclMatcher<CXXMethodDecl>().match(FromTU, DFP); |
| Import(FromD, Lang_CXX03); |
| } |
| { |
| Decl *FromTU = getTuDecl(CodeWithoutDef, Lang_CXX03, "input1.cc"); |
| auto *FromB = FirstDeclMatcher<CXXMethodDecl>().match(FromTU, BFP); |
| Import(FromB, Lang_CXX03); |
| } |
| |
| 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_CXX03); |
| auto *D = FirstDeclMatcher<CXXMethodDecl>().match(FromTU0, DFP); |
| Import(D, Lang_CXX03); |
| |
| Decl *FromTU1 = getTuDecl(Code, Lang_CXX03, "input1.cc"); |
| auto *B = FirstDeclMatcher<CXXMethodDecl>().match(FromTU1, BFP); |
| Import(B, Lang_CXX03); |
| |
| 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_CXX03, "input0.cc"); |
| auto *D = FirstDeclMatcher<CXXMethodDecl>().match(FromTU0, DFP); |
| Import(D, Lang_CXX03); |
| } |
| |
| { |
| Decl *FromTU1 = getTuDecl(CodeTU1, Lang_CXX03, "input1.cc"); |
| auto *Foo = FirstDeclMatcher<FunctionDecl>().match(FromTU1, FooDef); |
| Import(Foo, Lang_CXX03); |
| } |
| |
| 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_C99, "input0.c"); |
| |
| auto *From0 = FirstDeclMatcher<VarDecl>().match(FromTu, Pattern); |
| auto *From1 = LastDeclMatcher<VarDecl>().match(FromTu, Pattern); |
| |
| auto *To0 = Import(From0, Lang_C99); |
| auto *To1 = Import(From1, Lang_C99); |
| |
| 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_CXX03, "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_CXX03); |
| auto *ToF1 = Import(FromF1, Lang_CXX03); |
| |
| 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_CXX03, "input0.cc"); |
| auto *FromD = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("g0"))); |
| |
| Import(FromD, Lang_CXX03); |
| } |
| { |
| Decl *FromTU = |
| getTuDecl("namespace { void f() { int a; } } void g1() { f(); }", |
| Lang_CXX03, "input1.cc"); |
| auto *FromD = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("g1"))); |
| Import(FromD, Lang_CXX03); |
| } |
| |
| 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_CXX03); |
| 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_CXX03); |
| auto *FromD = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("f"))); |
| auto *ToD = Import(FromD, Lang_CXX03); |
| 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_CXX03); |
| auto *FromD = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("g"))); |
| auto *ToD = Import(FromD, Lang_CXX03); |
| 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_CXX03, "input1.cc"); |
| auto *FromD1 = FirstDeclMatcher<FunctionTemplateDecl>().match( |
| FromTU1, functionTemplateDecl(hasName("f"))); |
| auto *ToD1 = Import(FromD1, Lang_CXX03); |
| Decl *FromTU2 = getTuDecl(Code, Lang_CXX03, "input2.cc"); |
| auto *FromD2 = FirstDeclMatcher<FunctionTemplateDecl>().match( |
| FromTU2, functionTemplateDecl(hasName("f"))); |
| auto *ToD2 = Import(FromD2, Lang_CXX03); |
| 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_CXX03, "input1.cc"); |
| auto *FromD1 = FirstDeclMatcher<FunctionTemplateDecl>().match( |
| FromTU1, functionTemplateDecl(hasName("f"))); |
| auto *ToD1 = Import(FromD1, Lang_CXX03); |
| Decl *FromTU2 = getTuDecl(Code, Lang_CXX03, "input2.cc"); |
| auto *FromD2 = FirstDeclMatcher<FunctionTemplateDecl>().match( |
| FromTU2, functionTemplateDecl(hasName("f"))); |
| auto *ToD2 = Import(FromD2, Lang_CXX03); |
| EXPECT_EQ(ToD1, ToD2); |
| } |
| |
| TEST_P(ImportFunctionTemplates, |
| ImportFunctionWhenThereIsAFunTemplateWithSameName) { |
| getToTuDecl( |
| R"( |
| template <typename T> |
| void foo(T) {} |
| void foo(); |
| )", |
| Lang_CXX03); |
| Decl *FromTU = getTuDecl("void foo();", Lang_CXX03); |
| auto *FromD = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("foo"))); |
| auto *ImportedD = Import(FromD, Lang_CXX03); |
| EXPECT_TRUE(ImportedD); |
| } |
| |
| TEST_P(ImportFunctionTemplates, |
| ImportConstructorWhenThereIsAFunTemplateWithSameName) { |
| auto Code = |
| R"( |
| struct Foo { |
| template <typename T> |
| Foo(T) {} |
| Foo(); |
| }; |
| )"; |
| getToTuDecl(Code, Lang_CXX03); |
| Decl *FromTU = getTuDecl(Code, Lang_CXX03); |
| auto *FromD = |
| LastDeclMatcher<CXXConstructorDecl>().match(FromTU, cxxConstructorDecl()); |
| auto *ImportedD = Import(FromD, Lang_CXX03); |
| EXPECT_TRUE(ImportedD); |
| } |
| |
| TEST_P(ImportFunctionTemplates, |
| ImportOperatorWhenThereIsAFunTemplateWithSameName) { |
| getToTuDecl( |
| R"( |
| template <typename T> |
| void operator<(T,T) {} |
| struct X{}; |
| void operator<(X, X); |
| )", |
| Lang_CXX03); |
| Decl *FromTU = getTuDecl( |
| R"( |
| struct X{}; |
| void operator<(X, X); |
| )", |
| Lang_CXX03); |
| auto *FromD = LastDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasOverloadedOperatorName("<"))); |
| auto *ImportedD = Import(FromD, Lang_CXX03); |
| 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_CXX03, "input0.cc"); |
| auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); |
| |
| auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX03)); |
| 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_CXX03, "input0.cc"); |
| auto FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); |
| |
| auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX03)); |
| 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_CXX03, "input0.cc"); |
| auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); |
| |
| auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX03)); |
| 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_CXX03, "input0.cc"); |
| auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); |
| |
| auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX03)); |
| 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_CXX03, "input0.cc"); |
| auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); |
| |
| auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX03)); |
| 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_CXX03, "input0.cc"); |
| auto *FromD = LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern); |
| |
| auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX03)); |
| 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_CXX03, "input0.cc"); |
| auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); |
| ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX03)); |
| } |
| FunctionDecl *ImportedD1; |
| { |
| Decl *FromTU = getTuDecl("void f();", Lang_CXX03, "input1.cc"); |
| auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); |
| ImportedD1 = cast<FunctionDecl>(Import(FromD, Lang_CXX03)); |
| } |
| |
| 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_CXX03, "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_TRUE(LookupRes.empty()); |
| LookupRes = FromTU->noload_lookup(FromName); |
| ASSERT_TRUE(LookupRes.isSingleResult()); |
| } |
| |
| auto *ToD = cast<FunctionDecl>(Import(FromD, Lang_CXX03)); |
| auto ToName = ToD->getDeclName(); |
| |
| TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| auto *Class = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, ClassPattern); |
| auto LookupRes = Class->noload_lookup(ToName); |
| EXPECT_TRUE(LookupRes.empty()); |
| LookupRes = ToTU->noload_lookup(ToName); |
| EXPECT_TRUE(LookupRes.isSingleResult()); |
| |
| 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, 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_CXX03, "input0.cc"); |
| auto *FromFriend = |
| FirstDeclMatcher<FunctionDecl>().match(FromTU, FunctionPattern); |
| auto *FromNormal = |
| LastDeclMatcher<FunctionDecl>().match(FromTU, FunctionPattern); |
| ASSERT_TRUE(FromFriend->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); |
| ASSERT_FALSE(FromFriend->isInIdentifierNamespace(Decl::IDNS_Ordinary)); |
| ASSERT_FALSE(FromNormal->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); |
| ASSERT_TRUE(FromNormal->isInIdentifierNamespace(Decl::IDNS_Ordinary)); |
| |
| auto FromName = FromFriend->getDeclName(); |
| auto *FromClass = |
| FirstDeclMatcher<CXXRecordDecl>().match(FromTU, ClassPattern); |
| auto LookupRes = FromClass->noload_lookup(FromName); |
| ASSERT_TRUE(LookupRes.empty()); |
| LookupRes = FromTU->noload_lookup(FromName); |
| ASSERT_TRUE(LookupRes.isSingleResult()); |
| |
| auto *ToFriend = cast<FunctionDecl>(Import(FromFriend, Lang_CXX03)); |
| auto ToName = ToFriend->getDeclName(); |
| |
| TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| auto *ToClass = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, ClassPattern); |
| LookupRes = ToClass->noload_lookup(ToName); |
| EXPECT_TRUE(LookupRes.empty()); |
| LookupRes = ToTU->noload_lookup(ToName); |
| // Test is disabled because this result is 2. |
| EXPECT_TRUE(LookupRes.isSingleResult()); |
| |
| ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, FunctionPattern), 2u); |
| ToFriend = FirstDeclMatcher<FunctionDecl>().match(ToTU, FunctionPattern); |
| auto *ToNormal = LastDeclMatcher<FunctionDecl>().match(ToTU, FunctionPattern); |
| EXPECT_TRUE(ToFriend->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); |
| EXPECT_FALSE(ToFriend->isInIdentifierNamespace(Decl::IDNS_Ordinary)); |
| EXPECT_FALSE(ToNormal->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); |
| EXPECT_TRUE(ToNormal->isInIdentifierNamespace(Decl::IDNS_Ordinary)); |
| } |
| |
| TEST_P(ImportFriendFunctions, LookupWithProtoBefore) { |
| auto FunctionPattern = functionDecl(hasName("f")); |
| auto ClassPattern = cxxRecordDecl(hasName("X")); |
| |
| TranslationUnitDecl *FromTU = getTuDecl("void f();" |
| "struct X { friend void f(); };", |
| Lang_CXX03, "input0.cc"); |
| auto *FromNormal = |
| FirstDeclMatcher<FunctionDecl>().match(FromTU, FunctionPattern); |
| auto *FromFriend = |
| LastDeclMatcher<FunctionDecl>().match(FromTU, FunctionPattern); |
| ASSERT_FALSE(FromNormal->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); |
| ASSERT_TRUE(FromNormal->isInIdentifierNamespace(Decl::IDNS_Ordinary)); |
| ASSERT_TRUE(FromFriend->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); |
| ASSERT_TRUE(FromFriend->isInIdentifierNamespace(Decl::IDNS_Ordinary)); |
| |
| auto FromName = FromNormal->getDeclName(); |
| auto *FromClass = |
| FirstDeclMatcher<CXXRecordDecl>().match(FromTU, ClassPattern); |
| auto LookupRes = FromClass->noload_lookup(FromName); |
| ASSERT_TRUE(LookupRes.empty()); |
| LookupRes = FromTU->noload_lookup(FromName); |
| ASSERT_TRUE(LookupRes.isSingleResult()); |
| |
| auto *ToNormal = cast<FunctionDecl>(Import(FromNormal, Lang_CXX03)); |
| auto ToName = ToNormal->getDeclName(); |
| TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| |
| auto *ToClass = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, ClassPattern); |
| LookupRes = ToClass->noload_lookup(ToName); |
| EXPECT_TRUE(LookupRes.empty()); |
| LookupRes = ToTU->noload_lookup(ToName); |
| EXPECT_TRUE(LookupRes.isSingleResult()); |
| |
| EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, FunctionPattern), 2u); |
| ToNormal = FirstDeclMatcher<FunctionDecl>().match(ToTU, FunctionPattern); |
| auto *ToFriend = LastDeclMatcher<FunctionDecl>().match(ToTU, FunctionPattern); |
| EXPECT_FALSE(ToNormal->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); |
| EXPECT_TRUE(ToNormal->isInIdentifierNamespace(Decl::IDNS_Ordinary)); |
| EXPECT_TRUE(ToFriend->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); |
| EXPECT_TRUE(ToFriend->isInIdentifierNamespace(Decl::IDNS_Ordinary)); |
| } |
| |
| TEST_P(ImportFriendFunctions, ImportFriendChangesLookup) { |
| auto Pattern = functionDecl(hasName("f")); |
| |
| TranslationUnitDecl *FromNormalTU = |
| getTuDecl("void f();", Lang_CXX03, "input0.cc"); |
| auto *FromNormalF = |
| FirstDeclMatcher<FunctionDecl>().match(FromNormalTU, Pattern); |
| TranslationUnitDecl *FromFriendTU = |
| getTuDecl("class X { friend void f(); };", Lang_CXX03, "input1.cc"); |
| auto *FromFriendF = |
| FirstDeclMatcher<FunctionDecl>().match(FromFriendTU, Pattern); |
| auto FromNormalName = FromNormalF->getDeclName(); |
| auto FromFriendName = FromFriendF->getDeclName(); |
| |
| ASSERT_TRUE(FromNormalF->isInIdentifierNamespace(Decl::IDNS_Ordinary)); |
| ASSERT_FALSE(FromNormalF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); |
| ASSERT_FALSE(FromFriendF->isInIdentifierNamespace(Decl::IDNS_Ordinary)); |
| ASSERT_TRUE(FromFriendF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); |
| auto LookupRes = FromNormalTU->noload_lookup(FromNormalName); |
| ASSERT_TRUE(LookupRes.isSingleResult()); |
| LookupRes = FromFriendTU->noload_lookup(FromFriendName); |
| ASSERT_TRUE(LookupRes.isSingleResult()); |
| |
| auto *ToNormalF = cast<FunctionDecl>(Import(FromNormalF, Lang_CXX03)); |
| TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| auto ToName = ToNormalF->getDeclName(); |
| EXPECT_TRUE(ToNormalF->isInIdentifierNamespace(Decl::IDNS_Ordinary)); |
| EXPECT_FALSE(ToNormalF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); |
| LookupRes = ToTU->noload_lookup(ToName); |
| EXPECT_TRUE(LookupRes.isSingleResult()); |
| EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u); |
| |
| auto *ToFriendF = cast<FunctionDecl>(Import(FromFriendF, Lang_CXX03)); |
| LookupRes = ToTU->noload_lookup(ToName); |
| EXPECT_TRUE(LookupRes.isSingleResult()); |
| EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u); |
| |
| EXPECT_TRUE(ToNormalF->isInIdentifierNamespace(Decl::IDNS_Ordinary)); |
| EXPECT_FALSE(ToNormalF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); |
| |
| EXPECT_TRUE(ToFriendF->isInIdentifierNamespace(Decl::IDNS_Ordinary)); |
| EXPECT_TRUE(ToFriendF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); |
| } |
| |
| TEST_P(ImportFriendFunctions, ImportFriendList) { |
| TranslationUnitDecl *FromTU = getTuDecl("struct X { friend void f(); };" |
| "void f();", |
| Lang_CXX03, "input0.cc"); |
| auto *FromFriendF = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("f"))); |
| |
| auto *FromClass = FirstDeclMatcher<CXXRecordDecl>().match( |
| FromTU, cxxRecordDecl(hasName("X"))); |
| auto *FromFriend = FirstDeclMatcher<FriendDecl>().match(FromTU, friendDecl()); |
| auto FromFriends = FromClass->friends(); |
| unsigned int FrN = 0; |
| for (auto Fr : FromFriends) { |
| ASSERT_EQ(Fr, FromFriend); |
| ++FrN; |
| } |
| ASSERT_EQ(FrN, 1u); |
| |
| Import(FromFriendF, Lang_CXX03); |
| TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| auto *ToClass = FirstDeclMatcher<CXXRecordDecl>().match( |
| ToTU, cxxRecordDecl(hasName("X"))); |
| auto *ToFriend = FirstDeclMatcher<FriendDecl>().match(ToTU, friendDecl()); |
| auto ToFriends = ToClass->friends(); |
| FrN = 0; |
| for (auto Fr : ToFriends) { |
| EXPECT_EQ(Fr, ToFriend); |
| ++FrN; |
| } |
| EXPECT_EQ(FrN, 1u); |
| } |
| |
| AST_MATCHER_P(TagDecl, hasTypedefForAnonDecl, Matcher<TypedefNameDecl>, |
| 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_C99}}, |
| |
| {"foo.c", |
| {"typedef enum { THING_VALUE } thing_t;" |
| "void conflict(thing_t type);" |
| "void foo() { (void)THING_VALUE; }" |
| "void conflict(thing_t type) {}", |
| Lang_C99}}, |
| |
| {"moo.c", |
| {"typedef enum { THING_VALUE } thing_t;" |
| "void conflict(thing_t type);" |
| "void moo() { conflict(THING_VALUE); }", |
| Lang_C99}}}; |
| |
| 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); |
| } |
| |
| TEST_P(ImportDecl, ImportFieldOrder) { |
| MatchVerifier<Decl> Verifier; |
| testImport("struct declToImport {" |
| " int b = a + 2;" |
| " int a = 5;" |
| "};", |
| Lang_CXX11, "", Lang_CXX11, Verifier, |
| recordDecl(hasFieldOrder({"b", "a"}))); |
| } |
| |
| const internal::VariadicDynCastAllOfMatcher<Expr, DependentScopeDeclRefExpr> |
| dependentScopeDeclRefExpr; |
| |
| TEST_P(ImportExpr, DependentScopeDeclRefExpr) { |
| MatchVerifier<Decl> Verifier; |
| testImport("template <typename T> struct S { static T foo; };" |
| "template <typename T> void declToImport() {" |
| " (void) S<T>::foo;" |
| "}" |
| "void instantiate() { declToImport<int>(); }" |
| "template <typename T> T S<T>::foo;", |
| Lang_CXX11, "", Lang_CXX11, Verifier, |
| functionTemplateDecl(has(functionDecl(has(compoundStmt( |
| has(cStyleCastExpr(has(dependentScopeDeclRefExpr()))))))))); |
| |
| testImport("template <typename T> struct S {" |
| "template<typename S> static void foo(){};" |
| "};" |
| "template <typename T> void declToImport() {" |
| " S<T>::template foo<T>();" |
| "}" |
| "void instantiate() { declToImport<int>(); }", |
| Lang_CXX11, "", Lang_CXX11, Verifier, |
| functionTemplateDecl(has(functionDecl(has(compoundStmt( |
| has(callExpr(has(dependentScopeDeclRefExpr()))))))))); |
| } |
| |
| const internal::VariadicDynCastAllOfMatcher<Type, DependentNameType> |
| dependentNameType; |
| |
| TEST_P(ImportExpr, DependentNameType) { |
| MatchVerifier<Decl> Verifier; |
| testImport("template <typename T> struct declToImport {" |
| " typedef typename T::type dependent_name;" |
| "};", |
| Lang_CXX11, "", Lang_CXX11, Verifier, |
| classTemplateDecl(has( |
| cxxRecordDecl(has(typedefDecl(has(dependentNameType()))))))); |
| } |
| |
| TEST_P(ImportExpr, UnresolvedMemberExpr) { |
| MatchVerifier<Decl> Verifier; |
| testImport("struct S { template <typename T> void mem(); };" |
| "template <typename U> void declToImport() {" |
| " S s;" |
| " s.mem<U>();" |
| "}" |
| "void instantiate() { declToImport<int>(); }", |
| Lang_CXX11, "", Lang_CXX11, Verifier, |
| functionTemplateDecl(has(functionDecl(has( |
| compoundStmt(has(callExpr(has(unresolvedMemberExpr()))))))))); |
| } |
| |
| class ImportImplicitMethods : public ASTImporterOptionSpecificTestBase { |
| 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 <typename MatcherType> |
| void testImportOf( |
| const MatcherType &MethodMatcher, const char *Code = DefaultCode) { |
| test(MethodMatcher, Code, /*ExpectedCount=*/1u); |
| } |
| |
| template <typename MatcherType> |
| void testNoImportOf( |
| const MatcherType &MethodMatcher, const char *Code = DefaultCode) { |
| test(MethodMatcher, Code, /*ExpectedCount=*/0u); |
| } |
| |
| private: |
| template <typename MatcherType> |
| 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<CXXRecordDecl>().match( |
| ToTU, ClassMatcher); |
| |
| ASSERT_EQ(DeclCounter<CXXMethodDecl>().match(ToClass, MethodMatcher), 1u); |
| |
| { |
| CXXMethodDecl *Method = |
| FirstDeclMatcher<CXXMethodDecl>().match(ToClass, MethodMatcher); |
| ToClass->removeDecl(Method); |
| SharedStatePtr->getLookupTable()->remove(Method); |
| } |
| |
| ASSERT_EQ(DeclCounter<CXXMethodDecl>().match(ToClass, MethodMatcher), 0u); |
| |
| Decl *ImportedClass = nullptr; |
| { |
| Decl *FromTU = getTuDecl(Code, Lang_CXX11, "input1.cc"); |
| auto *FromClass = FirstDeclMatcher<CXXRecordDecl>().match( |
| FromTU, ClassMatcher); |
| ImportedClass = Import(FromClass, Lang_CXX11); |
| } |
| |
| EXPECT_EQ(ToClass, ImportedClass); |
| EXPECT_EQ(DeclCounter<CXXMethodDecl>().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(ASTImporterOptionSpecificTestBase, ImportOfEquivalentRecord) { |
| Decl *ToR1; |
| { |
| Decl *FromTU = getTuDecl("struct A { };", Lang_CXX03, "input0.cc"); |
| auto *FromR = FirstDeclMatcher<CXXRecordDecl>().match( |
| FromTU, cxxRecordDecl(hasName("A"))); |
| |
| ToR1 = Import(FromR, Lang_CXX03); |
| } |
| |
| Decl *ToR2; |
| { |
| Decl *FromTU = getTuDecl("struct A { };", Lang_CXX03, "input1.cc"); |
| auto *FromR = FirstDeclMatcher<CXXRecordDecl>().match( |
| FromTU, cxxRecordDecl(hasName("A"))); |
| |
| ToR2 = Import(FromR, Lang_CXX03); |
| } |
| |
| EXPECT_EQ(ToR1, ToR2); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ImportOfNonEquivalentRecord) { |
| Decl *ToR1; |
| { |
| Decl *FromTU = getTuDecl("struct A { int x; };", Lang_CXX03, "input0.cc"); |
| auto *FromR = FirstDeclMatcher<CXXRecordDecl>().match( |
| FromTU, cxxRecordDecl(hasName("A"))); |
| ToR1 = Import(FromR, Lang_CXX03); |
| } |
| Decl *ToR2; |
| { |
| Decl *FromTU = |
| getTuDecl("struct A { unsigned x; };", Lang_CXX03, "input1.cc"); |
| auto *FromR = FirstDeclMatcher<CXXRecordDecl>().match( |
| FromTU, cxxRecordDecl(hasName("A"))); |
| ToR2 = Import(FromR, Lang_CXX03); |
| } |
| EXPECT_NE(ToR1, ToR2); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ImportOfEquivalentField) { |
| Decl *ToF1; |
| { |
| Decl *FromTU = getTuDecl("struct A { int x; };", Lang_CXX03, "input0.cc"); |
| auto *FromF = FirstDeclMatcher<FieldDecl>().match( |
| FromTU, fieldDecl(hasName("x"))); |
| ToF1 = Import(FromF, Lang_CXX03); |
| } |
| Decl *ToF2; |
| { |
| Decl *FromTU = getTuDecl("struct A { int x; };", Lang_CXX03, "input1.cc"); |
| auto *FromF = FirstDeclMatcher<FieldDecl>().match( |
| FromTU, fieldDecl(hasName("x"))); |
| ToF2 = Import(FromF, Lang_CXX03); |
| } |
| EXPECT_EQ(ToF1, ToF2); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ImportOfNonEquivalentField) { |
| Decl *ToF1; |
| { |
| Decl *FromTU = getTuDecl("struct A { int x; };", Lang_CXX03, "input0.cc"); |
| auto *FromF = FirstDeclMatcher<FieldDecl>().match( |
| FromTU, fieldDecl(hasName("x"))); |
| ToF1 = Import(FromF, Lang_CXX03); |
| } |
| Decl *ToF2; |
| { |
| Decl *FromTU = |
| getTuDecl("struct A { unsigned x; };", Lang_CXX03, "input1.cc"); |
| auto *FromF = FirstDeclMatcher<FieldDecl>().match( |
| FromTU, fieldDecl(hasName("x"))); |
| ToF2 = Import(FromF, Lang_CXX03); |
| } |
| EXPECT_NE(ToF1, ToF2); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ImportOfEquivalentMethod) { |
| Decl *ToM1; |
| { |
| Decl *FromTU = getTuDecl("struct A { void x(); }; void A::x() { }", |
| Lang_CXX03, "input0.cc"); |
| auto *FromM = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("x"), isDefinition())); |
| ToM1 = Import(FromM, Lang_CXX03); |
| } |
| Decl *ToM2; |
| { |
| Decl *FromTU = getTuDecl("struct A { void x(); }; void A::x() { }", |
| Lang_CXX03, "input1.cc"); |
| auto *FromM = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("x"), isDefinition())); |
| ToM2 = Import(FromM, Lang_CXX03); |
| } |
| EXPECT_EQ(ToM1, ToM2); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ImportOfNonEquivalentMethod) { |
| Decl *ToM1; |
| { |
| Decl *FromTU = getTuDecl("struct A { void x(); }; void A::x() { }", |
| Lang_CXX03, "input0.cc"); |
| auto *FromM = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("x"), isDefinition())); |
| ToM1 = Import(FromM, Lang_CXX03); |
| } |
| Decl *ToM2; |
| { |
| Decl *FromTU = |
| getTuDecl("struct A { void x() const; }; void A::x() const { }", |
| Lang_CXX03, "input1.cc"); |
| auto *FromM = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("x"), isDefinition())); |
| ToM2 = Import(FromM, Lang_CXX03); |
| } |
| EXPECT_NE(ToM1, ToM2); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| ImportUnnamedStructsWithRecursingField) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| struct A { |
| struct { |
| struct A *next; |
| } entry0; |
| struct { |
| struct A *next; |
| } entry1; |
| }; |
| )", |
| Lang_C99, "input0.cc"); |
| auto *From = |
| FirstDeclMatcher<RecordDecl>().match(FromTU, recordDecl(hasName("A"))); |
| |
| Import(From, Lang_C99); |
| |
| auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| auto *Entry0 = |
| FirstDeclMatcher<FieldDecl>().match(ToTU, fieldDecl(hasName("entry0"))); |
| auto *Entry1 = |
| FirstDeclMatcher<FieldDecl>().match(ToTU, fieldDecl(hasName("entry1"))); |
| auto *R0 = getRecordDecl(Entry0); |
| auto *R1 = getRecordDecl(Entry1); |
| EXPECT_NE(R0, R1); |
| EXPECT_TRUE(MatchVerifier<RecordDecl>().match( |
| R0, recordDecl(has(fieldDecl(hasName("next")))))); |
| EXPECT_TRUE(MatchVerifier<RecordDecl>().match( |
| R1, recordDecl(has(fieldDecl(hasName("next")))))); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ImportUnnamedFieldsInCorrectOrder) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| void f(int X, int Y, bool Z) { |
| (void)[X, Y, Z] { (void)Z; }; |
| } |
| )", |
| Lang_CXX11, "input0.cc"); |
| auto *FromF = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("f"))); |
| auto *ToF = cast_or_null<FunctionDecl>(Import(FromF, Lang_CXX11)); |
| EXPECT_TRUE(ToF); |
| |
| CXXRecordDecl *FromLambda = |
| cast<LambdaExpr>(cast<CStyleCastExpr>(cast<CompoundStmt>( |
| FromF->getBody())->body_front())->getSubExpr())->getLambdaClass(); |
| |
| auto *ToLambda = cast_or_null<CXXRecordDecl>(Import(FromLambda, Lang_CXX11)); |
| EXPECT_TRUE(ToLambda); |
| |
| // Check if the fields of the lambda class are imported in correct order. |
| unsigned FromIndex = 0u; |
| for (auto *FromField : FromLambda->fields()) { |
| ASSERT_FALSE(FromField->getDeclName()); |
| auto *ToField = cast_or_null<FieldDecl>(Import(FromField, Lang_CXX11)); |
| EXPECT_TRUE(ToField); |
| Optional<unsigned> ToIndex = ASTImporter::getFieldIndex(ToField); |
| EXPECT_TRUE(ToIndex); |
| EXPECT_EQ(*ToIndex, FromIndex); |
| ++FromIndex; |
| } |
| |
| EXPECT_EQ(FromIndex, 3u); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| MergeFieldDeclsOfClassTemplateSpecialization) { |
| std::string ClassTemplate = |
| R"( |
| template <typename T> |
| struct X { |
| int a{0}; // FieldDecl with InitListExpr |
| X(char) : a(3) {} // (1) |
| X(int) {} // (2) |
| }; |
| )"; |
| Decl *ToTU = getToTuDecl(ClassTemplate + |
| R"( |
| void foo() { |
| // ClassTemplateSpec with ctor (1): FieldDecl without InitlistExpr |
| X<char> xc('c'); |
| } |
| )", Lang_CXX11); |
| auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match( |
| ToTU, classTemplateSpecializationDecl(hasName("X"))); |
| // FieldDecl without InitlistExpr: |
| auto *ToField = *ToSpec->field_begin(); |
| ASSERT_TRUE(ToField); |
| ASSERT_FALSE(ToField->getInClassInitializer()); |
| Decl *FromTU = getTuDecl(ClassTemplate + |
| R"( |
| void bar() { |
| // ClassTemplateSpec with ctor (2): FieldDecl WITH InitlistExpr |
| X<char> xc(1); |
| } |
| )", Lang_CXX11); |
| auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match( |
| FromTU, classTemplateSpecializationDecl(hasName("X"))); |
| // FieldDecl with InitlistExpr: |
| auto *FromField = *FromSpec->field_begin(); |
| ASSERT_TRUE(FromField); |
| ASSERT_TRUE(FromField->getInClassInitializer()); |
| |
| auto *ImportedSpec = Import(FromSpec, Lang_CXX11); |
| ASSERT_TRUE(ImportedSpec); |
| EXPECT_EQ(ImportedSpec, ToSpec); |
| // After the import, the FieldDecl has to be merged, thus it should have the |
| // InitListExpr. |
| EXPECT_TRUE(ToField->getInClassInitializer()); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| MergeFunctionOfClassTemplateSpecialization) { |
| std::string ClassTemplate = |
| R"( |
| template <typename T> |
| struct X { |
| void f() {} |
| void g() {} |
| }; |
| )"; |
| Decl *ToTU = getToTuDecl(ClassTemplate + |
| R"( |
| void foo() { |
| X<char> x; |
| x.f(); |
| } |
| )", Lang_CXX11); |
| Decl *FromTU = getTuDecl(ClassTemplate + |
| R"( |
| void bar() { |
| X<char> x; |
| x.g(); |
| } |
| )", Lang_CXX11); |
| auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match( |
| FromTU, classTemplateSpecializationDecl(hasName("X"))); |
| auto FunPattern = functionDecl(hasName("g"), |
| hasParent(classTemplateSpecializationDecl())); |
| auto *FromFun = |
| FirstDeclMatcher<FunctionDecl>().match(FromTU, FunPattern); |
| auto *ToFun = |
| FirstDeclMatcher<FunctionDecl>().match(ToTU, FunPattern); |
| ASSERT_TRUE(FromFun->hasBody()); |
| ASSERT_FALSE(ToFun->hasBody()); |
| auto *ImportedSpec = Import(FromSpec, Lang_CXX11); |
| ASSERT_TRUE(ImportedSpec); |
| auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match( |
| ToTU, classTemplateSpecializationDecl(hasName("X"))); |
| EXPECT_EQ(ImportedSpec, ToSpec); |
| EXPECT_TRUE(ToFun->hasBody()); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, MergeTemplateSpecWithForwardDecl) { |
| std::string ClassTemplate = |
| R"( |
| template<typename T> |
| struct X { int m; }; |
| template<> |
| struct X<int> { int m; }; |
| )"; |
| // Append a forward decl for our template specialization. |
| getToTuDecl(ClassTemplate + "template<> struct X<int>;", Lang_CXX11); |
| Decl *FromTU = getTuDecl(ClassTemplate, Lang_CXX11); |
| auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match( |
| FromTU, classTemplateSpecializationDecl(hasName("X"), isDefinition())); |
| auto *ImportedSpec = Import(FromSpec, Lang_CXX11); |
| // Check that our definition got merged with the existing definition. |
| EXPECT_TRUE(FromSpec->isThisDeclarationADefinition()); |
| EXPECT_TRUE(ImportedSpec->isThisDeclarationADefinition()); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| ODRViolationOfClassTemplateSpecializationsShouldBeReported) { |
| std::string ClassTemplate = |
| R"( |
| template <typename T> |
| struct X {}; |
| )"; |
| Decl *ToTU = getToTuDecl(ClassTemplate + |
| R"( |
| template <> |
| struct X<char> { |
| int a; |
| }; |
| void foo() { |
| X<char> x; |
| } |
| )", |
| Lang_CXX11); |
| Decl *FromTU = getTuDecl(ClassTemplate + |
| R"( |
| template <> |
| struct X<char> { |
| int b; |
| }; |
| void foo() { |
| X<char> x; |
| } |
| )", |
| Lang_CXX11); |
| auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match( |
| FromTU, classTemplateSpecializationDecl(hasName("X"))); |
| auto *ImportedSpec = Import(FromSpec, Lang_CXX11); |
| |
| // We expect one (ODR) warning during the import. |
| EXPECT_EQ(1u, ToTU->getASTContext().getDiagnostics().getNumWarnings()); |
| |
| // The second specialization is different from the first, thus it violates |
| // ODR, consequently we expect to keep the first specialization only, which is |
| // already in the "To" context. |
| EXPECT_FALSE(ImportedSpec); |
| EXPECT_EQ(1u, |
| DeclCounter<ClassTemplateSpecializationDecl>().match( |
| ToTU, classTemplateSpecializationDecl(hasName("X")))); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| MergeCtorOfClassTemplateSpecialization) { |
| std::string ClassTemplate = |
| R"( |
| template <typename T> |
| struct X { |
| X(char) {} |
| X(int) {} |
| }; |
| )"; |
| Decl *ToTU = getToTuDecl(ClassTemplate + |
| R"( |
| void foo() { |
| X<char> x('c'); |
| } |
| )", Lang_CXX11); |
| Decl *FromTU = getTuDecl(ClassTemplate + |
| R"( |
| void bar() { |
| X<char> x(1); |
| } |
| )", Lang_CXX11); |
| auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match( |
| FromTU, classTemplateSpecializationDecl(hasName("X"))); |
| // Match the void(int) ctor. |
| auto CtorPattern = |
| cxxConstructorDecl(hasParameter(0, varDecl(hasType(asString("int")))), |
| hasParent(classTemplateSpecializationDecl())); |
| auto *FromCtor = |
| FirstDeclMatcher<CXXConstructorDecl>().match(FromTU, CtorPattern); |
| auto *ToCtor = |
| FirstDeclMatcher<CXXConstructorDecl>().match(ToTU, CtorPattern); |
| ASSERT_TRUE(FromCtor->hasBody()); |
| ASSERT_FALSE(ToCtor->hasBody()); |
| auto *ImportedSpec = Import(FromSpec, Lang_CXX11); |
| ASSERT_TRUE(ImportedSpec); |
| auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match( |
| ToTU, classTemplateSpecializationDecl(hasName("X"))); |
| EXPECT_EQ(ImportedSpec, ToSpec); |
| EXPECT_TRUE(ToCtor->hasBody()); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ClassTemplateFriendDecl) { |
| const auto *Code = |
| R"( |
| template <class T> class X { friend T; }; |
| struct Y {}; |
| template class X<Y>; |
| )"; |
| Decl *ToTU = getToTuDecl(Code, Lang_CXX11); |
| Decl *FromTU = getTuDecl(Code, Lang_CXX11); |
| auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match( |
| FromTU, classTemplateSpecializationDecl()); |
| auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match( |
| ToTU, classTemplateSpecializationDecl()); |
| |
| auto *ImportedSpec = Import(FromSpec, Lang_CXX11); |
| EXPECT_EQ(ImportedSpec, ToSpec); |
| EXPECT_EQ(1u, DeclCounter<ClassTemplateSpecializationDecl>().match( |
| ToTU, classTemplateSpecializationDecl())); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| ClassTemplatePartialSpecializationsShouldNotBeDuplicated) { |
| auto Code = |
| R"( |
| // primary template |
| template<class T1, class T2, int I> |
| class A {}; |
| |
| // partial specialization |
| template<class T, int I> |
| class A<T, T*, I> {}; |
| )"; |
| Decl *ToTU = getToTuDecl(Code, Lang_CXX11); |
| Decl *FromTU = getTuDecl(Code, Lang_CXX11); |
| auto *FromSpec = |
| FirstDeclMatcher<ClassTemplatePartialSpecializationDecl>().match( |
| FromTU, classTemplatePartialSpecializationDecl()); |
| auto *ToSpec = |
| FirstDeclMatcher<ClassTemplatePartialSpecializationDecl>().match( |
| ToTU, classTemplatePartialSpecializationDecl()); |
| |
| auto *ImportedSpec = Import(FromSpec, Lang_CXX11); |
| EXPECT_EQ(ImportedSpec, ToSpec); |
| EXPECT_EQ(1u, DeclCounter<ClassTemplatePartialSpecializationDecl>().match( |
| ToTU, classTemplatePartialSpecializationDecl())); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| ClassTemplateSpecializationsShouldNotBeDuplicated) { |
| auto Code = |
| R"( |
| // primary template |
| template<class T1, class T2, int I> |
| class A {}; |
| |
| // full specialization |
| template<> |
| class A<int, int, 1> {}; |
| )"; |
| Decl *ToTU = getToTuDecl(Code, Lang_CXX11); |
| Decl *FromTU = getTuDecl(Code, Lang_CXX11); |
| auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match( |
| FromTU, classTemplateSpecializationDecl()); |
| auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match( |
| ToTU, classTemplateSpecializationDecl()); |
| |
| auto *ImportedSpec = Import(FromSpec, Lang_CXX11); |
| EXPECT_EQ(ImportedSpec, ToSpec); |
| EXPECT_EQ(1u, DeclCounter<ClassTemplateSpecializationDecl>().match( |
| ToTU, classTemplateSpecializationDecl())); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| ClassTemplateFullAndPartialSpecsShouldNotBeMixed) { |
| std::string PrimaryTemplate = |
| R"( |
| template<class T1, class T2, int I> |
| class A {}; |
| )"; |
| auto PartialSpec = |
| R"( |
| template<class T, int I> |
| class A<T, T*, I> {}; |
| )"; |
| auto FullSpec = |
| R"( |
| template<> |
| class A<int, int, 1> {}; |
| )"; |
| Decl *ToTU = getToTuDecl(PrimaryTemplate + FullSpec, Lang_CXX11); |
| Decl *FromTU = getTuDecl(PrimaryTemplate + PartialSpec, Lang_CXX11); |
| auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match( |
| FromTU, classTemplateSpecializationDecl()); |
| |
| auto *ImportedSpec = Import(FromSpec, Lang_CXX11); |
| EXPECT_TRUE(ImportedSpec); |
| // Check the number of partial specializations. |
| EXPECT_EQ(1u, DeclCounter<ClassTemplatePartialSpecializationDecl>().match( |
| ToTU, classTemplatePartialSpecializationDecl())); |
| // Check the number of full specializations. |
| EXPECT_EQ(1u, DeclCounter<ClassTemplateSpecializationDecl>().match( |
| ToTU, classTemplateSpecializationDecl( |
| unless(classTemplatePartialSpecializationDecl())))); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| InitListExprValueKindShouldBeImported) { |
| Decl *TU = getTuDecl( |
| R"( |
| const int &init(); |
| void foo() { const int &a{init()}; } |
| )", Lang_CXX11, "input0.cc"); |
| auto *FromD = FirstDeclMatcher<VarDecl>().match(TU, varDecl(hasName("a"))); |
| ASSERT_TRUE(FromD->getAnyInitializer()); |
| auto *InitExpr = FromD->getAnyInitializer(); |
| ASSERT_TRUE(InitExpr); |
| ASSERT_TRUE(InitExpr->isGLValue()); |
| |
| auto *ToD = Import(FromD, Lang_CXX11); |
| EXPECT_TRUE(ToD); |
| auto *ToInitExpr = cast<VarDecl>(ToD)->getAnyInitializer(); |
| EXPECT_TRUE(ToInitExpr); |
| EXPECT_TRUE(ToInitExpr->isGLValue()); |
| } |
| |
| struct ImportVariables : ASTImporterOptionSpecificTestBase {}; |
| |
| TEST_P(ImportVariables, ImportOfOneDeclBringsInTheWholeChain) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| struct A { |
| static const int a = 1 + 2; |
| }; |
| const int A::a; |
| )", |
| Lang_CXX03, "input1.cc"); |
| |
| auto *FromDWithInit = FirstDeclMatcher<VarDecl>().match( |
| FromTU, varDecl(hasName("a"))); // Decl with init |
| auto *FromDWithDef = LastDeclMatcher<VarDecl>().match( |
| FromTU, varDecl(hasName("a"))); // Decl with definition |
| ASSERT_NE(FromDWithInit, FromDWithDef); |
| ASSERT_EQ(FromDWithDef->getPreviousDecl(), FromDWithInit); |
| |
| auto *ToD0 = cast<VarDecl>(Import(FromDWithInit, Lang_CXX11)); |
| auto *ToD1 = cast<VarDecl>(Import(FromDWithDef, Lang_CXX11)); |
| ASSERT_TRUE(ToD0); |
| ASSERT_TRUE(ToD1); |
| EXPECT_NE(ToD0, ToD1); |
| EXPECT_EQ(ToD1->getPreviousDecl(), ToD0); |
| } |
| |
| TEST_P(ImportVariables, InitAndDefinitionAreInDifferentTUs) { |
| auto StructA = |
| R"( |
| struct A { |
| static const int a = 1 + 2; |
| }; |
| )"; |
| Decl *ToTU = getToTuDecl(StructA, Lang_CXX03); |
| Decl *FromTU = getTuDecl(std::string(StructA) + "const int A::a;", Lang_CXX03, |
| "input1.cc"); |
| |
| auto *FromDWithInit = FirstDeclMatcher<VarDecl>().match( |
| FromTU, varDecl(hasName("a"))); // Decl with init |
| auto *FromDWithDef = LastDeclMatcher<VarDecl>().match( |
| FromTU, varDecl(hasName("a"))); // Decl with definition |
| ASSERT_EQ(FromDWithInit, FromDWithDef->getPreviousDecl()); |
| ASSERT_TRUE(FromDWithInit->getInit()); |
| ASSERT_FALSE(FromDWithInit->isThisDeclarationADefinition()); |
| ASSERT_TRUE(FromDWithDef->isThisDeclarationADefinition()); |
| ASSERT_FALSE(FromDWithDef->getInit()); |
| |
| auto *ToD = FirstDeclMatcher<VarDecl>().match( |
| ToTU, varDecl(hasName("a"))); // Decl with init |
| ASSERT_TRUE(ToD->getInit()); |
| ASSERT_FALSE(ToD->getDefinition()); |
| |
| auto *ImportedD = cast<VarDecl>(Import(FromDWithDef, Lang_CXX11)); |
| EXPECT_TRUE(ImportedD->getAnyInitializer()); |
| EXPECT_TRUE(ImportedD->getDefinition()); |
| } |
| |
| TEST_P(ImportVariables, InitAndDefinitionAreInTheFromContext) { |
| auto StructA = |
| R"( |
| struct A { |
| static const int a; |
| }; |
| )"; |
| Decl *ToTU = getToTuDecl(StructA, Lang_CXX03); |
| Decl *FromTU = getTuDecl(std::string(StructA) + "const int A::a = 1 + 2;", |
| Lang_CXX03, "input1.cc"); |
| |
| auto *FromDDeclarationOnly = FirstDeclMatcher<VarDecl>().match( |
| FromTU, varDecl(hasName("a"))); |
| auto *FromDWithDef = LastDeclMatcher<VarDecl>().match( |
| FromTU, varDecl(hasName("a"))); // Decl with definition and with init. |
| ASSERT_EQ(FromDDeclarationOnly, FromDWithDef->getPreviousDecl()); |
| ASSERT_FALSE(FromDDeclarationOnly->getInit()); |
| ASSERT_FALSE(FromDDeclarationOnly->isThisDeclarationADefinition()); |
| ASSERT_TRUE(FromDWithDef->isThisDeclarationADefinition()); |
| ASSERT_TRUE(FromDWithDef->getInit()); |
| |
| auto *ToD = FirstDeclMatcher<VarDecl>().match( |
| ToTU, varDecl(hasName("a"))); |
| ASSERT_FALSE(ToD->getInit()); |
| ASSERT_FALSE(ToD->getDefinition()); |
| |
| auto *ImportedD = cast<VarDecl>(Import(FromDWithDef, Lang_CXX11)); |
| EXPECT_TRUE(ImportedD->getAnyInitializer()); |
| EXPECT_TRUE(ImportedD->getDefinition()); |
| } |
| |
| struct ImportClasses : ASTImporterOptionSpecificTestBase {}; |
| |
| TEST_P(ImportClasses, ImportDefinitionWhenProtoIsInNestedToContext) { |
| Decl *ToTU = getToTuDecl("struct A { struct X *Xp; };", Lang_C99); |
| Decl *FromTU1 = getTuDecl("struct X {};", Lang_C99, "input1.cc"); |
| auto Pattern = recordDecl(hasName("X"), unless(isImplicit())); |
| auto ToProto = FirstDeclMatcher<RecordDecl>().match(ToTU, Pattern); |
| auto FromDef = FirstDeclMatcher<RecordDecl>().match(FromTU1, Pattern); |
| |
| Decl *ImportedDef = Import(FromDef, Lang_C99); |
| |
| EXPECT_NE(ImportedDef, ToProto); |
| EXPECT_EQ(DeclCounter<RecordDecl>().match(ToTU, Pattern), 2u); |
| auto ToDef = LastDeclMatcher<RecordDecl>().match(ToTU, Pattern); |
| EXPECT_TRUE(ImportedDef == ToDef); |
| EXPECT_TRUE(ToDef->isThisDeclarationADefinition()); |
| EXPECT_FALSE(ToProto->isThisDeclarationADefinition()); |
| EXPECT_EQ(ToDef->getPreviousDecl(), ToProto); |
| } |
| |
| TEST_P(ImportClasses, ImportDefinitionWhenProtoIsInNestedToContextCXX) { |
| Decl *ToTU = getToTuDecl("struct A { struct X *Xp; };", Lang_CXX03); |
| Decl *FromTU1 = getTuDecl("struct X {};", Lang_CXX03, "input1.cc"); |
| auto Pattern = recordDecl(hasName("X"), unless(isImplicit())); |
| auto ToProto = FirstDeclMatcher<RecordDecl>().match(ToTU, Pattern); |
| auto FromDef = FirstDeclMatcher<RecordDecl>().match(FromTU1, Pattern); |
| |
| Decl *ImportedDef = Import(FromDef, Lang_CXX03); |
| |
| EXPECT_NE(ImportedDef, ToProto); |
| EXPECT_EQ(DeclCounter<RecordDecl>().match(ToTU, Pattern), 2u); |
| auto ToDef = LastDeclMatcher<RecordDecl>().match(ToTU, Pattern); |
| EXPECT_TRUE(ImportedDef == ToDef); |
| EXPECT_TRUE(ToDef->isThisDeclarationADefinition()); |
| EXPECT_FALSE(ToProto->isThisDeclarationADefinition()); |
| EXPECT_EQ(ToDef->getPreviousDecl(), ToProto); |
| } |
| |
| TEST_P(ImportClasses, ImportNestedPrototypeThenDefinition) { |
| Decl *FromTU0 = |
| getTuDecl("struct A { struct X *Xp; };", Lang_C99, "input0.cc"); |
| Decl *FromTU1 = getTuDecl("struct X {};", Lang_C99, "input1.cc"); |
| auto Pattern = recordDecl(hasName("X"), unless(isImplicit())); |
| auto FromProto = FirstDeclMatcher<RecordDecl>().match(FromTU0, Pattern); |
| auto FromDef = FirstDeclMatcher<RecordDecl>().match(FromTU1, Pattern); |
| |
| Decl *ImportedProto = Import(FromProto, Lang_C99); |
| Decl *ImportedDef = Import(FromDef, Lang_C99); |
| Decl *ToTU = ImportedDef->getTranslationUnitDecl(); |
| |
| EXPECT_NE(ImportedDef, ImportedProto); |
| EXPECT_EQ(DeclCounter<RecordDecl>().match(ToTU, Pattern), 2u); |
| auto ToProto = FirstDeclMatcher<RecordDecl>().match(ToTU, Pattern); |
| auto ToDef = LastDeclMatcher<RecordDecl>().match(ToTU, Pattern); |
| EXPECT_TRUE(ImportedDef == ToDef); |
| EXPECT_TRUE(ImportedProto == ToProto); |
| EXPECT_TRUE(ToDef->isThisDeclarationADefinition()); |
| EXPECT_FALSE(ToProto->isThisDeclarationADefinition()); |
| EXPECT_EQ(ToDef->getPreviousDecl(), ToProto); |
| } |
| |
| |
| struct ImportFriendClasses : ASTImporterOptionSpecificTestBase {}; |
| |
| TEST_P(ImportFriendClasses, ImportOfFriendRecordDoesNotMergeDefinition) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| class A { |
| template <int I> class F {}; |
| class X { |
| template <int I> friend class F; |
| }; |
| }; |
| )", |
| Lang_CXX03, "input0.cc"); |
| |
| auto *FromClass = FirstDeclMatcher<CXXRecordDecl>().match( |
| FromTU, cxxRecordDecl(hasName("F"), isDefinition())); |
| auto *FromFriendClass = LastDeclMatcher<CXXRecordDecl>().match( |
| FromTU, cxxRecordDecl(hasName("F"))); |
| |
| ASSERT_TRUE(FromClass); |
| ASSERT_TRUE(FromFriendClass); |
| ASSERT_NE(FromClass, FromFriendClass); |
| ASSERT_EQ(FromFriendClass->getDefinition(), FromClass); |
| ASSERT_EQ(FromFriendClass->getPreviousDecl(), FromClass); |
| ASSERT_EQ(FromFriendClass->getDescribedClassTemplate()->getPreviousDecl(), |
| FromClass->getDescribedClassTemplate()); |
| |
| auto *ToClass = cast<CXXRecordDecl>(Import(FromClass, Lang_CXX03)); |
| auto *ToFriendClass = |
| cast<CXXRecordDecl>(Import(FromFriendClass, Lang_CXX03)); |
| |
| EXPECT_TRUE(ToClass); |
| EXPECT_TRUE(ToFriendClass); |
| EXPECT_NE(ToClass, ToFriendClass); |
| EXPECT_EQ(ToFriendClass->getDefinition(), ToClass); |
| EXPECT_EQ(ToFriendClass->getPreviousDecl(), ToClass); |
| EXPECT_EQ(ToFriendClass->getDescribedClassTemplate()->getPreviousDecl(), |
| ToClass->getDescribedClassTemplate()); |
| } |
| |
| TEST_P(ImportFriendClasses, ImportOfRecursiveFriendClass) { |
| Decl *FromTu = getTuDecl( |
| R"( |
| class declToImport { |
| friend class declToImport; |
| }; |
| )", |
| Lang_CXX03, "input.cc"); |
| |
| auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match( |
| FromTu, cxxRecordDecl(hasName("declToImport"))); |
| auto *ToD = Import(FromD, Lang_CXX03); |
| auto Pattern = cxxRecordDecl(has(friendDecl())); |
| ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern)); |
| EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern)); |
| } |
| |
| TEST_P(ImportFriendClasses, UndeclaredFriendClassShouldNotBeVisible) { |
| Decl *FromTu = |
| getTuDecl("class X { friend class Y; };", Lang_CXX03, "from.cc"); |
| auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match( |
| FromTu, cxxRecordDecl(hasName("X"))); |
| auto *FromFriend = FirstDeclMatcher<FriendDecl>().match(FromTu, friendDecl()); |
| RecordDecl *FromRecordOfFriend = |
| const_cast<RecordDecl *>(getRecordDeclOfFriend(FromFriend)); |
| |
| ASSERT_EQ(FromRecordOfFriend->getDeclContext(), cast<DeclContext>(FromTu)); |
| ASSERT_EQ(FromRecordOfFriend->getLexicalDeclContext(), |
| cast<DeclContext>(FromX)); |
| ASSERT_FALSE( |
| FromRecordOfFriend->getDeclContext()->containsDecl(FromRecordOfFriend)); |
| ASSERT_FALSE(FromRecordOfFriend->getLexicalDeclContext()->containsDecl( |
| FromRecordOfFriend)); |
| ASSERT_FALSE(FromRecordOfFriend->getLookupParent() |
| ->lookup(FromRecordOfFriend->getDeclName()) |
| .empty()); |
| |
| auto *ToX = Import(FromX, Lang_CXX03); |
| ASSERT_TRUE(ToX); |
| |
| Decl *ToTu = ToX->getTranslationUnitDecl(); |
| auto *ToFriend = FirstDeclMatcher<FriendDecl>().match(ToTu, friendDecl()); |
| RecordDecl *ToRecordOfFriend = |
| const_cast<RecordDecl *>(getRecordDeclOfFriend(ToFriend)); |
| |
| ASSERT_EQ(ToRecordOfFriend->getDeclContext(), cast<DeclContext>(ToTu)); |
| ASSERT_EQ(ToRecordOfFriend->getLexicalDeclContext(), cast<DeclContext>(ToX)); |
| EXPECT_FALSE( |
| ToRecordOfFriend->getDeclContext()->containsDecl(ToRecordOfFriend)); |
| EXPECT_FALSE(ToRecordOfFriend->getLexicalDeclContext()->containsDecl( |
| ToRecordOfFriend)); |
| EXPECT_FALSE(ToRecordOfFriend->getLookupParent() |
| ->lookup(ToRecordOfFriend->getDeclName()) |
| .empty()); |
| } |
| |
| TEST_P(ImportFriendClasses, ImportOfRecursiveFriendClassTemplate) { |
| Decl *FromTu = getTuDecl( |
| R"( |
| template<class A> class declToImport { |
| template<class A1> friend class declToImport; |
| }; |
| )", |
| Lang_CXX03, "input.cc"); |
| |
| auto *FromD = |
| FirstDeclMatcher<ClassTemplateDecl>().match(FromTu, classTemplateDecl()); |
| auto *ToD = Import(FromD, Lang_CXX03); |
| |
| auto Pattern = classTemplateDecl( |
| has(cxxRecordDecl(has(friendDecl(has(classTemplateDecl())))))); |
| ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern)); |
| EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern)); |
| |
| auto *Class = |
| FirstDeclMatcher<ClassTemplateDecl>().match(ToD, classTemplateDecl()); |
| auto *Friend = FirstDeclMatcher<FriendDecl>().match(ToD, friendDecl()); |
| EXPECT_NE(Friend->getFriendDecl(), Class); |
| EXPECT_EQ(Friend->getFriendDecl()->getPreviousDecl(), Class); |
| } |
| |
| TEST_P(ImportFriendClasses, ProperPrevDeclForClassTemplateDecls) { |
| auto Pattern = classTemplateSpecializationDecl(hasName("X")); |
| |
| ClassTemplateSpecializationDecl *Imported1; |
| { |
| Decl *FromTU = getTuDecl("template<class T> class X;" |
| "struct Y { friend class X<int>; };", |
| Lang_CXX03, "input0.cc"); |
| auto *FromD = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match( |
| FromTU, Pattern); |
| |
| Imported1 = |
| cast<ClassTemplateSpecializationDecl>(Import(FromD, Lang_CXX03)); |
| } |
| ClassTemplateSpecializationDecl *Imported2; |
| { |
| Decl *FromTU = getTuDecl("template<class T> class X;" |
| "template<> class X<int>{};" |
| "struct Z { friend class X<int>; };", |
| Lang_CXX03, "input1.cc"); |
| auto *FromD = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match( |
| FromTU, Pattern); |
| |
| Imported2 = |
| cast<ClassTemplateSpecializationDecl>(Import(FromD, Lang_CXX03)); |
| } |
| |
| Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| EXPECT_EQ(DeclCounter<ClassTemplateSpecializationDecl>().match(ToTU, Pattern), |
| 2u); |
| ASSERT_TRUE(Imported2->getPreviousDecl()); |
| EXPECT_EQ(Imported2->getPreviousDecl(), Imported1); |
| } |
| |
| TEST_P(ImportFriendClasses, TypeForDeclShouldBeSetInTemplated) { |
| Decl *FromTU0 = getTuDecl( |
| R"( |
| class X { |
| class Y; |
| }; |
| class X::Y { |
| template <typename T> |
| friend class F; // The decl context of F is the global namespace. |
| }; |
| )", |
| Lang_CXX03, "input0.cc"); |
| auto *Fwd = FirstDeclMatcher<ClassTemplateDecl>().match( |
| FromTU0, classTemplateDecl(hasName("F"))); |
| auto *Imported0 = cast<ClassTemplateDecl>(Import(Fwd, Lang_CXX03)); |
| Decl *FromTU1 = getTuDecl( |
| R"( |
| template <typename T> |
| class F {}; |
| )", |
| Lang_CXX03, "input1.cc"); |
| auto *Definition = FirstDeclMatcher<ClassTemplateDecl>().match( |
| FromTU1, classTemplateDecl(hasName("F"))); |
| auto *Imported1 = cast<ClassTemplateDecl>(Import(Definition, Lang_CXX03)); |
| EXPECT_EQ(Imported0->getTemplatedDecl()->getTypeForDecl(), |
| Imported1->getTemplatedDecl()->getTypeForDecl()); |
| } |
| |
| TEST_P(ImportFriendClasses, DeclsFromFriendsShouldBeInRedeclChains) { |
| Decl *From, *To; |
| std::tie(From, To) = |
| getImportedDecl("class declToImport {};", Lang_CXX03, |
| "class Y { friend class declToImport; };", Lang_CXX03); |
| auto *Imported = cast<CXXRecordDecl>(To); |
| |
| EXPECT_TRUE(Imported->getPreviousDecl()); |
| } |
| |
| TEST_P(ImportFriendClasses, |
| ImportOfClassTemplateDefinitionShouldConnectToFwdFriend) { |
| Decl *ToTU = getToTuDecl( |
| R"( |
| class X { |
| class Y; |
| }; |
| class X::Y { |
| template <typename T> |
| friend class F; // The decl context of F is the global namespace. |
| }; |
| )", |
| Lang_CXX03); |
| auto *ToDecl = FirstDeclMatcher<ClassTemplateDecl>().match( |
| ToTU, classTemplateDecl(hasName("F"))); |
| Decl *FromTU = getTuDecl( |
| R"( |
| template <typename T> |
| class F {}; |
| )", |
| Lang_CXX03, "input0.cc"); |
| auto *Definition = FirstDeclMatcher<ClassTemplateDecl>().match( |
| FromTU, classTemplateDecl(hasName("F"))); |
| auto *ImportedDef = cast<ClassTemplateDecl>(Import(Definition, Lang_CXX03)); |
| EXPECT_TRUE(ImportedDef->getPreviousDecl()); |
| EXPECT_EQ(ToDecl, ImportedDef->getPreviousDecl()); |
| EXPECT_EQ(ToDecl->getTemplatedDecl(), |
| ImportedDef->getTemplatedDecl()->getPreviousDecl()); |
| } |
| |
| TEST_P(ImportFriendClasses, |
| ImportOfClassTemplateDefinitionAndFwdFriendShouldBeLinked) { |
| Decl *FromTU0 = getTuDecl( |
| R"( |
| class X { |
| class Y; |
| }; |
| class X::Y { |
| template <typename T> |
| friend class F; // The decl context of F is the global namespace. |
| }; |
| )", |
| Lang_CXX03, "input0.cc"); |
| auto *Fwd = FirstDeclMatcher<ClassTemplateDecl>().match( |
| FromTU0, classTemplateDecl(hasName("F"))); |
| auto *ImportedFwd = cast<ClassTemplateDecl>(Import(Fwd, Lang_CXX03)); |
| Decl *FromTU1 = getTuDecl( |
| R"( |
| template <typename T> |
| class F {}; |
| )", |
| Lang_CXX03, "input1.cc"); |
| auto *Definition = FirstDeclMatcher<ClassTemplateDecl>().match( |
| FromTU1, classTemplateDecl(hasName("F"))); |
| auto *ImportedDef = cast<ClassTemplateDecl>(Import(Definition, Lang_CXX03)); |
| EXPECT_TRUE(ImportedDef->getPreviousDecl()); |
| EXPECT_EQ(ImportedFwd, ImportedDef->getPreviousDecl()); |
| EXPECT_EQ(ImportedFwd->getTemplatedDecl(), |
| ImportedDef->getTemplatedDecl()->getPreviousDecl()); |
| } |
| |
| TEST_P(ImportFriendClasses, ImportOfClassDefinitionAndFwdFriendShouldBeLinked) { |
| Decl *FromTU0 = getTuDecl( |
| R"( |
| class X { |
| class Y; |
| }; |
| class X::Y { |
| friend class F; // The decl context of F is the global namespace. |
| }; |
| )", |
| Lang_CXX03, "input0.cc"); |
| auto *Friend = FirstDeclMatcher<FriendDecl>().match(FromTU0, friendDecl()); |
| QualType FT = Friend->getFriendType()->getType(); |
| FT = FromTU0->getASTContext().getCanonicalType(FT); |
| auto *Fwd = cast<TagType>(FT)->getDecl(); |
| auto *ImportedFwd = Import(Fwd, Lang_CXX03); |
| Decl *FromTU1 = getTuDecl( |
| R"( |
| class F {}; |
| )", |
| Lang_CXX03, "input1.cc"); |
| auto *Definition = FirstDeclMatcher<CXXRecordDecl>().match( |
| FromTU1, cxxRecordDecl(hasName("F"))); |
| auto *ImportedDef = Import(Definition, Lang_CXX03); |
| EXPECT_TRUE(ImportedDef->getPreviousDecl()); |
| EXPECT_EQ(ImportedFwd, ImportedDef->getPreviousDecl()); |
| } |
| |
| TEST_P(ImportFriendClasses, ImportOfRepeatedFriendType) { |
| const char *Code = |
| R"( |
| class Container { |
| friend class X; |
| friend class X; |
| }; |
| )"; |
| Decl *ToTu = getToTuDecl(Code, Lang_CXX03); |
| Decl *FromTu = getTuDecl(Code, Lang_CXX03, "from.cc"); |
| |
| auto *ToFriend1 = FirstDeclMatcher<FriendDecl>().match(ToTu, friendDecl()); |
| auto *ToFriend2 = LastDeclMatcher<FriendDecl>().match(ToTu, friendDecl()); |
| auto *FromFriend1 = |
| FirstDeclMatcher<FriendDecl>().match(FromTu, friendDecl()); |
| auto *FromFriend2 = LastDeclMatcher<FriendDecl>().match(FromTu, friendDecl()); |
| |
| FriendDecl *ToImportedFriend1 = Import(FromFriend1, Lang_CXX03); |
| FriendDecl *ToImportedFriend2 = Import(FromFriend2, Lang_CXX03); |
| |
| EXPECT_NE(ToImportedFriend1, ToImportedFriend2); |
| EXPECT_EQ(ToFriend1, ToImportedFriend1); |
| EXPECT_EQ(ToFriend2, ToImportedFriend2); |
| } |
| |
| TEST_P(ImportFriendClasses, ImportOfRepeatedFriendDecl) { |
| const char *Code = |
| R"( |
| class Container { |
| friend void f(); |
| friend void f(); |
| }; |
| )"; |
| Decl *ToTu = getToTuDecl(Code, Lang_CXX03); |
| Decl *FromTu = getTuDecl(Code, Lang_CXX03, "from.cc"); |
| |
| auto *ToFriend1 = FirstDeclMatcher<FriendDecl>().match(ToTu, friendDecl()); |
| auto *ToFriend2 = LastDeclMatcher<FriendDecl>().match(ToTu, friendDecl()); |
| auto *FromFriend1 = |
| FirstDeclMatcher<FriendDecl>().match(FromTu, friendDecl()); |
| auto *FromFriend2 = LastDeclMatcher<FriendDecl>().match(FromTu, friendDecl()); |
| |
| FriendDecl *ToImportedFriend1 = Import(FromFriend1, Lang_CXX03); |
| FriendDecl *ToImportedFriend2 = Import(FromFriend2, Lang_CXX03); |
| |
| EXPECT_NE(ToImportedFriend1, ToImportedFriend2); |
| EXPECT_EQ(ToFriend1, ToImportedFriend1); |
| EXPECT_EQ(ToFriend2, ToImportedFriend2); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, FriendFunInClassTemplate) { |
| auto *Code = R"( |
| template <class T> |
| struct X { |
| friend void foo(){} |
| }; |
| )"; |
| TranslationUnitDecl *ToTU = getToTuDecl(Code, Lang_CXX03); |
| auto *ToFoo = FirstDeclMatcher<FunctionDecl>().match( |
| ToTU, functionDecl(hasName("foo"))); |
| |
| TranslationUnitDecl *FromTU = getTuDecl(Code, Lang_CXX03, "input.cc"); |
| auto *FromFoo = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("foo"))); |
| auto *ImportedFoo = Import(FromFoo, Lang_CXX03); |
| EXPECT_EQ(ImportedFoo, ToFoo); |
| } |
| |
| struct DeclContextTest : ASTImporterOptionSpecificTestBase {}; |
| |
| TEST_P(DeclContextTest, removeDeclOfClassTemplateSpecialization) { |
| Decl *TU = getTuDecl( |
| R"( |
| namespace NS { |
| |
| template <typename T> |
| struct S {}; |
| template struct S<int>; |
| |
| inline namespace INS { |
| template <typename T> |
| struct S {}; |
| template struct S<int>; |
| } |
| |
| } |
| )", Lang_CXX11, "input0.cc"); |
| auto *NS = FirstDeclMatcher<NamespaceDecl>().match( |
| TU, namespaceDecl()); |
| auto *Spec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match( |
| TU, classTemplateSpecializationDecl()); |
| ASSERT_TRUE(NS->containsDecl(Spec)); |
| |
| NS->removeDecl(Spec); |
| EXPECT_FALSE(NS->containsDecl(Spec)); |
| } |
| |
| TEST_P(DeclContextTest, |
| removeDeclShouldNotFailEvenIfWeHaveExternalVisibleStorage) { |
| Decl *TU = getTuDecl("extern int A; int A;", Lang_CXX03); |
| auto *A0 = FirstDeclMatcher<VarDecl>().match(TU, varDecl(hasName("A"))); |
| auto *A1 = LastDeclMatcher<VarDecl>().match(TU, varDecl(hasName("A"))); |
| |
| // Investigate the list. |
| auto *DC = A0->getDeclContext(); |
| ASSERT_TRUE(DC->containsDecl(A0)); |
| ASSERT_TRUE(DC->containsDecl(A1)); |
| |
| // Investigate the lookup table. |
| auto *Map = DC->getLookupPtr(); |
| ASSERT_TRUE(Map); |
| auto I = Map->find(A0->getDeclName()); |
| ASSERT_NE(I, Map->end()); |
| StoredDeclsList &L = I->second; |
| // The lookup table contains the most recent decl of A. |
| ASSERT_NE(L.getAsDecl(), A0); |
| ASSERT_EQ(L.getAsDecl(), A1); |
| |
| ASSERT_TRUE(L.getAsDecl()); |
| // Simulate the private function DeclContext::reconcileExternalVisibleStorage. |
| // We do not have a list with one element. |
| L.setHasExternalDecls(); |
| ASSERT_FALSE(L.getAsList()); |
| auto Results = L.getLookupResult(); |
| ASSERT_EQ(1u, std::distance(Results.begin(), Results.end())); |
| |
| // This asserts in the old implementation. |
| DC->removeDecl(A0); |
| EXPECT_FALSE(DC->containsDecl(A0)); |
| } |
| |
| struct ImportFunctionTemplateSpecializations |
| : ASTImporterOptionSpecificTestBase {}; |
| |
| TEST_P(ImportFunctionTemplateSpecializations, |
| TUshouldNotContainFunctionTemplateImplicitInstantiation) { |
| |
| Decl *FromTU = getTuDecl( |
| R"( |
| template<class T> |
| int f() { return 0; } |
| void foo() { f<int>(); } |
| )", |
| Lang_CXX03, "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<Decl>{}.match(FromTU, Pattern)); |
| |
| auto *Foo = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("foo"))); |
| ASSERT_TRUE(Import(Foo, Lang_CXX03)); |
| |
| auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToTU, Pattern)); |
| } |
| |
| TEST_P(ImportFunctionTemplateSpecializations, |
| TUshouldNotContainFunctionTemplateExplicitInstantiation) { |
| |
| Decl *FromTU = getTuDecl( |
| R"( |
| template<class T> |
| int f() { return 0; } |
| template int f<int>(); |
| )", |
| Lang_CXX03, "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<Decl>{}.match(FromTU, Pattern)); |
| |
| ASSERT_TRUE(Import(FirstDeclMatcher<Decl>().match(FromTU, Instantiation), |
| Lang_CXX03)); |
| |
| auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToTU, Pattern)); |
| } |
| |
| TEST_P(ImportFunctionTemplateSpecializations, |
| TUshouldContainFunctionTemplateSpecialization) { |
| |
| Decl *FromTU = getTuDecl( |
| R"( |
| template<class T> |
| int f() { return 0; } |
| template <> int f<int>() { return 4; } |
| )", |
| Lang_CXX03, "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<Decl>{}.match(FromTU, Pattern)); |
| |
| ASSERT_TRUE(Import(FirstDeclMatcher<Decl>().match(FromTU, Specialization), |
| Lang_CXX03)); |
| |
| auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToTU, Pattern)); |
| } |
| |
| TEST_P(ImportFunctionTemplateSpecializations, |
| FunctionTemplateSpecializationRedeclChain) { |
| |
| Decl *FromTU = getTuDecl( |
| R"( |
| template<class T> |
| int f() { return 0; } |
| template <> int f<int>() { return 4; } |
| )", |
| Lang_CXX03, "input0.cc"); |
| |
| auto Spec = functionDecl(hasName("f"), isExplicitTemplateSpecialization(), |
| hasParent(translationUnitDecl())); |
| auto *FromSpecD = FirstDeclMatcher<Decl>().match(FromTU, Spec); |
| { |
| auto *TU = FromTU; |
| auto *SpecD = FromSpecD; |
| auto *TemplateD = FirstDeclMatcher<FunctionTemplateDecl>().match( |
| TU, functionTemplateDecl()); |
| auto *FirstSpecD = *(TemplateD->spec_begin()); |
| ASSERT_EQ(SpecD, FirstSpecD); |
| ASSERT_TRUE(SpecD->getPreviousDecl()); |
| ASSERT_FALSE(cast<FunctionDecl>(SpecD->getPreviousDecl()) |
| ->doesThisDeclarationHaveABody()); |
| } |
| |
| ASSERT_TRUE(Import(FromSpecD, Lang_CXX03)); |
| |
| { |
| auto *TU = ToAST->getASTContext().getTranslationUnitDecl(); |
| auto *SpecD = FirstDeclMatcher<Decl>().match(TU, Spec); |
| auto *TemplateD = FirstDeclMatcher<FunctionTemplateDecl>().match( |
| TU, functionTemplateDecl()); |
| auto *FirstSpecD = *(TemplateD->spec_begin()); |
| EXPECT_EQ(SpecD, FirstSpecD); |
| ASSERT_TRUE(SpecD->getPreviousDecl()); |
| EXPECT_FALSE(cast<FunctionDecl>(SpecD->getPreviousDecl()) |
| ->doesThisDeclarationHaveABody()); |
| } |
| } |
| |
| TEST_P(ImportFunctionTemplateSpecializations, |
| MatchNumberOfFunctionTemplateSpecializations) { |
| |
| Decl *FromTU = getTuDecl( |
| R"( |
| template <typename T> constexpr int f() { return 0; } |
| template <> constexpr int f<int>() { return 4; } |
| void foo() { |
| static_assert(f<char>() == 0, ""); |
| static_assert(f<int>() == 4, ""); |
| } |
| )", |
| Lang_CXX11, "input0.cc"); |
| auto *FromD = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("foo"))); |
| |
| Import(FromD, Lang_CXX11); |
| auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| EXPECT_EQ( |
| DeclCounter<FunctionDecl>().match(FromTU, functionDecl(hasName("f"))), |
| DeclCounter<FunctionDecl>().match(ToTU, functionDecl(hasName("f")))); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| ImportShouldNotReportFalseODRErrorWhenRecordIsBeingDefined) { |
| { |
| Decl *FromTU = getTuDecl( |
| R"( |
| template <typename T> |
| struct B; |
| )", |
| Lang_CXX03, "input0.cc"); |
| auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match( |
| FromTU, classTemplateDecl(hasName("B"))); |
| |
| Import(FromD, Lang_CXX03); |
| } |
| |
| { |
| Decl *FromTU = getTuDecl( |
| R"( |
| template <typename T> |
| struct B { |
| void f(); |
| B* b; |
| }; |
| )", |
| Lang_CXX03, "input1.cc"); |
| FunctionDecl *FromD = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("f"))); |
| Import(FromD, Lang_CXX03); |
| auto *FromCTD = FirstDeclMatcher<ClassTemplateDecl>().match( |
| FromTU, classTemplateDecl(hasName("B"))); |
| auto *ToCTD = cast<ClassTemplateDecl>(Import(FromCTD, Lang_CXX03)); |
| EXPECT_TRUE(ToCTD->isThisDeclarationADefinition()); |
| |
| // We expect no (ODR) warning during the import. |
| auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| EXPECT_EQ(0u, ToTU->getASTContext().getDiagnostics().getNumWarnings()); |
| } |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| ImportingTypedefShouldImportTheCompleteType) { |
| // We already have an incomplete underlying type in the "To" context. |
| auto Code = |
| R"( |
| template <typename T> |
| struct S { |
| void foo(); |
| }; |
| using U = S<int>; |
| )"; |
| Decl *ToTU = getToTuDecl(Code, Lang_CXX11); |
| auto *ToD = FirstDeclMatcher<TypedefNameDecl>().match(ToTU, |
| typedefNameDecl(hasName("U"))); |
| ASSERT_TRUE(ToD->getUnderlyingType()->isIncompleteType()); |
| |
| // The "From" context has the same typedef, but the underlying type is |
| // complete this time. |
| Decl *FromTU = getTuDecl(std::string(Code) + |
| R"( |
| void foo(U* u) { |
| u->foo(); |
| } |
| )", Lang_CXX11); |
| auto *FromD = FirstDeclMatcher<TypedefNameDecl>().match(FromTU, |
| typedefNameDecl(hasName("U"))); |
| ASSERT_FALSE(FromD->getUnderlyingType()->isIncompleteType()); |
| |
| // The imported type should be complete. |
| auto *ImportedD = cast<TypedefNameDecl>(Import(FromD, Lang_CXX11)); |
| EXPECT_FALSE(ImportedD->getUnderlyingType()->isIncompleteType()); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ImportTemplateParameterLists) { |
| auto Code = |
| R"( |
| template<class T> |
| int f() { return 0; } |
| template <> int f<int>() { return 4; } |
| )"; |
| |
| Decl *FromTU = getTuDecl(Code, Lang_CXX03); |
| auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, |
| functionDecl(hasName("f"), isExplicitTemplateSpecialization())); |
| ASSERT_EQ(FromD->getNumTemplateParameterLists(), 1u); |
| |
| auto *ToD = Import(FromD, Lang_CXX03); |
| // The template parameter list should exist. |
| EXPECT_EQ(ToD->getNumTemplateParameterLists(), 1u); |
| } |
| |
| struct ASTImporterLookupTableTest : ASTImporterOptionSpecificTestBase {}; |
| |
| TEST_P(ASTImporterLookupTableTest, OneDecl) { |
| auto *ToTU = getToTuDecl("int a;", Lang_CXX03); |
| auto *D = FirstDeclMatcher<VarDecl>().match(ToTU, varDecl(hasName("a"))); |
| ASTImporterLookupTable LT(*ToTU); |
| auto Res = LT.lookup(ToTU, D->getDeclName()); |
| ASSERT_EQ(Res.size(), 1u); |
| EXPECT_EQ(*Res.begin(), D); |
| } |
| |
| static Decl *findInDeclListOfDC(DeclContext *DC, DeclarationName Name) { |
| for (Decl *D : DC->decls()) { |
| if (auto *ND = dyn_cast<NamedDecl>(D)) |
| if (ND->getDeclName() == Name) |
| return ND; |
| } |
| return nullptr; |
| } |
| |
| TEST_P(ASTImporterLookupTableTest, |
| FriendWhichIsnotFoundByNormalLookupShouldBeFoundByImporterSpecificLookup) { |
| auto *Code = R"( |
| template <class T> |
| struct X { |
| friend void foo(){} |
| }; |
| )"; |
| TranslationUnitDecl *ToTU = getToTuDecl(Code, Lang_CXX03); |
| auto *X = FirstDeclMatcher<ClassTemplateDecl>().match( |
| ToTU, classTemplateDecl(hasName("X"))); |
| auto *Foo = FirstDeclMatcher<FunctionDecl>().match( |
| ToTU, functionDecl(hasName("foo"))); |
| DeclContext *FooDC = Foo->getDeclContext(); |
| DeclContext *FooLexicalDC = Foo->getLexicalDeclContext(); |
| ASSERT_EQ(cast<Decl>(FooLexicalDC), X->getTemplatedDecl()); |
| ASSERT_EQ(cast<Decl>(FooDC), ToTU); |
| DeclarationName FooName = Foo->getDeclName(); |
| |
| // Cannot find in the LookupTable of its DC (TUDecl) |
| SmallVector<NamedDecl *, 2> FoundDecls; |
| FooDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls); |
| EXPECT_EQ(FoundDecls.size(), 0u); |
| |
| // Cannot find in the LookupTable of its LexicalDC (X) |
| FooLexicalDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls); |
| EXPECT_EQ(FoundDecls.size(), 0u); |
| |
| // Can't find in the list of Decls of the DC. |
| EXPECT_EQ(findInDeclListOfDC(FooDC, FooName), nullptr); |
| |
| // Can't find in the list of Decls of the LexicalDC |
| EXPECT_EQ(findInDeclListOfDC(FooLexicalDC, FooName), nullptr); |
| |
| // ASTImporter specific lookup finds it. |
| ASTImporterLookupTable LT(*ToTU); |
| auto Res = LT.lookup(FooDC, Foo->getDeclName()); |
| ASSERT_EQ(Res.size(), 1u); |
| EXPECT_EQ(*Res.begin(), Foo); |
| } |
| |
| TEST_P(ASTImporterLookupTableTest, |
| FwdDeclStructShouldBeFoundByImporterSpecificLookup) { |
| TranslationUnitDecl *ToTU = |
| getToTuDecl("struct A { struct Foo *p; };", Lang_C99); |
| auto *Foo = |
| FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("Foo"))); |
| auto *A = |
| FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("A"))); |
| DeclContext *FooDC = Foo->getDeclContext(); |
| DeclContext *FooLexicalDC = Foo->getLexicalDeclContext(); |
| ASSERT_EQ(cast<Decl>(FooLexicalDC), A); |
| ASSERT_EQ(cast<Decl>(FooDC), ToTU); |
| DeclarationName FooName = Foo->getDeclName(); |
| |
| // Cannot find in the LookupTable of its DC (TUDecl). |
| SmallVector<NamedDecl *, 2> FoundDecls; |
| FooDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls); |
| EXPECT_EQ(FoundDecls.size(), 0u); |
| |
| // Cannot find in the LookupTable of its LexicalDC (A). |
| FooLexicalDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls); |
| EXPECT_EQ(FoundDecls.size(), 0u); |
| |
| // Can't find in the list of Decls of the DC. |
| EXPECT_EQ(findInDeclListOfDC(FooDC, FooName), nullptr); |
| |
| // Can find in the list of Decls of the LexicalDC. |
| EXPECT_EQ(findInDeclListOfDC(FooLexicalDC, FooName), Foo); |
| |
| // ASTImporter specific lookup finds it. |
| ASTImporterLookupTable LT(*ToTU); |
| auto Res = LT.lookup(FooDC, Foo->getDeclName()); |
| ASSERT_EQ(Res.size(), 1u); |
| EXPECT_EQ(*Res.begin(), Foo); |
| } |
| |
| TEST_P(ASTImporterLookupTableTest, LookupFindsNamesInDifferentDC) { |
| TranslationUnitDecl *ToTU = |
| getToTuDecl("int V; struct A { int V; }; struct B { int V; };", Lang_C99); |
| DeclarationName VName = FirstDeclMatcher<VarDecl>() |
| .match(ToTU, varDecl(hasName("V"))) |
| ->getDeclName(); |
| auto *A = |
| FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("A"))); |
| auto *B = |
| FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("B"))); |
| |
| ASTImporterLookupTable LT(*ToTU); |
| |
| auto Res = LT.lookup(cast<DeclContext>(A), VName); |
| ASSERT_EQ(Res.size(), 1u); |
| EXPECT_EQ(*Res.begin(), FirstDeclMatcher<FieldDecl>().match( |
| ToTU, fieldDecl(hasName("V"), |
| hasParent(recordDecl(hasName("A")))))); |
| Res = LT.lookup(cast<DeclContext>(B), VName); |
| ASSERT_EQ(Res.size(), 1u); |
| EXPECT_EQ(*Res.begin(), FirstDeclMatcher<FieldDecl>().match( |
| ToTU, fieldDecl(hasName("V"), |
| hasParent(recordDecl(hasName("B")))))); |
| Res = LT.lookup(ToTU, VName); |
| ASSERT_EQ(Res.size(), 1u); |
| EXPECT_EQ(*Res.begin(), FirstDeclMatcher<VarDecl>().match( |
| ToTU, varDecl(hasName("V"), |
| hasParent(translationUnitDecl())))); |
| } |
| |
| TEST_P(ASTImporterLookupTableTest, LookupFindsOverloadedNames) { |
| TranslationUnitDecl *ToTU = getToTuDecl( |
| R"( |
| void foo(); |
| void foo(int); |
| void foo(int, int); |
| )", |
| Lang_CXX03); |
| |
| ASTImporterLookupTable LT(*ToTU); |
| auto *F0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, functionDecl()); |
| auto *F2 = LastDeclMatcher<FunctionDecl>().match(ToTU, functionDecl()); |
| DeclarationName Name = F0->getDeclName(); |
| auto Res = LT.lookup(ToTU, Name); |
| EXPECT_EQ(Res.size(), 3u); |
| EXPECT_EQ(Res.count(F0), 1u); |
| EXPECT_EQ(Res.count(F2), 1u); |
| } |
| |
| TEST_P(ASTImporterLookupTableTest, |
| DifferentOperatorsShouldHaveDifferentResultSet) { |
| TranslationUnitDecl *ToTU = getToTuDecl( |
| R"( |
| struct X{}; |
| void operator+(X, X); |
| void operator-(X, X); |
| )", |
| Lang_CXX03); |
| |
| ASTImporterLookupTable LT(*ToTU); |
| auto *FPlus = FirstDeclMatcher<FunctionDecl>().match( |
| ToTU, functionDecl(hasOverloadedOperatorName("+"))); |
| auto *FMinus = FirstDeclMatcher<FunctionDecl>().match( |
| ToTU, functionDecl(hasOverloadedOperatorName("-"))); |
| DeclarationName NamePlus = FPlus->getDeclName(); |
| auto ResPlus = LT.lookup(ToTU, NamePlus); |
| EXPECT_EQ(ResPlus.size(), 1u); |
| EXPECT_EQ(ResPlus.count(FPlus), 1u); |
| EXPECT_EQ(ResPlus.count(FMinus), 0u); |
| DeclarationName NameMinus = FMinus->getDeclName(); |
| auto ResMinus = LT.lookup(ToTU, NameMinus); |
| EXPECT_EQ(ResMinus.size(), 1u); |
| EXPECT_EQ(ResMinus.count(FMinus), 1u); |
| EXPECT_EQ(ResMinus.count(FPlus), 0u); |
| EXPECT_NE(*ResMinus.begin(), *ResPlus.begin()); |
| } |
| |
| TEST_P(ASTImporterLookupTableTest, LookupDeclNamesFromDifferentTUs) { |
| TranslationUnitDecl *ToTU = getToTuDecl( |
| R"( |
| struct X {}; |
| void operator+(X, X); |
| )", |
| Lang_CXX03); |
| auto *ToPlus = FirstDeclMatcher<FunctionDecl>().match( |
| ToTU, functionDecl(hasOverloadedOperatorName("+"))); |
| |
| Decl *FromTU = getTuDecl( |
| R"( |
| struct X {}; |
| void operator+(X, X); |
| )", |
| Lang_CXX03); |
| auto *FromPlus = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasOverloadedOperatorName("+"))); |
| |
| // FromPlus have a different TU, thus its DeclarationName is different too. |
| ASSERT_NE(ToPlus->getDeclName(), FromPlus->getDeclName()); |
| |
| ASTImporterLookupTable LT(*ToTU); |
| auto Res = LT.lookup(ToTU, ToPlus->getDeclName()); |
| ASSERT_EQ(Res.size(), 1u); |
| EXPECT_EQ(*Res.begin(), ToPlus); |
| |
| // FromPlus have a different TU, thus its DeclarationName is different too. |
| Res = LT.lookup(ToTU, FromPlus->getDeclName()); |
| ASSERT_EQ(Res.size(), 0u); |
| } |
| |
| TEST_P(ASTImporterLookupTableTest, |
| LookupFindsFwdFriendClassDeclWithElaboratedType) { |
| TranslationUnitDecl *ToTU = getToTuDecl( |
| R"( |
| class Y { friend class F; }; |
| )", |
| Lang_CXX03); |
| |
| // In this case, the CXXRecordDecl is hidden, the FriendDecl is not a parent. |
| // So we must dig up the underlying CXXRecordDecl. |
| ASTImporterLookupTable LT(*ToTU); |
| auto *FriendD = FirstDeclMatcher<FriendDecl>().match(ToTU, friendDecl()); |
| const RecordDecl *RD = getRecordDeclOfFriend(FriendD); |
| auto *Y = FirstDeclMatcher<CXXRecordDecl>().match( |
| ToTU, cxxRecordDecl(hasName("Y"))); |
| |
| DeclarationName Name = RD->getDeclName(); |
| auto Res = LT.lookup(ToTU, Name); |
| EXPECT_EQ(Res.size(), 1u); |
| EXPECT_EQ(*Res.begin(), RD); |
| |
| Res = LT.lookup(Y, Name); |
| EXPECT_EQ(Res.size(), 0u); |
| } |
| |
| TEST_P(ASTImporterLookupTableTest, |
| LookupFindsFwdFriendClassDeclWithUnelaboratedType) { |
| TranslationUnitDecl *ToTU = getToTuDecl( |
| R"( |
| class F; |
| class Y { friend F; }; |
| )", |
| Lang_CXX11); |
| |
| // In this case, the CXXRecordDecl is hidden, the FriendDecl is not a parent. |
| // So we must dig up the underlying CXXRecordDecl. |
| ASTImporterLookupTable LT(*ToTU); |
| auto *FriendD = FirstDeclMatcher<FriendDecl>().match(ToTU, friendDecl()); |
| const RecordDecl *RD = getRecordDeclOfFriend(FriendD); |
| auto *Y = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, cxxRecordDecl(hasName("Y"))); |
| |
| DeclarationName Name = RD->getDeclName(); |
| auto Res = LT.lookup(ToTU, Name); |
| EXPECT_EQ(Res.size(), 1u); |
| EXPECT_EQ(*Res.begin(), RD); |
| |
| Res = LT.lookup(Y, Name); |
| EXPECT_EQ(Res.size(), 0u); |
| } |
| |
| TEST_P(ASTImporterLookupTableTest, |
| LookupFindsFriendClassDeclWithTypeAliasDoesNotAssert) { |
| TranslationUnitDecl *ToTU = getToTuDecl( |
| R"( |
| class F; |
| using alias_of_f = F; |
| class Y { friend alias_of_f; }; |
| )", |
| Lang_CXX11); |
| |
| // ASTImporterLookupTable constructor handles using declarations correctly, |
| // no assert is expected. |
| ASTImporterLookupTable LT(*ToTU); |
| |
| auto *Alias = FirstDeclMatcher<TypeAliasDecl>().match( |
| ToTU, typeAliasDecl(hasName("alias_of_f"))); |
| DeclarationName Name = Alias->getDeclName(); |
| auto Res = LT.lookup(ToTU, Name); |
| EXPECT_EQ(Res.count(Alias), 1u); |
| } |
| |
| TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendClassTemplateDecl) { |
| TranslationUnitDecl *ToTU = getToTuDecl( |
| R"( |
| class Y { template <class T> friend class F; }; |
| )", |
| Lang_CXX03); |
| |
| ASTImporterLookupTable LT(*ToTU); |
| auto *F = FirstDeclMatcher<ClassTemplateDecl>().match( |
| ToTU, classTemplateDecl(hasName("F"))); |
| DeclarationName Name = F->getDeclName(); |
| auto Res = LT.lookup(ToTU, Name); |
| EXPECT_EQ(Res.size(), 2u); |
| EXPECT_EQ(Res.count(F), 1u); |
| EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u); |
| } |
| |
| TEST_P(ASTImporterLookupTableTest, DependentFriendClass) { |
| TranslationUnitDecl *ToTU = getToTuDecl( |
| R"( |
| template <typename T> |
| class F; |
| |
| template <typename T> |
| class Y { |
| friend class F<T>; |
| }; |
| )", |
| Lang_CXX03); |
| |
| ASTImporterLookupTable LT(*ToTU); |
| auto *F = FirstDeclMatcher<ClassTemplateDecl>().match( |
| ToTU, classTemplateDecl(hasName("F"))); |
| DeclarationName Name = F->getDeclName(); |
| auto Res = LT.lookup(ToTU, Name); |
| EXPECT_EQ(Res.size(), 2u); |
| EXPECT_EQ(Res.count(F), 1u); |
| EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u); |
| } |
| |
| TEST_P(ASTImporterLookupTableTest, FriendClassTemplateSpecialization) { |
| TranslationUnitDecl *ToTU = getToTuDecl( |
| R"( |
| template <typename T> |
| class F; |
| |
| class Y { |
| friend class F<int>; |
| }; |
| )", |
| Lang_CXX03); |
| |
| ASTImporterLookupTable LT(*ToTU); |
| auto *F = FirstDeclMatcher<ClassTemplateDecl>().match( |
| ToTU, classTemplateDecl(hasName("F"))); |
| DeclarationName Name = F->getDeclName(); |
| auto Res = LT.lookup(ToTU, Name); |
| ASSERT_EQ(Res.size(), 3u); |
| EXPECT_EQ(Res.count(F), 1u); |
| EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u); |
| EXPECT_EQ(Res.count(*F->spec_begin()), 1u); |
| } |
| |
| TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendFunctionDecl) { |
| TranslationUnitDecl *ToTU = getToTuDecl( |
| R"( |
| class Y { friend void F(); }; |
| )", |
| Lang_CXX03); |
| |
| ASTImporterLookupTable LT(*ToTU); |
| auto *F = |
| FirstDeclMatcher<FunctionDecl>().match(ToTU, functionDecl(hasName("F"))); |
| DeclarationName Name = F->getDeclName(); |
| auto Res = LT.lookup(ToTU, Name); |
| EXPECT_EQ(Res.size(), 1u); |
| EXPECT_EQ(*Res.begin(), F); |
| } |
| |
| TEST_P(ASTImporterLookupTableTest, |
| LookupFindsDeclsInClassTemplateSpecialization) { |
| TranslationUnitDecl *ToTU = getToTuDecl( |
| R"( |
| template <typename T> |
| struct X { |
| int F; |
| }; |
| void foo() { |
| X<char> xc; |
| } |
| )", |
| Lang_CXX03); |
| |
| ASTImporterLookupTable LT(*ToTU); |
| |
| auto *Template = FirstDeclMatcher<ClassTemplateDecl>().match( |
| ToTU, classTemplateDecl(hasName("X"))); |
| auto *FieldInTemplate = FirstDeclMatcher<FieldDecl>().match( |
| ToTU, |
| fieldDecl(hasParent(cxxRecordDecl(hasParent(classTemplateDecl()))))); |
| |
| auto *Spec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match( |
| ToTU, classTemplateSpecializationDecl(hasName("X"))); |
| FieldDecl *FieldInSpec = *Spec->field_begin(); |
| ASSERT_TRUE(FieldInSpec); |
| |
| DeclarationName Name = FieldInSpec->getDeclName(); |
| auto TemplateDC = cast<DeclContext>(Template->getTemplatedDecl()); |
| |
| SmallVector<NamedDecl *, 2> FoundDecls; |
| TemplateDC->getRedeclContext()->localUncachedLookup(Name, FoundDecls); |
| EXPECT_EQ(FoundDecls.size(), 1u); |
| EXPECT_EQ(FoundDecls[0], FieldInTemplate); |
| |
| auto Res = LT.lookup(TemplateDC, Name); |
| ASSERT_EQ(Res.size(), 1u); |
| EXPECT_EQ(*Res.begin(), FieldInTemplate); |
| |
| cast<DeclContext>(Spec)->getRedeclContext()->localUncachedLookup(Name, |
| FoundDecls); |
| EXPECT_EQ(FoundDecls.size(), 1u); |
| EXPECT_EQ(FoundDecls[0], FieldInSpec); |
| |
| Res = LT.lookup(cast<DeclContext>(Spec), Name); |
| ASSERT_EQ(Res.size(), 1u); |
| EXPECT_EQ(*Res.begin(), FieldInSpec); |
| } |
| |
| TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendFunctionTemplateDecl) { |
| TranslationUnitDecl *ToTU = getToTuDecl( |
| R"( |
| class Y { template <class T> friend void F(); }; |
| )", |
| Lang_CXX03); |
| |
| ASTImporterLookupTable LT(*ToTU); |
| auto *F = FirstDeclMatcher<FunctionTemplateDecl>().match( |
| ToTU, functionTemplateDecl(hasName("F"))); |
| DeclarationName Name = F->getDeclName(); |
| auto Res = LT.lookup(ToTU, Name); |
| EXPECT_EQ(Res.size(), 2u); |
| EXPECT_EQ(Res.count(F), 1u); |
| EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u); |
| } |
| |
| TEST_P(ASTImporterLookupTableTest, MultipleBefriendingClasses) { |
| TranslationUnitDecl *ToTU = getToTuDecl( |
| R"( |
| struct X; |
| struct A { |
| friend struct X; |
| }; |
| struct B { |
| friend struct X; |
| }; |
| )", |
| Lang_CXX03); |
| |
| ASTImporterLookupTable LT(*ToTU); |
| auto *X = FirstDeclMatcher<CXXRecordDecl>().match( |
| ToTU, cxxRecordDecl(hasName("X"))); |
| auto *FriendD0 = FirstDeclMatcher<FriendDecl>().match(ToTU, friendDecl()); |
| auto *FriendD1 = LastDeclMatcher<FriendDecl>().match(ToTU, friendDecl()); |
| const RecordDecl *RD0 = getRecordDeclOfFriend(FriendD0); |
| const RecordDecl *RD1 = getRecordDeclOfFriend(FriendD1); |
| ASSERT_EQ(RD0, RD1); |
| ASSERT_EQ(RD1, X); |
| |
| DeclarationName Name = X->getDeclName(); |
| auto Res = LT.lookup(ToTU, Name); |
| EXPECT_EQ(Res.size(), 1u); |
| EXPECT_EQ(*Res.begin(), X); |
| } |
| |
| TEST_P(ASTImporterLookupTableTest, EnumConstantDecl) { |
| TranslationUnitDecl *ToTU = getToTuDecl( |
| R"( |
| enum E { |
| A, |
| B |
| }; |
| )", |
| Lang_C99); |
| |
| ASTImporterLookupTable LT(*ToTU); |
| auto *E = FirstDeclMatcher<EnumDecl>().match(ToTU, enumDecl(hasName("E"))); |
| auto *A = FirstDeclMatcher<EnumConstantDecl>().match( |
| ToTU, enumConstantDecl(hasName("A"))); |
| |
| DeclarationName Name = A->getDeclName(); |
| // Redecl context is the TU. |
| ASSERT_EQ(E->getRedeclContext(), ToTU); |
| |
| SmallVector<NamedDecl *, 2> FoundDecls; |
| // Normal lookup finds in the DC. |
| E->localUncachedLookup(Name, FoundDecls); |
| EXPECT_EQ(FoundDecls.size(), 1u); |
| |
| // Normal lookup finds in the Redecl context. |
| ToTU->localUncachedLookup(Name, FoundDecls); |
| EXPECT_EQ(FoundDecls.size(), 1u); |
| |
| // Import specific lookup finds in the DC. |
| auto Res = LT.lookup(E, Name); |
| ASSERT_EQ(Res.size(), 1u); |
| EXPECT_EQ(*Res.begin(), A); |
| |
| // Import specific lookup finds in the Redecl context. |
| Res = LT.lookup(ToTU, Name); |
| ASSERT_EQ(Res.size(), 1u); |
| EXPECT_EQ(*Res.begin(), A); |
| } |
| |
| TEST_P(ASTImporterLookupTableTest, LookupSearchesInTheWholeRedeclChain) { |
| TranslationUnitDecl *ToTU = getToTuDecl( |
| R"( |
| namespace N { |
| int A; |
| } |
| namespace N { |
| } |
| )", |
| Lang_CXX03); |
| auto *N1 = |
| LastDeclMatcher<NamespaceDecl>().match(ToTU, namespaceDecl(hasName("N"))); |
| auto *A = FirstDeclMatcher<VarDecl>().match(ToTU, varDecl(hasName("A"))); |
| DeclarationName Name = A->getDeclName(); |
| |
| ASTImporterLookupTable LT(*ToTU); |
| auto Res = LT.lookup(N1, Name); |
| ASSERT_EQ(Res.size(), 1u); |
| EXPECT_EQ(*Res.begin(), A); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| RedeclChainShouldBeCorrectAmongstNamespaces) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| namespace NS { |
| struct X; |
| struct Y { |
| static const int I = 3; |
| }; |
| } |
| namespace NS { |
| struct X { // <--- To be imported |
| void method(int i = Y::I) {} |
| int f; |
| }; |
| } |
| )", |
| Lang_CXX03); |
| auto *FromFwd = FirstDeclMatcher<CXXRecordDecl>().match( |
| FromTU, cxxRecordDecl(hasName("X"), unless(isImplicit()))); |
| auto *FromDef = LastDeclMatcher<CXXRecordDecl>().match( |
| FromTU, |
| cxxRecordDecl(hasName("X"), isDefinition(), unless(isImplicit()))); |
| ASSERT_NE(FromFwd, FromDef); |
| ASSERT_FALSE(FromFwd->isThisDeclarationADefinition()); |
| ASSERT_TRUE(FromDef->isThisDeclarationADefinition()); |
| ASSERT_EQ(FromFwd->getCanonicalDecl(), FromDef->getCanonicalDecl()); |
| |
| auto *ToDef = cast_or_null<CXXRecordDecl>(Import(FromDef, Lang_CXX03)); |
| auto *ToFwd = cast_or_null<CXXRecordDecl>(Import(FromFwd, Lang_CXX03)); |
| EXPECT_NE(ToFwd, ToDef); |
| EXPECT_FALSE(ToFwd->isThisDeclarationADefinition()); |
| EXPECT_TRUE(ToDef->isThisDeclarationADefinition()); |
| EXPECT_EQ(ToFwd->getCanonicalDecl(), ToDef->getCanonicalDecl()); |
| auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| // We expect no (ODR) warning during the import. |
| EXPECT_EQ(0u, ToTU->getASTContext().getDiagnostics().getNumWarnings()); |
| } |
| |
| struct ImportFriendFunctionTemplates : ASTImporterOptionSpecificTestBase {}; |
| |
| TEST_P(ImportFriendFunctionTemplates, LookupShouldFindPreviousFriend) { |
| Decl *ToTU = getToTuDecl( |
| R"( |
| class X { |
| template <typename T> friend void foo(); |
| }; |
| )", |
| Lang_CXX03); |
| auto *Friend = FirstDeclMatcher<FunctionTemplateDecl>().match( |
| ToTU, functionTemplateDecl(hasName("foo"))); |
| |
| Decl *FromTU = getTuDecl( |
| R"( |
| template <typename T> void foo(); |
| )", |
| Lang_CXX03); |
| auto *FromFoo = FirstDeclMatcher<FunctionTemplateDecl>().match( |
| FromTU, functionTemplateDecl(hasName("foo"))); |
| auto *Imported = Import(FromFoo, Lang_CXX03); |
| |
| EXPECT_EQ(Imported->getPreviousDecl(), Friend); |
| } |
| |
| struct ASTImporterWithFakeErrors : ASTImporter { |
| using ASTImporter::ASTImporter; |
| bool returnWithErrorInTest() override { return true; } |
| }; |
| |
| struct ErrorHandlingTest : ASTImporterOptionSpecificTestBase { |
| ErrorHandlingTest() { |
| Creator = [](ASTContext &ToContext, FileManager &ToFileManager, |
| ASTContext &FromContext, FileManager &FromFileManager, |
| bool MinimalImport, |
| const std::shared_ptr<ASTImporterSharedState> &SharedState) { |
| return new ASTImporterWithFakeErrors(ToContext, ToFileManager, |
| FromContext, FromFileManager, |
| MinimalImport, SharedState); |
| }; |
| } |
| // In this test we purposely report an error (UnsupportedConstruct) when |
| // importing the below stmt. |
| static constexpr auto* ErroneousStmt = R"( asm(""); )"; |
| }; |
| |
| // Check a case when no new AST node is created in the AST before encountering |
| // the error. |
| TEST_P(ErrorHandlingTest, ErrorHappensBeforeCreatingANewNode) { |
| TranslationUnitDecl *ToTU = getToTuDecl( |
| R"( |
| template <typename T> |
| class X {}; |
| template <> |
| class X<int> { int a; }; |
| )", |
| Lang_CXX03); |
| TranslationUnitDecl *FromTU = getTuDecl( |
| R"( |
| template <typename T> |
| class X {}; |
| template <> |
| class X<int> { double b; }; |
| )", |
| Lang_CXX03); |
| auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match( |
| FromTU, classTemplateSpecializationDecl(hasName("X"))); |
| ClassTemplateSpecializationDecl *ImportedSpec = Import(FromSpec, Lang_CXX03); |
| EXPECT_FALSE(ImportedSpec); |
| |
| // The original Decl is kept, no new decl is created. |
| EXPECT_EQ(DeclCounter<ClassTemplateSpecializationDecl>().match( |
| ToTU, classTemplateSpecializationDecl(hasName("X"))), |
| 1u); |
| |
| // But an error is set to the counterpart in the "from" context. |
| ASTImporter *Importer = findFromTU(FromSpec)->Importer.get(); |
| Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromSpec); |
| ASSERT_TRUE(OptErr); |
| EXPECT_EQ(OptErr->Error, ImportError::NameConflict); |
| } |
| |
| // Check a case when a new AST node is created but not linked to the AST before |
| // encountering the error. |
| TEST_P(ErrorHandlingTest, |
| ErrorHappensAfterCreatingTheNodeButBeforeLinkingThatToTheAST) { |
| TranslationUnitDecl *FromTU = getTuDecl( |
| std::string("void foo() { ") + ErroneousStmt + " }", Lang_CXX03); |
| auto *FromFoo = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("foo"))); |
| |
| FunctionDecl *ImportedFoo = Import(FromFoo, Lang_CXX03); |
| EXPECT_FALSE(ImportedFoo); |
| |
| TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| // Created, but not linked. |
| EXPECT_EQ( |
| DeclCounter<FunctionDecl>().match(ToTU, functionDecl(hasName("foo"))), |
| 0u); |
| |
| ASTImporter *Importer = findFromTU(FromFoo)->Importer.get(); |
| Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromFoo); |
| ASSERT_TRUE(OptErr); |
| EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct); |
| } |
| |
| // Check a case when a new AST node is created and linked to the AST before |
| // encountering the error. The error is set for the counterpart of the nodes in |
| // the "from" context. |
| TEST_P(ErrorHandlingTest, ErrorHappensAfterNodeIsCreatedAndLinked) { |
| TranslationUnitDecl *FromTU = getTuDecl(std::string(R"( |
| void f(); |
| void f() { )") + ErroneousStmt + R"( } |
| )", |
| Lang_CXX03); |
| auto *FromProto = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("f"))); |
| auto *FromDef = |
| LastDeclMatcher<FunctionDecl>().match(FromTU, functionDecl(hasName("f"))); |
| FunctionDecl *ImportedProto = Import(FromProto, Lang_CXX03); |
| EXPECT_FALSE(ImportedProto); // Could not import. |
| // However, we created two nodes in the AST. 1) the fwd decl 2) the |
| // definition. The definition is not added to its DC, but the fwd decl is |
| // there. |
| TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, functionDecl(hasName("f"))), |
| 1u); |
| // Match the fwd decl. |
| auto *ToProto = |
| FirstDeclMatcher<FunctionDecl>().match(ToTU, functionDecl(hasName("f"))); |
| EXPECT_TRUE(ToProto); |
| // An error is set to the counterpart in the "from" context both for the fwd |
| // decl and the definition. |
| ASTImporter *Importer = findFromTU(FromProto)->Importer.get(); |
| Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromProto); |
| ASSERT_TRUE(OptErr); |
| EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct); |
| OptErr = Importer->getImportDeclErrorIfAny(FromDef); |
| ASSERT_TRUE(OptErr); |
| EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct); |
| } |
| |
| // An error should be set for a class if we cannot import one member. |
| TEST_P(ErrorHandlingTest, ErrorIsPropagatedFromMemberToClass) { |
| TranslationUnitDecl *FromTU = getTuDecl(std::string(R"( |
| class X { |
| void f() { )") + ErroneousStmt + R"( } // This member has the error |
| // during import. |
| void ok(); // The error should not prevent importing this. |
| }; // An error will be set for X too. |
| )", |
| Lang_CXX03); |
| auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match( |
| FromTU, cxxRecordDecl(hasName("X"))); |
| CXXRecordDecl *ImportedX = Import(FromX, Lang_CXX03); |
| |
| // An error is set for X. |
| EXPECT_FALSE(ImportedX); |
| ASTImporter *Importer = findFromTU(FromX)->Importer.get(); |
| Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromX); |
| ASSERT_TRUE(OptErr); |
| EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct); |
| |
| // An error is set for f(). |
| auto *FromF = FirstDeclMatcher<CXXMethodDecl>().match( |
| FromTU, cxxMethodDecl(hasName("f"))); |
| OptErr = Importer->getImportDeclErrorIfAny(FromF); |
| ASSERT_TRUE(OptErr); |
| EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct); |
| // And any subsequent import should fail. |
| CXXMethodDecl *ImportedF = Import(FromF, Lang_CXX03); |
| EXPECT_FALSE(ImportedF); |
| |
| // There is an error set for the other member too. |
| auto *FromOK = FirstDeclMatcher<CXXMethodDecl>().match( |
| FromTU, cxxMethodDecl(hasName("ok"))); |
| OptErr = Importer->getImportDeclErrorIfAny(FromOK); |
| EXPECT_TRUE(OptErr); |
| // Cannot import the other member. |
| CXXMethodDecl *ImportedOK = Import(FromOK, Lang_CXX03); |
| EXPECT_FALSE(ImportedOK); |
| } |
| |
| // Check that an error propagates to the dependent AST nodes. |
| // In the below code it means that an error in X should propagate to A. |
| // And even to F since the containing A is erroneous. |
| // And to all AST nodes which we visit during the import process which finally |
| // ends up in a failure (in the error() function). |
| TEST_P(ErrorHandlingTest, ErrorPropagatesThroughImportCycles) { |
| Decl *FromTU = getTuDecl(std::string(R"( |
| namespace NS { |
| class A { |
| template <int I> class F {}; |
| class X { |
| template <int I> friend class F; |
| void error() { )") + |
| ErroneousStmt + R"( } |
| }; |
| }; |
| |
| class B {}; |
| } // NS |
| )", |
| Lang_CXX03, "input0.cc"); |
| |
| auto *FromFRD = FirstDeclMatcher<CXXRecordDecl>().match( |
| FromTU, cxxRecordDecl(hasName("F"), isDefinition())); |
| auto *FromA = FirstDeclMatcher<CXXRecordDecl>().match( |
| FromTU, cxxRecordDecl(hasName("A"), isDefinition())); |
| auto *FromB = FirstDeclMatcher<CXXRecordDecl>().match( |
| FromTU, cxxRecordDecl(hasName("B"), isDefinition())); |
| auto *FromNS = FirstDeclMatcher<NamespaceDecl>().match( |
| FromTU, namespaceDecl(hasName("NS"))); |
| |
| // Start by importing the templated CXXRecordDecl of F. |
| // Import fails for that. |
| EXPECT_FALSE(Import(FromFRD, Lang_CXX03)); |
| // Import fails for A. |
| EXPECT_FALSE(Import(FromA, Lang_CXX03)); |
| // But we should be able to import the independent B. |
| EXPECT_TRUE(Import(FromB, Lang_CXX03)); |
| // And the namespace. |
| EXPECT_TRUE(Import(FromNS, Lang_CXX03)); |
| |
| // An error is set to the templated CXXRecordDecl of F. |
| ASTImporter *Importer = findFromTU(FromFRD)->Importer.get(); |
| Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromFRD); |
| EXPECT_TRUE(OptErr); |
| |
| // An error is set to A. |
| OptErr = Importer->getImportDeclErrorIfAny(FromA); |
| EXPECT_TRUE(OptErr); |
| |
| // There is no error set to B. |
| OptErr = Importer->getImportDeclErrorIfAny(FromB); |
| EXPECT_FALSE(OptErr); |
| |
| // There is no error set to NS. |
| OptErr = Importer->getImportDeclErrorIfAny(FromNS); |
| EXPECT_FALSE(OptErr); |
| |
| // Check some of those decls whose ancestor is X, they all should have an |
| // error set if we visited them during an import process which finally failed. |
| // These decls are part of a cycle in an ImportPath. |
| // There would not be any error set for these decls if we hadn't follow the |
| // ImportPaths and the cycles. |
| OptErr = Importer->getImportDeclErrorIfAny( |
| FirstDeclMatcher<ClassTemplateDecl>().match( |
| FromTU, classTemplateDecl(hasName("F")))); |
| // An error is set to the 'F' ClassTemplateDecl. |
| EXPECT_TRUE(OptErr); |
| // An error is set to the FriendDecl. |
| OptErr = Importer->getImportDeclErrorIfAny( |
| FirstDeclMatcher<FriendDecl>().match( |
| FromTU, friendDecl())); |
| EXPECT_TRUE(OptErr); |
| // An error is set to the implicit class of A. |
| OptErr = |
| Importer->getImportDeclErrorIfAny(FirstDeclMatcher<CXXRecordDecl>().match( |
| FromTU, cxxRecordDecl(hasName("A"), isImplicit()))); |
| EXPECT_TRUE(OptErr); |
| // An error is set to the implicit class of X. |
| OptErr = |
| Importer->getImportDeclErrorIfAny(FirstDeclMatcher<CXXRecordDecl>().match( |
| FromTU, cxxRecordDecl(hasName("X"), isImplicit()))); |
| EXPECT_TRUE(OptErr); |
| } |
| |
| TEST_P(ErrorHandlingTest, ErrorIsNotPropagatedFromMemberToNamespace) { |
| TranslationUnitDecl *FromTU = getTuDecl(std::string(R"( |
| namespace X { |
| void f() { )") + ErroneousStmt + R"( } // This member has the error |
| // during import. |
| void ok(); // The error should not prevent importing this. |
| }; // An error will be set for X too. |
| )", |
| Lang_CXX03); |
| auto *FromX = FirstDeclMatcher<NamespaceDecl>().match( |
| FromTU, namespaceDecl(hasName("X"))); |
| NamespaceDecl *ImportedX = Import(FromX, Lang_CXX03); |
| |
| // There is no error set for X. |
| EXPECT_TRUE(ImportedX); |
| ASTImporter *Importer = findFromTU(FromX)->Importer.get(); |
| Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromX); |
| ASSERT_FALSE(OptErr); |
| |
| // An error is set for f(). |
| auto *FromF = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("f"))); |
| OptErr = Importer->getImportDeclErrorIfAny(FromF); |
| ASSERT_TRUE(OptErr); |
| EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct); |
| // And any subsequent import should fail. |
| FunctionDecl *ImportedF = Import(FromF, Lang_CXX03); |
| EXPECT_FALSE(ImportedF); |
| |
| // There is no error set for ok(). |
| auto *FromOK = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("ok"))); |
| OptErr = Importer->getImportDeclErrorIfAny(FromOK); |
| EXPECT_FALSE(OptErr); |
| // And we should be able to import. |
| FunctionDecl *ImportedOK = Import(FromOK, Lang_CXX03); |
| EXPECT_TRUE(ImportedOK); |
| } |
| |
| // An error should be set for a class if it had a previous import with an error |
| // from another TU. |
| TEST_P(ErrorHandlingTest, |
| ImportedDeclWithErrorShouldFailTheImportOfDeclWhichMapToIt) { |
| // We already have a fwd decl. |
| TranslationUnitDecl *ToTU = getToTuDecl("class X;", Lang_CXX03); |
| // Then we import a definition. |
| { |
| TranslationUnitDecl *FromTU = getTuDecl(std::string(R"( |
| class X { |
| void f() { )") + ErroneousStmt + R"( } |
| void ok(); |
| }; |
| )", |
| Lang_CXX03); |
| auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match( |
| FromTU, cxxRecordDecl(hasName("X"))); |
| CXXRecordDecl *ImportedX = Import(FromX, Lang_CXX03); |
| |
| // An error is set for X ... |
| EXPECT_FALSE(ImportedX); |
| ASTImporter *Importer = findFromTU(FromX)->Importer.get(); |
| Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromX); |
| ASSERT_TRUE(OptErr); |
| EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct); |
| } |
| // ... but the node had been created. |
| auto *ToXDef = FirstDeclMatcher<CXXRecordDecl>().match( |
| ToTU, cxxRecordDecl(hasName("X"), isDefinition())); |
| // An error is set for "ToXDef" in the shared state. |
| Optional<ImportError> OptErr = |
| SharedStatePtr->getImportDeclErrorIfAny(ToXDef); |
| ASSERT_TRUE(OptErr); |
| EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct); |
| |
| auto *ToXFwd = FirstDeclMatcher<CXXRecordDecl>().match( |
| ToTU, cxxRecordDecl(hasName("X"), unless(isDefinition()))); |
| // An error is NOT set for the fwd Decl of X in the shared state. |
| OptErr = SharedStatePtr->getImportDeclErrorIfAny(ToXFwd); |
| ASSERT_FALSE(OptErr); |
| |
| // Try to import X again but from another TU. |
| { |
| TranslationUnitDecl *FromTU = getTuDecl(std::string(R"( |
| class X { |
| void f() { )") + ErroneousStmt + R"( } |
| void ok(); |
| }; |
| )", |
| Lang_CXX03, "input1.cc"); |
| |
| auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match( |
| FromTU, cxxRecordDecl(hasName("X"))); |
| CXXRecordDecl *ImportedX = Import(FromX, Lang_CXX03); |
| |
| // If we did not save the errors for the "to" context then the below checks |
| // would fail, because the lookup finds the fwd Decl of the existing |
| // definition in the "to" context. We can reach the existing definition via |
| // the found fwd Decl. That existing definition is structurally equivalent |
| // (we check only the fields) with this one we want to import, so we return |
| // with the existing definition, which is erroneous (one method is missing). |
| |
| // The import should fail. |
| EXPECT_FALSE(ImportedX); |
| ASTImporter *Importer = findFromTU(FromX)->Importer.get(); |
| Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromX); |
| // And an error is set for this new X in the "from" ctx. |
| ASSERT_TRUE(OptErr); |
| EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct); |
| } |
| } |
| |
| TEST_P(ErrorHandlingTest, ImportOfOverriddenMethods) { |
| auto MatchFooA = |
| functionDecl(hasName("foo"), hasAncestor(cxxRecordDecl(hasName("A")))); |
| auto MatchFooB = |
| functionDecl(hasName("foo"), hasAncestor(cxxRecordDecl(hasName("B")))); |
| auto MatchFooC = |
| functionDecl(hasName("foo"), hasAncestor(cxxRecordDecl(hasName("C")))); |
| |
| // Provoke import of a method that has overridden methods with import error. |
| TranslationUnitDecl *FromTU = getTuDecl(std::string(R"( |
| struct C; |
| struct A { |
| virtual void foo(); |
| void f1(C *); |
| }; |
| void A::foo() { |
| )") + ErroneousStmt + R"( |
| } |
| struct B : public A { |
| void foo() override; |
| }; |
| struct C : public B { |
| void foo() override; |
| }; |
| )", |
| Lang_CXX11); |
| auto *FromFooA = FirstDeclMatcher<FunctionDecl>().match(FromTU, MatchFooA); |
| auto *FromFooB = FirstDeclMatcher<FunctionDecl>().match(FromTU, MatchFooB); |
| auto *FromFooC = FirstDeclMatcher<FunctionDecl>().match(FromTU, MatchFooC); |
| |
| EXPECT_FALSE(Import(FromFooA, Lang_CXX11)); |
| ASTImporter *Importer = findFromTU(FromFooA)->Importer.get(); |
| auto CheckError = [&Importer](Decl *FromD) { |
| Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromD); |
| ASSERT_TRUE(OptErr); |
| EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct); |
| }; |
| CheckError(FromFooA); |
| EXPECT_FALSE(Import(FromFooB, Lang_CXX11)); |
| CheckError(FromFooB); |
| EXPECT_FALSE(Import(FromFooC, Lang_CXX11)); |
| CheckError(FromFooC); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, LambdaInFunctionBody) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| void f() { |
| auto L = [](){}; |
| } |
| )", |
| Lang_CXX11, "input0.cc"); |
| auto Pattern = lambdaExpr(); |
| CXXRecordDecl *FromL = |
| FirstDeclMatcher<LambdaExpr>().match(FromTU, Pattern)->getLambdaClass(); |
| |
| auto ToL = Import(FromL, Lang_CXX11); |
| unsigned ToLSize = std::distance(ToL->decls().begin(), ToL->decls().end()); |
| unsigned FromLSize = |
| std::distance(FromL->decls().begin(), FromL->decls().end()); |
| EXPECT_NE(ToLSize, 0u); |
| EXPECT_EQ(ToLSize, FromLSize); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, LambdaInFunctionParam) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| template <typename F> |
| void f(F L = [](){}) {} |
| )", |
| Lang_CXX11, "input0.cc"); |
| auto Pattern = lambdaExpr(); |
| CXXRecordDecl *FromL = |
| FirstDeclMatcher<LambdaExpr>().match(FromTU, Pattern)->getLambdaClass(); |
| |
| auto ToL = Import(FromL, Lang_CXX11); |
| unsigned ToLSize = std::distance(ToL->decls().begin(), ToL->decls().end()); |
| unsigned FromLSize = |
| std::distance(FromL->decls().begin(), FromL->decls().end()); |
| EXPECT_NE(ToLSize, 0u); |
| EXPECT_EQ(ToLSize, FromLSize); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, LambdaInGlobalScope) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| auto l1 = [](unsigned lp) { return 1; }; |
| auto l2 = [](int lp) { return 2; }; |
| int f(int p) { |
| return l1(p) + l2(p); |
| } |
| )", |
| Lang_CXX11, "input0.cc"); |
| FunctionDecl *FromF = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("f"))); |
| FunctionDecl *ToF = Import(FromF, Lang_CXX11); |
| EXPECT_TRUE(ToF); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| ImportExistingFriendClassTemplateDef) { |
| auto Code = |
| R"( |
| template <class T1, class T2> |
| struct Base { |
| template <class U1, class U2> |
| friend struct Class; |
| }; |
| template <class T1, class T2> |
| struct Class { }; |
| )"; |
| |
| TranslationUnitDecl *ToTU = getToTuDecl(Code, Lang_CXX03); |
| TranslationUnitDecl *FromTU = getTuDecl(Code, Lang_CXX03, "input.cc"); |
| |
| auto *ToClassProto = FirstDeclMatcher<ClassTemplateDecl>().match( |
| ToTU, classTemplateDecl(hasName("Class"))); |
| auto *ToClassDef = LastDeclMatcher<ClassTemplateDecl>().match( |
| ToTU, classTemplateDecl(hasName("Class"))); |
| ASSERT_FALSE(ToClassProto->isThisDeclarationADefinition()); |
| ASSERT_TRUE(ToClassDef->isThisDeclarationADefinition()); |
| // Previous friend decl is not linked to it! |
| ASSERT_FALSE(ToClassDef->getPreviousDecl()); |
| ASSERT_EQ(ToClassDef->getMostRecentDecl(), ToClassDef); |
| ASSERT_EQ(ToClassProto->getMostRecentDecl(), ToClassProto); |
| |
| auto *FromClassProto = FirstDeclMatcher<ClassTemplateDecl>().match( |
| FromTU, classTemplateDecl(hasName("Class"))); |
| auto *FromClassDef = LastDeclMatcher<ClassTemplateDecl>().match( |
| FromTU, classTemplateDecl(hasName("Class"))); |
| ASSERT_FALSE(FromClassProto->isThisDeclarationADefinition()); |
| ASSERT_TRUE(FromClassDef->isThisDeclarationADefinition()); |
| ASSERT_FALSE(FromClassDef->getPreviousDecl()); |
| ASSERT_EQ(FromClassDef->getMostRecentDecl(), FromClassDef); |
| ASSERT_EQ(FromClassProto->getMostRecentDecl(), FromClassProto); |
| |
| auto *ImportedDef = Import(FromClassDef, Lang_CXX03); |
| // At import we should find the definition for 'Class' even if the |
| // prototype (inside 'friend') for it comes first in the AST and is not |
| // linked to the definition. |
| EXPECT_EQ(ImportedDef, ToClassDef); |
| } |
| |
| struct LLDBLookupTest : ASTImporterOptionSpecificTestBase { |
| LLDBLookupTest() { |
| Creator = [](ASTContext &ToContext, FileManager &ToFileManager, |
| ASTContext &FromContext, FileManager &FromFileManager, |
| bool MinimalImport, |
| const std::shared_ptr<ASTImporterSharedState> &SharedState) { |
| return new ASTImporter(ToContext, ToFileManager, FromContext, |
| FromFileManager, MinimalImport, |
| // We use the regular lookup. |
| /*SharedState=*/nullptr); |
| }; |
| } |
| }; |
| |
| TEST_P(LLDBLookupTest, ImporterShouldFindInTransparentContext) { |
| TranslationUnitDecl *ToTU = getToTuDecl( |
| R"( |
| extern "C" { |
| class X{}; |
| }; |
| )", |
| Lang_CXX03); |
| auto *ToX = FirstDeclMatcher<CXXRecordDecl>().match( |
| ToTU, cxxRecordDecl(hasName("X"))); |
| |
| // Set up a stub external storage. |
| ToTU->setHasExternalLexicalStorage(true); |
| // Set up DeclContextBits.HasLazyExternalLexicalLookups to true. |
| ToTU->setMustBuildLookupTable(); |
| struct TestExternalASTSource : ExternalASTSource {}; |
| ToTU->getASTContext().setExternalSource(new TestExternalASTSource()); |
| |
| Decl *FromTU = getTuDecl( |
| R"( |
| class X; |
| )", |
| Lang_CXX03); |
| auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match( |
| FromTU, cxxRecordDecl(hasName("X"))); |
| auto *ImportedX = Import(FromX, Lang_CXX03); |
| // The lookup must find the existing class definition in the LinkageSpecDecl. |
| // Then the importer renders the existing and the new decl into one chain. |
| EXPECT_EQ(ImportedX->getCanonicalDecl(), ToX->getCanonicalDecl()); |
| } |
| |
| struct SVEBuiltins : ASTImporterOptionSpecificTestBase {}; |
| |
| TEST_P(SVEBuiltins, ImportTypes) { |
| static const char *const TypeNames[] = { |
| "__SVInt8_t", |
| "__SVInt16_t", |
| "__SVInt32_t", |
| "__SVInt64_t", |
| "__SVUint8_t", |
| "__SVUint16_t", |
| "__SVUint32_t", |
| "__SVUint64_t", |
| "__SVFloat16_t", |
| "__SVBFloat16_t", |
| "__SVFloat32_t", |
| "__SVFloat64_t", |
| "__SVBool_t" |
| }; |
| |
| TranslationUnitDecl *ToTU = getToTuDecl("", Lang_CXX03); |
| TranslationUnitDecl *FromTU = getTuDecl("", Lang_CXX03, "input.cc"); |
| for (auto *TypeName : TypeNames) { |
| auto *ToTypedef = FirstDeclMatcher<TypedefDecl>().match( |
| ToTU, typedefDecl(hasName(TypeName))); |
| QualType ToType = ToTypedef->getUnderlyingType(); |
| |
| auto *FromTypedef = FirstDeclMatcher<TypedefDecl>().match( |
| FromTU, typedefDecl(hasName(TypeName))); |
| QualType FromType = FromTypedef->getUnderlyingType(); |
| |
| QualType ImportedType = ImportType(FromType, FromTypedef, Lang_CXX03); |
| EXPECT_EQ(ImportedType, ToType); |
| } |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ImportOfDefaultImplicitFunctions) { |
| // Test that import of implicit functions works and the functions |
| // are merged into one chain. |
| auto GetDeclToImport = [this](StringRef File) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| struct X { }; |
| // Force generating some implicit operator definitions for X. |
| void f() { X x1, x2; x1 = x2; X *x3 = new X; delete x3; } |
| )", |
| Lang_CXX11, File); |
| auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match( |
| FromTU, cxxRecordDecl(hasName("X"), unless(isImplicit()))); |
| // Destructor is picked as one example of implicit function. |
| return FromD->getDestructor(); |
| }; |
| |
| auto *ToD1 = Import(GetDeclToImport("input1.cc"), Lang_CXX11); |
| ASSERT_TRUE(ToD1); |
| |
| auto *ToD2 = Import(GetDeclToImport("input2.cc"), Lang_CXX11); |
| ASSERT_TRUE(ToD2); |
| |
| EXPECT_EQ(ToD1->getCanonicalDecl(), ToD2->getCanonicalDecl()); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| ImportOfExplicitlyDefaultedOrDeleted) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| struct X { X() = default; X(const X&) = delete; }; |
| )", |
| Lang_CXX11); |
| auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match( |
| FromTU, cxxRecordDecl(hasName("X"))); |
| auto *ImportedX = Import(FromX, Lang_CXX11); |
| auto *Constr1 = FirstDeclMatcher<CXXConstructorDecl>().match( |
| ImportedX, cxxConstructorDecl(hasName("X"), unless(isImplicit()))); |
| auto *Constr2 = LastDeclMatcher<CXXConstructorDecl>().match( |
| ImportedX, cxxConstructorDecl(hasName("X"), unless(isImplicit()))); |
| |
| ASSERT_TRUE(ImportedX); |
| EXPECT_TRUE(Constr1->isDefaulted()); |
| EXPECT_TRUE(Constr1->isExplicitlyDefaulted()); |
| EXPECT_TRUE(Constr2->isDeletedAsWritten()); |
| EXPECT_EQ(ImportedX->isAggregate(), FromX->isAggregate()); |
| } |
| |
| INSTANTIATE_TEST_CASE_P(ParameterizedTests, SVEBuiltins, |
| ::testing::Values(std::vector<std::string>{ |
| "-target", "aarch64-linux-gnu"}), ); |
| |
| INSTANTIATE_TEST_CASE_P(ParameterizedTests, DeclContextTest, |
| ::testing::Values(std::vector<std::string>()), ); |
| |
| INSTANTIATE_TEST_CASE_P(ParameterizedTests, CanonicalRedeclChain, |
| ::testing::Values(std::vector<std::string>()), ); |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, LambdasAreDifferentiated) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| void f() { |
| auto L0 = [](){}; |
| auto L1 = [](){}; |
| } |
| )", |
| Lang_CXX11, "input0.cc"); |
| auto Pattern = lambdaExpr(); |
| CXXRecordDecl *FromL0 = |
| FirstDeclMatcher<LambdaExpr>().match(FromTU, Pattern)->getLambdaClass(); |
| CXXRecordDecl *FromL1 = |
| LastDeclMatcher<LambdaExpr>().match(FromTU, Pattern)->getLambdaClass(); |
| ASSERT_NE(FromL0, FromL1); |
| |
| CXXRecordDecl *ToL0 = Import(FromL0, Lang_CXX11); |
| CXXRecordDecl *ToL1 = Import(FromL1, Lang_CXX11); |
| EXPECT_NE(ToL0, ToL1); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| LambdasInFunctionParamsAreDifferentiated) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| template <typename F0, typename F1> |
| void f(F0 L0 = [](){}, F1 L1 = [](){}) {} |
| )", |
| Lang_CXX11, "input0.cc"); |
| auto Pattern = cxxRecordDecl(isLambda()); |
| CXXRecordDecl *FromL0 = |
| FirstDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern); |
| CXXRecordDecl *FromL1 = |
| LastDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern); |
| ASSERT_NE(FromL0, FromL1); |
| |
| CXXRecordDecl *ToL0 = Import(FromL0, Lang_CXX11); |
| CXXRecordDecl *ToL1 = Import(FromL1, Lang_CXX11); |
| ASSERT_NE(ToL0, ToL1); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| LambdasInFunctionParamsAreDifferentiatedWhenMacroIsUsed) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| #define LAMBDA [](){} |
| template <typename F0, typename F1> |
| void f(F0 L0 = LAMBDA, F1 L1 = LAMBDA) {} |
| )", |
| Lang_CXX11, "input0.cc"); |
| auto Pattern = cxxRecordDecl(isLambda()); |
| CXXRecordDecl *FromL0 = |
| FirstDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern); |
| CXXRecordDecl *FromL1 = |
| LastDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern); |
| ASSERT_NE(FromL0, FromL1); |
| |
| Import(FromL0, Lang_CXX11); |
| Import(FromL1, Lang_CXX11); |
| CXXRecordDecl *ToL0 = Import(FromL0, Lang_CXX11); |
| CXXRecordDecl *ToL1 = Import(FromL1, Lang_CXX11); |
| ASSERT_NE(ToL0, ToL1); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ImportAssignedLambda) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| void f() { |
| auto x = []{} = {}; auto x2 = x; |
| } |
| )", |
| Lang_CXX20, "input0.cc"); |
| auto FromF = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("f"))); |
| // We have only one lambda class. |
| ASSERT_EQ( |
| DeclCounter<CXXRecordDecl>().match(FromTU, cxxRecordDecl(isLambda())), |
| 1u); |
| |
| FunctionDecl *ToF = Import(FromF, Lang_CXX20); |
| EXPECT_TRUE(ToF); |
| TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| // We have only one lambda class after the import. |
| EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, cxxRecordDecl(isLambda())), |
| 1u); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ImportDefaultConstructibleLambdas) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| void f() { |
| auto x = []{} = {}; |
| auto xb = []{} = {}; |
| } |
| )", |
| Lang_CXX20, "input0.cc"); |
| auto FromF = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("f"))); |
| // We have two lambda classes. |
| ASSERT_EQ( |
| DeclCounter<CXXRecordDecl>().match(FromTU, cxxRecordDecl(isLambda())), |
| 2u); |
| |
| FunctionDecl *ToF = Import(FromF, Lang_CXX20); |
| EXPECT_TRUE(ToF); |
| TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); |
| // We have two lambda classes after the import. |
| EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, cxxRecordDecl(isLambda())), |
| 2u); |
| } |
| |
| struct ImportAutoFunctions : ASTImporterOptionSpecificTestBase {}; |
| |
| TEST_P(ImportAutoFunctions, ReturnWithTypedefDeclaredInside) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| auto X = [](long l) { |
| using int_type = long; |
| auto dur = 13; |
| return static_cast<int_type>(dur); |
| }; |
| )", |
| Lang_CXX14, "input0.cc"); |
| CXXMethodDecl *From = |
| FirstDeclMatcher<CXXMethodDecl>().match(FromTU, cxxMethodDecl()); |
| |
| // Explicitly set the return type of the lambda's operator() to the TypeAlias. |
| // Normally the return type would be the built-in 'long' type. However, there |
| // are cases when Clang does not use the canonical type and the TypeAlias is |
| // used. I could not create such an AST from regular source code, it requires |
| // some special state in the preprocessor. I've found such an AST when Clang |
| // parsed libcxx/src/filesystem/directory_iterator.cpp, but could not reduce |
| // that with creduce, because after preprocessing, the AST no longer |
| // contained the TypeAlias as a return type of the lambda. |
| ASTContext &Ctx = From->getASTContext(); |
| TypeAliasDecl *FromTA = |
| FirstDeclMatcher<TypeAliasDecl>().match(FromTU, typeAliasDecl()); |
| QualType TT = Ctx.getTypedefType(FromTA); |
| const FunctionProtoType *FPT = cast<FunctionProtoType>(From->getType()); |
| QualType NewFunType = |
| Ctx.getFunctionType(TT, FPT->getParamTypes(), FPT->getExtProtoInfo()); |
| From->setType(NewFunType); |
| |
| CXXMethodDecl *To = Import(From, Lang_CXX14); |
| EXPECT_TRUE(To); |
| EXPECT_TRUE(isa<TypedefType>(To->getReturnType())); |
| } |
| |
| TEST_P(ImportAutoFunctions, ReturnWithStructDeclaredInside) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| auto foo() { |
| struct X {}; |
| return X(); |
| } |
| )", |
| Lang_CXX14, "input0.cc"); |
| FunctionDecl *From = |
| FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl()); |
| |
| FunctionDecl *To = Import(From, Lang_CXX14); |
| EXPECT_TRUE(To); |
| EXPECT_TRUE(isa<AutoType>(To->getReturnType())); |
| } |
| |
| TEST_P(ImportAutoFunctions, ReturnWithStructDeclaredInside2) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| auto foo() { |
| struct X {}; |
| return X(); |
| } |
| )", |
| Lang_CXX14, "input0.cc"); |
| FunctionDecl *From = |
| FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl()); |
| |
| // This time import the type directly. |
| QualType ToT = ImportType(From->getType(), From, Lang_CXX14); |
| const FunctionProtoType *FPT = cast<FunctionProtoType>(ToT); |
| EXPECT_TRUE(isa<AutoType>(FPT->getReturnType())); |
| } |
| |
| TEST_P(ImportAutoFunctions, ReturnWithTypedefToStructDeclaredInside) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| auto foo() { |
| struct X {}; |
| using Y = X; |
| return Y(); |
| } |
| )", |
| Lang_CXX14, "input0.cc"); |
| FunctionDecl *From = |
| FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl()); |
| |
| FunctionDecl *To = Import(From, Lang_CXX14); |
| EXPECT_TRUE(To); |
| EXPECT_TRUE(isa<AutoType>(To->getReturnType())); |
| } |
| |
| TEST_P(ImportAutoFunctions, ReturnWithStructDeclaredNestedInside) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| auto foo() { |
| struct X { struct Y{}; }; |
| return X::Y(); |
| } |
| )", |
| Lang_CXX14, "input0.cc"); |
| FunctionDecl *From = |
| FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl()); |
| |
| FunctionDecl *To = Import(From, Lang_CXX14); |
| EXPECT_TRUE(To); |
| EXPECT_TRUE(isa<AutoType>(To->getReturnType())); |
| } |
| |
| TEST_P(ImportAutoFunctions, ReturnWithInternalLambdaType) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| auto f() { |
| auto l = []() { |
| struct X {}; |
| return X(); |
| }; |
| return l(); |
| } |
| )", |
| Lang_CXX17, "input0.cc"); |
| FunctionDecl *From = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("f"))); |
| |
| FunctionDecl *To = Import(From, Lang_CXX17); |
| EXPECT_TRUE(To); |
| EXPECT_TRUE(isa<AutoType>(To->getReturnType())); |
| } |
| |
| TEST_P(ImportAutoFunctions, ReturnWithTypeInIf) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| auto f() { |
| if (struct X {} x; true) |
| return X(); |
| else |
| return X(); |
| } |
| )", |
| Lang_CXX17, "input0.cc"); |
| FunctionDecl *From = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("f"))); |
| |
| FunctionDecl *To = Import(From, Lang_CXX17); |
| EXPECT_TRUE(To); |
| EXPECT_TRUE(isa<AutoType>(To->getReturnType())); |
| } |
| |
| TEST_P(ImportAutoFunctions, ReturnWithTypeInFor) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| auto f() { |
| for (struct X {} x;;) |
| return X(); |
| } |
| )", |
| Lang_CXX17, "input0.cc"); |
| FunctionDecl *From = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("f"))); |
| |
| FunctionDecl *To = Import(From, Lang_CXX17); |
| EXPECT_TRUE(To); |
| EXPECT_TRUE(isa<AutoType>(To->getReturnType())); |
| } |
| |
| TEST_P(ImportAutoFunctions, ReturnWithTypeInSwitch) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| auto f() { |
| switch (struct X {} x; 10) { |
| case 10: |
| return X(); |
| } |
| } |
| )", |
| Lang_CXX17, "input0.cc"); |
| FunctionDecl *From = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("f"))); |
| |
| FunctionDecl *To = Import(From, Lang_CXX17); |
| EXPECT_TRUE(To); |
| EXPECT_TRUE(isa<AutoType>(To->getReturnType())); |
| } |
| |
| struct ImportSourceLocations : ASTImporterOptionSpecificTestBase {}; |
| |
| TEST_P(ImportSourceLocations, PreserveFileIDTreeStructure) { |
| // Tests that the FileID tree structure (with the links being the include |
| // chains) is preserved while importing other files (which need to be |
| // added to this structure with fake include locations. |
| |
| SourceLocation Location1; |
| { |
| auto Pattern = varDecl(hasName("X")); |
| Decl *FromTU = getTuDecl("int X;", Lang_C99, "input0.c"); |
| auto *FromD = FirstDeclMatcher<VarDecl>().match(FromTU, Pattern); |
| |
| Location1 = Import(FromD, Lang_C99)->getLocation(); |
| } |
| SourceLocation Location2; |
| { |
| auto Pattern = varDecl(hasName("Y")); |
| Decl *FromTU = getTuDecl("int Y;", Lang_C99, "input1.c"); |
| auto *FromD = FirstDeclMatcher<VarDecl>().match(FromTU, Pattern); |
| |
| Location2 = Import(FromD, Lang_C99)->getLocation(); |
| } |
| |
| SourceManager &ToSM = ToAST->getSourceManager(); |
| FileID FileID1 = ToSM.getFileID(Location1); |
| FileID FileID2 = ToSM.getFileID(Location2); |
| |
| // Check that the imported files look like as if they were included from the |
| // start of the main file. |
| SourceLocation FileStart = ToSM.getLocForStartOfFile(ToSM.getMainFileID()); |
| EXPECT_NE(FileID1, ToSM.getMainFileID()); |
| EXPECT_NE(FileID2, ToSM.getMainFileID()); |
| EXPECT_EQ(ToSM.getIncludeLoc(FileID1), FileStart); |
| EXPECT_EQ(ToSM.getIncludeLoc(FileID2), FileStart); |
| |
| // Let the SourceManager check the order of the locations. The order should |
| // be the order in which the declarations are imported. |
| EXPECT_TRUE(ToSM.isBeforeInTranslationUnit(Location1, Location2)); |
| EXPECT_FALSE(ToSM.isBeforeInTranslationUnit(Location2, Location1)); |
| } |
| |
| TEST_P(ImportSourceLocations, NormalFileBuffer) { |
| // Test importing normal file buffers. |
| |
| std::string Path = "input0.c"; |
| std::string Source = "int X;"; |
| TranslationUnitDecl *FromTU = getTuDecl(Source, Lang_C99, Path); |
| |
| SourceLocation ImportedLoc; |
| { |
| // Import the VarDecl to trigger the importing of the FileID. |
| auto Pattern = varDecl(hasName("X")); |
| VarDecl *FromD = FirstDeclMatcher<VarDecl>().match(FromTU, Pattern); |
| ImportedLoc = Import(FromD, Lang_C99)->getLocation(); |
| } |
| |
| // Make sure the imported buffer has the original contents. |
| SourceManager &ToSM = ToAST->getSourceManager(); |
| FileID ImportedID = ToSM.getFileID(ImportedLoc); |
| EXPECT_EQ(Source, |
| ToSM.getBufferOrFake(ImportedID, SourceLocation()).getBuffer()); |
| } |
| |
| TEST_P(ImportSourceLocations, OverwrittenFileBuffer) { |
| // Test importing overwritten file buffers. |
| |
| std::string Path = "input0.c"; |
| TranslationUnitDecl *FromTU = getTuDecl("int X;", Lang_C99, Path); |
| |
| // Overwrite the file buffer for our input file with new content. |
| const std::string Contents = "overwritten contents"; |
| SourceLocation ImportedLoc; |
| { |
| SourceManager &FromSM = FromTU->getASTContext().getSourceManager(); |
| clang::FileManager &FM = FromSM.getFileManager(); |
| const clang::FileEntry &FE = |
| *FM.getVirtualFile(Path, static_cast<off_t>(Contents.size()), 0); |
| |
| llvm::SmallVector<char, 64> Buffer; |
| Buffer.append(Contents.begin(), Contents.end()); |
| auto FileContents = |
| std::make_unique<llvm::SmallVectorMemoryBuffer>(std::move(Buffer), Path); |
| FromSM.overrideFileContents(&FE, std::move(FileContents)); |
| |
| // Import the VarDecl to trigger the importing of the FileID. |
| auto Pattern = varDecl(hasName("X")); |
| VarDecl *FromD = FirstDeclMatcher<VarDecl>().match(FromTU, Pattern); |
| ImportedLoc = Import(FromD, Lang_C99)->getLocation(); |
| } |
| |
| // Make sure the imported buffer has the overwritten contents. |
| SourceManager &ToSM = ToAST->getSourceManager(); |
| FileID ImportedID = ToSM.getFileID(ImportedLoc); |
| EXPECT_EQ(Contents, |
| ToSM.getBufferOrFake(ImportedID, SourceLocation()).getBuffer()); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ImportExprOfAlignmentAttr) { |
| // Test if import of these packed and aligned attributes does not trigger an |
| // error situation where source location from 'From' context is referenced in |
| // 'To' context through evaluation of the alignof attribute. |
| // This happens if the 'alignof(A)' expression is not imported correctly. |
| Decl *FromTU = getTuDecl( |
| R"( |
| struct __attribute__((packed)) A { int __attribute__((aligned(8))) X; }; |
| struct alignas(alignof(A)) S {}; |
| )", |
| Lang_CXX11, "input.cc"); |
| auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match( |
| FromTU, cxxRecordDecl(hasName("S"), unless(isImplicit()))); |
| ASSERT_TRUE(FromD); |
| |
| auto *ToD = Import(FromD, Lang_CXX11); |
| ASSERT_TRUE(ToD); |
| |
| auto *FromAttr = FromD->getAttr<AlignedAttr>(); |
| auto *ToAttr = ToD->getAttr<AlignedAttr>(); |
| EXPECT_EQ(FromAttr->isInherited(), ToAttr->isInherited()); |
| EXPECT_EQ(FromAttr->isPackExpansion(), ToAttr->isPackExpansion()); |
| EXPECT_EQ(FromAttr->isImplicit(), ToAttr->isImplicit()); |
| EXPECT_EQ(FromAttr->getSyntax(), ToAttr->getSyntax()); |
| EXPECT_EQ(FromAttr->getSemanticSpelling(), ToAttr->getSemanticSpelling()); |
| EXPECT_TRUE(ToAttr->getAlignmentExpr()); |
| |
| auto *ToA = FirstDeclMatcher<CXXRecordDecl>().match( |
| ToD->getTranslationUnitDecl(), |
| cxxRecordDecl(hasName("A"), unless(isImplicit()))); |
| // Ensure that 'struct A' was imported (through reference from attribute of |
| // 'S'). |
| EXPECT_TRUE(ToA); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ImportFormatAttr) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| int foo(const char * fmt, ...) |
| __attribute__ ((__format__ (__scanf__, 1, 2))); |
| )", |
| Lang_CXX03, "input.cc"); |
| auto *FromD = FirstDeclMatcher<FunctionDecl>().match( |
| FromTU, functionDecl(hasName("foo"))); |
| ASSERT_TRUE(FromD); |
| |
| auto *ToD = Import(FromD, Lang_CXX03); |
| ASSERT_TRUE(ToD); |
| ToD->dump(); // Should not crash! |
| |
| auto *FromAttr = FromD->getAttr<FormatAttr>(); |
| auto *ToAttr = ToD->getAttr<FormatAttr>(); |
| EXPECT_EQ(FromAttr->isInherited(), ToAttr->isInherited()); |
| EXPECT_EQ(FromAttr->isPackExpansion(), ToAttr->isPackExpansion()); |
| EXPECT_EQ(FromAttr->isImplicit(), ToAttr->isImplicit()); |
| EXPECT_EQ(FromAttr->getSyntax(), ToAttr->getSyntax()); |
| EXPECT_EQ(FromAttr->getAttributeSpellingListIndex(), |
| ToAttr->getAttributeSpellingListIndex()); |
| EXPECT_EQ(FromAttr->getType()->getName(), ToAttr->getType()->getName()); |
| } |
| template <typename T> |
| auto ExtendWithOptions(const T &Values, const std::vector<std::string> &Args) { |
| auto Copy = Values; |
| for (std::vector<std::string> &ArgV : Copy) { |
| for (const std::string &Arg : Args) { |
| ArgV.push_back(Arg); |
| } |
| } |
| return ::testing::ValuesIn(Copy); |
| } |
| |
| struct ImportWithExternalSource : ASTImporterOptionSpecificTestBase { |
| ImportWithExternalSource() { |
| Creator = [](ASTContext &ToContext, FileManager &ToFileManager, |
| ASTContext &FromContext, FileManager &FromFileManager, |
| bool MinimalImport, |
| const std::shared_ptr<ASTImporterSharedState> &SharedState) { |
| return new ASTImporter(ToContext, ToFileManager, FromContext, |
| FromFileManager, MinimalImport, |
| // We use the regular lookup. |
| /*SharedState=*/nullptr); |
| }; |
| } |
| }; |
| |
| /// An ExternalASTSource that keeps track of the tags is completed. |
| struct SourceWithCompletedTagList : clang::ExternalASTSource { |
| std::vector<clang::TagDecl *> &CompletedTags; |
| SourceWithCompletedTagList(std::vector<clang::TagDecl *> &CompletedTags) |
| : CompletedTags(CompletedTags) {} |
| void CompleteType(TagDecl *Tag) override { |
| auto *Record = cast<CXXRecordDecl>(Tag); |
| Record->startDefinition(); |
| Record->completeDefinition(); |
| CompletedTags.push_back(Tag); |
| } |
| using clang::ExternalASTSource::CompleteType; |
| }; |
| |
| TEST_P(ImportWithExternalSource, CompleteRecordBeforeImporting) { |
| // Create an empty TU. |
| TranslationUnitDecl *FromTU = getTuDecl("", Lang_CXX03, "input.cpp"); |
| |
| // Create and add the test ExternalASTSource. |
| std::vector<clang::TagDecl *> CompletedTags; |
| IntrusiveRefCntPtr<ExternalASTSource> source = |
| new SourceWithCompletedTagList(CompletedTags); |
| clang::ASTContext &Context = FromTU->getASTContext(); |
| Context.setExternalSource(std::move(source)); |
| |
| // Create a dummy class by hand with external lexical storage. |
| IdentifierInfo &Ident = Context.Idents.get("test_class"); |
| auto *Record = CXXRecordDecl::Create( |
| Context, TTK_Class, FromTU, SourceLocation(), SourceLocation(), &Ident); |
| Record->setHasExternalLexicalStorage(); |
| FromTU->addDecl(Record); |
| |
| // Do a minimal import of the created class. |
| EXPECT_EQ(0U, CompletedTags.size()); |
| Import(Record, Lang_CXX03); |
| EXPECT_EQ(0U, CompletedTags.size()); |
| |
| // Import the definition of the created class. |
| llvm::Error Err = findFromTU(Record)->Importer->ImportDefinition(Record); |
| EXPECT_FALSE((bool)Err); |
| consumeError(std::move(Err)); |
| |
| // Make sure the class was completed once. |
| EXPECT_EQ(1U, CompletedTags.size()); |
| EXPECT_EQ(Record, CompletedTags.front()); |
| } |
| |
| TEST_P(ImportFunctions, CTADImplicit) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| template <typename T> struct A { |
| A(T); |
| }; |
| A a{(int)0}; |
| )", |
| Lang_CXX17, "input.cc"); |
| auto *FromD = FirstDeclMatcher<CXXDeductionGuideDecl>().match( |
| FromTU, |
| cxxDeductionGuideDecl(hasParameter(0, hasType(asString("A<T>"))))); |
| auto *ToD = Import(FromD, Lang_CXX17); |
| ASSERT_TRUE(ToD); |
| EXPECT_TRUE(ToD->isCopyDeductionCandidate()); |
| // Check that the deduced class template is also imported. |
| EXPECT_TRUE(findFromTU(FromD)->Importer->GetAlreadyImportedOrNull( |
| FromD->getDeducedTemplate())); |
| } |
| |
| TEST_P(ImportFunctions, CTADUserDefinedExplicit) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| template <typename T> struct A { |
| A(T); |
| }; |
| template <typename T> explicit A(T) -> A<float>; |
| A a{(int)0}; // calls A<float>::A(float) |
| )", |
| Lang_CXX17, "input.cc"); |
| auto *FromD = FirstDeclMatcher<CXXDeductionGuideDecl>().match( |
| FromTU, cxxDeductionGuideDecl(unless(isImplicit()))); |
| // Not-implicit: i.e. not compiler-generated, user defined. |
| ASSERT_FALSE(FromD->isImplicit()); |
| ASSERT_TRUE(FromD->isExplicit()); // Has the explicit keyword. |
| auto *ToD = Import(FromD, Lang_CXX17); |
| ASSERT_TRUE(ToD); |
| EXPECT_FALSE(FromD->isImplicit()); |
| EXPECT_TRUE(ToD->isExplicit()); |
| } |
| |
| TEST_P(ImportFunctions, CTADWithLocalTypedef) { |
| Decl *TU = getTuDecl( |
| R"( |
| template <typename T> struct A { |
| typedef T U; |
| A(U); |
| }; |
| A a{(int)0}; |
| )", |
| Lang_CXX17, "input.cc"); |
| auto *FromD = FirstDeclMatcher<CXXDeductionGuideDecl>().match( |
| TU, cxxDeductionGuideDecl()); |
| auto *ToD = Import(FromD, Lang_CXX17); |
| ASSERT_TRUE(ToD); |
| } |
| |
| // FIXME Move these tests out of ASTImporterTest. For that we need to factor |
| // out the ASTImporter specific pars from ASTImporterOptionSpecificTestBase |
| // into a new test Fixture. Then we should lift up this Fixture to its own |
| // implementation file and only then could we reuse the Fixture in other AST |
| // unitttests. |
| struct CTAD : ASTImporterOptionSpecificTestBase {}; |
| |
| TEST_P(CTAD, DeductionGuideShouldReferToANonLocalTypedef) { |
| Decl *TU = getTuDecl( |
| R"( |
| typedef int U; |
| template <typename T> struct A { |
| A(U, T); |
| }; |
| A a{(int)0, (int)0}; |
| )", |
| Lang_CXX17, "input.cc"); |
| auto *Guide = FirstDeclMatcher<CXXDeductionGuideDecl>().match( |
| TU, cxxDeductionGuideDecl()); |
| auto *Typedef = FirstDeclMatcher<TypedefNameDecl>().match( |
| TU, typedefNameDecl(hasName("U"))); |
| ParmVarDecl *Param = Guide->getParamDecl(0); |
| // The type of the first param (which is a typedef) should match the typedef |
| // in the global scope. |
| EXPECT_EQ(Param->getType()->getAs<TypedefType>()->getDecl(), Typedef); |
| } |
| |
| TEST_P(CTAD, DeductionGuideShouldReferToANonLocalTypedefInParamPtr) { |
| Decl *TU = getTuDecl( |
| R"( |
| typedef int U; |
| template <typename T> struct A { |
| A(U*, T); |
| }; |
| A a{(int*)0, (int)0}; |
| )", |
| Lang_CXX17, "input.cc"); |
| auto *Guide = FirstDeclMatcher<CXXDeductionGuideDecl>().match( |
| TU, cxxDeductionGuideDecl()); |
| auto *Typedef = FirstDeclMatcher<TypedefNameDecl>().match( |
| TU, typedefNameDecl(hasName("U"))); |
| ParmVarDecl *Param = Guide->getParamDecl(0); |
| EXPECT_EQ(Param->getType() |
| ->getAs<PointerType>() |
| ->getPointeeType() |
| ->getAs<TypedefType>() |
| ->getDecl(), |
| Typedef); |
| } |
| |
| TEST_P(CTAD, DeductionGuideShouldCopyALocalTypedef) { |
| Decl *TU = getTuDecl( |
| R"( |
| template <typename T> struct A { |
| typedef T U; |
| A(U, T); |
| }; |
| A a{(int)0, (int)0}; |
| )", |
| Lang_CXX17, "input.cc"); |
| auto *Guide = FirstDeclMatcher<CXXDeductionGuideDecl>().match( |
| TU, cxxDeductionGuideDecl()); |
| auto *Typedef = FirstDeclMatcher<TypedefNameDecl>().match( |
| TU, typedefNameDecl(hasName("U"))); |
| ParmVarDecl *Param = Guide->getParamDecl(0); |
| EXPECT_NE(Param->getType()->getAs<TypedefType>()->getDecl(), Typedef); |
| } |
| |
| INSTANTIATE_TEST_CASE_P(ParameterizedTests, CTAD, |
| DefaultTestValuesForRunOptions, ); |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, TypedefWithAttribute) { |
| Decl *TU = getTuDecl( |
| R"( |
| namespace N { |
| typedef int X __attribute__((annotate("A"))); |
| } |
| )", |
| Lang_CXX17, "input.cc"); |
| auto *FromD = |
| FirstDeclMatcher<TypedefDecl>().match(TU, typedefDecl(hasName("X"))); |
| auto *ToD = Import(FromD, Lang_CXX17); |
| ASSERT_TRUE(ToD); |
| ASSERT_EQ(ToD->getAttrs().size(), 1U); |
| auto *ToAttr = dyn_cast<AnnotateAttr>(ToD->getAttrs()[0]); |
| ASSERT_TRUE(ToAttr); |
| EXPECT_EQ(ToAttr->getAnnotation(), "A"); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, |
| ImportOfTemplatedDeclWhenPreviousDeclHasNoDescribedTemplateSet) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| |
| namespace std { |
| template<typename T> |
| class basic_stringbuf; |
| } |
| namespace std { |
| class char_traits; |
| template<typename T = char_traits> |
| class basic_stringbuf; |
| } |
| namespace std { |
| template<typename T> |
| class basic_stringbuf {}; |
| } |
| |
| )", |
| Lang_CXX11); |
| |
| auto *From1 = FirstDeclMatcher<ClassTemplateDecl>().match( |
| FromTU, |
| classTemplateDecl(hasName("basic_stringbuf"), unless(isImplicit()))); |
| auto *To1 = cast_or_null<ClassTemplateDecl>(Import(From1, Lang_CXX11)); |
| EXPECT_TRUE(To1); |
| |
| auto *From2 = LastDeclMatcher<ClassTemplateDecl>().match( |
| FromTU, |
| classTemplateDecl(hasName("basic_stringbuf"), unless(isImplicit()))); |
| auto *To2 = cast_or_null<ClassTemplateDecl>(Import(From2, Lang_CXX11)); |
| EXPECT_TRUE(To2); |
| } |
| |
| TEST_P(ASTImporterOptionSpecificTestBase, ImportOfCapturedVLAType) { |
| Decl *FromTU = getTuDecl( |
| R"( |
| void declToImport(int N) { |
| int VLA[N]; |
| [&VLA] {}; // FieldDecl inside the lambda. |
| } |
| )", |
| Lang_CXX14); |
| auto *FromFD = FirstDeclMatcher<FieldDecl>().match(FromTU, fieldDecl()); |
| ASSERT_TRUE(FromFD); |
| ASSERT_TRUE(FromFD->hasCapturedVLAType()); |
| |
| auto *ToFD = Import(FromFD, Lang_CXX14); |
| EXPECT_TRUE(ToFD); |
| EXPECT_TRUE(ToFD->hasCapturedVLAType()); |
| EXPECT_NE(FromFD->getCapturedVLAType(), ToFD->getCapturedVLAType()); |
| } |
| |
| INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterLookupTableTest, |
| DefaultTestValuesForRunOptions, ); |
| |
| INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportPath, |
| ::testing::Values(std::vector<std::string>()), ); |
| |
| INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportExpr, |
| DefaultTestValuesForRunOptions, ); |
| |
| INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFixedPointExpr, |
| ExtendWithOptions(DefaultTestArrayForRunOptions, |
| std::vector<std::string>{ |
| "-ffixed-point"}), ); |
| |
| INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportType, |
| DefaultTestValuesForRunOptions, ); |
| |
| INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportDecl, |
| DefaultTestValuesForRunOptions, ); |
| |
| INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterOptionSpecificTestBase, |
| DefaultTestValuesForRunOptions, ); |
| |
| INSTANTIATE_TEST_CASE_P(ParameterizedTests, ErrorHandlingTest, |
| DefaultTestValuesForRunOptions, ); |
| |
| INSTANTIATE_TEST_CASE_P(ParameterizedTests, RedirectingImporterTest, |
| DefaultTestValuesForRunOptions, ); |
| |
| INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctions, |
| DefaultTestValuesForRunOptions, ); |
| |
| INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportAutoFunctions, |
| DefaultTestValuesForRunOptions, ); |
| |
| INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctionTemplates, |
| DefaultTestValuesForRunOptions, ); |
| |
| INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendFunctionTemplates, |
| DefaultTestValuesForRunOptions, ); |
| |
| INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClasses, |
| DefaultTestValuesForRunOptions, ); |
| |
| INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendFunctions, |
| DefaultTestValuesForRunOptions, ); |
| |
| INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendClasses, |
| DefaultTestValuesForRunOptions, ); |
| |
| INSTANTIATE_TEST_CASE_P(ParameterizedTests, |
| ImportFunctionTemplateSpecializations, |
| DefaultTestValuesForRunOptions, ); |
| |
| INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportImplicitMethods, |
| DefaultTestValuesForRunOptions, ); |
| |
| INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportVariables, |
| DefaultTestValuesForRunOptions, ); |
| |
| INSTANTIATE_TEST_CASE_P(ParameterizedTests, LLDBLookupTest, |
| DefaultTestValuesForRunOptions, ); |
| |
| INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportSourceLocations, |
| DefaultTestValuesForRunOptions, ); |
| |
| INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportWithExternalSource, |
| DefaultTestValuesForRunOptions, ); |
| |
| } // end namespace ast_matchers |
| } // end namespace clang |