[clang] b765eaf - [ASTMatchers] Add support for CXXRewrittenBinaryOperator
Stephen Kelly via cfe-commits
cfe-commits at lists.llvm.org
Sat Jan 16 06:23:20 PST 2021
Author: Stephen Kelly
Date: 2021-01-16T13:44:22Z
New Revision: b765eaf9a617bd3da30f47ece731b33593929885
URL: https://github.com/llvm/llvm-project/commit/b765eaf9a617bd3da30f47ece731b33593929885
DIFF: https://github.com/llvm/llvm-project/commit/b765eaf9a617bd3da30f47ece731b33593929885.diff
LOG: [ASTMatchers] Add support for CXXRewrittenBinaryOperator
Differential Revision: https://reviews.llvm.org/D94130
Added:
Modified:
clang/docs/LibASTMatchersReference.html
clang/include/clang/AST/ASTNodeTraverser.h
clang/include/clang/AST/ExprCXX.h
clang/include/clang/ASTMatchers/ASTMatchers.h
clang/include/clang/ASTMatchers/ASTMatchersInternal.h
clang/lib/ASTMatchers/ASTMatchFinder.cpp
clang/lib/ASTMatchers/ASTMatchersInternal.cpp
clang/lib/ASTMatchers/Dynamic/Registry.cpp
clang/unittests/AST/ASTTraverserTest.cpp
clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
Removed:
################################################################################
diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html
index 453d8f16c25a..912da6e62c2f 100644
--- a/clang/docs/LibASTMatchersReference.html
+++ b/clang/docs/LibASTMatchersReference.html
@@ -498,6 +498,42 @@ <h2 id="traverse-mode">Traverse Mode</h2>
</td>
</tr>
+
+
+
+
+
+<tr>
+ <td>Rewritten binary operators
+<pre>
+binaryOperator(
+ hasOperatorName("<"),
+ hasRHS(integerLiteral(equals(0)))
+ )
+</pre>
+given:
+<pre>
+#include <compare>
+
+class HasSpaceship {
+public:
+ int x;
+ bool operator==(const HasSpaceship&) const = default;
+ std::strong_ordering operator<=>(const HasSpaceship&) const = default;
+};
+
+bool isLess(const HasSpaceship& a, const HasSpaceship& b) {
+ return a < b;
+}
+</pre>
+</td>
+<td>
+1 match found.
+</td>
+<td>
+No match found.
+</td>
+</tr>
</table>
<!-- ======================================================================= -->
@@ -1523,6 +1559,25 @@ <h2 id="decl-matchers">Node Matchers</h2>
</pre></td></tr>
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>></td><td class="name" onclick="toggle('cxxRewrittenBinaryOperator0')"><a name="cxxRewrittenBinaryOperator0Anchor">cxxRewrittenBinaryOperator</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRewrittenBinaryOperator.html">CXXRewrittenBinaryOperator</a>>...</td></tr>
+<tr><td colspan="4" class="doc" id="cxxRewrittenBinaryOperator0"><pre>Matches rewritten binary operators
+
+Example matches use of "<":
+ #include <compare>
+ struct HasSpaceshipMem {
+ int a;
+ constexpr auto operator<=>(const HasSpaceshipMem&) const = default;
+ };
+ void compare() {
+ HasSpaceshipMem hs1, hs2;
+ if (hs1 < hs2)
+ return;
+ }
+See also the binaryOperation() matcher for more-general matching
+of this AST node.
+</pre></td></tr>
+
+
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>></td><td class="name" onclick="toggle('cxxStaticCastExpr0')"><a name="cxxStaticCastExpr0Anchor">cxxStaticCastExpr</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXStaticCastExpr.html">CXXStaticCastExpr</a>>...</td></tr>
<tr><td colspan="4" class="doc" id="cxxStaticCastExpr0"><pre>Matches a C++ static_cast expression.
@@ -3405,6 +3460,53 @@ <h2 id="narrowing-matchers">Narrowing Matchers</h2>
</pre></td></tr>
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRewrittenBinaryOperator.html">CXXRewrittenBinaryOperator</a>></td><td class="name" onclick="toggle('hasAnyOperatorName2')"><a name="hasAnyOperatorName2Anchor">hasAnyOperatorName</a></td><td>StringRef, ..., StringRef</td></tr>
+<tr><td colspan="4" class="doc" id="hasAnyOperatorName2"><pre>Matches operator expressions (binary or unary) that have any of the
+specified names.
+
+ hasAnyOperatorName("+", "-")
+ Is equivalent to
+ anyOf(hasOperatorName("+"), hasOperatorName("-"))
+</pre></td></tr>
+
+
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRewrittenBinaryOperator.html">CXXRewrittenBinaryOperator</a>></td><td class="name" onclick="toggle('hasOperatorName2')"><a name="hasOperatorName2Anchor">hasOperatorName</a></td><td>std::string Name</td></tr>
+<tr><td colspan="4" class="doc" id="hasOperatorName2"><pre>Matches the operator Name of operator expressions (binary or
+unary).
+
+Example matches a || b (matcher = binaryOperator(hasOperatorName("||")))
+ !(a || b)
+</pre></td></tr>
+
+
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRewrittenBinaryOperator.html">CXXRewrittenBinaryOperator</a>></td><td class="name" onclick="toggle('isAssignmentOperator2')"><a name="isAssignmentOperator2Anchor">isAssignmentOperator</a></td><td></td></tr>
+<tr><td colspan="4" class="doc" id="isAssignmentOperator2"><pre>Matches all kinds of assignment operators.
+
+Example 1: matches a += b (matcher = binaryOperator(isAssignmentOperator()))
+ if (a == b)
+ a += b;
+
+Example 2: matches s1 = s2
+ (matcher = cxxOperatorCallExpr(isAssignmentOperator()))
+ struct S { S& operator=(const S&); };
+ void x() { S s1, s2; s1 = s2; }
+</pre></td></tr>
+
+
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRewrittenBinaryOperator.html">CXXRewrittenBinaryOperator</a>></td><td class="name" onclick="toggle('isComparisonOperator2')"><a name="isComparisonOperator2Anchor">isComparisonOperator</a></td><td></td></tr>
+<tr><td colspan="4" class="doc" id="isComparisonOperator2"><pre>Matches comparison operators.
+
+Example 1: matches a == b (matcher = binaryOperator(isComparisonOperator()))
+ if (a == b)
+ a += b;
+
+Example 2: matches s1 < s2
+ (matcher = cxxOperatorCallExpr(isComparisonOperator()))
+ struct S { bool operator<(const S& other); };
+ void x(S s1, S s2) { bool b1 = s1 < s2; }
+</pre></td></tr>
+
+
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXUnresolvedConstructExpr.html">CXXUnresolvedConstructExpr</a>></td><td class="name" onclick="toggle('argumentCountIs2')"><a name="argumentCountIs2Anchor">argumentCountIs</a></td><td>unsigned N</td></tr>
<tr><td colspan="4" class="doc" id="argumentCountIs2"><pre>Checks that a call expression or a constructor call expression has
a specific number of arguments (including absent default arguments).
@@ -5174,8 +5276,8 @@ <h2 id="narrowing-matchers">Narrowing Matchers</h2>
</pre></td></tr>
-<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1UnaryOperator.html">UnaryOperator</a>></td><td class="name" onclick="toggle('hasAnyOperatorName2')"><a name="hasAnyOperatorName2Anchor">hasAnyOperatorName</a></td><td>StringRef, ..., StringRef</td></tr>
-<tr><td colspan="4" class="doc" id="hasAnyOperatorName2"><pre>Matches operator expressions (binary or unary) that have any of the
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1UnaryOperator.html">UnaryOperator</a>></td><td class="name" onclick="toggle('hasAnyOperatorName3')"><a name="hasAnyOperatorName3Anchor">hasAnyOperatorName</a></td><td>StringRef, ..., StringRef</td></tr>
+<tr><td colspan="4" class="doc" id="hasAnyOperatorName3"><pre>Matches operator expressions (binary or unary) that have any of the
specified names.
hasAnyOperatorName("+", "-")
@@ -5184,8 +5286,8 @@ <h2 id="narrowing-matchers">Narrowing Matchers</h2>
</pre></td></tr>
-<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1UnaryOperator.html">UnaryOperator</a>></td><td class="name" onclick="toggle('hasOperatorName2')"><a name="hasOperatorName2Anchor">hasOperatorName</a></td><td>std::string Name</td></tr>
-<tr><td colspan="4" class="doc" id="hasOperatorName2"><pre>Matches the operator Name of operator expressions (binary or
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1UnaryOperator.html">UnaryOperator</a>></td><td class="name" onclick="toggle('hasOperatorName3')"><a name="hasOperatorName3Anchor">hasOperatorName</a></td><td>std::string Name</td></tr>
+<tr><td colspan="4" class="doc" id="hasOperatorName3"><pre>Matches the operator Name of operator expressions (binary or
unary).
Example matches a || b (matcher = binaryOperator(hasOperatorName("||")))
@@ -5446,13 +5548,16 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
The code
var1 != var2;
-might be represented in the clang AST as a binaryOperator or a
-cxxOperatorCallExpr, depending on
+might be represented in the clang AST as a binaryOperator, a
+cxxOperatorCallExpr or a cxxRewrittenBinaryOperator, 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)
+* whether the code relies on a rewritten binary operator, such as a
+spaceship operator or an inverted equality operator
+(cxxRewrittenBinaryOperator)
This matcher elides details in places where the matchers for the nodes are
compatible.
@@ -5480,6 +5585,31 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
1 != 2;
T() != S();
}
+ struct HasOpEq
+ {
+ bool operator==(const HasOpEq &) const;
+ };
+
+ void inverse()
+ {
+ HasOpEq s1;
+ HasOpEq s2;
+ if (s1 != s2)
+ return;
+ }
+
+ struct HasSpaceship
+ {
+ bool operator<=>(const HasOpEq &) const;
+ };
+
+ void use_spaceship()
+ {
+ HasSpaceship s1;
+ HasSpaceship s2;
+ if (s1 != s2)
+ return;
+ }
</pre></td></tr>
@@ -5753,16 +5883,16 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
</pre></td></tr>
-<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ArraySubscriptExpr.html">ArraySubscriptExpr</a>></td><td class="name" onclick="toggle('hasLHS2')"><a name="hasLHS2Anchor">hasLHS</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr>
-<tr><td colspan="4" class="doc" id="hasLHS2"><pre>Matches the left hand side of binary operator expressions.
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ArraySubscriptExpr.html">ArraySubscriptExpr</a>></td><td class="name" onclick="toggle('hasLHS3')"><a name="hasLHS3Anchor">hasLHS</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr>
+<tr><td colspan="4" class="doc" id="hasLHS3"><pre>Matches the left hand side of binary operator expressions.
Example matches a (matcher = binaryOperator(hasLHS()))
a || b
</pre></td></tr>
-<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ArraySubscriptExpr.html">ArraySubscriptExpr</a>></td><td class="name" onclick="toggle('hasRHS2')"><a name="hasRHS2Anchor">hasRHS</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr>
-<tr><td colspan="4" class="doc" id="hasRHS2"><pre>Matches the right hand side of binary operator expressions.
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ArraySubscriptExpr.html">ArraySubscriptExpr</a>></td><td class="name" onclick="toggle('hasRHS3')"><a name="hasRHS3Anchor">hasRHS</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr>
+<tr><td colspan="4" class="doc" id="hasRHS3"><pre>Matches the right hand side of binary operator expressions.
Example matches b (matcher = binaryOperator(hasRHS()))
a || b
@@ -6480,6 +6610,40 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
</pre></td></tr>
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRewrittenBinaryOperator.html">CXXRewrittenBinaryOperator</a>></td><td class="name" onclick="toggle('hasEitherOperand2')"><a name="hasEitherOperand2Anchor">hasEitherOperand</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr>
+<tr><td colspan="4" class="doc" id="hasEitherOperand2"><pre>Matches if either the left hand side or the right hand side of a
+binary operator matches.
+</pre></td></tr>
+
+
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRewrittenBinaryOperator.html">CXXRewrittenBinaryOperator</a>></td><td class="name" onclick="toggle('hasLHS2')"><a name="hasLHS2Anchor">hasLHS</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr>
+<tr><td colspan="4" class="doc" id="hasLHS2"><pre>Matches the left hand side of binary operator expressions.
+
+Example matches a (matcher = binaryOperator(hasLHS()))
+ a || b
+</pre></td></tr>
+
+
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRewrittenBinaryOperator.html">CXXRewrittenBinaryOperator</a>></td><td class="name" onclick="toggle('hasOperands2')"><a name="hasOperands2Anchor">hasOperands</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> Matcher1, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> Matcher2</td></tr>
+<tr><td colspan="4" class="doc" id="hasOperands2"><pre>Matches if both matchers match with opposite sides of the binary operator.
+
+Example matcher = binaryOperator(hasOperands(integerLiteral(equals(1),
+ integerLiteral(equals(2)))
+ 1 + 2 // Match
+ 2 + 1 // Match
+ 1 + 1 // No match
+ 2 + 2 // No match
+</pre></td></tr>
+
+
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRewrittenBinaryOperator.html">CXXRewrittenBinaryOperator</a>></td><td class="name" onclick="toggle('hasRHS2')"><a name="hasRHS2Anchor">hasRHS</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr>
+<tr><td colspan="4" class="doc" id="hasRHS2"><pre>Matches the right hand side of binary operator expressions.
+
+Example matches b (matcher = binaryOperator(hasRHS()))
+ a || b
+</pre></td></tr>
+
+
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXUnresolvedConstructExpr.html">CXXUnresolvedConstructExpr</a>></td><td class="name" onclick="toggle('hasAnyArgument2')"><a name="hasAnyArgument2Anchor">hasAnyArgument</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr>
<tr><td colspan="4" class="doc" id="hasAnyArgument2"><pre>Matches any argument of a call expression or a constructor call
expression, or an ObjC-message-send expression.
diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h
index 6f7affe66273..bb5b0c73f028 100644
--- a/clang/include/clang/AST/ASTNodeTraverser.h
+++ b/clang/include/clang/AST/ASTNodeTraverser.h
@@ -145,7 +145,8 @@ class ASTNodeTraverser
return;
if (Traversal == TK_IgnoreUnlessSpelledInSource &&
- isa<LambdaExpr, CXXForRangeStmt, CallExpr>(S))
+ isa<LambdaExpr, CXXForRangeStmt, CallExpr,
+ CXXRewrittenBinaryOperator>(S))
return;
for (const Stmt *SubStmt : S->children())
@@ -746,6 +747,15 @@ class ASTNodeTraverser
}
}
+ void VisitCXXRewrittenBinaryOperator(const CXXRewrittenBinaryOperator *Node) {
+ if (Traversal == TK_IgnoreUnlessSpelledInSource) {
+ Visit(Node->getLHS());
+ Visit(Node->getRHS());
+ } else {
+ ConstStmtVisitor<Derived>::VisitCXXRewrittenBinaryOperator(Node);
+ }
+ }
+
void VisitExpressionTemplateArgument(const TemplateArgument &TA) {
Visit(TA.getAsExpr());
}
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 1efa78fc4294..2656952377b3 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -320,6 +320,16 @@ class CXXRewrittenBinaryOperator : public Expr {
bool isReversed() const { return CXXRewrittenBinaryOperatorBits.IsReversed; }
BinaryOperatorKind getOperator() const { return getDecomposedForm().Opcode; }
+ BinaryOperatorKind getOpcode() const { return getOperator(); }
+ static StringRef getOpcodeStr(BinaryOperatorKind Op) {
+ return BinaryOperator::getOpcodeStr(Op);
+ }
+ StringRef getOpcodeStr() const {
+ return BinaryOperator::getOpcodeStr(getOpcode());
+ }
+ bool isComparisonOp() const { return true; }
+ bool isAssignmentOp() const { return false; }
+
const Expr *getLHS() const { return getDecomposedForm().LHS; }
const Expr *getRHS() const { return getDecomposedForm().RHS; }
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index 8140e20c9cad..6f6dfab59a39 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -1976,6 +1976,27 @@ extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXDefaultArgExpr>
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXOperatorCallExpr>
cxxOperatorCallExpr;
+/// Matches rewritten binary operators
+///
+/// Example matches use of "<":
+/// \code
+/// #include <compare>
+/// struct HasSpaceshipMem {
+/// int a;
+/// constexpr auto operator<=>(const HasSpaceshipMem&) const = default;
+/// };
+/// void compare() {
+/// HasSpaceshipMem hs1, hs2;
+/// if (hs1 < hs2)
+/// return;
+/// }
+/// \endcode
+/// See also the binaryOperation() matcher for more-general matching
+/// of this AST node.
+extern const internal::VariadicDynCastAllOfMatcher<Stmt,
+ CXXRewrittenBinaryOperator>
+ cxxRewrittenBinaryOperator;
+
/// Matches expressions.
///
/// Example matches x()
@@ -2738,13 +2759,16 @@ auto mapAnyOf(internal::VariadicDynCastAllOfMatcher<T, U> const &...) {
/// \code
/// var1 != var2;
/// \endcode
-/// might be represented in the clang AST as a binaryOperator or a
-/// cxxOperatorCallExpr, depending on
+/// might be represented in the clang AST as a binaryOperator, a
+/// cxxOperatorCallExpr or a cxxRewrittenBinaryOperator, 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)
+/// * whether the code relies on a rewritten binary operator, such as a
+/// spaceship operator or an inverted equality operator
+/// (cxxRewrittenBinaryOperator)
///
/// This matcher elides details in places where the matchers for the nodes are
/// compatible.
@@ -2775,8 +2799,34 @@ auto mapAnyOf(internal::VariadicDynCastAllOfMatcher<T, U> const &...) {
/// 1 != 2;
/// T() != S();
/// }
+/// struct HasOpEq
+/// {
+/// bool operator==(const HasOpEq &) const;
+/// };
+///
+/// void inverse()
+/// {
+/// HasOpEq s1;
+/// HasOpEq s2;
+/// if (s1 != s2)
+/// return;
+/// }
+///
+/// struct HasSpaceship
+/// {
+/// bool operator<=>(const HasOpEq &) const;
+/// };
+///
+/// void use_spaceship()
+/// {
+/// HasSpaceship s1;
+/// HasSpaceship s2;
+/// if (s1 != s2)
+/// return;
+/// }
/// \endcode
-extern const internal::MapAnyOfMatcher<BinaryOperator, CXXOperatorCallExpr>
+extern const internal::MapAnyOfMatcher<BinaryOperator, CXXOperatorCallExpr,
+ CXXRewrittenBinaryOperator>
binaryOperation;
/// Matches unary expressions that have a specific type of argument.
@@ -5245,11 +5295,11 @@ AST_POLYMORPHIC_MATCHER_P_OVERLOAD(equals,
/// \code
/// !(a || b)
/// \endcode
-AST_POLYMORPHIC_MATCHER_P(hasOperatorName,
- AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator,
- CXXOperatorCallExpr,
- UnaryOperator),
- std::string, Name) {
+AST_POLYMORPHIC_MATCHER_P(
+ hasOperatorName,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
+ CXXRewrittenBinaryOperator, UnaryOperator),
+ std::string, Name) {
if (Optional<StringRef> OpName = internal::getOpName(Node))
return *OpName == Name;
return false;
@@ -5265,6 +5315,7 @@ extern const internal::VariadicFunction<
internal::PolymorphicMatcherWithParam1<
internal::HasAnyOperatorNameMatcher, std::vector<std::string>,
AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
+ CXXRewrittenBinaryOperator,
UnaryOperator)>,
StringRef, internal::hasAnyOperatorNameFunc>
hasAnyOperatorName;
@@ -5283,9 +5334,10 @@ extern const internal::VariadicFunction<
/// struct S { S& operator=(const S&); };
/// void x() { S s1, s2; s1 = s2; }
/// \endcode
-AST_POLYMORPHIC_MATCHER(isAssignmentOperator,
- AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator,
- CXXOperatorCallExpr)) {
+AST_POLYMORPHIC_MATCHER(
+ isAssignmentOperator,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
+ CXXRewrittenBinaryOperator)) {
return Node.isAssignmentOp();
}
@@ -5303,9 +5355,10 @@ AST_POLYMORPHIC_MATCHER(isAssignmentOperator,
/// struct S { bool operator<(const S& other); };
/// void x(S s1, S s2) { bool b1 = s1 < s2; }
/// \endcode
-AST_POLYMORPHIC_MATCHER(isComparisonOperator,
- AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator,
- CXXOperatorCallExpr)) {
+AST_POLYMORPHIC_MATCHER(
+ isComparisonOperator,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
+ CXXRewrittenBinaryOperator)) {
return Node.isComparisonOp();
}
@@ -5316,9 +5369,9 @@ AST_POLYMORPHIC_MATCHER(isComparisonOperator,
/// a || b
/// \endcode
AST_POLYMORPHIC_MATCHER_P(hasLHS,
- AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator,
- CXXOperatorCallExpr,
- ArraySubscriptExpr),
+ AST_POLYMORPHIC_SUPPORTED_TYPES(
+ BinaryOperator, CXXOperatorCallExpr,
+ CXXRewrittenBinaryOperator, ArraySubscriptExpr),
internal::Matcher<Expr>, InnerMatcher) {
const Expr *LeftHandSide = internal::getLHS(Node);
return (LeftHandSide != nullptr &&
@@ -5332,9 +5385,9 @@ AST_POLYMORPHIC_MATCHER_P(hasLHS,
/// a || b
/// \endcode
AST_POLYMORPHIC_MATCHER_P(hasRHS,
- AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator,
- CXXOperatorCallExpr,
- ArraySubscriptExpr),
+ AST_POLYMORPHIC_SUPPORTED_TYPES(
+ BinaryOperator, CXXOperatorCallExpr,
+ CXXRewrittenBinaryOperator, ArraySubscriptExpr),
internal::Matcher<Expr>, InnerMatcher) {
const Expr *RightHandSide = internal::getRHS(Node);
return (RightHandSide != nullptr &&
@@ -5343,10 +5396,11 @@ AST_POLYMORPHIC_MATCHER_P(hasRHS,
/// Matches if either the left hand side or the right hand side of a
/// binary operator matches.
-AST_POLYMORPHIC_MATCHER_P(hasEitherOperand,
- AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator,
- CXXOperatorCallExpr),
- internal::Matcher<Expr>, InnerMatcher) {
+AST_POLYMORPHIC_MATCHER_P(
+ hasEitherOperand,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
+ CXXRewrittenBinaryOperator),
+ internal::Matcher<Expr>, InnerMatcher) {
return internal::VariadicDynCastAllOfMatcher<Stmt, NodeType>()(
anyOf(hasLHS(InnerMatcher), hasRHS(InnerMatcher)))
.matches(Node, Finder, Builder);
@@ -5362,11 +5416,11 @@ AST_POLYMORPHIC_MATCHER_P(hasEitherOperand,
/// 1 + 1 // No match
/// 2 + 2 // No match
/// \endcode
-AST_POLYMORPHIC_MATCHER_P2(hasOperands,
- AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator,
- CXXOperatorCallExpr),
- internal::Matcher<Expr>, Matcher1,
- internal::Matcher<Expr>, Matcher2) {
+AST_POLYMORPHIC_MATCHER_P2(
+ hasOperands,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
+ CXXRewrittenBinaryOperator),
+ internal::Matcher<Expr>, Matcher1, internal::Matcher<Expr>, Matcher2) {
return internal::VariadicDynCastAllOfMatcher<Stmt, NodeType>()(
anyOf(allOf(hasLHS(Matcher1), hasRHS(Matcher2)),
allOf(hasLHS(Matcher2), hasRHS(Matcher1))))
diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
index 7f97eb3cd438..f56cda318f4e 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -2089,6 +2089,9 @@ inline Optional<StringRef> getOpName(const UnaryOperator &Node) {
inline Optional<StringRef> getOpName(const BinaryOperator &Node) {
return Node.getOpcodeStr();
}
+inline StringRef getOpName(const CXXRewrittenBinaryOperator &Node) {
+ return Node.getOpcodeStr();
+}
inline Optional<StringRef> getOpName(const CXXOperatorCallExpr &Node) {
auto optBinaryOpcode = equivalentBinaryOperator(Node);
if (!optBinaryOpcode) {
@@ -2108,9 +2111,10 @@ template <typename T, typename ArgT = std::vector<std::string>>
class HasAnyOperatorNameMatcher : public SingleNodeMatcherInterface<T> {
static_assert(std::is_same<T, BinaryOperator>::value ||
std::is_same<T, CXXOperatorCallExpr>::value ||
+ std::is_same<T, CXXRewrittenBinaryOperator>::value ||
std::is_same<T, UnaryOperator>::value,
- "Matcher only supports `BinaryOperator`, `UnaryOperator` and "
- "`CXXOperatorCallExpr`");
+ "Matcher only supports `BinaryOperator`, `UnaryOperator`, "
+ "`CXXOperatorCallExpr` and `CXXRewrittenBinaryOperator`");
static_assert(std::is_same<ArgT, std::vector<std::string>>::value,
"Matcher ArgT must be std::vector<std::string>");
@@ -2128,12 +2132,33 @@ class HasAnyOperatorNameMatcher : public SingleNodeMatcherInterface<T> {
}
private:
+ static Optional<StringRef> getOpName(const UnaryOperator &Node) {
+ return Node.getOpcodeStr(Node.getOpcode());
+ }
+ static Optional<StringRef> getOpName(const BinaryOperator &Node) {
+ return Node.getOpcodeStr();
+ }
+ static StringRef getOpName(const CXXRewrittenBinaryOperator &Node) {
+ return Node.getOpcodeStr();
+ }
+ static Optional<StringRef> getOpName(const CXXOperatorCallExpr &Node) {
+ auto optBinaryOpcode = equivalentBinaryOperator(Node);
+ if (!optBinaryOpcode) {
+ auto optUnaryOpcode = equivalentUnaryOperator(Node);
+ if (!optUnaryOpcode)
+ return None;
+ return UnaryOperator::getOpcodeStr(*optUnaryOpcode);
+ }
+ return BinaryOperator::getOpcodeStr(*optBinaryOpcode);
+ }
+
const std::vector<std::string> Names;
};
using HasOpNameMatcher = PolymorphicMatcherWithParam1<
HasAnyOperatorNameMatcher, std::vector<std::string>,
- void(TypeList<BinaryOperator, CXXOperatorCallExpr, UnaryOperator>)>;
+ void(TypeList<BinaryOperator, CXXOperatorCallExpr,
+ CXXRewrittenBinaryOperator, UnaryOperator>)>;
HasOpNameMatcher hasAnyOperatorNameFunc(ArrayRef<const StringRef *> NameRefs);
diff --git a/clang/lib/ASTMatchers/ASTMatchFinder.cpp b/clang/lib/ASTMatchers/ASTMatchFinder.cpp
index 39bdb94e62c6..e35600a083d3 100644
--- a/clang/lib/ASTMatchers/ASTMatchFinder.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchFinder.cpp
@@ -250,6 +250,18 @@ class MatchChildASTVisitor
return false;
return VisitorBase::TraverseStmt(Node->getBody());
}
+ bool TraverseCXXRewrittenBinaryOperator(CXXRewrittenBinaryOperator *Node) {
+ if (!Finder->isTraversalIgnoringImplicitNodes())
+ return VisitorBase::TraverseCXXRewrittenBinaryOperator(Node);
+ if (!Node)
+ return true;
+ ScopedIncrement ScopedDepth(&CurrentDepth);
+
+ if (!match(*Node->getDecomposedForm().LHS) ||
+ !match(*Node->getDecomposedForm().RHS))
+ return false;
+ return true;
+ }
bool TraverseLambdaExpr(LambdaExpr *Node) {
if (!Finder->isTraversalIgnoringImplicitNodes())
return VisitorBase::TraverseLambdaExpr(Node);
@@ -489,6 +501,19 @@ class MatchASTVisitor : public RecursiveASTVisitor<MatchASTVisitor>,
}
}
return true;
+ } else if (auto *RBO = dyn_cast<CXXRewrittenBinaryOperator>(S)) {
+ {
+ ASTNodeNotAsIsSourceScope RAII(this, true);
+ TraverseStmt(const_cast<Expr *>(RBO->getLHS()));
+ TraverseStmt(const_cast<Expr *>(RBO->getRHS()));
+ }
+ {
+ ASTNodeNotSpelledInSourceScope RAII(this, true);
+ for (auto *SubStmt : RBO->children()) {
+ TraverseStmt(SubStmt);
+ }
+ }
+ return true;
} else if (auto *LE = dyn_cast<LambdaExpr>(S)) {
for (auto I : llvm::zip(LE->captures(), LE->capture_inits())) {
auto C = std::get<0>(I);
diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
index 36917cf0db46..6c7e14e3499a 100644
--- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -868,6 +868,8 @@ const internal::VariadicDynCastAllOfMatcher<Stmt, CXXDefaultArgExpr>
cxxDefaultArgExpr;
const internal::VariadicDynCastAllOfMatcher<Stmt, CXXOperatorCallExpr>
cxxOperatorCallExpr;
+const internal::VariadicDynCastAllOfMatcher<Stmt, CXXRewrittenBinaryOperator>
+ cxxRewrittenBinaryOperator;
const internal::VariadicDynCastAllOfMatcher<Stmt, Expr> expr;
const internal::VariadicDynCastAllOfMatcher<Stmt, DeclRefExpr> declRefExpr;
const internal::VariadicDynCastAllOfMatcher<Stmt, ObjCIvarRefExpr> objcIvarRefExpr;
@@ -919,7 +921,8 @@ const internal::VariadicDynCastAllOfMatcher<Stmt, AtomicExpr> atomicExpr;
const internal::VariadicDynCastAllOfMatcher<Stmt, StmtExpr> stmtExpr;
const internal::VariadicDynCastAllOfMatcher<Stmt, BinaryOperator>
binaryOperator;
-const internal::MapAnyOfMatcher<BinaryOperator, CXXOperatorCallExpr>
+const internal::MapAnyOfMatcher<BinaryOperator, CXXOperatorCallExpr,
+ CXXRewrittenBinaryOperator>
binaryOperation;
const internal::VariadicDynCastAllOfMatcher<Stmt, UnaryOperator> unaryOperator;
const internal::VariadicDynCastAllOfMatcher<Stmt, ConditionalOperator>
diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
index 9dc92a881cbc..00a7c74a0b90 100644
--- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp
+++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -192,6 +192,7 @@ RegistryMaps::RegistryMaps() {
REGISTER_MATCHER(cxxOperatorCallExpr);
REGISTER_MATCHER(cxxRecordDecl);
REGISTER_MATCHER(cxxReinterpretCastExpr);
+ REGISTER_MATCHER(cxxRewrittenBinaryOperator);
REGISTER_MATCHER(cxxStaticCastExpr);
REGISTER_MATCHER(cxxStdInitializerListExpr);
REGISTER_MATCHER(cxxTemporaryObjectExpr);
diff --git a/clang/unittests/AST/ASTTraverserTest.cpp b/clang/unittests/AST/ASTTraverserTest.cpp
index 184bddce7f2e..94b9572ad50d 100644
--- a/clang/unittests/AST/ASTTraverserTest.cpp
+++ b/clang/unittests/AST/ASTTraverserTest.cpp
@@ -1733,4 +1733,64 @@ ClassTemplateSpecializationDecl 'TemplStruct'
}
}
+TEST(Traverse, CXXRewrittenBinaryOperator) {
+
+ auto AST = buildASTFromCodeWithArgs(R"cpp(
+namespace std {
+struct strong_ordering {
+ int n;
+ constexpr operator int() const { return n; }
+ static const strong_ordering equal, greater, less;
+};
+constexpr strong_ordering strong_ordering::equal = {0};
+constexpr strong_ordering strong_ordering::greater = {1};
+constexpr strong_ordering strong_ordering::less = {-1};
+}
+
+struct HasSpaceshipMem {
+ int a;
+ constexpr auto operator<=>(const HasSpaceshipMem&) const = default;
+};
+
+void binop()
+{
+ HasSpaceshipMem hs1, hs2;
+ if (hs1 < hs2)
+ return;
+}
+)cpp",
+ {"-std=c++20"});
+ {
+ auto BN = ast_matchers::match(cxxRewrittenBinaryOperator().bind("binop"),
+ AST->getASTContext());
+ EXPECT_EQ(BN.size(), 1u);
+
+ EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Stmt>("binop")),
+ R"cpp(
+CXXRewrittenBinaryOperator
+`-BinaryOperator
+ |-ImplicitCastExpr
+ | `-CXXMemberCallExpr
+ | `-MemberExpr
+ | `-ImplicitCastExpr
+ | `-MaterializeTemporaryExpr
+ | `-CXXOperatorCallExpr
+ | |-ImplicitCastExpr
+ | | `-DeclRefExpr 'operator<=>'
+ | |-ImplicitCastExpr
+ | | `-DeclRefExpr 'hs1'
+ | `-ImplicitCastExpr
+ | `-DeclRefExpr 'hs2'
+ `-IntegerLiteral
+)cpp");
+ EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
+ BN[0].getNodeAs<Stmt>("binop")),
+ R"cpp(
+CXXRewrittenBinaryOperator
+|-DeclRefExpr 'hs1'
+`-DeclRefExpr 'hs2'
+)cpp");
+ }
+}
+
} // namespace clang
diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
index ae57383273e7..b3582a02243a 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -714,6 +714,65 @@ void templ()
hasLHS(declRefExpr(to(varDecl(hasName("s1"))))),
hasRHS(declRefExpr(to(varDecl(hasName("s2")))))))));
+ Code = R"cpp(
+struct HasOpEq
+{
+ bool operator==(const HasOpEq &) const;
+};
+
+void inverse()
+{
+ HasOpEq s1;
+ HasOpEq s2;
+ if (s1 != s2)
+ return;
+}
+
+namespace std {
+struct strong_ordering {
+ int n;
+ constexpr operator int() const { return n; }
+ static const strong_ordering equal, greater, less;
+};
+constexpr strong_ordering strong_ordering::equal = {0};
+constexpr strong_ordering strong_ordering::greater = {1};
+constexpr strong_ordering strong_ordering::less = {-1};
+}
+
+struct HasSpaceshipMem {
+ int a;
+ constexpr auto operator<=>(const HasSpaceshipMem&) const = default;
+};
+
+void rewritten()
+{
+ HasSpaceshipMem s1;
+ HasSpaceshipMem s2;
+ if (s1 != s2)
+ return;
+}
+)cpp";
+
+ EXPECT_TRUE(matchesConditionally(
+ Code,
+ traverse(
+ TK_IgnoreUnlessSpelledInSource,
+ binaryOperation(hasOperatorName("!="),
+ forFunction(functionDecl(hasName("inverse"))),
+ hasLHS(declRefExpr(to(varDecl(hasName("s1"))))),
+ hasRHS(declRefExpr(to(varDecl(hasName("s2"))))))),
+ true, {"-std=c++20"}));
+
+ EXPECT_TRUE(matchesConditionally(
+ Code,
+ traverse(
+ TK_IgnoreUnlessSpelledInSource,
+ binaryOperation(hasOperatorName("!="),
+ forFunction(functionDecl(hasName("rewritten"))),
+ hasLHS(declRefExpr(to(varDecl(hasName("s1"))))),
+ hasRHS(declRefExpr(to(varDecl(hasName("s2"))))))),
+ true, {"-std=c++20"}));
+
Code = R"cpp(
struct HasOpBangMem
{
diff --git a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
index 06bcd65d8911..92bf244b0e4a 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -3330,6 +3330,527 @@ void foo() {
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, lambdaImplicitCapture)));
EXPECT_FALSE(matches(
Code, traverse(TK_IgnoreUnlessSpelledInSource, lambdaImplicitCapture)));
+
+ Code = R"cpp(
+struct S {};
+
+struct HasOpEq
+{
+ bool operator==(const S& other)
+ {
+ return true;
+ }
+};
+
+void binop()
+{
+ HasOpEq s1;
+ S s2;
+ if (s1 != s2)
+ return;
+}
+)cpp";
+ {
+ auto M = unaryOperator(
+ hasOperatorName("!"),
+ has(cxxOperatorCallExpr(hasOverloadedOperatorName("=="))));
+ EXPECT_TRUE(
+ matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"}));
+ EXPECT_FALSE(
+ matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
+ true, {"-std=c++20"}));
+ }
+ {
+ auto M = declRefExpr(to(varDecl(hasName("s1"))));
+ EXPECT_TRUE(
+ matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"}));
+ EXPECT_TRUE(
+ matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
+ true, {"-std=c++20"}));
+ }
+ {
+ auto M = cxxOperatorCallExpr(hasOverloadedOperatorName("=="));
+ EXPECT_TRUE(
+ matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"}));
+ EXPECT_FALSE(
+ matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
+ true, {"-std=c++20"}));
+ }
+ {
+ auto M = cxxOperatorCallExpr(hasOverloadedOperatorName("!="));
+ EXPECT_FALSE(
+ matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"}));
+ EXPECT_FALSE(
+ matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
+ true, {"-std=c++20"}));
+ }
+ auto withDescendants = [](StringRef lName, StringRef rName) {
+ return stmt(hasDescendant(declRefExpr(to(varDecl(hasName(lName))))),
+ hasDescendant(declRefExpr(to(varDecl(hasName(rName))))));
+ };
+ {
+ auto M = cxxRewrittenBinaryOperator(withDescendants("s1", "s2"));
+ EXPECT_TRUE(
+ matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"}));
+ EXPECT_TRUE(
+ matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
+ true, {"-std=c++20"}));
+ }
+ {
+ auto M = cxxRewrittenBinaryOperator(
+ has(declRefExpr(to(varDecl(hasName("s1"))))),
+ has(declRefExpr(to(varDecl(hasName("s2"))))));
+ EXPECT_FALSE(
+ matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"}));
+ EXPECT_TRUE(
+ matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
+ true, {"-std=c++20"}));
+ }
+ {
+ EXPECT_TRUE(matchesConditionally(
+ Code,
+ traverse(TK_AsIs,
+ cxxRewrittenBinaryOperator(
+ hasOperatorName("!="), hasAnyOperatorName("<", "!="),
+ isComparisonOperator(),
+ hasLHS(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("s1")))))),
+ hasRHS(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("s2")))))),
+ hasEitherOperand(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("s2")))))),
+ hasOperands(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("s1"))))),
+ ignoringImplicit(declRefExpr(
+ to(varDecl(hasName("s2")))))))),
+ true, {"-std=c++20"}));
+ EXPECT_TRUE(matchesConditionally(
+ Code,
+ traverse(TK_IgnoreUnlessSpelledInSource,
+ cxxRewrittenBinaryOperator(
+ hasOperatorName("!="), hasAnyOperatorName("<", "!="),
+ isComparisonOperator(),
+ hasLHS(declRefExpr(to(varDecl(hasName("s1"))))),
+ hasRHS(declRefExpr(to(varDecl(hasName("s2"))))),
+ hasEitherOperand(declRefExpr(to(varDecl(hasName("s2"))))),
+ hasOperands(declRefExpr(to(varDecl(hasName("s1")))),
+ declRefExpr(to(varDecl(hasName("s2"))))))),
+ true, {"-std=c++20"}));
+ }
+
+ Code = R"cpp(
+namespace std {
+struct strong_ordering {
+ int n;
+ constexpr operator int() const { return n; }
+ static const strong_ordering equal, greater, less;
+};
+constexpr strong_ordering strong_ordering::equal = {0};
+constexpr strong_ordering strong_ordering::greater = {1};
+constexpr strong_ordering strong_ordering::less = {-1};
+}
+
+struct HasSpaceshipMem {
+ int a;
+ constexpr auto operator<=>(const HasSpaceshipMem&) const = default;
+};
+
+void binop()
+{
+ HasSpaceshipMem hs1, hs2;
+ if (hs1 == hs2)
+ return;
+
+ HasSpaceshipMem hs3, hs4;
+ if (hs3 != hs4)
+ return;
+
+ HasSpaceshipMem hs5, hs6;
+ if (hs5 < hs6)
+ return;
+
+ HasSpaceshipMem hs7, hs8;
+ if (hs7 > hs8)
+ return;
+
+ HasSpaceshipMem hs9, hs10;
+ if (hs9 <= hs10)
+ return;
+
+ HasSpaceshipMem hs11, hs12;
+ if (hs11 >= hs12)
+ return;
+}
+)cpp";
+ auto withArgs = [](StringRef lName, StringRef rName) {
+ return cxxOperatorCallExpr(
+ hasArgument(0, declRefExpr(to(varDecl(hasName(lName))))),
+ hasArgument(1, declRefExpr(to(varDecl(hasName(rName))))));
+ };
+ {
+ auto M = ifStmt(hasCondition(cxxOperatorCallExpr(
+ hasOverloadedOperatorName("=="), withArgs("hs1", "hs2"))));
+ EXPECT_TRUE(
+ matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"}));
+ EXPECT_TRUE(
+ matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
+ true, {"-std=c++20"}));
+ }
+ {
+ auto M =
+ unaryOperator(hasOperatorName("!"),
+ has(cxxOperatorCallExpr(hasOverloadedOperatorName("=="),
+ withArgs("hs3", "hs4"))));
+ EXPECT_TRUE(
+ matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"}));
+ EXPECT_FALSE(
+ matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
+ true, {"-std=c++20"}));
+ }
+ {
+ auto M =
+ unaryOperator(hasOperatorName("!"),
+ has(cxxOperatorCallExpr(hasOverloadedOperatorName("=="),
+ withArgs("hs3", "hs4"))));
+ EXPECT_TRUE(
+ matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"}));
+ EXPECT_FALSE(
+ matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
+ true, {"-std=c++20"}));
+ }
+ {
+ auto M = binaryOperator(
+ hasOperatorName("<"),
+ hasLHS(hasDescendant(cxxOperatorCallExpr(
+ hasOverloadedOperatorName("<=>"), withArgs("hs5", "hs6")))),
+ hasRHS(integerLiteral(equals(0))));
+ EXPECT_TRUE(
+ matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"}));
+ EXPECT_FALSE(
+ matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
+ true, {"-std=c++20"}));
+ }
+ {
+ auto M = cxxRewrittenBinaryOperator(withDescendants("hs3", "hs4"));
+ EXPECT_TRUE(
+ matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"}));
+ EXPECT_TRUE(
+ matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
+ true, {"-std=c++20"}));
+ }
+ {
+ auto M = declRefExpr(to(varDecl(hasName("hs3"))));
+ EXPECT_TRUE(
+ matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"}));
+ EXPECT_TRUE(
+ matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
+ true, {"-std=c++20"}));
+ }
+ {
+ auto M = cxxRewrittenBinaryOperator(has(
+ unaryOperator(hasOperatorName("!"), withDescendants("hs3", "hs4"))));
+ EXPECT_TRUE(
+ matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"}));
+ EXPECT_FALSE(
+ matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
+ true, {"-std=c++20"}));
+ }
+ {
+ auto M = cxxRewrittenBinaryOperator(
+ has(declRefExpr(to(varDecl(hasName("hs3"))))),
+ has(declRefExpr(to(varDecl(hasName("hs4"))))));
+ EXPECT_FALSE(
+ matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"}));
+ EXPECT_TRUE(
+ matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
+ true, {"-std=c++20"}));
+ }
+ {
+ EXPECT_TRUE(matchesConditionally(
+ Code,
+ traverse(TK_AsIs,
+ cxxRewrittenBinaryOperator(
+ hasOperatorName("!="), hasAnyOperatorName("<", "!="),
+ isComparisonOperator(),
+ hasLHS(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("hs3")))))),
+ hasRHS(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("hs4")))))),
+ hasEitherOperand(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("hs3")))))),
+ hasOperands(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("hs3"))))),
+ ignoringImplicit(declRefExpr(
+ to(varDecl(hasName("hs4")))))))),
+ true, {"-std=c++20"}));
+ EXPECT_TRUE(matchesConditionally(
+ Code,
+ traverse(TK_IgnoreUnlessSpelledInSource,
+ cxxRewrittenBinaryOperator(
+ hasOperatorName("!="), hasAnyOperatorName("<", "!="),
+ isComparisonOperator(),
+ hasLHS(declRefExpr(to(varDecl(hasName("hs3"))))),
+ hasRHS(declRefExpr(to(varDecl(hasName("hs4"))))),
+ hasEitherOperand(declRefExpr(to(varDecl(hasName("hs3"))))),
+ hasOperands(declRefExpr(to(varDecl(hasName("hs3")))),
+ declRefExpr(to(varDecl(hasName("hs4"))))))),
+ true, {"-std=c++20"}));
+ }
+ {
+ EXPECT_TRUE(matchesConditionally(
+ Code,
+ traverse(TK_AsIs,
+ cxxRewrittenBinaryOperator(
+ hasOperatorName("<"), hasAnyOperatorName("<", "!="),
+ isComparisonOperator(),
+ hasLHS(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("hs5")))))),
+ hasRHS(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("hs6")))))),
+ hasEitherOperand(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("hs5")))))),
+ hasOperands(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("hs5"))))),
+ ignoringImplicit(declRefExpr(
+ to(varDecl(hasName("hs6")))))))),
+ true, {"-std=c++20"}));
+ EXPECT_TRUE(matchesConditionally(
+ Code,
+ traverse(TK_IgnoreUnlessSpelledInSource,
+ cxxRewrittenBinaryOperator(
+ hasOperatorName("<"), hasAnyOperatorName("<", "!="),
+ isComparisonOperator(),
+ hasLHS(declRefExpr(to(varDecl(hasName("hs5"))))),
+ hasRHS(declRefExpr(to(varDecl(hasName("hs6"))))),
+ hasEitherOperand(declRefExpr(to(varDecl(hasName("hs5"))))),
+ hasOperands(declRefExpr(to(varDecl(hasName("hs5")))),
+ declRefExpr(to(varDecl(hasName("hs6"))))))),
+ true, {"-std=c++20"}));
+ }
+ {
+ EXPECT_TRUE(matchesConditionally(
+ Code,
+ traverse(TK_AsIs,
+ cxxRewrittenBinaryOperator(
+ hasOperatorName(">"), hasAnyOperatorName("<", ">"),
+ isComparisonOperator(),
+ hasLHS(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("hs7")))))),
+ hasRHS(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("hs8")))))),
+ hasEitherOperand(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("hs7")))))),
+ hasOperands(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("hs7"))))),
+ ignoringImplicit(declRefExpr(
+ to(varDecl(hasName("hs8")))))))),
+ true, {"-std=c++20"}));
+ EXPECT_TRUE(matchesConditionally(
+ Code,
+ traverse(TK_IgnoreUnlessSpelledInSource,
+ cxxRewrittenBinaryOperator(
+ hasOperatorName(">"), hasAnyOperatorName("<", ">"),
+ isComparisonOperator(),
+ hasLHS(declRefExpr(to(varDecl(hasName("hs7"))))),
+ hasRHS(declRefExpr(to(varDecl(hasName("hs8"))))),
+ hasEitherOperand(declRefExpr(to(varDecl(hasName("hs7"))))),
+ hasOperands(declRefExpr(to(varDecl(hasName("hs7")))),
+ declRefExpr(to(varDecl(hasName("hs8"))))))),
+ true, {"-std=c++20"}));
+ }
+ {
+ EXPECT_TRUE(matchesConditionally(
+ Code,
+ traverse(TK_AsIs,
+ cxxRewrittenBinaryOperator(
+ hasOperatorName("<="), hasAnyOperatorName("<", "<="),
+ isComparisonOperator(),
+ hasLHS(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("hs9")))))),
+ hasRHS(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("hs10")))))),
+ hasEitherOperand(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("hs9")))))),
+ hasOperands(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("hs9"))))),
+ ignoringImplicit(declRefExpr(
+ to(varDecl(hasName("hs10")))))))),
+ true, {"-std=c++20"}));
+ EXPECT_TRUE(matchesConditionally(
+ Code,
+ traverse(TK_IgnoreUnlessSpelledInSource,
+ cxxRewrittenBinaryOperator(
+ hasOperatorName("<="), hasAnyOperatorName("<", "<="),
+ isComparisonOperator(),
+ hasLHS(declRefExpr(to(varDecl(hasName("hs9"))))),
+ hasRHS(declRefExpr(to(varDecl(hasName("hs10"))))),
+ hasEitherOperand(declRefExpr(to(varDecl(hasName("hs9"))))),
+ hasOperands(declRefExpr(to(varDecl(hasName("hs9")))),
+ declRefExpr(to(varDecl(hasName("hs10"))))))),
+ true, {"-std=c++20"}));
+ }
+ {
+ EXPECT_TRUE(matchesConditionally(
+ Code,
+ traverse(TK_AsIs,
+ cxxRewrittenBinaryOperator(
+ hasOperatorName(">="), hasAnyOperatorName("<", ">="),
+ isComparisonOperator(),
+ hasLHS(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("hs11")))))),
+ hasRHS(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("hs12")))))),
+ hasEitherOperand(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("hs11")))))),
+ hasOperands(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("hs11"))))),
+ ignoringImplicit(declRefExpr(
+ to(varDecl(hasName("hs12")))))))),
+ true, {"-std=c++20"}));
+ EXPECT_TRUE(matchesConditionally(
+ Code,
+ traverse(
+ TK_IgnoreUnlessSpelledInSource,
+ cxxRewrittenBinaryOperator(
+ hasOperatorName(">="), hasAnyOperatorName("<", ">="),
+ isComparisonOperator(),
+ hasLHS(declRefExpr(to(varDecl(hasName("hs11"))))),
+ hasRHS(declRefExpr(to(varDecl(hasName("hs12"))))),
+ hasEitherOperand(declRefExpr(to(varDecl(hasName("hs11"))))),
+ hasOperands(declRefExpr(to(varDecl(hasName("hs11")))),
+ declRefExpr(to(varDecl(hasName("hs12"))))))),
+ true, {"-std=c++20"}));
+ }
+
+ Code = R"cpp(
+struct S {};
+
+struct HasOpEq
+{
+ bool operator==(const S& other) const
+ {
+ return true;
+ }
+};
+
+struct HasOpEqMem {
+ bool operator==(const HasOpEqMem&) const { return true; }
+};
+
+struct HasOpEqFree {
+};
+bool operator==(const HasOpEqFree&, const HasOpEqFree&) { return true; }
+
+void binop()
+{
+ {
+ HasOpEq s1;
+ S s2;
+ if (s1 != s2)
+ return;
+ }
+
+ {
+ int i1;
+ int i2;
+ if (i1 != i2)
+ return;
+ }
+
+ {
+ HasOpEqMem M1;
+ HasOpEqMem M2;
+ if (M1 == M2)
+ return;
+ }
+
+ {
+ HasOpEqFree F1;
+ HasOpEqFree F2;
+ if (F1 == F2)
+ return;
+ }
+}
+)cpp";
+ {
+ EXPECT_TRUE(matchesConditionally(
+ Code,
+ traverse(TK_AsIs,
+ binaryOperation(
+ hasOperatorName("!="), hasAnyOperatorName("<", "!="),
+ isComparisonOperator(),
+ hasLHS(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("s1")))))),
+ hasRHS(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("s2")))))),
+ hasEitherOperand(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("s2")))))),
+ hasOperands(ignoringImplicit(
+ declRefExpr(to(varDecl(hasName("s1"))))),
+ ignoringImplicit(declRefExpr(
+ to(varDecl(hasName("s2")))))))),
+ true, {"-std=c++20"}));
+ EXPECT_TRUE(matchesConditionally(
+ Code,
+ traverse(TK_AsIs, binaryOperation(hasOperatorName("!="),
+ hasLHS(ignoringImplicit(declRefExpr(
+ to(varDecl(hasName("i1")))))),
+ hasRHS(ignoringImplicit(declRefExpr(
+ to(varDecl(hasName("i2")))))))),
+ true, {"-std=c++20"}));
+ EXPECT_TRUE(matchesConditionally(
+ Code,
+ traverse(TK_AsIs, binaryOperation(hasOperatorName("=="),
+ hasLHS(ignoringImplicit(declRefExpr(
+ to(varDecl(hasName("M1")))))),
+ hasRHS(ignoringImplicit(declRefExpr(
+ to(varDecl(hasName("M2")))))))),
+ true, {"-std=c++20"}));
+ EXPECT_TRUE(matchesConditionally(
+ Code,
+ traverse(TK_AsIs, binaryOperation(hasOperatorName("=="),
+ hasLHS(ignoringImplicit(declRefExpr(
+ to(varDecl(hasName("F1")))))),
+ hasRHS(ignoringImplicit(declRefExpr(
+ to(varDecl(hasName("F2")))))))),
+ true, {"-std=c++20"}));
+ EXPECT_TRUE(matchesConditionally(
+ Code,
+ traverse(TK_IgnoreUnlessSpelledInSource,
+ binaryOperation(
+ hasOperatorName("!="), hasAnyOperatorName("<", "!="),
+ isComparisonOperator(),
+ hasLHS(declRefExpr(to(varDecl(hasName("s1"))))),
+ hasRHS(declRefExpr(to(varDecl(hasName("s2"))))),
+ hasEitherOperand(declRefExpr(to(varDecl(hasName("s2"))))),
+ hasOperands(declRefExpr(to(varDecl(hasName("s1")))),
+ declRefExpr(to(varDecl(hasName("s2"))))))),
+ true, {"-std=c++20"}));
+ EXPECT_TRUE(matchesConditionally(
+ Code,
+ traverse(
+ TK_IgnoreUnlessSpelledInSource,
+ binaryOperation(hasOperatorName("!="),
+ hasLHS(declRefExpr(to(varDecl(hasName("i1"))))),
+ hasRHS(declRefExpr(to(varDecl(hasName("i2"))))))),
+ true, {"-std=c++20"}));
+ EXPECT_TRUE(matchesConditionally(
+ Code,
+ traverse(
+ TK_IgnoreUnlessSpelledInSource,
+ binaryOperation(hasOperatorName("=="),
+ hasLHS(declRefExpr(to(varDecl(hasName("M1"))))),
+ hasRHS(declRefExpr(to(varDecl(hasName("M2"))))))),
+ true, {"-std=c++20"}));
+ EXPECT_TRUE(matchesConditionally(
+ Code,
+ traverse(
+ TK_IgnoreUnlessSpelledInSource,
+ binaryOperation(hasOperatorName("=="),
+ hasLHS(declRefExpr(to(varDecl(hasName("F1"))))),
+ hasRHS(declRefExpr(to(varDecl(hasName("F2"))))))),
+ true, {"-std=c++20"}));
+ }
}
TEST(IgnoringImpCasts, MatchesImpCasts) {
More information about the cfe-commits
mailing list