[clang-tools-extra] dbd45b2 - [ASTMatchers] Fix `hasBody` for the descendants of `FunctionDecl`
Adam Balogh via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 16 04:27:59 PDT 2020
Author: Adam Balogh
Date: 2020-09-16T13:16:51+02:00
New Revision: dbd45b2db8e0c396fa20d4c72734c4f31f54af96
URL: https://github.com/llvm/llvm-project/commit/dbd45b2db8e0c396fa20d4c72734c4f31f54af96
DIFF: https://github.com/llvm/llvm-project/commit/dbd45b2db8e0c396fa20d4c72734c4f31f54af96.diff
LOG: [ASTMatchers] Fix `hasBody` for the descendants of `FunctionDecl`
//AST Matcher// `hasBody` is a polymorphic matcher that behaves
differently for loop statements and function declarations. The main
difference is the for functions declarations it does not only call
`FunctionDecl::getBody()` but first checks whether the declaration in
question is that specific declaration which has the body by calling
`FunctionDecl::doesThisDeclarationHaveABody()`. This is achieved by
specialization of the template `GetBodyMatcher`. Unfortunately template
specializations do not catch the descendants of the class for which the
template is specialized. Therefore it does not work correcly for the
descendants of `FunctionDecl`, such as `CXXMethodDecl`,
`CXXConstructorDecl`, `CXXDestructorDecl` etc. This patch fixes this
issue by using a template metaprogram.
The patch also introduces a new matcher `hasAnyBody` which matches
declarations which have a body present in the AST but not necessarily
belonging to that particular declaration.
Differential Revision: https://reviews.llvm.org/D87527
Added:
Modified:
clang-tools-extra/clang-tidy/modernize/UseEqualsDeleteCheck.cpp
clang/include/clang/ASTMatchers/ASTMatchers.h
clang/include/clang/ASTMatchers/ASTMatchersInternal.h
clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
Removed:
################################################################################
diff --git a/clang-tools-extra/clang-tidy/modernize/UseEqualsDeleteCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseEqualsDeleteCheck.cpp
index ea4bf91b0d438..7d5ae89551731 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseEqualsDeleteCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseEqualsDeleteCheck.cpp
@@ -36,12 +36,12 @@ void UseEqualsDeleteCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
cxxMethodDecl(
PrivateSpecialFn,
- unless(anyOf(hasBody(stmt()), isDefaulted(), isDeleted(),
+ unless(anyOf(hasAnyBody(stmt()), isDefaulted(), isDeleted(),
ast_matchers::isTemplateInstantiation(),
// Ensure that all methods except private special member
// functions are defined.
hasParent(cxxRecordDecl(hasMethod(unless(
- anyOf(PrivateSpecialFn, hasBody(stmt()), isPure(),
+ anyOf(PrivateSpecialFn, hasAnyBody(stmt()), isPure(),
isDefaulted(), isDeleted()))))))))
.bind(SpecialFunction),
this);
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index e670459fe8a2f..bd89906eadb0f 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -4879,7 +4879,9 @@ AST_MATCHER_P(ArraySubscriptExpr, hasBase,
}
/// Matches a 'for', 'while', 'do while' statement or a function
-/// definition that has a given body.
+/// definition that has a given body. Note that in case of functions
+/// this matcher only matches the definition itself and not the other
+/// declarations of the same function.
///
/// Given
/// \code
@@ -4889,6 +4891,18 @@ AST_MATCHER_P(ArraySubscriptExpr, hasBase,
/// matches 'for (;;) {}'
/// with compoundStmt()
/// matching '{}'
+///
+/// Given
+/// \code
+/// void f();
+/// void f() {}
+/// \endcode
+/// hasBody(functionDecl())
+/// matches 'void f() {}'
+/// with compoundStmt()
+/// matching '{}'
+/// but does not match 'void f();'
+
AST_POLYMORPHIC_MATCHER_P(hasBody,
AST_POLYMORPHIC_SUPPORTED_TYPES(DoStmt, ForStmt,
WhileStmt,
@@ -4900,6 +4914,30 @@ AST_POLYMORPHIC_MATCHER_P(hasBody,
InnerMatcher.matches(*Statement, Finder, Builder));
}
+/// Matches a function declaration that has a given body present in the AST.
+/// Note that this matcher matches all the declarations of a function whose
+/// body is present in the AST.
+///
+/// Given
+/// \code
+/// void f();
+/// void f() {}
+/// void g();
+/// \endcode
+/// hasAnyBody(functionDecl())
+/// matches both 'void f();'
+/// and 'void f() {}'
+/// with compoundStmt()
+/// matching '{}'
+/// but does not match 'void g();'
+AST_MATCHER_P(FunctionDecl, hasAnyBody,
+ internal::Matcher<Stmt>, InnerMatcher) {
+ const Stmt *const Statement = Node.getBody();
+ return (Statement != nullptr &&
+ InnerMatcher.matches(*Statement, Finder, Builder));
+}
+
+
/// Matches compound statements where at least one substatement matches
/// a given matcher. Also matches StmtExprs that have CompoundStmt as children.
///
diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
index 09774b3c912c7..2a3f503f99516 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -1835,18 +1835,18 @@ struct NotEqualsBoundNodePredicate {
DynTypedNode Node;
};
+template <typename Ty, typename Enable = void> struct GetBodyMatcher {
+ static const Stmt *get(const Ty &Node) { return Node.getBody(); }
+};
+
template <typename Ty>
-struct GetBodyMatcher {
+struct GetBodyMatcher<Ty, typename std::enable_if<
+ std::is_base_of<FunctionDecl, Ty>::value>::type> {
static const Stmt *get(const Ty &Node) {
- return Node.getBody();
+ return Node.doesThisDeclarationHaveABody() ? Node.getBody() : nullptr;
}
};
-template <>
-inline const Stmt *GetBodyMatcher<FunctionDecl>::get(const FunctionDecl &Node) {
- return Node.doesThisDeclarationHaveABody() ? Node.getBody() : nullptr;
-}
-
template <typename Ty>
struct HasSizeMatcher {
static bool hasSize(const Ty &Node, unsigned int N) {
diff --git a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
index 72fbef5cdc175..39222fbe42491 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -1612,10 +1612,49 @@ TEST(HasBody, FindsBodyOfForWhileDoLoops) {
doStmt(hasBody(compoundStmt()))));
EXPECT_TRUE(matches("void f() { int p[2]; for (auto x : p) {} }",
cxxForRangeStmt(hasBody(compoundStmt()))));
+}
+
+TEST(HasBody, FindsBodyOfFunctions) {
EXPECT_TRUE(matches("void f() {}", functionDecl(hasBody(compoundStmt()))));
EXPECT_TRUE(notMatches("void f();", functionDecl(hasBody(compoundStmt()))));
- EXPECT_TRUE(matches("void f(); void f() {}",
- functionDecl(hasBody(compoundStmt()))));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "void f(); void f() {}",
+ functionDecl(hasBody(compoundStmt())).bind("func"),
+ std::make_unique<VerifyIdIsBoundTo<FunctionDecl>>("func", 1)));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "class C { void f(); }; void C::f() {}",
+ cxxMethodDecl(hasBody(compoundStmt())).bind("met"),
+ std::make_unique<VerifyIdIsBoundTo<CXXMethodDecl>>("met", 1)));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "class C { C(); }; C::C() {}",
+ cxxConstructorDecl(hasBody(compoundStmt())).bind("ctr"),
+ std::make_unique<VerifyIdIsBoundTo<CXXConstructorDecl>>("ctr", 1)));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "class C { ~C(); }; C::~C() {}",
+ cxxDestructorDecl(hasBody(compoundStmt())).bind("dtr"),
+ std::make_unique<VerifyIdIsBoundTo<CXXDestructorDecl>>("dtr", 1)));
+}
+
+TEST(HasAnyBody, FindsAnyBodyOfFunctions) {
+ EXPECT_TRUE(matches("void f() {}", functionDecl(hasAnyBody(compoundStmt()))));
+ EXPECT_TRUE(notMatches("void f();",
+ functionDecl(hasAnyBody(compoundStmt()))));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "void f(); void f() {}",
+ functionDecl(hasAnyBody(compoundStmt())).bind("func"),
+ std::make_unique<VerifyIdIsBoundTo<FunctionDecl>>("func", 2)));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "class C { void f(); }; void C::f() {}",
+ cxxMethodDecl(hasAnyBody(compoundStmt())).bind("met"),
+ std::make_unique<VerifyIdIsBoundTo<CXXMethodDecl>>("met", 2)));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "class C { C(); }; C::C() {}",
+ cxxConstructorDecl(hasAnyBody(compoundStmt())).bind("ctr"),
+ std::make_unique<VerifyIdIsBoundTo<CXXConstructorDecl>>("ctr", 2)));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "class C { ~C(); }; C::~C() {}",
+ cxxDestructorDecl(hasAnyBody(compoundStmt())).bind("dtr"),
+ std::make_unique<VerifyIdIsBoundTo<CXXDestructorDecl>>("dtr", 2)));
}
TEST(HasAnySubstatement, MatchesForTopLevelCompoundStatement) {
More information about the cfe-commits
mailing list