r261574 - [ASTMatchers] Add matcher hasAnyName.

Samuel Benzaquen via cfe-commits cfe-commits at lists.llvm.org
Mon Feb 22 13:13:03 PST 2016


Author: sbenza
Date: Mon Feb 22 15:13:02 2016
New Revision: 261574

URL: http://llvm.org/viewvc/llvm-project?rev=261574&view=rev
Log:
[ASTMatchers] Add matcher hasAnyName.

Summary: Add matcher hasAnyName as an optimization over anyOf(hasName(),...)

Reviewers: alexfh

Subscribers: klimek, cfe-commits

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

Modified:
    cfe/trunk/docs/LibASTMatchersReference.html
    cfe/trunk/docs/tools/dump_ast_matchers.py
    cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h
    cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h
    cfe/trunk/lib/ASTMatchers/ASTMatchersInternal.cpp
    cfe/trunk/lib/ASTMatchers/Dynamic/Registry.cpp
    cfe/trunk/unittests/ASTMatchers/ASTMatchersTest.cpp

Modified: cfe/trunk/docs/LibASTMatchersReference.html
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/LibASTMatchersReference.html?rev=261574&r1=261573&r2=261574&view=diff
==============================================================================
--- cfe/trunk/docs/LibASTMatchersReference.html (original)
+++ cfe/trunk/docs/LibASTMatchersReference.html Mon Feb 22 15:13:02 2016
@@ -3102,6 +3102,16 @@ expr(nullPointerConstant())
 </pre></td></tr>
 
 
+<tr><td>Matcher<internal::Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1NamedDecl.html">NamedDecl</a>>></td><td class="name" onclick="toggle('hasAnyName0')"><a name="hasAnyName0Anchor">hasAnyName</a></td><td>StringRef, ..., StringRef</td></tr>
+<tr><td colspan="4" class="doc" id="hasAnyName0"><pre>Matches NamedDecl nodes that have any of the specified names.
+
+This matcher is only provided as a performance optimization of hasName.
+    hasAnyName(a, b, c)
+ is equivalent but faster than
+    anyOf(hasName(a), hasName(b), hasName(c))
+</pre></td></tr>
+
+
 <tr><td>Matcher<internal::Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>>></td><td class="name" onclick="toggle('isInTemplateInstantiation0')"><a name="isInTemplateInstantiation0Anchor">isInTemplateInstantiation</a></td><td></td></tr>
 <tr><td colspan="4" class="doc" id="isInTemplateInstantiation0"><pre>Matches statements inside of a template instantiation.
 

