| //===- unittest/AST/ASTImporterTest.cpp - AST node import test ------------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Tests for the correct import of AST nodes from one AST context to another. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/AST/ASTContext.h" |
| #include "clang/AST/ASTImporter.h" |
| #include "MatchVerifier.h" |
| #include "clang/ASTMatchers/ASTMatchFinder.h" |
| #include "clang/ASTMatchers/ASTMatchers.h" |
| #include "clang/Tooling/Tooling.h" |
| #include "gtest/gtest.h" |
| |
| namespace clang { |
| namespace ast_matchers { |
| |
| typedef std::vector<std::string> StringVector; |
| |
| void getLangArgs(Language Lang, StringVector &Args) { |
| switch (Lang) { |
| case Lang_C: |
| Args.insert(Args.end(), { "-x", "c", "-std=c99" }); |
| break; |
| case Lang_C89: |
| Args.insert(Args.end(), { "-x", "c", "-std=c89" }); |
| break; |
| case Lang_CXX: |
| Args.push_back("-std=c++98"); |
| break; |
| case Lang_CXX11: |
| Args.push_back("-std=c++11"); |
| break; |
| case Lang_OpenCL: |
| case Lang_OBJCXX: |
| break; |
| } |
| } |
| |
| template<typename NodeType, typename MatcherType> |
| testing::AssertionResult |
| testImport(const std::string &FromCode, Language FromLang, |
| const std::string &ToCode, Language ToLang, |
| MatchVerifier<NodeType> &Verifier, |
| const MatcherType &AMatcher) { |
| StringVector FromArgs, ToArgs; |
| getLangArgs(FromLang, FromArgs); |
| getLangArgs(ToLang, ToArgs); |
| |
| const char *const InputFileName = "input.cc"; |
| const char *const OutputFileName = "output.cc"; |
| |
| std::unique_ptr<ASTUnit> |
| FromAST = tooling::buildASTFromCodeWithArgs( |
| FromCode, FromArgs, InputFileName), |
| ToAST = tooling::buildASTFromCodeWithArgs(ToCode, ToArgs, OutputFileName); |
| |
| ASTContext &FromCtx = FromAST->getASTContext(), |
| &ToCtx = ToAST->getASTContext(); |
| |
| // Add input.cc to virtual file system so importer can 'find' it |
| // while importing SourceLocations. |
| vfs::OverlayFileSystem *OFS = static_cast<vfs::OverlayFileSystem *>( |
| ToCtx.getSourceManager().getFileManager().getVirtualFileSystem().get()); |
| vfs::InMemoryFileSystem *MFS = static_cast<vfs::InMemoryFileSystem *>( |
| OFS->overlays_begin()->get()); |
| MFS->addFile(InputFileName, 0, |
| llvm::MemoryBuffer::getMemBuffer(FromCode.c_str())); |
| |
| ASTImporter Importer(ToCtx, ToAST->getFileManager(), |
| FromCtx, FromAST->getFileManager(), false); |
| |
| IdentifierInfo *ImportedII = &FromCtx.Idents.get("declToImport"); |
| assert(ImportedII && "Declaration with 'declToImport' name" |
| "should be specified in test!"); |
| DeclarationName ImportDeclName(ImportedII); |
| SmallVector<NamedDecl *, 4> FoundDecls; |
| FromCtx.getTranslationUnitDecl()->localUncachedLookup( |
| ImportDeclName, FoundDecls); |
| |
| if (FoundDecls.size() != 1) |
| return testing::AssertionFailure() << "Multiple declarations were found!"; |
| |
| auto Imported = Importer.Import(*FoundDecls.begin()); |
| if (!Imported) |
| return testing::AssertionFailure() << "Import failed, nullptr returned!"; |
| |
| // This should dump source locations and assert if some source locations |
| // were not imported |
| SmallString<1024> ImportChecker; |
| llvm::raw_svector_ostream ToNothing(ImportChecker); |
| ToCtx.getTranslationUnitDecl()->print(ToNothing); |
| |
| return Verifier.match(Imported, AMatcher); |
| } |
| |
| TEST(ImportExpr, ImportStringLiteral) { |
| MatchVerifier<Decl> Verifier; |
| EXPECT_TRUE(testImport("void declToImport() { \"foo\"; }", |
| Lang_CXX, "", Lang_CXX, Verifier, |
| functionDecl( |
| hasBody( |
| compoundStmt( |
| has( |
| stringLiteral( |
| hasType( |
| asString("const char [4]"))))))))); |
| EXPECT_TRUE(testImport("void declToImport() { L\"foo\"; }", |
| Lang_CXX, "", Lang_CXX, Verifier, |
| functionDecl( |
| hasBody( |
| compoundStmt( |
| has( |
| stringLiteral( |
| hasType( |
| asString("const wchar_t [4]"))))))))); |
| EXPECT_TRUE(testImport("void declToImport() { \"foo\" \"bar\"; }", |
| Lang_CXX, "", Lang_CXX, Verifier, |
| functionDecl( |
| hasBody( |
| compoundStmt( |
| has( |
| stringLiteral( |
| hasType( |
| asString("const char [7]"))))))))); |
| } |
| |
| TEST(ImportExpr, ImportGNUNullExpr) { |
| MatchVerifier<Decl> Verifier; |
| EXPECT_TRUE(testImport("void declToImport() { __null; }", |
| Lang_CXX, "", Lang_CXX, Verifier, |
| functionDecl( |
| hasBody( |
| compoundStmt( |
| has( |
| gnuNullExpr( |
| hasType(isInteger())))))))); |
| } |
| |
| TEST(ImportExpr, ImportCXXNullPtrLiteralExpr) { |
| MatchVerifier<Decl> Verifier; |
| EXPECT_TRUE(testImport("void declToImport() { nullptr; }", |
| Lang_CXX11, "", Lang_CXX11, Verifier, |
| functionDecl( |
| hasBody( |
| compoundStmt( |
| has( |
| cxxNullPtrLiteralExpr())))))); |
| } |
| |
| |
| TEST(ImportExpr, ImportFloatinglLiteralExpr) { |
| MatchVerifier<Decl> Verifier; |
| EXPECT_TRUE(testImport("void declToImport() { 1.0; }", |
| Lang_CXX, "", Lang_CXX, Verifier, |
| functionDecl( |
| hasBody( |
| compoundStmt( |
| has( |
| floatLiteral( |
| equals(1.0), |
| hasType(asString("double"))))))))); |
| EXPECT_TRUE(testImport("void declToImport() { 1.0e-5f; }", |
| Lang_CXX, "", Lang_CXX, Verifier, |
| functionDecl( |
| hasBody( |
| compoundStmt( |
| has( |
| floatLiteral( |
| equals(1.0e-5f), |
| hasType(asString("float"))))))))); |
| } |
| |
| TEST(ImportExpr, ImportCompoundLiteralExpr) { |
| MatchVerifier<Decl> Verifier; |
| EXPECT_TRUE( |
| testImport( |
| "void declToImport() {" |
| " struct s { int x; long y; unsigned z; }; " |
| " (struct s){ 42, 0L, 1U }; }", |
| Lang_CXX, "", Lang_CXX, Verifier, |
| functionDecl( |
| hasBody( |
| compoundStmt( |
| has( |
| 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(ImportExpr, ImportCXXThisExpr) { |
| MatchVerifier<Decl> Verifier; |
| EXPECT_TRUE( |
| testImport("class declToImport { void f() { this; } };", |
| Lang_CXX, "", Lang_CXX, Verifier, |
| cxxRecordDecl( |
| hasMethod( |
| hasBody( |
| compoundStmt( |
| has( |
| cxxThisExpr( |
| hasType( |
| asString("class declToImport *")))))))))); |
| } |
| |
| TEST(ImportExpr, ImportAtomicExpr) { |
| MatchVerifier<Decl> Verifier; |
| EXPECT_TRUE(testImport( |
| "void declToImport() { int *ptr; __atomic_load_n(ptr, 1); }", Lang_CXX, |
| "", Lang_CXX, Verifier, |
| functionDecl(hasBody(compoundStmt(has(atomicExpr( |
| has(ignoringParenImpCasts( |
| declRefExpr(hasDeclaration(varDecl(hasName("ptr"))), |
| hasType(asString("int *"))))), |
| has(integerLiteral(equals(1), hasType(asString("int"))))))))))); |
| } |
| |
| TEST(ImportExpr, ImportLabelDeclAndAddrLabelExpr) { |
| MatchVerifier<Decl> Verifier; |
| EXPECT_TRUE( |
| testImport( |
| "void declToImport() { loop: goto loop; &&loop; }", |
| Lang_CXX, "", Lang_CXX, Verifier, |
| functionDecl( |
| hasBody( |
| compoundStmt( |
| has(labelStmt(hasDeclaration(labelDecl(hasName("loop"))))), |
| has(addrLabelExpr(hasDeclaration(labelDecl(hasName("loop"))))) |
| ))))); |
| } |
| |
| AST_MATCHER_P(TemplateDecl, hasTemplateDecl, |
| internal::Matcher<NamedDecl>, InnerMatcher) { |
| const NamedDecl *Template = Node.getTemplatedDecl(); |
| return Template && InnerMatcher.matches(*Template, Finder, Builder); |
| } |
| |
| TEST(ImportExpr, ImportParenListExpr) { |
| MatchVerifier<Decl> Verifier; |
| EXPECT_TRUE( |
| testImport( |
| "template<typename T> class dummy { void f() { dummy X(*this); } };" |
| "typedef dummy<int> declToImport;" |
| "template class dummy<int>;", |
| Lang_CXX, "", Lang_CXX, Verifier, |
| typedefDecl( |
| hasType( |
| templateSpecializationType( |
| hasDeclaration( |
| classTemplateDecl( |
| hasTemplateDecl( |
| cxxRecordDecl( |
| hasMethod( |
| allOf( |
| hasName("f"), |
| hasBody( |
| compoundStmt( |
| has( |
| declStmt( |
| hasSingleDecl( |
| varDecl( |
| hasInitializer( |
| parenListExpr( |
| has( |
| unaryOperator( |
| hasOperatorName("*"), |
| hasUnaryOperand(cxxThisExpr()) |
| ))))))))))))))))))))); |
| } |
| |
| TEST(ImportExpr, ImportStmtExpr) { |
| MatchVerifier<Decl> Verifier; |
| // NOTE: has() ignores implicit casts, using hasDescendant() to match it |
| EXPECT_TRUE( |
| testImport( |
| "void declToImport() { int b; int a = b ?: 1; int C = ({int X=4; X;}); }", |
| Lang_CXX, "", Lang_CXX, Verifier, |
| functionDecl( |
| hasBody( |
| compoundStmt( |
| has( |
| declStmt( |
| hasSingleDecl( |
| varDecl( |
| hasName("C"), |
| hasType(asString("int")), |
| hasInitializer( |
| stmtExpr( |
| hasAnySubstatement( |
| declStmt( |
| hasSingleDecl( |
| varDecl( |
| hasName("X"), |
| hasType(asString("int")), |
| hasInitializer( |
| integerLiteral(equals(4))))))), |
| hasDescendant( |
| implicitCastExpr() |
| )))))))))))); |
| } |
| |
| TEST(ImportExpr, ImportConditionalOperator) { |
| MatchVerifier<Decl> Verifier; |
| EXPECT_TRUE( |
| testImport( |
| "void declToImport() { true ? 1 : -5; }", |
| Lang_CXX, "", Lang_CXX, Verifier, |
| functionDecl( |
| hasBody( |
| compoundStmt( |
| has( |
| conditionalOperator( |
| hasCondition(cxxBoolLiteral(equals(true))), |
| hasTrueExpression(integerLiteral(equals(1))), |
| hasFalseExpression( |
| unaryOperator(hasUnaryOperand(integerLiteral(equals(5)))) |
| )))))))); |
| } |
| |
| TEST(ImportExpr, ImportBinaryConditionalOperator) { |
| MatchVerifier<Decl> Verifier; |
| EXPECT_TRUE( |
| testImport( |
| "void declToImport() { 1 ?: -5; }", |
| Lang_CXX, "", Lang_CXX, Verifier, |
| functionDecl( |
| hasBody( |
| compoundStmt( |
| has( |
| 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(ImportExpr, ImportDesignatedInitExpr) { |
| MatchVerifier<Decl> Verifier; |
| EXPECT_TRUE(testImport("void declToImport() {" |
| " struct point { double x; double y; };" |
| " struct point ptarray[10] = " |
| "{ [2].y = 1.0, [2].x = 2.0, [0].x = 1.0 }; }", |
| Lang_C, "", Lang_C, Verifier, |
| functionDecl( |
| hasBody( |
| compoundStmt( |
| has( |
| declStmt( |
| hasSingleDecl( |
| varDecl( |
| hasInitializer( |
| initListExpr( |
| hasSyntacticForm( |
| initListExpr( |
| has( |
| designatedInitExpr( |
| designatorCountIs(2), |
| has(floatLiteral( |
| equals(1.0))), |
| has(integerLiteral( |
| equals(2))))), |
| has( |
| designatedInitExpr( |
| designatorCountIs(2), |
| has(floatLiteral( |
| equals(2.0))), |
| has(integerLiteral( |
| equals(2))))), |
| has( |
| designatedInitExpr( |
| designatorCountIs(2), |
| has(floatLiteral( |
| equals(1.0))), |
| has(integerLiteral( |
| equals(0))))) |
| ))))))))))))); |
| } |
| |
| |
| TEST(ImportExpr, ImportPredefinedExpr) { |
| MatchVerifier<Decl> Verifier; |
| // __func__ expands as StringLiteral("declToImport") |
| EXPECT_TRUE(testImport("void declToImport() { __func__; }", |
| Lang_CXX, "", Lang_CXX, Verifier, |
| functionDecl( |
| hasBody( |
| compoundStmt( |
| has( |
| predefinedExpr( |
| hasType( |
| asString("const char [13]")), |
| has( |
| stringLiteral( |
| hasType( |
| asString("const char [13]"))))))))))); |
| } |
| |
| TEST(ImportExpr, ImportInitListExpr) { |
| MatchVerifier<Decl> Verifier; |
| EXPECT_TRUE( |
| testImport( |
| "void declToImport() {" |
| " struct point { double x; double y; };" |
| " point ptarray[10] = { [2].y = 1.0, [2].x = 2.0," |
| " [0].x = 1.0 }; }", |
| Lang_CXX, "", Lang_CXX, Verifier, |
| functionDecl( |
| hasBody( |
| compoundStmt( |
| has( |
| declStmt( |
| hasSingleDecl( |
| varDecl( |
| hasInitializer( |
| 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))))) |
| ))))))))))); |
| } |
| |
| |
| } // end namespace ast_matchers |
| } // end namespace clang |