[PATCH] D73270: [clang-tidy] Fix false positive in bugprone-infinite-loop

Dmitri Gribenko via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Thu Jan 23 11:14:48 PST 2020


gribozavr2 added inline comments.


================
Comment at: clang-tools-extra/clang-tidy/bugprone/InfiniteLoopCheck.cpp:186
+    }
+  }
+
----------------
baloghadamsoftware wrote:
> gribozavr2 wrote:
> > I think this logic should go into `isChanged()`, similarly to how it handles for loops today.
> `isChanged()` looks for changes of a specific variable. This part collects the variables to check. The fundamental problem with the current logic is that it only look for the condition variables in the `Condition` of the loop statement, but it does not contain the initial value of the variable declared inside the condition part of the `while` statement. Thus in case of `while (int m = n) { n--; }` the condition is only the expression `m`. Thus we simply cannot grab `n` by only checking the condition thus consider this loop as infinite because `m` is not changed. Taking the whole loop statement instead of the condition is also wrong because then the following obviously infinite loop is not detected: `while (n) { m--; }` because `m` is also grabbed from the body which must not. We must explicitly check the variable of the initialization expression of variable declared in the condition of `while` loops because it is a separate child of the statement.
Yes, I see what you mean -- and my suggestion was incorrect.

However, right now the variable declared in the while loop is being treated like something that carries over to the next loop iteration:

```
while (int k = Limit) { k--; } // no warning with this patch
```

This is because this while loop still has a condition that checks that `k != 0`, and we scan that in the isAtLeastOneCondVarChanged call on line 173.

I think a better fix would bind `Cond` to the initialization expression of the while loop variable. Like this:

```
void InfiniteLoopCheck::check(const MatchFinder::MatchResult &Result) {
  const auto *Cond = Result.Nodes.getNodeAs<Expr>("condition");
  const auto *LoopStmt = Result.Nodes.getNodeAs<Stmt>("loop-stmt");
  const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");

  bool ShouldHaveConditionVariables = true;
  if (const auto *While = dyn_cast<WhileStmt>(LoopStmt)) {
    if (const VarDecl *LoopVarDecl = While->getConditionVariable()) {
      if (const Expr *Init = LoopVarDecl->getInit()) {
        ShouldHaveConditionVariables = false;
        Cond = Init;
      }
    }
  }
  Cond->dump();

  if (isAtLeastOneCondVarChanged(Func, LoopStmt, Cond, Result.Context)) {
    return;
  }

  std::string CondVarNames = getCondVarNames(Cond);
  if (ShouldHaveConditionVariables && CondVarNames.empty())
    return;

  if (CondVarNames.empty()) {
    diag(LoopStmt->getBeginLoc(),
         "this loop is infinite; it does not check any variables in the condition");
  } else {
    diag(LoopStmt->getBeginLoc(),
         "this loop is infinite; none of its condition variables (%0)"
         " are updated in the loop body")
        << CondVarNames;
  }
}
```

If you agree, please also add `while (int k = Limit) { k--; }` to the tests.


================
Comment at: clang-tools-extra/test/clang-tidy/checkers/bugprone-infinite-loop.cpp:69
   }
 }
 
----------------
There seems to be no test coverage for the following case:

```
while (i--) { // no warning
  j--;
}
```

I'd appreciate if you could add that.

And also this one, relevant to the bug you're fixing:

```
while (int k = Limit--) { // no warning
  i--;
}
```


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D73270/new/

https://reviews.llvm.org/D73270





More information about the cfe-commits mailing list