diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -1206,6 +1206,7 @@ Example matches a || b !(a || b) +See also the binaryOperation() matcher for more-general matching. @@ -1505,6 +1506,8 @@ 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. @@ -8100,6 +8103,48 @@ +
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();
+ }
+Matches declaration of the function the statement belongs to
diff --git a/clang/docs/tools/dump_ast_matchers.py b/clang/docs/tools/dump_ast_matchers.py
--- a/clang/docs/tools/dump_ast_matchers.py
+++ b/clang/docs/tools/dump_ast_matchers.py
@@ -126,6 +126,9 @@
args = "nodeMatcherFunction..."
docs_result_type = "unspecified"
+ if name == 'binaryOperation':
+ args = 'Matcher<*>, ..., Matcher<*>'
+
matcher_html = TD_TEMPLATE % {
'result': docs_result_type,
'name': name,
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -1973,6 +1973,8 @@
/// 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
cxxOperatorCallExpr;
@@ -2395,6 +2397,7 @@
/// \code
/// !(a || b)
/// \endcode
+/// See also the binaryOperation() matcher for more-general matching.
extern const internal::VariadicDynCastAllOfMatcher
binaryOperator;
@@ -2733,6 +2736,56 @@
NodeMatcher...);
}
+/// 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
+/// void templ()
+/// {
+/// 1 != 2;
+/// T() != S();
+/// }
+/// \endcode
+template
+internal::BindableMatcher
+binaryOperation(InnerMatcherType &&... Matchers) {
+ return mapAnyOf(binaryOperator, cxxOperatorCallExpr).with(Matchers...);
+}
+
/// Matches unary expressions that have a specific type of argument.
///
/// Given
diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
--- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -478,6 +478,10 @@
return;
}
+ if (GetParam().hasDelayedTemplateParsing()) {
+ return;
+ }
+
StringRef Code = R"cpp(
void F() {
if (true) {}
@@ -549,6 +553,15 @@
if (s1 != s2)
return;
}
+
+template
+void templ()
+{
+ T s1;
+ T s2;
+ if (s1 != s2)
+ return;
+}
)cpp";
EXPECT_TRUE(matches(
@@ -625,6 +638,30 @@
.with(hasAnyOperatorName("==", "!="),
forFunction(functionDecl(hasName("opCall")))))));
+ 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("opCall"))),
+ 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 HasOpBang
{