[ASTMatchers] Add support for CXXRewrittenBinaryOperator
Differential Revision: https://reviews.llvm.org/D94130
This commit is contained in:
parent
e810e95e4b
commit
b765eaf9a6
|
@ -498,6 +498,42 @@ void func() {
|
|||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td>Rewritten binary operators
|
||||
<pre>
|
||||
binaryOperator(
|
||||
hasOperatorName("<"),
|
||||
hasRHS(integerLiteral(equals(0)))
|
||||
)
|
||||
</pre>
|
||||
given:
|
||||
<pre>
|
||||
#include <compare>
|
||||
|
||||
class HasSpaceship {
|
||||
public:
|
||||
int x;
|
||||
bool operator==(const HasSpaceship&) const = default;
|
||||
std::strong_ordering operator<=>(const HasSpaceship&) const = default;
|
||||
};
|
||||
|
||||
bool isLess(const HasSpaceship& a, const HasSpaceship& b) {
|
||||
return a < b;
|
||||
}
|
||||
</pre>
|
||||
</td>
|
||||
<td>
|
||||
1 match found.
|
||||
</td>
|
||||
<td>
|
||||
No match found.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- ======================================================================= -->
|
||||
|
@ -1523,6 +1559,25 @@ Example matches reinterpret_cast<char*>(&p) in
|
|||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>></td><td class="name" onclick="toggle('cxxRewrittenBinaryOperator0')"><a name="cxxRewrittenBinaryOperator0Anchor">cxxRewrittenBinaryOperator</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRewrittenBinaryOperator.html">CXXRewrittenBinaryOperator</a>>...</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="cxxRewrittenBinaryOperator0"><pre>Matches rewritten binary operators
|
||||
|
||||
Example matches use of "<":
|
||||
#include <compare>
|
||||
struct HasSpaceshipMem {
|
||||
int a;
|
||||
constexpr auto operator<=>(const HasSpaceshipMem&) const = default;
|
||||
};
|
||||
void compare() {
|
||||
HasSpaceshipMem hs1, hs2;
|
||||
if (hs1 < hs2)
|
||||
return;
|
||||
}
|
||||
See also the binaryOperation() matcher for more-general matching
|
||||
of this AST node.
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>></td><td class="name" onclick="toggle('cxxStaticCastExpr0')"><a name="cxxStaticCastExpr0Anchor">cxxStaticCastExpr</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXStaticCastExpr.html">CXXStaticCastExpr</a>>...</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="cxxStaticCastExpr0"><pre>Matches a C++ static_cast expression.
|
||||
|
||||
|
@ -3405,6 +3460,53 @@ Usable as: Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Func
|
|||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRewrittenBinaryOperator.html">CXXRewrittenBinaryOperator</a>></td><td class="name" onclick="toggle('hasAnyOperatorName2')"><a name="hasAnyOperatorName2Anchor">hasAnyOperatorName</a></td><td>StringRef, ..., StringRef</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="hasAnyOperatorName2"><pre>Matches operator expressions (binary or unary) that have any of the
|
||||
specified names.
|
||||
|
||||
hasAnyOperatorName("+", "-")
|
||||
Is equivalent to
|
||||
anyOf(hasOperatorName("+"), hasOperatorName("-"))
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRewrittenBinaryOperator.html">CXXRewrittenBinaryOperator</a>></td><td class="name" onclick="toggle('hasOperatorName2')"><a name="hasOperatorName2Anchor">hasOperatorName</a></td><td>std::string Name</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="hasOperatorName2"><pre>Matches the operator Name of operator expressions (binary or
|
||||
unary).
|
||||
|
||||
Example matches a || b (matcher = binaryOperator(hasOperatorName("||")))
|
||||
!(a || b)
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRewrittenBinaryOperator.html">CXXRewrittenBinaryOperator</a>></td><td class="name" onclick="toggle('isAssignmentOperator2')"><a name="isAssignmentOperator2Anchor">isAssignmentOperator</a></td><td></td></tr>
|
||||
<tr><td colspan="4" class="doc" id="isAssignmentOperator2"><pre>Matches all kinds of assignment operators.
|
||||
|
||||
Example 1: matches a += b (matcher = binaryOperator(isAssignmentOperator()))
|
||||
if (a == b)
|
||||
a += b;
|
||||
|
||||
Example 2: matches s1 = s2
|
||||
(matcher = cxxOperatorCallExpr(isAssignmentOperator()))
|
||||
struct S { S& operator=(const S&); };
|
||||
void x() { S s1, s2; s1 = s2; }
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRewrittenBinaryOperator.html">CXXRewrittenBinaryOperator</a>></td><td class="name" onclick="toggle('isComparisonOperator2')"><a name="isComparisonOperator2Anchor">isComparisonOperator</a></td><td></td></tr>
|
||||
<tr><td colspan="4" class="doc" id="isComparisonOperator2"><pre>Matches comparison operators.
|
||||
|
||||
Example 1: matches a == b (matcher = binaryOperator(isComparisonOperator()))
|
||||
if (a == b)
|
||||
a += b;
|
||||
|
||||
Example 2: matches s1 < s2
|
||||
(matcher = cxxOperatorCallExpr(isComparisonOperator()))
|
||||
struct S { bool operator<(const S& other); };
|
||||
void x(S s1, S s2) { bool b1 = s1 < s2; }
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXUnresolvedConstructExpr.html">CXXUnresolvedConstructExpr</a>></td><td class="name" onclick="toggle('argumentCountIs2')"><a name="argumentCountIs2Anchor">argumentCountIs</a></td><td>unsigned N</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="argumentCountIs2"><pre>Checks that a call expression or a constructor call expression has
|
||||
a specific number of arguments (including absent default arguments).
|
||||
|
@ -5174,8 +5276,8 @@ should be passed as a quoted string. e.g., ofKind("UETT_SizeOf").
|
|||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1UnaryOperator.html">UnaryOperator</a>></td><td class="name" onclick="toggle('hasAnyOperatorName2')"><a name="hasAnyOperatorName2Anchor">hasAnyOperatorName</a></td><td>StringRef, ..., StringRef</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="hasAnyOperatorName2"><pre>Matches operator expressions (binary or unary) that have any of the
|
||||
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1UnaryOperator.html">UnaryOperator</a>></td><td class="name" onclick="toggle('hasAnyOperatorName3')"><a name="hasAnyOperatorName3Anchor">hasAnyOperatorName</a></td><td>StringRef, ..., StringRef</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="hasAnyOperatorName3"><pre>Matches operator expressions (binary or unary) that have any of the
|
||||
specified names.
|
||||
|
||||
hasAnyOperatorName("+", "-")
|
||||
|
@ -5184,8 +5286,8 @@ specified names.
|
|||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1UnaryOperator.html">UnaryOperator</a>></td><td class="name" onclick="toggle('hasOperatorName2')"><a name="hasOperatorName2Anchor">hasOperatorName</a></td><td>std::string Name</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="hasOperatorName2"><pre>Matches the operator Name of operator expressions (binary or
|
||||
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1UnaryOperator.html">UnaryOperator</a>></td><td class="name" onclick="toggle('hasOperatorName3')"><a name="hasOperatorName3Anchor">hasOperatorName</a></td><td>std::string Name</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="hasOperatorName3"><pre>Matches the operator Name of operator expressions (binary or
|
||||
unary).
|
||||
|
||||
Example matches a || b (matcher = binaryOperator(hasOperatorName("||")))
|
||||
|
@ -5446,13 +5548,16 @@ 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 &) const;
|
||||
};
|
||||
|
||||
void inverse()
|
||||
{
|
||||
HasOpEq s1;
|
||||
HasOpEq s2;
|
||||
if (s1 != s2)
|
||||
return;
|
||||
}
|
||||
|
||||
struct HasSpaceship
|
||||
{
|
||||
bool operator<=>(const HasOpEq &) const;
|
||||
};
|
||||
|
||||
void use_spaceship()
|
||||
{
|
||||
HasSpaceship s1;
|
||||
HasSpaceship s2;
|
||||
if (s1 != s2)
|
||||
return;
|
||||
}
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
|
@ -5753,16 +5883,16 @@ arraySubscriptExpression(hasIndex(integerLiteral()))
|
|||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ArraySubscriptExpr.html">ArraySubscriptExpr</a>></td><td class="name" onclick="toggle('hasLHS2')"><a name="hasLHS2Anchor">hasLHS</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="hasLHS2"><pre>Matches the left hand side of binary operator expressions.
|
||||
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ArraySubscriptExpr.html">ArraySubscriptExpr</a>></td><td class="name" onclick="toggle('hasLHS3')"><a name="hasLHS3Anchor">hasLHS</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="hasLHS3"><pre>Matches the left hand side of binary operator expressions.
|
||||
|
||||
Example matches a (matcher = binaryOperator(hasLHS()))
|
||||
a || b
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ArraySubscriptExpr.html">ArraySubscriptExpr</a>></td><td class="name" onclick="toggle('hasRHS2')"><a name="hasRHS2Anchor">hasRHS</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="hasRHS2"><pre>Matches the right hand side of binary operator expressions.
|
||||
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ArraySubscriptExpr.html">ArraySubscriptExpr</a>></td><td class="name" onclick="toggle('hasRHS3')"><a name="hasRHS3Anchor">hasRHS</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="hasRHS3"><pre>Matches the right hand side of binary operator expressions.
|
||||
|
||||
Example matches b (matcher = binaryOperator(hasRHS()))
|
||||
a || b
|
||||
|
@ -6480,6 +6610,40 @@ match Base.
|
|||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRewrittenBinaryOperator.html">CXXRewrittenBinaryOperator</a>></td><td class="name" onclick="toggle('hasEitherOperand2')"><a name="hasEitherOperand2Anchor">hasEitherOperand</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="hasEitherOperand2"><pre>Matches if either the left hand side or the right hand side of a
|
||||
binary operator matches.
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRewrittenBinaryOperator.html">CXXRewrittenBinaryOperator</a>></td><td class="name" onclick="toggle('hasLHS2')"><a name="hasLHS2Anchor">hasLHS</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="hasLHS2"><pre>Matches the left hand side of binary operator expressions.
|
||||
|
||||
Example matches a (matcher = binaryOperator(hasLHS()))
|
||||
a || b
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRewrittenBinaryOperator.html">CXXRewrittenBinaryOperator</a>></td><td class="name" onclick="toggle('hasOperands2')"><a name="hasOperands2Anchor">hasOperands</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> Matcher1, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> Matcher2</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="hasOperands2"><pre>Matches if both matchers match with opposite sides of the binary operator.
|
||||
|
||||
Example matcher = binaryOperator(hasOperands(integerLiteral(equals(1),
|
||||
integerLiteral(equals(2)))
|
||||
1 + 2 // Match
|
||||
2 + 1 // Match
|
||||
1 + 1 // No match
|
||||
2 + 2 // No match
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRewrittenBinaryOperator.html">CXXRewrittenBinaryOperator</a>></td><td class="name" onclick="toggle('hasRHS2')"><a name="hasRHS2Anchor">hasRHS</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="hasRHS2"><pre>Matches the right hand side of binary operator expressions.
|
||||
|
||||
Example matches b (matcher = binaryOperator(hasRHS()))
|
||||
a || b
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXUnresolvedConstructExpr.html">CXXUnresolvedConstructExpr</a>></td><td class="name" onclick="toggle('hasAnyArgument2')"><a name="hasAnyArgument2Anchor">hasAnyArgument</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="hasAnyArgument2"><pre>Matches any argument of a call expression or a constructor call
|
||||
expression, or an ObjC-message-send expression.
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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,10 +5295,10 @@ AST_POLYMORPHIC_MATCHER_P_OVERLOAD(equals,
|
|||
/// \code
|
||||
/// !(a || b)
|
||||
/// \endcode
|
||||
AST_POLYMORPHIC_MATCHER_P(hasOperatorName,
|
||||
AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator,
|
||||
CXXOperatorCallExpr,
|
||||
UnaryOperator),
|
||||
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;
|
||||
|
@ -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,9 +5396,10 @@ 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),
|
||||
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)))
|
||||
|
@ -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))))
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue