diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h --- a/clang/include/clang/AST/ASTNodeTraverser.h +++ b/clang/include/clang/AST/ASTNodeTraverser.h @@ -113,6 +113,9 @@ case ast_type_traits::TK_IgnoreImplicitCastsAndParentheses: S = E->IgnoreParenImpCasts(); break; + case ast_type_traits::TK_IgnoreUnlessSpelledInSource: + S = E->IgnoreUnlessSpelledInSource(); + break; } } diff --git a/clang/include/clang/AST/ASTTypeTraits.h b/clang/include/clang/AST/ASTTypeTraits.h --- a/clang/include/clang/AST/ASTTypeTraits.h +++ b/clang/include/clang/AST/ASTTypeTraits.h @@ -43,7 +43,10 @@ /// Will not traverse implicit casts and parentheses. /// Corresponds to Expr::IgnoreParenImpCasts() - TK_IgnoreImplicitCastsAndParentheses + TK_IgnoreImplicitCastsAndParentheses, + + /// Ignore AST nodes not written in the source + TK_IgnoreUnlessSpelledInSource }; /// Kind identifier. diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -762,6 +762,15 @@ /// member expression. static QualType findBoundMemberType(const Expr *expr); + /// Skip past any invisble AST nodes which might surround this + /// statement, such as ExprWithCleanups or ImplicitCastExpr nodes, + /// but also injected CXXMemberExpr and CXXConstructExpr which represent + /// implicit conversions. + Expr *IgnoreUnlessSpelledInSource(); + const Expr *IgnoreUnlessSpelledInSource() const { + return const_cast(this)->IgnoreUnlessSpelledInSource(); + } + /// Skip past any implicit casts which might surround this expression until /// reaching a fixed point. Skips: /// * ImplicitCastExpr diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -112,6 +112,8 @@ return E; case ast_type_traits::TK_IgnoreImplicitCastsAndParentheses: return E->IgnoreParenImpCasts(); + case ast_type_traits::TK_IgnoreUnlessSpelledInSource: + return E->IgnoreUnlessSpelledInSource(); } llvm_unreachable("Invalid Traversal type!"); } diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -3020,6 +3020,34 @@ }); } +Expr *Expr::IgnoreUnlessSpelledInSource() { + Expr *E = this; + + Expr *LastE = nullptr; + while (E != LastE) { + LastE = E; + E = E->IgnoreImplicit(); + + auto SR = E->getSourceRange(); + + if (auto *C = dyn_cast(E)) { + if (C->getNumArgs() == 1) { + Expr *A = C->getArg(0); + if (A->getSourceRange() == SR || !isa(C)) + E = A; + } + } + + if (auto *C = dyn_cast(E)) { + Expr *ExprNode = C->getImplicitObjectArgument()->IgnoreParenImpCasts(); + if (ExprNode->getSourceRange() == SR) + E = ExprNode; + } + } + + return E; +} + bool Expr::isDefaultArgument() const { const Expr *E = this; if (const MaterializeTemporaryExpr *M = dyn_cast(E)) diff --git a/clang/unittests/AST/ASTTraverserTest.cpp b/clang/unittests/AST/ASTTraverserTest.cpp --- a/clang/unittests/AST/ASTTraverserTest.cpp +++ b/clang/unittests/AST/ASTTraverserTest.cpp @@ -85,6 +85,21 @@ return OS.str(); } +template std::string dumpASTString(ast_type_traits::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(N)...); + + return OS.str(); +} + const FunctionDecl *getFunctionNode(clang::ASTUnit *AST, const std::string &Name) { auto Result = ast_matchers::match(functionDecl(hasName(Name)).bind("fn"), @@ -131,12 +146,12 @@ template struct templ -{ +{ }; template<> struct templ -{ +{ }; void parmvardecl_attr(struct A __attribute__((address_space(19)))*); @@ -234,4 +249,184 @@ EXPECT_EQ(toTargetAddressSpace(static_cast(AS->getAddressSpace())), 19u); } + +TEST(Traverse, IgnoreUnlessSpelledInSource) { + + auto AST = buildASTFromCode(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"); + + 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("fn"); + }; + + { + auto FN = getFunctionNode("func1"); + + EXPECT_EQ(dumpASTString(ast_type_traits::TK_AsIs, FN), + R"cpp( +FunctionDecl +`-CompoundStmt + `-ReturnStmt + `-ExprWithCleanups + `-CXXConstructExpr + `-MaterializeTemporaryExpr + `-ImplicitCastExpr + `-CXXConstructExpr + `-IntegerLiteral +)cpp"); + + EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, FN), + R"cpp( +FunctionDecl +`-CompoundStmt + `-ReturnStmt + `-IntegerLiteral +)cpp"); + } + + { + EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, getFunctionNode("func2")), + R"cpp( +FunctionDecl +`-CompoundStmt + `-ReturnStmt + `-CXXTemporaryObjectExpr + `-IntegerLiteral +)cpp"); + } + + { + EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, getFunctionNode("func3")), + R"cpp( +FunctionDecl +`-CompoundStmt + `-ReturnStmt + `-CXXFunctionalCastExpr + `-IntegerLiteral +)cpp"); + } + + { + auto TempObjectAST = R"cpp( +FunctionDecl +`-CompoundStmt + `-ReturnStmt + `-CXXTemporaryObjectExpr +)cpp"; + + EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, getFunctionNode("func4")), + TempObjectAST); + EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, getFunctionNode("func5")), + TempObjectAST); + EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, getFunctionNode("func6")), + TempObjectAST); + EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, getFunctionNode("func7")), + TempObjectAST); + } + { + auto FuncCastAST = R"cpp( +FunctionDecl +`-CompoundStmt + `-ReturnStmt + `-CXXFunctionalCastExpr + `-InitListExpr +)cpp"; + + EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, getFunctionNode("func8")), + FuncCastAST); + EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, getFunctionNode("func9")), + FuncCastAST); + } + + { + auto DeclRefAST = R"cpp( +FunctionDecl +`-CompoundStmt + |-DeclStmt + | `-VarDecl + | `-CXXConstructExpr + `-ReturnStmt + `-DeclRefExpr +)cpp"; + + EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, getFunctionNode("func10")), + DeclRefAST); + EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, getFunctionNode("func11")), + DeclRefAST); + EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, getFunctionNode("func12")), + DeclRefAST); + } +} + } // namespace clang diff --git a/clang/unittests/AST/CMakeLists.txt b/clang/unittests/AST/CMakeLists.txt --- a/clang/unittests/AST/CMakeLists.txt +++ b/clang/unittests/AST/CMakeLists.txt @@ -15,6 +15,7 @@ ASTImporterVisibilityTest.cpp ASTTraverserTest.cpp ASTTypeTraitsTest.cpp + ASTTraverserTest.cpp ASTVectorTest.cpp CommentLexer.cpp CommentParser.cpp diff --git a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp --- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -1680,6 +1680,149 @@ functionDecl(hasDescendant(Matcher))))))); } +TEST(Traversal, traverseUnlessSpelledInSource) { + + StringRef Code = 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"; + + EXPECT_TRUE(matches( + Code, traverse(ast_type_traits::TK_IgnoreUnlessSpelledInSource, + returnStmt(forFunction(functionDecl(hasName("func1"))), + hasReturnValue(integerLiteral(equals(42))))))); + + EXPECT_TRUE(matches( + Code, + traverse(ast_type_traits::TK_IgnoreUnlessSpelledInSource, + returnStmt(forFunction(functionDecl(hasName("func2"))), + hasReturnValue(cxxTemporaryObjectExpr( + hasArgument(0, integerLiteral(equals(42))))))))); + + EXPECT_TRUE(matches( + Code, traverse(ast_type_traits::TK_IgnoreUnlessSpelledInSource, + returnStmt(forFunction(functionDecl(hasName("func3"))), + hasReturnValue( + cxxFunctionalCastExpr(hasSourceExpression( + integerLiteral(equals(42))))))))); + + EXPECT_TRUE(matches( + Code, traverse(ast_type_traits::TK_IgnoreUnlessSpelledInSource, + returnStmt(forFunction(functionDecl(hasName("func4"))), + hasReturnValue(cxxTemporaryObjectExpr()))))); + + EXPECT_TRUE(matches( + Code, traverse(ast_type_traits::TK_IgnoreUnlessSpelledInSource, + returnStmt(forFunction(functionDecl(hasName("func5"))), + hasReturnValue(cxxTemporaryObjectExpr()))))); + + EXPECT_TRUE(matches( + Code, traverse(ast_type_traits::TK_IgnoreUnlessSpelledInSource, + returnStmt(forFunction(functionDecl(hasName("func6"))), + hasReturnValue(cxxTemporaryObjectExpr()))))); + + EXPECT_TRUE(matches( + Code, traverse(ast_type_traits::TK_IgnoreUnlessSpelledInSource, + returnStmt(forFunction(functionDecl(hasName("func7"))), + hasReturnValue(cxxTemporaryObjectExpr()))))); + + EXPECT_TRUE(matches( + Code, traverse(ast_type_traits::TK_IgnoreUnlessSpelledInSource, + returnStmt(forFunction(functionDecl(hasName("func8"))), + hasReturnValue(cxxFunctionalCastExpr( + hasSourceExpression(initListExpr()))))))); + + EXPECT_TRUE(matches( + Code, traverse(ast_type_traits::TK_IgnoreUnlessSpelledInSource, + returnStmt(forFunction(functionDecl(hasName("func9"))), + hasReturnValue(cxxFunctionalCastExpr( + hasSourceExpression(initListExpr()))))))); + + EXPECT_TRUE(matches( + Code, traverse(ast_type_traits::TK_IgnoreUnlessSpelledInSource, + returnStmt(forFunction(functionDecl(hasName("func10"))), + hasReturnValue( + declRefExpr(to(varDecl(hasName("a"))))))))); + + EXPECT_TRUE(matches( + Code, traverse(ast_type_traits::TK_IgnoreUnlessSpelledInSource, + returnStmt(forFunction(functionDecl(hasName("func11"))), + hasReturnValue( + declRefExpr(to(varDecl(hasName("b"))))))))); + + EXPECT_TRUE(matches( + Code, traverse(ast_type_traits::TK_IgnoreUnlessSpelledInSource, + returnStmt(forFunction(functionDecl(hasName("func12"))), + hasReturnValue( + declRefExpr(to(varDecl(hasName("c"))))))))); +} + TEST(IgnoringImpCasts, MatchesImpCasts) { // This test checks that ignoringImpCasts matches when implicit casts are // present and its inner matcher alone does not match.