From dfad990d887c6c644b2e544fc6cd5aa1cca84d26 Mon Sep 17 00:00:00 2001 From: Samuel Benzaquen <sbenza@google.com> Date: Mon, 22 Feb 2016 21:13:02 +0000 Subject: [PATCH] [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 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261574 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/LibASTMatchersReference.html | 10 ++ docs/tools/dump_ast_matchers.py | 10 ++ include/clang/ASTMatchers/ASTMatchers.h | 19 ++- .../clang/ASTMatchers/ASTMatchersInternal.h | 10 +- lib/ASTMatchers/ASTMatchersInternal.cpp | 158 ++++++++++++------ lib/ASTMatchers/Dynamic/Registry.cpp | 1 + unittests/ASTMatchers/ASTMatchersTest.cpp | 13 ++ 7 files changed, 164 insertions(+), 57 deletions(-) diff --git a/docs/LibASTMatchersReference.html b/docs/LibASTMatchersReference.html index 4dabc4b4549..fb055be7b8c 100644 --- a/docs/LibASTMatchersReference.html +++ b/docs/LibASTMatchersReference.html @@ -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. diff --git a/docs/tools/dump_ast_matchers.py b/docs/tools/dump_ast_matchers.py index c2fdcc28834..104189c4af3 100644 --- a/docs/tools/dump_ast_matchers.py +++ b/docs/tools/dump_ast_matchers.py @@ -264,6 +264,16 @@ def act_on_decl(declaration, comment, allowed_types): 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* diff --git a/include/clang/ASTMatchers/ASTMatchers.h b/include/clang/ASTMatchers/ASTMatchers.h index 9759d638f37..29cce667e1b 100644 --- a/include/clang/ASTMatchers/ASTMatchers.h +++ b/include/clang/ASTMatchers/ASTMatchers.h @@ -1844,11 +1844,24 @@ inline internal::Matcher<Stmt> sizeOfExpr( /// \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. /// diff --git a/include/clang/ASTMatchers/ASTMatchersInternal.h b/include/clang/ASTMatchers/ASTMatchersInternal.h index 8600dc56cb9..26bb8c89b28 100644 --- a/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -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 SingleNodeMatcherInterface<NamedDecl> { 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 diff --git a/lib/ASTMatchers/ASTMatchersInternal.cpp b/lib/ASTMatchers/ASTMatchersInternal.cpp index 9905f1e4bd5..ad785b5d61c 100644 --- a/lib/ASTMatchers/ASTMatchersInternal.cpp +++ b/lib/ASTMatchers/ASTMatchersInternal.cpp @@ -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_type_traits::DynTypedNode &DynNode, 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 &FullName, StringRef Suffix) { 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 NamedDecl &Node) const { 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(const NamedDecl &Node) const { 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 NamedDecl &Node) const { 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; + } } } diff --git a/lib/ASTMatchers/Dynamic/Registry.cpp b/lib/ASTMatchers/Dynamic/Registry.cpp index 24f7e981271..4cfd3292aa1 100644 --- a/lib/ASTMatchers/Dynamic/Registry.cpp +++ b/lib/ASTMatchers/Dynamic/Registry.cpp @@ -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); diff --git a/unittests/ASTMatchers/ASTMatchersTest.cpp b/unittests/ASTMatchers/ASTMatchersTest.cpp index eb871f91def..133dc70513c 100644 --- a/unittests/ASTMatchers/ASTMatchersTest.cpp +++ b/unittests/ASTMatchers/ASTMatchersTest.cpp @@ -2881,6 +2881,19 @@ TEST(Matcher, HasNameSupportsFunctionScope) { 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()); -- GitLab