Skip to content

Commit

Permalink
[ASTMatchers] Add binaryOperation matcher
Browse files Browse the repository at this point in the history
This is a simple utility which allows matching on binaryOperator and
cxxOperatorCallExpr. It can also be extended to support
cxxRewrittenBinaryOperator.

Add generic support for MapAnyOfMatchers to auto-marshalling functions.

Differential Revision: https://reviews.llvm.org/D94129
  • Loading branch information
steveire committed Jan 16, 2021
1 parent 4f15556 commit e810e95
Show file tree
Hide file tree
Showing 7 changed files with 203 additions and 0 deletions.
45 changes: 45 additions & 0 deletions clang/docs/LibASTMatchersReference.html
Expand Up @@ -1206,6 +1206,7 @@ <h2 id="decl-matchers">Node Matchers</h2>

Example matches a || b
!(a || b)
See also the binaryOperation() matcher for more-general matching.
</pre></td></tr>


Expand Down Expand Up @@ -1505,6 +1506,8 @@ <h2 id="decl-matchers">Node Matchers</h2>
ostream &amp;operator&lt;&lt; (ostream &amp;out, int i) { };
ostream &amp;o; int b = 1, c = 1;
o &lt;&lt; b &lt;&lt; c;
See also the binaryOperation() matcher for more-general matching of binary
uses of this AST node.
</pre></td></tr>


Expand Down Expand Up @@ -5438,6 +5441,48 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
<tr style="text-align:left"><th>Return type</th><th>Name</th><th>Parameters</th></tr>
<!-- START_TRAVERSAL_MATCHERS -->

<tr><td>Matcher&lt;*&gt;</td><td class="name" onclick="toggle('binaryOperation0')"><a name="binaryOperation0Anchor">binaryOperation</a></td><td>Matcher&lt;*&gt;...Matcher&lt;*&gt;</td></tr>
<tr><td colspan="4" class="doc" id="binaryOperation0"><pre>Matches nodes which can be used with binary operators.

The code
var1 != var2;
might be represented in the clang AST as a binaryOperator or a
cxxOperatorCallExpr, depending on

* whether the types of var1 and var2 are fundamental (binaryOperator) or at
least one is a class type (cxxOperatorCallExpr)
* whether the code appears in a template declaration, if at least one of the
vars is a dependent-type (binaryOperator)

This matcher elides details in places where the matchers for the nodes are
compatible.

Given
binaryOperation(
hasOperatorName("!="),
hasLHS(expr().bind("lhs")),
hasRHS(expr().bind("rhs"))
)
matches each use of "!=" in:
struct S{
bool operator!=(const S&amp;) const;
};

void foo()
{
1 != 2;
S() != S();
}

template&lt;typename T&gt;
void templ()
{
1 != 2;
T() != S();
}
</pre></td></tr>


<tr><td>Matcher&lt;*&gt;</td><td class="name" onclick="toggle('eachOf0')"><a name="eachOf0Anchor">eachOf</a></td><td>Matcher&lt;*&gt;, ..., Matcher&lt;*&gt;</td></tr>
<tr><td colspan="4" class="doc" id="eachOf0"><pre>Matches if any of the given matchers matches.

Expand Down
8 changes: 8 additions & 0 deletions clang/docs/tools/dump_ast_matchers.py
Expand Up @@ -379,6 +379,14 @@ def act_on_decl(declaration, comment, allowed_types):
add_matcher('*', name, 'Matcher<*>, ..., Matcher<*>', comment)
return

m = re.match(
r"""^.*MapAnyOfMatcher<.*>\s*
([a-zA-Z]*);$""",
declaration, flags=re.X)
if m:
name = m.groups()[0]
add_matcher('*', name, 'Matcher<*>...Matcher<*>', comment)
return

# Parse free standing matcher functions, like:
# Matcher<ResultType> Name(Matcher<ArgumentType> InnerMatcher) {
Expand Down
50 changes: 50 additions & 0 deletions clang/include/clang/ASTMatchers/ASTMatchers.h
Expand Up @@ -1971,6 +1971,8 @@ extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXDefaultArgExpr>
/// ostream &o; int b = 1, c = 1;
/// o << b << c;
/// \endcode
/// See also the binaryOperation() matcher for more-general matching of binary
/// uses of this AST node.
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXOperatorCallExpr>
cxxOperatorCallExpr;

