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. @@ -5438,6 +5441,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 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
--- a/clang/docs/tools/dump_ast_matchers.py
+++ b/clang/docs/tools/dump_ast_matchers.py
@@ -379,6 +379,14 @@
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 Name(Matcher InnerMatcher) {
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
@@ -1971,6 +1971,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;
@@ -2393,6 +2395,7 @@
/// \code
/// !(a || b)
/// \endcode
+/// See also the binaryOperation() matcher for more-general matching.
extern const internal::VariadicDynCastAllOfMatcher
binaryOperator;
@@ -2729,6 +2732,53 @@
return internal::MapAnyOfHelper();
}
+/// 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
+extern const internal::MapAnyOfMatcher
+ 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
--- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -919,6 +919,8 @@
const internal::VariadicDynCastAllOfMatcher stmtExpr;
const internal::VariadicDynCastAllOfMatcher
binaryOperator;
+const internal::MapAnyOfMatcher
+ binaryOperation;
const internal::VariadicDynCastAllOfMatcher unaryOperator;
const internal::VariadicDynCastAllOfMatcher
conditionalOperator;
diff --git a/clang/lib/ASTMatchers/Dynamic/Marshallers.h b/clang/lib/ASTMatchers/Dynamic/Marshallers.h
--- a/clang/lib/ASTMatchers/Dynamic/Marshallers.h
+++ b/clang/lib/ASTMatchers/Dynamic/Marshallers.h
@@ -925,6 +925,50 @@
const StringRef MatcherName;
};
+template
+class MapAnyOfMatcherDescriptor : public MatcherDescriptor {
+ std::vector Funcs;
+
+public:
+ MapAnyOfMatcherDescriptor(StringRef MatcherName)
+ : Funcs{DynCastAllOfMatcherDescriptor(
+ ast_matchers::internal::VariadicDynCastAllOfMatcher{},
+ MatcherName)...} {}
+
+ VariantMatcher create(SourceRange NameRange, ArrayRef Args,
+ Diagnostics *Error) const override {
+ std::vector 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(
+ VariantMatcher::VariadicOperatorMatcher(
+ ast_matchers::internal::DynTypedMatcher::VO_AnyOf,
+ std::move(InnerArgs))
+ .getTypedMatcher()));
+ }
+
+ bool isVariadic() const override { return true; }
+ unsigned getNumArgs() const override { return 0; }
+
+ void getArgKinds(ASTNodeKind ThisKind, unsigned,
+ std::vector &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 @@
MinCount, MaxCount, Func.Op, MatcherName);
}
+template
+std::unique_ptr makeMatcherAutoMarshall(
+ ast_matchers::internal::MapAnyOfMatcherImpl,
+ StringRef MatcherName) {
+ return std::make_unique>(
+ MatcherName);
+}
+
} // namespace internal
} // namespace dynamic
} // namespace ast_matchers
diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
--- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp
+++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -143,6 +143,7 @@
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
--- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -470,6 +470,10 @@
return;
}
+ if (GetParam().hasDelayedTemplateParsing()) {
+ return;
+ }
+
StringRef Code = R"cpp(
void F() {
if (true) {}
@@ -556,6 +560,15 @@
if (s1 != s2)
return;
}
+
+template
+void templ()
+{
+ T s1;
+ T s2;
+ if (s1 != s2)
+ return;
+}
)cpp";
EXPECT_TRUE(matches(
@@ -669,6 +682,38 @@
.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
{