| //===- unittests/AST/ASTTraverserTest.h------------------------------------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/AST/ASTContext.h" |
| #include "clang/AST/ASTNodeTraverser.h" |
| #include "clang/AST/TextNodeDumper.h" |
| #include "clang/ASTMatchers/ASTMatchFinder.h" |
| #include "clang/ASTMatchers/ASTMatchers.h" |
| #include "clang/Tooling/Tooling.h" |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| |
| using namespace clang::tooling; |
| using namespace clang::ast_matchers; |
| |
| namespace clang { |
| |
| class NodeTreePrinter : public TextTreeStructure { |
| llvm::raw_ostream &OS; |
| |
| public: |
| NodeTreePrinter(llvm::raw_ostream &OS) |
| : TextTreeStructure(OS, /* showColors */ false), OS(OS) {} |
| |
| void Visit(const Decl *D) { |
| OS << D->getDeclKindName() << "Decl"; |
| if (auto *ND = dyn_cast<NamedDecl>(D)) { |
| OS << " '" << ND->getDeclName() << "'"; |
| } |
| } |
| |
| void Visit(const Stmt *S) { |
| if (!S) { |
| OS << "<<<NULL>>>"; |
| return; |
| } |
| OS << S->getStmtClassName(); |
| if (auto *E = dyn_cast<DeclRefExpr>(S)) { |
| OS << " '" << E->getDecl()->getDeclName() << "'"; |
| } |
| } |
| |
| void Visit(QualType QT) { |
| OS << "QualType " << QT.split().Quals.getAsString(); |
| } |
| |
| void Visit(const Type *T) { OS << T->getTypeClassName() << "Type"; } |
| |
| void Visit(const comments::Comment *C, const comments::FullComment *FC) { |
| OS << C->getCommentKindName(); |
| } |
| |
| void Visit(const CXXCtorInitializer *Init) { |
| OS << "CXXCtorInitializer"; |
| if (const auto *F = Init->getAnyMember()) { |
| OS << " '" << F->getNameAsString() << "'"; |
| } else if (auto const *TSI = Init->getTypeSourceInfo()) { |
| OS << " '" << TSI->getType() << "'"; |
| } |
| } |
| |
| void Visit(const Attr *A) { |
| switch (A->getKind()) { |
| #define ATTR(X) \ |
| case attr::X: \ |
| OS << #X; \ |
| break; |
| #include "clang/Basic/AttrList.inc" |
| } |
| OS << "Attr"; |
| } |
| |
| void Visit(const OMPClause *C) { OS << "OMPClause"; } |
| void Visit(const TemplateArgument &A, SourceRange R = {}, |
| const Decl *From = nullptr, const char *Label = nullptr) { |
| OS << "TemplateArgument"; |
| switch (A.getKind()) { |
| case TemplateArgument::Type: { |
| OS << " type " << A.getAsType(); |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| |
| template <typename... T> void Visit(T...) {} |
| }; |
| |
| class TestASTDumper : public ASTNodeTraverser<TestASTDumper, NodeTreePrinter> { |
| |
| NodeTreePrinter MyNodeRecorder; |
| |
| public: |
| TestASTDumper(llvm::raw_ostream &OS) : MyNodeRecorder(OS) {} |
| NodeTreePrinter &doGetNodeDelegate() { return MyNodeRecorder; } |
| }; |
| |
| template <typename... NodeType> std::string dumpASTString(NodeType &&... N) { |
| std::string Buffer; |
| llvm::raw_string_ostream OS(Buffer); |
| |
| TestASTDumper Dumper(OS); |
| |
| OS << "\n"; |
| |
| Dumper.Visit(std::forward<NodeType &&>(N)...); |
| |
| return Buffer; |
| } |
| |
| template <typename... NodeType> |
| std::string dumpASTString(TraversalKind TK, NodeType &&... N) { |
| std::string Buffer; |
| llvm::raw_string_ostream OS(Buffer); |
| |
| TestASTDumper Dumper(OS); |
| Dumper.SetTraversalKind(TK); |
| |
| OS << "\n"; |
| |
| Dumper.Visit(std::forward<NodeType &&>(N)...); |
| |
| return Buffer; |
| } |
| |
| const FunctionDecl *getFunctionNode(clang::ASTUnit *AST, |
| const std::string &Name) { |
| auto Result = ast_matchers::match(functionDecl(hasName(Name)).bind("fn"), |
| AST->getASTContext()); |
| EXPECT_EQ(Result.size(), 1u); |
| return Result[0].getNodeAs<FunctionDecl>("fn"); |
| } |
| |
| template <typename T> struct Verifier { |
| static void withDynNode(T Node, const std::string &DumpString) { |
| EXPECT_EQ(dumpASTString(DynTypedNode::create(Node)), DumpString); |
| } |
| }; |
| |
| template <typename T> struct Verifier<T *> { |
| static void withDynNode(T *Node, const std::string &DumpString) { |
| EXPECT_EQ(dumpASTString(DynTypedNode::create(*Node)), DumpString); |
| } |
| }; |
| |
| template <typename T> |
| void verifyWithDynNode(T Node, const std::string &DumpString) { |
| EXPECT_EQ(dumpASTString(Node), DumpString); |
| |
| Verifier<T>::withDynNode(Node, DumpString); |
| } |
| |
| TEST(Traverse, Dump) { |
| |
| auto AST = buildASTFromCode(R"cpp( |
| struct A { |
| int m_number; |
| |
| /// CTor |
| A() : m_number(42) {} |
| |
| [[nodiscard]] const int func() { |
| return 42; |
| } |
| |
| }; |
| |
| template<typename T> |
| struct templ |
| { |
| }; |
| |
| template<> |
| struct templ<int> |
| { |
| }; |
| |
| void parmvardecl_attr(struct A __attribute__((address_space(19)))*); |
| |
| )cpp"); |
| |
| const FunctionDecl *Func = getFunctionNode(AST.get(), "func"); |
| |
| verifyWithDynNode(Func, |
| R"cpp( |
| CXXMethodDecl 'func' |
| |-CompoundStmt |
| | `-ReturnStmt |
| | `-IntegerLiteral |
| `-WarnUnusedResultAttr |
| )cpp"); |
| |
| Stmt *Body = Func->getBody(); |
| |
| verifyWithDynNode(Body, |
| R"cpp( |
| CompoundStmt |
| `-ReturnStmt |
| `-IntegerLiteral |
| )cpp"); |
| |
| QualType QT = Func->getType(); |
| |
| verifyWithDynNode(QT, |
| R"cpp( |
| FunctionProtoType |
| `-QualType const |
| `-BuiltinType |
| )cpp"); |
| |
| const FunctionDecl *CTorFunc = getFunctionNode(AST.get(), "A"); |
| |
| verifyWithDynNode(CTorFunc->getType(), |
| R"cpp( |
| FunctionProtoType |
| `-BuiltinType |
| )cpp"); |
| |
| Attr *A = *Func->attr_begin(); |
| |
| { |
| std::string expectedString = R"cpp( |
| WarnUnusedResultAttr |
| )cpp"; |
| |
| EXPECT_EQ(dumpASTString(A), expectedString); |
| } |
| |
| auto *CTor = dyn_cast<CXXConstructorDecl>(CTorFunc); |
| const CXXCtorInitializer *Init = *CTor->init_begin(); |
| |
| verifyWithDynNode(Init, |
| R"cpp( |
| CXXCtorInitializer 'm_number' |
| `-IntegerLiteral |
| )cpp"); |
| |
| const comments::FullComment *Comment = |
| AST->getASTContext().getLocalCommentForDeclUncached(CTorFunc); |
| { |
| std::string expectedString = R"cpp( |
| FullComment |
| `-ParagraphComment |
| `-TextComment |
| )cpp"; |
| EXPECT_EQ(dumpASTString(Comment, Comment), expectedString); |
| } |
| |
| auto Result = ast_matchers::match( |
| classTemplateSpecializationDecl(hasName("templ")).bind("fn"), |
| AST->getASTContext()); |
| EXPECT_EQ(Result.size(), 1u); |
| auto Templ = Result[0].getNodeAs<ClassTemplateSpecializationDecl>("fn"); |
| |
| TemplateArgument TA = Templ->getTemplateArgs()[0]; |
| |
| verifyWithDynNode(TA, |
| R"cpp( |
| TemplateArgument type int |
| `-BuiltinType |
| )cpp"); |
| |
| Func = getFunctionNode(AST.get(), "parmvardecl_attr"); |
| |
| const auto *Parm = Func->getParamDecl(0); |
| const auto TL = Parm->getTypeSourceInfo()->getTypeLoc(); |
| ASSERT_TRUE(TL.getType()->isPointerType()); |
| |
| const auto ATL = TL.getNextTypeLoc().getAs<AttributedTypeLoc>(); |
| const auto *AS = cast<AddressSpaceAttr>(ATL.getAttr()); |
| EXPECT_EQ(toTargetAddressSpace(static_cast<LangAS>(AS->getAddressSpace())), |
| 19u); |
| } |
| |
| TEST(Traverse, IgnoreUnlessSpelledInSourceVars) { |
| |
| auto AST = buildASTFromCodeWithArgs(R"cpp( |
| |
| struct String |
| { |
| String(const char*, int = -1) {} |
| |
| int overloaded() const; |
| int& overloaded(); |
| }; |
| |
| void stringConstruct() |
| { |
| String s = "foo"; |
| s = "bar"; |
| } |
| |
| void overloadCall() |
| { |
| String s = "foo"; |
| (s).overloaded(); |
| } |
| |
| struct C1 {}; |
| struct C2 { operator C1(); }; |
| |
| void conversionOperator() |
| { |
| C2* c2; |
| C1 c1 = (*c2); |
| } |
| |
| template <unsigned alignment> |
| void template_test() { |
| static_assert(alignment, ""); |
| } |
| void actual_template_test() { |
| template_test<4>(); |
| } |
| |
| struct OneParamCtor { |
| explicit OneParamCtor(int); |
| }; |
| struct TwoParamCtor { |
| explicit TwoParamCtor(int, int); |
| }; |
| |
| void varDeclCtors() { |
| { |
| auto var1 = OneParamCtor(5); |
| auto var2 = TwoParamCtor(6, 7); |
| } |
| { |
| OneParamCtor var3(5); |
| TwoParamCtor var4(6, 7); |
| } |
| int i = 0; |
| { |
| auto var5 = OneParamCtor(i); |
| auto var6 = TwoParamCtor(i, 7); |
| } |
| { |
| OneParamCtor var7(i); |
| TwoParamCtor var8(i, 7); |
| } |
| } |
| |
| )cpp", {"-std=c++14"}); |
| |
| { |
| auto FN = |
| ast_matchers::match(functionDecl(hasName("stringConstruct")).bind("fn"), |
| AST->getASTContext()); |
| EXPECT_EQ(FN.size(), 1u); |
| |
| EXPECT_EQ(dumpASTString(TK_AsIs, FN[0].getNodeAs<Decl>("fn")), |
| R"cpp( |
| FunctionDecl 'stringConstruct' |
| `-CompoundStmt |
| |-DeclStmt |
| | `-VarDecl 's' |
| | `-ExprWithCleanups |
| | `-CXXConstructExpr |
| | `-MaterializeTemporaryExpr |
| | `-ImplicitCastExpr |
| | `-CXXConstructExpr |
| | |-ImplicitCastExpr |
| | | `-StringLiteral |
| | `-CXXDefaultArgExpr |
| | `-UnaryOperator |
| | `-IntegerLiteral |
| `-ExprWithCleanups |
| `-CXXOperatorCallExpr |
| |-ImplicitCastExpr |
| | `-DeclRefExpr 'operator=' |
| |-DeclRefExpr 's' |
| `-MaterializeTemporaryExpr |
| `-CXXConstructExpr |
| |-ImplicitCastExpr |
| | `-StringLiteral |
| `-CXXDefaultArgExpr |
| `-UnaryOperator |
| `-IntegerLiteral |
| )cpp"); |
| |
| EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, |
| FN[0].getNodeAs<Decl>("fn")), |
| R"cpp( |
| FunctionDecl 'stringConstruct' |
| `-CompoundStmt |
| |-DeclStmt |
| | `-VarDecl 's' |
| | `-StringLiteral |
| `-CXXOperatorCallExpr |
| |-DeclRefExpr 'operator=' |
| |-DeclRefExpr 's' |
| `-StringLiteral |
| )cpp"); |
| } |
| |
| { |
| auto FN = |
| ast_matchers::match(functionDecl(hasName("overloadCall")).bind("fn"), |
| AST->getASTContext()); |
| EXPECT_EQ(FN.size(), 1u); |
| |
| EXPECT_EQ(dumpASTString(TK_AsIs, FN[0].getNodeAs<Decl>("fn")), |
| R"cpp( |
| FunctionDecl 'overloadCall' |
| `-CompoundStmt |
| |-DeclStmt |
| | `-VarDecl 's' |
| | `-ExprWithCleanups |
| | `-CXXConstructExpr |
| | `-MaterializeTemporaryExpr |
| | `-ImplicitCastExpr |
| | `-CXXConstructExpr |
| | |-ImplicitCastExpr |
| | | `-StringLiteral |
| | `-CXXDefaultArgExpr |
| | `-UnaryOperator |
| | `-IntegerLiteral |
| `-CXXMemberCallExpr |
| `-MemberExpr |
| `-ParenExpr |
| `-DeclRefExpr 's' |
| )cpp"); |
| |
| EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, |
| FN[0].getNodeAs<Decl>("fn")), |
| R"cpp( |
| FunctionDecl 'overloadCall' |
| `-CompoundStmt |
| |-DeclStmt |
| | `-VarDecl 's' |
| | `-StringLiteral |
| `-CXXMemberCallExpr |
| `-MemberExpr |
| `-DeclRefExpr 's' |
| )cpp"); |
| } |
| |
| { |
| auto FN = ast_matchers::match( |
| functionDecl(hasName("conversionOperator"), |
| hasDescendant(varDecl(hasName("c1")).bind("var"))), |
| AST->getASTContext()); |
| EXPECT_EQ(FN.size(), 1u); |
| |
| EXPECT_EQ(dumpASTString(TK_AsIs, FN[0].getNodeAs<Decl>("var")), |
| R"cpp( |
| VarDecl 'c1' |
| `-ExprWithCleanups |
| `-CXXConstructExpr |
| `-MaterializeTemporaryExpr |
| `-ImplicitCastExpr |
| `-CXXMemberCallExpr |
| `-MemberExpr |
| `-ParenExpr |
| `-UnaryOperator |
| `-ImplicitCastExpr |
| `-DeclRefExpr 'c2' |
| )cpp"); |
| |
| EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, |
| FN[0].getNodeAs<Decl>("var")), |
| R"cpp( |
| VarDecl 'c1' |
| `-UnaryOperator |
| `-DeclRefExpr 'c2' |
| )cpp"); |
| } |
| |
| { |
| auto FN = ast_matchers::match( |
| functionDecl(hasName("template_test"), |
| hasDescendant(staticAssertDecl().bind("staticAssert"))), |
| AST->getASTContext()); |
| EXPECT_EQ(FN.size(), 2u); |
| |
| EXPECT_EQ(dumpASTString(TK_AsIs, FN[1].getNodeAs<Decl>("staticAssert")), |
| R"cpp( |
| StaticAssertDecl |
| |-ImplicitCastExpr |
| | `-SubstNonTypeTemplateParmExpr |
| | |-NonTypeTemplateParmDecl 'alignment' |
| | `-IntegerLiteral |
| `-StringLiteral |
| )cpp"); |
| |
| EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, |
| FN[1].getNodeAs<Decl>("staticAssert")), |
| R"cpp( |
| StaticAssertDecl |
| |-IntegerLiteral |
| `-StringLiteral |
| )cpp"); |
| } |
| |
| auto varChecker = [&AST](StringRef varName, StringRef SemanticDump, |
| StringRef SyntacticDump) { |
| auto FN = ast_matchers::match( |
| functionDecl( |
| hasName("varDeclCtors"), |
| forEachDescendant(varDecl(hasName(varName)).bind("varDeclCtor"))), |
| AST->getASTContext()); |
| EXPECT_EQ(FN.size(), 1u); |
| |
| EXPECT_EQ(dumpASTString(TK_AsIs, FN[0].getNodeAs<Decl>("varDeclCtor")), |
| SemanticDump); |
| |
| EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, |
| FN[0].getNodeAs<Decl>("varDeclCtor")), |
| SyntacticDump); |
| }; |
| |
| varChecker("var1", |
| R"cpp( |
| VarDecl 'var1' |
| `-ExprWithCleanups |
| `-CXXConstructExpr |
| `-MaterializeTemporaryExpr |
| `-CXXFunctionalCastExpr |
| `-CXXConstructExpr |
| `-IntegerLiteral |
| )cpp", |
| R"cpp( |
| VarDecl 'var1' |
| `-CXXConstructExpr |
| `-IntegerLiteral |
| )cpp"); |
| |
| varChecker("var2", |
| R"cpp( |
| VarDecl 'var2' |
| `-ExprWithCleanups |
| `-CXXConstructExpr |
| `-MaterializeTemporaryExpr |
| `-CXXTemporaryObjectExpr |
| |-IntegerLiteral |
| `-IntegerLiteral |
| )cpp", |
| R"cpp( |
| VarDecl 'var2' |
| `-CXXTemporaryObjectExpr |
| |-IntegerLiteral |
| `-IntegerLiteral |
| )cpp"); |
| |
| varChecker("var3", |
| R"cpp( |
| VarDecl 'var3' |
| `-CXXConstructExpr |
| `-IntegerLiteral |
| )cpp", |
| R"cpp( |
| VarDecl 'var3' |
| `-CXXConstructExpr |
| `-IntegerLiteral |
| )cpp"); |
| |
| varChecker("var4", |
| R"cpp( |
| VarDecl 'var4' |
| `-CXXConstructExpr |
| |-IntegerLiteral |
| `-IntegerLiteral |
| )cpp", |
| R"cpp( |
| VarDecl 'var4' |
| `-CXXConstructExpr |
| |-IntegerLiteral |
| `-IntegerLiteral |
| )cpp"); |
| |
| varChecker("var5", |
| R"cpp( |
| VarDecl 'var5' |
| `-ExprWithCleanups |
| `-CXXConstructExpr |
| `-MaterializeTemporaryExpr |
| `-CXXFunctionalCastExpr |
| `-CXXConstructExpr |
| `-ImplicitCastExpr |
| `-DeclRefExpr 'i' |
| )cpp", |
| R"cpp( |
| VarDecl 'var5' |
| `-CXXConstructExpr |
| `-DeclRefExpr 'i' |
| )cpp"); |
| |
| varChecker("var6", |
| R"cpp( |
| VarDecl 'var6' |
| `-ExprWithCleanups |
| `-CXXConstructExpr |
| `-MaterializeTemporaryExpr |
| `-CXXTemporaryObjectExpr |
| |-ImplicitCastExpr |
| | `-DeclRefExpr 'i' |
| `-IntegerLiteral |
| )cpp", |
| R"cpp( |
| VarDecl 'var6' |
| `-CXXTemporaryObjectExpr |
| |-DeclRefExpr 'i' |
| `-IntegerLiteral |
| )cpp"); |
| |
| varChecker("var7", |
| R"cpp( |
| VarDecl 'var7' |
| `-CXXConstructExpr |
| `-ImplicitCastExpr |
| `-DeclRefExpr 'i' |
| )cpp", |
| R"cpp( |
| VarDecl 'var7' |
| `-CXXConstructExpr |
| `-DeclRefExpr 'i' |
| )cpp"); |
| |
| varChecker("var8", |
| R"cpp( |
| VarDecl 'var8' |
| `-CXXConstructExpr |
| |-ImplicitCastExpr |
| | `-DeclRefExpr 'i' |
| `-IntegerLiteral |
| )cpp", |
| R"cpp( |
| VarDecl 'var8' |
| `-CXXConstructExpr |
| |-DeclRefExpr 'i' |
| `-IntegerLiteral |
| )cpp"); |
| } |
| |
| TEST(Traverse, IgnoreUnlessSpelledInSourceStructs) { |
| auto AST = buildASTFromCode(R"cpp( |
| |
| struct MyStruct { |
| MyStruct(); |
| MyStruct(int i) { |
| MyStruct(); |
| } |
| ~MyStruct(); |
| }; |
| |
| )cpp"); |
| |
| auto BN = ast_matchers::match( |
| cxxConstructorDecl(hasName("MyStruct"), |
| hasParameter(0, parmVarDecl(hasType(isInteger())))) |
| .bind("ctor"), |
| AST->getASTContext()); |
| EXPECT_EQ(BN.size(), 1u); |
| |
| EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, |
| BN[0].getNodeAs<Decl>("ctor")), |
| R"cpp( |
| CXXConstructorDecl 'MyStruct' |
| |-ParmVarDecl 'i' |
| `-CompoundStmt |
| `-CXXTemporaryObjectExpr |
| )cpp"); |
| |
| EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("ctor")), |
| R"cpp( |
| CXXConstructorDecl 'MyStruct' |
| |-ParmVarDecl 'i' |
| `-CompoundStmt |
| `-ExprWithCleanups |
| `-CXXBindTemporaryExpr |
| `-CXXTemporaryObjectExpr |
| )cpp"); |
| } |
| |
| TEST(Traverse, IgnoreUnlessSpelledInSourceReturnStruct) { |
| |
| auto AST = buildASTFromCode(R"cpp( |
| struct Retval { |
| Retval() {} |
| ~Retval() {} |
| }; |
| |
| Retval someFun(); |
| |
| void foo() |
| { |
| someFun(); |
| } |
| )cpp"); |
| |
| auto BN = ast_matchers::match(functionDecl(hasName("foo")).bind("fn"), |
| AST->getASTContext()); |
| EXPECT_EQ(BN.size(), 1u); |
| |
| EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, |
| BN[0].getNodeAs<Decl>("fn")), |
| R"cpp( |
| FunctionDecl 'foo' |
| `-CompoundStmt |
| `-CallExpr |
| `-DeclRefExpr 'someFun' |
| )cpp"); |
| |
| EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("fn")), |
| R"cpp( |
| FunctionDecl 'foo' |
| `-CompoundStmt |
| `-ExprWithCleanups |
| `-CXXBindTemporaryExpr |
| `-CallExpr |
| `-ImplicitCastExpr |
| `-DeclRefExpr 'someFun' |
| )cpp"); |
| } |
| |
| TEST(Traverse, IgnoreUnlessSpelledInSourceReturns) { |
| |
| auto AST = buildASTFromCodeWithArgs(R"cpp( |
| |
| struct A |
| { |
| }; |
| |
| struct B |
| { |
| B(int); |
| B(A const& a); |
| B(); |
| }; |
| |
| struct C |
| { |
| operator B(); |
| }; |
| |
| B func1() { |
| return 42; |
| } |
| |
| B func2() { |
| return B{42}; |
| } |
| |
| B func3() { |
| return B(42); |
| } |
| |
| B func4() { |
| return B(); |
| } |
| |
| B func5() { |
| return B{}; |
| } |
| |
| B func6() { |
| return C(); |
| } |
| |
| B func7() { |
| return A(); |
| } |
| |
| B func8() { |
| return C{}; |
| } |
| |
| B func9() { |
| return A{}; |
| } |
| |
| B func10() { |
| A a; |
| return a; |
| } |
| |
| B func11() { |
| B b; |
| return b; |
| } |
| |
| B func12() { |
| C c; |
| return c; |
| } |
| |
| )cpp", {"-std=c++14"}); |
| |
| auto getFunctionNode = [&AST](const std::string &name) { |
| auto BN = ast_matchers::match(functionDecl(hasName(name)).bind("fn"), |
| AST->getASTContext()); |
| EXPECT_EQ(BN.size(), 1u); |
| return BN[0].getNodeAs<Decl>("fn"); |
| }; |
| |
| { |
| auto FN = getFunctionNode("func1"); |
| llvm::StringRef Expected = R"cpp( |
| FunctionDecl 'func1' |
| `-CompoundStmt |
| `-ReturnStmt |
| `-ExprWithCleanups |
| `-CXXConstructExpr |
| `-MaterializeTemporaryExpr |
| `-ImplicitCastExpr |
| `-CXXConstructExpr |
| `-IntegerLiteral |
| )cpp"; |
| |
| EXPECT_EQ(dumpASTString(TK_AsIs, FN), Expected); |
| |
| Expected = R"cpp( |
| FunctionDecl 'func1' |
| `-CompoundStmt |
| `-ReturnStmt |
| `-IntegerLiteral |
| )cpp"; |
| EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, FN), Expected); |
| } |
| |
| llvm::StringRef Expected = R"cpp( |
| FunctionDecl 'func2' |
| `-CompoundStmt |
| `-ReturnStmt |
| `-CXXTemporaryObjectExpr |
| `-IntegerLiteral |
| )cpp"; |
| EXPECT_EQ( |
| dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func2")), |
| Expected); |
| |
| Expected = R"cpp( |
| FunctionDecl 'func3' |
| `-CompoundStmt |
| `-ReturnStmt |
| `-CXXConstructExpr |
| `-IntegerLiteral |
| )cpp"; |
| EXPECT_EQ( |
| dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func3")), |
| Expected); |
| |
| Expected = R"cpp( |
| FunctionDecl 'func4' |
| `-CompoundStmt |
| `-ReturnStmt |
| `-CXXTemporaryObjectExpr |
| )cpp"; |
| EXPECT_EQ( |
| dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func4")), |
| Expected); |
| |
| Expected = R"cpp( |
| FunctionDecl 'func5' |
| `-CompoundStmt |
| `-ReturnStmt |
| `-CXXTemporaryObjectExpr |
| )cpp"; |
| EXPECT_EQ( |
| dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func5")), |
| Expected); |
| |
| Expected = R"cpp( |
| FunctionDecl 'func6' |
| `-CompoundStmt |
| `-ReturnStmt |
| `-CXXTemporaryObjectExpr |
| )cpp"; |
| EXPECT_EQ( |
| dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func6")), |
| Expected); |
| |
| Expected = R"cpp( |
| FunctionDecl 'func7' |
| `-CompoundStmt |
| `-ReturnStmt |
| `-CXXTemporaryObjectExpr |
| )cpp"; |
| EXPECT_EQ( |
| dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func7")), |
| Expected); |
| |
| Expected = R"cpp( |
| FunctionDecl 'func8' |
| `-CompoundStmt |
| `-ReturnStmt |
| `-CXXFunctionalCastExpr |
| `-InitListExpr |
| )cpp"; |
| EXPECT_EQ( |
| dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func8")), |
| Expected); |
| |
| Expected = R"cpp( |
| FunctionDecl 'func9' |
| `-CompoundStmt |
| `-ReturnStmt |
| `-CXXFunctionalCastExpr |
| `-InitListExpr |
| )cpp"; |
| EXPECT_EQ( |
| dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func9")), |
| Expected); |
| |
| Expected = R"cpp( |
| FunctionDecl 'func10' |
| `-CompoundStmt |
| |-DeclStmt |
| | `-VarDecl 'a' |
| | `-CXXConstructExpr |
| `-ReturnStmt |
| `-DeclRefExpr 'a' |
| )cpp"; |
| EXPECT_EQ( |
| dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func10")), |
| Expected); |
| |
| Expected = R"cpp( |
| FunctionDecl 'func11' |
| `-CompoundStmt |
| |-DeclStmt |
| | `-VarDecl 'b' |
| | `-CXXConstructExpr |
| `-ReturnStmt |
| `-DeclRefExpr 'b' |
| )cpp"; |
| EXPECT_EQ( |
| dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func11")), |
| Expected); |
| |
| Expected = R"cpp( |
| FunctionDecl 'func12' |
| `-CompoundStmt |
| |-DeclStmt |
| | `-VarDecl 'c' |
| | `-CXXConstructExpr |
| `-ReturnStmt |
| `-DeclRefExpr 'c' |
| )cpp"; |
| EXPECT_EQ( |
| dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func12")), |
| Expected); |
| } |
| |
| TEST(Traverse, LambdaUnlessSpelledInSource) { |
| |
| auto AST = |
| buildASTFromCodeWithArgs(R"cpp( |
| |
| void captures() { |
| int a = 0; |
| int b = 0; |
| int d = 0; |
| int f = 0; |
| |
| [a, &b, c = d, &e = f](int g, int h = 42) {}; |
| } |
| |
| void templated() { |
| int a = 0; |
| [a]<typename T>(T t) {}; |
| } |
| |
| struct SomeStruct { |
| int a = 0; |
| void capture_this() { |
| [this]() {}; |
| } |
| void capture_this_copy() { |
| [self = *this]() {}; |
| } |
| }; |
| )cpp", |
| {"-Wno-unused-value", "-Wno-c++2a-extensions"}); |
| |
| auto getLambdaNode = [&AST](const std::string &name) { |
| auto BN = ast_matchers::match( |
| lambdaExpr(hasAncestor(functionDecl(hasName(name)))).bind("lambda"), |
| AST->getASTContext()); |
| EXPECT_EQ(BN.size(), 1u); |
| return BN[0].getNodeAs<LambdaExpr>("lambda"); |
| }; |
| |
| { |
| auto L = getLambdaNode("captures"); |
| |
| llvm::StringRef Expected = R"cpp( |
| LambdaExpr |
| |-DeclRefExpr 'a' |
| |-DeclRefExpr 'b' |
| |-VarDecl 'c' |
| | `-DeclRefExpr 'd' |
| |-VarDecl 'e' |
| | `-DeclRefExpr 'f' |
| |-ParmVarDecl 'g' |
| |-ParmVarDecl 'h' |
| | `-IntegerLiteral |
| `-CompoundStmt |
| )cpp"; |
| EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, L), Expected); |
| |
| Expected = R"cpp( |
| LambdaExpr |
| |-CXXRecordDecl '' |
| | |-CXXMethodDecl 'operator()' |
| | | |-ParmVarDecl 'g' |
| | | |-ParmVarDecl 'h' |
| | | | `-IntegerLiteral |
| | | `-CompoundStmt |
| | |-FieldDecl '' |
| | |-FieldDecl '' |
| | |-FieldDecl '' |
| | |-FieldDecl '' |
| | `-CXXDestructorDecl '~(lambda at input.cc:9:3)' |
| |-ImplicitCastExpr |
| | `-DeclRefExpr 'a' |
| |-DeclRefExpr 'b' |
| |-ImplicitCastExpr |
| | `-DeclRefExpr 'd' |
| |-DeclRefExpr 'f' |
| `-CompoundStmt |
| )cpp"; |
| EXPECT_EQ(dumpASTString(TK_AsIs, L), Expected); |
| } |
| |
| { |
| auto L = getLambdaNode("templated"); |
| |
| llvm::StringRef Expected = R"cpp( |
| LambdaExpr |
| |-DeclRefExpr 'a' |
| |-TemplateTypeParmDecl 'T' |
| |-ParmVarDecl 't' |
| `-CompoundStmt |
| )cpp"; |
| EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, L), Expected); |
| } |
| |
| { |
| auto L = getLambdaNode("capture_this"); |
| |
| llvm::StringRef Expected = R"cpp( |
| LambdaExpr |
| |-CXXThisExpr |
| `-CompoundStmt |
| )cpp"; |
| EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, L), Expected); |
| } |
| |
| { |
| auto L = getLambdaNode("capture_this_copy"); |
| |
| llvm::StringRef Expected = R"cpp( |
| LambdaExpr |
| |-VarDecl 'self' |
| | `-UnaryOperator |
| | `-CXXThisExpr |
| `-CompoundStmt |
| )cpp"; |
| EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, L), Expected); |
| } |
| } |
| |
| TEST(Traverse, IgnoreUnlessSpelledInSourceImplicit) { |
| { |
| auto AST = buildASTFromCode(R"cpp( |
| int i = 0; |
| )cpp"); |
| const auto *TUDecl = AST->getASTContext().getTranslationUnitDecl(); |
| |
| EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, TUDecl), |
| R"cpp( |
| TranslationUnitDecl |
| `-VarDecl 'i' |
| `-IntegerLiteral |
| )cpp"); |
| } |
| |
| auto AST2 = buildASTFromCodeWithArgs(R"cpp( |
| struct Simple { |
| }; |
| struct Other { |
| }; |
| |
| struct Record : Simple, Other { |
| Record() : Simple(), m_i(42) {} |
| private: |
| int m_i; |
| int m_i2 = 42; |
| Simple m_s; |
| }; |
| |
| struct NonTrivial { |
| NonTrivial() {} |
| NonTrivial(NonTrivial&) {} |
| NonTrivial& operator=(NonTrivial&) { return *this; } |
| |
| ~NonTrivial() {} |
| }; |
| |
| struct ContainsArray { |
| NonTrivial arr[2]; |
| int irr[2]; |
| ContainsArray& operator=(ContainsArray &) = default; |
| }; |
| |
| void copyIt() |
| { |
| ContainsArray ca; |
| ContainsArray ca2; |
| ca2 = ca; |
| } |
| |
| void forLoop() |
| { |
| int arr[2]; |
| for (auto i : arr) |
| { |
| |
| } |
| for (auto& a = arr; auto i : a) |
| { |
| |
| } |
| } |
| |
| struct DefaultedAndDeleted { |
| NonTrivial nt; |
| DefaultedAndDeleted() = default; |
| ~DefaultedAndDeleted() = default; |
| DefaultedAndDeleted(DefaultedAndDeleted &) = default; |
| DefaultedAndDeleted& operator=(DefaultedAndDeleted &) = default; |
| DefaultedAndDeleted(DefaultedAndDeleted &&) = delete; |
| DefaultedAndDeleted& operator=(DefaultedAndDeleted &&) = delete; |
| }; |
| |
| void copyIt2() |
| { |
| DefaultedAndDeleted ca; |
| DefaultedAndDeleted ca2; |
| ca2 = ca; |
| } |
| |
| void hasDefaultArg(int i, int j = 0) |
| { |
| } |
| void callDefaultArg() |
| { |
| hasDefaultArg(42); |
| } |
| |
| void decomposition() |
| { |
| int arr[3]; |
| auto &[f, s, t] = arr; |
| |
| f = 42; |
| } |
| |
| typedef __typeof(sizeof(int)) size_t; |
| |
| struct Pair |
| { |
| int x, y; |
| }; |
| |
| // Note: these utilities are required to force binding to tuple like structure |
| namespace std |
| { |
| template <typename E> |
| struct tuple_size |
| { |
| }; |
| |
| template <> |
| struct tuple_size<Pair> |
| { |
| static constexpr size_t value = 2; |
| }; |
| |
| template <size_t I, class T> |
| struct tuple_element |
| { |
| using type = int; |
| }; |
| |
| }; |
| |
| template <size_t I> |
| int &&get(Pair &&p); |
| |
| void decompTuple() |
| { |
| Pair p{1, 2}; |
| auto [a, b] = p; |
| |
| a = 3; |
| } |
| |
| )cpp", |
| {"-std=c++20"}); |
| |
| { |
| auto BN = ast_matchers::match( |
| cxxRecordDecl(hasName("Record"), unless(isImplicit())).bind("rec"), |
| AST2->getASTContext()); |
| EXPECT_EQ(BN.size(), 1u); |
| |
| EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("rec")), |
| R"cpp( |
| CXXRecordDecl 'Record' |
| |-CXXRecordDecl 'Record' |
| |-CXXConstructorDecl 'Record' |
| | |-CXXCtorInitializer 'Simple' |
| | | `-CXXConstructExpr |
| | |-CXXCtorInitializer 'Other' |
| | | `-CXXConstructExpr |
| | |-CXXCtorInitializer 'm_i' |
| | | `-IntegerLiteral |
| | |-CXXCtorInitializer 'm_i2' |
| | | `-CXXDefaultInitExpr |
| | | `-IntegerLiteral |
| | |-CXXCtorInitializer 'm_s' |
| | | `-CXXConstructExpr |
| | `-CompoundStmt |
| |-AccessSpecDecl |
| |-FieldDecl 'm_i' |
| |-FieldDecl 'm_i2' |
| | `-IntegerLiteral |
| `-FieldDecl 'm_s' |
| )cpp"); |
| |
| EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, |
| BN[0].getNodeAs<Decl>("rec")), |
| R"cpp( |
| CXXRecordDecl 'Record' |
| |-CXXConstructorDecl 'Record' |
| | |-CXXCtorInitializer 'Simple' |
| | | `-CXXConstructExpr |
| | |-CXXCtorInitializer 'm_i' |
| | | `-IntegerLiteral |
| | `-CompoundStmt |
| |-AccessSpecDecl |
| |-FieldDecl 'm_i' |
| |-FieldDecl 'm_i2' |
| | `-IntegerLiteral |
| `-FieldDecl 'm_s' |
| )cpp"); |
| } |
| { |
| auto BN = ast_matchers::match( |
| cxxRecordDecl(hasName("ContainsArray"), unless(isImplicit())) |
| .bind("rec"), |
| AST2->getASTContext()); |
| EXPECT_EQ(BN.size(), 1u); |
| |
| EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("rec")), |
| R"cpp( |
| CXXRecordDecl 'ContainsArray' |
| |-CXXRecordDecl 'ContainsArray' |
| |-FieldDecl 'arr' |
| |-FieldDecl 'irr' |
| |-CXXMethodDecl 'operator=' |
| | |-ParmVarDecl '' |
| | `-CompoundStmt |
| | |-ForStmt |
| | | |-DeclStmt |
| | | | `-VarDecl '__i0' |
| | | | `-IntegerLiteral |
| | | |-<<<NULL>>> |
| | | |-BinaryOperator |
| | | | |-ImplicitCastExpr |
| | | | | `-DeclRefExpr '__i0' |
| | | | `-IntegerLiteral |
| | | |-UnaryOperator |
| | | | `-DeclRefExpr '__i0' |
| | | `-CXXMemberCallExpr |
| | | |-MemberExpr |
| | | | `-ArraySubscriptExpr |
| | | | |-ImplicitCastExpr |
| | | | | `-MemberExpr |
| | | | | `-CXXThisExpr |
| | | | `-ImplicitCastExpr |
| | | | `-DeclRefExpr '__i0' |
| | | `-ArraySubscriptExpr |
| | | |-ImplicitCastExpr |
| | | | `-MemberExpr |
| | | | `-DeclRefExpr '' |
| | | `-ImplicitCastExpr |
| | | `-DeclRefExpr '__i0' |
| | |-CallExpr |
| | | |-ImplicitCastExpr |
| | | | `-DeclRefExpr '__builtin_memcpy' |
| | | |-ImplicitCastExpr |
| | | | `-UnaryOperator |
| | | | `-MemberExpr |
| | | | `-CXXThisExpr |
| | | |-ImplicitCastExpr |
| | | | `-UnaryOperator |
| | | | `-MemberExpr |
| | | | `-DeclRefExpr '' |
| | | `-IntegerLiteral |
| | `-ReturnStmt |
| | `-UnaryOperator |
| | `-CXXThisExpr |
| |-CXXConstructorDecl 'ContainsArray' |
| | `-ParmVarDecl '' |
| |-CXXDestructorDecl '~ContainsArray' |
| | `-CompoundStmt |
| `-CXXConstructorDecl 'ContainsArray' |
| |-CXXCtorInitializer 'arr' |
| | `-CXXConstructExpr |
| `-CompoundStmt |
| )cpp"); |
| |
| EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, |
| BN[0].getNodeAs<Decl>("rec")), |
| R"cpp( |
| CXXRecordDecl 'ContainsArray' |
| |-FieldDecl 'arr' |
| |-FieldDecl 'irr' |
| `-CXXMethodDecl 'operator=' |
| `-ParmVarDecl '' |
| )cpp"); |
| } |
| { |
| auto BN = ast_matchers::match(functionDecl(hasName("forLoop")).bind("func"), |
| AST2->getASTContext()); |
| EXPECT_EQ(BN.size(), 1u); |
| |
| EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("func")), |
| R"cpp( |
| FunctionDecl 'forLoop' |
| `-CompoundStmt |
| |-DeclStmt |
| | `-VarDecl 'arr' |
| |-CXXForRangeStmt |
| | |-<<<NULL>>> |
| | |-DeclStmt |
| | | `-VarDecl '__range1' |
| | | `-DeclRefExpr 'arr' |
| | |-DeclStmt |
| | | `-VarDecl '__begin1' |
| | | `-ImplicitCastExpr |
| | | `-DeclRefExpr '__range1' |
| | |-DeclStmt |
| | | `-VarDecl '__end1' |
| | | `-BinaryOperator |
| | | |-ImplicitCastExpr |
| | | | `-DeclRefExpr '__range1' |
| | | `-IntegerLiteral |
| | |-BinaryOperator |
| | | |-ImplicitCastExpr |
| | | | `-DeclRefExpr '__begin1' |
| | | `-ImplicitCastExpr |
| | | `-DeclRefExpr '__end1' |
| | |-UnaryOperator |
| | | `-DeclRefExpr '__begin1' |
| | |-DeclStmt |
| | | `-VarDecl 'i' |
| | | `-ImplicitCastExpr |
| | | `-UnaryOperator |
| | | `-ImplicitCastExpr |
| | | `-DeclRefExpr '__begin1' |
| | `-CompoundStmt |
| `-CXXForRangeStmt |
| |-DeclStmt |
| | `-VarDecl 'a' |
| | `-DeclRefExpr 'arr' |
| |-DeclStmt |
| | `-VarDecl '__range1' |
| | `-DeclRefExpr 'a' |
| |-DeclStmt |
| | `-VarDecl '__begin1' |
| | `-ImplicitCastExpr |
| | `-DeclRefExpr '__range1' |
| |-DeclStmt |
| | `-VarDecl '__end1' |
| | `-BinaryOperator |
| | |-ImplicitCastExpr |
| | | `-DeclRefExpr '__range1' |
| | `-IntegerLiteral |
| |-BinaryOperator |
| | |-ImplicitCastExpr |
| | | `-DeclRefExpr '__begin1' |
| | `-ImplicitCastExpr |
| | `-DeclRefExpr '__end1' |
| |-UnaryOperator |
| | `-DeclRefExpr '__begin1' |
| |-DeclStmt |
| | `-VarDecl 'i' |
| | `-ImplicitCastExpr |
| | `-UnaryOperator |
| | `-ImplicitCastExpr |
| | `-DeclRefExpr '__begin1' |
| `-CompoundStmt |
| )cpp"); |
| |
| EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, |
| BN[0].getNodeAs<Decl>("func")), |
| R"cpp( |
| FunctionDecl 'forLoop' |
| `-CompoundStmt |
| |-DeclStmt |
| | `-VarDecl 'arr' |
| |-CXXForRangeStmt |
| | |-<<<NULL>>> |
| | |-VarDecl 'i' |
| | |-DeclRefExpr 'arr' |
| | `-CompoundStmt |
| `-CXXForRangeStmt |
| |-DeclStmt |
| | `-VarDecl 'a' |
| | `-DeclRefExpr 'arr' |
| |-VarDecl 'i' |
| |-DeclRefExpr 'a' |
| `-CompoundStmt |
| )cpp"); |
| } |
| { |
| auto BN = ast_matchers::match( |
| cxxRecordDecl(hasName("DefaultedAndDeleted"), unless(isImplicit())) |
| .bind("rec"), |
| AST2->getASTContext()); |
| EXPECT_EQ(BN.size(), 1u); |
| |
| EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("rec")), |
| R"cpp( |
| CXXRecordDecl 'DefaultedAndDeleted' |
| |-CXXRecordDecl 'DefaultedAndDeleted' |
| |-FieldDecl 'nt' |
| |-CXXConstructorDecl 'DefaultedAndDeleted' |
| | |-CXXCtorInitializer 'nt' |
| | | `-CXXConstructExpr |
| | `-CompoundStmt |
| |-CXXDestructorDecl '~DefaultedAndDeleted' |
| | `-CompoundStmt |
| |-CXXConstructorDecl 'DefaultedAndDeleted' |
| | `-ParmVarDecl '' |
| |-CXXMethodDecl 'operator=' |
| | |-ParmVarDecl '' |
| | `-CompoundStmt |
| | |-CXXMemberCallExpr |
| | | |-MemberExpr |
| | | | `-MemberExpr |
| | | | `-CXXThisExpr |
| | | `-MemberExpr |
| | | `-DeclRefExpr '' |
| | `-ReturnStmt |
| | `-UnaryOperator |
| | `-CXXThisExpr |
| |-CXXConstructorDecl 'DefaultedAndDeleted' |
| | `-ParmVarDecl '' |
| `-CXXMethodDecl 'operator=' |
| `-ParmVarDecl '' |
| )cpp"); |
| |
| EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, |
| BN[0].getNodeAs<Decl>("rec")), |
| R"cpp( |
| CXXRecordDecl 'DefaultedAndDeleted' |
| |-FieldDecl 'nt' |
| |-CXXConstructorDecl 'DefaultedAndDeleted' |
| |-CXXDestructorDecl '~DefaultedAndDeleted' |
| |-CXXConstructorDecl 'DefaultedAndDeleted' |
| | `-ParmVarDecl '' |
| |-CXXMethodDecl 'operator=' |
| | `-ParmVarDecl '' |
| |-CXXConstructorDecl 'DefaultedAndDeleted' |
| | `-ParmVarDecl '' |
| `-CXXMethodDecl 'operator=' |
| `-ParmVarDecl '' |
| )cpp"); |
| } |
| { |
| auto BN = ast_matchers::match( |
| callExpr(callee(functionDecl(hasName("hasDefaultArg")))) |
| .bind("funcCall"), |
| AST2->getASTContext()); |
| EXPECT_EQ(BN.size(), 1u); |
| |
| EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<CallExpr>("funcCall")), |
| R"cpp( |
| CallExpr |
| |-ImplicitCastExpr |
| | `-DeclRefExpr 'hasDefaultArg' |
| |-IntegerLiteral |
| `-CXXDefaultArgExpr |
| `-IntegerLiteral |
| )cpp"); |
| EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, |
| BN[0].getNodeAs<CallExpr>("funcCall")), |
| R"cpp( |
| CallExpr |
| |-DeclRefExpr 'hasDefaultArg' |
| `-IntegerLiteral |
| )cpp"); |
| } |
| |
| { |
| auto FN = ast_matchers::match( |
| functionDecl(hasName("decomposition"), |
| hasDescendant(decompositionDecl().bind("decomp"))), |
| AST2->getASTContext()); |
| EXPECT_EQ(FN.size(), 1u); |
| |
| EXPECT_EQ( |
| dumpASTString(TK_AsIs, FN[0].getNodeAs<DecompositionDecl>("decomp")), |
| R"cpp( |
| DecompositionDecl '' |
| |-DeclRefExpr 'arr' |
| |-BindingDecl 'f' |
| | `-ArraySubscriptExpr |
| | |-ImplicitCastExpr |
| | | `-DeclRefExpr '' |
| | `-IntegerLiteral |
| |-BindingDecl 's' |
| | `-ArraySubscriptExpr |
| | |-ImplicitCastExpr |
| | | `-DeclRefExpr '' |
| | `-IntegerLiteral |
| `-BindingDecl 't' |
| `-ArraySubscriptExpr |
| |-ImplicitCastExpr |
| | `-DeclRefExpr '' |
| `-IntegerLiteral |
| )cpp"); |
| |
| EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, |
| FN[0].getNodeAs<DecompositionDecl>("decomp")), |
| R"cpp( |
| DecompositionDecl '' |
| |-DeclRefExpr 'arr' |
| |-BindingDecl 'f' |
| |-BindingDecl 's' |
| `-BindingDecl 't' |
| )cpp"); |
| } |
| |
| { |
| auto FN = ast_matchers::match( |
| functionDecl(hasName("decompTuple"), |
| hasDescendant(decompositionDecl().bind("decomp"))), |
| AST2->getASTContext()); |
| EXPECT_EQ(FN.size(), 1u); |
| |
| EXPECT_EQ( |
| dumpASTString(TK_AsIs, FN[0].getNodeAs<DecompositionDecl>("decomp")), |
| R"cpp( |
| DecompositionDecl '' |
| |-CXXConstructExpr |
| | `-ImplicitCastExpr |
| | `-DeclRefExpr 'p' |
| |-BindingDecl 'a' |
| | |-VarDecl 'a' |
| | | `-CallExpr |
| | | |-ImplicitCastExpr |
| | | | `-DeclRefExpr 'get' |
| | | `-ImplicitCastExpr |
| | | `-DeclRefExpr '' |
| | `-DeclRefExpr 'a' |
| `-BindingDecl 'b' |
| |-VarDecl 'b' |
| | `-CallExpr |
| | |-ImplicitCastExpr |
| | | `-DeclRefExpr 'get' |
| | `-ImplicitCastExpr |
| | `-DeclRefExpr '' |
| `-DeclRefExpr 'b' |
| )cpp"); |
| |
| EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, |
| FN[0].getNodeAs<DecompositionDecl>("decomp")), |
| R"cpp( |
| DecompositionDecl '' |
| |-DeclRefExpr 'p' |
| |-BindingDecl 'a' |
| `-BindingDecl 'b' |
| )cpp"); |
| } |
| } |
| |
| TEST(Traverse, IgnoreUnlessSpelledInSourceTemplateInstantiations) { |
| |
| auto AST = buildASTFromCode(R"cpp( |
| template<typename T> |
| struct TemplStruct { |
| TemplStruct() {} |
| ~TemplStruct() {} |
| |
| private: |
| T m_t; |
| }; |
| |
| template<typename T> |
| T timesTwo(T input) |
| { |
| return input * 2; |
| } |
| |
| void instantiate() |
| { |
| TemplStruct<int> ti; |
| TemplStruct<double> td; |
| (void)timesTwo<int>(2); |
| (void)timesTwo<double>(2); |
| } |
| |
| template class TemplStruct<float>; |
| |
| extern template class TemplStruct<long>; |
| |
| template<> class TemplStruct<bool> { |
| TemplStruct() {} |
| ~TemplStruct() {} |
| |
| void foo() {} |
| private: |
| bool m_t; |
| }; |
| |
| // Explicit instantiation of template functions do not appear in the AST |
| template float timesTwo(float); |
| |
| template<> bool timesTwo<bool>(bool) { |
| return true; |
| } |
| )cpp"); |
| { |
| auto BN = ast_matchers::match( |
| classTemplateDecl(hasName("TemplStruct")).bind("rec"), |
| AST->getASTContext()); |
| EXPECT_EQ(BN.size(), 1u); |
| |
| EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, |
| BN[0].getNodeAs<Decl>("rec")), |
| R"cpp( |
| ClassTemplateDecl 'TemplStruct' |
| |-TemplateTypeParmDecl 'T' |
| `-CXXRecordDecl 'TemplStruct' |
| |-CXXConstructorDecl 'TemplStruct<T>' |
| | `-CompoundStmt |
| |-CXXDestructorDecl '~TemplStruct<T>' |
| | `-CompoundStmt |
| |-AccessSpecDecl |
| `-FieldDecl 'm_t' |
| )cpp"); |
| |
| EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("rec")), |
| R"cpp( |
| ClassTemplateDecl 'TemplStruct' |
| |-TemplateTypeParmDecl 'T' |
| |-CXXRecordDecl 'TemplStruct' |
| | |-CXXRecordDecl 'TemplStruct' |
| | |-CXXConstructorDecl 'TemplStruct<T>' |
| | | `-CompoundStmt |
| | |-CXXDestructorDecl '~TemplStruct<T>' |
| | | `-CompoundStmt |
| | |-AccessSpecDecl |
| | `-FieldDecl 'm_t' |
| |-ClassTemplateSpecializationDecl 'TemplStruct' |
| | |-TemplateArgument type int |
| | | `-BuiltinType |
| | |-CXXRecordDecl 'TemplStruct' |
| | |-CXXConstructorDecl 'TemplStruct' |
| | | `-CompoundStmt |
| | |-CXXDestructorDecl '~TemplStruct' |
| | | `-CompoundStmt |
| | |-AccessSpecDecl |
| | |-FieldDecl 'm_t' |
| | `-CXXConstructorDecl 'TemplStruct' |
| | `-ParmVarDecl '' |
| |-ClassTemplateSpecializationDecl 'TemplStruct' |
| | |-TemplateArgument type double |
| | | `-BuiltinType |
| | |-CXXRecordDecl 'TemplStruct' |
| | |-CXXConstructorDecl 'TemplStruct' |
| | | `-CompoundStmt |
| | |-CXXDestructorDecl '~TemplStruct' |
| | | `-CompoundStmt |
| | |-AccessSpecDecl |
| | |-FieldDecl 'm_t' |
| | `-CXXConstructorDecl 'TemplStruct' |
| | `-ParmVarDecl '' |
| |-ClassTemplateSpecializationDecl 'TemplStruct' |
| | |-TemplateArgument type float |
| | | `-BuiltinType |
| | |-CXXRecordDecl 'TemplStruct' |
| | |-CXXConstructorDecl 'TemplStruct' |
| | | `-CompoundStmt |
| | |-CXXDestructorDecl '~TemplStruct' |
| | | `-CompoundStmt |
| | |-AccessSpecDecl |
| | `-FieldDecl 'm_t' |
| |-ClassTemplateSpecializationDecl 'TemplStruct' |
| | |-TemplateArgument type long |
| | | `-BuiltinType |
| | |-CXXRecordDecl 'TemplStruct' |
| | |-CXXConstructorDecl 'TemplStruct' |
| | |-CXXDestructorDecl '~TemplStruct' |
| | |-AccessSpecDecl |
| | `-FieldDecl 'm_t' |
| `-ClassTemplateSpecializationDecl 'TemplStruct' |
| |-TemplateArgument type _Bool |
| | `-BuiltinType |
| |-CXXRecordDecl 'TemplStruct' |
| |-CXXConstructorDecl 'TemplStruct' |
| | `-CompoundStmt |
| |-CXXDestructorDecl '~TemplStruct' |
| | `-CompoundStmt |
| |-CXXMethodDecl 'foo' |
| | `-CompoundStmt |
| |-AccessSpecDecl |
| `-FieldDecl 'm_t' |
| )cpp"); |
| } |
| { |
| auto BN = ast_matchers::match( |
| classTemplateSpecializationDecl( |
| hasTemplateArgument( |
| 0, templateArgument(refersToType(asString("_Bool"))))) |
| .bind("templSpec"), |
| AST->getASTContext()); |
| EXPECT_EQ(BN.size(), 1u); |
| |
| EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("templSpec")), |
| R"cpp( |
| ClassTemplateSpecializationDecl 'TemplStruct' |
| |-TemplateArgument type _Bool |
| | `-BuiltinType |
| |-CXXRecordDecl 'TemplStruct' |
| |-CXXConstructorDecl 'TemplStruct' |
| | `-CompoundStmt |
| |-CXXDestructorDecl '~TemplStruct' |
| | `-CompoundStmt |
| |-CXXMethodDecl 'foo' |
| | `-CompoundStmt |
| |-AccessSpecDecl |
| `-FieldDecl 'm_t' |
| )cpp"); |
| |
| EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, |
| BN[0].getNodeAs<Decl>("templSpec")), |
| R"cpp( |
| ClassTemplateSpecializationDecl 'TemplStruct' |
| |-TemplateArgument type _Bool |
| | `-BuiltinType |
| |-CXXConstructorDecl 'TemplStruct' |
| | `-CompoundStmt |
| |-CXXDestructorDecl '~TemplStruct' |
| | `-CompoundStmt |
| |-CXXMethodDecl 'foo' |
| | `-CompoundStmt |
| |-AccessSpecDecl |
| `-FieldDecl 'm_t' |
| )cpp"); |
| } |
| { |
| auto BN = ast_matchers::match( |
| functionTemplateDecl(hasName("timesTwo")).bind("fn"), |
| AST->getASTContext()); |
| EXPECT_EQ(BN.size(), 1u); |
| |
| EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, |
| BN[0].getNodeAs<Decl>("fn")), |
| R"cpp( |
| FunctionTemplateDecl 'timesTwo' |
| |-TemplateTypeParmDecl 'T' |
| `-FunctionDecl 'timesTwo' |
| |-ParmVarDecl 'input' |
| `-CompoundStmt |
| `-ReturnStmt |
| `-BinaryOperator |
| |-DeclRefExpr 'input' |
| `-IntegerLiteral |
| )cpp"); |
| |
| EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("fn")), |
| R"cpp( |
| FunctionTemplateDecl 'timesTwo' |
| |-TemplateTypeParmDecl 'T' |
| |-FunctionDecl 'timesTwo' |
| | |-ParmVarDecl 'input' |
| | `-CompoundStmt |
| | `-ReturnStmt |
| | `-BinaryOperator |
| | |-DeclRefExpr 'input' |
| | `-IntegerLiteral |
| |-FunctionDecl 'timesTwo' |
| | |-TemplateArgument type int |
| | | `-BuiltinType |
| | |-ParmVarDecl 'input' |
| | `-CompoundStmt |
| | `-ReturnStmt |
| | `-BinaryOperator |
| | |-ImplicitCastExpr |
| | | `-DeclRefExpr 'input' |
| | `-IntegerLiteral |
| |-FunctionDecl 'timesTwo' |
| | |-TemplateArgument type double |
| | | `-BuiltinType |
| | |-ParmVarDecl 'input' |
| | `-CompoundStmt |
| | `-ReturnStmt |
| | `-BinaryOperator |
| | |-ImplicitCastExpr |
| | | `-DeclRefExpr 'input' |
| | `-ImplicitCastExpr |
| | `-IntegerLiteral |
| |-FunctionDecl 'timesTwo' |
| | |-TemplateArgument type float |
| | | `-BuiltinType |
| | |-ParmVarDecl 'input' |
| | `-CompoundStmt |
| | `-ReturnStmt |
| | `-BinaryOperator |
| | |-ImplicitCastExpr |
| | | `-DeclRefExpr 'input' |
| | `-ImplicitCastExpr |
| | `-IntegerLiteral |
| |-FunctionDecl 'timesTwo' |
| | |-TemplateArgument type _Bool |
| | | `-BuiltinType |
| | |-ParmVarDecl '' |
| | `-CompoundStmt |
| | `-ReturnStmt |
| | `-CXXBoolLiteralExpr |
| `-FunctionDecl 'timesTwo' |
| |-TemplateArgument type _Bool |
| | `-BuiltinType |
| `-ParmVarDecl 'input' |
| )cpp"); |
| } |
| { |
| auto BN = ast_matchers::match( |
| classTemplateSpecializationDecl( |
| hasName("TemplStruct"), |
| hasTemplateArgument( |
| 0, templateArgument(refersToType(asString("float")))), |
| hasParent(translationUnitDecl())) |
| .bind("rec"), |
| AST->getASTContext()); |
| EXPECT_EQ(BN.size(), 1u); |
| |
| EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, |
| BN[0].getNodeAs<Decl>("rec")), |
| R"cpp( |
| ClassTemplateSpecializationDecl 'TemplStruct' |
| `-TemplateArgument type float |
| `-BuiltinType |
| )cpp"); |
| |
| EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("rec")), |
| R"cpp( |
| ClassTemplateSpecializationDecl 'TemplStruct' |
| |-TemplateArgument type float |
| | `-BuiltinType |
| |-CXXRecordDecl 'TemplStruct' |
| |-CXXConstructorDecl 'TemplStruct' |
| | `-CompoundStmt |
| |-CXXDestructorDecl '~TemplStruct' |
| | `-CompoundStmt |
| |-AccessSpecDecl |
| `-FieldDecl 'm_t' |
| )cpp"); |
| } |
| } |
| |
| TEST(Traverse, CXXRewrittenBinaryOperator) { |
| |
| auto AST = buildASTFromCodeWithArgs(R"cpp( |
| namespace std { |
| struct strong_ordering { |
| int n; |
| constexpr operator int() const { return n; } |
| static const strong_ordering equal, greater, less; |
| }; |
| constexpr strong_ordering strong_ordering::equal = {0}; |
| constexpr strong_ordering strong_ordering::greater = {1}; |
| constexpr strong_ordering strong_ordering::less = {-1}; |
| } |
| |
| struct HasSpaceshipMem { |
| int a; |
| constexpr auto operator<=>(const HasSpaceshipMem&) const = default; |
| }; |
| |
| void binop() |
| { |
| HasSpaceshipMem hs1, hs2; |
| if (hs1 < hs2) |
| return; |
| } |
| )cpp", |
| {"-std=c++20"}); |
| { |
| auto BN = ast_matchers::match(cxxRewrittenBinaryOperator().bind("binop"), |
| AST->getASTContext()); |
| EXPECT_EQ(BN.size(), 1u); |
| |
| EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Stmt>("binop")), |
| R"cpp( |
| CXXRewrittenBinaryOperator |
| `-BinaryOperator |
| |-ImplicitCastExpr |
| | `-CXXMemberCallExpr |
| | `-MemberExpr |
| | `-ImplicitCastExpr |
| | `-MaterializeTemporaryExpr |
| | `-CXXOperatorCallExpr |
| | |-ImplicitCastExpr |
| | | `-DeclRefExpr 'operator<=>' |
| | |-ImplicitCastExpr |
| | | `-DeclRefExpr 'hs1' |
| | `-ImplicitCastExpr |
| | `-DeclRefExpr 'hs2' |
| `-IntegerLiteral |
| )cpp"); |
| EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, |
| BN[0].getNodeAs<Stmt>("binop")), |
| R"cpp( |
| CXXRewrittenBinaryOperator |
| |-DeclRefExpr 'hs1' |
| `-DeclRefExpr 'hs2' |
| )cpp"); |
| } |
| } |
| |
| } // namespace clang |