Expand Down Expand Up @@ -2393,6 +2395,7 @@ extern const internal::VariadicDynCastAllOfMatcher<Stmt, StmtExpr> stmtExpr;
/// \code
/// !(a || b)
/// \endcode
/// See also the binaryOperation() matcher for more-general matching.
extern const internal::VariadicDynCastAllOfMatcher<Stmt, BinaryOperator>
binaryOperator;

Expand Down Expand Up @@ -2729,6 +2732,53 @@ auto mapAnyOf(internal::VariadicDynCastAllOfMatcher<T, U> const &...) {
return internal::MapAnyOfHelper<U...>();
}

/// Matches nodes which can be used with binary operators.
///
/// The code
/// \code
/// var1 != var2;
/// \endcode
/// might be represented in the clang AST as a binaryOperator or a
/// cxxOperatorCallExpr, depending on
///
/// * whether the types of var1 and var2 are fundamental (binaryOperator) or at
/// least one is a class type (cxxOperatorCallExpr)
/// * whether the code appears in a template declaration, if at least one of the
/// vars is a dependent-type (binaryOperator)
///
/// This matcher elides details in places where the matchers for the nodes are
/// compatible.
///
/// Given
/// \code
/// binaryOperation(
/// hasOperatorName("!="),
/// hasLHS(expr().bind("lhs")),
/// hasRHS(expr().bind("rhs"))
/// )
/// \endcode
/// matches each use of "!=" in:
/// \code
/// struct S{
/// bool operator!=(const S&) const;
/// };
///
/// void foo()
/// {
/// 1 != 2;
/// S() != S();
/// }
///
/// template<typename T>
/// void templ()
/// {
/// 1 != 2;
/// T() != S();
/// }
/// \endcode
extern const internal::MapAnyOfMatcher<BinaryOperator, CXXOperatorCallExpr>
binaryOperation;

/// Matches unary expressions that have a specific type of argument.
///
/// Given
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/ASTMatchers/ASTMatchersInternal.cpp
Expand Up @@ -919,6 +919,8 @@ const internal::VariadicDynCastAllOfMatcher<Stmt, AtomicExpr> atomicExpr;
const internal::VariadicDynCastAllOfMatcher<Stmt, StmtExpr> stmtExpr;
const internal::VariadicDynCastAllOfMatcher<Stmt, BinaryOperator>
binaryOperator;
const internal::MapAnyOfMatcher<BinaryOperator, CXXOperatorCallExpr>
binaryOperation;
const internal::VariadicDynCastAllOfMatcher<Stmt, UnaryOperator> unaryOperator;
const internal::VariadicDynCastAllOfMatcher<Stmt, ConditionalOperator>
conditionalOperator;
Expand Down
52 changes: 52 additions & 0 deletions clang/lib/ASTMatchers/Dynamic/Marshallers.h
Expand Up @@ -925,6 +925,50 @@ class VariadicOperatorMatcherDescriptor : public MatcherDescriptor {
const StringRef MatcherName;
};

template <typename CladeType, typename... MatcherT>
class MapAnyOfMatcherDescriptor : public MatcherDescriptor {
std::vector<DynCastAllOfMatcherDescriptor> Funcs;

public:
MapAnyOfMatcherDescriptor(StringRef MatcherName)
: Funcs{DynCastAllOfMatcherDescriptor(
ast_matchers::internal::VariadicDynCastAllOfMatcher<CladeType,
MatcherT>{},
MatcherName)...} {}

VariantMatcher create(SourceRange NameRange, ArrayRef<ParserValue> Args,
Diagnostics *Error) const override {
std::vector<VariantMatcher> InnerArgs;

for (auto const &F : Funcs) {
InnerArgs.push_back(F.create(NameRange, Args, Error));
if (!Error->errors().empty())
return {};
}
return VariantMatcher::SingleMatcher(
ast_matchers::internal::BindableMatcher<CladeType>(
VariantMatcher::VariadicOperatorMatcher(
ast_matchers::internal::DynTypedMatcher::VO_AnyOf,
std::move(InnerArgs))
.getTypedMatcher<CladeType>()));
}

bool isVariadic() const override { return true; }
unsigned getNumArgs() const override { return 0; }

void getArgKinds(ASTNodeKind ThisKind, unsigned,
std::vector<ArgKind> &Kinds) const override {
Kinds.push_back(ThisKind);
}

bool isConvertibleTo(ASTNodeKind Kind, unsigned *Specificity,
ASTNodeKind *LeastDerivedKind) const override {
return llvm::all_of(Funcs, [=](const auto &F) {
return F.isConvertibleTo(Kind, Specificity, LeastDerivedKind);
});
}
};

