On Mon, Feb 22, 2016 at 4:13 PM, Samuel Benzaquen via cfe-commits <cfe-commits@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@lists.llvm.org > http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits