[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> </td>
</tr> </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> </table>
<!-- ======================================================================= --> <!-- ======================================================================= -->
@ -1523,6 +1559,25 @@ Example matches reinterpret_cast&lt;char*&gt;(&amp;p) in
</pre></td></tr> </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>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. <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> </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>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 <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). 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> </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>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="hasAnyOperatorName2"><pre>Matches operator expressions (binary or unary) that have any of the <tr><td colspan="4" class="doc" id="hasAnyOperatorName3"><pre>Matches operator expressions (binary or unary) that have any of the
specified names. specified names.
hasAnyOperatorName("+", "-") hasAnyOperatorName("+", "-")
@ -5184,8 +5286,8 @@ specified names.
</pre></td></tr> </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>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="hasOperatorName2"><pre>Matches the operator Name of operator expressions (binary or <tr><td colspan="4" class="doc" id="hasOperatorName3"><pre>Matches the operator Name of operator expressions (binary or
unary). unary).
Example matches a || b (matcher = binaryOperator(hasOperatorName("||"))) Example matches a || b (matcher = binaryOperator(hasOperatorName("||")))
@ -5446,13 +5548,16 @@ match expressions.</p>
The code The code
var1 != var2; var1 != var2;
might be represented in the clang AST as a binaryOperator or a might be represented in the clang AST as a binaryOperator, a
cxxOperatorCallExpr, depending on cxxOperatorCallExpr or a cxxRewrittenBinaryOperator, depending on
* whether the types of var1 and var2 are fundamental (binaryOperator) or at * whether the types of var1 and var2 are fundamental (binaryOperator) or at
least one is a class type (cxxOperatorCallExpr) least one is a class type (cxxOperatorCallExpr)
* whether the code appears in a template declaration, if at least one of the * whether the code appears in a template declaration, if at least one of the
vars is a dependent-type (binaryOperator) 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 This matcher elides details in places where the matchers for the nodes are
compatible. compatible.
@ -5480,6 +5585,31 @@ matches each use of "!=" in:
1 != 2; 1 != 2;
T() != S(); 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> </pre></td></tr>
@ -5753,16 +5883,16 @@ arraySubscriptExpression(hasIndex(integerLiteral()))
</pre></td></tr> </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>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="hasLHS2"><pre>Matches the left hand side of binary operator expressions. <tr><td colspan="4" class="doc" id="hasLHS3"><pre>Matches the left hand side of binary operator expressions.
Example matches a (matcher = binaryOperator(hasLHS())) Example matches a (matcher = binaryOperator(hasLHS()))
a || b a || b
</pre></td></tr> </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>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="hasRHS2"><pre>Matches the right hand side of binary operator expressions. <tr><td colspan="4" class="doc" id="hasRHS3"><pre>Matches the right hand side of binary operator expressions.
Example matches b (matcher = binaryOperator(hasRHS())) Example matches b (matcher = binaryOperator(hasRHS()))
a || b a || b
@ -6480,6 +6610,40 @@ match Base.
</pre></td></tr> </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>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 <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. expression, or an ObjC-message-send expression.

View File

@ -145,7 +145,8 @@ public:
return; return;
if (Traversal == TK_IgnoreUnlessSpelledInSource && if (Traversal == TK_IgnoreUnlessSpelledInSource &&
isa<LambdaExpr, CXXForRangeStmt, CallExpr>(S)) isa<LambdaExpr, CXXForRangeStmt, CallExpr,
CXXRewrittenBinaryOperator>(S))
return; return;
for (const Stmt *SubStmt : S->children()) 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) { void VisitExpressionTemplateArgument(const TemplateArgument &TA) {
Visit(TA.getAsExpr()); Visit(TA.getAsExpr());
} }

View File

