baloghadamsoftware created this revision. baloghadamsoftware added reviewers: klimek, sbenza. baloghadamsoftware added a project: clang. Herald added subscribers: martong, gamesh411, Szelethus, dkrupp, rnkovacs. baloghadamsoftware requested review of this revision.
//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. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D87527 Files: clang/include/clang/ASTMatchers/ASTMatchersInternal.h clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp Index: clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp =================================================================== --- clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -1454,10 +1454,30 @@ 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))); +} + +TEST(HasBody, FindsBodyOfFunctionChildren) { + 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(HasAnySubstatement, MatchesForTopLevelCompoundStatement) { Index: clang/include/clang/ASTMatchers/ASTMatchersInternal.h =================================================================== --- clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -1835,18 +1835,18 @@ 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) {
Index: clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp =================================================================== --- clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -1454,10 +1454,30 @@ 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))); +} + +TEST(HasBody, FindsBodyOfFunctionChildren) { + 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(HasAnySubstatement, MatchesForTopLevelCompoundStatement) { Index: clang/include/clang/ASTMatchers/ASTMatchersInternal.h =================================================================== --- clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -1835,18 +1835,18 @@ 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) {
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits