diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -8813,9 +8813,41 @@ +
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'. +
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; }); 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 @@ -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, InnerMatcher) { + const auto &Parents = Finder->getASTContext().getParents(Node); + + llvm::SmallVector Stack(Parents.begin(), Parents.end()); + while (!Stack.empty()) { + const auto &CurNode = Stack.back(); + Stack.pop_back(); + if (const auto *FuncDeclNode = CurNode.get ()) { + if (InnerMatcher.matches(*FuncDeclNode, Finder, Builder)) { + return true; + } + } else if (const auto *LambdaExprNode = CurNode.get ()) { + if (InnerMatcher.matches(*LambdaExprNode->getCallOperator(), Finder, + Builder)) { + return true; + } + } else if (const auto *ObjCMethodDeclNode = CurNode.get ()) { + if (InnerMatcher.matches(*ObjCMethodDeclNode, Finder, Builder)) { + return true; + } + } else if (const auto *BlockDeclNode = CurNode.get ()) { + 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())) diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp --- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ b/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); 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 @@ -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(),