]> granicus.if.org Git - clang/commitdiff
[ASTMatchers] New forEachOverriden matcher.
authorClement Courbet <courbet@google.com>
Tue, 5 Jul 2016 07:49:31 +0000 (07:49 +0000)
committerClement Courbet <courbet@google.com>
Tue, 5 Jul 2016 07:49:31 +0000 (07:49 +0000)
Matches methods overridden by the given method.

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

docs/LibASTMatchersReference.html
include/clang/AST/ASTContext.h
include/clang/AST/DeclCXX.h
include/clang/ASTMatchers/ASTMatchers.h
lib/AST/ASTContext.cpp
lib/AST/DeclCXX.cpp
unittests/ASTMatchers/ASTMatchersTraversalTest.cpp

index 6f06b9164a4fae1a8284a1db1cd71b02d5229522..fff5366775cd2a08718fac8877ac1718c1b7bfa0 100644 (file)
@@ -3994,6 +3994,30 @@ matcher, or is a pointer to a type that matches the InnerMatcher.
 </pre></td></tr>
 
 
+<tr><td>Matcher&lt;<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXMethodDecl.html">CXXMethodDecl</a>&gt;</td><td class="name" onclick="toggle('forEachOverridden0')"><a name="forEachOverridden0Anchor">forEachOverridden</a></td><td>Matcher&lt;<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXMethodDecl.html">CXXMethodDecl</a>&gt; InnerMatcher</td></tr>
+<tr><td colspan="4" class="doc" id="forEachOverridden0"><pre>Matches each method overriden by the given method. This matcher may
+produce multiple matches.
+
+Given
+  class A { virtual void f(); };
+  class B : public A { void f(); };
+  class C : public B { void f(); };
+cxxMethodDecl(ofClass(hasName("C")),
+              forEachOverridden(cxxMethodDecl().bind("b"))).bind("d")
+  matches once, with "b" binding "A::f" and "d" binding "C::f" (Note
+  that B::f is not overridden by C::f).
+
+The check can produce multiple matches in case of multiple inheritance, e.g.
+  class A1 { virtual void f(); };
+  class A2 { virtual void f(); };
+  class C : public A1, public A2 { void f(); };
+cxxMethodDecl(ofClass(hasName("C")),
+              forEachOverridden(cxxMethodDecl().bind("b"))).bind("d")
+  matches twice, once with "b" binding "A1::f" and "d" binding "C::f", and
+  once with "b" binding "A2::f" and "d" binding "C::f".
+</pre></td></tr>
+
+
 <tr><td>Matcher&lt;<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXMethodDecl.html">CXXMethodDecl</a>&gt;</td><td class="name" onclick="toggle('ofClass0')"><a name="ofClass0Anchor">ofClass</a></td><td>Matcher&lt;<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html">CXXRecordDecl</a>&gt; InnerMatcher</td></tr>
 <tr><td colspan="4" class="doc" id="ofClass0"><pre>Matches the class declaration that the given method declaration
 belongs to.
index 93a0b48133c67044245edba5f53215f0fa15740f..2efef57c4f89300894c9b2d964fac7e3b8aa50af 100644 (file)
@@ -825,6 +825,9 @@ public:
   overridden_methods_end(const CXXMethodDecl *Method) const;
 
   unsigned overridden_methods_size(const CXXMethodDecl *Method) const;
+  typedef llvm::iterator_range<overridden_cxx_method_iterator>
+      overridden_method_range;
+  overridden_method_range overridden_methods(const CXXMethodDecl *Method) const;
 
   /// \brief Note that the given C++ \p Method overrides the given \p
   /// Overridden method.
index a8f00cb327dae582cab01a5856d0f78c0d181a59..66acfee60db056b412894c88b356096af1a5f5de 100644 (file)
@@ -16,6 +16,7 @@
 #ifndef LLVM_CLANG_AST_DECLCXX_H
 #define LLVM_CLANG_AST_DECLCXX_H
 
+#include "clang/AST/ASTContext.h"
 #include "clang/AST/ASTUnresolvedSet.h"
 #include "clang/AST/Attr.h"
 #include "clang/AST/Decl.h"
@@ -1829,6 +1830,8 @@ public:
   method_iterator begin_overridden_methods() const;
   method_iterator end_overridden_methods() const;
   unsigned size_overridden_methods() const;
