Skip to content

Commit

Permalink
[ASTMatchers] Introduce a matcher for matching any given Objective-C …
Browse files Browse the repository at this point in the history
…selector

Incudes a tiny related refactoring.

Differential Revision: https://reviews.llvm.org/D44858

llvm-svn: 328747
  • Loading branch information
George Karpenkov committed Mar 29, 2018
1 parent 9d1d0c4 commit 88a16a0
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 9 deletions.
11 changes: 11 additions & 0 deletions clang/docs/LibASTMatchersReference.html
Expand Up @@ -3958,6 +3958,17 @@ <h2 id="narrowing-matchers">Narrowing Matchers</h2>
</pre></td></tr>


<tr><td>Matcher&lt;internal::Matcher&lt;<a href="http://clang.llvm.org/doxygen/classclang_1_1ObjCMessageExpr.html">ObjCMessageExpr</a>&gt;&gt;</td><td class="name" onclick="toggle('hasAnySelector0')"><a name="hasAnySelector0Anchor">hasAnySelector</a></td><td>StringRef, ..., StringRef</td></tr>
<tr><td colspan="4" class="doc" id="hasAnySelector0"><pre>Matches when at least one of the supplied string equals to the
Selector.getAsString()

matcher = objCMessageExpr(hasSelector("methodA:", "methodB:"));
matches both of the expressions below:
[myObj methodA:argA];
[myObj methodB:argB];
</pre></td></tr>


<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>
<tr><td colspan="4" class="doc" id="isInTemplateInstantiation0"><pre>Matches statements inside of a template instantiation.

Expand Down
22 changes: 17 additions & 5 deletions clang/include/clang/ASTMatchers/ASTMatchers.h
Expand Up @@ -2313,9 +2313,7 @@ inline internal::Matcher<Stmt> sizeOfExpr(
/// namespace a { namespace b { class X; } }
/// \endcode
inline internal::Matcher<NamedDecl> hasName(const std::string &Name) {
std::vector<std::string> Names;
Names.push_back(Name);
return internal::Matcher<NamedDecl>(new internal::HasNameMatcher(Names));
return internal::Matcher<NamedDecl>(new internal::HasNameMatcher({Name}));
}

/// \brief Matches NamedDecl nodes that have any of the specified names.
Expand Down Expand Up @@ -2711,7 +2709,7 @@ AST_MATCHER_P(ObjCMessageExpr, hasReceiverType, internal::Matcher<QualType>,
const QualType TypeDecl = Node.getReceiverType();
return InnerMatcher.matches(TypeDecl, Finder, Builder);
}

/// \brief Matches when BaseName == Selector.getAsString()
///
/// matcher = objCMessageExpr(hasSelector("loadHTMLString:baseURL:"));
Expand All @@ -2725,7 +2723,21 @@ AST_MATCHER_P(ObjCMessageExpr, hasSelector, std::string, BaseName) {
return BaseName.compare(Sel.getAsString()) == 0;
}



/// \brief Matches when at least one of the supplied string equals to the
/// Selector.getAsString()
///
/// matcher = objCMessageExpr(hasSelector("methodA:", "methodB:"));
/// matches both of the expressions below:
/// \code
/// [myObj methodA:argA];
/// [myObj methodB:argB];
/// \endcode
extern const internal::VariadicFunction<internal::Matcher<ObjCMessageExpr>,
StringRef,
internal::hasAnySelectorFunc>
hasAnySelector;

/// \brief Matches ObjC selectors whose name contains
/// a substring matched by the given RegExp.
/// matcher = objCMessageExpr(matchesSelector("loadHTMLString\:baseURL?"));
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/ASTMatchers/ASTMatchersInternal.h
Expand Up @@ -731,6 +731,11 @@ class HasNameMatcher : public SingleNodeMatcherInterface<NamedDecl> {
/// HasNameMatcher.
Matcher<NamedDecl> hasAnyNameFunc(ArrayRef<const StringRef *> NameRefs);

/// \brief Trampoline function to use VariadicFunction<> to construct a
/// hasAnySelector matcher.
Matcher<ObjCMessageExpr> hasAnySelectorFunc(
ArrayRef<const StringRef *> NameRefs);

/// \brief Matches declarations for QualType and CallExpr.
///
/// Type argument DeclMatcherT is required by PolymorphicMatcherWithParam1 but
Expand Down
28 changes: 24 additions & 4 deletions clang/lib/ASTMatchers/ASTMatchersInternal.cpp
Expand Up @@ -315,12 +315,31 @@ bool AnyOfVariadicOperator(const ast_type_traits::DynTypedNode &DynNode,
return false;
}

Matcher<NamedDecl> hasAnyNameFunc(ArrayRef<const StringRef *> NameRefs) {
inline static
std::vector<std::string> vectorFromRefs(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)));
return Names;
}