Modified: cfe/trunk/docs/tools/dump_ast_matchers.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/tools/dump_ast_matchers.py?rev=261574&r1=261573&r2=261574&view=diff
==============================================================================
--- cfe/trunk/docs/tools/dump_ast_matchers.py (original)
+++ cfe/trunk/docs/tools/dump_ast_matchers.py Mon Feb 22 15:13:02 2016
@@ -264,6 +264,16 @@ def act_on_decl(declaration, comment, al
       add_matcher('*', name, 'Matcher<*>', comment)
       return
 
+    # Parse Variadic functions.
+    m = re.match(
+        r"""^.*llvm::VariadicFunction\s*<\s*([^,]+),\s*([^,]+),\s*[^>]+>\s*
+              ([a-zA-Z]*)\s*=\s*{.*};$""",
+        declaration, flags=re.X)
+    if m:
+      result, arg, name = m.groups()[:3]
+      add_matcher(result, name, '%s, ..., %s' % (arg, arg), comment)
+      return
+
     # Parse Variadic operator matchers.
     m = re.match(
         r"""^.*VariadicOperatorMatcherFunc\s*<\s*([^,]+),\s*([^\s>]+)\s*>\s*

Modified: cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h?rev=261574&r1=261573&r2=261574&view=diff
==============================================================================
--- cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h (original)
+++ cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h Mon Feb 22 15:13:02 2016
@@ -1844,11 +1844,24 @@ inline internal::Matcher<Stmt> sizeOfExp
 /// \code
 ///   namespace a { namespace b { class X; } }
 /// \endcode
-inline internal::Matcher<NamedDecl> hasName(std::string Name) {
-  return internal::Matcher<NamedDecl>(
-      new internal::HasNameMatcher(std::move(Name)));
+inline internal::Matcher<NamedDecl> hasName(const std::string &Name) {
+  return internal::Matcher<NamedDecl>(new internal::HasNameMatcher({Name}));
 }
 
+/// \brief Matches NamedDecl nodes that have any of the specified names.
+///
+/// This matcher is only provided as a performance optimization of hasName.
+/// \code
+///     hasAnyName(a, b, c)
+/// \endcode
+///  is equivalent to, but faster than
+/// \code
+///     anyOf(hasName(a), hasName(b), hasName(c))
+/// \endcode
+const llvm::VariadicFunction<internal::Matcher<NamedDecl>, StringRef,
+                             internal::hasAnyNameFunc>
+    hasAnyName = {};
+
 /// \brief Matches NamedDecl nodes whose fully qualified names contain
 /// a substring matched by the given RegExp.
 ///

Modified: cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h?rev=261574&r1=261573&r2=261574&view=diff
==============================================================================
--- cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h (original)
+++ cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h Mon Feb 22 15:13:02 2016
@@ -637,10 +637,10 @@ private:
 
 /// \brief Matches named declarations with a specific name.
 ///
-/// See \c hasName() in ASTMatchers.h for details.
+/// See \c hasName() and \c hasAnyName() in ASTMatchers.h for details.
 class HasNameMatcher : public SingleNodeMatcherInterface<NamedDecl> {
  public:
-  explicit HasNameMatcher(std::string Name);
+  explicit HasNameMatcher(std::vector<std::string> Names);
 
   bool matchesNode(const NamedDecl &Node) const override;
 
@@ -667,9 +667,13 @@ class HasNameMatcher : public SingleNode
   bool matchesNodeFullSlow(const NamedDecl &Node) const;
 
   const bool UseUnqualifiedMatch;
-  const std::string Name;
+  const std::vector<std::string> Names;
 };
 
+/// \brief Trampoline function to use VariadicFunction<> to construct a
+///        HasNameMatcher.
+Matcher<NamedDecl> hasAnyNameFunc(ArrayRef<const StringRef *> NameRefs);
+
 /// \brief Matches declarations for QualType and CallExpr.
 ///
 /// Type argument DeclMatcherT is required by PolymorphicMatcherWithParam1 but

Modified: cfe/trunk/lib/ASTMatchers/ASTMatchersInternal.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/ASTMatchers/ASTMatchersInternal.cpp?rev=261574&r1=261573&r2=261574&view=diff
==============================================================================
--- cfe/trunk/lib/ASTMatchers/ASTMatchersInternal.cpp (original)
+++ cfe/trunk/lib/ASTMatchers/ASTMatchersInternal.cpp Mon Feb 22 15:13:02 2016
@@ -14,6 +14,7 @@
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/ASTMatchers/ASTMatchersInternal.h"
 #include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/ManagedStatic.h"
 
 namespace clang {
@@ -293,15 +294,26 @@ bool AnyOfVariadicOperator(const ast_typ
   return false;
 }
 
-HasNameMatcher::HasNameMatcher(std::string NameRef)
-    : UseUnqualifiedMatch(NameRef.find("::") == NameRef.npos),
-      Name(std::move(NameRef)) {
-  assert(!Name.empty());
+Matcher<NamedDecl> hasAnyNameFunc(ArrayRef<const StringRef *> NameRefs) {
+  std::vector<std::string> Names;
+  for (auto *Name : NameRefs)
+    Names.emplace_back(*Name);
+  return internal::Matcher<NamedDecl>(
+      new internal::HasNameMatcher(std::move(Names)));
+}
+
+HasNameMatcher::HasNameMatcher(std::vector<std::string> N)
+    : UseUnqualifiedMatch(std::all_of(
+          N.begin(), N.end(),
+          [](StringRef Name) { return Name.find("::") == Name.npos; })),
+      Names(std::move(N)) {
+  for (StringRef Name : Names)
+    assert(!Name.empty());
 }
 
 namespace {
 
-bool ConsumeNameSuffix(StringRef &FullName, StringRef Suffix) {
+bool consumeNameSuffix(StringRef &FullName, StringRef Suffix) {
   StringRef Name = FullName;
   if (!Name.endswith(Suffix))
     return false;
@@ -315,42 +327,101 @@ bool ConsumeNameSuffix(StringRef &FullNa
   return true;
 }
 
-bool ConsumeNodeName(StringRef &Name, const NamedDecl &Node) {
+StringRef getNodeName(const NamedDecl &Node, llvm::SmallString<128> &Scratch) {
   // Simple name.
   if (Node.getIdentifier())
-    return ConsumeNameSuffix(Name, Node.getName());
+    return Node.getName();
 
   if (Node.getDeclName()) {
     // Name needs to be constructed.
-    llvm::SmallString<128> NodeName;
-    llvm::raw_svector_ostream OS(NodeName);
+    Scratch.clear();
+    llvm::raw_svector_ostream OS(Scratch);
     Node.printName(OS);
-    return ConsumeNameSuffix(Name, OS.str());
+    return OS.str();
   }
 
-  return ConsumeNameSuffix(Name, "(anonymous)");
+  return "(anonymous)";
 }
 
+StringRef getNodeName(const RecordDecl &Node, llvm::SmallString<128> &Scratch) {
+  if (Node.getIdentifier()) {
+    return Node.getName();
+  }
+  Scratch.clear();
+  return ("(anonymous " + Node.getKindName() + ")").toStringRef(Scratch);
+}
+
+StringRef getNodeName(const NamespaceDecl &Node,
+                      llvm::SmallString<128> &Scratch) {
+  return Node.isAnonymousNamespace() ? "(anonymous namespace)" : Node.getName();
+}
+
+
+class PatternSet {
+public:
+  PatternSet(ArrayRef<std::string> Names) {
+    for (StringRef Name : Names)
+      Patterns.push_back({Name, Name.startswith("::")});
+  }
+
+  /// Consumes the name suffix from each pattern in the set and removes the ones
+  /// that didn't match.
+  /// Return true if there are still any patterns left.
+  bool consumeNameSuffix(StringRef NodeName, bool CanSkip) {
+    for (size_t I = 0; I < Patterns.size();) {
+      if (internal::consumeNameSuffix(Patterns[I].Pattern, NodeName) ||
+          CanSkip) {
+        ++I;
+      } else {
+        Patterns.erase(Patterns.begin() + I);
+      }
+    }
+    return !Patterns.empty();
+  }
+
+  /// Check if any of the patterns are a match.
+  /// A match will be a pattern that was fully consumed, that also matches the
+  /// 'fully qualified' requirement.
+  bool foundMatch(bool AllowFullyQualified) const {
+    for (auto& P: Patterns)
+      if (P.Pattern.empty() && (AllowFullyQualified || !P.IsFullyQualified))
+        return true;
+    return false;
+  }
+
+private:
+  struct Pattern {
+    StringRef Pattern;
+    bool IsFullyQualified;
+  };
+  llvm::SmallVector<Pattern, 8> Patterns;
+};
+
 }  // namespace
 
 bool HasNameMatcher::matchesNodeUnqualified(const NamedDecl &Node) const {
   assert(UseUnqualifiedMatch);
-  StringRef NodeName = Name;
-  return ConsumeNodeName(NodeName, Node) && NodeName.empty();
+  llvm::SmallString<128> Scratch;
+  StringRef NodeName = getNodeName(Node, Scratch);
+  return std::any_of(Names.begin(), Names.end(), [&](StringRef Name) {
+    return consumeNameSuffix(Name, NodeName) && Name.empty();
+  });
 }
 
 bool HasNameMatcher::matchesNodeFullFast(const NamedDecl &Node) const {
+  PatternSet Patterns(Names);
+  llvm::SmallString<128> Scratch;
+
   // This function is copied and adapted from NamedDecl::printQualifiedName()
   // By matching each part individually we optimize in a couple of ways:
   //  - We can exit early on the first failure.
   //  - We can skip inline/anonymous namespaces without another pass.
   //  - We print one name at a time, reducing the chance of overflowing the
   //    inlined space of the SmallString.
-  StringRef Pattern = Name;
-  const bool IsFullyQualified = Pattern.startswith("::");
 
   // First, match the name.
-  if (!ConsumeNodeName(Pattern, Node))
+  if (!Patterns.consumeNameSuffix(getNodeName(Node, Scratch),
+                                  /*CanSkip=*/false))
     return false;
 
   // Try to match each declaration context.
@@ -358,36 +429,25 @@ bool HasNameMatcher::matchesNodeFullFast
   const DeclContext *Ctx = Node.getDeclContext();
 
   if (Ctx->isFunctionOrMethod())
-    return Pattern.empty() && !IsFullyQualified;
+    return Patterns.foundMatch(/*AllowFullyQualified=*/false);
 
-  for (; !Pattern.empty() && Ctx && isa<NamedDecl>(Ctx);
-       Ctx = Ctx->getParent()) {
-    if (const auto *ND = dyn_cast<NamespaceDecl>(Ctx)) {
-      StringRef NSName =
-          ND->isAnonymousNamespace() ? "(anonymous namespace)" : ND->getName();
+  for (; Ctx && isa<NamedDecl>(Ctx); Ctx = Ctx->getParent()) {
+    if (Patterns.foundMatch(/*AllowFullyQualified=*/false))
+      return true;
 
-      // If it matches, continue.
-      if (ConsumeNameSuffix(Pattern, NSName))
-        continue;
-      // If it didn't match but we can skip it, continue.
-      if (ND->isAnonymousNamespace() || ND->isInline())
+    if (const auto *ND = dyn_cast<NamespaceDecl>(Ctx)) {
+      // If it matches (or we can skip it), continue.
+      if (Patterns.consumeNameSuffix(getNodeName(*ND, Scratch),
+                                     /*CanSkip=*/ND->isAnonymousNamespace() ||
+                                         ND->isInline()))
         continue;
-
       return false;
     }
     if (const auto *RD = dyn_cast<RecordDecl>(Ctx)) {
       if (!isa<ClassTemplateSpecializationDecl>(Ctx)) {
-        if (RD->getIdentifier()) {
-          if (ConsumeNameSuffix(Pattern, RD->getName()))
-            continue;
-        } else {
-          llvm::SmallString<128> NodeName;
-          NodeName += StringRef("(anonymous ");
-          NodeName += RD->getKindName();
-          NodeName += ')';
-          if (ConsumeNameSuffix(Pattern, NodeName))
-            continue;
-        }
+        if (Patterns.consumeNameSuffix(getNodeName(*RD, Scratch),
+                                       /*CanSkip=*/false))
+          continue;
 
         return false;
       }
@@ -398,16 +458,10 @@ bool HasNameMatcher::matchesNodeFullFast
     return matchesNodeFullSlow(Node);
   }
 
-  // If we are fully qualified, we must not have any leftover context.
-  if (IsFullyQualified && Ctx && isa<NamedDecl>(Ctx))
-    return false;
-
-  return Pattern.empty();
+  return Patterns.foundMatch(/*AllowFullyQualified=*/true);
 }
 
 bool HasNameMatcher::matchesNodeFullSlow(const NamedDecl &Node) const {
-  const StringRef Pattern = Name;
-
   const bool SkipUnwrittenCases[] = {false, true};
   for (bool SkipUnwritten : SkipUnwrittenCases) {
     llvm::SmallString<128> NodeName = StringRef("::");
@@ -423,12 +477,14 @@ bool HasNameMatcher::matchesNodeFullSlow
 
     const StringRef FullName = OS.str();
 
-    if (Pattern.startswith("::")) {
-      if (FullName == Pattern)
+    for (const StringRef Pattern : Names) {
+      if (Pattern.startswith("::")) {
+        if (FullName == Pattern)
+          return true;
+      } else if (FullName.endswith(Pattern) &&
+                 FullName.drop_back(Pattern.size()).endswith("::")) {
         return true;
-    } else if (FullName.endswith(Pattern) &&
-               FullName.drop_back(Pattern.size()).endswith("::")) {
-      return true;
+      }
     }
   }
 

Modified: cfe/trunk/lib/ASTMatchers/Dynamic/Registry.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/ASTMatchers/Dynamic/Registry.cpp?rev=261574&r1=261573&r2=261574&view=diff
==============================================================================
--- cfe/trunk/lib/ASTMatchers/Dynamic/Registry.cpp (original)
+++ cfe/trunk/lib/ASTMatchers/Dynamic/Registry.cpp Mon Feb 22 15:13:02 2016
@@ -191,6 +191,7 @@ RegistryMaps::RegistryMaps() {
   REGISTER_MATCHER(hasAncestor);
   REGISTER_MATCHER(hasAnyArgument);
   REGISTER_MATCHER(hasAnyConstructorInitializer);
+  REGISTER_MATCHER(hasAnyName);
   REGISTER_MATCHER(hasAnyParameter);
   REGISTER_MATCHER(hasAnySubstatement);
   REGISTER_MATCHER(hasAnyTemplateArgument);

Modified: cfe/trunk/unittests/ASTMatchers/ASTMatchersTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/ASTMatchers/ASTMatchersTest.cpp?rev=261574&r1=261573&r2=261574&view=diff
==============================================================================
--- cfe/trunk/unittests/ASTMatchers/ASTMatchersTest.cpp (original)
+++ cfe/trunk/unittests/ASTMatchers/ASTMatchersTest.cpp Mon Feb 22 15:13:02 2016
@@ -2881,6 +2881,19 @@ TEST(Matcher, HasNameSupportsFunctionSco
   EXPECT_TRUE(matches(code, fieldDecl(hasName("::a::F(int)::S::m"))));
 }
 
+TEST(Matcher, HasAnyName) {
+  const std::string Code = "namespace a { namespace b { class C; } }";
+
+  EXPECT_TRUE(matches(Code, recordDecl(hasAnyName("XX", "a::b::C"))));
+  EXPECT_TRUE(matches(Code, recordDecl(hasAnyName("a::b::C", "XX"))));
+  EXPECT_TRUE(matches(Code, recordDecl(hasAnyName("XX::C", "a::b::C"))));
+  EXPECT_TRUE(matches(Code, recordDecl(hasAnyName("XX", "C"))));
+
+  EXPECT_TRUE(notMatches(Code, recordDecl(hasAnyName("::C", "::b::C"))));
+  EXPECT_TRUE(
+      matches(Code, recordDecl(hasAnyName("::C", "::b::C", "::a::b::C"))));
+}
+
 TEST(Matcher, IsDefinition) {
   DeclarationMatcher DefinitionOfClassA =
       recordDecl(hasName("A"), isDefinition());




More information about the cfe-commits mailing list