Skip to content

Commit 922bef4

Browse files
committedFeb 22, 2016
[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 llvm-svn: 261574
1 parent 2ec4717 commit 922bef4

File tree

7 files changed

+164
-57
lines changed

7 files changed

+164
-57
lines changed
 

Diff for: ‎clang/docs/LibASTMatchersReference.html

+10
Original file line numberDiff line numberDiff line change
@@ -3102,6 +3102,16 @@ <h2 id="narrowing-matchers">Narrowing Matchers</h2>
31023102
</pre></td></tr>
31033103

31043104

3105+
<tr><td>Matcher&lt;internal::Matcher&lt;<a href="http://clang.llvm.org/doxygen/classclang_1_1NamedDecl.html">NamedDecl</a>&gt;&gt;</td><td class="name" onclick="toggle('hasAnyName0')"><a name="hasAnyName0Anchor">hasAnyName</a></td><td>StringRef, ..., StringRef</td></tr>
3106+
<tr><td colspan="4" class="doc" id="hasAnyName0"><pre>Matches NamedDecl nodes that have any of the specified names.
3107+
3108+
This matcher is only provided as a performance optimization of hasName.
3109+
hasAnyName(a, b, c)
3110+
is equivalent but faster than
3111+
anyOf(hasName(a), hasName(b), hasName(c))
3112+
</pre></td></tr>
3113+
3114+
31053115
<tr><td>Matcher&lt;internal::Matcher&lt;<a href="http://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>&gt;&gt;</td><td class="name" onclick="toggle('isInTemplateInstantiation0')"><a name="isInTemplateInstantiation0Anchor">isInTemplateInstantiation</a></td><td></td></tr>
31063116
<tr><td colspan="4" class="doc" id="isInTemplateInstantiation0"><pre>Matches statements inside of a template instantiation.
31073117

Diff for: ‎clang/docs/tools/dump_ast_matchers.py

+10
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,16 @@ def act_on_decl(declaration, comment, allowed_types):
264264
add_matcher('*', name, 'Matcher<*>', comment)
265265
return
266266

267+
# Parse Variadic functions.
268+
m = re.match(
269+
r"""^.*llvm::VariadicFunction\s*<\s*([^,]+),\s*([^,]+),\s*[^>]+>\s*
270+
([a-zA-Z]*)\s*=\s*{.*};$""",
271+
declaration, flags=re.X)
272+
if m:
273+
result, arg, name = m.groups()[:3]
274+
add_matcher(result, name, '%s, ..., %s' % (arg, arg), comment)
275+
return
276+
267277
# Parse Variadic operator matchers.
268278
m = re.match(
269279
r"""^.*VariadicOperatorMatcherFunc\s*<\s*([^,]+),\s*([^\s>]+)\s*>\s*

Diff for: ‎clang/include/clang/ASTMatchers/ASTMatchers.h

+16-3
Original file line numberDiff line numberDiff line change
@@ -1844,11 +1844,24 @@ inline internal::Matcher<Stmt> sizeOfExpr(
18441844
/// \code
18451845
/// namespace a { namespace b { class X; } }
18461846
/// \endcode
1847-
inline internal::Matcher<NamedDecl> hasName(std::string Name) {
1848-
return internal::Matcher<NamedDecl>(
1849-
new internal::HasNameMatcher(std::move(Name)));
1847+
inline internal::Matcher<NamedDecl> hasName(const std::string &Name) {
1848+
return internal::Matcher<NamedDecl>(new internal::HasNameMatcher({Name}));
18501849
}
18511850

1851+
/// \brief Matches NamedDecl nodes that have any of the specified names.
1852+
///
1853+
/// This matcher is only provided as a performance optimization of hasName.
1854+
/// \code
1855+
/// hasAnyName(a, b, c)
1856+
/// \endcode
1857+
/// is equivalent to, but faster than
1858+
/// \code
1859+
/// anyOf(hasName(a), hasName(b), hasName(c))
1860+
/// \endcode
1861+
const llvm::VariadicFunction<internal::Matcher<NamedDecl>, StringRef,
1862+
internal::hasAnyNameFunc>
1863+
hasAnyName = {};
1864+
18521865
/// \brief Matches NamedDecl nodes whose fully qualified names contain
18531866
/// a substring matched by the given RegExp.
18541867
///

Diff for: ‎clang/include/clang/ASTMatchers/ASTMatchersInternal.h

+7-3
Original file line numberDiff line numberDiff line change
@@ -637,10 +637,10 @@ class HasOverloadedOperatorNameMatcher : public SingleNodeMatcherInterface<T> {
637637

638638
/// \brief Matches named declarations with a specific name.
639639
///
640-
/// See \c hasName() in ASTMatchers.h for details.
640+
/// See \c hasName() and \c hasAnyName() in ASTMatchers.h for details.
641641
class HasNameMatcher : public SingleNodeMatcherInterface<NamedDecl> {
642642
public:
643-
explicit HasNameMatcher(std::string Name);
643+
explicit HasNameMatcher(std::vector<std::string> Names);
644644

645645
bool matchesNode(const NamedDecl &Node) const override;
646646

@@ -667,9 +667,13 @@ class HasNameMatcher : public SingleNodeMatcherInterface<NamedDecl> {
667667
bool matchesNodeFullSlow(const NamedDecl &Node) const;
668668

669669
const bool UseUnqualifiedMatch;
670-
const std::string Name;
670+
const std::vector<std::string> Names;
671671
};
672672

673+
/// \brief Trampoline function to use VariadicFunction<> to construct a
674+
/// HasNameMatcher.
675+
Matcher<NamedDecl> hasAnyNameFunc(ArrayRef<const StringRef *> NameRefs);
676+
673677
/// \brief Matches declarations for QualType and CallExpr.
674678
///
675679
/// Type argument DeclMatcherT is required by PolymorphicMatcherWithParam1 but

Diff for: ‎clang/lib/ASTMatchers/ASTMatchersInternal.cpp

+107-51
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "clang/ASTMatchers/ASTMatchers.h"
1515
#include "clang/ASTMatchers/ASTMatchersInternal.h"
1616
#include "llvm/ADT/SmallString.h"
17+
#include "llvm/ADT/SmallVector.h"
1718
#include "llvm/Support/ManagedStatic.h"
1819

1920
namespace clang {
@@ -293,15 +294,26 @@ bool AnyOfVariadicOperator(const ast_type_traits::DynTypedNode &DynNode,
293294
return false;
294295
}
295296

296-
HasNameMatcher::HasNameMatcher(std::string NameRef)
297-
: UseUnqualifiedMatch(NameRef.find("::") == NameRef.npos),
298-
Name(std::move(NameRef)) {
299-
assert(!Name.empty());
297+
Matcher<NamedDecl> hasAnyNameFunc(ArrayRef<const StringRef *> NameRefs) {
298+
std::vector<std::string> Names;
299+
for (auto *Name : NameRefs)
300+
Names.emplace_back(*Name);
301+
return internal::Matcher<NamedDecl>(
302+
new internal::HasNameMatcher(std::move(Names)));
303+
}
304+
305+
HasNameMatcher::HasNameMatcher(std::vector<std::string> N)
306+
: UseUnqualifiedMatch(std::all_of(
307+
N.begin(), N.end(),
308+
[](StringRef Name) { return Name.find("::") == Name.npos; })),
309+
Names(std::move(N)) {
310+
for (StringRef Name : Names)
311+
assert(!Name.empty());
300312
}
301313

302314
namespace {
303315

304-
bool ConsumeNameSuffix(StringRef &FullName, StringRef Suffix) {
316+
bool consumeNameSuffix(StringRef &FullName, StringRef Suffix) {
305317
StringRef Name = FullName;
306318
if (!Name.endswith(Suffix))
307319
return false;
@@ -315,79 +327,127 @@ bool ConsumeNameSuffix(StringRef &FullName, StringRef Suffix) {
315327
return true;
316328
}
317329

318-
bool ConsumeNodeName(StringRef &Name, const NamedDecl &Node) {
330+
StringRef getNodeName(const NamedDecl &Node, llvm::SmallString<128> &Scratch) {
319331
// Simple name.
320332
if (Node.getIdentifier())
321-
return ConsumeNameSuffix(Name, Node.getName());
333+
return Node.getName();
322334

323335
if (Node.getDeclName()) {
324336
// Name needs to be constructed.
325-
llvm::SmallString<128> NodeName;
326-
llvm::raw_svector_ostream OS(NodeName);
337+
Scratch.clear();
338+
llvm::raw_svector_ostream OS(Scratch);
327339
Node.printName(OS);
328-
return ConsumeNameSuffix(Name, OS.str());
340+
return OS.str();
329341
}
330342

331-
return ConsumeNameSuffix(Name, "(anonymous)");
343+
return "(anonymous)";
332344
}
333345

346+
StringRef getNodeName(const RecordDecl &Node, llvm::SmallString<128> &Scratch) {
347+
if (Node.getIdentifier()) {
348+
return Node.getName();
349+
}
350+
Scratch.clear();
351+
return ("(anonymous " + Node.getKindName() + ")").toStringRef(Scratch);
352+
}
353+
354+
StringRef getNodeName(const NamespaceDecl &Node,
355+
llvm::SmallString<128> &Scratch) {
356+
return Node.isAnonymousNamespace() ? "(anonymous namespace)" : Node.getName();
357+
}
358+
359+
360+
class PatternSet {
361+
public:
362+
PatternSet(ArrayRef<std::string> Names) {
363+
for (StringRef Name : Names)
364+
Patterns.push_back({Name, Name.startswith("::")});
365+
}
366+
367+
/// Consumes the name suffix from each pattern in the set and removes the ones
368+
/// that didn't match.
369+
/// Return true if there are still any patterns left.
370+
bool consumeNameSuffix(StringRef NodeName, bool CanSkip) {
371+
for (size_t I = 0; I < Patterns.size();) {
372+
if (internal::consumeNameSuffix(Patterns[I].Pattern, NodeName) ||
373+
CanSkip) {
374+
++I;
375+
} else {
376+
Patterns.erase(Patterns.begin() + I);
377+
}
378+
}
379+
return !Patterns.empty();
380+
}
381+
382+
/// Check if any of the patterns are a match.
383+
/// A match will be a pattern that was fully consumed, that also matches the
384+
/// 'fully qualified' requirement.
385+
bool foundMatch(bool AllowFullyQualified) const {
386+
for (auto& P: Patterns)
387+
if (P.Pattern.empty() && (AllowFullyQualified || !P.IsFullyQualified))
388+
return true;
389+
return false;
390+
}
391+
392+
private:
393+
struct Pattern {
394+
StringRef Pattern;
395+
bool IsFullyQualified;
396+
};
397+
llvm::SmallVector<Pattern, 8> Patterns;
398+
};
399+
334400
} // namespace
335401

336402
bool HasNameMatcher::matchesNodeUnqualified(const NamedDecl &Node) const {
337403
assert(UseUnqualifiedMatch);
338-
StringRef NodeName = Name;
339-
return ConsumeNodeName(NodeName, Node) && NodeName.empty();
404+
llvm::SmallString<128> Scratch;
405+
StringRef NodeName = getNodeName(Node, Scratch);
406+
return std::any_of(Names.begin(), Names.end(), [&](StringRef Name) {
407+
return consumeNameSuffix(Name, NodeName) && Name.empty();
408+
});
340409
}
341410

342411
bool HasNameMatcher::matchesNodeFullFast(const NamedDecl &Node) const {
412+
PatternSet Patterns(Names);
413+
llvm::SmallString<128> Scratch;
414+
343415
// This function is copied and adapted from NamedDecl::printQualifiedName()
344416
// By matching each part individually we optimize in a couple of ways:
345417
// - We can exit early on the first failure.
346418
// - We can skip inline/anonymous namespaces without another pass.
347419
// - We print one name at a time, reducing the chance of overflowing the
348420
// inlined space of the SmallString.
349-
StringRef Pattern = Name;
350-
const bool IsFullyQualified = Pattern.startswith("::");
351421

352422
// First, match the name.
353-
if (!ConsumeNodeName(Pattern, Node))
423+
if (!Patterns.consumeNameSuffix(getNodeName(Node, Scratch),
424+
/*CanSkip=*/false))
354425
return false;
355426

356427
// Try to match each declaration context.
357428
// We are allowed to skip anonymous and inline namespaces if they don't match.
358429
const DeclContext *Ctx = Node.getDeclContext();
359430

360431
if (Ctx->isFunctionOrMethod())
361-
return Pattern.empty() && !IsFullyQualified;
432+
return Patterns.foundMatch(/*AllowFullyQualified=*/false);
362433

363-
for (; !Pattern.empty() && Ctx && isa<NamedDecl>(Ctx);
364-
Ctx = Ctx->getParent()) {
365-
if (const auto *ND = dyn_cast<NamespaceDecl>(Ctx)) {
366-
StringRef NSName =
367-
ND->isAnonymousNamespace() ? "(anonymous namespace)" : ND->getName();
434+
for (; Ctx && isa<NamedDecl>(Ctx); Ctx = Ctx->getParent()) {
435+
if (Patterns.foundMatch(/*AllowFullyQualified=*/false))
436+
return true;
368437

369-
// If it matches, continue.
370-
if (ConsumeNameSuffix(Pattern, NSName))
371-
continue;
372-
// If it didn't match but we can skip it, continue.
373-
if (ND->isAnonymousNamespace() || ND->isInline())
438+
if (const auto *ND = dyn_cast<NamespaceDecl>(Ctx)) {
439+
// If it matches (or we can skip it), continue.
440+
if (Patterns.consumeNameSuffix(getNodeName(*ND, Scratch),
441+
/*CanSkip=*/ND->isAnonymousNamespace() ||
442+
ND->isInline()))
374443
continue;
375-
376444
return false;
377445
}
378446
if (const auto *RD = dyn_cast<RecordDecl>(Ctx)) {
379447
if (!isa<ClassTemplateSpecializationDecl>(Ctx)) {
380-
if (RD->getIdentifier()) {
381-
if (ConsumeNameSuffix(Pattern, RD->getName()))
382-
continue;
383-
} else {
384-
llvm::SmallString<128> NodeName;
385-
NodeName += StringRef("(anonymous ");
386-
NodeName += RD->getKindName();
387-
NodeName += ')';
388-
if (ConsumeNameSuffix(Pattern, NodeName))
389-
continue;
390-
}
448+
if (Patterns.consumeNameSuffix(getNodeName(*RD, Scratch),
449+
/*CanSkip=*/false))
450+
continue;
391451

392452
return false;
393453
}
@@ -398,16 +458,10 @@ bool HasNameMatcher::matchesNodeFullFast(const NamedDecl &Node) const {
398458
return matchesNodeFullSlow(Node);
399459
}
400460

401-
// If we are fully qualified, we must not have any leftover context.
402-
if (IsFullyQualified && Ctx && isa<NamedDecl>(Ctx))
403-
return false;
404-
405-
return Pattern.empty();
461+
return Patterns.foundMatch(/*AllowFullyQualified=*/true);
406462
}
407463

408464
bool HasNameMatcher::matchesNodeFullSlow(const NamedDecl &Node) const {
409-
const StringRef Pattern = Name;
410-
411465
const bool SkipUnwrittenCases[] = {false, true};
412466
for (bool SkipUnwritten : SkipUnwrittenCases) {
413467
llvm::SmallString<128> NodeName = StringRef("::");
@@ -423,12 +477,14 @@ bool HasNameMatcher::matchesNodeFullSlow(const NamedDecl &Node) const {
423477

424478
const StringRef FullName = OS.str();
425479

426-
if (Pattern.startswith("::")) {
427-
if (FullName == Pattern)
480+
for (const StringRef Pattern : Names) {
481+
if (Pattern.startswith("::")) {
482+
if (FullName == Pattern)
483+
return true;
484+
} else if (FullName.endswith(Pattern) &&
485+
FullName.drop_back(Pattern.size()).endswith("::")) {
428486
return true;
429-
} else if (FullName.endswith(Pattern) &&
430-
FullName.drop_back(Pattern.size()).endswith("::")) {
431-
return true;
487+
}
432488
}
433489
}
434490

Diff for: ‎clang/lib/ASTMatchers/Dynamic/Registry.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ RegistryMaps::RegistryMaps() {
191191
REGISTER_MATCHER(hasAncestor);
192192
REGISTER_MATCHER(hasAnyArgument);
193193
REGISTER_MATCHER(hasAnyConstructorInitializer);
194+
REGISTER_MATCHER(hasAnyName);
194195
REGISTER_MATCHER(hasAnyParameter);
195196
REGISTER_MATCHER(hasAnySubstatement);
196197
REGISTER_MATCHER(hasAnyTemplateArgument);

Diff for: ‎clang/unittests/ASTMatchers/ASTMatchersTest.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -2881,6 +2881,19 @@ TEST(Matcher, HasNameSupportsFunctionScope) {
28812881
EXPECT_TRUE(matches(code, fieldDecl(hasName("::a::F(int)::S::m"))));
28822882
}
28832883

2884+
TEST(Matcher, HasAnyName) {
2885+
const std::string Code = "namespace a { namespace b { class C; } }";
2886+
2887+
EXPECT_TRUE(matches(Code, recordDecl(hasAnyName("XX", "a::b::C"))));
2888+
EXPECT_TRUE(matches(Code, recordDecl(hasAnyName("a::b::C", "XX"))));
2889+
EXPECT_TRUE(matches(Code, recordDecl(hasAnyName("XX::C", "a::b::C"))));
2890+
EXPECT_TRUE(matches(Code, recordDecl(hasAnyName("XX", "C"))));
2891+
2892+
EXPECT_TRUE(notMatches(Code, recordDecl(hasAnyName("::C", "::b::C"))));
2893+
EXPECT_TRUE(
2894+
matches(Code, recordDecl(hasAnyName("::C", "::b::C", "::a::b::C"))));
2895+
}
2896+
28842897
TEST(Matcher, IsDefinition) {
28852898
DeclarationMatcher DefinitionOfClassA =
28862899
recordDecl(hasName("A"), isDefinition());

0 commit comments

Comments
 (0)