forked from OSchip/llvm-project
[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
This commit is contained in:
parent
2ec4717c2c
commit
922bef4f38
|
@ -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.
|
||||
|
||||
|
|
|
@ -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*
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 (; Ctx && isa<NamedDecl>(Ctx); Ctx = Ctx->getParent()) {
|
||||
if (Patterns.foundMatch(/*AllowFullyQualified=*/false))
|
||||
return true;
|
||||
|
||||
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();
|
||||
|
||||
// If it matches, continue.
|
||||
if (ConsumeNameSuffix(Pattern, NSName))
|
||||
// If it matches (or we can skip it), continue.
|
||||
if (Patterns.consumeNameSuffix(getNodeName(*ND, Scratch),
|
||||
/*CanSkip=*/ND->isAnonymousNamespace() ||
|
||||
ND->isInline()))
|
||||
continue;
|
||||
// If it didn't match but we can skip it, continue.
|
||||
if (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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Reference in New Issue