[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>
|
</td>
|
||||||
|
|
||||||
</tr>
|
</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>
|
</table>
|
||||||
|
|
||||||
<!-- ======================================================================= -->
|
<!-- ======================================================================= -->
|
||||||
|
@ -1523,6 +1559,25 @@ Example matches reinterpret_cast<char*>(&p) in
|
||||||
</pre></td></tr>
|
</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>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.
|
<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>
|
</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>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
|
<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<<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>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="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<<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>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="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 &) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
void inverse()
|
||||||
|
{
|
||||||
|
HasOpEq s1;
|
||||||
|
HasOpEq s2;
|
||||||
|
if (s1 != s2)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HasSpaceship
|
||||||
|
{
|
||||||
|
bool operator<=>(const HasOpEq &) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
void use_spaceship()
|
||||||
|
{
|
||||||
|
HasSpaceship s1;
|
||||||
|
HasSpaceship s2;
|
||||||
|
if (s1 != s2)
|
||||||
|
return;
|
||||||
|
}
|
||||||
</pre></td></tr>
|
</pre></td></tr>
|
||||||
|
|
||||||
|
|
||||||
|
@ -5753,16 +5883,16 @@ arraySubscriptExpression(hasIndex(integerLiteral()))
|
||||||
</pre></td></tr>
|
</pre></td></tr>
|
||||||
|
|
||||||
|
|
||||||
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ArraySubscriptExpr.html">ArraySubscriptExpr</a>></td><td class="name" onclick="toggle('hasLHS2')"><a name="hasLHS2Anchor">hasLHS</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr>
|
<tr><td>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="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<<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>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="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<<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>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
|
<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.
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
||||||
|
|
|
@ -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,10 +5295,10 @@ 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;
|
||||||
|
@ -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,9 +5396,10 @@ 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,
|
||||||
|
CXXRewrittenBinaryOperator),
|
||||||
internal::Matcher<Expr>, InnerMatcher) {
|
internal::Matcher<Expr>, InnerMatcher) {
|
||||||
return internal::VariadicDynCastAllOfMatcher<Stmt, NodeType>()(
|
return internal::VariadicDynCastAllOfMatcher<Stmt, NodeType>()(
|
||||||
anyOf(hasLHS(InnerMatcher), hasRHS(InnerMatcher)))
|
anyOf(hasLHS(InnerMatcher), hasRHS(InnerMatcher)))
|
||||||
|
@ -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))))
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue