]> granicus.if.org Git - clang/commitdiff
[ASTMatchers] New matcher forFunction
authorGabor Horvath <xazax.hun@gmail.com>
Wed, 4 May 2016 11:59:39 +0000 (11:59 +0000)
committerGabor Horvath <xazax.hun@gmail.com>
Wed, 4 May 2016 11:59:39 +0000 (11:59 +0000)
Summary: Matcher proposed in the review of checker misc-assign-operator (name pending). Its goal is to find the direct enclosing function declaration of a statement and run the inner matcher on it. Two version is attached in this patch (thus it will not compile), to be decided which approach to take. The second one always chooses one single parent while the first one does a depth-first search upwards (thus a height-first search) and returns the first positive match of the inner matcher (thus it always returns zero or one matches, not more). Further questions: is it enough to implement it in-place, or ASTMatchersInternals or maybe ASTMatchFinder should be involved?

Reviewers: sbenza

Subscribers: aaron.ballman, klimek, o.gyorgy, xazax.hun, cfe-commits

Differential Revision: http://reviews.llvm.org/D19357

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@268490 91177308-0d34-0410-b5e6-96231b3b80d8

docs/LibASTMatchersReference.html
include/clang/ASTMatchers/ASTMatchers.h
lib/ASTMatchers/Dynamic/Registry.cpp
unittests/ASTMatchers/ASTMatchersTest.cpp

index 0f474136ee2cee1cbd22aacbe6cf23230a46c8d0..d17a32d7a48760768aed9127f36a9952fed8119e 100644 (file)
@@ -4923,6 +4923,20 @@ alignof.
 </pre></td></tr>
 
 
+<tr><td>Matcher&lt;<a href="http://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>&gt;</td><td class="name" onclick="toggle('forFunction0')"><a name="forFunction0Anchor">forFunction</a></td><td>Matcher&lt;<a href="http://clang.llvm.org/doxygen/classclang_1_1FunctionDecl.html">FunctionDecl</a>&gt; InnerMatcher</td></tr>
+<tr><td colspan="4" class="doc" id="forFunction0"><pre>Matches declaration of the function the statemenet belongs to
+
+Given:
+F&amp; operator=(const F&amp; o) {
+  std::copy_if(o.begin(), o.end(), begin(), [](V v) { return v &gt; 0; });
+  return *this;
+}
+returnStmt(forFunction(hasName("operator=")))
+  matches 'return *this'
+  but does match 'return &gt; 0'
+</pre></td></tr>
+
+
 <tr><td>Matcher&lt;<a href="http://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>&gt;</td><td class="name" onclick="toggle('sizeOfExpr0')"><a name="sizeOfExpr0Anchor">sizeOfExpr</a></td><td>Matcher&lt;<a href="http://clang.llvm.org/doxygen/classclang_1_1UnaryExprOrTypeTraitExpr.html">UnaryExprOrTypeTraitExpr</a>&gt;  InnerMatcher</td></tr>
 <tr><td colspan="4" class="doc" id="sizeOfExpr0"><pre>Same as unaryExprOrTypeTraitExpr, but only matching
 sizeof.
index 5ae71a93960facaa404166e106f2c119976d8ece..300dca9ff54dcdedf576bfb4d672355a80f21d84 100644 (file)
@@ -5108,6 +5108,45 @@ AST_MATCHER_FUNCTION(internal::Matcher<Expr>, nullPointerConstant) {
       gnuNullExpr(), cxxNullPtrLiteralExpr(),
       integerLiteral(equals(0), hasParent(expr(hasType(pointerType())))));
 }
+
+/// \brief Matches declaration of the function the statemenet 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(forFunction(hasName("operator=")))
+///   matches 'return *this'
+///   but does match 'return > 0'
+AST_MATCHER_P(Stmt, forFunction, internal::Matcher<FunctionDecl>,
+              InnerMatcher) {
+  const auto &Parents = Finder->getASTContext().getParents(Node);
+
+  llvm::SmallVector<ast_type_traits::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 {
+      for(const auto &Parent: Finder->getASTContext().getParents(CurNode))
+        Stack.push_back(Parent);
+    }
+  }
+  return false;
+}
+
 } // end namespace ast_matchers
 } // end namespace clang
 
index 9fd43332b7f36fcfe72f9579ebd87d8dd278e4d6..e43960241d7883919a79eefe3f0ab35ccfc5bac2 100644 (file)
@@ -184,6 +184,7 @@ RegistryMaps::RegistryMaps() {
   REGISTER_MATCHER(forEachDescendant);
   REGISTER_MATCHER(forEachSwitchCase);
   REGISTER_MATCHER(forField);
+  REGISTER_MATCHER(forFunction);
   REGISTER_MATCHER(forStmt);
   REGISTER_MATCHER(friendDecl);
   REGISTER_MATCHER(functionDecl);
index 332438ede1b19e5ba3a7ba00b0496d568aa174dd..adf3b27cb1334228b234f1189cee1a167d745531 100644 (file)
@@ -5572,5 +5572,40 @@ TEST(StatementMatcher, HasReturnValue) {
   EXPECT_FALSE(matches("void F() { return; }", RetVal));
 }
 
+TEST(StatementMatcher, ForFunction) {
+  const auto CppString1 =
+    "struct PosVec {"
+    "  PosVec& operator=(const PosVec&) {"
+    "    auto x = [] { return 1; };"
+    "    return *this;"
+    "  }"
+    "};";
+  const auto CppString2 =
+    "void F() {"
+    "  struct S {"
+    "    void F2() {"
+    "       return;"
+    "    }"
+    "  };"
+    "}";
+  EXPECT_TRUE(
+    matches(
+      CppString1,
+      returnStmt(forFunction(hasName("operator=")),
+                 has(unaryOperator(hasOperatorName("*"))))));
+  EXPECT_TRUE(
+    notMatches(
+      CppString1,
+      returnStmt(forFunction(hasName("operator=")),
+                 has(integerLiteral()))));
+  EXPECT_TRUE(
+    matches(
+      CppString1,
+      returnStmt(forFunction(hasName("operator()")),
+                 has(integerLiteral()))));
+  EXPECT_TRUE(matches(CppString2, returnStmt(forFunction(hasName("F2")))));
+  EXPECT_TRUE(notMatches(CppString2, returnStmt(forFunction(hasName("F")))));
+}
+
 } // end namespace ast_matchers
 } // end namespace clang