Matcher<NamedDecl> hasAnyNameFunc(ArrayRef<const StringRef *> NameRefs) {
std::vector<std::string> Names = vectorFromRefs(NameRefs);
return internal::Matcher<NamedDecl>(new internal::HasNameMatcher(Names));
}

AST_MATCHER_P(ObjCMessageExpr, hasAnySelectorMatcher, std::vector<std::string>,
Matches) {
std::string SelString = Node.getSelector().getAsString();
for (const std::string &S : Matches)
if (S == SelString)
return true;
return false;
}

Matcher<ObjCMessageExpr> hasAnySelectorFunc(
ArrayRef<const StringRef *> NameRefs) {
return hasAnySelectorMatcher(vectorFromRefs(NameRefs));
}

HasNameMatcher::HasNameMatcher(std::vector<std::string> N)
Expand Down Expand Up @@ -393,7 +412,8 @@ class PatternSet {
/// 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].P, NodeName) ||
if (::clang::ast_matchers::internal::consumeNameSuffix(Patterns[I].P,
NodeName) ||
CanSkip) {
++I;
} else {
Expand Down
1 change: 1 addition & 0 deletions clang/lib/ASTMatchers/Dynamic/Registry.cpp
Expand Up @@ -286,6 +286,7 @@ RegistryMaps::RegistryMaps() {
REGISTER_MATCHER(hasReturnValue);
REGISTER_MATCHER(hasRHS);
REGISTER_MATCHER(hasSelector);
REGISTER_MATCHER(hasAnySelector);
REGISTER_MATCHER(hasSingleDecl);
REGISTER_MATCHER(hasSize);
REGISTER_MATCHER(hasSizeExpr);
Expand Down
11 changes: 11 additions & 0 deletions clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
Expand Up @@ -1565,9 +1565,20 @@ TEST(ObjCMessageExprMatcher, SimpleExprs) {
EXPECT_TRUE(matchesObjC(
Objc1String,
objcMessageExpr(anything())));
EXPECT_TRUE(matchesObjC(Objc1String,
objcMessageExpr(hasAnySelector({
"contents", "meth:"}))

));
EXPECT_TRUE(matchesObjC(
Objc1String,
objcMessageExpr(hasSelector("contents"))));
EXPECT_TRUE(matchesObjC(
Objc1String,
objcMessageExpr(hasAnySelector("contents", "contentsA"))));
EXPECT_FALSE(matchesObjC(
Objc1String,
objcMessageExpr(hasAnySelector("contentsB", "contentsC"))));
EXPECT_TRUE(matchesObjC(
Objc1String,
objcMessageExpr(matchesSelector("cont*"))));
Expand Down
11 changes: 11 additions & 0 deletions clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp
Expand Up @@ -236,6 +236,17 @@ TEST(ParserTest, FullParserTest) {
Error.toStringFull());
}

TEST(ParserTest, VariadicMatchTest) {
Diagnostics Error;
llvm::Optional<DynTypedMatcher> OM(Parser::parseMatcherExpression(
"stmt(objcMessageExpr(hasAnySelector(\"methodA\", \"methodB:\")))",
&Error));
EXPECT_EQ("", Error.toStringFull());
auto M = OM->unconditionalConvertTo<Stmt>();
EXPECT_TRUE(matchesObjC("@interface I @end "
"void foo(I* i) { [i methodA]; }", M));
}

std::string ParseWithError(StringRef Code) {
Diagnostics Error;
VariantValue Value;
Expand Down

0 comments on commit 88a16a0

Please sign in to comment.