[ASTMatchers] Add binaryOperation matcher

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
This commit is contained in:
Stephen Kelly 2021-01-05 01:33:13 +00:00
parent 4f15556731
commit e810e95e4b
7 changed files with 203 additions and 0 deletions

View File

@ -1206,6 +1206,7 @@ Example matches a ?: b
Example matches a || b
!(a || b)
See also the binaryOperation() matcher for more-general matching.
</pre></td></tr>
@ -1505,6 +1506,8 @@ Example matches both operator&lt;&lt;((o &lt;&lt; b), c) and operator&lt;&lt;(o,
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>
@ -5438,6 +5441,48 @@ match expressions.</p>
<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.

View File

@ -379,6 +379,14 @@ Flags can be combined with '|' example \"IgnoreCase | BasicRegex\"
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) {

View File

@ -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;
@ -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;
@ -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

View File

@ -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;

View File

@ -925,6 +925,50 @@ private:
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.
@ -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

View File

@ -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);

View File

@ -470,6 +470,10 @@ TEST_P(ASTMatchersTest, MapAnyOf) {
return;
}
if (GetParam().hasDelayedTemplateParsing()) {
return;
}
StringRef Code = R"cpp(
void F() {
if (true) {}
@ -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(
@ -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
{