[clang] dbe056c - [ASTMatchers] Make cxxOperatorCallExpr matchers API-compatible with n-ary operators
Stephen Kelly via cfe-commits
cfe-commits at lists.llvm.org
Sat Jan 16 05:21:18 PST 2021
Author: Stephen Kelly
Date: 2021-01-16T12:53:11Z
New Revision: dbe056c2e37f00b9f33ab63bba73dbb004e13562
URL: https://github.com/llvm/llvm-project/commit/dbe056c2e37f00b9f33ab63bba73dbb004e13562
DIFF: https://github.com/llvm/llvm-project/commit/dbe056c2e37f00b9f33ab63bba73dbb004e13562.diff
LOG: [ASTMatchers] Make cxxOperatorCallExpr matchers API-compatible with n-ary operators
This makes them composable with mapAnyOf().
Differential Revision: https://reviews.llvm.org/D94128
Added:
Modified:
clang/docs/LibASTMatchersReference.html
clang/include/clang/ASTMatchers/ASTMatchers.h
clang/include/clang/ASTMatchers/ASTMatchersInternal.h
clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
Removed:
################################################################################
diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html
index e5de645266b7..497e2d4584a2 100644
--- a/clang/docs/LibASTMatchersReference.html
+++ b/clang/docs/LibASTMatchersReference.html
@@ -3232,6 +3232,16 @@ <h2 id="narrowing-matchers">Narrowing Matchers</h2>
</pre></td></tr>
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXOperatorCallExpr.html">CXXOperatorCallExpr</a>></td><td class="name" onclick="toggle('hasAnyOperatorName1')"><a name="hasAnyOperatorName1Anchor">hasAnyOperatorName</a></td><td>StringRef, ..., StringRef</td></tr>
+<tr><td colspan="4" class="doc" id="hasAnyOperatorName1"><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_1CXXOperatorCallExpr.html">CXXOperatorCallExpr</a>></td><td class="name" onclick="toggle('hasAnyOverloadedOperatorName0')"><a name="hasAnyOverloadedOperatorName0Anchor">hasAnyOverloadedOperatorName</a></td><td>StringRef, ..., StringRef</td></tr>
<tr><td colspan="4" class="doc" id="hasAnyOverloadedOperatorName0"><pre>Matches overloaded operator names.
@@ -3244,6 +3254,15 @@ <h2 id="narrowing-matchers">Narrowing Matchers</h2>
</pre></td></tr>
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXOperatorCallExpr.html">CXXOperatorCallExpr</a>></td><td class="name" onclick="toggle('hasOperatorName1')"><a name="hasOperatorName1Anchor">hasOperatorName</a></td><td>std::string Name</td></tr>
+<tr><td colspan="4" class="doc" id="hasOperatorName1"><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_1CXXOperatorCallExpr.html">CXXOperatorCallExpr</a>></td><td class="name" onclick="toggle('hasOverloadedOperatorName1')"><a name="hasOverloadedOperatorName1Anchor">hasOverloadedOperatorName</a></td><td>StringRef Name</td></tr>
<tr><td colspan="4" class="doc" id="hasOverloadedOperatorName1"><pre>Matches overloaded operator names.
@@ -5152,8 +5171,8 @@ <h2 id="narrowing-matchers">Narrowing Matchers</h2>
</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('hasAnyOperatorName1')"><a name="hasAnyOperatorName1Anchor">hasAnyOperatorName</a></td><td>StringRef, ..., StringRef</td></tr>
-<tr><td colspan="4" class="doc" id="hasAnyOperatorName1"><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('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("+", "-")
@@ -5162,8 +5181,8 @@ <h2 id="narrowing-matchers">Narrowing Matchers</h2>
</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('hasOperatorName1')"><a name="hasOperatorName1Anchor">hasOperatorName</a></td><td>std::string Name</td></tr>
-<tr><td colspan="4" class="doc" id="hasOperatorName1"><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('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("||")))
@@ -5689,16 +5708,16 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
</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('hasLHS1')"><a name="hasLHS1Anchor">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="hasLHS1"><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('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_1ArraySubscriptExpr.html">ArraySubscriptExpr</a>></td><td class="name" onclick="toggle('hasRHS1')"><a name="hasRHS1Anchor">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="hasRHS1"><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('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
@@ -5749,7 +5768,7 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
</pre></td></tr>
-<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1BinaryOperator.html">BinaryOperator</a>></td><td class="name" onclick="toggle('hasEitherOperand0')"><a name="hasEitherOperand0Anchor">hasEitherOperand</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_1BinaryOperator.html">BinaryOperator</a>></td><td class="name" onclick="toggle('hasEitherOperand0')"><a name="hasEitherOperand0Anchor">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="hasEitherOperand0"><pre>Matches if either the left hand side or the right hand side of a
binary operator matches.
</pre></td></tr>
@@ -5763,7 +5782,7 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
</pre></td></tr>
-<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1BinaryOperator.html">BinaryOperator</a>></td><td class="name" onclick="toggle('hasOperands0')"><a name="hasOperands0Anchor">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>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1BinaryOperator.html">BinaryOperator</a>></td><td class="name" onclick="toggle('hasOperands0')"><a name="hasOperands0Anchor">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="hasOperands0"><pre>Matches if both matchers match with opposite sides of the binary operator.
Example matcher = binaryOperator(hasOperands(integerLiteral(equals(1),
@@ -6276,6 +6295,49 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
</pre></td></tr>
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXOperatorCallExpr.html">CXXOperatorCallExpr</a>></td><td class="name" onclick="toggle('hasEitherOperand1')"><a name="hasEitherOperand1Anchor">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="hasEitherOperand1"><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_1CXXOperatorCallExpr.html">CXXOperatorCallExpr</a>></td><td class="name" onclick="toggle('hasLHS1')"><a name="hasLHS1Anchor">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="hasLHS1"><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_1CXXOperatorCallExpr.html">CXXOperatorCallExpr</a>></td><td class="name" onclick="toggle('hasOperands1')"><a name="hasOperands1Anchor">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="hasOperands1"><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_1CXXOperatorCallExpr.html">CXXOperatorCallExpr</a>></td><td class="name" onclick="toggle('hasRHS1')"><a name="hasRHS1Anchor">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="hasRHS1"><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_1CXXOperatorCallExpr.html">CXXOperatorCallExpr</a>></td><td class="name" onclick="toggle('hasUnaryOperand1')"><a name="hasUnaryOperand1Anchor">hasUnaryOperand</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="hasUnaryOperand1"><pre>Matches if the operand of a unary operator matches.
+
+Example matches true (matcher = hasUnaryOperand(
+ cxxBoolLiteral(equals(true))))
+ !true
+</pre></td></tr>
+
+
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html">CXXRecordDecl</a>></td><td class="name" onclick="toggle('hasAnyBase0')"><a name="hasAnyBase0Anchor">hasAnyBase</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXBaseSpecifier.html">CXXBaseSpecifier</a>> BaseSpecMatcher</td></tr>
<tr><td colspan="4" class="doc" id="hasAnyBase0"><pre>Matches C++ classes that have a direct or indirect base matching BaseSpecMatcher.
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index dd99e6a420af..e81ac4cb7bf8 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -5197,9 +5197,12 @@ AST_POLYMORPHIC_MATCHER_P_OVERLOAD(equals,
/// \endcode
AST_POLYMORPHIC_MATCHER_P(hasOperatorName,
AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator,
+ CXXOperatorCallExpr,
UnaryOperator),
std::string, Name) {
- return Name == Node.getOpcodeStr(Node.getOpcode());
+ if (Optional<StringRef> OpName = internal::getOpName(Node))
+ return *OpName == Name;
+ return false;
}
/// Matches operator expressions (binary or unary) that have any of the
@@ -5211,7 +5214,8 @@ AST_POLYMORPHIC_MATCHER_P(hasOperatorName,
extern const internal::VariadicFunction<
internal::PolymorphicMatcherWithParam1<
internal::HasAnyOperatorNameMatcher, std::vector<std::string>,
- AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, UnaryOperator)>,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
+ UnaryOperator)>,
StringRef, internal::hasAnyOperatorNameFunc>
hasAnyOperatorName;
@@ -5263,9 +5267,10 @@ AST_POLYMORPHIC_MATCHER(isComparisonOperator,
/// \endcode
AST_POLYMORPHIC_MATCHER_P(hasLHS,
AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator,
+ CXXOperatorCallExpr,
ArraySubscriptExpr),
internal::Matcher<Expr>, InnerMatcher) {
- const Expr *LeftHandSide = Node.getLHS();
+ const Expr *LeftHandSide = internal::getLHS(Node);
return (LeftHandSide != nullptr &&
InnerMatcher.matches(*LeftHandSide, Finder, Builder));
}
@@ -5278,18 +5283,23 @@ AST_POLYMORPHIC_MATCHER_P(hasLHS,
/// \endcode
AST_POLYMORPHIC_MATCHER_P(hasRHS,
AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator,
+ CXXOperatorCallExpr,
ArraySubscriptExpr),
internal::Matcher<Expr>, InnerMatcher) {
- const Expr *RightHandSide = Node.getRHS();
+ const Expr *RightHandSide = internal::getRHS(Node);
return (RightHandSide != nullptr &&
InnerMatcher.matches(*RightHandSide, Finder, Builder));
}
/// Matches if either the left hand side or the right hand side of a
/// binary operator matches.
-inline internal::Matcher<BinaryOperator> hasEitherOperand(
- const internal::Matcher<Expr> &InnerMatcher) {
- return anyOf(hasLHS(InnerMatcher), hasRHS(InnerMatcher));
+AST_POLYMORPHIC_MATCHER_P(hasEitherOperand,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator,
+ CXXOperatorCallExpr),
+ internal::Matcher<Expr>, InnerMatcher) {
+ return internal::VariadicDynCastAllOfMatcher<Stmt, NodeType>()(
+ anyOf(hasLHS(InnerMatcher), hasRHS(InnerMatcher)))
+ .matches(Node, Finder, Builder);
}
/// Matches if both matchers match with opposite sides of the binary operator.
@@ -5302,11 +5312,15 @@ inline internal::Matcher<BinaryOperator> hasEitherOperand(
/// 1 + 1 // No match
/// 2 + 2 // No match
/// \endcode
-inline internal::Matcher<BinaryOperator>
-hasOperands(const internal::Matcher<Expr> &Matcher1,
- const internal::Matcher<Expr> &Matcher2) {
- return anyOf(allOf(hasLHS(Matcher1), hasRHS(Matcher2)),
- allOf(hasLHS(Matcher2), hasRHS(Matcher1)));
+AST_POLYMORPHIC_MATCHER_P2(hasOperands,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator,
+ CXXOperatorCallExpr),
+ internal::Matcher<Expr>, Matcher1,
+ internal::Matcher<Expr>, Matcher2) {
+ return internal::VariadicDynCastAllOfMatcher<Stmt, NodeType>()(
+ anyOf(allOf(hasLHS(Matcher1), hasRHS(Matcher2)),
+ allOf(hasLHS(Matcher2), hasRHS(Matcher1))))
+ .matches(Node, Finder, Builder);
}
/// Matches if the operand of a unary operator matches.
@@ -5316,9 +5330,11 @@ hasOperands(const internal::Matcher<Expr> &Matcher1,
/// \code
/// !true
/// \endcode
-AST_MATCHER_P(UnaryOperator, hasUnaryOperand,
- internal::Matcher<Expr>, InnerMatcher) {
- const Expr * const Operand = Node.getSubExpr();
+AST_POLYMORPHIC_MATCHER_P(hasUnaryOperand,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(UnaryOperator,
+ CXXOperatorCallExpr),
+ internal::Matcher<Expr>, InnerMatcher) {
+ const Expr *const Operand = internal::getSubExpr(Node);
return (Operand != nullptr &&
InnerMatcher.matches(*Operand, Finder, Builder));
}
diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
index 2c2b99ba918d..7f97eb3cd438 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -1882,6 +1882,160 @@ struct GetBodyMatcher<Ty, typename std::enable_if<
}
};
+template <typename NodeType>
+inline Optional<BinaryOperatorKind>
+equivalentBinaryOperator(const NodeType &Node) {
+ return Node.getOpcode();
+}
+
+template <>
+inline Optional<BinaryOperatorKind>
+equivalentBinaryOperator<CXXOperatorCallExpr>(const CXXOperatorCallExpr &Node) {
+ if (Node.getNumArgs() != 2)
+ return None;
+ switch (Node.getOperator()) {
+ default:
+ return None;
+ case OO_ArrowStar:
+ return BO_PtrMemI;
+ case OO_Star:
+ return BO_Mul;
+ case OO_Slash:
+ return BO_Div;
+ case OO_Percent:
+ return BO_Rem;
+ case OO_Plus:
+ return BO_Add;
+ case OO_Minus:
+ return BO_Sub;
+ case OO_LessLess:
+ return BO_Shl;
+ case OO_GreaterGreater:
+ return BO_Shr;
+ case OO_Spaceship:
+ return BO_Cmp;
+ case OO_Less:
+ return BO_LT;
+ case OO_Greater:
+ return BO_GT;
+ case OO_LessEqual:
+ return BO_LE;
+ case OO_GreaterEqual:
+ return BO_GE;
+ case OO_EqualEqual:
+ return BO_EQ;
+ case OO_ExclaimEqual:
+ return BO_NE;
+ case OO_Amp:
+ return BO_And;
+ case OO_Caret:
+ return BO_Xor;
+ case OO_Pipe:
+ return BO_Or;
+ case OO_AmpAmp:
+ return BO_LAnd;
+ case OO_PipePipe:
+ return BO_LOr;
+ case OO_Equal:
+ return BO_Assign;
+ case OO_StarEqual:
+ return BO_MulAssign;
+ case OO_SlashEqual:
+ return BO_DivAssign;
+ case OO_PercentEqual:
+ return BO_RemAssign;
+ case OO_PlusEqual:
+ return BO_AddAssign;
+ case OO_MinusEqual:
+ return BO_SubAssign;
+ case OO_LessLessEqual:
+ return BO_ShlAssign;
+ case OO_GreaterGreaterEqual:
+ return BO_ShrAssign;
+ case OO_AmpEqual:
+ return BO_AndAssign;
+ case OO_CaretEqual:
+ return BO_XorAssign;
+ case OO_PipeEqual:
+ return BO_OrAssign;
+ case OO_Comma:
+ return BO_Comma;
+ }
+}
+
+template <typename NodeType>
+inline Optional<UnaryOperatorKind>
+equivalentUnaryOperator(const NodeType &Node) {
+ return Node.getOpcode();
+}
+
+template <>
+inline Optional<UnaryOperatorKind>
+equivalentUnaryOperator<CXXOperatorCallExpr>(const CXXOperatorCallExpr &Node) {
+ if (Node.getNumArgs() != 1)
+ return None;
+ switch (Node.getOperator()) {
+ default:
+ return None;
+ case OO_Plus:
+ return UO_Plus;
+ case OO_Minus:
+ return UO_Minus;
+ case OO_Amp:
+ return UO_AddrOf;
+ case OO_Tilde:
+ return UO_Not;
+ case OO_Exclaim:
+ return UO_LNot;
+ case OO_PlusPlus: {
+ const auto *FD = Node.getDirectCallee();
+ if (!FD)
+ return None;
+ return FD->getNumParams() > 0 ? UO_PostInc : UO_PreInc;
+ }
+ case OO_MinusMinus: {
+ const auto *FD = Node.getDirectCallee();
+ if (!FD)
+ return None;
+ return FD->getNumParams() > 0 ? UO_PostDec : UO_PreDec;
+ }
+ case OO_Coawait:
+ return UO_Coawait;
+ }
+}
+
+template <typename NodeType> inline const Expr *getLHS(const NodeType &Node) {
+ return Node.getLHS();
+}
+template <>
+inline const Expr *
+getLHS<CXXOperatorCallExpr>(const CXXOperatorCallExpr &Node) {
+ if (!internal::equivalentBinaryOperator(Node))
+ return nullptr;
+ return Node.getArg(0);
+}
+template <typename NodeType> inline const Expr *getRHS(const NodeType &Node) {
+ return Node.getRHS();
+}
+template <>
+inline const Expr *
+getRHS<CXXOperatorCallExpr>(const CXXOperatorCallExpr &Node) {
+ if (!internal::equivalentBinaryOperator(Node))
+ return nullptr;
+ return Node.getArg(1);
+}
+template <typename NodeType>
+inline const Expr *getSubExpr(const NodeType &Node) {
+ return Node.getSubExpr();
+}
+template <>
+inline const Expr *
+getSubExpr<CXXOperatorCallExpr>(const CXXOperatorCallExpr &Node) {
+ if (!internal::equivalentUnaryOperator(Node))
+ return nullptr;
+ return Node.getArg(0);
+}
+
template <typename Ty>
struct HasSizeMatcher {
static bool hasSize(const Ty &Node, unsigned int N) {
@@ -1929,6 +2083,23 @@ llvm::Optional<SourceLocation>
getExpansionLocOfMacro(StringRef MacroName, SourceLocation Loc,
const ASTContext &Context);
+inline Optional<StringRef> getOpName(const UnaryOperator &Node) {
+ return Node.getOpcodeStr(Node.getOpcode());
+}
+inline Optional<StringRef> getOpName(const BinaryOperator &Node) {
+ return Node.getOpcodeStr();
+}
+inline 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);
+}
+
/// Matches overloaded operators with a specific name.
///
/// The type argument ArgT is not used by this matcher but is used by
@@ -1936,8 +2107,10 @@ getExpansionLocOfMacro(StringRef MacroName, SourceLocation Loc,
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, UnaryOperator>::value,
- "Matcher only supports `BinaryOperator` and `UnaryOperator`");
+ "Matcher only supports `BinaryOperator`, `UnaryOperator` and "
+ "`CXXOperatorCallExpr`");
static_assert(std::is_same<ArgT, std::vector<std::string>>::value,
"Matcher ArgT must be std::vector<std::string>");
@@ -1946,26 +2119,21 @@ class HasAnyOperatorNameMatcher : public SingleNodeMatcherInterface<T> {
: SingleNodeMatcherInterface<T>(), Names(std::move(Names)) {}
bool matchesNode(const T &Node) const override {
- StringRef OpName = getOpName(Node);
- return llvm::any_of(
- Names, [&](const std::string &Name) { return Name == OpName; });
+ Optional<StringRef> OptOpName = getOpName(Node);
+ if (!OptOpName)
+ return false;
+ return llvm::any_of(Names, [OpName = *OptOpName](const std::string &Name) {
+ return Name == OpName;
+ });
}
private:
- static StringRef getOpName(const UnaryOperator &Node) {
- return Node.getOpcodeStr(Node.getOpcode());
- }
- static StringRef getOpName(const BinaryOperator &Node) {
- return Node.getOpcodeStr();
- }
-
const std::vector<std::string> Names;
};
-using HasOpNameMatcher =
- PolymorphicMatcherWithParam1<HasAnyOperatorNameMatcher,
- std::vector<std::string>,
- void(TypeList<BinaryOperator, UnaryOperator>)>;
+using HasOpNameMatcher = PolymorphicMatcherWithParam1<
+ HasAnyOperatorNameMatcher, std::vector<std::string>,
+ void(TypeList<BinaryOperator, CXXOperatorCallExpr, UnaryOperator>)>;
HasOpNameMatcher hasAnyOperatorNameFunc(ArrayRef<const StringRef *> NameRefs);
diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
index 49b010feeb73..9e011892f6e5 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -516,6 +516,238 @@ void F() {
EXPECT_TRUE(matches(
Code, traverse(TK_IgnoreUnlessSpelledInSource,
mapAnyOf(callExpr, cxxConstructExpr).bind("call"))));
+
+ Code = R"cpp(
+struct HasOpNeqMem
+{
+ bool operator!=(const HasOpNeqMem& other) const
+ {
+ return true;
+ }
+};
+struct HasOpFree
+{
+};
+bool operator!=(const HasOpFree& lhs, const HasOpFree& rhs)
+{
+ return true;
+}
+
+void binop()
+{
+ int s1;
+ int s2;
+ if (s1 != s2)
+ return;
+}
+
+void opMem()
+{
+ HasOpNeqMem s1;
+ HasOpNeqMem s2;
+ if (s1 != s2)
+ return;
+}
+
+void opFree()
+{
+ HasOpFree s1;
+ HasOpFree s2;
+ if (s1 != s2)
+ return;
+}
+)cpp";
+
+ EXPECT_TRUE(matches(
+ Code,
+ traverse(TK_IgnoreUnlessSpelledInSource,
+ mapAnyOf(binaryOperator, cxxOperatorCallExpr)
+ .with(hasOperatorName("!="),
+ forFunction(functionDecl(hasName("binop"))),
+ hasLHS(declRefExpr(to(varDecl(hasName("s1"))))),
+ hasRHS(declRefExpr(to(varDecl(hasName("s2")))))))));
+
+ EXPECT_TRUE(matches(
+ Code,
+ traverse(TK_IgnoreUnlessSpelledInSource,
+ mapAnyOf(binaryOperator, cxxOperatorCallExpr)
+ .with(hasOperatorName("!="),
+ forFunction(functionDecl(hasName("opMem"))),
+ hasLHS(declRefExpr(to(varDecl(hasName("s1"))))),
+ hasRHS(declRefExpr(to(varDecl(hasName("s2")))))))));
+
+ EXPECT_TRUE(matches(
+ Code,
+ traverse(TK_IgnoreUnlessSpelledInSource,
+ mapAnyOf(binaryOperator, cxxOperatorCallExpr)
+ .with(hasOperatorName("!="),
+ forFunction(functionDecl(hasName("opFree"))),
+ hasLHS(declRefExpr(to(varDecl(hasName("s1"))))),
+ hasRHS(declRefExpr(to(varDecl(hasName("s2")))))))));
+
+ EXPECT_TRUE(matches(
+ Code, traverse(TK_IgnoreUnlessSpelledInSource,
+ mapAnyOf(binaryOperator, cxxOperatorCallExpr)
+ .with(hasOperatorName("!="),
+ forFunction(functionDecl(hasName("binop"))),
+ hasEitherOperand(
+ declRefExpr(to(varDecl(hasName("s1"))))),
+ hasEitherOperand(
+ declRefExpr(to(varDecl(hasName("s2")))))))));
+
+ EXPECT_TRUE(matches(
+ Code, traverse(TK_IgnoreUnlessSpelledInSource,
+ mapAnyOf(binaryOperator, cxxOperatorCallExpr)
+ .with(hasOperatorName("!="),
+ forFunction(functionDecl(hasName("opMem"))),
+ hasEitherOperand(
+ declRefExpr(to(varDecl(hasName("s1"))))),
+ hasEitherOperand(
+ declRefExpr(to(varDecl(hasName("s2")))))))));
+
+ EXPECT_TRUE(matches(
+ Code, traverse(TK_IgnoreUnlessSpelledInSource,
+ mapAnyOf(binaryOperator, cxxOperatorCallExpr)
+ .with(hasOperatorName("!="),
+ forFunction(functionDecl(hasName("opFree"))),
+ hasEitherOperand(
+ declRefExpr(to(varDecl(hasName("s1"))))),
+ hasEitherOperand(
+ declRefExpr(to(varDecl(hasName("s2")))))))));
+
+ EXPECT_TRUE(matches(
+ Code,
+ traverse(
+ TK_IgnoreUnlessSpelledInSource,
+ mapAnyOf(binaryOperator, cxxOperatorCallExpr)
+ .with(hasOperatorName("!="),
+ forFunction(functionDecl(hasName("binop"))),
+ hasOperands(declRefExpr(to(varDecl(hasName("s1")))),
+ declRefExpr(to(varDecl(hasName("s2"))))),
+ hasOperands(declRefExpr(to(varDecl(hasName("s2")))),
+ declRefExpr(to(varDecl(hasName("s1")))))))));
+
+ EXPECT_TRUE(matches(
+ Code,
+ traverse(
+ TK_IgnoreUnlessSpelledInSource,
+ mapAnyOf(binaryOperator, cxxOperatorCallExpr)
+ .with(hasOperatorName("!="),
+ forFunction(functionDecl(hasName("opMem"))),
+ hasOperands(declRefExpr(to(varDecl(hasName("s1")))),
+ declRefExpr(to(varDecl(hasName("s2"))))),
+ hasOperands(declRefExpr(to(varDecl(hasName("s2")))),
+ declRefExpr(to(varDecl(hasName("s1")))))))));
+
+ EXPECT_TRUE(matches(
+ Code,
+ traverse(
+ TK_IgnoreUnlessSpelledInSource,
+ mapAnyOf(binaryOperator, cxxOperatorCallExpr)
+ .with(hasOperatorName("!="),
+ forFunction(functionDecl(hasName("opFree"))),
+ hasOperands(declRefExpr(to(varDecl(hasName("s1")))),
+ declRefExpr(to(varDecl(hasName("s2"))))),
+ hasOperands(declRefExpr(to(varDecl(hasName("s2")))),
+ declRefExpr(to(varDecl(hasName("s1")))))))));
+
+ EXPECT_TRUE(matches(
+ Code, traverse(TK_IgnoreUnlessSpelledInSource,
+ mapAnyOf(binaryOperator, cxxOperatorCallExpr)
+ .with(hasAnyOperatorName("==", "!="),
+ forFunction(functionDecl(hasName("binop")))))));
+
+ EXPECT_TRUE(matches(
+ Code, traverse(TK_IgnoreUnlessSpelledInSource,
+ mapAnyOf(binaryOperator, cxxOperatorCallExpr)
+ .with(hasAnyOperatorName("==", "!="),
+ forFunction(functionDecl(hasName("opMem")))))));
+
+ EXPECT_TRUE(matches(
+ Code, traverse(TK_IgnoreUnlessSpelledInSource,
+ mapAnyOf(binaryOperator, cxxOperatorCallExpr)
+ .with(hasAnyOperatorName("==", "!="),
+ forFunction(functionDecl(hasName("opFree")))))));
+
+ Code = R"cpp(
+struct HasOpBangMem
+{
+ bool operator!() const
+ {
+ return false;
+ }
+};
+struct HasOpBangFree
+{
+};
+bool operator!(HasOpBangFree const&)
+{
+ return false;
+}
+
+void unop()
+{
+ int s1;
+ if (!s1)
+ return;
+}
+
+void opMem()
+{
+ HasOpBangMem s1;
+ if (!s1)
+ return;
+}
+
+void opFree()
+{
+ HasOpBangFree s1;
+ if (!s1)
+ return;
+}
+)cpp";
+
+ EXPECT_TRUE(matches(
+ Code, traverse(TK_IgnoreUnlessSpelledInSource,
+ mapAnyOf(unaryOperator, cxxOperatorCallExpr)
+ .with(hasOperatorName("!"),
+ forFunction(functionDecl(hasName("unop"))),
+ hasUnaryOperand(
+ declRefExpr(to(varDecl(hasName("s1")))))))));
+
+ EXPECT_TRUE(matches(
+ Code, traverse(TK_IgnoreUnlessSpelledInSource,
+ mapAnyOf(unaryOperator, cxxOperatorCallExpr)
+ .with(hasOperatorName("!"),
+ forFunction(functionDecl(hasName("opMem"))),
+ hasUnaryOperand(
+ declRefExpr(to(varDecl(hasName("s1")))))))));
+
+ EXPECT_TRUE(matches(
+ Code, traverse(TK_IgnoreUnlessSpelledInSource,
+ mapAnyOf(unaryOperator, cxxOperatorCallExpr)
+ .with(hasOperatorName("!"),
+ forFunction(functionDecl(hasName("opFree"))),
+ hasUnaryOperand(
+ declRefExpr(to(varDecl(hasName("s1")))))))));
+
+ EXPECT_TRUE(matches(
+ Code, traverse(TK_IgnoreUnlessSpelledInSource,
+ mapAnyOf(unaryOperator, cxxOperatorCallExpr)
+ .with(hasAnyOperatorName("+", "!"),
+ forFunction(functionDecl(hasName("unop")))))));
+
+ EXPECT_TRUE(matches(
+ Code, traverse(TK_IgnoreUnlessSpelledInSource,
+ mapAnyOf(unaryOperator, cxxOperatorCallExpr)
+ .with(hasAnyOperatorName("+", "!"),
+ forFunction(functionDecl(hasName("opMem")))))));
+
+ EXPECT_TRUE(matches(
+ Code, traverse(TK_IgnoreUnlessSpelledInSource,
+ mapAnyOf(unaryOperator, cxxOperatorCallExpr)
+ .with(hasAnyOperatorName("+", "!"),
+ forFunction(functionDecl(hasName("opFree")))))));
}
TEST_P(ASTMatchersTest, IsDerivedFrom) {
diff --git a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
index 19ab6187d960..06bcd65d8911 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -1313,6 +1313,85 @@ TEST(MatchBinaryOperator, HasLHSAndHasRHS) {
traverse(TK_AsIs, hasRHS(hasType(pointsTo(qualType())))));
EXPECT_TRUE(matches("void x() { 1[\"abc\"]; }", OperatorIntPointer));
EXPECT_TRUE(notMatches("void x() { \"abc\"[1]; }", OperatorIntPointer));
+
+ StringRef Code = R"cpp(
+struct HasOpEqMem
+{
+ bool operator==(const HasOpEqMem& other) const
+ {
+ return true;
+ }
+};
+
+struct HasOpFree
+{
+};
+bool operator==(const HasOpFree& lhs, const HasOpFree& rhs)
+{
+ return true;
+}
+
+void opMem()
+{
+ HasOpEqMem s1;
+ HasOpEqMem s2;
+ if (s1 == s2)
+ return;
+}
+
+void opFree()
+{
+ HasOpFree s1;
+ HasOpFree s2;
+ if (s1 == s2)
+ return;
+}
+)cpp";
+ auto s1Expr = declRefExpr(to(varDecl(hasName("s1"))));
+ auto s2Expr = declRefExpr(to(varDecl(hasName("s2"))));
+ EXPECT_TRUE(matches(
+ Code,
+ traverse(TK_IgnoreUnlessSpelledInSource,
+ cxxOperatorCallExpr(forFunction(functionDecl(hasName("opMem"))),
+ hasOperatorName("=="), hasLHS(s1Expr),
+ hasRHS(s2Expr)))));
+ EXPECT_TRUE(matches(
+ Code, traverse(TK_IgnoreUnlessSpelledInSource,
+ cxxOperatorCallExpr(
+ forFunction(functionDecl(hasName("opMem"))),
+ hasAnyOperatorName("!=", "=="), hasLHS(s1Expr)))));
+ EXPECT_TRUE(matches(
+ Code, traverse(TK_IgnoreUnlessSpelledInSource,
+ cxxOperatorCallExpr(
+ forFunction(functionDecl(hasName("opMem"))),
+ hasOperatorName("=="), hasOperands(s1Expr, s2Expr)))));
+ EXPECT_TRUE(matches(
+ Code, traverse(TK_IgnoreUnlessSpelledInSource,
+ cxxOperatorCallExpr(
+ forFunction(functionDecl(hasName("opMem"))),
+ hasOperatorName("=="), hasEitherOperand(s2Expr)))));
+
+ EXPECT_TRUE(matches(
+ Code,
+ traverse(TK_IgnoreUnlessSpelledInSource,
+ cxxOperatorCallExpr(forFunction(functionDecl(hasName("opFree"))),
+ hasOperatorName("=="), hasLHS(s1Expr),
+ hasRHS(s2Expr)))));
+ EXPECT_TRUE(matches(
+ Code, traverse(TK_IgnoreUnlessSpelledInSource,
+ cxxOperatorCallExpr(
+ forFunction(functionDecl(hasName("opFree"))),
+ hasAnyOperatorName("!=", "=="), hasLHS(s1Expr)))));
+ EXPECT_TRUE(matches(
+ Code, traverse(TK_IgnoreUnlessSpelledInSource,
+ cxxOperatorCallExpr(
+ forFunction(functionDecl(hasName("opFree"))),
+ hasOperatorName("=="), hasOperands(s1Expr, s2Expr)))));
+ EXPECT_TRUE(matches(
+ Code, traverse(TK_IgnoreUnlessSpelledInSource,
+ cxxOperatorCallExpr(
+ forFunction(functionDecl(hasName("opFree"))),
+ hasOperatorName("=="), hasEitherOperand(s2Expr)))));
}
TEST(MatchBinaryOperator, HasEitherOperand) {
@@ -1461,6 +1540,60 @@ TEST(MatchUnaryOperator, HasUnaryOperand) {
EXPECT_TRUE(matches("void x() { !false; }", OperatorOnFalse));
EXPECT_TRUE(notMatches("void x() { !true; }", OperatorOnFalse));
+
+ StringRef Code = R"cpp(
+struct HasOpBangMem
+{
+ bool operator!() const
+ {
+ return false;
+ }
+};
+struct HasOpBangFree
+{
+};
+bool operator!(HasOpBangFree const&)
+{
+ return false;
+}
+
+void opMem()
+{
+ HasOpBangMem s1;
+ if (!s1)
+ return;
+}
+void opFree()
+{
+ HasOpBangFree s1;
+ if (!s1)
+ return;
+}
+)cpp";
+ auto s1Expr = declRefExpr(to(varDecl(hasName("s1"))));
+ EXPECT_TRUE(matches(
+ Code, traverse(TK_IgnoreUnlessSpelledInSource,
+ cxxOperatorCallExpr(
+ forFunction(functionDecl(hasName("opMem"))),
+ hasOperatorName("!"), hasUnaryOperand(s1Expr)))));
+ EXPECT_TRUE(matches(
+ Code,
+ traverse(TK_IgnoreUnlessSpelledInSource,
+ cxxOperatorCallExpr(forFunction(functionDecl(hasName("opMem"))),
+ hasAnyOperatorName("+", "!"),
+ hasUnaryOperand(s1Expr)))));
+
+ EXPECT_TRUE(matches(
+ Code, traverse(TK_IgnoreUnlessSpelledInSource,
+ cxxOperatorCallExpr(
+ forFunction(functionDecl(hasName("opFree"))),
+ hasOperatorName("!"), hasUnaryOperand(s1Expr)))));
+ EXPECT_TRUE(matches(
+ Code,
+ traverse(TK_IgnoreUnlessSpelledInSource,
+ cxxOperatorCallExpr(forFunction(functionDecl(hasName("opFree"))),
+ hasAnyOperatorName("+", "!"),
+ hasUnaryOperand(s1Expr)))));
}
TEST(Matcher, UnaryOperatorTypes) {
More information about the cfe-commits
mailing list