diff --git a/clang-tools-extra/clang-tidy/modernize/UseEqualsDeleteCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseEqualsDeleteCheck.cpp --- a/clang-tools-extra/clang-tidy/modernize/UseEqualsDeleteCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseEqualsDeleteCheck.cpp @@ -36,12 +36,12 @@ 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 --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -4879,7 +4879,9 @@ } /// 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 @@ /// 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 @@ 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, 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 --- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -1835,18 +1835,18 @@ DynTypedNode Node; }; +template struct GetBodyMatcher { + static const Stmt *get(const Ty &Node) { return Node.getBody(); } +}; + template -struct GetBodyMatcher { +struct GetBodyMatcher::value>::type> { static const Stmt *get(const Ty &Node) { - return Node.getBody(); + return Node.doesThisDeclarationHaveABody() ? Node.getBody() : nullptr; } }; -template <> -inline const Stmt *GetBodyMatcher::get(const FunctionDecl &Node) { - return Node.doesThisDeclarationHaveABody() ? Node.getBody() : nullptr; -} - template 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 --- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -1612,10 +1612,49 @@ 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>("func", 1))); + EXPECT_TRUE(matchAndVerifyResultTrue( + "class C { void f(); }; void C::f() {}", + cxxMethodDecl(hasBody(compoundStmt())).bind("met"), + std::make_unique>("met", 1))); + EXPECT_TRUE(matchAndVerifyResultTrue( + "class C { C(); }; C::C() {}", + cxxConstructorDecl(hasBody(compoundStmt())).bind("ctr"), + std::make_unique>("ctr", 1))); + EXPECT_TRUE(matchAndVerifyResultTrue( + "class C { ~C(); }; C::~C() {}", + cxxDestructorDecl(hasBody(compoundStmt())).bind("dtr"), + std::make_unique>("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>("func", 2))); + EXPECT_TRUE(matchAndVerifyResultTrue( + "class C { void f(); }; void C::f() {}", + cxxMethodDecl(hasAnyBody(compoundStmt())).bind("met"), + std::make_unique>("met", 2))); + EXPECT_TRUE(matchAndVerifyResultTrue( + "class C { C(); }; C::C() {}", + cxxConstructorDecl(hasAnyBody(compoundStmt())).bind("ctr"), + std::make_unique>("ctr", 2))); + EXPECT_TRUE(matchAndVerifyResultTrue( + "class C { ~C(); }; C::~C() {}", + cxxDestructorDecl(hasAnyBody(compoundStmt())).bind("dtr"), + std::make_unique>("dtr", 2))); } TEST(HasAnySubstatement, MatchesForTopLevelCompoundStatement) {