case ast_type_traits::TK_IgnoreImplicitCastsAndParentheses:
S = E->IgnoreParenImpCasts();
break;
+ case ast_type_traits::TK_IgnoreUnlessSpelledInSource:
+ S = E->IgnoreUnlessSpelledInSource();
+ break;
}
}
/// 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.
/// 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<Expr *>(this)->IgnoreUnlessSpelledInSource();
+ }
+
/// Skip past any implicit casts which might surround this expression until
/// reaching a fixed point. Skips:
/// * ImplicitCastExpr
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!");
}
});
}
+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<CXXConstructExpr>(E)) {
+ if (C->getNumArgs() == 1) {
+ Expr *A = C->getArg(0);
+ if (A->getSourceRange() == SR || !isa<CXXTemporaryObjectExpr>(C))
+ E = A;
+ }
+ }
+
+ if (auto *C = dyn_cast<CXXMemberCallExpr>(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<MaterializeTemporaryExpr>(E))
return OS.str();
}
+template <typename... NodeType>
+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<NodeType &&>(N)...);
+
+ return OS.str();
+}
+
const FunctionDecl *getFunctionNode(clang::ASTUnit *AST,
const std::string &Name) {
auto Result = ast_matchers::match(functionDecl(hasName(Name)).bind("fn"),
EXPECT_EQ(toTargetAddressSpace(static_cast<LangAS>(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<Decl>("fn");
+ };
+
+ {
+ auto FN = getFunctionNode("func1");
+
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_AsIs, FN),
+ R"cpp(
+FunctionDecl 'func1'
+`-CompoundStmt
+ `-ReturnStmt
+ `-ExprWithCleanups
+ `-CXXConstructExpr
+ `-MaterializeTemporaryExpr
+ `-ImplicitCastExpr
+ `-CXXConstructExpr
+ `-IntegerLiteral
+)cpp");
+
+ EXPECT_EQ(
+ dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource, FN),
+ R"cpp(
+FunctionDecl 'func1'
+`-CompoundStmt
+ `-ReturnStmt
+ `-IntegerLiteral
+)cpp");
+ }
+
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
+ getFunctionNode("func2")),
+ R"cpp(
+FunctionDecl 'func2'
+`-CompoundStmt
+ `-ReturnStmt
+ `-CXXTemporaryObjectExpr
+ `-IntegerLiteral
+)cpp");
+
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
+ getFunctionNode("func3")),
+ R"cpp(
+FunctionDecl 'func3'
+`-CompoundStmt
+ `-ReturnStmt
+ `-CXXFunctionalCastExpr
+ `-IntegerLiteral
+)cpp");
+
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
+ getFunctionNode("func4")),
+ R"cpp(
+FunctionDecl 'func4'
+`-CompoundStmt
+ `-ReturnStmt
+ `-CXXTemporaryObjectExpr
+)cpp");
+
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
+ getFunctionNode("func5")),
+ R"cpp(
+FunctionDecl 'func5'
+`-CompoundStmt
+ `-ReturnStmt
+ `-CXXTemporaryObjectExpr
+)cpp");
+
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
+ getFunctionNode("func6")),
+ R"cpp(
+FunctionDecl 'func6'
+`-CompoundStmt
+ `-ReturnStmt
+ `-CXXTemporaryObjectExpr
+)cpp");
+
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
+ getFunctionNode("func7")),
+ R"cpp(
+FunctionDecl 'func7'
+`-CompoundStmt
+ `-ReturnStmt
+ `-CXXTemporaryObjectExpr
+)cpp");
+
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
+ getFunctionNode("func8")),
+ R"cpp(
+FunctionDecl 'func8'
+`-CompoundStmt
+ `-ReturnStmt
+ `-CXXFunctionalCastExpr
+ `-InitListExpr
+)cpp");
+
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
+ getFunctionNode("func9")),
+ R"cpp(
+FunctionDecl 'func9'
+`-CompoundStmt
+ `-ReturnStmt
+ `-CXXFunctionalCastExpr
+ `-InitListExpr
+)cpp");
+
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
+ getFunctionNode("func10")),
+ R"cpp(
+FunctionDecl 'func10'
+`-CompoundStmt
+ |-DeclStmt
+ | `-VarDecl 'a'
+ | `-CXXConstructExpr
+ `-ReturnStmt
+ `-DeclRefExpr 'a'
+)cpp");
+
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
+ getFunctionNode("func11")),
+ R"cpp(
+FunctionDecl 'func11'
+`-CompoundStmt
+ |-DeclStmt
+ | `-VarDecl 'b'
+ | `-CXXConstructExpr
+ `-ReturnStmt
+ `-DeclRefExpr 'b'
+)cpp");
+
+ EXPECT_EQ(dumpASTString(ast_type_traits::TK_IgnoreUnlessSpelledInSource,
+ getFunctionNode("func12")),
+ R"cpp(
+FunctionDecl 'func12'
+`-CompoundStmt
+ |-DeclStmt
+ | `-VarDecl 'c'
+ | `-CXXConstructExpr
+ `-ReturnStmt
+ `-DeclRefExpr 'c'
+)cpp");
+}
+
} // namespace clang
ASTImporterVisibilityTest.cpp
ASTTraverserTest.cpp
ASTTypeTraitsTest.cpp
+ ASTTraverserTest.cpp
ASTVectorTest.cpp
CommentLexer.cpp
CommentParser.cpp
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.