/// Helper functions to select the appropriate marshaller functions.
/// They detect the number of arguments, arguments types and return type.

Expand Down Expand Up @@ -1029,6 +1073,14 @@ std::unique_ptr<MatcherDescriptor> makeMatcherAutoMarshall(
MinCount, MaxCount, Func.Op, MatcherName);
}

template <typename CladeType, typename... MatcherT>
std::unique_ptr<MatcherDescriptor> makeMatcherAutoMarshall(
ast_matchers::internal::MapAnyOfMatcherImpl<CladeType, MatcherT...>,
StringRef MatcherName) {
return std::make_unique<MapAnyOfMatcherDescriptor<CladeType, MatcherT...>>(
MatcherName);
}

} // namespace internal
} // namespace dynamic
} // namespace ast_matchers
Expand Down
1 change: 1 addition & 0 deletions clang/lib/ASTMatchers/Dynamic/Registry.cpp
Expand Up @@ -143,6 +143,7 @@ RegistryMaps::RegistryMaps() {
REGISTER_MATCHER(autoreleasePoolStmt)
REGISTER_MATCHER(binaryConditionalOperator);
REGISTER_MATCHER(binaryOperator);
REGISTER_MATCHER(binaryOperation);
REGISTER_MATCHER(blockDecl);
REGISTER_MATCHER(blockExpr);
REGISTER_MATCHER(blockPointerType);
Expand Down
45 changes: 45 additions & 0 deletions clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
Expand Up @@ -470,6 +470,10 @@ TEST_P(ASTMatchersTest, MapAnyOf) {
return;
}

if (GetParam().hasDelayedTemplateParsing()) {
return;
}

StringRef Code = R"cpp(
void F() {
if (true) {}
Expand Down Expand Up @@ -556,6 +560,15 @@ void opFree()
if (s1 != s2)
return;
}
template<typename T>
void templ()
{
T s1;
T s2;
if (s1 != s2)
return;
}
)cpp";

EXPECT_TRUE(matches(
Expand Down Expand Up @@ -669,6 +682,38 @@ void opFree()
.with(hasAnyOperatorName("==", "!="),
forFunction(functionDecl(hasName("opFree")))))));

EXPECT_TRUE(matches(
Code, traverse(TK_IgnoreUnlessSpelledInSource,
binaryOperation(
hasOperatorName("!="),
forFunction(functionDecl(hasName("binop"))),
hasLHS(declRefExpr(to(varDecl(hasName("s1"))))),
hasRHS(declRefExpr(to(varDecl(hasName("s2")))))))));

EXPECT_TRUE(matches(
Code, traverse(TK_IgnoreUnlessSpelledInSource,
binaryOperation(
hasOperatorName("!="),
forFunction(functionDecl(hasName("opMem"))),
hasLHS(declRefExpr(to(varDecl(hasName("s1"))))),
hasRHS(declRefExpr(to(varDecl(hasName("s2")))))))));

EXPECT_TRUE(matches(
Code, traverse(TK_IgnoreUnlessSpelledInSource,
binaryOperation(
hasOperatorName("!="),
forFunction(functionDecl(hasName("opFree"))),
hasLHS(declRefExpr(to(varDecl(hasName("s1"))))),
hasRHS(declRefExpr(to(varDecl(hasName("s2")))))))));

EXPECT_TRUE(matches(
Code, traverse(TK_IgnoreUnlessSpelledInSource,
binaryOperation(
hasOperatorName("!="),
forFunction(functionDecl(hasName("templ"))),
hasLHS(declRefExpr(to(varDecl(hasName("s1"))))),
hasRHS(declRefExpr(to(varDecl(hasName("s2")))))))));

Code = R"cpp(
struct HasOpBangMem
{
Expand Down

0 comments on commit e810e95

Please sign in to comment.