diff --git a/clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp b/clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp --- a/clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp @@ -567,7 +567,7 @@ std::string OverloadId = (Id + "-overload").str(); const auto RelationalExpr = ignoringParenImpCasts(binaryOperator( - isComparisonOperator(), expr().bind(Id), + matchers::isComparisonOperator(), expr().bind(Id), anyOf(allOf(hasLHS(matchSymbolicExpr(Id)), hasRHS(matchIntegerConstantExpr(Id))), allOf(hasLHS(matchIntegerConstantExpr(Id)), @@ -943,7 +943,7 @@ const auto SymRight = matchSymbolicExpr("rhs"); // Match expressions like: x 0xFF == 0xF00. - Finder->addMatcher(binaryOperator(isComparisonOperator(), + Finder->addMatcher(binaryOperator(matchers::isComparisonOperator(), hasEitherOperand(BinOpCstLeft), hasEitherOperand(CstRight)) .bind("binop-const-compare-to-const"), @@ -951,14 +951,14 @@ // Match expressions like: x 0xFF == x. Finder->addMatcher( - binaryOperator(isComparisonOperator(), + binaryOperator(matchers::isComparisonOperator(), anyOf(allOf(hasLHS(BinOpCstLeft), hasRHS(SymRight)), allOf(hasLHS(SymRight), hasRHS(BinOpCstLeft)))) .bind("binop-const-compare-to-sym"), this); // Match expressions like: x 10 == x 12. - Finder->addMatcher(binaryOperator(isComparisonOperator(), + Finder->addMatcher(binaryOperator(matchers::isComparisonOperator(), hasLHS(BinOpCstLeft), hasRHS(BinOpCstRight), // Already reported as redundant. unless(operandsAreEquivalent())) diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -2157,7 +2157,21 @@ Example 2: matches s1 = s2 (matcher = cxxOperatorCallExpr(isAssignmentOperator())) struct S { S& operator=(const S&); }; - void x() { S s1, s2; s1 = s2; }) + void x() { S s1, s2; s1 = s2; } + + + +Matcher<BinaryOperator>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; }
 
@@ -2616,7 +2630,21 @@ Example 2: matches s1 = s2 (matcher = cxxOperatorCallExpr(isAssignmentOperator())) struct S { S& operator=(const S&); }; - void x() { S s1, s2; s1 = s2; }) + void x() { S s1, s2; s1 = s2; } + + + +Matcher<CXXOperatorCallExpr>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; }
 
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 @@ -118,6 +118,22 @@ } bool isAssignmentOp() const { return isAssignmentOp(getOperator()); } + static bool isComparisonOp(OverloadedOperatorKind Opc) { + switch (Opc) { + case OO_EqualEqual: + case OO_ExclaimEqual: + case OO_Greater: + case OO_GreaterEqual: + case OO_Less: + case OO_LessEqual: + case OO_Spaceship: + return true; + default: + return false; + } + } + bool isComparisonOp() const { return isComparisonOp(getOperator()); } + /// Is this written as an infix binary operator? bool isInfixBinaryOp() const; 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 @@ -4783,7 +4783,7 @@ /// (matcher = cxxOperatorCallExpr(isAssignmentOperator())) /// \code /// struct S { S& operator=(const S&); }; -/// void x() { S s1, s2; s1 = s2; }) +/// void x() { S s1, s2; s1 = s2; } /// \endcode AST_POLYMORPHIC_MATCHER(isAssignmentOperator, AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, @@ -4791,6 +4791,26 @@ return Node.isAssignmentOp(); } +/// Matches comparison operators. +/// +/// Example 1: matches a == b (matcher = binaryOperator(isComparisonOperator())) +/// \code +/// if (a == b) +/// a += b; +/// \endcode +/// +/// Example 2: matches s1 < s2 +/// (matcher = cxxOperatorCallExpr(isComparisonOperator())) +/// \code +/// 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)) { + return Node.isComparisonOp(); +} + /// Matches the left hand side of binary operator expressions. /// /// Example matches a (matcher = binaryOperator(hasLHS())) diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp --- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -358,6 +358,7 @@ REGISTER_MATCHER(isClass); REGISTER_MATCHER(isClassMessage); REGISTER_MATCHER(isClassMethod); + REGISTER_MATCHER(isComparisonOperator); REGISTER_MATCHER(isConst); REGISTER_MATCHER(isConstQualified); REGISTER_MATCHER(isConstexpr); 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 @@ -2689,6 +2689,20 @@ notMatches("void x() { int a; if(a == 0) return; }", BinAsgmtOperator)); } +TEST(IsComparisonOperator, Basic) { + StatementMatcher BinCompOperator = binaryOperator(isComparisonOperator()); + StatementMatcher CXXCompOperator = + cxxOperatorCallExpr(isComparisonOperator()); + + EXPECT_TRUE(matches("void x() { int a; a == 1; }", BinCompOperator)); + EXPECT_TRUE(matches("void x() { int a; a > 2; }", BinCompOperator)); + EXPECT_TRUE(matches("struct S { bool operator==(const S&); };" + "void x() { S s1, s2; bool b1 = s1 == s2; }", + CXXCompOperator)); + EXPECT_TRUE( + notMatches("void x() { int a; if(a = 0) return; }", BinCompOperator)); +} + TEST(HasInit, Basic) { EXPECT_TRUE( matches("int x{0};",