[ASTMatchers] Add support for CXXRewrittenBinaryOperator

Differential Revision: https://reviews.llvm.org/D94130
This commit is contained in:
Stephen Kelly 2021-01-05 23:04:31 +00:00
parent e810e95e4b
commit b765eaf9a6
11 changed files with 976 additions and 44 deletions

View File

@ -498,6 +498,42 @@ void func() {
</td>
</tr>
<tr>
<td>Rewritten binary operators
<pre>
binaryOperator(
hasOperatorName("&lt;"),
hasRHS(integerLiteral(equals(0)))
)
</pre>
given:
<pre>
#include &lt;compare&gt;
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 @@ Example matches reinterpret_cast&lt;char*&gt;(&amp;p) in
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>&gt;</td><td class="name" onclick="toggle('cxxRewrittenBinaryOperator0')"><a name="cxxRewrittenBinaryOperator0Anchor">cxxRewrittenBinaryOperator</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRewrittenBinaryOperator.html">CXXRewrittenBinaryOperator</a>&gt;...</td></tr>
<tr><td colspan="4" class="doc" id="cxxRewrittenBinaryOperator0"><pre>Matches rewritten binary operators
Example matches use of "&lt;":
#include &lt;compare&gt;
struct HasSpaceshipMem {
int a;
constexpr auto operator&lt;=&gt;(const HasSpaceshipMem&amp;) const = default;
};
void compare() {
HasSpaceshipMem hs1, hs2;
if (hs1 &lt; hs2)
return;
}
See also the binaryOperation() matcher for more-general matching
of this AST node.
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>&gt;</td><td class="name" onclick="toggle('cxxStaticCastExpr0')"><a name="cxxStaticCastExpr0Anchor">cxxStaticCastExpr</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXStaticCastExpr.html">CXXStaticCastExpr</a>&gt;...</td></tr>
<tr><td colspan="4" class="doc" id="cxxStaticCastExpr0"><pre>Matches a C++ static_cast expression.
@ -3405,6 +3460,53 @@ Usable as: Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Func
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRewrittenBinaryOperator.html">CXXRewrittenBinaryOperator</a>&gt;</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&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRewrittenBinaryOperator.html">CXXRewrittenBinaryOperator</a>&gt;</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&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRewrittenBinaryOperator.html">CXXRewrittenBinaryOperator</a>&gt;</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&amp; operator=(const S&amp;); };
void x() { S s1, s2; s1 = s2; }
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRewrittenBinaryOperator.html">CXXRewrittenBinaryOperator</a>&gt;</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 &lt; s2
(matcher = cxxOperatorCallExpr(isComparisonOperator()))
struct S { bool operator&lt;(const S&amp; other); };
void x(S s1, S s2) { bool b1 = s1 &lt; s2; }
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXUnresolvedConstructExpr.html">CXXUnresolvedConstructExpr</a>&gt;</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 @@ should be passed as a quoted string. e.g., ofKind("UETT_SizeOf").
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1UnaryOperator.html">UnaryOperator</a>&gt;</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&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1UnaryOperator.html">UnaryOperator</a>&gt;</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 @@ specified names.
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1UnaryOperator.html">UnaryOperator</a>&gt;</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&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1UnaryOperator.html">UnaryOperator</a>&gt;</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 @@ match expressions.</p>
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 @@ matches each use of "!=" in:
1 != 2;
T() != S();
}
struct HasOpEq
{
bool operator==(const HasOpEq &amp;) const;
};
void inverse()
{
HasOpEq s1;
HasOpEq s2;
if (s1 != s2)
return;
}
struct HasSpaceship
{
bool operator&lt;=&gt;(const HasOpEq &amp;) const;
};
void use_spaceship()
{
HasSpaceship s1;
HasSpaceship s2;
if (s1 != s2)
return;
}
</pre></td></tr>
@ -5753,16 +5883,16 @@ arraySubscriptExpression(hasIndex(integerLiteral()))
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1ArraySubscriptExpr.html">ArraySubscriptExpr</a>&gt;</td><td class="name" onclick="toggle('hasLHS2')"><a name="hasLHS2Anchor">hasLHS</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt; InnerMatcher</td></tr>
<tr><td colspan="4" class="doc" id="hasLHS2"><pre>Matches the left hand side of binary operator expressions.
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1ArraySubscriptExpr.html">ArraySubscriptExpr</a>&gt;</td><td class="name" onclick="toggle('hasLHS3')"><a name="hasLHS3Anchor">hasLHS</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt; 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&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1ArraySubscriptExpr.html">ArraySubscriptExpr</a>&gt;</td><td class="name" onclick="toggle('hasRHS2')"><a name="hasRHS2Anchor">hasRHS</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt; InnerMatcher</td></tr>
<tr><td colspan="4" class="doc" id="hasRHS2"><pre>Matches the right hand side of binary operator expressions.
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1ArraySubscriptExpr.html">ArraySubscriptExpr</a>&gt;</td><td class="name" onclick="toggle('hasRHS3')"><a name="hasRHS3Anchor">hasRHS</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt; 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 @@ match Base.
</pre></td></tr>
<tr><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRewrittenBinaryOperator.html">CXXRewrittenBinaryOperator</a>&gt;</td><td class="name" onclick="toggle('hasEitherOperand2')"><a name="hasEitherOperand2Anchor">hasEitherOperand</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt; 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&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRewrittenBinaryOperator.html">CXXRewrittenBinaryOperator</a>&gt;</td><td class="name" onclick="toggle('hasLHS2')"><a name="hasLHS2Anchor">hasLHS</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt; 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&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRewrittenBinaryOperator.html">CXXRewrittenBinaryOperator</a>&gt;</td><td class="name" onclick="toggle('hasOperands2')"><a name="hasOperands2Anchor">hasOperands</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt; Matcher1, Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt; 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&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRewrittenBinaryOperator.html">CXXRewrittenBinaryOperator</a>&gt;</td><td class="name" onclick="toggle('hasRHS2')"><a name="hasRHS2Anchor">hasRHS</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt; 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&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXUnresolvedConstructExpr.html">CXXUnresolvedConstructExpr</a>&gt;</td><td class="name" onclick="toggle('hasAnyArgument2')"><a name="hasAnyArgument2Anchor">hasAnyArgument</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt; 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.

View File

@ -145,7 +145,8 @@ public:
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 @@ public:
}
}
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());
}

View File

@ -320,6 +320,16 @@ public:
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; }

View File

@ -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))))

View File

@ -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 @@ public:
}
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);

View File

@ -250,6 +250,18 @@ public:
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 @@ public:
}
}
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);

View File

@ -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>

View File

@ -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);

View File

@ -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

View File

@ -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
{

View File

@ -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) {