diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -1523,6 +1523,25 @@ +Matcher<Stmt>cxxRewrittenBinaryOperatorMatcher<CXXRewrittenBinaryOperator>... +
Matches replacement binary operators
+
+Example matches x()
+  #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.
+
+ + Matcher<Stmt>cxxStaticCastExprMatcher<CXXStaticCastExpr>...
Matches a C++ static_cast expression.
 
@@ -3405,6 +3424,53 @@
 
+Matcher<CXXRewrittenBinaryOperator>hasAnyOperatorNameStringRef, ..., StringRef +
Matches operator expressions (binary or unary) that have any of the
+specified names.
+
+   hasAnyOperatorName("+", "-")
+ Is equivalent to
+   anyOf(hasOperatorName("+"), hasOperatorName("-"))
+
+ + +Matcher<CXXRewrittenBinaryOperator>hasOperatorNamestd::string Name +
Matches the operator Name of operator expressions (binary or
+unary).
+
+Example matches a || b (matcher = binaryOperator(hasOperatorName("||")))
+  !(a || b)
+
+ + +Matcher<CXXRewrittenBinaryOperator>isAssignmentOperator +
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; }
+
+ + +Matcher<CXXRewrittenBinaryOperator>isComparisonOperator +
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; }
+
+ + Matcher<CXXUnresolvedConstructExpr>argumentCountIsunsigned N
Checks that a call expression or a constructor call expression has
 a specific number of arguments (including absent default arguments).
@@ -5174,8 +5240,8 @@
 
-Matcher<UnaryOperator>hasAnyOperatorNameStringRef, ..., StringRef -
Matches operator expressions (binary or unary) that have any of the
+Matcher<UnaryOperator>hasAnyOperatorNameStringRef, ..., StringRef
+
Matches operator expressions (binary or unary) that have any of the
 specified names.
 
    hasAnyOperatorName("+", "-")
@@ -5184,8 +5250,8 @@
 
-Matcher<UnaryOperator>hasOperatorNamestd::string Name -
Matches the operator Name of operator expressions (binary or
+Matcher<UnaryOperator>hasOperatorNamestd::string Name
+
Matches the operator Name of operator expressions (binary or
 unary).
 
 Example matches a || b (matcher = binaryOperator(hasOperatorName("||")))
@@ -5711,16 +5777,16 @@
 
-Matcher<ArraySubscriptExpr>hasLHSMatcher<Expr> InnerMatcher -
Matches the left hand side of binary operator expressions.
+Matcher<ArraySubscriptExpr>hasLHSMatcher<Expr> InnerMatcher
+
Matches the left hand side of binary operator expressions.
 
 Example matches a (matcher = binaryOperator(hasLHS()))
   a || b
 
-Matcher<ArraySubscriptExpr>hasRHSMatcher<Expr> InnerMatcher -
Matches the right hand side of binary operator expressions.
+Matcher<ArraySubscriptExpr>hasRHSMatcher<Expr> InnerMatcher
+
Matches the right hand side of binary operator expressions.
 
 Example matches b (matcher = binaryOperator(hasRHS()))
   a || b
@@ -6438,6 +6504,40 @@
 
+Matcher<CXXRewrittenBinaryOperator>hasEitherOperandMatcher<Expr> InnerMatcher +
Matches if either the left hand side or the right hand side of a
+binary operator matches.
+
+ + +Matcher<CXXRewrittenBinaryOperator>hasLHSMatcher<Expr> InnerMatcher +
Matches the left hand side of binary operator expressions.
+
+Example matches a (matcher = binaryOperator(hasLHS()))
+  a || b
+
+ + +Matcher<CXXRewrittenBinaryOperator>hasOperandsMatcher<Expr> Matcher1, Matcher<Expr> Matcher2 +
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
+
+ + +Matcher<CXXRewrittenBinaryOperator>hasRHSMatcher<Expr> InnerMatcher +
Matches the right hand side of binary operator expressions.
+
+Example matches b (matcher = binaryOperator(hasRHS()))
+  a || b
+
+ + Matcher<CXXUnresolvedConstructExpr>hasAnyArgumentMatcher<Expr> InnerMatcher
Matches any argument of a call expression or a constructor call
 expression, or an ObjC-message-send expression.
@@ -8108,13 +8208,16 @@
 
 The code
   var1 != var2;
-might be represented in the clang AST as a binaryOperator or a
-cxxOperatorCallExpr, depending on
+might be represented in the clang AST as a binaryOperator, a
+cxxOperatorCallExpr or a cxxRewrittenBinaryOperator, depending on
 
 * whether the types of var1 and var2 are fundamental (binaryOperator) or at
   least one is a class type (cxxOperatorCallExpr)
 * whether the code appears in a template declaration, if at least one of the
   vars is a dependent-type (binaryOperator)
+* whether the code relies on a rewritten binary operator, such as a
+spaceship operator or an inverted equality operator
+(cxxRewrittenBinaryOperator)
 
 This matcher elides details in places where the matchers for the nodes are
 compatible.
@@ -8142,6 +8245,31 @@
      1 != 2;
      T() != S();
   }
