-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[clang-tidy] Apply DeMorgan to overloaded operator in the 'readability-simplify-boolean-expr' check #164141
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Thank you for submitting a Pull Request (PR) to the LLVM Project! This PR will be automatically labeled and the relevant teams will be notified. If you wish to, you can add reviewers by using the "Reviewers" section on this page. If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers. If you have further questions, they may be answered by the LLVM GitHub User Guide. You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums. |
@llvm/pr-subscribers-clang-tools-extra Author: None (jlallas384) ChangesThis doesn't check whether the negated operator is defined, following the same behavior in other parts of the code that deal with overloaded operators. Fixes #163128 Full diff: https://github.com/llvm/llvm-project/pull/164141.diff 2 Files Affected:
diff --git a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp
index 4184c295b5f0a..4cea2b5030f42 100644
--- a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp
@@ -809,14 +809,14 @@ void SimplifyBooleanExprCheck::replaceWithAssignment(const ASTContext &Context,
Range, Replacement);
}
-/// Swaps a \c BinaryOperator opcode from `&&` to `||` or vice-versa.
+/// Swaps \p BO from `&&` to `||` or vice-versa.
static bool flipDemorganOperator(llvm::SmallVectorImpl<FixItHint> &Output,
- const BinaryOperator *BO) {
- assert(BO->isLogicalOp());
- if (BO->getOperatorLoc().isMacroID())
+ BinaryOperatorKind BO, SourceLocation BOLoc) {
+ assert(BinaryOperator::isLogicalOp(BO));
+ if (BOLoc.isMacroID())
return true;
- Output.push_back(FixItHint::CreateReplacement(
- BO->getOperatorLoc(), BO->getOpcode() == BO_LAnd ? "||" : "&&"));
+ Output.push_back(
+ FixItHint::CreateReplacement(BOLoc, BO == BO_LAnd ? "||" : "&&"));
return false;
}
@@ -829,22 +829,22 @@ static bool flipDemorganSide(SmallVectorImpl<FixItHint> &Fixes,
const ASTContext &Ctx, const Expr *E,
std::optional<BinaryOperatorKind> OuterBO);
-/// Inverts \p BinOp, Removing \p Parens if they exist and are safe to remove.
-/// returns \c true if there is any issue building the Fixes, \c false
-/// otherwise.
-static bool
-flipDemorganBinaryOperator(SmallVectorImpl<FixItHint> &Fixes,
- const ASTContext &Ctx, const BinaryOperator *BinOp,
- std::optional<BinaryOperatorKind> OuterBO,
- const ParenExpr *Parens = nullptr) {
- switch (BinOp->getOpcode()) {
+/// Inverts \p BO, \p LHS, and \p RHS, Removing \p Parens if they exist and are
+/// safe to remove. returns \c true if there is any issue building the Fixes, \c
+/// false otherwise.
+static bool flipDemorganBinaryOperator(
+ SmallVectorImpl<FixItHint> &Fixes, const ASTContext &Ctx,
+ BinaryOperatorKind BO, const Expr *LHS, const Expr *RHS, SourceRange Range,
+ SourceLocation BOLoc, std::optional<BinaryOperatorKind> OuterBO,
+ const ParenExpr *Parens = nullptr) {
+ switch (BO) {
case BO_LAnd:
case BO_LOr: {
// if we have 'a && b' or 'a || b', use demorgan to flip it to '!a || !b'
// or '!a && !b'.
- if (flipDemorganOperator(Fixes, BinOp))
+ if (flipDemorganOperator(Fixes, BO, BOLoc))
return true;
- auto NewOp = getDemorganFlippedOperator(BinOp->getOpcode());
+ auto NewOp = getDemorganFlippedOperator(BO);
if (OuterBO) {
// The inner parens are technically needed in a fix for
// `!(!A1 && !(A2 || A3)) -> (A1 || (A2 && A3))`,
@@ -862,16 +862,15 @@ flipDemorganBinaryOperator(SmallVectorImpl<FixItHint> &Fixes,
}
}
if (*OuterBO == BO_LAnd && NewOp == BO_LOr && !Parens) {
- Fixes.push_back(FixItHint::CreateInsertion(BinOp->getBeginLoc(), "("));
+ Fixes.push_back(FixItHint::CreateInsertion(Range.getBegin(), "("));
Fixes.push_back(FixItHint::CreateInsertion(
- Lexer::getLocForEndOfToken(BinOp->getEndLoc(), 0,
- Ctx.getSourceManager(),
- Ctx.getLangOpts()),
+ Lexer::getLocForEndOfToken(
+ Range.getEnd(), 0, Ctx.getSourceManager(), Ctx.getLangOpts()),
")"));
}
}
- if (flipDemorganSide(Fixes, Ctx, BinOp->getLHS(), NewOp) ||
- flipDemorganSide(Fixes, Ctx, BinOp->getRHS(), NewOp))
+ if (flipDemorganSide(Fixes, Ctx, LHS, NewOp) ||
+ flipDemorganSide(Fixes, Ctx, RHS, NewOp))
return true;
return false;
};
@@ -882,12 +881,11 @@ flipDemorganBinaryOperator(SmallVectorImpl<FixItHint> &Fixes,
case BO_EQ:
case BO_NE:
// For comparison operators, just negate the comparison.
- if (BinOp->getOperatorLoc().isMacroID())
+ if (BOLoc.isMacroID())
return true;
Fixes.push_back(FixItHint::CreateReplacement(
- BinOp->getOperatorLoc(),
- BinaryOperator::getOpcodeStr(
- BinaryOperator::negateComparisonOp(BinOp->getOpcode()))));
+ BOLoc,
+ BinaryOperator::getOpcodeStr(BinaryOperator::negateComparisonOp(BO))));
return false;
default:
// for any other binary operator, just use logical not and wrap in
@@ -897,11 +895,11 @@ flipDemorganBinaryOperator(SmallVectorImpl<FixItHint> &Fixes,
return true;
Fixes.push_back(FixItHint::CreateInsertion(Parens->getBeginLoc(), "!"));
} else {
- if (BinOp->getBeginLoc().isMacroID() || BinOp->getEndLoc().isMacroID())
+ if (Range.getBegin().isMacroID() || Range.getEnd().isMacroID())
return true;
- Fixes.append({FixItHint::CreateInsertion(BinOp->getBeginLoc(), "!("),
+ Fixes.append({FixItHint::CreateInsertion(Range.getBegin(), "!("),
FixItHint::CreateInsertion(
- Lexer::getLocForEndOfToken(BinOp->getEndLoc(), 0,
+ Lexer::getLocForEndOfToken(Range.getEnd(), 0,
Ctx.getSourceManager(),
Ctx.getLangOpts()),
")")});
@@ -911,6 +909,28 @@ flipDemorganBinaryOperator(SmallVectorImpl<FixItHint> &Fixes,
return false;
}
+static bool
+flipDemorganBinaryOperator(SmallVectorImpl<FixItHint> &Fixes,
+ const ASTContext &Ctx, const BinaryOperator *BinOp,
+ std::optional<BinaryOperatorKind> OuterBO,
+ const ParenExpr *Parens = nullptr) {
+ return flipDemorganBinaryOperator(
+ Fixes, Ctx, BinOp->getOpcode(), BinOp->getLHS(), BinOp->getRHS(),
+ BinOp->getSourceRange(), BinOp->getOperatorLoc(), OuterBO, Parens);
+}
+
+static bool
+flipDemorganOverloadedBinaryOperator(SmallVectorImpl<FixItHint> &Fixes,
+ const ASTContext &Ctx,
+ const CXXOperatorCallExpr *OpCall,
+ std::optional<BinaryOperatorKind> OuterBO,
+ const ParenExpr *Parens = nullptr) {
+ auto BO = BinaryOperator::getOverloadedOpcode(OpCall->getOperator());
+ return flipDemorganBinaryOperator(Fixes, Ctx, BO, OpCall->getArg(0),
+ OpCall->getArg(1), OpCall->getSourceRange(),
+ OpCall->getOperatorLoc(), OuterBO, Parens);
+}
+
static bool flipDemorganSide(SmallVectorImpl<FixItHint> &Fixes,
const ASTContext &Ctx, const Expr *E,
std::optional<BinaryOperatorKind> OuterBO) {
@@ -930,6 +950,17 @@ static bool flipDemorganSide(SmallVectorImpl<FixItHint> &Fixes,
return flipDemorganBinaryOperator(Fixes, Ctx, BinOp, OuterBO, Paren);
}
}
+ if (const auto *OpCall = dyn_cast<CXXOperatorCallExpr>(E);
+ OpCall && OpCall->isInfixBinaryOp()) {
+ return flipDemorganOverloadedBinaryOperator(Fixes, Ctx, OpCall, OuterBO);
+ }
+ if (const auto *Paren = dyn_cast<ParenExpr>(E)) {
+ if (const auto *OpCall = dyn_cast<CXXOperatorCallExpr>(E);
+ OpCall && OpCall->isInfixBinaryOp()) {
+ return flipDemorganOverloadedBinaryOperator(Fixes, Ctx, OpCall, OuterBO,
+ Paren);
+ }
+ }
// Fallback case just insert a logical not operator.
if (E->getBeginLoc().isMacroID())
return true;
@@ -991,7 +1022,7 @@ bool SimplifyBooleanExprCheck::reportDeMorgan(const ASTContext &Context,
} else {
Fixes.push_back(FixItHint::CreateRemoval(Outer->getOperatorLoc()));
}
- if (flipDemorganOperator(Fixes, Inner))
+ if (flipDemorganOperator(Fixes, Inner->getOpcode(), Inner->getOperatorLoc()))
return false;
if (flipDemorganSide(Fixes, Context, Inner->getLHS(), NewOpcode) ||
flipDemorganSide(Fixes, Context, Inner->getRHS(), NewOpcode))
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/simplify-boolean-expr-demorgan.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/simplify-boolean-expr-demorgan.cpp
index bab9e17a7775b..b70ea72610b16 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/simplify-boolean-expr-demorgan.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/simplify-boolean-expr-demorgan.cpp
@@ -105,4 +105,16 @@ void foo(bool A1, bool A2, bool A3, bool A4) {
// CHECK-FIXES: X = A1 && A2 && A3;
// CHECK-FIXES-NEXT: X = A1 || (A2 && A3);
// CHECK-FIXES-NEXT: X = A1 && (A2 || A3);
+
+ struct T {
+ bool operator==(const T&) const;
+ bool operator<(const T&) const;
+ };
+ T T1, T2;
+ X = !(T1 == T2 && A1 == A2);
+ X = !(T1 < T2 || (A1 || !A2));
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: boolean expression can be simplified by DeMorgan's theorem
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: boolean expression can be simplified by DeMorgan's theorem
+ // CHECK-FIXES: X = T1 != T2 || A1 != A2;
+ // CHECK-FIXES-NEXT: X = T1 >= T2 && !A1 && A2;
}
|
@llvm/pr-subscribers-clang-tidy Author: None (jlallas384) ChangesThis doesn't check whether the negated operator is defined, following the same behavior in other parts of the code that deal with overloaded operators. Fixes #163128 Full diff: https://github.com/llvm/llvm-project/pull/164141.diff 2 Files Affected:
diff --git a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp
index 4184c295b5f0a..4cea2b5030f42 100644
--- a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp
@@ -809,14 +809,14 @@ void SimplifyBooleanExprCheck::replaceWithAssignment(const ASTContext &Context,
Range, Replacement);
}
-/// Swaps a \c BinaryOperator opcode from `&&` to `||` or vice-versa.
+/// Swaps \p BO from `&&` to `||` or vice-versa.
static bool flipDemorganOperator(llvm::SmallVectorImpl<FixItHint> &Output,
- const BinaryOperator *BO) {
- assert(BO->isLogicalOp());
- if (BO->getOperatorLoc().isMacroID())
+ BinaryOperatorKind BO, SourceLocation BOLoc) {
+ assert(BinaryOperator::isLogicalOp(BO));
+ if (BOLoc.isMacroID())
return true;
- Output.push_back(FixItHint::CreateReplacement(
- BO->getOperatorLoc(), BO->getOpcode() == BO_LAnd ? "||" : "&&"));
+ Output.push_back(
+ FixItHint::CreateReplacement(BOLoc, BO == BO_LAnd ? "||" : "&&"));
return false;
}
@@ -829,22 +829,22 @@ static bool flipDemorganSide(SmallVectorImpl<FixItHint> &Fixes,
const ASTContext &Ctx, const Expr *E,
std::optional<BinaryOperatorKind> OuterBO);
-/// Inverts \p BinOp, Removing \p Parens if they exist and are safe to remove.
-/// returns \c true if there is any issue building the Fixes, \c false
-/// otherwise.
-static bool
-flipDemorganBinaryOperator(SmallVectorImpl<FixItHint> &Fixes,
- const ASTContext &Ctx, const BinaryOperator *BinOp,
- std::optional<BinaryOperatorKind> OuterBO,
- const ParenExpr *Parens = nullptr) {
- switch (BinOp->getOpcode()) {
+/// Inverts \p BO, \p LHS, and \p RHS, Removing \p Parens if they exist and are
+/// safe to remove. returns \c true if there is any issue building the Fixes, \c
+/// false otherwise.
+static bool flipDemorganBinaryOperator(
+ SmallVectorImpl<FixItHint> &Fixes, const ASTContext &Ctx,
+ BinaryOperatorKind BO, const Expr *LHS, const Expr *RHS, SourceRange Range,
+ SourceLocation BOLoc, std::optional<BinaryOperatorKind> OuterBO,
+ const ParenExpr *Parens = nullptr) {
+ switch (BO) {
case BO_LAnd:
case BO_LOr: {
// if we have 'a && b' or 'a || b', use demorgan to flip it to '!a || !b'
// or '!a && !b'.
- if (flipDemorganOperator(Fixes, BinOp))
+ if (flipDemorganOperator(Fixes, BO, BOLoc))
return true;
- auto NewOp = getDemorganFlippedOperator(BinOp->getOpcode());
+ auto NewOp = getDemorganFlippedOperator(BO);
if (OuterBO) {
// The inner parens are technically needed in a fix for
// `!(!A1 && !(A2 || A3)) -> (A1 || (A2 && A3))`,
@@ -862,16 +862,15 @@ flipDemorganBinaryOperator(SmallVectorImpl<FixItHint> &Fixes,
}
}
if (*OuterBO == BO_LAnd && NewOp == BO_LOr && !Parens) {
- Fixes.push_back(FixItHint::CreateInsertion(BinOp->getBeginLoc(), "("));
+ Fixes.push_back(FixItHint::CreateInsertion(Range.getBegin(), "("));
Fixes.push_back(FixItHint::CreateInsertion(
- Lexer::getLocForEndOfToken(BinOp->getEndLoc(), 0,
- Ctx.getSourceManager(),
- Ctx.getLangOpts()),
+ Lexer::getLocForEndOfToken(
+ Range.getEnd(), 0, Ctx.getSourceManager(), Ctx.getLangOpts()),
")"));
}
}
- if (flipDemorganSide(Fixes, Ctx, BinOp->getLHS(), NewOp) ||
- flipDemorganSide(Fixes, Ctx, BinOp->getRHS(), NewOp))
+ if (flipDemorganSide(Fixes, Ctx, LHS, NewOp) ||
+ flipDemorganSide(Fixes, Ctx, RHS, NewOp))
return true;
return false;
};
@@ -882,12 +881,11 @@ flipDemorganBinaryOperator(SmallVectorImpl<FixItHint> &Fixes,
case BO_EQ:
case BO_NE:
// For comparison operators, just negate the comparison.
- if (BinOp->getOperatorLoc().isMacroID())
+ if (BOLoc.isMacroID())
return true;
Fixes.push_back(FixItHint::CreateReplacement(
- BinOp->getOperatorLoc(),
- BinaryOperator::getOpcodeStr(
- BinaryOperator::negateComparisonOp(BinOp->getOpcode()))));
+ BOLoc,
+ BinaryOperator::getOpcodeStr(BinaryOperator::negateComparisonOp(BO))));
return false;
default:
// for any other binary operator, just use logical not and wrap in
@@ -897,11 +895,11 @@ flipDemorganBinaryOperator(SmallVectorImpl<FixItHint> &Fixes,
return true;
Fixes.push_back(FixItHint::CreateInsertion(Parens->getBeginLoc(), "!"));
} else {
- if (BinOp->getBeginLoc().isMacroID() || BinOp->getEndLoc().isMacroID())
+ if (Range.getBegin().isMacroID() || Range.getEnd().isMacroID())
return true;
- Fixes.append({FixItHint::CreateInsertion(BinOp->getBeginLoc(), "!("),
+ Fixes.append({FixItHint::CreateInsertion(Range.getBegin(), "!("),
FixItHint::CreateInsertion(
- Lexer::getLocForEndOfToken(BinOp->getEndLoc(), 0,
+ Lexer::getLocForEndOfToken(Range.getEnd(), 0,
Ctx.getSourceManager(),
Ctx.getLangOpts()),
")")});
@@ -911,6 +909,28 @@ flipDemorganBinaryOperator(SmallVectorImpl<FixItHint> &Fixes,
return false;
}
+static bool
+flipDemorganBinaryOperator(SmallVectorImpl<FixItHint> &Fixes,
+ const ASTContext &Ctx, const BinaryOperator *BinOp,
+ std::optional<BinaryOperatorKind> OuterBO,
+ const ParenExpr *Parens = nullptr) {
+ return flipDemorganBinaryOperator(
+ Fixes, Ctx, BinOp->getOpcode(), BinOp->getLHS(), BinOp->getRHS(),
+ BinOp->getSourceRange(), BinOp->getOperatorLoc(), OuterBO, Parens);
+}
+
+static bool
+flipDemorganOverloadedBinaryOperator(SmallVectorImpl<FixItHint> &Fixes,
+ const ASTContext &Ctx,
+ const CXXOperatorCallExpr *OpCall,
+ std::optional<BinaryOperatorKind> OuterBO,
+ const ParenExpr *Parens = nullptr) {
+ auto BO = BinaryOperator::getOverloadedOpcode(OpCall->getOperator());
+ return flipDemorganBinaryOperator(Fixes, Ctx, BO, OpCall->getArg(0),
+ OpCall->getArg(1), OpCall->getSourceRange(),
+ OpCall->getOperatorLoc(), OuterBO, Parens);
+}
+
static bool flipDemorganSide(SmallVectorImpl<FixItHint> &Fixes,
const ASTContext &Ctx, const Expr *E,
std::optional<BinaryOperatorKind> OuterBO) {
@@ -930,6 +950,17 @@ static bool flipDemorganSide(SmallVectorImpl<FixItHint> &Fixes,
return flipDemorganBinaryOperator(Fixes, Ctx, BinOp, OuterBO, Paren);
}
}
+ if (const auto *OpCall = dyn_cast<CXXOperatorCallExpr>(E);
+ OpCall && OpCall->isInfixBinaryOp()) {
+ return flipDemorganOverloadedBinaryOperator(Fixes, Ctx, OpCall, OuterBO);
+ }
+ if (const auto *Paren = dyn_cast<ParenExpr>(E)) {
+ if (const auto *OpCall = dyn_cast<CXXOperatorCallExpr>(E);
+ OpCall && OpCall->isInfixBinaryOp()) {
+ return flipDemorganOverloadedBinaryOperator(Fixes, Ctx, OpCall, OuterBO,
+ Paren);
+ }
+ }
// Fallback case just insert a logical not operator.
if (E->getBeginLoc().isMacroID())
return true;
@@ -991,7 +1022,7 @@ bool SimplifyBooleanExprCheck::reportDeMorgan(const ASTContext &Context,
} else {
Fixes.push_back(FixItHint::CreateRemoval(Outer->getOperatorLoc()));
}
- if (flipDemorganOperator(Fixes, Inner))
+ if (flipDemorganOperator(Fixes, Inner->getOpcode(), Inner->getOperatorLoc()))
return false;
if (flipDemorganSide(Fixes, Context, Inner->getLHS(), NewOpcode) ||
flipDemorganSide(Fixes, Context, Inner->getRHS(), NewOpcode))
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/simplify-boolean-expr-demorgan.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/simplify-boolean-expr-demorgan.cpp
index bab9e17a7775b..b70ea72610b16 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/simplify-boolean-expr-demorgan.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/simplify-boolean-expr-demorgan.cpp
@@ -105,4 +105,16 @@ void foo(bool A1, bool A2, bool A3, bool A4) {
// CHECK-FIXES: X = A1 && A2 && A3;
// CHECK-FIXES-NEXT: X = A1 || (A2 && A3);
// CHECK-FIXES-NEXT: X = A1 && (A2 || A3);
+
+ struct T {
+ bool operator==(const T&) const;
+ bool operator<(const T&) const;
+ };
+ T T1, T2;
+ X = !(T1 == T2 && A1 == A2);
+ X = !(T1 < T2 || (A1 || !A2));
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: boolean expression can be simplified by DeMorgan's theorem
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: boolean expression can be simplified by DeMorgan's theorem
+ // CHECK-FIXES: X = T1 != T2 || A1 != A2;
+ // CHECK-FIXES-NEXT: X = T1 >= T2 && !A1 && A2;
}
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your patch! Please add a release note entry to "clang-tools-extra/docs/ReleaseNotes.rst".
clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp
Outdated
Show resolved
Hide resolved
…y-simplify-boolean-expr' check This doesn't check whether the negated operator is defined, following the same behavior in other parts of the code that deal with overloaded operators. Fixes llvm#163128
25e48fa
to
9e440a1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For operator==
LGTM.
For operator>, <=
, that is not true, when then overloaded operation provide the partial order sema, when the check will cause the wrong result.
e.g.
#include <compare>
#include <iostream>
struct A {
int v;
bool isValid;
std::partial_ordering operator<=>(const A &other) const {
if (!isValid || !other.isValid) {
return std::partial_ordering::unordered;
}
if (v < other.v) {
return std::partial_ordering::less;
} else if (v > other.v) {
return std::partial_ordering::greater;
} else {
return std::partial_ordering::equivalent;
}
}
};
int main() {
A a{5, true};
A b{0, false};
std::cout << "a > b: " << (a > b) << std::endl;
std::cout << "a <= b: " << (a <= b) << std::endl;
return 0;
}
The result will be
a > b: 0
a <= b: 0
This doesn't check whether the negated operator is defined, following the same behavior in other parts of the code that deal with overloaded operators.
Fixes #163128