forked from OSchip/llvm-project
[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:
parent
6a079dfdc9
commit
46c6c08c94
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue