Skip to content

Commit 07675b0

Browse files
committed
[clang-tidy] Fix false positives in misc-redundant-expression check
Reviewed By: aaron.ballman Differential Revision: https://reviews.llvm.org/D122111
1 parent e22b78d commit 07675b0

File tree

3 files changed

+58
-6
lines changed

3 files changed

+58
-6
lines changed

clang-tools-extra/clang-tidy/misc/RedundantExpressionCheck.cpp

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,7 @@ matchRelationalIntegerConstantExpr(StringRef Id) {
569569
std::string SwapId = (Id + "-swap").str();
570570
std::string NegateId = (Id + "-negate").str();
571571
std::string OverloadId = (Id + "-overload").str();
572+
std::string ConstId = (Id + "-const").str();
572573

573574
const auto RelationalExpr = ignoringParenImpCasts(binaryOperator(
574575
isComparisonOperator(), expr().bind(Id),
@@ -600,7 +601,9 @@ matchRelationalIntegerConstantExpr(StringRef Id) {
600601
cxxOperatorCallExpr(
601602
hasAnyOverloadedOperatorName("==", "!=", "<", "<=", ">", ">="),
602603
// Filter noisy false positives.
603-
unless(isMacro()), unless(isInTemplateInstantiation()))
604+
unless(isMacro()), unless(isInTemplateInstantiation()),
605+
anyOf(hasLHS(ignoringParenImpCasts(integerLiteral().bind(ConstId))),
606+
hasRHS(ignoringParenImpCasts(integerLiteral().bind(ConstId)))))
604607
.bind(OverloadId);
605608

606609
return anyOf(RelationalExpr, CastExpr, NegateRelationalExpr,
@@ -674,16 +677,38 @@ static bool retrieveRelationalIntegerConstantExpr(
674677
if (canOverloadedOperatorArgsBeModified(OverloadedOperatorExpr, false))
675678
return false;
676679

680+
bool IntegerConstantIsFirstArg = false;
681+
677682
if (const auto *Arg = OverloadedOperatorExpr->getArg(1)) {
678683
if (!Arg->isValueDependent() &&
679-
!Arg->isIntegerConstantExpr(*Result.Context))
680-
return false;
681-
}
682-
Symbol = OverloadedOperatorExpr->getArg(0);
684+
!Arg->isIntegerConstantExpr(*Result.Context)) {
685+
IntegerConstantIsFirstArg = true;
686+
if (const auto *Arg = OverloadedOperatorExpr->getArg(0)) {
687+
if (!Arg->isValueDependent() &&
688+
!Arg->isIntegerConstantExpr(*Result.Context))
689+
return false;
690+
} else
691+
return false;
692+
}
693+
} else
694+
return false;
695+
696+
Symbol = OverloadedOperatorExpr->getArg(IntegerConstantIsFirstArg ? 1 : 0);
683697
OperandExpr = OverloadedOperatorExpr;
684698
Opcode = BinaryOperator::getOverloadedOpcode(OverloadedOperatorExpr->getOperator());
685699

686-
return BinaryOperator::isComparisonOp(Opcode);
700+
if (!retrieveIntegerConstantExpr(Result, Id, Value, ConstExpr))
701+
return false;
702+
703+
if (!BinaryOperator::isComparisonOp(Opcode))
704+
return false;
705+
706+
// The call site of this function expects the constant on the RHS,
707+
// so change the opcode accordingly.
708+
if (IntegerConstantIsFirstArg)
709+
Opcode = BinaryOperator::reverseComparisonOp(Opcode);
710+
711+
return true;
687712
} else {
688713
return false;
689714
}

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ Changes in existing checks
116116
<clang-tidy/checks/readability-const-return-type>` when a pure virtual function
117117
overrided has a const return type. Removed the fix for a virtual function.
118118

119+
- Fixed a false positive in :doc:`misc-redundant-expression <clang-tidy/checks/misc-redundant-expression>`
120+
involving overloaded comparison operators.
121+
119122
Removed checks
120123
^^^^^^^^^^^^^^
121124

clang-tools-extra/test/clang-tidy/checkers/misc-redundant-expression.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,11 @@ struct S {
369369
bool operator<=(const S &s, int i) { return s.x <= i; } // not modifying
370370
bool operator>=(const S &s, const int &i) { return s.x >= i; } // not modifying
371371

372+
bool operator==(int i, const S &s) { return s == i; } // not modifying
373+
bool operator<(const int &i, const S &s) { return s > i; } // not modifying
374+
bool operator<=(const int &i, const S &s) { return s >= i; } // not modifying
375+
bool operator>(const int &i, const S &s) { return s < i; } // not modifying
376+
372377
struct S2 {
373378
S2() { x = 1; }
374379
int x;
@@ -452,6 +457,25 @@ int TestLogical(int X, int Y){
452457
// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always false
453458
if (s1 >= 1 || s1 <= 1) return true;
454459
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always true
460+
if (s1 >= 2 && s1 <= 0) return true;
461+
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always false
462+
463+
// Same test as above but with swapped LHS/RHS on one side of the logical operator.
464+
if (1 == s1 && s1 == 1) return true;
465+
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: equivalent expression on both sides of logical operator
466+
if (1 == s1 || s1 != 1) return true;
467+
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always true
468+
if (1 < s1 && s1 < 1) return true;
469+
// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always false
470+
if (1 <= s1 || s1 <= 1) return true;
471+
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always true
472+
if (2 < s1 && 0 > s1) return true;
473+
// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always false
474+
475+
// Test for absence of false positives (issue #54011).
476+
if (s1 == 1 || s1 == 2) return true;
477+
if (s1 > 1 && s1 < 3) return true;
478+
if (s1 >= 2 || s1 <= 0) return true;
455479

456480
// Test for overloaded operators that may modify their params.
457481
S2 s2;

0 commit comments

Comments
 (0)