</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>
<!-- ======================================================================= -->
</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.
</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).
</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("+", "-")
</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("||")))
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.
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>
</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
</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.
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()
/// \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.
/// 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.
/// \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;
internal::PolymorphicMatcherWithParam1<
internal::HasAnyOperatorNameMatcher, std::vector<std::string>,
AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
+ CXXRewrittenBinaryOperator,
UnaryOperator)>,
StringRef, internal::hasAnyOperatorNameFunc>
hasAnyOperatorName;
/// 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();
}
/// 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();
}
/// 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 &&
/// 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 &&
/// 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);
/// 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))))
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) {