+  typedef ASTContext::overridden_method_range overridden_method_range;
+  overridden_method_range overridden_methods() const;
 
   /// Returns the parent of this method declaration, which
   /// is the class in which this method is defined.
index 23d2f84f67a32ccd2ccd33907ae2db5fe6753d72..5ba154d335ae35561210b15575230ffe658c1a99 100644 (file)
@@ -3809,6 +3809,47 @@ AST_MATCHER_P(CXXMethodDecl, ofClass,
           InnerMatcher.matches(*Parent, Finder, Builder));
 }
 
+/// \brief Matches each method overriden by the given method. This matcher may
+/// produce multiple matches.
+///
+/// Given
+/// \code
+///   class A { virtual void f(); };
+///   class B : public A { void f(); };
+///   class C : public B { void f(); };
+/// \endcode
+/// cxxMethodDecl(ofClass(hasName("C")),
+///               forEachOverridden(cxxMethodDecl().bind("b"))).bind("d")
+///   matches once, with "b" binding "A::f" and "d" binding "C::f" (Note
+///   that B::f is not overridden by C::f).
+///
+/// The check can produce multiple matches in case of multiple inheritance, e.g.
+/// \code
+///   class A1 { virtual void f(); };
+///   class A2 { virtual void f(); };
+///   class C : public A1, public A2 { void f(); };
+/// \endcode
+/// cxxMethodDecl(ofClass(hasName("C")),
+///               forEachOverridden(cxxMethodDecl().bind("b"))).bind("d")
+///   matches twice, once with "b" binding "A1::f" and "d" binding "C::f", and
+///   once with "b" binding "A2::f" and "d" binding "C::f".
+AST_MATCHER_P(CXXMethodDecl, forEachOverridden,
+              internal::Matcher<CXXMethodDecl>, InnerMatcher) {
+  BoundNodesTreeBuilder Result;
+  bool Matched = false;
+  for (const auto *Overridden : Node.overridden_methods()) {
+    BoundNodesTreeBuilder OverriddenBuilder(*Builder);
+    const bool OverriddenMatched =
+        InnerMatcher.matches(*Overridden, Finder, &OverriddenBuilder);
+    if (OverriddenMatched) {
+      Matched = true;
+      Result.addMatch(OverriddenBuilder);
+    }
+  }
+  *Builder = std::move(Result);
+  return Matched;
+}
+
 /// \brief Matches if the given method declaration is virtual.
 ///
 /// Given
