forked from OSchip/llvm-project
[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:
parent
4f15556731
commit
e810e95e4b
|
@ -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<<((o << b), c) and operator<<(o,
|
|||
ostream &operator<< (ostream &out, int i) { };
|
||||
ostream &o; int b = 1, c = 1;
|
||||
o << b << 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<*></td><td class="name" onclick="toggle('binaryOperation0')"><a name="binaryOperation0Anchor">binaryOperation</a></td><td>Matcher<*>...Matcher<*></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&) const;
|
||||
};
|
||||
|
||||
void foo()
|
||||
{
|
||||
1 != 2;
|
||||
S() != S();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void templ()
|
||||
{
|
||||
1 != 2;
|
||||
T() != S();
|
||||
}
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<*></td><td class="name" onclick="toggle('eachOf0')"><a name="eachOf0Anchor">eachOf</a></td><td>Matcher<*>, ..., Matcher<*></td></tr>
|
||||
<tr><td colspan="4" class="doc" id="eachOf0"><pre>Matches if any of the given matchers matches.
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue