//===- 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(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().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().match(FromTU, Pattern); auto *D2 = LastDeclMatcher().match(FromTU, Pattern); FunctionDecl *D1 = D2->getPreviousDecl(); auto Redecls = getCanonicalForwardRedeclChain(D0); ASSERT_EQ(Redecls.size(), 3u); EXPECT_EQ(D0, Redecls[0]); EXPECT_EQ(D1, Redecls[1]); EXPECT_EQ(D2, Redecls[2]); } TEST_P(CanonicalRedeclChain, ShouldBeSameForAllDeclInTheChain) { Decl *FromTU = getTuDecl("void f(); void f(); void f();", Lang_CXX03); auto Pattern = functionDecl(hasName("f")); auto *D0 = FirstDeclMatcher().match(FromTU, Pattern); auto *D2 = LastDeclMatcher().match(FromTU, Pattern); FunctionDecl *D1 = D2->getPreviousDecl(); auto RedeclsD0 = getCanonicalForwardRedeclChain(D0); auto RedeclsD1 = getCanonicalForwardRedeclChain(D1); auto RedeclsD2 = getCanonicalForwardRedeclChain(D2); EXPECT_THAT(RedeclsD0, ::testing::ContainerEq(RedeclsD1)); EXPECT_THAT(RedeclsD1, ::testing::ContainerEq(RedeclsD2)); } namespace { struct RedirectingImporter : public ASTImporter { using ASTImporter::ASTImporter; protected: llvm::Expected ImportImpl(Decl *FromD) override { auto *ND = dyn_cast(FromD); if (!ND || ND->getName() != "shouldNotBeImported") return ASTImporter::ImportImpl(FromD); for (Decl *D : getToContext().getTranslationUnitDecl()->decls()) { if (auto *ND = dyn_cast(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 &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(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().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().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().match(FromTU, Pattern); D2 = LastDeclMatcher().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 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 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 Res; int i = 0; for (Decl *Di : path.getCycleAtBack()) { Res[i++] = Di; } ASSERT_EQ(i, 4); EXPECT_EQ(Res[0], D0); EXPECT_EQ(Res[1], D2); EXPECT_EQ(Res[2], D1); EXPECT_EQ(Res[3], D0); path.pop(); path.pop(); path.pop(); EXPECT_TRUE(path.hasCycleAtBack()); i = 0; for (Decl *Di : path.getCycleAtBack()) { Res[i++] = Di; } ASSERT_EQ(i, 3); EXPECT_EQ(Res[0], D0); EXPECT_EQ(Res[1], D1); EXPECT_EQ(Res[2], D0); path.pop(); EXPECT_FALSE(path.hasCycleAtBack()); } TEST_P(ImportExpr, ImportStringLiteral) { MatchVerifier 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 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 Verifier; testImport("void declToImport() { (void)__null; }", Lang_CXX03, "", Lang_CXX03, Verifier, functionDecl(hasDescendant(gnuNullExpr(hasType(isInteger()))))); } TEST_P(ImportExpr, ImportGenericSelectionExpr) { MatchVerifier 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 Verifier; testImport( "void declToImport() { (void)nullptr; }", Lang_CXX11, "", Lang_CXX11, Verifier, functionDecl(hasDescendant(cxxNullPtrLiteralExpr()))); } TEST_P(ImportExpr, ImportFloatinglLiteralExpr) { MatchVerifier Verifier; testImport("void declToImport() { (void)1.0; }", Lang_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 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 Verifier; testImport( "void declToImport() { (void)1.0i; }", Lang_CXX14, "", Lang_CXX14, Verifier, functionDecl(hasDescendant(imaginaryLiteral()))); } TEST_P(ImportExpr, ImportCompoundLiteralExpr) { MatchVerifier 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 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 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 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, InnerMatcher) { const NamedDecl *Template = Node.getTemplatedDecl(); return Template && InnerMatcher.matches(*Template, Finder, Builder); } TEST_P(ImportExpr, ImportParenListExpr) { MatchVerifier Verifier; testImport( "template class dummy { void f() { dummy X(*this); } };" "typedef dummy declToImport;" "template class dummy;", Lang_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 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 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 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 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 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 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 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 vaArgExpr; TEST_P(ImportExpr, ImportVAArgExpr) { MatchVerifier 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 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 Verifier; testImport( "void declToImport() { typedef _Atomic(int) a_int; }", Lang_CXX11, "", Lang_CXX11, Verifier, functionDecl(hasDescendant(typedefDecl(has(atomicType()))))); } TEST_P(ImportDecl, ImportFunctionTemplateDecl) { MatchVerifier Verifier; testImport("template void declToImport() { };", Lang_CXX03, "", Lang_CXX03, Verifier, functionTemplateDecl()); } TEST_P(ImportExpr, ImportCXXDependentScopeMemberExpr) { MatchVerifier Verifier; testImport("template struct C { T t; };" "template void declToImport() {" " C d;" " (void)d.t;" "}" "void instantiate() { declToImport(); }", Lang_CXX03, "", Lang_CXX03, Verifier, functionTemplateDecl(hasDescendant( cStyleCastExpr(has(cxxDependentScopeMemberExpr()))))); testImport("template struct C { T t; };" "template void declToImport() {" " C d;" " (void)(&d)->t;" "}" "void instantiate() { declToImport(); }", Lang_CXX03, "", Lang_CXX03, Verifier, functionTemplateDecl(hasDescendant( cStyleCastExpr(has(cxxDependentScopeMemberExpr()))))); } TEST_P(ImportType, ImportTypeAliasTemplate) { MatchVerifier Verifier; testImport( "template " "struct dummy { static const int i = K; };" "template using dummy2 = dummy;" "int declToImport() { return dummy2<3>::i; }", Lang_CXX11, "", Lang_CXX11, Verifier, traverse(TK_AsIs, functionDecl(hasDescendant(implicitCastExpr(has(declRefExpr()))), unless(hasAncestor( translationUnitDecl(has(typeAliasDecl()))))))); } const internal::VariadicDynCastAllOfMatcher varTemplateSpecializationDecl; TEST_P(ImportDecl, ImportVarTemplate) { MatchVerifier Verifier; testImport( "template " "T pi = T(3.1415926535897932385L);" "void declToImport() { (void)pi; }", Lang_CXX14, "", Lang_CXX14, Verifier, functionDecl( hasDescendant(declRefExpr(to(varTemplateSpecializationDecl()))), unless(hasAncestor(translationUnitDecl(has(varDecl( hasName("pi"), unless(varTemplateSpecializationDecl())))))))); } TEST_P(ImportType, ImportPackExpansion) { MatchVerifier Verifier; testImport("template " "struct dummy {" " dummy(Args... args) {}" " static const int i = 4;" "};" "int declToImport() { return dummy::i; }", Lang_CXX11, "", Lang_CXX11, Verifier, traverse(TK_AsIs, functionDecl(hasDescendant(returnStmt(has( implicitCastExpr(has(declRefExpr())))))))); } const internal::VariadicDynCastAllOfMatcher dependentTemplateSpecializationType; TEST_P(ImportType, ImportDependentTemplateSpecialization) { MatchVerifier Verifier; testImport("template" "struct A;" "template" "struct declToImport {" " typename A::template B a;" "};", Lang_CXX03, "", Lang_CXX03, Verifier, classTemplateDecl(has(cxxRecordDecl(has( fieldDecl(hasType(dependentTemplateSpecializationType()))))))); } const internal::VariadicDynCastAllOfMatcher sizeOfPackExpr; TEST_P(ImportExpr, ImportSizeOfPackExpr) { MatchVerifier Verifier; testImport( "template " "void declToImport() {" " const int i = sizeof...(Ts);" "};" "void g() { declToImport(); }", Lang_CXX11, "", Lang_CXX11, Verifier, functionTemplateDecl(hasDescendant(sizeOfPackExpr()))); testImport( "template " "using X = int[sizeof...(Ts)];" "template " "struct Y {" " X f;" "};" "Y declToImport;", Lang_CXX11, "", Lang_CXX11, Verifier, varDecl(hasType(classTemplateSpecializationDecl(has(fieldDecl(hasType( hasUnqualifiedDesugaredType(constantArrayType(hasSize(7)))))))))); } const internal::VariadicDynCastAllOfMatcher 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 Verifier; testImport("template " "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 typeTraitExpr; TEST_P(ImportExpr, ImportTypeTraitExpr) { MatchVerifier 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 cxxTypeidExpr; TEST_P(ImportExpr, ImportCXXTypeidExpr) { MatchVerifier Verifier; testImport( "namespace std { class type_info {}; }" "void declToImport() {" " int x;" " auto a = typeid(int); auto b = typeid(x);" "}", Lang_CXX11, "", Lang_CXX11, Verifier, traverse( TK_AsIs, functionDecl( hasDescendant(varDecl(hasName("a"), hasInitializer(hasDescendant( cxxTypeidExpr())))), hasDescendant(varDecl(hasName("b"), hasInitializer(hasDescendant( cxxTypeidExpr()))))))); } TEST_P(ImportExpr, ImportTypeTraitExprValDep) { MatchVerifier Verifier; testImport( "template struct declToImport {" " void m() { (void)__is_pod(T); }" "};" "void f() { declToImport().m(); }", Lang_CXX11, "", Lang_CXX11, Verifier, classTemplateDecl(has(cxxRecordDecl(has( functionDecl(hasDescendant( typeTraitExpr(hasType(booleanType()))))))))); } TEST_P(ImportDecl, ImportRecordDeclInFunc) { MatchVerifier Verifier; testImport("int declToImport() { " " struct data_t {int a;int b;};" " struct data_t d;" " return 0;" "}", Lang_C99, "", Lang_C99, Verifier, functionDecl(hasBody(compoundStmt( has(declStmt(hasSingleDecl(varDecl(hasName("d"))))))))); } TEST_P(ASTImporterOptionSpecificTestBase, ImportRecordTypeInFunc) { Decl *FromTU = getTuDecl("int declToImport() { " " struct data_t {int a;int b;};" " struct data_t d;" " return 0;" "}", Lang_C99, "input.c"); auto *FromVar = FirstDeclMatcher().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().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().match( FromTU, functionDecl(hasName("declToImport"))); ASSERT_TRUE(From); auto *To = Import(From, Lang_C99); ASSERT_TRUE(To); EXPECT_TRUE(MatchVerifier().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().match( FromTU, functionDecl(hasName("declToImport"))); ASSERT_TRUE(From); auto *To = Import(From, Lang_C99); EXPECT_EQ(To, nullptr); } const internal::VariadicDynCastAllOfMatcher cxxPseudoDestructorExpr; TEST_P(ImportExpr, ImportCXXPseudoDestructorExpr) { MatchVerifier Verifier; testImport( "typedef int T;" "void declToImport(int *p) {" " T t;" " p->T::~T();" "}", Lang_CXX03, "", Lang_CXX03, Verifier, functionDecl(hasDescendant(callExpr(has(cxxPseudoDestructorExpr()))))); } TEST_P(ImportDecl, ImportUsingDecl) { MatchVerifier 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 usingShadowDecl; TEST_P(ImportDecl, ImportUsingShadowDecl) { MatchVerifier Verifier; testImport("namespace foo { int bar; }" "namespace declToImport { using foo::bar; }", Lang_CXX03, "", Lang_CXX03, Verifier, namespaceDecl(has(usingShadowDecl()))); } TEST_P(ImportExpr, ImportUnresolvedLookupExpr) { MatchVerifier Verifier; testImport("template int foo();" "template void declToImport() {" " (void)::foo;" " (void)::template foo;" "}" "void instantiate() { declToImport(); }", Lang_CXX03, "", Lang_CXX03, Verifier, functionTemplateDecl(hasDescendant(unresolvedLookupExpr()))); } TEST_P(ImportExpr, ImportCXXUnresolvedConstructExpr) { MatchVerifier Verifier; testImport("template struct C { T t; };" "template void declToImport() {" " C d;" " d.t = T();" "}" "void instantiate() { declToImport(); }", Lang_CXX03, "", Lang_CXX03, Verifier, functionTemplateDecl(hasDescendant( binaryOperator(has(cxxUnresolvedConstructExpr()))))); testImport("template struct C { T t; };" "template void declToImport() {" " C d;" " (&d)->t = T();" "}" "void instantiate() { declToImport(); }", 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 Verifier; testImport("template void declToImport() { T a = 1; }" "void instantiate() { declToImport(); }", Lang_CXX03, "", Lang_CXX03, Verifier, functionTemplateDecl(hasAncestor(translationUnitDecl( unless(has(functionDecl(hasName("declToImport")))))))); testImport("template struct declToImport { T t; };" "void instantiate() { declToImport(); }", Lang_CXX03, "", Lang_CXX03, Verifier, classTemplateDecl(hasAncestor(translationUnitDecl( unless(has(cxxRecordDecl(hasName("declToImport")))))))); } TEST_P(ImportDecl, ImportClassTemplatePartialSpecialization) { MatchVerifier Verifier; auto Code = R"s( struct declToImport { template struct X; template struct X {}; }; )s"; testImport(Code, Lang_CXX03, "", Lang_CXX03, Verifier, recordDecl(has(classTemplateDecl()), has(classTemplateSpecializationDecl()))); } TEST_P(ImportExpr, CXXOperatorCallExpr) { MatchVerifier Verifier; testImport( "class declToImport {" " void f() { *this = declToImport(); }" "};", Lang_CXX03, "", Lang_CXX03, Verifier, cxxRecordDecl(has(cxxMethodDecl(hasDescendant(cxxOperatorCallExpr()))))); } TEST_P(ImportExpr, DependentSizedArrayType) { MatchVerifier Verifier; testImport("template 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 struct X {};", Lang_CXX03); auto From = FirstDeclMatcher().match( FromTU, templateTypeParmDecl(hasName("T"))); TemplateTypeParmDecl *To = Import(From, Lang_CXX03); ASSERT_FALSE(To->hasDefaultArgument()); } TEST_P(ASTImporterOptionSpecificTestBase, TemplateTypeParmDeclDefaultArg) { Decl *FromTU = getTuDecl("template struct X {};", Lang_CXX03); auto From = FirstDeclMatcher().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().match( FromTU, functionDecl(hasName("f"))); ASSERT_TRUE(From); ASSERT_TRUE( cast(cast(From->getBody())->body_front()) ->getSubExpr() ->getBeginLoc() .isValid()); FunctionDecl *To = Import(From, Lang_CXX03); ASSERT_TRUE(To); ASSERT_TRUE( cast(cast(To->getBody())->body_front()) ->getSubExpr() ->getBeginLoc() .isValid()); } TEST_P(ASTImporterOptionSpecificTestBase, TemplateTemplateParmDeclNoDefaultArg) { Decl *FromTU = getTuDecl(R"( template typename TT> struct Y {}; )", Lang_CXX17); auto From = FirstDeclMatcher().match( FromTU, templateTemplateParmDecl(hasName("TT"))); TemplateTemplateParmDecl *To = Import(From, Lang_CXX17); ASSERT_FALSE(To->hasDefaultArgument()); } TEST_P(ASTImporterOptionSpecificTestBase, TemplateTemplateParmDeclDefaultArg) { Decl *FromTU = getTuDecl(R"( template struct X {}; template typename TT = X> struct Y {}; )", Lang_CXX17); auto From = FirstDeclMatcher().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().match( To->getTranslationUnitDecl(), classTemplateDecl(hasName("X"))); ASSERT_EQ(ToTemplate, ToExpectedDecl); } TEST_P(ASTImporterOptionSpecificTestBase, NonTypeTemplateParmDeclNoDefaultArg) { Decl *FromTU = getTuDecl("template struct X {};", Lang_CXX03); auto From = FirstDeclMatcher().match( FromTU, nonTypeTemplateParmDecl(hasName("N"))); NonTypeTemplateParmDecl *To = Import(From, Lang_CXX03); ASSERT_FALSE(To->hasDefaultArgument()); } TEST_P(ASTImporterOptionSpecificTestBase, NonTypeTemplateParmDeclDefaultArg) { Decl *FromTU = getTuDecl("template struct X {};", Lang_CXX03); auto From = FirstDeclMatcher().match( FromTU, nonTypeTemplateParmDecl(hasName("S"))); NonTypeTemplateParmDecl *To = Import(From, Lang_CXX03); ASSERT_TRUE(To->hasDefaultArgument()); Stmt *ToArg = To->getDefaultArgument(); ASSERT_TRUE(isa(ToArg)); ToArg = *ToArg->child_begin(); ASSERT_TRUE(isa(ToArg)); ASSERT_EQ(cast(ToArg)->getValue().getLimitedValue(), 1U); } TEST_P(ASTImporterOptionSpecificTestBase, ImportOfTemplatedDeclOfClassTemplateDecl) { Decl *FromTU = getTuDecl("template struct S{};", Lang_CXX03); auto From = FirstDeclMatcher().match(FromTU, classTemplateDecl()); ASSERT_TRUE(From); auto To = cast(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 void f(){}", Lang_CXX03); auto From = FirstDeclMatcher().match( FromTU, functionTemplateDecl()); ASSERT_TRUE(From); auto To = cast(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 struct S{};", Lang_CXX03); auto FromFT = FirstDeclMatcher().match(FromTU, classTemplateDecl()); ASSERT_TRUE(FromFT); auto ToTemplated = cast(Import(FromFT->getTemplatedDecl(), Lang_CXX03)); EXPECT_TRUE(ToTemplated); auto ToTU = ToTemplated->getTranslationUnitDecl(); auto ToFT = FirstDeclMatcher().match(ToTU, classTemplateDecl()); EXPECT_TRUE(ToFT); } TEST_P(ASTImporterOptionSpecificTestBase, ImportOfTemplatedDeclShouldImportTheFunctionTemplateDecl) { Decl *FromTU = getTuDecl("template void f(){}", Lang_CXX03); auto FromFT = FirstDeclMatcher().match( FromTU, functionTemplateDecl()); ASSERT_TRUE(FromFT); auto ToTemplated = cast(Import(FromFT->getTemplatedDecl(), Lang_CXX03)); EXPECT_TRUE(ToTemplated); auto ToTU = ToTemplated->getTranslationUnitDecl(); auto ToFT = FirstDeclMatcher().match( ToTU, functionTemplateDecl()); EXPECT_TRUE(ToFT); } TEST_P(ASTImporterOptionSpecificTestBase, ImportCorrectTemplatedDecl) { auto Code = R"( namespace x { template struct S1{}; template struct S2{}; template struct S3{}; } )"; Decl *FromTU = getTuDecl(Code, Lang_CXX03); auto FromNs = FirstDeclMatcher().match(FromTU, namespaceDecl()); auto ToNs = cast(Import(FromNs, Lang_CXX03)); ASSERT_TRUE(ToNs); auto From = FirstDeclMatcher().match(FromTU, classTemplateDecl( hasName("S2"))); auto To = FirstDeclMatcher().match(ToNs, classTemplateDecl( hasName("S2"))); ASSERT_TRUE(From); ASSERT_TRUE(To); auto ToTemplated = To->getTemplatedDecl(); auto ToTemplated1 = cast(Import(From->getTemplatedDecl(), Lang_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("choose", FromResults); ASSERT_TRUE(FromChooseExpr); const ChooseExpr *ToChooseExpr = selectFirst("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("expr", FromResults); ASSERT_TRUE(FromGenericSelectionExpr); const GenericSelectionExpr *ToGenericSelectionExpr = selectFirst("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 struct X {}; void declToImport(int y, X &x) {} template <> struct X { void g() { X x; declToImport(0, x); } }; )", Lang_CXX03, "", Lang_CXX03); MatchVerifier Verifier; auto Matcher = functionDecl(hasName("declToImport"), parameterCountIs(2), hasParameter(0, hasName("y")), hasParameter(1, hasName("x")), hasParameter(1, hasType(asString("X &")))); ASSERT_TRUE(Verifier.match(From, Matcher)); EXPECT_TRUE(Verifier.match(To, Matcher)); } TEST_P(ASTImporterOptionSpecificTestBase, TUshouldNotContainTemplatedDeclOfFunctionTemplates) { Decl *From, *To; std::tie(From, To) = getImportedDecl("template void declToImport() { T a = 1; }" "void instantiate() { declToImport(); }", Lang_CXX03, "", Lang_CXX03); auto Check = [](Decl *D) -> bool { auto TU = D->getTranslationUnitDecl(); for (auto Child : TU->decls()) { if (auto *FD = dyn_cast(Child)) { if (FD->getNameAsString() == "declToImport") { GTEST_NONFATAL_FAILURE_( "TU should not contain any FunctionDecl with name declToImport"); return false; } } } return true; }; ASSERT_TRUE(Check(From)); EXPECT_TRUE(Check(To)); } TEST_P(ASTImporterOptionSpecificTestBase, TUshouldNotContainTemplatedDeclOfClassTemplates) { Decl *From, *To; std::tie(From, To) = getImportedDecl("template struct declToImport { T t; };" "void instantiate() { declToImport(); }", Lang_CXX03, "", Lang_CXX03); auto Check = [](Decl *D) -> bool { auto TU = D->getTranslationUnitDecl(); for (auto Child : TU->decls()) { if (auto *RD = dyn_cast(Child)) { if (RD->getNameAsString() == "declToImport") { GTEST_NONFATAL_FAILURE_( "TU should not contain any CXXRecordDecl with name declToImport"); return false; } } } return true; }; ASSERT_TRUE(Check(From)); EXPECT_TRUE(Check(To)); } TEST_P(ASTImporterOptionSpecificTestBase, TUshouldNotContainTemplatedDeclOfTypeAlias) { Decl *From, *To; std::tie(From, To) = getImportedDecl( "template struct X {};" "template using declToImport = X;" "void instantiate() { declToImport a; }", Lang_CXX11, "", Lang_CXX11); auto Check = [](Decl *D) -> bool { auto TU = D->getTranslationUnitDecl(); for (auto Child : TU->decls()) { if (auto *AD = dyn_cast(Child)) { if (AD->getNameAsString() == "declToImport") { GTEST_NONFATAL_FAILURE_( "TU should not contain any TypeAliasDecl with name declToImport"); return false; } } } return true; }; ASSERT_TRUE(Check(From)); EXPECT_TRUE(Check(To)); } TEST_P(ASTImporterOptionSpecificTestBase, TUshouldNotContainClassTemplateSpecializationOfImplicitInstantiation) { Decl *From, *To; std::tie(From, To) = getImportedDecl( R"( template class Base {}; class declToImport : public Base {}; )", Lang_CXX03, "", Lang_CXX03); // Check that the ClassTemplateSpecializationDecl is NOT the child of the TU. auto Pattern = translationUnitDecl(unless(has(classTemplateSpecializationDecl()))); ASSERT_TRUE( MatchVerifier{}.match(From->getTranslationUnitDecl(), Pattern)); EXPECT_TRUE( MatchVerifier{}.match(To->getTranslationUnitDecl(), Pattern)); // Check that the ClassTemplateSpecializationDecl is the child of the // ClassTemplateDecl. Pattern = translationUnitDecl(has(classTemplateDecl( hasName("Base"), has(classTemplateSpecializationDecl())))); ASSERT_TRUE( MatchVerifier{}.match(From->getTranslationUnitDecl(), Pattern)); EXPECT_TRUE( MatchVerifier{}.match(To->getTranslationUnitDecl(), Pattern)); } AST_MATCHER_P(RecordDecl, hasFieldOrder, std::vector, Order) { size_t Index = 0; for (Decl *D : Node.decls()) { if (isa(D) || isa(D)) { auto *ND = cast(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 X {}; template class X; } )", 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{}.match(From, Pattern)); EXPECT_TRUE(MatchVerifier{}.match(To, Pattern)); // Check that the ClassTemplateSpecializationDecl is the child of the // NamespaceDecl. Pattern = namespaceDecl(has(classTemplateSpecializationDecl(hasName("X")))); ASSERT_TRUE(MatchVerifier{}.match(From, Pattern)); EXPECT_TRUE(MatchVerifier{}.match(To, Pattern)); } TEST_P(ASTImporterOptionSpecificTestBase, CXXRecordDeclFieldsShouldBeInCorrectOrder) { Decl *From, *To; std::tie(From, To) = getImportedDecl( "struct declToImport { int a; int b; };", Lang_CXX11, "", Lang_CXX11); MatchVerifier Verifier; ASSERT_TRUE(Verifier.match(From, cxxRecordDecl(hasFieldOrder({"a", "b"})))); EXPECT_TRUE(Verifier.match(To, cxxRecordDecl(hasFieldOrder({"a", "b"})))); } TEST_P(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 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 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 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 struct declToImport { }; )", Lang_CXX03, "", Lang_CXX03); MatchVerifier Verifier; // Match the implicit Decl. auto Matcher = classTemplateDecl(has(cxxRecordDecl(has(cxxRecordDecl())))); ASSERT_TRUE(Verifier.match(From, Matcher)); EXPECT_TRUE(Verifier.match(To, Matcher)); } TEST_P(ASTImporterOptionSpecificTestBase, ShouldImportImplicitCXXRecordDeclOfClassTemplateSpecializationDecl) { Decl *From, *To; std::tie(From, To) = getImportedDecl( R"( template class Base {}; class declToImport : public Base {}; )", Lang_CXX03, "", Lang_CXX03); auto hasImplicitClass = has(cxxRecordDecl()); auto Pattern = translationUnitDecl(has(classTemplateDecl( hasName("Base"), has(classTemplateSpecializationDecl(hasImplicitClass))))); ASSERT_TRUE( MatchVerifier{}.match(From->getTranslationUnitDecl(), Pattern)); EXPECT_TRUE( MatchVerifier{}.match(To->getTranslationUnitDecl(), Pattern)); } TEST_P(ASTImporterOptionSpecificTestBase, IDNSOrdinary) { Decl *From, *To; std::tie(From, To) = getImportedDecl("void declToImport() {}", Lang_CXX03, "", Lang_CXX03); MatchVerifier Verifier; auto Matcher = functionDecl(); ASSERT_TRUE(Verifier.match(From, Matcher)); EXPECT_TRUE(Verifier.match(To, Matcher)); EXPECT_EQ(From->getIdentifierNamespace(), To->getIdentifierNamespace()); } TEST_P(ASTImporterOptionSpecificTestBase, IDNSOfNonmemberOperator) { Decl *FromTU = getTuDecl( R"( struct X {}; void operator<<(int, X); )", Lang_CXX03); Decl *From = LastDeclMatcher{}.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 Base { int a; }; class declToImport : Base {}; )", Lang_CXX03, "", Lang_CXX03); auto Pattern = translationUnitDecl(has(classTemplateDecl( hasName("Base"), has(classTemplateSpecializationDecl(has(fieldDecl(hasName("a")))))))); ASSERT_TRUE( MatchVerifier{}.match(From->getTranslationUnitDecl(), Pattern)); EXPECT_TRUE( MatchVerifier{}.match(To->getTranslationUnitDecl(), Pattern)); } TEST_P(ASTImporterOptionSpecificTestBase, ImportDefinitionOfClassTemplateAfterFwdDecl) { { Decl *FromTU = getTuDecl( R"( template struct B; )", Lang_CXX03, "input0.cc"); auto *FromD = FirstDeclMatcher().match( FromTU, classTemplateDecl(hasName("B"))); Import(FromD, Lang_CXX03); } { Decl *FromTU = getTuDecl( R"( template struct B { void f(); }; )", Lang_CXX03, "input1.cc"); FunctionDecl *FromD = FirstDeclMatcher().match( FromTU, functionDecl(hasName("f"))); Import(FromD, Lang_CXX03); auto *FromCTD = FirstDeclMatcher().match( FromTU, classTemplateDecl(hasName("B"))); auto *ToCTD = cast(Import(FromCTD, Lang_CXX03)); EXPECT_TRUE(ToCTD->isThisDeclarationADefinition()); } } TEST_P(ASTImporterOptionSpecificTestBase, ImportDefinitionOfClassTemplateIfThereIsAnExistingFwdDeclAndDefinition) { Decl *ToTU = getToTuDecl( R"( template struct B { void f(); }; template struct B; )", Lang_CXX03); ASSERT_EQ(1u, DeclCounterWithPredicate( [](const ClassTemplateDecl *T) { return T->isThisDeclarationADefinition(); }) .match(ToTU, classTemplateDecl())); Decl *FromTU = getTuDecl( R"( template struct B { void f(); }; )", Lang_CXX03, "input1.cc"); ClassTemplateDecl *FromD = FirstDeclMatcher().match( FromTU, classTemplateDecl(hasName("B"))); Import(FromD, Lang_CXX03); // We should have only one definition. EXPECT_EQ(1u, DeclCounterWithPredicate( [](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().match( ToTU, cxxRecordDecl(unless(isImplicit())))); Decl *FromTU = getTuDecl( R"( struct B { void f(); }; )", Lang_CXX03, "input1.cc"); auto *FromD = FirstDeclMatcher().match( FromTU, cxxRecordDecl(hasName("B"))); Import(FromD, Lang_CXX03); EXPECT_EQ(2u, DeclCounter().match( ToTU, cxxRecordDecl(unless(isImplicit())))); } static void CompareSourceLocs(FullSourceLoc Loc1, FullSourceLoc Loc2) { EXPECT_EQ(Loc1.getExpansionLineNumber(), Loc2.getExpansionLineNumber()); EXPECT_EQ(Loc1.getExpansionColumnNumber(), Loc2.getExpansionColumnNumber()); EXPECT_EQ(Loc1.getSpellingLineNumber(), Loc2.getSpellingLineNumber()); EXPECT_EQ(Loc1.getSpellingColumnNumber(), Loc2.getSpellingColumnNumber()); } static void CompareSourceRanges(SourceRange Range1, SourceRange Range2, SourceManager &SM1, SourceManager &SM2) { CompareSourceLocs(FullSourceLoc{ Range1.getBegin(), SM1 }, FullSourceLoc{ Range2.getBegin(), SM2 }); CompareSourceLocs(FullSourceLoc{ Range1.getEnd(), SM1 }, FullSourceLoc{ Range2.getEnd(), SM2 }); } TEST_P(ASTImporterOptionSpecificTestBase, ImportSourceLocs) { Decl *FromTU = getTuDecl( R"( #define MFOO(arg) arg = arg + 1 void foo() { int a = 5; MFOO(a); } )", Lang_CXX03); auto FromD = FirstDeclMatcher().match(FromTU, functionDecl()); auto ToD = Import(FromD, Lang_CXX03); auto ToLHS = LastDeclMatcher().match(ToD, declRefExpr()); auto FromLHS = LastDeclMatcher().match(FromTU, declRefExpr()); auto ToRHS = LastDeclMatcher().match(ToD, integerLiteral()); auto FromRHS = LastDeclMatcher().match(FromTU, integerLiteral()); SourceManager &ToSM = ToAST->getASTContext().getSourceManager(); SourceManager &FromSM = FromD->getASTContext().getSourceManager(); CompareSourceRanges(ToD->getSourceRange(), FromD->getSourceRange(), ToSM, FromSM); CompareSourceRanges(ToLHS->getSourceRange(), FromLHS->getSourceRange(), ToSM, FromSM); CompareSourceRanges(ToRHS->getSourceRange(), FromRHS->getSourceRange(), ToSM, FromSM); } TEST_P(ASTImporterOptionSpecificTestBase, ImportNestedMacro) { Decl *FromTU = getTuDecl( R"( #define FUNC_INT void declToImport #define FUNC FUNC_INT FUNC(int a); )", Lang_CXX03); auto FromD = FirstDeclMatcher().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 struct B; template <> struct B {}; template <> struct B; )", Lang_CXX03); // We should have only one definition. ASSERT_EQ(1u, DeclCounterWithPredicate( [](const ClassTemplateSpecializationDecl *T) { return T->isThisDeclarationADefinition(); }) .match(ToTU, classTemplateSpecializationDecl())); Decl *FromTU = getTuDecl( R"( template struct B; template <> struct B {}; )", Lang_CXX03, "input1.cc"); auto *FromD = FirstDeclMatcher().match( FromTU, classTemplateSpecializationDecl(hasName("B"))); Import(FromD, Lang_CXX03); // We should have only one definition. EXPECT_EQ(1u, DeclCounterWithPredicate( [](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().match(FromTU, varDecl(hasName("object0"))); auto *From0 = getRecordDecl(Obj0); auto *Obj1 = FirstDeclMatcher().match(FromTU, varDecl(hasName("object1"))); auto *From1 = getRecordDecl(Obj1); auto *To0 = Import(From0, Lang_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().match(FromTU0, recordDecl(hasName("X"))); auto *X1 = FirstDeclMatcher().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().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().match(FromTU0, recordDecl(hasName("X"))); auto *X1 = FirstDeclMatcher().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().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().match(FromTU, Pattern); Imported1 = cast(Import(FromD, Lang_CXX03)); } VarDecl *Imported2; { Decl *FromTU = getTuDecl("int x;", Lang_CXX03, "input1.cc"); auto *FromD = FirstDeclMatcher().match(FromTU, Pattern); Imported2 = cast(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().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().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().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().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().match( FromTU, functionDecl(hasName("f"))); auto *FromD = FirstDeclMatcher().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().match(FromTU, Pattern); auto *Imported1 = cast(Import(FromD, Lang_CXX03)); ASSERT_FALSE(Imported1->isUsed(false)); FromD->setIsUsed(); auto *Imported2 = cast(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().match(FromTU, Pattern); // Proto Decl *ImportedD = Import(From, Lang_CXX03); Decl *ToTU = ImportedD->getTranslationUnitDecl(); EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 2u); auto *To0 = FirstDeclMatcher().match(ToTU, Pattern); auto *To1 = LastDeclMatcher().match(ToTU, Pattern); EXPECT_TRUE(ImportedD == To0); EXPECT_FALSE(To0->doesThisDeclarationHaveABody()); EXPECT_TRUE(To1->doesThisDeclarationHaveABody()); EXPECT_EQ(To1->getPreviousDecl(), To0); } TEST_P(ImportFunctions, ImportDefinitionOfRecursiveFunction) { Decl *FromTU = getTuDecl("void f(); void f() { f(); }", Lang_CXX03); auto Pattern = functionDecl(hasName("f")); auto *From = LastDeclMatcher().match(FromTU, Pattern); // Def Decl *ImportedD = Import(From, Lang_CXX03); Decl *ToTU = ImportedD->getTranslationUnitDecl(); EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 2u); auto *To0 = FirstDeclMatcher().match(ToTU, Pattern); auto *To1 = LastDeclMatcher().match(ToTU, Pattern); EXPECT_TRUE(ImportedD == To1); EXPECT_FALSE(To0->doesThisDeclarationHaveABody()); EXPECT_TRUE(To1->doesThisDeclarationHaveABody()); EXPECT_EQ(To1->getPreviousDecl(), To0); } TEST_P(ImportFunctions, 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().match(FromTU, Pattern); ASSERT_EQ(Proto->size_overridden_methods(), 1u); CXXMethodDecl *To = cast(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().match(FromTU, Pattern); CXXMethodDecl *Def = LastDeclMatcher().match(FromTU, Pattern); ASSERT_TRUE(Proto->isVirtual()); ASSERT_TRUE(Def->isVirtual()); CXXMethodDecl *To = cast(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([](const FunctionDecl *FD) { return FD->doesThisDeclarationHaveABody(); }).match(ToTU, functionDecl())); Decl *FromTU = getTuDecl("void f() {}", Lang_CXX03, "input0.cc"); auto *FromD = FirstDeclMatcher().match(FromTU, functionDecl()); Import(FromD, Lang_CXX03); EXPECT_EQ(1u, DeclCounterWithPredicate([](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().match(FromTU0, DFP); Import(DF, Lang_CXX03); Decl *FromTU1 = getTuDecl(Code, Lang_CXX03, "input1.cc"); auto *BF = FirstDeclMatcher().match(FromTU1, BFP); Import(BF, Lang_CXX03); auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); EXPECT_EQ(DeclCounter().match(ToTU, BFP), 1u); EXPECT_EQ(DeclCounter().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().match(FromTU, DFP); Import(FromD, Lang_CXX03); } { Decl *FromTU = getTuDecl(CodeWithoutDef, Lang_CXX03, "input1.cc"); auto *FromB = FirstDeclMatcher().match(FromTU, BFP); Import(FromB, Lang_CXX03); } auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); EXPECT_EQ(DeclCounter().match(ToTU, BFP), 1u); EXPECT_EQ(DeclCounter().match(ToTU, DFP), 1u); EXPECT_EQ(DeclCounter().match(ToTU, BFDefP), 1u); EXPECT_EQ(DeclCounter().match(ToTU, DFDefP), 1u); EXPECT_EQ(DeclCounter().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().match(FromTU0, DFP); Import(D, Lang_CXX03); Decl *FromTU1 = getTuDecl(Code, Lang_CXX03, "input1.cc"); auto *B = FirstDeclMatcher().match(FromTU1, BFP); Import(B, Lang_CXX03); auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); EXPECT_EQ(DeclCounter().match(ToTU, BFP), 1u); EXPECT_EQ(DeclCounter().match(ToTU, BFDefP), 0u); auto *ToB = FirstDeclMatcher().match( ToTU, cxxRecordDecl(hasName("B"))); auto *ToBFInClass = FirstDeclMatcher().match(ToTU, BFP); auto *ToBFOutOfClass = FirstDeclMatcher().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().match(FromTU0, DFP); Import(D, Lang_CXX03); } { Decl *FromTU1 = getTuDecl(CodeTU1, Lang_CXX03, "input1.cc"); auto *Foo = FirstDeclMatcher().match(FromTU1, FooDef); Import(Foo, Lang_CXX03); } auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); EXPECT_EQ(DeclCounter().match(ToTU, BFP), 1u); EXPECT_EQ(DeclCounter().match(ToTU, DFP), 1u); EXPECT_EQ(DeclCounter().match(ToTU, BFDefP), 0u); EXPECT_EQ(DeclCounter().match(ToTU, DFDefP), 0u); auto *ToB = FirstDeclMatcher().match( ToTU, cxxRecordDecl(hasName("B"))); auto *ToD = FirstDeclMatcher().match( ToTU, cxxRecordDecl(hasName("D"))); auto *ToBFInClass = FirstDeclMatcher().match(ToTU, BFP); auto *ToBFOutOfClass = FirstDeclMatcher().match( ToTU, cxxMethodDecl(hasName("f"), isDefinition())); auto *ToDFInClass = FirstDeclMatcher().match(ToTU, DFP); auto *ToDFOutOfClass = LastDeclMatcher().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().match(FromTu, Pattern); auto *From1 = LastDeclMatcher().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().match(FromTu, Pattern); auto *FromF1 = LastDeclMatcher().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().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().match( FromTU, functionDecl(hasName("g1"))); Import(FromD, Lang_CXX03); } Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); ASSERT_EQ(DeclCounter().match(ToTU, functionDecl(hasName("f"))), 2u); } TEST_P(ImportFunctions, ImportImplicitFunctionsInLambda) { Decl *FromTU = getTuDecl( R"( void foo() { (void)[]() { ; }; } )", Lang_CXX11); auto *FromD = FirstDeclMatcher().match( FromTU, functionDecl(hasName("foo"))); auto *ToD = Import(FromD, Lang_CXX03); EXPECT_TRUE(ToD); CXXRecordDecl *LambdaRec = cast(cast( *cast(ToD->getBody())->body_begin()) ->getSubExpr()) ->getLambdaClass(); EXPECT_TRUE(LambdaRec->getDestructor()); } TEST_P(ImportFunctions, CallExprOfMemberFunctionTemplateWithExplicitTemplateArgs) { Decl *FromTU = getTuDecl( R"( struct X { template void foo(){} }; void f() { X x; x.foo(); } )", Lang_CXX03); auto *FromD = FirstDeclMatcher().match( FromTU, functionDecl(hasName("f"))); auto *ToD = Import(FromD, Lang_CXX03); EXPECT_TRUE(ToD); EXPECT_TRUE(MatchVerifier().match( ToD, functionDecl(hasName("f"), hasDescendant(declRefExpr())))); } TEST_P(ImportFunctions, DependentCallExprOfMemberFunctionTemplateWithExplicitTemplateArgs) { Decl *FromTU = getTuDecl( R"( struct X { template void foo(){} }; template void f() { X x; x.foo(); } void g() { f(); } )", Lang_CXX03); auto *FromD = FirstDeclMatcher().match( FromTU, functionDecl(hasName("g"))); auto *ToD = Import(FromD, Lang_CXX03); EXPECT_TRUE(ToD); Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); EXPECT_TRUE(MatchVerifier().match( ToTU, translationUnitDecl(hasDescendant( functionDecl(hasName("f"), hasDescendant(declRefExpr())))))); } struct ImportFunctionTemplates : ASTImporterOptionSpecificTestBase {}; TEST_P(ImportFunctionTemplates, ImportFunctionTemplateInRecordDeclTwice) { auto Code = R"( class X { template void f(T t); }; )"; Decl *FromTU1 = getTuDecl(Code, Lang_CXX03, "input1.cc"); auto *FromD1 = FirstDeclMatcher().match( FromTU1, functionTemplateDecl(hasName("f"))); auto *ToD1 = Import(FromD1, Lang_CXX03); Decl *FromTU2 = getTuDecl(Code, Lang_CXX03, "input2.cc"); auto *FromD2 = FirstDeclMatcher().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 void f(T t); }; template void X::f(T t) {}; )"; Decl *FromTU1 = getTuDecl(Code, Lang_CXX03, "input1.cc"); auto *FromD1 = FirstDeclMatcher().match( FromTU1, functionTemplateDecl(hasName("f"))); auto *ToD1 = Import(FromD1, Lang_CXX03); Decl *FromTU2 = getTuDecl(Code, Lang_CXX03, "input2.cc"); auto *FromD2 = FirstDeclMatcher().match( FromTU2, functionTemplateDecl(hasName("f"))); auto *ToD2 = Import(FromD2, Lang_CXX03); EXPECT_EQ(ToD1, ToD2); } TEST_P(ImportFunctionTemplates, ImportFunctionWhenThereIsAFunTemplateWithSameName) { getToTuDecl( R"( template void foo(T) {} void foo(); )", Lang_CXX03); Decl *FromTU = getTuDecl("void foo();", Lang_CXX03); auto *FromD = FirstDeclMatcher().match( FromTU, functionDecl(hasName("foo"))); auto *ImportedD = Import(FromD, Lang_CXX03); EXPECT_TRUE(ImportedD); } TEST_P(ImportFunctionTemplates, ImportConstructorWhenThereIsAFunTemplateWithSameName) { auto Code = R"( struct Foo { template Foo(T) {} Foo(); }; )"; getToTuDecl(Code, Lang_CXX03); Decl *FromTU = getTuDecl(Code, Lang_CXX03); auto *FromD = LastDeclMatcher().match(FromTU, cxxConstructorDecl()); auto *ImportedD = Import(FromD, Lang_CXX03); EXPECT_TRUE(ImportedD); } TEST_P(ImportFunctionTemplates, ImportOperatorWhenThereIsAFunTemplateWithSameName) { getToTuDecl( R"( template 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().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().match(FromTU, Pattern); auto *ImportedD = cast(Import(FromD, Lang_CXX03)); Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); ASSERT_EQ(DeclCounter().match(ToTU, Pattern), 2u); EXPECT_FALSE(ImportedD->doesThisDeclarationHaveABody()); auto *ToFD = LastDeclMatcher().match(ToTU, Pattern); EXPECT_FALSE(ToFD->doesThisDeclarationHaveABody()); EXPECT_EQ(ToFD->getPreviousDecl(), ImportedD); } TEST_P(ImportFriendFunctions, ImportFriendFunctionRedeclChainProto_OutOfClassProtoFirst) { auto Pattern = functionDecl(hasName("f")); Decl *FromTU = getTuDecl("void f();" "struct X { friend void f(); };", Lang_CXX03, "input0.cc"); auto FromD = FirstDeclMatcher().match(FromTU, Pattern); auto *ImportedD = cast(Import(FromD, Lang_CXX03)); Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); ASSERT_EQ(DeclCounter().match(ToTU, Pattern), 2u); EXPECT_FALSE(ImportedD->doesThisDeclarationHaveABody()); auto *ToFD = LastDeclMatcher().match(ToTU, Pattern); EXPECT_FALSE(ToFD->doesThisDeclarationHaveABody()); EXPECT_EQ(ToFD->getPreviousDecl(), ImportedD); } TEST_P(ImportFriendFunctions, ImportFriendFunctionRedeclChainDef) { auto Pattern = functionDecl(hasName("f")); Decl *FromTU = getTuDecl("struct X { friend void f(){} };" "void f();", Lang_CXX03, "input0.cc"); auto *FromD = FirstDeclMatcher().match(FromTU, Pattern); auto *ImportedD = cast(Import(FromD, Lang_CXX03)); Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); ASSERT_EQ(DeclCounter().match(ToTU, Pattern), 2u); EXPECT_TRUE(ImportedD->doesThisDeclarationHaveABody()); auto *ToFD = LastDeclMatcher().match(ToTU, Pattern); EXPECT_FALSE(ToFD->doesThisDeclarationHaveABody()); EXPECT_EQ(ToFD->getPreviousDecl(), ImportedD); } TEST_P(ImportFriendFunctions, ImportFriendFunctionRedeclChainDef_OutOfClassDef) { auto Pattern = functionDecl(hasName("f")); Decl *FromTU = getTuDecl("struct X { friend void f(); };" "void f(){}", Lang_CXX03, "input0.cc"); auto *FromD = FirstDeclMatcher().match(FromTU, Pattern); auto *ImportedD = cast(Import(FromD, Lang_CXX03)); Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); ASSERT_EQ(DeclCounter().match(ToTU, Pattern), 2u); EXPECT_FALSE(ImportedD->doesThisDeclarationHaveABody()); auto *ToFD = LastDeclMatcher().match(ToTU, Pattern); EXPECT_TRUE(ToFD->doesThisDeclarationHaveABody()); EXPECT_EQ(ToFD->getPreviousDecl(), ImportedD); } 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().match(FromTU, Pattern); auto *ImportedD = cast(Import(FromD, Lang_CXX03)); Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); ASSERT_EQ(DeclCounter().match(ToTU, Pattern), 2u); EXPECT_TRUE(ImportedD->doesThisDeclarationHaveABody()); auto *InClassFD = cast(FirstDeclMatcher() .match(ToTU, friendDecl()) ->getFriendDecl()); EXPECT_FALSE(InClassFD->doesThisDeclarationHaveABody()); EXPECT_EQ(InClassFD->getPreviousDecl(), ImportedD); // The parameters must refer the same type EXPECT_EQ((*InClassFD->param_begin())->getOriginalType(), (*ImportedD->param_begin())->getOriginalType()); } 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().match(FromTU, Pattern); auto *ImportedD = cast(Import(FromD, Lang_CXX03)); Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); ASSERT_EQ(DeclCounter().match(ToTU, Pattern), 2u); EXPECT_FALSE(ImportedD->doesThisDeclarationHaveABody()); auto *OutOfClassFD = FirstDeclMatcher().match( ToTU, functionDecl(unless(hasParent(friendDecl())))); EXPECT_TRUE(OutOfClassFD->doesThisDeclarationHaveABody()); EXPECT_EQ(ImportedD->getPreviousDecl(), OutOfClassFD); // The parameters must refer the same type EXPECT_EQ((*OutOfClassFD->param_begin())->getOriginalType(), (*ImportedD->param_begin())->getOriginalType()); } TEST_P(ImportFriendFunctions, ImportFriendFunctionFromMultipleTU) { auto Pattern = functionDecl(hasName("f")); FunctionDecl *ImportedD; { Decl *FromTU = getTuDecl("struct X { friend void f(){} };", Lang_CXX03, "input0.cc"); auto *FromD = FirstDeclMatcher().match(FromTU, Pattern); ImportedD = cast(Import(FromD, Lang_CXX03)); } FunctionDecl *ImportedD1; { Decl *FromTU = getTuDecl("void f();", Lang_CXX03, "input1.cc"); auto *FromD = FirstDeclMatcher().match(FromTU, Pattern); ImportedD1 = cast(Import(FromD, Lang_CXX03)); } Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); ASSERT_EQ(DeclCounter().match(ToTU, Pattern), 2u); EXPECT_TRUE(ImportedD->doesThisDeclarationHaveABody()); EXPECT_FALSE(ImportedD1->doesThisDeclarationHaveABody()); EXPECT_EQ(ImportedD1->getPreviousDecl(), ImportedD); } 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().match(FromTU, FunctionPattern); ASSERT_TRUE(FromD->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); ASSERT_FALSE(FromD->isInIdentifierNamespace(Decl::IDNS_Ordinary)); { auto FromName = FromD->getDeclName(); auto *Class = FirstDeclMatcher().match(FromTU, ClassPattern); auto LookupRes = Class->noload_lookup(FromName); ASSERT_EQ(LookupRes.size(), 0u); LookupRes = FromTU->noload_lookup(FromName); ASSERT_EQ(LookupRes.size(), 1u); } auto *ToD = cast(Import(FromD, Lang_CXX03)); auto ToName = ToD->getDeclName(); TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); auto *Class = FirstDeclMatcher().match(ToTU, ClassPattern); auto LookupRes = Class->noload_lookup(ToName); EXPECT_EQ(LookupRes.size(), 0u); LookupRes = ToTU->noload_lookup(ToName); EXPECT_EQ(LookupRes.size(), 1u); EXPECT_EQ(DeclCounter().match(ToTU, FunctionPattern), 1u); auto *To0 = FirstDeclMatcher().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().match(FromTU, FunctionPattern); auto *FromNormal = LastDeclMatcher().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().match(FromTU, ClassPattern); auto LookupRes = FromClass->noload_lookup(FromName); ASSERT_EQ(LookupRes.size(), 0u); LookupRes = FromTU->noload_lookup(FromName); ASSERT_EQ(LookupRes.size(), 1u); auto *ToFriend = cast(Import(FromFriend, Lang_CXX03)); auto ToName = ToFriend->getDeclName(); TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); auto *ToClass = FirstDeclMatcher().match(ToTU, ClassPattern); LookupRes = ToClass->noload_lookup(ToName); EXPECT_EQ(LookupRes.size(), 0u); LookupRes = ToTU->noload_lookup(ToName); // Test is disabled because this result is 2. EXPECT_EQ(LookupRes.size(), 1u); ASSERT_EQ(DeclCounter().match(ToTU, FunctionPattern), 2u); ToFriend = FirstDeclMatcher().match(ToTU, FunctionPattern); auto *ToNormal = LastDeclMatcher().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().match(FromTU, FunctionPattern); auto *FromFriend = LastDeclMatcher().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().match(FromTU, ClassPattern); auto LookupRes = FromClass->noload_lookup(FromName); ASSERT_EQ(LookupRes.size(), 0u); LookupRes = FromTU->noload_lookup(FromName); ASSERT_EQ(LookupRes.size(), 1u); auto *ToNormal = cast(Import(FromNormal, Lang_CXX03)); auto ToName = ToNormal->getDeclName(); TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); auto *ToClass = FirstDeclMatcher().match(ToTU, ClassPattern); LookupRes = ToClass->noload_lookup(ToName); EXPECT_EQ(LookupRes.size(), 0u); LookupRes = ToTU->noload_lookup(ToName); EXPECT_EQ(LookupRes.size(), 1u); EXPECT_EQ(DeclCounter().match(ToTU, FunctionPattern), 2u); ToNormal = FirstDeclMatcher().match(ToTU, FunctionPattern); auto *ToFriend = LastDeclMatcher().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().match(FromNormalTU, Pattern); TranslationUnitDecl *FromFriendTU = getTuDecl("class X { friend void f(); };", Lang_CXX03, "input1.cc"); auto *FromFriendF = FirstDeclMatcher().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_EQ(LookupRes.size(), 1u); LookupRes = FromFriendTU->noload_lookup(FromFriendName); ASSERT_EQ(LookupRes.size(), 1u); auto *ToNormalF = cast(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_EQ(LookupRes.size(), 1u); EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 1u); auto *ToFriendF = cast(Import(FromFriendF, Lang_CXX03)); LookupRes = ToTU->noload_lookup(ToName); EXPECT_EQ(LookupRes.size(), 1u); EXPECT_EQ(DeclCounter().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().match( FromTU, functionDecl(hasName("f"))); auto *FromClass = FirstDeclMatcher().match( FromTU, cxxRecordDecl(hasName("X"))); auto *FromFriend = FirstDeclMatcher().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().match( ToTU, cxxRecordDecl(hasName("X"))); auto *ToFriend = FirstDeclMatcher().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, 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 Verifier; testImport("struct declToImport {" " int b = a + 2;" " int a = 5;" "};", Lang_CXX11, "", Lang_CXX11, Verifier, recordDecl(hasFieldOrder({"b", "a"}))); } const internal::VariadicDynCastAllOfMatcher dependentScopeDeclRefExpr; TEST_P(ImportExpr, DependentScopeDeclRefExpr) { MatchVerifier Verifier; testImport("template struct S { static T foo; };" "template void declToImport() {" " (void) S::foo;" "}" "void instantiate() { declToImport(); }" "template T S::foo;", Lang_CXX11, "", Lang_CXX11, Verifier, functionTemplateDecl(has(functionDecl(has(compoundStmt( has(cStyleCastExpr(has(dependentScopeDeclRefExpr()))))))))); testImport("template struct S {" "template static void foo(){};" "};" "template void declToImport() {" " S::template foo();" "}" "void instantiate() { declToImport(); }", Lang_CXX11, "", Lang_CXX11, Verifier, functionTemplateDecl(has(functionDecl(has(compoundStmt( has(callExpr(has(dependentScopeDeclRefExpr()))))))))); } const internal::VariadicDynCastAllOfMatcher dependentNameType; TEST_P(ImportExpr, DependentNameType) { MatchVerifier Verifier; testImport("template struct declToImport {" " typedef typename T::type dependent_name;" "};", Lang_CXX11, "", Lang_CXX11, Verifier, classTemplateDecl(has( cxxRecordDecl(has(typedefDecl(has(dependentNameType()))))))); } TEST_P(ImportExpr, UnresolvedMemberExpr) { MatchVerifier Verifier; testImport("struct S { template void mem(); };" "template void declToImport() {" " S s;" " s.mem();" "}" "void instantiate() { declToImport(); }", Lang_CXX11, "", Lang_CXX11, Verifier, functionTemplateDecl(has(functionDecl(has( compoundStmt(has(callExpr(has(unresolvedMemberExpr()))))))))); } class ImportImplicitMethods : public 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 void testImportOf( const MatcherType &MethodMatcher, const char *Code = DefaultCode) { test(MethodMatcher, Code, /*ExpectedCount=*/1u); } template void testNoImportOf( const MatcherType &MethodMatcher, const char *Code = DefaultCode) { test(MethodMatcher, Code, /*ExpectedCount=*/0u); } private: template void test(const MatcherType &MethodMatcher, const char *Code, unsigned int ExpectedCount) { auto ClassMatcher = cxxRecordDecl(unless(isImplicit())); Decl *ToTU = getToTuDecl(Code, Lang_CXX11); auto *ToClass = FirstDeclMatcher().match( ToTU, ClassMatcher); ASSERT_EQ(DeclCounter().match(ToClass, MethodMatcher), 1u); { CXXMethodDecl *Method = FirstDeclMatcher().match(ToClass, MethodMatcher); ToClass->removeDecl(Method); SharedStatePtr->getLookupTable()->remove(Method); } ASSERT_EQ(DeclCounter().match(ToClass, MethodMatcher), 0u); Decl *ImportedClass = nullptr; { Decl *FromTU = getTuDecl(Code, Lang_CXX11, "input1.cc"); auto *FromClass = FirstDeclMatcher().match( FromTU, ClassMatcher); ImportedClass = Import(FromClass, Lang_CXX11); } EXPECT_EQ(ToClass, ImportedClass); EXPECT_EQ(DeclCounter().match(ToClass, MethodMatcher), ExpectedCount); } }; TEST_P(ImportImplicitMethods, DefaultConstructor) { testImportOf(cxxConstructorDecl(isDefaultConstructor())); } TEST_P(ImportImplicitMethods, CopyConstructor) { testImportOf(cxxConstructorDecl(isCopyConstructor())); } TEST_P(ImportImplicitMethods, MoveConstructor) { testImportOf(cxxConstructorDecl(isMoveConstructor())); } TEST_P(ImportImplicitMethods, Destructor) { testImportOf(cxxDestructorDecl()); } TEST_P(ImportImplicitMethods, CopyAssignment) { testImportOf(cxxMethodDecl(isCopyAssignmentOperator())); } TEST_P(ImportImplicitMethods, MoveAssignment) { testImportOf(cxxMethodDecl(isMoveAssignmentOperator())); } TEST_P(ImportImplicitMethods, DoNotImportUserProvided) { auto Code = R"( struct A { A() { int x; } }; )"; testNoImportOf(cxxConstructorDecl(isDefaultConstructor()), Code); } TEST_P(ImportImplicitMethods, DoNotImportDefault) { auto Code = R"( struct A { A() = default; }; )"; testNoImportOf(cxxConstructorDecl(isDefaultConstructor()), Code); } TEST_P(ImportImplicitMethods, DoNotImportDeleted) { auto Code = R"( struct A { A() = delete; }; )"; testNoImportOf(cxxConstructorDecl(isDefaultConstructor()), Code); } TEST_P(ImportImplicitMethods, DoNotImportOtherMethod) { auto Code = R"( struct A { void f() { } }; )"; testNoImportOf(cxxMethodDecl(hasName("f")), Code); } TEST_P(ASTImporterOptionSpecificTestBase, ImportOfEquivalentRecord) { Decl *ToR1; { Decl *FromTU = getTuDecl("struct A { };", Lang_CXX03, "input0.cc"); auto *FromR = FirstDeclMatcher().match( FromTU, cxxRecordDecl(hasName("A"))); ToR1 = Import(FromR, Lang_CXX03); } Decl *ToR2; { Decl *FromTU = getTuDecl("struct A { };", Lang_CXX03, "input1.cc"); auto *FromR = FirstDeclMatcher().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().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().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().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().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().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().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().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().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().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().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().match(FromTU, recordDecl(hasName("A"))); Import(From, Lang_C99); auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); auto *Entry0 = FirstDeclMatcher().match(ToTU, fieldDecl(hasName("entry0"))); auto *Entry1 = FirstDeclMatcher().match(ToTU, fieldDecl(hasName("entry1"))); auto *R0 = getRecordDecl(Entry0); auto *R1 = getRecordDecl(Entry1); EXPECT_NE(R0, R1); EXPECT_TRUE(MatchVerifier().match( R0, recordDecl(has(fieldDecl(hasName("next")))))); EXPECT_TRUE(MatchVerifier().match( R1, recordDecl(has(fieldDecl(hasName("next")))))); } 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().match( FromTU, functionDecl(hasName("f"))); auto *ToF = cast_or_null(Import(FromF, Lang_CXX11)); EXPECT_TRUE(ToF); CXXRecordDecl *FromLambda = cast(cast(cast( FromF->getBody())->body_front())->getSubExpr())->getLambdaClass(); auto *ToLambda = cast_or_null(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(Import(FromField, Lang_CXX11)); EXPECT_TRUE(ToField); Optional 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 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 xc('c'); } )", Lang_CXX11); auto *ToSpec = FirstDeclMatcher().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 xc(1); } )", Lang_CXX11); auto *FromSpec = FirstDeclMatcher().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 struct X { void f() {} void g() {} }; )"; Decl *ToTU = getToTuDecl(ClassTemplate + R"( void foo() { X x; x.f(); } )", Lang_CXX11); Decl *FromTU = getTuDecl(ClassTemplate + R"( void bar() { X x; x.g(); } )", Lang_CXX11); auto *FromSpec = FirstDeclMatcher().match( FromTU, classTemplateSpecializationDecl(hasName("X"))); auto FunPattern = functionDecl(hasName("g"), hasParent(classTemplateSpecializationDecl())); auto *FromFun = FirstDeclMatcher().match(FromTU, FunPattern); auto *ToFun = FirstDeclMatcher().match(ToTU, FunPattern); ASSERT_TRUE(FromFun->hasBody()); ASSERT_FALSE(ToFun->hasBody()); auto *ImportedSpec = Import(FromSpec, Lang_CXX11); ASSERT_TRUE(ImportedSpec); auto *ToSpec = FirstDeclMatcher().match( ToTU, classTemplateSpecializationDecl(hasName("X"))); EXPECT_EQ(ImportedSpec, ToSpec); EXPECT_TRUE(ToFun->hasBody()); } TEST_P(ASTImporterOptionSpecificTestBase, MergeTemplateSpecWithForwardDecl) { std::string ClassTemplate = R"( template struct X { int m; }; template<> struct X { int m; }; )"; // Append a forward decl for our template specialization. getToTuDecl(ClassTemplate + "template<> struct X;", Lang_CXX11); Decl *FromTU = getTuDecl(ClassTemplate, Lang_CXX11); auto *FromSpec = FirstDeclMatcher().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 struct X {}; )"; Decl *ToTU = getToTuDecl(ClassTemplate + R"( template <> struct X { int a; }; void foo() { X x; } )", Lang_CXX11); Decl *FromTU = getTuDecl(ClassTemplate + R"( template <> struct X { int b; }; void foo() { X x; } )", Lang_CXX11); auto *FromSpec = FirstDeclMatcher().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().match( ToTU, classTemplateSpecializationDecl(hasName("X")))); } TEST_P(ASTImporterOptionSpecificTestBase, MergeCtorOfClassTemplateSpecialization) { std::string ClassTemplate = R"( template struct X { X(char) {} X(int) {} }; )"; Decl *ToTU = getToTuDecl(ClassTemplate + R"( void foo() { X x('c'); } )", Lang_CXX11); Decl *FromTU = getTuDecl(ClassTemplate + R"( void bar() { X x(1); } )", Lang_CXX11); auto *FromSpec = FirstDeclMatcher().match( FromTU, classTemplateSpecializationDecl(hasName("X"))); // Match the void(int) ctor. auto CtorPattern = cxxConstructorDecl(hasParameter(0, varDecl(hasType(asString("int")))), hasParent(classTemplateSpecializationDecl())); auto *FromCtor = FirstDeclMatcher().match(FromTU, CtorPattern); auto *ToCtor = FirstDeclMatcher().match(ToTU, CtorPattern); ASSERT_TRUE(FromCtor->hasBody()); ASSERT_FALSE(ToCtor->hasBody()); auto *ImportedSpec = Import(FromSpec, Lang_CXX11); ASSERT_TRUE(ImportedSpec); auto *ToSpec = FirstDeclMatcher().match( ToTU, classTemplateSpecializationDecl(hasName("X"))); EXPECT_EQ(ImportedSpec, ToSpec); EXPECT_TRUE(ToCtor->hasBody()); } TEST_P(ASTImporterOptionSpecificTestBase, ClassTemplateFriendDecl) { const auto *Code = R"( template class X { friend T; }; struct Y {}; template class X; )"; Decl *ToTU = getToTuDecl(Code, Lang_CXX11); Decl *FromTU = getTuDecl(Code, Lang_CXX11); auto *FromSpec = FirstDeclMatcher().match( FromTU, classTemplateSpecializationDecl()); auto *ToSpec = FirstDeclMatcher().match( ToTU, classTemplateSpecializationDecl()); auto *ImportedSpec = Import(FromSpec, Lang_CXX11); EXPECT_EQ(ImportedSpec, ToSpec); EXPECT_EQ(1u, DeclCounter().match( ToTU, classTemplateSpecializationDecl())); } TEST_P(ASTImporterOptionSpecificTestBase, ClassTemplatePartialSpecializationsShouldNotBeDuplicated) { auto Code = R"( // primary template template class A {}; // partial specialization template class A {}; )"; Decl *ToTU = getToTuDecl(Code, Lang_CXX11); Decl *FromTU = getTuDecl(Code, Lang_CXX11); auto *FromSpec = FirstDeclMatcher().match( FromTU, classTemplatePartialSpecializationDecl()); auto *ToSpec = FirstDeclMatcher().match( ToTU, classTemplatePartialSpecializationDecl()); auto *ImportedSpec = Import(FromSpec, Lang_CXX11); EXPECT_EQ(ImportedSpec, ToSpec); EXPECT_EQ(1u, DeclCounter().match( ToTU, classTemplatePartialSpecializationDecl())); } TEST_P(ASTImporterOptionSpecificTestBase, ClassTemplateSpecializationsShouldNotBeDuplicated) { auto Code = R"( // primary template template class A {}; // full specialization template<> class A {}; )"; Decl *ToTU = getToTuDecl(Code, Lang_CXX11); Decl *FromTU = getTuDecl(Code, Lang_CXX11); auto *FromSpec = FirstDeclMatcher().match( FromTU, classTemplateSpecializationDecl()); auto *ToSpec = FirstDeclMatcher().match( ToTU, classTemplateSpecializationDecl()); auto *ImportedSpec = Import(FromSpec, Lang_CXX11); EXPECT_EQ(ImportedSpec, ToSpec); EXPECT_EQ(1u, DeclCounter().match( ToTU, classTemplateSpecializationDecl())); } TEST_P(ASTImporterOptionSpecificTestBase, ClassTemplateFullAndPartialSpecsShouldNotBeMixed) { std::string PrimaryTemplate = R"( template class A {}; )"; auto PartialSpec = R"( template class A {}; )"; auto FullSpec = R"( template<> class A {}; )"; Decl *ToTU = getToTuDecl(PrimaryTemplate + FullSpec, Lang_CXX11); Decl *FromTU = getTuDecl(PrimaryTemplate + PartialSpec, Lang_CXX11); auto *FromSpec = FirstDeclMatcher().match( FromTU, classTemplateSpecializationDecl()); auto *ImportedSpec = Import(FromSpec, Lang_CXX11); EXPECT_TRUE(ImportedSpec); // Check the number of partial specializations. EXPECT_EQ(1u, DeclCounter().match( ToTU, classTemplatePartialSpecializationDecl())); // Check the number of full specializations. EXPECT_EQ(1u, DeclCounter().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().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(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().match( FromTU, varDecl(hasName("a"))); // Decl with init auto *FromDWithDef = LastDeclMatcher().match( FromTU, varDecl(hasName("a"))); // Decl with definition ASSERT_NE(FromDWithInit, FromDWithDef); ASSERT_EQ(FromDWithDef->getPreviousDecl(), FromDWithInit); auto *ToD0 = cast(Import(FromDWithInit, Lang_CXX11)); auto *ToD1 = cast(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().match( FromTU, varDecl(hasName("a"))); // Decl with init auto *FromDWithDef = LastDeclMatcher().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().match( ToTU, varDecl(hasName("a"))); // Decl with init ASSERT_TRUE(ToD->getInit()); ASSERT_FALSE(ToD->getDefinition()); auto *ImportedD = cast(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().match( FromTU, varDecl(hasName("a"))); auto *FromDWithDef = LastDeclMatcher().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().match( ToTU, varDecl(hasName("a"))); ASSERT_FALSE(ToD->getInit()); ASSERT_FALSE(ToD->getDefinition()); auto *ImportedD = cast(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().match(ToTU, Pattern); auto FromDef = FirstDeclMatcher().match(FromTU1, Pattern); Decl *ImportedDef = Import(FromDef, Lang_C99); EXPECT_NE(ImportedDef, ToProto); EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 2u); auto ToDef = LastDeclMatcher().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().match(ToTU, Pattern); auto FromDef = FirstDeclMatcher().match(FromTU1, Pattern); Decl *ImportedDef = Import(FromDef, Lang_CXX03); EXPECT_NE(ImportedDef, ToProto); EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 2u); auto ToDef = LastDeclMatcher().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().match(FromTU0, Pattern); auto FromDef = FirstDeclMatcher().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().match(ToTU, Pattern), 2u); auto ToProto = FirstDeclMatcher().match(ToTU, Pattern); auto ToDef = LastDeclMatcher().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 class F {}; class X { template friend class F; }; }; )", Lang_CXX03, "input0.cc"); auto *FromClass = FirstDeclMatcher().match( FromTU, cxxRecordDecl(hasName("F"), isDefinition())); auto *FromFriendClass = LastDeclMatcher().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(Import(FromClass, Lang_CXX03)); auto *ToFriendClass = cast(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().match( FromTu, cxxRecordDecl(hasName("declToImport"))); auto *ToD = Import(FromD, Lang_CXX03); auto Pattern = cxxRecordDecl(has(friendDecl())); ASSERT_TRUE(MatchVerifier{}.match(FromD, Pattern)); EXPECT_TRUE(MatchVerifier{}.match(ToD, Pattern)); } TEST_P(ImportFriendClasses, UndeclaredFriendClassShouldNotBeVisible) { Decl *FromTu = getTuDecl("class X { friend class Y; };", Lang_CXX03, "from.cc"); auto *FromX = FirstDeclMatcher().match( FromTu, cxxRecordDecl(hasName("X"))); auto *FromFriend = FirstDeclMatcher().match(FromTu, friendDecl()); RecordDecl *FromRecordOfFriend = const_cast(getRecordDeclOfFriend(FromFriend)); ASSERT_EQ(FromRecordOfFriend->getDeclContext(), cast(FromTu)); ASSERT_EQ(FromRecordOfFriend->getLexicalDeclContext(), cast(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().match(ToTu, friendDecl()); RecordDecl *ToRecordOfFriend = const_cast(getRecordDeclOfFriend(ToFriend)); ASSERT_EQ(ToRecordOfFriend->getDeclContext(), cast(ToTu)); ASSERT_EQ(ToRecordOfFriend->getLexicalDeclContext(), cast(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 declToImport { template friend class declToImport; }; )", Lang_CXX03, "input.cc"); auto *FromD = FirstDeclMatcher().match(FromTu, classTemplateDecl()); auto *ToD = Import(FromD, Lang_CXX03); auto Pattern = classTemplateDecl( has(cxxRecordDecl(has(friendDecl(has(classTemplateDecl())))))); ASSERT_TRUE(MatchVerifier{}.match(FromD, Pattern)); EXPECT_TRUE(MatchVerifier{}.match(ToD, Pattern)); auto *Class = FirstDeclMatcher().match(ToD, classTemplateDecl()); auto *Friend = FirstDeclMatcher().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 X;" "struct Y { friend class X; };", Lang_CXX03, "input0.cc"); auto *FromD = FirstDeclMatcher().match( FromTU, Pattern); Imported1 = cast(Import(FromD, Lang_CXX03)); } ClassTemplateSpecializationDecl *Imported2; { Decl *FromTU = getTuDecl("template class X;" "template<> class X{};" "struct Z { friend class X; };", Lang_CXX03, "input1.cc"); auto *FromD = FirstDeclMatcher().match( FromTU, Pattern); Imported2 = cast(Import(FromD, Lang_CXX03)); } Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); EXPECT_EQ(DeclCounter().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 friend class F; // The decl context of F is the global namespace. }; )", Lang_CXX03, "input0.cc"); auto *Fwd = FirstDeclMatcher().match( FromTU0, classTemplateDecl(hasName("F"))); auto *Imported0 = cast(Import(Fwd, Lang_CXX03)); Decl *FromTU1 = getTuDecl( R"( template class F {}; )", Lang_CXX03, "input1.cc"); auto *Definition = FirstDeclMatcher().match( FromTU1, classTemplateDecl(hasName("F"))); auto *Imported1 = cast(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(To); EXPECT_TRUE(Imported->getPreviousDecl()); } TEST_P(ImportFriendClasses, ImportOfClassTemplateDefinitionShouldConnectToFwdFriend) { Decl *ToTU = getToTuDecl( R"( class X { class Y; }; class X::Y { template friend class F; // The decl context of F is the global namespace. }; )", Lang_CXX03); auto *ToDecl = FirstDeclMatcher().match( ToTU, classTemplateDecl(hasName("F"))); Decl *FromTU = getTuDecl( R"( template class F {}; )", Lang_CXX03, "input0.cc"); auto *Definition = FirstDeclMatcher().match( FromTU, classTemplateDecl(hasName("F"))); auto *ImportedDef = cast(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 friend class F; // The decl context of F is the global namespace. }; )", Lang_CXX03, "input0.cc"); auto *Fwd = FirstDeclMatcher().match( FromTU0, classTemplateDecl(hasName("F"))); auto *ImportedFwd = cast(Import(Fwd, Lang_CXX03)); Decl *FromTU1 = getTuDecl( R"( template class F {}; )", Lang_CXX03, "input1.cc"); auto *Definition = FirstDeclMatcher().match( FromTU1, classTemplateDecl(hasName("F"))); auto *ImportedDef = cast(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().match(FromTU0, friendDecl()); QualType FT = Friend->getFriendType()->getType(); FT = FromTU0->getASTContext().getCanonicalType(FT); auto *Fwd = cast(FT)->getDecl(); auto *ImportedFwd = Import(Fwd, Lang_CXX03); Decl *FromTU1 = getTuDecl( R"( class F {}; )", Lang_CXX03, "input1.cc"); auto *Definition = FirstDeclMatcher().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().match(ToTu, friendDecl()); auto *ToFriend2 = LastDeclMatcher().match(ToTu, friendDecl()); auto *FromFriend1 = FirstDeclMatcher().match(FromTu, friendDecl()); auto *FromFriend2 = LastDeclMatcher().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().match(ToTu, friendDecl()); auto *ToFriend2 = LastDeclMatcher().match(ToTu, friendDecl()); auto *FromFriend1 = FirstDeclMatcher().match(FromTu, friendDecl()); auto *FromFriend2 = LastDeclMatcher().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 struct X { friend void foo(){} }; )"; TranslationUnitDecl *ToTU = getToTuDecl(Code, Lang_CXX03); auto *ToFoo = FirstDeclMatcher().match( ToTU, functionDecl(hasName("foo"))); TranslationUnitDecl *FromTU = getTuDecl(Code, Lang_CXX03, "input.cc"); auto *FromFoo = FirstDeclMatcher().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 struct S {}; template struct S; inline namespace INS { template struct S {}; template struct S; } } )", Lang_CXX11, "input0.cc"); auto *NS = FirstDeclMatcher().match( TU, namespaceDecl()); auto *Spec = FirstDeclMatcher().match( TU, classTemplateSpecializationDecl()); ASSERT_TRUE(NS->containsDecl(Spec)); NS->removeDecl(Spec); EXPECT_FALSE(NS->containsDecl(Spec)); } TEST_P(DeclContextTest, removeDeclShouldNotFailEvenIfWeHaveExternalVisibleStorage) { Decl *TU = getTuDecl("extern int A; int A;", Lang_CXX03); auto *A0 = FirstDeclMatcher().match(TU, varDecl(hasName("A"))); auto *A1 = LastDeclMatcher().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. // The point here is to have a Vec with only one element, which is not the // one we are going to delete from the DC later. L.setHasExternalDecls(); ASSERT_TRUE(L.getAsVector()); ASSERT_EQ(1u, L.getAsVector()->size()); // 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 int f() { return 0; } void foo() { f(); } )", 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{}.match(FromTU, Pattern)); auto *Foo = FirstDeclMatcher().match( FromTU, functionDecl(hasName("foo"))); ASSERT_TRUE(Import(Foo, Lang_CXX03)); auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); EXPECT_TRUE(MatchVerifier{}.match(ToTU, Pattern)); } TEST_P(ImportFunctionTemplateSpecializations, TUshouldNotContainFunctionTemplateExplicitInstantiation) { Decl *FromTU = getTuDecl( R"( template int f() { return 0; } template int f(); )", Lang_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{}.match(FromTU, Pattern)); ASSERT_TRUE(Import(FirstDeclMatcher().match(FromTU, Instantiation), Lang_CXX03)); auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); EXPECT_TRUE(MatchVerifier{}.match(ToTU, Pattern)); } TEST_P(ImportFunctionTemplateSpecializations, TUshouldContainFunctionTemplateSpecialization) { Decl *FromTU = getTuDecl( R"( template int f() { return 0; } template <> int f() { return 4; } )", Lang_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{}.match(FromTU, Pattern)); ASSERT_TRUE(Import(FirstDeclMatcher().match(FromTU, Specialization), Lang_CXX03)); auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); EXPECT_TRUE(MatchVerifier{}.match(ToTU, Pattern)); } TEST_P(ImportFunctionTemplateSpecializations, FunctionTemplateSpecializationRedeclChain) { Decl *FromTU = getTuDecl( R"( template int f() { return 0; } template <> int f() { return 4; } )", Lang_CXX03, "input0.cc"); auto Spec = functionDecl(hasName("f"), isExplicitTemplateSpecialization(), hasParent(translationUnitDecl())); auto *FromSpecD = FirstDeclMatcher().match(FromTU, Spec); { auto *TU = FromTU; auto *SpecD = FromSpecD; auto *TemplateD = FirstDeclMatcher().match( TU, functionTemplateDecl()); auto *FirstSpecD = *(TemplateD->spec_begin()); ASSERT_EQ(SpecD, FirstSpecD); ASSERT_TRUE(SpecD->getPreviousDecl()); ASSERT_FALSE(cast(SpecD->getPreviousDecl()) ->doesThisDeclarationHaveABody()); } ASSERT_TRUE(Import(FromSpecD, Lang_CXX03)); { auto *TU = ToAST->getASTContext().getTranslationUnitDecl(); auto *SpecD = FirstDeclMatcher().match(TU, Spec); auto *TemplateD = FirstDeclMatcher().match( TU, functionTemplateDecl()); auto *FirstSpecD = *(TemplateD->spec_begin()); EXPECT_EQ(SpecD, FirstSpecD); ASSERT_TRUE(SpecD->getPreviousDecl()); EXPECT_FALSE(cast(SpecD->getPreviousDecl()) ->doesThisDeclarationHaveABody()); } } TEST_P(ImportFunctionTemplateSpecializations, MatchNumberOfFunctionTemplateSpecializations) { Decl *FromTU = getTuDecl( R"( template constexpr int f() { return 0; } template <> constexpr int f() { return 4; } void foo() { static_assert(f() == 0, ""); static_assert(f() == 4, ""); } )", Lang_CXX11, "input0.cc"); auto *FromD = FirstDeclMatcher().match( FromTU, functionDecl(hasName("foo"))); Import(FromD, Lang_CXX11); auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); EXPECT_EQ( DeclCounter().match(FromTU, functionDecl(hasName("f"))), DeclCounter().match(ToTU, functionDecl(hasName("f")))); } TEST_P(ASTImporterOptionSpecificTestBase, ImportShouldNotReportFalseODRErrorWhenRecordIsBeingDefined) { { Decl *FromTU = getTuDecl( R"( template struct B; )", Lang_CXX03, "input0.cc"); auto *FromD = FirstDeclMatcher().match( FromTU, classTemplateDecl(hasName("B"))); Import(FromD, Lang_CXX03); } { Decl *FromTU = getTuDecl( R"( template struct B { void f(); B* b; }; )", Lang_CXX03, "input1.cc"); FunctionDecl *FromD = FirstDeclMatcher().match( FromTU, functionDecl(hasName("f"))); Import(FromD, Lang_CXX03); auto *FromCTD = FirstDeclMatcher().match( FromTU, classTemplateDecl(hasName("B"))); auto *ToCTD = cast(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 struct S { void foo(); }; using U = S; )"; Decl *ToTU = getToTuDecl(Code, Lang_CXX11); auto *ToD = FirstDeclMatcher().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().match(FromTU, typedefNameDecl(hasName("U"))); ASSERT_FALSE(FromD->getUnderlyingType()->isIncompleteType()); // The imported type should be complete. auto *ImportedD = cast(Import(FromD, Lang_CXX11)); EXPECT_FALSE(ImportedD->getUnderlyingType()->isIncompleteType()); } TEST_P(ASTImporterOptionSpecificTestBase, ImportTemplateParameterLists) { auto Code = R"( template int f() { return 0; } template <> int f() { return 4; } )"; Decl *FromTU = getTuDecl(Code, Lang_CXX03); auto *FromD = FirstDeclMatcher().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().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(D)) if (ND->getDeclName() == Name) return ND; } return nullptr; } TEST_P(ASTImporterLookupTableTest, FriendWhichIsnotFoundByNormalLookupShouldBeFoundByImporterSpecificLookup) { auto *Code = R"( template struct X { friend void foo(){} }; )"; TranslationUnitDecl *ToTU = getToTuDecl(Code, Lang_CXX03); auto *X = FirstDeclMatcher().match( ToTU, classTemplateDecl(hasName("X"))); auto *Foo = FirstDeclMatcher().match( ToTU, functionDecl(hasName("foo"))); DeclContext *FooDC = Foo->getDeclContext(); DeclContext *FooLexicalDC = Foo->getLexicalDeclContext(); ASSERT_EQ(cast(FooLexicalDC), X->getTemplatedDecl()); ASSERT_EQ(cast(FooDC), ToTU); DeclarationName FooName = Foo->getDeclName(); // Cannot find in the LookupTable of its DC (TUDecl) SmallVector 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().match(ToTU, recordDecl(hasName("Foo"))); auto *A = FirstDeclMatcher().match(ToTU, recordDecl(hasName("A"))); DeclContext *FooDC = Foo->getDeclContext(); DeclContext *FooLexicalDC = Foo->getLexicalDeclContext(); ASSERT_EQ(cast(FooLexicalDC), A); ASSERT_EQ(cast(FooDC), ToTU); DeclarationName FooName = Foo->getDeclName(); // Cannot find in the LookupTable of its DC (TUDecl). SmallVector 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() .match(ToTU, varDecl(hasName("V"))) ->getDeclName(); auto *A = FirstDeclMatcher().match(ToTU, recordDecl(hasName("A"))); auto *B = FirstDeclMatcher().match(ToTU, recordDecl(hasName("B"))); ASTImporterLookupTable LT(*ToTU); auto Res = LT.lookup(cast(A), VName); ASSERT_EQ(Res.size(), 1u); EXPECT_EQ(*Res.begin(), FirstDeclMatcher().match( ToTU, fieldDecl(hasName("V"), hasParent(recordDecl(hasName("A")))))); Res = LT.lookup(cast(B), VName); ASSERT_EQ(Res.size(), 1u); EXPECT_EQ(*Res.begin(), FirstDeclMatcher().match( ToTU, fieldDecl(hasName("V"), hasParent(recordDecl(hasName("B")))))); Res = LT.lookup(ToTU, VName); ASSERT_EQ(Res.size(), 1u); EXPECT_EQ(*Res.begin(), FirstDeclMatcher().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().match(ToTU, functionDecl()); auto *F2 = LastDeclMatcher().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().match( ToTU, functionDecl(hasOverloadedOperatorName("+"))); auto *FMinus = FirstDeclMatcher().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().match( ToTU, functionDecl(hasOverloadedOperatorName("+"))); Decl *FromTU = getTuDecl( R"( struct X {}; void operator+(X, X); )", Lang_CXX03); auto *FromPlus = FirstDeclMatcher().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().match(ToTU, friendDecl()); const RecordDecl *RD = getRecordDeclOfFriend(FriendD); auto *Y = FirstDeclMatcher().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().match(ToTU, friendDecl()); const RecordDecl *RD = getRecordDeclOfFriend(FriendD); auto *Y = FirstDeclMatcher().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().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 friend class F; }; )", Lang_CXX03); ASTImporterLookupTable LT(*ToTU); auto *F = FirstDeclMatcher().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 class F; template class Y { friend class F; }; )", Lang_CXX03); ASTImporterLookupTable LT(*ToTU); auto *F = FirstDeclMatcher().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 class F; class Y { friend class F; }; )", Lang_CXX03); ASTImporterLookupTable LT(*ToTU); auto *F = FirstDeclMatcher().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().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 struct X { int F; }; void foo() { X xc; } )", Lang_CXX03); ASTImporterLookupTable LT(*ToTU); auto *Template = FirstDeclMatcher().match( ToTU, classTemplateDecl(hasName("X"))); auto *FieldInTemplate = FirstDeclMatcher().match( ToTU, fieldDecl(hasParent(cxxRecordDecl(hasParent(classTemplateDecl()))))); auto *Spec = FirstDeclMatcher().match( ToTU, classTemplateSpecializationDecl(hasName("X"))); FieldDecl *FieldInSpec = *Spec->field_begin(); ASSERT_TRUE(FieldInSpec); DeclarationName Name = FieldInSpec->getDeclName(); auto TemplateDC = cast(Template->getTemplatedDecl()); SmallVector 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(Spec)->getRedeclContext()->localUncachedLookup(Name, FoundDecls); EXPECT_EQ(FoundDecls.size(), 1u); EXPECT_EQ(FoundDecls[0], FieldInSpec); Res = LT.lookup(cast(Spec), Name); ASSERT_EQ(Res.size(), 1u); EXPECT_EQ(*Res.begin(), FieldInSpec); } TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendFunctionTemplateDecl) { TranslationUnitDecl *ToTU = getToTuDecl( R"( class Y { template friend void F(); }; )", Lang_CXX03); ASTImporterLookupTable LT(*ToTU); auto *F = FirstDeclMatcher().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().match( ToTU, cxxRecordDecl(hasName("X"))); auto *FriendD0 = FirstDeclMatcher().match(ToTU, friendDecl()); auto *FriendD1 = LastDeclMatcher().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().match(ToTU, enumDecl(hasName("E"))); auto *A = FirstDeclMatcher().match( ToTU, enumConstantDecl(hasName("A"))); DeclarationName Name = A->getDeclName(); // Redecl context is the TU. ASSERT_EQ(E->getRedeclContext(), ToTU); SmallVector 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().match(ToTU, namespaceDecl(hasName("N"))); auto *A = FirstDeclMatcher().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().match( FromTU, cxxRecordDecl(hasName("X"), unless(isImplicit()))); auto *FromDef = LastDeclMatcher().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(Import(FromDef, Lang_CXX03)); auto *ToFwd = cast_or_null(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 friend void foo(); }; )", Lang_CXX03); auto *Friend = FirstDeclMatcher().match( ToTU, functionTemplateDecl(hasName("foo"))); Decl *FromTU = getTuDecl( R"( template void foo(); )", Lang_CXX03); auto *FromFoo = FirstDeclMatcher().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 &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 class X {}; template <> class X { int a; }; )", Lang_CXX03); TranslationUnitDecl *FromTU = getTuDecl( R"( template class X {}; template <> class X { double b; }; )", Lang_CXX03); auto *FromSpec = FirstDeclMatcher().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().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 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().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().match(ToTU, functionDecl(hasName("foo"))), 0u); ASTImporter *Importer = findFromTU(FromFoo)->Importer.get(); Optional 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().match( FromTU, functionDecl(hasName("f"))); auto *FromDef = LastDeclMatcher().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().match(ToTU, functionDecl(hasName("f"))), 1u); // Match the fwd decl. auto *ToProto = FirstDeclMatcher().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 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().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 OptErr = Importer->getImportDeclErrorIfAny(FromX); ASSERT_TRUE(OptErr); EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct); // An error is set for f(). auto *FromF = FirstDeclMatcher().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().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 class F {}; class X { template friend class F; void error() { )") + ErroneousStmt + R"( } }; }; class B {}; } // NS )", Lang_CXX03, "input0.cc"); auto *FromFRD = FirstDeclMatcher().match( FromTU, cxxRecordDecl(hasName("F"), isDefinition())); auto *FromA = FirstDeclMatcher().match( FromTU, cxxRecordDecl(hasName("A"), isDefinition())); auto *FromB = FirstDeclMatcher().match( FromTU, cxxRecordDecl(hasName("B"), isDefinition())); auto *FromNS = FirstDeclMatcher().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 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().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().match( FromTU, friendDecl())); EXPECT_TRUE(OptErr); // An error is set to the implicit class of A. OptErr = Importer->getImportDeclErrorIfAny(FirstDeclMatcher().match( FromTU, cxxRecordDecl(hasName("A"), isImplicit()))); EXPECT_TRUE(OptErr); // An error is set to the implicit class of X. OptErr = Importer->getImportDeclErrorIfAny(FirstDeclMatcher().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().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 OptErr = Importer->getImportDeclErrorIfAny(FromX); ASSERT_FALSE(OptErr); // An error is set for f(). auto *FromF = FirstDeclMatcher().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().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().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 OptErr = Importer->getImportDeclErrorIfAny(FromX); ASSERT_TRUE(OptErr); EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct); } // ... but the node had been created. auto *ToXDef = FirstDeclMatcher().match( ToTU, cxxRecordDecl(hasName("X"), isDefinition())); // An error is set for "ToXDef" in the shared state. Optional OptErr = SharedStatePtr->getImportDeclErrorIfAny(ToXDef); ASSERT_TRUE(OptErr); EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct); auto *ToXFwd = FirstDeclMatcher().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().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 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().match(FromTU, MatchFooA); auto *FromFooB = FirstDeclMatcher().match(FromTU, MatchFooB); auto *FromFooC = FirstDeclMatcher().match(FromTU, MatchFooC); EXPECT_FALSE(Import(FromFooA, Lang_CXX11)); ASTImporter *Importer = findFromTU(FromFooA)->Importer.get(); auto CheckError = [&Importer](Decl *FromD) { Optional 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().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 void f(F L = [](){}) {} )", Lang_CXX11, "input0.cc"); auto Pattern = lambdaExpr(); CXXRecordDecl *FromL = FirstDeclMatcher().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().match( FromTU, functionDecl(hasName("f"))); FunctionDecl *ToF = Import(FromF, Lang_CXX11); EXPECT_TRUE(ToF); } TEST_P(ASTImporterOptionSpecificTestBase, ImportExistingFriendClassTemplateDef) { auto Code = R"( template struct Base { template friend struct Class; }; template struct Class { }; )"; TranslationUnitDecl *ToTU = getToTuDecl(Code, Lang_CXX03); TranslationUnitDecl *FromTU = getTuDecl(Code, Lang_CXX03, "input.cc"); auto *ToClassProto = FirstDeclMatcher().match( ToTU, classTemplateDecl(hasName("Class"))); auto *ToClassDef = LastDeclMatcher().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().match( FromTU, classTemplateDecl(hasName("Class"))); auto *FromClassDef = LastDeclMatcher().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 &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().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().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().match( ToTU, typedefDecl(hasName(TypeName))); QualType ToType = ToTypedef->getUnderlyingType(); auto *FromTypedef = FirstDeclMatcher().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().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().match( FromTU, cxxRecordDecl(hasName("X"))); auto *ImportedX = Import(FromX, Lang_CXX11); auto *Constr1 = FirstDeclMatcher().match( ImportedX, cxxConstructorDecl(hasName("X"), unless(isImplicit()))); auto *Constr2 = LastDeclMatcher().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{ "-target", "aarch64-linux-gnu"}), ); INSTANTIATE_TEST_CASE_P(ParameterizedTests, DeclContextTest, ::testing::Values(std::vector()), ); INSTANTIATE_TEST_CASE_P(ParameterizedTests, CanonicalRedeclChain, ::testing::Values(std::vector()), ); TEST_P(ASTImporterOptionSpecificTestBase, LambdasAreDifferentiated) { Decl *FromTU = getTuDecl( R"( void f() { auto L0 = [](){}; auto L1 = [](){}; } )", Lang_CXX11, "input0.cc"); auto Pattern = lambdaExpr(); CXXRecordDecl *FromL0 = FirstDeclMatcher().match(FromTU, Pattern)->getLambdaClass(); CXXRecordDecl *FromL1 = LastDeclMatcher().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 void f(F0 L0 = [](){}, F1 L1 = [](){}) {} )", Lang_CXX11, "input0.cc"); auto Pattern = cxxRecordDecl(isLambda()); CXXRecordDecl *FromL0 = FirstDeclMatcher().match(FromTU, Pattern); CXXRecordDecl *FromL1 = LastDeclMatcher().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 void f(F0 L0 = LAMBDA, F1 L1 = LAMBDA) {} )", Lang_CXX11, "input0.cc"); auto Pattern = cxxRecordDecl(isLambda()); CXXRecordDecl *FromL0 = FirstDeclMatcher().match(FromTU, Pattern); CXXRecordDecl *FromL1 = LastDeclMatcher().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().match( FromTU, functionDecl(hasName("f"))); // We have only one lambda class. ASSERT_EQ( DeclCounter().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().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().match( FromTU, functionDecl(hasName("f"))); // We have two lambda classes. ASSERT_EQ( DeclCounter().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().match(ToTU, cxxRecordDecl(isLambda())), 2u); } TEST_P(ASTImporterOptionSpecificTestBase, ImplicitlyDeclareSelf) { Decl *FromTU = getTuDecl(R"( __attribute__((objc_root_class)) @interface Root @end @interface C : Root -(void)method; @end @implementation C -(void)method {} @end )", Lang_OBJCXX, "input.mm"); auto *FromMethod = LastDeclMatcher().match( FromTU, namedDecl(hasName("method"))); ASSERT_TRUE(FromMethod); auto ToMethod = Import(FromMethod, Lang_OBJCXX); ASSERT_TRUE(ToMethod); // Both methods should have their implicit parameters. EXPECT_TRUE(FromMethod->getSelfDecl() != nullptr); EXPECT_TRUE(ToMethod->getSelfDecl() != nullptr); } 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(dur); }; )", Lang_CXX14, "input0.cc"); CXXMethodDecl *From = FirstDeclMatcher().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().match(FromTU, typeAliasDecl()); QualType TT = Ctx.getTypedefType(FromTA); const FunctionProtoType *FPT = cast(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(To->getReturnType())); } TEST_P(ImportAutoFunctions, ReturnWithStructDeclaredInside) { Decl *FromTU = getTuDecl( R"( auto foo() { struct X {}; return X(); } )", Lang_CXX14, "input0.cc"); FunctionDecl *From = FirstDeclMatcher().match(FromTU, functionDecl()); FunctionDecl *To = Import(From, Lang_CXX14); EXPECT_TRUE(To); EXPECT_TRUE(isa(To->getReturnType())); } TEST_P(ImportAutoFunctions, ReturnWithStructDeclaredInside2) { Decl *FromTU = getTuDecl( R"( auto foo() { struct X {}; return X(); } )", Lang_CXX14, "input0.cc"); FunctionDecl *From = FirstDeclMatcher().match(FromTU, functionDecl()); // This time import the type directly. QualType ToT = ImportType(From->getType(), From, Lang_CXX14); const FunctionProtoType *FPT = cast(ToT); EXPECT_TRUE(isa(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().match(FromTU, functionDecl()); FunctionDecl *To = Import(From, Lang_CXX14); EXPECT_TRUE(To); EXPECT_TRUE(isa(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().match(FromTU, functionDecl()); FunctionDecl *To = Import(From, Lang_CXX14); EXPECT_TRUE(To); EXPECT_TRUE(isa(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().match( FromTU, functionDecl(hasName("f"))); FunctionDecl *To = Import(From, Lang_CXX17); EXPECT_TRUE(To); EXPECT_TRUE(isa(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().match( FromTU, functionDecl(hasName("f"))); FunctionDecl *To = Import(From, Lang_CXX17); EXPECT_TRUE(To); EXPECT_TRUE(isa(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().match( FromTU, functionDecl(hasName("f"))); FunctionDecl *To = Import(From, Lang_CXX17); EXPECT_TRUE(To); EXPECT_TRUE(isa(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().match( FromTU, functionDecl(hasName("f"))); FunctionDecl *To = Import(From, Lang_CXX17); EXPECT_TRUE(To); EXPECT_TRUE(isa(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().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().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().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(Contents.size()), 0); llvm::SmallVector Buffer; Buffer.append(Contents.begin(), Contents.end()); auto FileContents = std::make_unique(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().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().match( FromTU, cxxRecordDecl(hasName("S"), unless(isImplicit()))); ASSERT_TRUE(FromD); auto *ToD = Import(FromD, Lang_CXX11); ASSERT_TRUE(ToD); auto *FromAttr = FromD->getAttr(); auto *ToAttr = ToD->getAttr(); 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().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().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(); auto *ToAttr = ToD->getAttr(); 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 auto ExtendWithOptions(const T &Values, const std::vector &Args) { auto Copy = Values; for (std::vector &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 &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 &CompletedTags; SourceWithCompletedTagList(std::vector &CompletedTags) : CompletedTags(CompletedTags) {} void CompleteType(TagDecl *Tag) override { auto *Record = cast(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 CompletedTags; IntrusiveRefCntPtr 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 struct A { A(T); }; A a{(int)0}; )", Lang_CXX17, "input.cc"); auto *FromD = FirstDeclMatcher().match( FromTU, cxxDeductionGuideDecl(hasParameter(0, hasType(asString("A"))))); 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 struct A { A(T); }; template explicit A(T) -> A; A a{(int)0}; // calls A::A(float) )", Lang_CXX17, "input.cc"); auto *FromD = FirstDeclMatcher().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 struct A { typedef T U; A(U); }; A a{(int)0}; )", Lang_CXX17, "input.cc"); auto *FromD = FirstDeclMatcher().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 struct A { A(U, T); }; A a{(int)0, (int)0}; )", Lang_CXX17, "input.cc"); auto *Guide = FirstDeclMatcher().match( TU, cxxDeductionGuideDecl()); auto *Typedef = FirstDeclMatcher().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()->getDecl(), Typedef); } TEST_P(CTAD, DeductionGuideShouldReferToANonLocalTypedefInParamPtr) { Decl *TU = getTuDecl( R"( typedef int U; template struct A { A(U*, T); }; A a{(int*)0, (int)0}; )", Lang_CXX17, "input.cc"); auto *Guide = FirstDeclMatcher().match( TU, cxxDeductionGuideDecl()); auto *Typedef = FirstDeclMatcher().match( TU, typedefNameDecl(hasName("U"))); ParmVarDecl *Param = Guide->getParamDecl(0); EXPECT_EQ(Param->getType() ->getAs() ->getPointeeType() ->getAs() ->getDecl(), Typedef); } TEST_P(CTAD, DeductionGuideShouldCopyALocalTypedef) { Decl *TU = getTuDecl( R"( template struct A { typedef T U; A(U, T); }; A a{(int)0, (int)0}; )", Lang_CXX17, "input.cc"); auto *Guide = FirstDeclMatcher().match( TU, cxxDeductionGuideDecl()); auto *Typedef = FirstDeclMatcher().match( TU, typedefNameDecl(hasName("U"))); ParmVarDecl *Param = Guide->getParamDecl(0); EXPECT_NE(Param->getType()->getAs()->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().match(TU, typedefDecl(hasName("X"))); auto *ToD = Import(FromD, Lang_CXX17); ASSERT_TRUE(ToD); ASSERT_EQ(ToD->getAttrs().size(), 1U); auto *ToAttr = dyn_cast(ToD->getAttrs()[0]); ASSERT_TRUE(ToAttr); EXPECT_EQ(ToAttr->getAnnotation(), "A"); } TEST_P(ASTImporterOptionSpecificTestBase, ImportOfTemplatedDeclWhenPreviousDeclHasNoDescribedTemplateSet) { Decl *FromTU = getTuDecl( R"( namespace std { template class basic_stringbuf; } namespace std { class char_traits; template class basic_stringbuf; } namespace std { template class basic_stringbuf {}; } )", Lang_CXX11); auto *From1 = FirstDeclMatcher().match( FromTU, classTemplateDecl(hasName("basic_stringbuf"), unless(isImplicit()))); auto *To1 = cast_or_null(Import(From1, Lang_CXX11)); EXPECT_TRUE(To1); auto *From2 = LastDeclMatcher().match( FromTU, classTemplateDecl(hasName("basic_stringbuf"), unless(isImplicit()))); auto *To2 = cast_or_null(Import(From2, Lang_CXX11)); EXPECT_TRUE(To2); } INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterLookupTableTest, DefaultTestValuesForRunOptions, ); INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportPath, ::testing::Values(std::vector()), ); INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportExpr, DefaultTestValuesForRunOptions, ); INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFixedPointExpr, ExtendWithOptions(DefaultTestArrayForRunOptions, std::vector{ "-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