[clang] e810e95 - [ASTMatchers] Add binaryOperation matcher
Stephen Kelly via cfe-commits
cfe-commits at lists.llvm.org
Sat Jan 16 05:44:21 PST 2021
Author: Stephen Kelly
Date: 2021-01-16T13:44:09Z
New Revision: e810e95e4bb908d1c8844e2c6f7da999732cabc9
URL: https://github.com/llvm/llvm-project/commit/e810e95e4bb908d1c8844e2c6f7da999732cabc9
DIFF: https://github.com/llvm/llvm-project/commit/e810e95e4bb908d1c8844e2c6f7da999732cabc9.diff
LOG: [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
Added:
Modified:
clang/docs/LibASTMatchersReference.html
clang/docs/tools/dump_ast_matchers.py
clang/include/clang/ASTMatchers/ASTMatchers.h
clang/lib/ASTMatchers/ASTMatchersInternal.cpp
clang/lib/ASTMatchers/Dynamic/Marshallers.h
clang/lib/ASTMatchers/Dynamic/Registry.cpp
clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
Removed:
################################################################################
diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html
index 497e2d4584a2..453d8f16c25a 100644
--- a/clang/docs/LibASTMatchersReference.html
+++ b/clang/docs/LibASTMatchersReference.html
@@ -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>
@@ -1505,6 +1506,8 @@ <h2 id="decl-matchers">Node Matchers</h2>
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 @@ <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<*></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.
diff --git a/clang/docs/tools/dump_ast_matchers.py b/clang/docs/tools/dump_ast_matchers.py
index d3aff1284f60..18afbdd36c6e 100755
--- a/clang/docs/tools/dump_ast_matchers.py
+++ b/clang/docs/tools/dump_ast_matchers.py
@@ -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) {
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index e81ac4cb7bf8..8140e20c9cad 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -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
diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
index eb0fffcc3c37..36917cf0db46 100644
--- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -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;
diff --git a/clang/lib/ASTMatchers/Dynamic/Marshallers.h b/clang/lib/ASTMatchers/Dynamic/Marshallers.h
index f53df1f6528a..23e26dcd9db6 100644
--- a/clang/lib/ASTMatchers/Dynamic/Marshallers.h
+++ b/clang/lib/ASTMatchers/Dynamic/Marshallers.h
@@ -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.
@@ -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
diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
index 390d883dc5e3..9dc92a881cbc 100644
--- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp
+++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -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);
diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
index 9e011892f6e5..ae57383273e7 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -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
{
More information about the cfe-commits
mailing list