@ -320,6 +320,16 @@ public:
bool isReversed() const { return CXXRewrittenBinaryOperatorBits.IsReversed; } bool isReversed() const { return CXXRewrittenBinaryOperatorBits.IsReversed; }
BinaryOperatorKind getOperator() const { return getDecomposedForm().Opcode; } 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 *getLHS() const { return getDecomposedForm().LHS; }
const Expr *getRHS() const { return getDecomposedForm().RHS; } 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> extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXOperatorCallExpr>
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. /// Matches expressions.
/// ///
/// Example matches x() /// Example matches x()
@ -2738,13 +2759,16 @@ auto mapAnyOf(internal::VariadicDynCastAllOfMatcher<T, U> const &...) {
/// \code /// \code
/// var1 != var2; /// var1 != var2;
/// \endcode /// \endcode
/// might be represented in the clang AST as a binaryOperator or a /// might be represented in the clang AST as a binaryOperator, a
/// cxxOperatorCallExpr, depending on /// cxxOperatorCallExpr or a cxxRewrittenBinaryOperator, depending on
/// ///
/// * whether the types of var1 and var2 are fundamental (binaryOperator) or at /// * whether the types of var1 and var2 are fundamental (binaryOperator) or at
/// least one is a class type (cxxOperatorCallExpr) /// least one is a class type (cxxOperatorCallExpr)
/// * whether the code appears in a template declaration, if at least one of the /// * whether the code appears in a template declaration, if at least one of the
/// vars is a dependent-type (binaryOperator) /// 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 /// This matcher elides details in places where the matchers for the nodes are
/// compatible. /// compatible.
@ -2775,8 +2799,34 @@ auto mapAnyOf(internal::VariadicDynCastAllOfMatcher<T, U> const &...) {
/// 1 != 2; /// 1 != 2;
/// T() != S(); /// 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 /// \endcode
extern const internal::MapAnyOfMatcher<BinaryOperator, CXXOperatorCallExpr> extern const internal::MapAnyOfMatcher<BinaryOperator, CXXOperatorCallExpr,
CXXRewrittenBinaryOperator>
binaryOperation; binaryOperation;
/// Matches unary expressions that have a specific type of argument. /// Matches unary expressions that have a specific type of argument.
@ -5245,11 +5295,11 @@ AST_POLYMORPHIC_MATCHER_P_OVERLOAD(equals,
/// \code /// \code
/// !(a || b) /// !(a || b)
/// \endcode /// \endcode
AST_POLYMORPHIC_MATCHER_P(hasOperatorName, AST_POLYMORPHIC_MATCHER_P(
AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, hasOperatorName,
CXXOperatorCallExpr, AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
UnaryOperator), CXXRewrittenBinaryOperator, UnaryOperator),
std::string, Name) { std::string, Name) {
if (Optional<StringRef> OpName = internal::getOpName(Node)) if (Optional<StringRef> OpName = internal::getOpName(Node))
return *OpName == Name; return *OpName == Name;
return false; return false;
@ -5265,6 +5315,7 @@ extern const internal::VariadicFunction<
internal::PolymorphicMatcherWithParam1< internal::PolymorphicMatcherWithParam1<
internal::HasAnyOperatorNameMatcher, std::vector<std::string>, internal::HasAnyOperatorNameMatcher, std::vector<std::string>,
AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr, AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
CXXRewrittenBinaryOperator,
UnaryOperator)>, UnaryOperator)>,
StringRef, internal::hasAnyOperatorNameFunc> StringRef, internal::hasAnyOperatorNameFunc>
hasAnyOperatorName; hasAnyOperatorName;
@ -5283,9 +5334,10 @@ extern const internal::VariadicFunction<
/// struct S { S& operator=(const S&); }; /// struct S { S& operator=(const S&); };
/// void x() { S s1, s2; s1 = s2; } /// void x() { S s1, s2; s1 = s2; }
/// \endcode /// \endcode
AST_POLYMORPHIC_MATCHER(isAssignmentOperator, AST_POLYMORPHIC_MATCHER(
AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, isAssignmentOperator,
CXXOperatorCallExpr)) { AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
CXXRewrittenBinaryOperator)) {
return Node.isAssignmentOp(); return Node.isAssignmentOp();
} }
@ -5303,9 +5355,10 @@ AST_POLYMORPHIC_MATCHER(isAssignmentOperator,
/// struct S { bool operator<(const S& other); }; /// struct S { bool operator<(const S& other); };
/// void x(S s1, S s2) { bool b1 = s1 < s2; } /// void x(S s1, S s2) { bool b1 = s1 < s2; }
/// \endcode /// \endcode
AST_POLYMORPHIC_MATCHER(isComparisonOperator, AST_POLYMORPHIC_MATCHER(
AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, isComparisonOperator,
CXXOperatorCallExpr)) { AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
CXXRewrittenBinaryOperator)) {
return Node.isComparisonOp(); return Node.isComparisonOp();
} }
@ -5316,9 +5369,9 @@ AST_POLYMORPHIC_MATCHER(isComparisonOperator,
/// a || b /// a || b
/// \endcode /// \endcode
AST_POLYMORPHIC_MATCHER_P(hasLHS, AST_POLYMORPHIC_MATCHER_P(hasLHS,
AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, AST_POLYMORPHIC_SUPPORTED_TYPES(
CXXOperatorCallExpr, BinaryOperator, CXXOperatorCallExpr,
ArraySubscriptExpr), CXXRewrittenBinaryOperator, ArraySubscriptExpr),
internal::Matcher<Expr>, InnerMatcher) { internal::Matcher<Expr>, InnerMatcher) {
const Expr *LeftHandSide = internal::getLHS(Node); const Expr *LeftHandSide = internal::getLHS(Node);
return (LeftHandSide != nullptr && return (LeftHandSide != nullptr &&
@ -5332,9 +5385,9 @@ AST_POLYMORPHIC_MATCHER_P(hasLHS,
/// a || b /// a || b
/// \endcode /// \endcode
AST_POLYMORPHIC_MATCHER_P(hasRHS, AST_POLYMORPHIC_MATCHER_P(hasRHS,
AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, AST_POLYMORPHIC_SUPPORTED_TYPES(
CXXOperatorCallExpr, BinaryOperator, CXXOperatorCallExpr,
ArraySubscriptExpr), CXXRewrittenBinaryOperator, ArraySubscriptExpr),
internal::Matcher<Expr>, InnerMatcher) { internal::Matcher<Expr>, InnerMatcher) {
const Expr *RightHandSide = internal::getRHS(Node); const Expr *RightHandSide = internal::getRHS(Node);
return (RightHandSide != nullptr && 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 /// Matches if either the left hand side or the right hand side of a
/// binary operator matches. /// binary operator matches.
AST_POLYMORPHIC_MATCHER_P(hasEitherOperand, AST_POLYMORPHIC_MATCHER_P(
AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, hasEitherOperand,
CXXOperatorCallExpr), AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
internal::Matcher<Expr>, InnerMatcher) { CXXRewrittenBinaryOperator),
internal::Matcher<Expr>, InnerMatcher) {
return internal::VariadicDynCastAllOfMatcher<Stmt, NodeType>()( return internal::VariadicDynCastAllOfMatcher<Stmt, NodeType>()(
anyOf(hasLHS(InnerMatcher), hasRHS(InnerMatcher))) anyOf(hasLHS(InnerMatcher), hasRHS(InnerMatcher)))
.matches(Node, Finder, Builder); .matches(Node, Finder, Builder);
@ -5362,11 +5416,11 @@ AST_POLYMORPHIC_MATCHER_P(hasEitherOperand,
/// 1 + 1 // No match /// 1 + 1 // No match
/// 2 + 2 // No match /// 2 + 2 // No match
/// \endcode /// \endcode
AST_POLYMORPHIC_MATCHER_P2(hasOperands, AST_POLYMORPHIC_MATCHER_P2(
AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, hasOperands,
CXXOperatorCallExpr), AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
internal::Matcher<Expr>, Matcher1, CXXRewrittenBinaryOperator),
internal::Matcher<Expr>, Matcher2) { internal::Matcher<Expr>, Matcher1, internal::Matcher<Expr>, Matcher2) {
return internal::VariadicDynCastAllOfMatcher<Stmt, NodeType>()( return internal::VariadicDynCastAllOfMatcher<Stmt, NodeType>()(
anyOf(allOf(hasLHS(Matcher1), hasRHS(Matcher2)), anyOf(allOf(hasLHS(Matcher1), hasRHS(Matcher2)),
allOf(hasLHS(Matcher2), hasRHS(Matcher1)))) 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) { inline Optional<StringRef> getOpName(const BinaryOperator &Node) {
return Node.getOpcodeStr(); return Node.getOpcodeStr();
} }
inline StringRef getOpName(const CXXRewrittenBinaryOperator &Node) {
return Node.getOpcodeStr();
}
inline Optional<StringRef> getOpName(const CXXOperatorCallExpr &Node) { inline Optional<StringRef> getOpName(const CXXOperatorCallExpr &Node) {
auto optBinaryOpcode = equivalentBinaryOperator(Node); auto optBinaryOpcode = equivalentBinaryOperator(Node);
if (!optBinaryOpcode) { if (!optBinaryOpcode) {
@ -2108,9 +2111,10 @@ template <typename T, typename ArgT = std::vector<std::string>>
class HasAnyOperatorNameMatcher : public SingleNodeMatcherInterface<T> { class HasAnyOperatorNameMatcher : public SingleNodeMatcherInterface<T> {
static_assert(std::is_same<T, BinaryOperator>::value || static_assert(std::is_same<T, BinaryOperator>::value ||
std::is_same<T, CXXOperatorCallExpr>::value || std::is_same<T, CXXOperatorCallExpr>::value ||
std::is_same<T, CXXRewrittenBinaryOperator>::value ||
std::is_same<T, UnaryOperator>::value, std::is_same<T, UnaryOperator>::value,
"Matcher only supports `BinaryOperator`, `UnaryOperator` and " "Matcher only supports `BinaryOperator`, `UnaryOperator`, "
"`CXXOperatorCallExpr`"); "`CXXOperatorCallExpr` and `CXXRewrittenBinaryOperator`");
static_assert(std::is_same<ArgT, std::vector<std::string>>::value, static_assert(std::is_same<ArgT, std::vector<std::string>>::value,
"Matcher ArgT must be std::vector<std::string>"); "Matcher ArgT must be std::vector<std::string>");
@ -2128,12 +2132,33 @@ public:
} }
private: 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; const std::vector<std::string> Names;
}; };
using HasOpNameMatcher = PolymorphicMatcherWithParam1< using HasOpNameMatcher = PolymorphicMatcherWithParam1<
HasAnyOperatorNameMatcher, std::vector<std::string>, HasAnyOperatorNameMatcher, std::vector<std::string>,
void(TypeList<BinaryOperator, CXXOperatorCallExpr, UnaryOperator>)>; void(TypeList<BinaryOperator, CXXOperatorCallExpr,
CXXRewrittenBinaryOperator, UnaryOperator>)>;
HasOpNameMatcher hasAnyOperatorNameFunc(ArrayRef<const StringRef *> NameRefs); HasOpNameMatcher hasAnyOperatorNameFunc(ArrayRef<const StringRef *> NameRefs);

View File

@ -250,6 +250,18 @@ public:
return false; return false;
return VisitorBase::TraverseStmt(Node->getBody()); 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) { bool TraverseLambdaExpr(LambdaExpr *Node) {
if (!Finder->isTraversalIgnoringImplicitNodes()) if (!Finder->isTraversalIgnoringImplicitNodes())
return VisitorBase::TraverseLambdaExpr(Node); return VisitorBase::TraverseLambdaExpr(Node);
@ -489,6 +501,19 @@ public:
} }
} }
return true; 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)) { } else if (auto *LE = dyn_cast<LambdaExpr>(S)) {
for (auto I : llvm::zip(LE->captures(), LE->capture_inits())) { for (auto I : llvm::zip(LE->captures(), LE->capture_inits())) {
auto C = std::get<0>(I); auto C = std::get<0>(I);

View File

@ -868,6 +868,8 @@ const internal::VariadicDynCastAllOfMatcher<Stmt, CXXDefaultArgExpr>
cxxDefaultArgExpr; cxxDefaultArgExpr;
const internal::VariadicDynCastAllOfMatcher<Stmt, CXXOperatorCallExpr> const internal::VariadicDynCastAllOfMatcher<Stmt, CXXOperatorCallExpr>
cxxOperatorCallExpr; cxxOperatorCallExpr;
const internal::VariadicDynCastAllOfMatcher<Stmt, CXXRewrittenBinaryOperator>
cxxRewrittenBinaryOperator;
const internal::VariadicDynCastAllOfMatcher<Stmt, Expr> expr; const internal::VariadicDynCastAllOfMatcher<Stmt, Expr> expr;
const internal::VariadicDynCastAllOfMatcher<Stmt, DeclRefExpr> declRefExpr; const internal::VariadicDynCastAllOfMatcher<Stmt, DeclRefExpr> declRefExpr;
const internal::VariadicDynCastAllOfMatcher<Stmt, ObjCIvarRefExpr> objcIvarRefExpr; 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, StmtExpr> stmtExpr;
const internal::VariadicDynCastAllOfMatcher<Stmt, BinaryOperator> const internal::VariadicDynCastAllOfMatcher<Stmt, BinaryOperator>
binaryOperator; binaryOperator;
const internal::MapAnyOfMatcher<BinaryOperator, CXXOperatorCallExpr> const internal::MapAnyOfMatcher<BinaryOperator, CXXOperatorCallExpr,
CXXRewrittenBinaryOperator>
binaryOperation; binaryOperation;
const internal::VariadicDynCastAllOfMatcher<Stmt, UnaryOperator> unaryOperator; const internal::VariadicDynCastAllOfMatcher<Stmt, UnaryOperator> unaryOperator;
const internal::VariadicDynCastAllOfMatcher<Stmt, ConditionalOperator> const internal::VariadicDynCastAllOfMatcher<Stmt, ConditionalOperator>

View File

@ -192,6 +192,7 @@ RegistryMaps::RegistryMaps() {
REGISTER_MATCHER(cxxOperatorCallExpr); REGISTER_MATCHER(cxxOperatorCallExpr);
REGISTER_MATCHER(cxxRecordDecl); REGISTER_MATCHER(cxxRecordDecl);
REGISTER_MATCHER(cxxReinterpretCastExpr); REGISTER_MATCHER(cxxReinterpretCastExpr);
REGISTER_MATCHER(cxxRewrittenBinaryOperator);
REGISTER_MATCHER(cxxStaticCastExpr); REGISTER_MATCHER(cxxStaticCastExpr);
REGISTER_MATCHER(cxxStdInitializerListExpr); REGISTER_MATCHER(cxxStdInitializerListExpr);
REGISTER_MATCHER(cxxTemporaryObjectExpr); 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 } // namespace clang

View File

@ -714,6 +714,65 @@ void templ()
hasLHS(declRefExpr(to(varDecl(hasName("s1"))))), hasLHS(declRefExpr(to(varDecl(hasName("s1"))))),
hasRHS(declRefExpr(to(varDecl(hasName("s2"))))))))); 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( Code = R"cpp(
struct HasOpBangMem struct HasOpBangMem
{ {

View File

@ -3330,6 +3330,527 @@ void foo() {
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, lambdaImplicitCapture))); EXPECT_TRUE(matches(Code, traverse(TK_AsIs, lambdaImplicitCapture)));
EXPECT_FALSE(matches( EXPECT_FALSE(matches(
Code, traverse(TK_IgnoreUnlessSpelledInSource, lambdaImplicitCapture))); 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) { TEST(IgnoringImpCasts, MatchesImpCasts) {