r261574 - [ASTMatchers] Add matcher hasAnyName.

Aaron Ballman via cfe-commits cfe-commits at lists.llvm.org
Mon Feb 22 13:19:07 PST 2016


On Mon, Feb 22, 2016 at 4:13 PM, Samuel Benzaquen via cfe-commits
<cfe-commits at lists.llvm.org> wrote:
> 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(),...)

Does this mean we can get a clang-tidy check to convert
anyOf(hasName(), ...) into hasAnyName()? ;-)

~Aaron

>
> 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());
>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


More information about the cfe-commits mailing list