[llvm-branch-commits] [clang] dbe056c - [ASTMatchers] Make cxxOperatorCallExpr matchers API-compatible with n-ary operators

Stephen Kelly via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Sat Jan 16 05:25:44 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 llvm-branch-commits mailing list