+  struct HasOpEq
+  {
+      bool operator==(const HasOpEq &) const;
+  };
+
+  void inverse()
+  {
+      HasOpEq s1;
+      HasOpEq s2;
+      if (s1 != s2)
+          return;
+  }
+
+  struct HasSpaceship
+  {
+      bool operator<=>(const HasOpEq &) const;
+  };
+
+  void use_spaceship()
+  {
+      HasSpaceship s1;
+      HasSpaceship s2;
+      if (s1 != s2)
+          return;
+  }
 
diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h --- a/clang/include/clang/AST/ASTNodeTraverser.h +++ b/clang/include/clang/AST/ASTNodeTraverser.h @@ -145,7 +145,8 @@ return; if (Traversal == TK_IgnoreUnlessSpelledInSource && - isa(S)) + isa(S)) return; for (const Stmt *SubStmt : S->children()) @@ -746,6 +747,15 @@ } } + void VisitCXXRewrittenBinaryOperator(const CXXRewrittenBinaryOperator *Node) { + if (Traversal == TK_IgnoreUnlessSpelledInSource) { + Visit(Node->getLHS()); + Visit(Node->getRHS()); + } else { + ConstStmtVisitor::VisitCXXRewrittenBinaryOperator(Node); + } + } + void VisitExpressionTemplateArgument(const TemplateArgument &TA) { Visit(TA.getAsExpr()); } diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -320,6 +320,16 @@ bool isReversed() const { return CXXRewrittenBinaryOperatorBits.IsReversed; } BinaryOperatorKind getOperator() const { return getDecomposedForm().Opcode; } + BinaryOperatorKind getOpcode() const { return getOperator(); } + static StringRef getOpcodeStr(BinaryOperatorKind Op) { + return BinaryOperator::getOpcodeStr(Op); + } + StringRef getOpcodeStr() const { + return BinaryOperator::getOpcodeStr(getOpcode()); + } + bool isComparisonOp() const { return true; } + bool isAssignmentOp() const { return false; } + const Expr *getLHS() const { return getDecomposedForm().LHS; } const Expr *getRHS() const { return getDecomposedForm().RHS; } diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -1978,6 +1978,27 @@ extern const internal::VariadicDynCastAllOfMatcher cxxOperatorCallExpr; +/// Matches replacement binary operators +/// +/// Example matches x() +/// \code +/// #include +/// 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 + cxxRewrittenBinaryOperator; + /// Matches expressions. /// /// Example matches x() @@ -2742,13 +2763,16 @@ /// \code /// var1 != var2; /// \endcode -/// might be represented in the clang AST as a binaryOperator or a -/// cxxOperatorCallExpr, depending on +/// might be represented in the clang AST as a binaryOperator, a +/// cxxOperatorCallExpr or a cxxRewrittenBinaryOperator, depending on /// /// * whether the types of var1 and var2 are fundamental (binaryOperator) or at /// least one is a class type (cxxOperatorCallExpr) /// * whether the code appears in a template declaration, if at least one of the /// vars is a dependent-type (binaryOperator) +/// * whether the code relies on a rewritten binary operator, such as a +/// spaceship operator or an inverted equality operator +/// (cxxRewrittenBinaryOperator) /// /// This matcher elides details in places where the matchers for the nodes are /// compatible. @@ -2779,11 +2803,38 @@ /// 1 != 2; /// T() != S(); /// } +/// struct HasOpEq +/// { +/// bool operator==(const HasOpEq &) const; +/// }; +/// +/// void inverse() +/// { +/// HasOpEq s1; +/// HasOpEq s2; +/// if (s1 != s2) +/// return; +/// } +/// +/// struct HasSpaceship +/// { +/// bool operator<=>(const HasOpEq &) const; +/// }; +/// +/// void use_spaceship() +/// { +/// HasSpaceship s1; +/// HasSpaceship s2; +/// if (s1 != s2) +/// return; +/// } /// \endcode template internal::BindableMatcher binaryOperation(InnerMatcherType &&... Matchers) { - return mapAnyOf(binaryOperator, cxxOperatorCallExpr).with(Matchers...); + return mapAnyOf(binaryOperator, cxxOperatorCallExpr, + cxxRewrittenBinaryOperator) + .with(Matchers...); } /// Matches unary expressions that have a specific type of argument. @@ -5252,11 +5303,11 @@ /// \code /// !(a || b) /// \endcode -AST_POLYMORPHIC_MATCHER_P(hasOperatorName, - AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, - CXXOperatorCallExpr, - UnaryOperator), - std::string, Name) { +AST_POLYMORPHIC_MATCHER_P( + hasOperatorName, + AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr, + CXXRewrittenBinaryOperator, UnaryOperator), + std::string, Name) { return internal::nodeHasOperatorName(Node, Name); } @@ -5270,6 +5321,7 @@ internal::PolymorphicMatcherWithParam1< internal::HasAnyOperatorNameMatcher, std::vector, AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr, + CXXRewrittenBinaryOperator, UnaryOperator)>, StringRef, internal::hasAnyOperatorNameFunc> hasAnyOperatorName; @@ -5288,9 +5340,10 @@ /// struct S { S& operator=(const S&); }; /// void x() { S s1, s2; s1 = s2; } /// \endcode -AST_POLYMORPHIC_MATCHER(isAssignmentOperator, - AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, - CXXOperatorCallExpr)) { +AST_POLYMORPHIC_MATCHER( + isAssignmentOperator, + AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr, + CXXRewrittenBinaryOperator)) { return Node.isAssignmentOp(); } @@ -5308,9 +5361,10 @@ /// struct S { bool operator<(const S& other); }; /// void x(S s1, S s2) { bool b1 = s1 < s2; } /// \endcode -AST_POLYMORPHIC_MATCHER(isComparisonOperator, - AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, - CXXOperatorCallExpr)) { +AST_POLYMORPHIC_MATCHER( + isComparisonOperator, + AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr, + CXXRewrittenBinaryOperator)) { return Node.isComparisonOp(); } @@ -5321,9 +5375,9 @@ /// a || b /// \endcode AST_POLYMORPHIC_MATCHER_P(hasLHS, - AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, - CXXOperatorCallExpr, - ArraySubscriptExpr), + AST_POLYMORPHIC_SUPPORTED_TYPES( + BinaryOperator, CXXOperatorCallExpr, + CXXRewrittenBinaryOperator, ArraySubscriptExpr), internal::Matcher, InnerMatcher) { const Expr *LeftHandSide = internal::getLHS(Node); return (LeftHandSide != nullptr && @@ -5337,9 +5391,9 @@ /// a || b /// \endcode AST_POLYMORPHIC_MATCHER_P(hasRHS, - AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, - CXXOperatorCallExpr, - ArraySubscriptExpr), + AST_POLYMORPHIC_SUPPORTED_TYPES( + BinaryOperator, CXXOperatorCallExpr, + CXXRewrittenBinaryOperator, ArraySubscriptExpr), internal::Matcher, InnerMatcher) { const Expr *RightHandSide = internal::getRHS(Node); return (RightHandSide != nullptr && @@ -5348,10 +5402,11 @@ /// Matches if either the left hand side or the right hand side of a /// binary operator matches. -AST_POLYMORPHIC_MATCHER_P(hasEitherOperand, - AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, - CXXOperatorCallExpr), - internal::Matcher, InnerMatcher) { +AST_POLYMORPHIC_MATCHER_P( + hasEitherOperand, + AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr, + CXXRewrittenBinaryOperator), + internal::Matcher, InnerMatcher) { return internal::VariadicDynCastAllOfMatcher()( anyOf(hasLHS(InnerMatcher), hasRHS(InnerMatcher))) .matches(Node, Finder, Builder); @@ -5367,11 +5422,11 @@ /// 1 + 1 // No match /// 2 + 2 // No match /// \endcode -AST_POLYMORPHIC_MATCHER_P2(hasOperands, - AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, - CXXOperatorCallExpr), - internal::Matcher, Matcher1, - internal::Matcher, Matcher2) { +AST_POLYMORPHIC_MATCHER_P2( + hasOperands, + AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr, + CXXRewrittenBinaryOperator), + internal::Matcher, Matcher1, internal::Matcher, Matcher2) { return internal::VariadicDynCastAllOfMatcher()( anyOf(allOf(hasLHS(Matcher1), hasRHS(Matcher2)), allOf(hasLHS(Matcher2), hasRHS(Matcher1)))) diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h --- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -2091,9 +2091,10 @@ class HasAnyOperatorNameMatcher : public SingleNodeMatcherInterface { static_assert(std::is_same::value || std::is_same::value || + std::is_same::value || std::is_same::value, - "Matcher only supports `BinaryOperator`, `UnaryOperator` and " - "`CXXOperatorCallExpr`"); + "Matcher only supports `BinaryOperator`, `UnaryOperator`, " + "`CXXOperatorCallExpr` and `CXXRewrittenBinaryOperator`"); static_assert(std::is_same>::value, "Matcher ArgT must be std::vector"); @@ -2117,6 +2118,9 @@ static Optional getOpName(const BinaryOperator &Node) { return Node.getOpcodeStr(); } + static StringRef getOpName(const CXXRewrittenBinaryOperator &Node) { + return Node.getOpcodeStr(); + } static Optional getOpName(const CXXOperatorCallExpr &Node) { auto optBinaryOpcode = equivalentBinaryOperator(Node); if (!optBinaryOpcode) { @@ -2152,7 +2156,8 @@ using HasOpNameMatcher = PolymorphicMatcherWithParam1< HasAnyOperatorNameMatcher, std::vector, - void(TypeList)>; + void(TypeList)>; HasOpNameMatcher hasAnyOperatorNameFunc(ArrayRef NameRefs); diff --git a/clang/lib/ASTMatchers/ASTMatchFinder.cpp b/clang/lib/ASTMatchers/ASTMatchFinder.cpp --- a/clang/lib/ASTMatchers/ASTMatchFinder.cpp +++ b/clang/lib/ASTMatchers/ASTMatchFinder.cpp @@ -250,6 +250,18 @@ return false; return VisitorBase::TraverseStmt(Node->getBody()); } + bool TraverseCXXRewrittenBinaryOperator(CXXRewrittenBinaryOperator *Node) { + if (!Finder->isTraversalIgnoringImplicitNodes()) + return VisitorBase::TraverseCXXRewrittenBinaryOperator(Node); + if (!Node) + return true; + ScopedIncrement ScopedDepth(&CurrentDepth); + + auto DCF = Node->getDecomposedForm(); + if (!match(*DCF.LHS) || !match(*DCF.RHS)) + return false; + return true; + } bool TraverseLambdaExpr(LambdaExpr *Node) { if (!Finder->isTraversalIgnoringImplicitNodes()) return VisitorBase::TraverseLambdaExpr(Node); @@ -489,6 +501,19 @@ } } return true; + } else if (auto *RBO = dyn_cast(S)) { + { + ASTNodeNotAsIsSourceScope RAII(this, true); + TraverseStmt(const_cast(RBO->getLHS())); + TraverseStmt(const_cast(RBO->getRHS())); + } + { + ASTNodeNotSpelledInSourceScope RAII(this, true); + for (auto *SubStmt : RBO->children()) { + TraverseStmt(SubStmt); + } + } + return true; } else if (auto *LE = dyn_cast(S)) { for (auto I : llvm::zip(LE->captures(), LE->capture_inits())) { auto C = std::get<0>(I); diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp --- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp +++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp @@ -872,6 +872,8 @@ cxxDefaultArgExpr; const internal::VariadicDynCastAllOfMatcher cxxOperatorCallExpr; +const internal::VariadicDynCastAllOfMatcher + cxxRewrittenBinaryOperator; const internal::VariadicDynCastAllOfMatcher expr; const internal::VariadicDynCastAllOfMatcher declRefExpr; const internal::VariadicDynCastAllOfMatcher objcIvarRefExpr; diff --git a/clang/unittests/AST/ASTTraverserTest.cpp b/clang/unittests/AST/ASTTraverserTest.cpp --- a/clang/unittests/AST/ASTTraverserTest.cpp +++ b/clang/unittests/AST/ASTTraverserTest.cpp @@ -1733,4 +1733,64 @@ } } +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("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("binop")), + R"cpp( +CXXRewrittenBinaryOperator +|-DeclRefExpr 'hs1' +`-DeclRefExpr 'hs2' +)cpp"); + } +} + } // namespace clang diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp --- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp @@ -662,6 +662,65 @@ hasLHS(declRefExpr(to(varDecl(hasName("s1"))))), hasRHS(declRefExpr(to(varDecl(hasName("s2"))))))))); + Code = R"cpp( +struct HasOpEq +{ + bool operator==(const HasOpEq &) const; +}; + +void inverse() +{ + HasOpEq s1; + HasOpEq s2; + if (s1 != s2) + return; +} + +namespace std { +struct strong_ordering { + int n; + constexpr operator int() const { return n; } + static const strong_ordering equal, greater, less; +}; +constexpr strong_ordering strong_ordering::equal = {0}; +constexpr strong_ordering strong_ordering::greater = {1}; +constexpr strong_ordering strong_ordering::less = {-1}; +} + +struct HasSpaceshipMem { + int a; + constexpr auto operator<=>(const HasSpaceshipMem&) const = default; +}; + +void rewritten() +{ + HasSpaceshipMem s1; + HasSpaceshipMem s2; + if (s1 != s2) + return; +} +)cpp"; + + EXPECT_TRUE(matchesConditionally( + Code, + traverse( + TK_IgnoreUnlessSpelledInSource, + binaryOperation(hasOperatorName("!="), + forFunction(functionDecl(hasName("inverse"))), + hasLHS(declRefExpr(to(varDecl(hasName("s1"))))), + hasRHS(declRefExpr(to(varDecl(hasName("s2"))))))), + true, {"-std=c++20"})); + + EXPECT_TRUE(matchesConditionally( + Code, + traverse( + TK_IgnoreUnlessSpelledInSource, + binaryOperation(hasOperatorName("!="), + forFunction(functionDecl(hasName("rewritten"))), + hasLHS(declRefExpr(to(varDecl(hasName("s1"))))), + hasRHS(declRefExpr(to(varDecl(hasName("s2"))))))), + true, {"-std=c++20"})); + Code = R"cpp( struct HasOpBang { diff --git a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp --- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -3259,6 +3259,527 @@ EXPECT_TRUE(matches(Code, traverse(TK_AsIs, lambdaImplicitCapture))); EXPECT_FALSE(matches( Code, traverse(TK_IgnoreUnlessSpelledInSource, lambdaImplicitCapture))); + + Code = R"cpp( +struct S {}; + +struct HasOpEq +{ + bool operator==(const S& other) + { + return true; + } +}; + +void binop() +{ + HasOpEq s1; + S s2; + if (s1 != s2) + return; +} +)cpp"; + { + auto M = unaryOperator( + hasOperatorName("!"), + has(cxxOperatorCallExpr(hasOverloadedOperatorName("==")))); + EXPECT_TRUE( + matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"})); + EXPECT_FALSE( + matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M), + true, {"-std=c++20"})); + } + { + auto M = declRefExpr(to(varDecl(hasName("s1")))); + EXPECT_TRUE( + matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"})); + EXPECT_TRUE( + matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M), + true, {"-std=c++20"})); + } + { + auto M = cxxOperatorCallExpr(hasOverloadedOperatorName("==")); + EXPECT_TRUE( + matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"})); + EXPECT_FALSE( + matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M), + true, {"-std=c++20"})); + } + { + auto M = cxxOperatorCallExpr(hasOverloadedOperatorName("!=")); + EXPECT_FALSE( + matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"})); + EXPECT_FALSE( + matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M), + true, {"-std=c++20"})); + } + auto withDescendants = [](StringRef lName, StringRef rName) { + return stmt(hasDescendant(declRefExpr(to(varDecl(hasName(lName))))), + hasDescendant(declRefExpr(to(varDecl(hasName(rName)))))); + }; + { + auto M = cxxRewrittenBinaryOperator(withDescendants("s1", "s2")); + EXPECT_TRUE( + matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"})); + EXPECT_TRUE( + matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M), + true, {"-std=c++20"})); + } + { + auto M = cxxRewrittenBinaryOperator( + has(declRefExpr(to(varDecl(hasName("s1"))))), + has(declRefExpr(to(varDecl(hasName("s2")))))); + EXPECT_FALSE( + matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"})); + EXPECT_TRUE( + matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M), + true, {"-std=c++20"})); + } + { + EXPECT_TRUE(matchesConditionally( + Code, + traverse(TK_AsIs, + cxxRewrittenBinaryOperator( + hasOperatorName("!="), hasAnyOperatorName("<", "!="), + isComparisonOperator(), + hasLHS(ignoringImplicit( + declRefExpr(to(varDecl(hasName("s1")))))), + hasRHS(ignoringImplicit( + declRefExpr(to(varDecl(hasName("s2")))))), + hasEitherOperand(ignoringImplicit( + declRefExpr(to(varDecl(hasName("s2")))))), + hasOperands(ignoringImplicit( + declRefExpr(to(varDecl(hasName("s1"))))), + ignoringImplicit(declRefExpr( + to(varDecl(hasName("s2")))))))), + true, {"-std=c++20"})); + EXPECT_TRUE(matchesConditionally( + Code, + traverse(TK_IgnoreUnlessSpelledInSource, + cxxRewrittenBinaryOperator( + hasOperatorName("!="), hasAnyOperatorName("<", "!="), + isComparisonOperator(), + hasLHS(declRefExpr(to(varDecl(hasName("s1"))))), + hasRHS(declRefExpr(to(varDecl(hasName("s2"))))), + hasEitherOperand(declRefExpr(to(varDecl(hasName("s2"))))), + hasOperands(declRefExpr(to(varDecl(hasName("s1")))), + declRefExpr(to(varDecl(hasName("s2"))))))), + true, {"-std=c++20"})); + } + + Code = R"cpp( +namespace std { +struct strong_ordering { + int n; + constexpr operator int() const { return n; } + static const strong_ordering equal, greater, less; +}; +constexpr strong_ordering strong_ordering::equal = {0}; +constexpr strong_ordering strong_ordering::greater = {1}; +constexpr strong_ordering strong_ordering::less = {-1}; +} + +struct HasSpaceshipMem { + int a; + constexpr auto operator<=>(const HasSpaceshipMem&) const = default; +}; + +void binop() +{ + HasSpaceshipMem hs1, hs2; + if (hs1 == hs2) + return; + + HasSpaceshipMem hs3, hs4; + if (hs3 != hs4) + return; + + HasSpaceshipMem hs5, hs6; + if (hs5 < hs6) + return; + + HasSpaceshipMem hs7, hs8; + if (hs7 > hs8) + return; + + HasSpaceshipMem hs9, hs10; + if (hs9 <= hs10) + return; + + HasSpaceshipMem hs11, hs12; + if (hs11 >= hs12) + return; +} +)cpp"; + auto withArgs = [](StringRef lName, StringRef rName) { + return cxxOperatorCallExpr( + hasArgument(0, declRefExpr(to(varDecl(hasName(lName))))), + hasArgument(1, declRefExpr(to(varDecl(hasName(rName)))))); + }; + { + auto M = ifStmt(hasCondition(cxxOperatorCallExpr( + hasOverloadedOperatorName("=="), withArgs("hs1", "hs2")))); + EXPECT_TRUE( + matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"})); + EXPECT_TRUE( + matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M), + true, {"-std=c++20"})); + } + { + auto M = + unaryOperator(hasOperatorName("!"), + has(cxxOperatorCallExpr(hasOverloadedOperatorName("=="), + withArgs("hs3", "hs4")))); + EXPECT_TRUE( + matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"})); + EXPECT_FALSE( + matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M), + true, {"-std=c++20"})); + } + { + auto M = + unaryOperator(hasOperatorName("!"), + has(cxxOperatorCallExpr(hasOverloadedOperatorName("=="), + withArgs("hs3", "hs4")))); + EXPECT_TRUE( + matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"})); + EXPECT_FALSE( + matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M), + true, {"-std=c++20"})); + } + { + auto M = binaryOperator( + hasOperatorName("<"), + hasLHS(hasDescendant(cxxOperatorCallExpr( + hasOverloadedOperatorName("<=>"), withArgs("hs5", "hs6")))), + hasRHS(integerLiteral(equals(0)))); + EXPECT_TRUE( + matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"})); + EXPECT_FALSE( + matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M), + true, {"-std=c++20"})); + } + { + auto M = cxxRewrittenBinaryOperator(withDescendants("hs3", "hs4")); + EXPECT_TRUE( + matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"})); + EXPECT_TRUE( + matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M), + true, {"-std=c++20"})); + } + { + auto M = declRefExpr(to(varDecl(hasName("hs3")))); + EXPECT_TRUE( + matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"})); + EXPECT_TRUE( + matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M), + true, {"-std=c++20"})); + } + { + auto M = cxxRewrittenBinaryOperator(has( + unaryOperator(hasOperatorName("!"), withDescendants("hs3", "hs4")))); + EXPECT_TRUE( + matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"})); + EXPECT_FALSE( + matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M), + true, {"-std=c++20"})); + } + { + auto M = cxxRewrittenBinaryOperator( + has(declRefExpr(to(varDecl(hasName("hs3"))))), + has(declRefExpr(to(varDecl(hasName("hs4")))))); + EXPECT_FALSE( + matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"})); + EXPECT_TRUE( + matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M), + true, {"-std=c++20"})); + } + { + EXPECT_TRUE(matchesConditionally( + Code, + traverse(TK_AsIs, + cxxRewrittenBinaryOperator( + hasOperatorName("!="), hasAnyOperatorName("<", "!="), + isComparisonOperator(), + hasLHS(ignoringImplicit( + declRefExpr(to(varDecl(hasName("hs3")))))), + hasRHS(ignoringImplicit( + declRefExpr(to(varDecl(hasName("hs4")))))), + hasEitherOperand(ignoringImplicit( + declRefExpr(to(varDecl(hasName("hs3")))))), + hasOperands(ignoringImplicit( + declRefExpr(to(varDecl(hasName("hs3"))))), + ignoringImplicit(declRefExpr( + to(varDecl(hasName("hs4")))))))), + true, {"-std=c++20"})); + EXPECT_TRUE(matchesConditionally( + Code, + traverse(TK_IgnoreUnlessSpelledInSource, + cxxRewrittenBinaryOperator( + hasOperatorName("!="), hasAnyOperatorName("<", "!="), + isComparisonOperator(), + hasLHS(declRefExpr(to(varDecl(hasName("hs3"))))), + hasRHS(declRefExpr(to(varDecl(hasName("hs4"))))), + hasEitherOperand(declRefExpr(to(varDecl(hasName("hs3"))))), + hasOperands(declRefExpr(to(varDecl(hasName("hs3")))), + declRefExpr(to(varDecl(hasName("hs4"))))))), + true, {"-std=c++20"})); + } + { + EXPECT_TRUE(matchesConditionally( + Code, + traverse(TK_AsIs, + cxxRewrittenBinaryOperator( + hasOperatorName("<"), hasAnyOperatorName("<", "!="), + isComparisonOperator(), + hasLHS(ignoringImplicit( + declRefExpr(to(varDecl(hasName("hs5")))))), + hasRHS(ignoringImplicit( + declRefExpr(to(varDecl(hasName("hs6")))))), + hasEitherOperand(ignoringImplicit( + declRefExpr(to(varDecl(hasName("hs5")))))), + hasOperands(ignoringImplicit( + declRefExpr(to(varDecl(hasName("hs5"))))), + ignoringImplicit(declRefExpr( + to(varDecl(hasName("hs6")))))))), + true, {"-std=c++20"})); + EXPECT_TRUE(matchesConditionally( + Code, + traverse(TK_IgnoreUnlessSpelledInSource, + cxxRewrittenBinaryOperator( + hasOperatorName("<"), hasAnyOperatorName("<", "!="), + isComparisonOperator(), + hasLHS(declRefExpr(to(varDecl(hasName("hs5"))))), + hasRHS(declRefExpr(to(varDecl(hasName("hs6"))))), + hasEitherOperand(declRefExpr(to(varDecl(hasName("hs5"))))), + hasOperands(declRefExpr(to(varDecl(hasName("hs5")))), + declRefExpr(to(varDecl(hasName("hs6"))))))), + true, {"-std=c++20"})); + } + { + EXPECT_TRUE(matchesConditionally( + Code, + traverse(TK_AsIs, + cxxRewrittenBinaryOperator( + hasOperatorName(">"), hasAnyOperatorName("<", ">"), + isComparisonOperator(), + hasLHS(ignoringImplicit( + declRefExpr(to(varDecl(hasName("hs7")))))), + hasRHS(ignoringImplicit( + declRefExpr(to(varDecl(hasName("hs8")))))), + hasEitherOperand(ignoringImplicit( + declRefExpr(to(varDecl(hasName("hs7")))))), + hasOperands(ignoringImplicit( + declRefExpr(to(varDecl(hasName("hs7"))))), + ignoringImplicit(declRefExpr( + to(varDecl(hasName("hs8")))))))), + true, {"-std=c++20"})); + EXPECT_TRUE(matchesConditionally( + Code, + traverse(TK_IgnoreUnlessSpelledInSource, + cxxRewrittenBinaryOperator( + hasOperatorName(">"), hasAnyOperatorName("<", ">"), + isComparisonOperator(), + hasLHS(declRefExpr(to(varDecl(hasName("hs7"))))), + hasRHS(declRefExpr(to(varDecl(hasName("hs8"))))), + hasEitherOperand(declRefExpr(to(varDecl(hasName("hs7"))))), + hasOperands(declRefExpr(to(varDecl(hasName("hs7")))), + declRefExpr(to(varDecl(hasName("hs8"))))))), + true, {"-std=c++20"})); + } + { + EXPECT_TRUE(matchesConditionally( + Code, + traverse(TK_AsIs, + cxxRewrittenBinaryOperator( + hasOperatorName("<="), hasAnyOperatorName("<", "<="), + isComparisonOperator(), + hasLHS(ignoringImplicit( + declRefExpr(to(varDecl(hasName("hs9")))))), + hasRHS(ignoringImplicit( + declRefExpr(to(varDecl(hasName("hs10")))))), + hasEitherOperand(ignoringImplicit( + declRefExpr(to(varDecl(hasName("hs9")))))), + hasOperands(ignoringImplicit( + declRefExpr(to(varDecl(hasName("hs9"))))), + ignoringImplicit(declRefExpr( + to(varDecl(hasName("hs10")))))))), + true, {"-std=c++20"})); + EXPECT_TRUE(matchesConditionally( + Code, + traverse(TK_IgnoreUnlessSpelledInSource, + cxxRewrittenBinaryOperator( + hasOperatorName("<="), hasAnyOperatorName("<", "<="), + isComparisonOperator(), + hasLHS(declRefExpr(to(varDecl(hasName("hs9"))))), + hasRHS(declRefExpr(to(varDecl(hasName("hs10"))))), + hasEitherOperand(declRefExpr(to(varDecl(hasName("hs9"))))), + hasOperands(declRefExpr(to(varDecl(hasName("hs9")))), + declRefExpr(to(varDecl(hasName("hs10"))))))), + true, {"-std=c++20"})); + } + { + EXPECT_TRUE(matchesConditionally( + Code, + traverse(TK_AsIs, + cxxRewrittenBinaryOperator( + hasOperatorName(">="), hasAnyOperatorName("<", ">="), + isComparisonOperator(), + hasLHS(ignoringImplicit( + declRefExpr(to(varDecl(hasName("hs11")))))), + hasRHS(ignoringImplicit( + declRefExpr(to(varDecl(hasName("hs12")))))), + hasEitherOperand(ignoringImplicit( + declRefExpr(to(varDecl(hasName("hs11")))))), + hasOperands(ignoringImplicit( + declRefExpr(to(varDecl(hasName("hs11"))))), + ignoringImplicit(declRefExpr( + to(varDecl(hasName("hs12")))))))), + true, {"-std=c++20"})); + EXPECT_TRUE(matchesConditionally( + Code, + traverse( + TK_IgnoreUnlessSpelledInSource, + cxxRewrittenBinaryOperator( + hasOperatorName(">="), hasAnyOperatorName("<", ">="), + isComparisonOperator(), + hasLHS(declRefExpr(to(varDecl(hasName("hs11"))))), + hasRHS(declRefExpr(to(varDecl(hasName("hs12"))))), + hasEitherOperand(declRefExpr(to(varDecl(hasName("hs11"))))), + hasOperands(declRefExpr(to(varDecl(hasName("hs11")))), + declRefExpr(to(varDecl(hasName("hs12"))))))), + true, {"-std=c++20"})); + } + + Code = R"cpp( +struct S {}; + +struct HasOpEq +{ + bool operator==(const S& other) const + { + return true; + } +}; + +struct HasOpEqMem { + bool operator==(const HasOpEqMem&) const { return true; } +}; + +struct HasOpEqFree { +}; +bool operator==(const HasOpEqFree&, const HasOpEqFree&) { return true; } + +void binop() +{ + { + HasOpEq s1; + S s2; + if (s1 != s2) + return; + } + + { + int i1; + int i2; + if (i1 != i2) + return; + } + + { + HasOpEqMem M1; + HasOpEqMem M2; + if (M1 == M2) + return; + } + + { + HasOpEqFree F1; + HasOpEqFree F2; + if (F1 == F2) + return; + } +} +)cpp"; + { + EXPECT_TRUE(matchesConditionally( + Code, + traverse(TK_AsIs, + binaryOperation( + hasOperatorName("!="), hasAnyOperatorName("<", "!="), + isComparisonOperator(), + hasLHS(ignoringImplicit( + declRefExpr(to(varDecl(hasName("s1")))))), + hasRHS(ignoringImplicit( + declRefExpr(to(varDecl(hasName("s2")))))), + hasEitherOperand(ignoringImplicit( + declRefExpr(to(varDecl(hasName("s2")))))), + hasOperands(ignoringImplicit( + declRefExpr(to(varDecl(hasName("s1"))))), + ignoringImplicit(declRefExpr( + to(varDecl(hasName("s2")))))))), + true, {"-std=c++20"})); + EXPECT_TRUE(matchesConditionally( + Code, + traverse(TK_AsIs, binaryOperation(hasOperatorName("!="), + hasLHS(ignoringImplicit(declRefExpr( + to(varDecl(hasName("i1")))))), + hasRHS(ignoringImplicit(declRefExpr( + to(varDecl(hasName("i2")))))))), + true, {"-std=c++20"})); + EXPECT_TRUE(matchesConditionally( + Code, + traverse(TK_AsIs, binaryOperation(hasOperatorName("=="), + hasLHS(ignoringImplicit(declRefExpr( + to(varDecl(hasName("M1")))))), + hasRHS(ignoringImplicit(declRefExpr( + to(varDecl(hasName("M2")))))))), + true, {"-std=c++20"})); + EXPECT_TRUE(matchesConditionally( + Code, + traverse(TK_AsIs, binaryOperation(hasOperatorName("=="), + hasLHS(ignoringImplicit(declRefExpr( + to(varDecl(hasName("F1")))))), + hasRHS(ignoringImplicit(declRefExpr( + to(varDecl(hasName("F2")))))))), + true, {"-std=c++20"})); + EXPECT_TRUE(matchesConditionally( + Code, + traverse(TK_IgnoreUnlessSpelledInSource, + binaryOperation( + hasOperatorName("!="), hasAnyOperatorName("<", "!="), + isComparisonOperator(), + hasLHS(declRefExpr(to(varDecl(hasName("s1"))))), + hasRHS(declRefExpr(to(varDecl(hasName("s2"))))), + hasEitherOperand(declRefExpr(to(varDecl(hasName("s2"))))), + hasOperands(declRefExpr(to(varDecl(hasName("s1")))), + declRefExpr(to(varDecl(hasName("s2"))))))), + true, {"-std=c++20"})); + EXPECT_TRUE(matchesConditionally( + Code, + traverse( + TK_IgnoreUnlessSpelledInSource, + binaryOperation(hasOperatorName("!="), + hasLHS(declRefExpr(to(varDecl(hasName("i1"))))), + hasRHS(declRefExpr(to(varDecl(hasName("i2"))))))), + true, {"-std=c++20"})); + EXPECT_TRUE(matchesConditionally( + Code, + traverse( + TK_IgnoreUnlessSpelledInSource, + binaryOperation(hasOperatorName("=="), + hasLHS(declRefExpr(to(varDecl(hasName("M1"))))), + hasRHS(declRefExpr(to(varDecl(hasName("M2"))))))), + true, {"-std=c++20"})); + EXPECT_TRUE(matchesConditionally( + Code, + traverse( + TK_IgnoreUnlessSpelledInSource, + binaryOperation(hasOperatorName("=="), + hasLHS(declRefExpr(to(varDecl(hasName("F1"))))), + hasRHS(declRefExpr(to(varDecl(hasName("F2"))))))), + true, {"-std=c++20"})); + } } TEST(IgnoringImpCasts, MatchesImpCasts) {