[clang-tidy] bugprone-infinite-loop: Fix false positives with volatile addresses.

Low-level code may occasionally deal with direct access by concrete addresses
such as 0x1234. Values at these addresses act like globals: they can change
at any time. They typically wear volatile qualifiers.

Suppress all warnings on loops with conditions that involve casting anything to
a pointer-to-...-pointer-to-volatile type.

The closely related bugprone-redundant-branch-condition check
doesn't seem to be affected. Add a test just in case.

Differential Revision: https://reviews.llvm.org/D108808
This commit is contained in:
Artem Dergachev 2021-09-07 14:58:13 -07:00
parent eeabd90efd
commit dcde8fdeeb
3 changed files with 52 additions and 0 deletions

View file

@ -65,6 +65,17 @@ static bool isVarThatIsPossiblyChanged(const Decl *Func, const Stmt *LoopStmt,
ObjCIvarRefExpr, ObjCPropertyRefExpr, ObjCMessageExpr>(Cond)) {
// FIXME: Handle MemberExpr.
return true;
} else if (const auto *CE = dyn_cast<CastExpr>(Cond)) {
QualType T = CE->getType();
while (true) {
if (T.isVolatileQualified())
return true;
if (!T->isAnyPointerType() && !T->isReferenceType())
break;
T = T->getPointeeType();
}
}
return false;

View file

@ -591,3 +591,34 @@ void test_structured_bindings_bad() {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (x) are updated in the loop body [bugprone-infinite-loop]
}
}
void test_volatile_cast() {
// This is a no-op cast. Clang ignores the qualifier, we should too.
for (int i = 0; (volatile int)i < 10;) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this loop is infinite; none of its condition variables (i) are updated in the loop body [bugprone-infinite-loop]
}
}
void test_volatile_concrete_address(int i, int size) {
// No warning. The value behind the volatile concrete address
// is beyond our control. It may change at any time.
for (; *((volatile int *)0x1234) < size;) {
}
for (; *((volatile int *)(0x1234 + i)) < size;) {
}
for (; **((volatile int **)0x1234) < size;) {
}
volatile int *x = (volatile int *)0x1234;
for (; *x < 10;) {
}
// FIXME: This one should probably also be suppressed.
// Whatever the developer is doing here, they can do that again anywhere else
// which basically makes it a global.
for (; *(int *)0x1234 < size;) {
// 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]
}
}

View file

@ -1387,3 +1387,13 @@ void relational_operator_reversed() {
}
}
}
void volatile_concrete_address() {
// No warning. The value behind the volatile concrete address
// is beyond our control. It may change at any time.
if (*(volatile int *)0x1234) {
if (*(volatile int *)0x1234) {
doSomething();
}
}
}