Skip to content

Commit cce7951

Browse files
committed
[clang-tidy] Reduce false positives for bugprone-infinite-loop with dependent expressions
Fixes #51423. Reviewed By: aaron.ballman Differential Revision: https://reviews.llvm.org/D113499
1 parent 3659780 commit cce7951

File tree

3 files changed

+56
-5
lines changed

3 files changed

+56
-5
lines changed

clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,32 @@ static std::string getCondVarNames(const Stmt *Cond) {
117117
return Result;
118118
}
119119

120-
static bool isKnownFalse(const Expr &Cond, const ASTContext &Ctx) {
121-
if (Cond.isValueDependent())
120+
static bool isKnownToHaveValue(const Expr &Cond, const ASTContext &Ctx,
121+
bool ExpectedValue) {
122+
if (Cond.isValueDependent()) {
123+
if (const auto *BinOp = dyn_cast<BinaryOperator>(&Cond)) {
124+
// Conjunctions (disjunctions) can still be handled if at least one
125+
// conjunct (disjunct) is known to be false (true).
126+
if (!ExpectedValue && BinOp->getOpcode() == BO_LAnd)
127+
return isKnownToHaveValue(*BinOp->getLHS(), Ctx, false) ||
128+
isKnownToHaveValue(*BinOp->getRHS(), Ctx, false);
129+
if (ExpectedValue && BinOp->getOpcode() == BO_LOr)
130+
return isKnownToHaveValue(*BinOp->getLHS(), Ctx, true) ||
131+
isKnownToHaveValue(*BinOp->getRHS(), Ctx, true);
132+
if (BinOp->getOpcode() == BO_Comma)
133+
return isKnownToHaveValue(*BinOp->getRHS(), Ctx, ExpectedValue);
134+
} else if (const auto *UnOp = dyn_cast<UnaryOperator>(&Cond)) {
135+
if (UnOp->getOpcode() == UO_LNot)
136+
return isKnownToHaveValue(*UnOp->getSubExpr(), Ctx, !ExpectedValue);
137+
} else if (const auto *Paren = dyn_cast<ParenExpr>(&Cond))
138+
return isKnownToHaveValue(*Paren->getSubExpr(), Ctx, ExpectedValue);
139+
else if (const auto *ImplCast = dyn_cast<ImplicitCastExpr>(&Cond))
140+
return isKnownToHaveValue(*ImplCast->getSubExpr(), Ctx, ExpectedValue);
122141
return false;
142+
}
123143
bool Result = false;
124144
if (Cond.EvaluateAsBooleanCondition(Result, Ctx))
125-
return !Result;
145+
return Result == ExpectedValue;
126146
return false;
127147
}
128148

@@ -144,7 +164,7 @@ void InfiniteLoopCheck::check(const MatchFinder::MatchResult &Result) {
144164
const auto *LoopStmt = Result.Nodes.getNodeAs<Stmt>("loop-stmt");
145165
const auto *Func = Result.Nodes.getNodeAs<Decl>("func");
146166

147-
if (isKnownFalse(*Cond, *Result.Context))
167+
if (isKnownToHaveValue(*Cond, *Result.Context, false))
148168
return;
149169

150170
bool ShouldHaveConditionVariables = true;

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,9 @@ Changes in existing checks
176176
- Fixed incorrect suggestions for :doc:`readability-container-size-empty
177177
<clang-tidy/checks/readability-container-size-empty>` when smart pointers are involved.
178178

179+
- Fixed some false positives in :doc:`bugprone-infinite-loop
180+
<clang-tidy/checks/bugprone-infinite-loop>` involving dependent expressions.
181+
179182
Removed checks
180183
^^^^^^^^^^^^^^
181184

clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// RUN: %check_clang_tidy %s bugprone-infinite-loop %t \
2-
// RUN: -- -- -fexceptions -fblocks
2+
// RUN: -- -- -fexceptions -fblocks -fno-delayed-template-parsing
33

44
void simple_infinite_loop1() {
55
int i = 0;
@@ -622,3 +622,31 @@ void test_volatile_concrete_address(int i, int size) {
622622
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (size) are updated in the loop body [bugprone-infinite-loop]
623623
}
624624
}
625+
626+
template <typename T>
627+
int some_template_fn() { return 1; }
628+
629+
template <typename T>
630+
void test_dependent_condition() {
631+
const int error = some_template_fn<T>();
632+
do {
633+
} while (false && error == 0);
634+
635+
const int val = some_template_fn<T>();
636+
for (; !(val == 0 || true);) {
637+
}
638+
639+
const int val2 = some_template_fn<T>();
640+
for (; !(val2 == 0 || false);) {
641+
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (val2) are updated in the loop body [bugprone-infinite-loop]
642+
}
643+
644+
const int val3 = some_template_fn<T>();
645+
do {
646+
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (val3) are updated in the loop body [bugprone-infinite-loop]
647+
} while (1, (true) && val3 == 1);
648+
649+
const int val4 = some_template_fn<T>();
650+
do {
651+
} while (1, (false) && val4 == 1);
652+
}

0 commit comments

Comments
 (0)