[clang-tidy] bugprone-infinite-loop: forFunction() -> forCallable().

Take advantage of the new ASTMatcher added in D102213 to fix massive false negatives of the infinite loop checker on Objective-C.

Differential Revision: https://reviews.llvm.org/D102214
This commit is contained in:
Artem Dergachev 2021-05-10 20:09:32 -07:00
parent 6a079dfdc9
commit 46c6c08c94
5 changed files with 47 additions and 14 deletions

View File

@ -21,6 +21,7 @@ namespace bugprone {
static internal::Matcher<Stmt>
loopEndingStmt(internal::Matcher<Stmt> Internal) {
// FIXME: Cover noreturn ObjC methods (and blocks?).
return stmt(anyOf(
mapAnyOf(breakStmt, returnStmt, gotoStmt, cxxThrowExpr).with(Internal),
callExpr(Internal, callee(functionDecl(isNoReturn())))));
@ -43,9 +44,8 @@ static bool isChanged(const Stmt *LoopStmt, const VarDecl *Var,
}
/// Return whether `Cond` is a variable that is possibly changed in `LoopStmt`.
static bool isVarThatIsPossiblyChanged(const FunctionDecl *Func,
const Stmt *LoopStmt, const Stmt *Cond,
ASTContext *Context) {
static bool isVarThatIsPossiblyChanged(const Decl *Func, const Stmt *LoopStmt,
const Stmt *Cond, ASTContext *Context) {
if (const auto *DRE = dyn_cast<DeclRefExpr>(Cond)) {
if (const auto *Var = dyn_cast<VarDecl>(DRE->getDecl())) {
if (!Var->isLocalVarDeclOrParm())
@ -70,9 +70,8 @@ static bool isVarThatIsPossiblyChanged(const FunctionDecl *Func,
}
/// Return whether at least one variable of `Cond` changed in `LoopStmt`.
static bool isAtLeastOneCondVarChanged(const FunctionDecl *Func,
const Stmt *LoopStmt, const Stmt *Cond,
ASTContext *Context) {
static bool isAtLeastOneCondVarChanged(const Decl *Func, const Stmt *LoopStmt,
const Stmt *Cond, ASTContext *Context) {
if (isVarThatIsPossiblyChanged(Func, LoopStmt, Cond, Context))
return true;
@ -118,9 +117,9 @@ static bool isKnownFalse(const Expr &Cond, const ASTContext &Ctx) {
void InfiniteLoopCheck::registerMatchers(MatchFinder *Finder) {
const auto LoopCondition = allOf(
hasCondition(
expr(forFunction(functionDecl().bind("func"))).bind("condition")),
expr(forCallable(decl().bind("func"))).bind("condition")),
unless(hasBody(hasDescendant(
loopEndingStmt(forFunction(equalsBoundNode("func")))))));
loopEndingStmt(forCallable(equalsBoundNode("func")))))));
Finder->addMatcher(mapAnyOf(whileStmt, doStmt, forStmt)
.with(LoopCondition)
@ -131,7 +130,7 @@ void InfiniteLoopCheck::registerMatchers(MatchFinder *Finder) {
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");
const auto *Func = Result.Nodes.getNodeAs<Decl>("func");
if (isKnownFalse(*Cond, *Result.Context))
return;

View File

@ -78,7 +78,7 @@ static bool hasPtrOrReferenceInStmt(const Stmt *S, const VarDecl *Var) {
return false;
}
static bool refersToEnclosingLambdaCaptureByRef(const FunctionDecl *Func,
static bool refersToEnclosingLambdaCaptureByRef(const Decl *Func,
const VarDecl *Var) {
const auto *MD = dyn_cast<CXXMethodDecl>(Func);
if (!MD)
@ -91,7 +91,7 @@ static bool refersToEnclosingLambdaCaptureByRef(const FunctionDecl *Func,
return capturesByRef(RD, Var);
}
bool hasPtrOrReferenceInFunc(const FunctionDecl *Func, const VarDecl *Var) {
bool hasPtrOrReferenceInFunc(const Decl *Func, const VarDecl *Var) {
return hasPtrOrReferenceInStmt(Func->getBody(), Var) ||
refersToEnclosingLambdaCaptureByRef(Func, Var);
}

View File

@ -27,7 +27,7 @@ namespace utils {
/// For `f()` and `n` the function returns ``true`` because `p` is a
/// pointer to `n` created in `f()`.
bool hasPtrOrReferenceInFunc(const FunctionDecl *Func, const VarDecl *Var);
bool hasPtrOrReferenceInFunc(const Decl *Func, const VarDecl *Var);
} // namespace utils
} // namespace tidy

View File

@ -511,8 +511,7 @@ void block_capture_with_loop_inside_block_bad() {
bool finished = false;
auto block = ^() {
while (!finished) {
// FIXME: This should warn. It currently reacts to &finished
// outside the block which ideally shouldn't have any effect.
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: this loop is infinite; none of its condition variables (finished) are updated in the loop body [bugprone-infinite-loop]
wait();
}
};

View File

@ -0,0 +1,35 @@
// RUN: %check_clang_tidy %s bugprone-infinite-loop %t -- -- -fblocks
@interface I
-(void) instanceMethod;
+(void) classMethod;
@end
void plainCFunction() {
int i = 0;
int j = 0;
while (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]
j++;
}
}
@implementation I
- (void)instanceMethod {
int i = 0;
int j = 0;
while (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]
j++;
}
}
+ (void)classMethod {
int i = 0;
int j = 0;
while (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]
j++;
}
}
@end