diff options
author | Richard Trieu <rtrieu@google.com> | 2013-12-21 02:33:43 +0000 |
---|---|---|
committer | Richard Trieu <rtrieu@google.com> | 2013-12-21 02:33:43 +0000 |
commit | 7510c83c6e5bfdfee9a4740be8471efd75520617 (patch) | |
tree | 01b37d2d5f5a52ca9fb61b84358ed50ec4b5d648 /lib/Sema/AnalysisBasedWarnings.cpp | |
parent | 4ba94238989e21a8cce1aea03662c8d82267f81c (diff) |
Add -Winfinite-recursion to Clang
This new warning detects when a function will recursively call itself on every
code path though that function. This catches simple recursive cases such as:
void foo() {
foo();
}
As well as more complex functions like:
void bar() {
if (test()) {
bar();
return;
} else {
bar();
}
return;
}
This warning uses the CFG. As with other CFG-based warnings, this is off
by default. Due to false positives, this warning is also disabled for
templated functions.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@197853 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Sema/AnalysisBasedWarnings.cpp')
-rw-r--r-- | lib/Sema/AnalysisBasedWarnings.cpp | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/lib/Sema/AnalysisBasedWarnings.cpp b/lib/Sema/AnalysisBasedWarnings.cpp index 93e3ecfb29..57c0ac311c 100644 --- a/lib/Sema/AnalysisBasedWarnings.cpp +++ b/lib/Sema/AnalysisBasedWarnings.cpp @@ -78,6 +78,95 @@ static void CheckUnreachable(Sema &S, AnalysisDeclContext &AC) { } //===----------------------------------------------------------------------===// +// Check for infinite self-recursion in functions +//===----------------------------------------------------------------------===// + +// All blocks are in one of three states. States are ordered so that blocks +// can only move to higher states. +enum RecursiveState { + FoundNoPath, + FoundPath, + FoundPathWithNoRecursiveCall +}; + +static void checkForFunctionCall(Sema &S, const FunctionDecl *FD, + CFGBlock &Block, unsigned ExitID, + llvm::SmallVectorImpl<RecursiveState> &States, + RecursiveState State) { + unsigned ID = Block.getBlockID(); + + // A block's state can only move to a higher state. + if (States[ID] >= State) + return; + + States[ID] = State; + + // Found a path to the exit node without a recursive call. + if (ID == ExitID && State == FoundPathWithNoRecursiveCall) + return; + + if (State == FoundPathWithNoRecursiveCall) { + // If the current state is FoundPathWithNoRecursiveCall, the successors + // will be either FoundPathWithNoRecursiveCall or FoundPath. To determine + // which, process all the Stmt's in this block to find any recursive calls. + for (CFGBlock::iterator I = Block.begin(), E = Block.end(); I != E; ++I) { + if (I->getKind() != CFGElement::Statement) + continue; + + const CallExpr *CE = dyn_cast<CallExpr>(I->getAs<CFGStmt>()->getStmt()); + if (CE && CE->getCalleeDecl() && + CE->getCalleeDecl()->getCanonicalDecl() == FD) { + if (const CXXMemberCallExpr *MCE = dyn_cast<CXXMemberCallExpr>(CE)) { + if (isa<CXXThisExpr>(MCE->getImplicitObjectArgument()) || + !MCE->getMethodDecl()->isVirtual()) { + State = FoundPath; + break; + } + } else { + State = FoundPath; + break; + } + } + } + } + + for (CFGBlock::succ_iterator I = Block.succ_begin(), E = Block.succ_end(); + I != E; ++I) + if (*I) + checkForFunctionCall(S, FD, **I, ExitID, States, State); +} + +static void checkRecursiveFunction(Sema &S, const FunctionDecl *FD, + const Stmt *Body, + AnalysisDeclContext &AC) { + FD = FD->getCanonicalDecl(); + + // Only run on non-templated functions and non-templated members of + // templated classes. + if (FD->getTemplatedKind() != FunctionDecl::TK_NonTemplate && + FD->getTemplatedKind() != FunctionDecl::TK_MemberSpecialization) + return; + + CFG *cfg = AC.getCFG(); + if (cfg == 0) return; + + // If the exit block is unreachable, skip processing the function. + if (cfg->getExit().pred_empty()) + return; + + // Mark all nodes as FoundNoPath, then begin processing the entry block. + llvm::SmallVector<RecursiveState, 16> states(cfg->getNumBlockIDs(), + FoundNoPath); + checkForFunctionCall(S, FD, cfg->getEntry(), cfg->getExit().getBlockID(), + states, FoundPathWithNoRecursiveCall); + + // Check that the exit block is reachable. This prevents triggering the + // warning on functions that do not terminate. + if (states[cfg->getExit().getBlockID()] == FoundPath) + S.Diag(Body->getLocStart(), diag::warn_infinite_recursive_function); +} + +//===----------------------------------------------------------------------===// // Check for missing return value. //===----------------------------------------------------------------------===// @@ -1789,6 +1878,16 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P, D->getLocStart()) != DiagnosticsEngine::Ignored) diagnoseRepeatedUseOfWeak(S, fscope, D, AC.getParentMap()); + + // Check for infinite self-recursion in functions + if (Diags.getDiagnosticLevel(diag::warn_infinite_recursive_function, + D->getLocStart()) + != DiagnosticsEngine::Ignored) { + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + checkRecursiveFunction(S, FD, Body, AC); + } + } + // Collect statistics about the CFG if it was built. if (S.CollectStats && AC.isCFGBuilt()) { ++NumFunctionsAnalyzed; |