index 0414c7a9faf85a89bf1f42e2b567bd0ad5010392..4b127358d5aed83f914c37b7556a7de3ca7a6797 100644 (file)
@@ -1262,34 +1262,37 @@ void ASTContext::setInstantiatedFromUnnamedFieldDecl(FieldDecl *Inst,
 
 ASTContext::overridden_cxx_method_iterator
 ASTContext::overridden_methods_begin(const CXXMethodDecl *Method) const {
-  llvm::DenseMap<const CXXMethodDecl *, CXXMethodVector>::const_iterator Pos
-    = OverriddenMethods.find(Method->getCanonicalDecl());
+  llvm::DenseMap<const CXXMethodDecl *, CXXMethodVector>::const_iterator Pos =
+      OverriddenMethods.find(Method->getCanonicalDecl());
   if (Pos == OverriddenMethods.end())
     return nullptr;
-
   return Pos->second.begin();
 }
 
 ASTContext::overridden_cxx_method_iterator
 ASTContext::overridden_methods_end(const CXXMethodDecl *Method) const {
-  llvm::DenseMap<const CXXMethodDecl *, CXXMethodVector>::const_iterator Pos
-    = OverriddenMethods.find(Method->getCanonicalDecl());
+  llvm::DenseMap<const CXXMethodDecl *, CXXMethodVector>::const_iterator Pos =
+      OverriddenMethods.find(Method->getCanonicalDecl());
   if (Pos == OverriddenMethods.end())
     return nullptr;
-
   return Pos->second.end();
 }
 
 unsigned
 ASTContext::overridden_methods_size(const CXXMethodDecl *Method) const {
-  llvm::DenseMap<const CXXMethodDecl *, CXXMethodVector>::const_iterator Pos
-    = OverriddenMethods.find(Method->getCanonicalDecl());
+  llvm::DenseMap<const CXXMethodDecl *, CXXMethodVector>::const_iterator Pos =
+      OverriddenMethods.find(Method->getCanonicalDecl());
   if (Pos == OverriddenMethods.end())
     return 0;
-
   return Pos->second.size();
 }
 
+ASTContext::overridden_method_range
+ASTContext::overridden_methods(const CXXMethodDecl *Method) const {
+  return overridden_method_range(overridden_methods_begin(Method),
+                                 overridden_methods_end(Method));
+}
+
 void ASTContext::addOverriddenMethod(const CXXMethodDecl *Method, 
                                      const CXXMethodDecl *Overridden) {
   assert(Method->isCanonicalDecl() && Overridden->isCanonicalDecl());
index de06ecb4ca1f9a8e38f149746313099312b632a8..d069bfdc3dc28cdc43b535b17a8b6f72d53a19eb 100644 (file)
@@ -1651,6 +1651,13 @@ unsigned CXXMethodDecl::size_overridden_methods() const {
   return getASTContext().overridden_methods_size(this);
 }
 
+CXXMethodDecl::overridden_method_range
+CXXMethodDecl::overridden_methods() const {
+  if (isa<CXXConstructorDecl>(this))
+    return overridden_method_range(nullptr, nullptr);
+  return getASTContext().overridden_methods(this);
+}
+
 QualType CXXMethodDecl::getThisType(ASTContext &C) const {
   // C++ 9.3.2p1: The type of this in a member function of a class X is X*.
   // If the member function is declared const, the type of this is const X*,
index a3f616967df6699209f2ee94f187d086da20a9c2..4149d4fd0b114290f36d1f23543cc7958ee2e4bd 100644 (file)
@@ -2007,5 +2007,49 @@ TEST(StatementMatcher, ForFunction) {
   EXPECT_TRUE(notMatches(CppString2, returnStmt(forFunction(hasName("F")))));
 }
 
+TEST(Matcher, ForEachOverriden) {
+  const auto ForEachOverriddenInClass = [](const char *ClassName) {
+    return cxxMethodDecl(ofClass(hasName(ClassName)), isVirtual(),
+                         forEachOverridden(cxxMethodDecl().bind("overridden")))
+        .bind("override");
+  };
+  static const char Code1[] = "class A { virtual void f(); };"
+                              "class B : public A { void f(); };"
+                              "class C : public B { void f(); };";
+  // C::f overrides A::f.
+  EXPECT_TRUE(matchAndVerifyResultTrue(
+      Code1, ForEachOverriddenInClass("C"),
+      llvm::make_unique<VerifyIdIsBoundTo<CXXMethodDecl>>("override", "f", 1)));
+  EXPECT_TRUE(matchAndVerifyResultTrue(
+      Code1, ForEachOverriddenInClass("C"),
+      llvm::make_unique<VerifyIdIsBoundTo<CXXMethodDecl>>("overridden", "f",
+                                                          1)));
+  // B::f overrides A::f.
+  EXPECT_TRUE(matchAndVerifyResultTrue(
+      Code1, ForEachOverriddenInClass("B"),
+      llvm::make_unique<VerifyIdIsBoundTo<CXXMethodDecl>>("override", "f", 1)));
+  EXPECT_TRUE(matchAndVerifyResultTrue(
+      Code1, ForEachOverriddenInClass("B"),
+      llvm::make_unique<VerifyIdIsBoundTo<CXXMethodDecl>>("overridden", "f",
+                                                          1)));
+  // A::f overrides nothing.
+  EXPECT_TRUE(notMatches(Code1, ForEachOverriddenInClass("A")));
+
+  static const char Code2[] =
+      "class A1 { virtual void f(); };"
+      "class A2 { virtual void f(); };"
+      "class B : public A1, public A2 { void f(); };";
+  // B::f overrides A1::f and A2::f. This produces two matches.
+  EXPECT_TRUE(matchAndVerifyResultTrue(
+      Code2, ForEachOverriddenInClass("B"),
+      llvm::make_unique<VerifyIdIsBoundTo<CXXMethodDecl>>("override", "f", 2)));
+  EXPECT_TRUE(matchAndVerifyResultTrue(
+      Code2, ForEachOverriddenInClass("B"),
+      llvm::make_unique<VerifyIdIsBoundTo<CXXMethodDecl>>("overridden", "f",
+                                                          2)));
+  // A1::f overrides nothing.
+  EXPECT_TRUE(notMatches(Code2, ForEachOverriddenInClass("A1")));
+}
+
 } // namespace ast_matchers
 } // namespace clang