This revision was landed with ongoing or failed builds. This revision was automatically updated to reflect the committed changes. Closed by commit rG6a079dfdc992: [ASTMatchers] Add forCallable(), a generalization of forFunction(). (authored by dergachev.a). Herald added a project: clang.
Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D102213/new/ https://reviews.llvm.org/D102213 Files: clang/docs/LibASTMatchersReference.html clang/include/clang/ASTMatchers/ASTMatchers.h clang/lib/ASTMatchers/Dynamic/Registry.cpp clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
Index: clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp =================================================================== --- clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -5524,6 +5524,83 @@ EXPECT_TRUE(notMatches(CppString2, returnStmt(forFunction(hasName("F"))))); } +TEST(StatementMatcher, ForCallable) { + // These tests are copied over from the forFunction() test above. + StringRef CppString1 = "struct PosVec {" + " PosVec& operator=(const PosVec&) {" + " auto x = [] { return 1; };" + " return *this;" + " }" + "};"; + StringRef CppString2 = "void F() {" + " struct S {" + " void F2() {" + " return;" + " }" + " };" + "}"; + + EXPECT_TRUE( + matches( + CppString1, + returnStmt(forCallable(functionDecl(hasName("operator="))), + has(unaryOperator(hasOperatorName("*")))))); + EXPECT_TRUE( + notMatches( + CppString1, + returnStmt(forCallable(functionDecl(hasName("operator="))), + has(integerLiteral())))); + EXPECT_TRUE( + matches( + CppString1, + returnStmt(forCallable(functionDecl(hasName("operator()"))), + has(integerLiteral())))); + EXPECT_TRUE(matches(CppString2, + returnStmt(forCallable(functionDecl(hasName("F2")))))); + EXPECT_TRUE(notMatches(CppString2, + returnStmt(forCallable(functionDecl(hasName("F")))))); + + // These tests are specific to forCallable(). + StringRef ObjCString1 = "@interface I" + "-(void) foo;" + "@end" + "@implementation I" + "-(void) foo {" + " void (^block)() = ^{ 0x2b | ~0x2b; };" + "}" + "@end"; + + EXPECT_TRUE( + matchesObjC( + ObjCString1, + binaryOperator(forCallable(blockDecl())))); + + EXPECT_TRUE( + notMatchesObjC( + ObjCString1, + binaryOperator(forCallable(objcMethodDecl())))); + + StringRef ObjCString2 = "@interface I" + "-(void) foo;" + "@end" + "@implementation I" + "-(void) foo {" + " 0x2b | ~0x2b;" + " void (^block)() = ^{};" + "}" + "@end"; + + EXPECT_TRUE( + matchesObjC( + ObjCString2, + binaryOperator(forCallable(objcMethodDecl())))); + + EXPECT_TRUE( + notMatchesObjC( + ObjCString2, + binaryOperator(forCallable(blockDecl())))); +} + TEST(Matcher, ForEachOverriden) { const auto ForEachOverriddenInClass = [](const char *ClassName) { return cxxMethodDecl(ofClass(hasName(ClassName)), isVirtual(), Index: clang/lib/ASTMatchers/Dynamic/Registry.cpp =================================================================== --- clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -236,6 +236,7 @@ REGISTER_MATCHER(fieldDecl); REGISTER_MATCHER(fixedPointLiteral); REGISTER_MATCHER(floatLiteral); + REGISTER_MATCHER(forCallable); REGISTER_MATCHER(forDecomposition); REGISTER_MATCHER(forEach); REGISTER_MATCHER(forEachArgumentWithParam); Index: clang/include/clang/ASTMatchers/ASTMatchers.h =================================================================== --- clang/include/clang/ASTMatchers/ASTMatchers.h +++ clang/include/clang/ASTMatchers/ASTMatchers.h @@ -7545,6 +7545,11 @@ /// Matches declaration of the function the statement belongs to. /// +/// Deprecated. Use forCallable() to correctly handle the situation when +/// the declaration is not a function (but a block or an Objective-C method). +/// forFunction() not only fails to take non-functions into account but also +/// may match the wrong declaration in their presence. +/// /// Given: /// \code /// F& operator=(const F& o) { @@ -7580,6 +7585,65 @@ return false; } +/// Matches declaration of the function, method, or block the statement +/// belongs to. +/// +/// Given: +/// \code +/// F& operator=(const F& o) { +/// std::copy_if(o.begin(), o.end(), begin(), [](V v) { return v > 0; }); +/// return *this; +/// } +/// \endcode +/// returnStmt(forCallable(functionDecl(hasName("operator=")))) +/// matches 'return *this' +/// but does not match 'return v > 0' +/// +/// Given: +/// \code +/// -(void) foo { +/// int x = 1; +/// dispatch_sync(queue, ^{ int y = 2; }); +/// } +/// \endcode +/// declStmt(forCallable(objcMethodDecl())) +/// matches 'int x = 1' +/// but does not match 'int y = 2'. +/// whereas declStmt(forCallable(blockDecl())) +/// matches 'int y = 2' +/// but does not match 'int x = 1'. +AST_MATCHER_P(Stmt, forCallable, internal::Matcher<Decl>, InnerMatcher) { + const auto &Parents = Finder->getASTContext().getParents(Node); + + llvm::SmallVector<DynTypedNode, 8> Stack(Parents.begin(), Parents.end()); + while (!Stack.empty()) { + const auto &CurNode = Stack.back(); + Stack.pop_back(); + if (const auto *FuncDeclNode = CurNode.get<FunctionDecl>()) { + if (InnerMatcher.matches(*FuncDeclNode, Finder, Builder)) { + return true; + } + } else if (const auto *LambdaExprNode = CurNode.get<LambdaExpr>()) { + if (InnerMatcher.matches(*LambdaExprNode->getCallOperator(), Finder, + Builder)) { + return true; + } + } else if (const auto *ObjCMethodDeclNode = CurNode.get<ObjCMethodDecl>()) { + if (InnerMatcher.matches(*ObjCMethodDeclNode, Finder, Builder)) { + return true; + } + } else if (const auto *BlockDeclNode = CurNode.get<BlockDecl>()) { + if (InnerMatcher.matches(*BlockDeclNode, Finder, Builder)) { + return true; + } + } else { + for (const auto &Parent : Finder->getASTContext().getParents(CurNode)) + Stack.push_back(Parent); + } + } + return false; +} + /// Matches a declaration that has external formal linkage. /// /// Example matches only z (matcher = varDecl(hasExternalFormalLinkage())) Index: clang/docs/LibASTMatchersReference.html =================================================================== --- clang/docs/LibASTMatchersReference.html +++ clang/docs/LibASTMatchersReference.html @@ -8813,9 +8813,41 @@ </pre></td></tr> +<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>></td><td class="name" onclick="toggle('forCallable0')"><a name="forCallable0Anchor">forCallable</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>> InnerMatcher</td></tr> +<tr><td colspan="4" class="doc" id="forCallable0"><pre>Matches declaration of the function, method, or block the statement +belongs to. + +Given: +F& operator=(const F& o) { + std::copy_if(o.begin(), o.end(), begin(), [](V v) { return v > 0; }); + return *this; +} +returnStmt(forCallable(functionDecl(hasName("operator=")))) + matches 'return *this' + but does not match 'return v > 0' + +Given: +-(void) foo { + int x = 1; + dispatch_sync(queue, ^{ int y = 2; }); +} +declStmt(forCallable(objcMethodDecl())) + matches 'int x = 1' + but does not match 'int y = 2'. +whereas declStmt(forCallable(blockDecl())) + matches 'int y = 2' + but does not match 'int x = 1'. +</pre></td></tr> + + <tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>></td><td class="name" onclick="toggle('forFunction0')"><a name="forFunction0Anchor">forFunction</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1FunctionDecl.html">FunctionDecl</a>> InnerMatcher</td></tr> <tr><td colspan="4" class="doc" id="forFunction0"><pre>Matches declaration of the function the statement belongs to. +Deprecated. Use forCallable() to correctly handle the situation when +the declaration is not a function (but a block or an Objective-C method). +forFunction() not only fails to take non-functions into account but also +may match the wrong declaration in their presence. + Given: F& operator=(const F& o) { std::copy_if(o.begin(), o.end(), begin(), [](V v) { return v > 0; });
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits