r197853 - Add -Winfinite-recursion to Clang
Richard Trieu
rtrieu at google.com
Fri Dec 20 18:33:43 PST 2013
Author: rtrieu
Date: Fri Dec 20 20:33:43 2013
New Revision: 197853
URL: http://llvm.org/viewvc/llvm-project?rev=197853&view=rev
Log:
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.
Added:
cfe/trunk/test/SemaCXX/warn-infinite-recursion.cpp
Modified:
cfe/trunk/include/clang/Basic/DiagnosticGroups.td
cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
Modified: cfe/trunk/include/clang/Basic/DiagnosticGroups.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticGroups.td?rev=197853&r1=197852&r2=197853&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticGroups.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticGroups.td Fri Dec 20 20:33:43 2013
@@ -169,6 +169,7 @@ def OverloadedShiftOpParentheses: DiagGr
def DanglingElse: DiagGroup<"dangling-else">;
def DanglingField : DiagGroup<"dangling-field">;
def DistributedObjectModifiers : DiagGroup<"distributed-object-modifiers">;
+def InfiniteRecursion : DiagGroup<"infinite-recursion">;
def GNUImaginaryConstant : DiagGroup<"gnu-imaginary-constant">;
def IgnoredQualifiers : DiagGroup<"ignored-qualifiers">;
def : DiagGroup<"import">;
Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=197853&r1=197852&r2=197853&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Fri Dec 20 20:33:43 2013
@@ -30,6 +30,10 @@ def warn_duplicate_enum_values : Warning
"been assigned">, InGroup<DiagGroup<"duplicate-enum">>, DefaultIgnore;
def note_duplicate_element : Note<"element %0 also has value %1">;
+def warn_infinite_recursive_function : Warning<
+ "all paths through this function will call itself">,
+ InGroup<InfiniteRecursion>, DefaultIgnore;
+
// Constant expressions
def err_expr_not_ice : Error<
"expression is not an %select{integer|integral}0 constant expression">;
Modified: cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp?rev=197853&r1=197852&r2=197853&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp (original)
+++ cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp Fri Dec 20 20:33:43 2013
@@ -78,6 +78,95 @@ static void CheckUnreachable(Sema &S, An
}
//===----------------------------------------------------------------------===//
+// 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(sem
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;
Added: cfe/trunk/test/SemaCXX/warn-infinite-recursion.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-infinite-recursion.cpp?rev=197853&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-infinite-recursion.cpp (added)
+++ cfe/trunk/test/SemaCXX/warn-infinite-recursion.cpp Fri Dec 20 20:33:43 2013
@@ -0,0 +1,129 @@
+// RUN: %clang_cc1 %s -fsyntax-only -verify -Winfinite-recursion
+
+void a() { // expected-warning{{call itself}}
+ a();
+}
+
+void b(int x) { // expected-warning{{call itself}}
+ if (x)
+ b(x);
+ else
+ b(x+1);
+}
+
+void c(int x) {
+ if (x)
+ c(5);
+}
+
+void d(int x) { // expected-warning{{call itself}}
+ if (x)
+ ++x;
+ return d(x);
+}
+
+// Doesn't warn on mutually recursive functions
+void e();
+void f();
+
+void e() { f(); }
+void f() { e(); }
+
+// Don't warn on infinite loops
+void g() {
+ while (true)
+ g();
+
+ g();
+}
+
+void h(int x) {
+ while (x < 5) {
+ h(x+1);
+ }
+}
+
+void i(int x) { // expected-warning{{call itself}}
+ while (x < 5) {
+ --x;
+ }
+ i(0);
+}
+
+int j() { // expected-warning{{call itself}}
+ return 5 + j();
+}
+
+class S {
+ static void a();
+ void b();
+};
+
+void S::a() { // expected-warning{{call itself}}
+ return a();
+}
+
+void S::b() { // expected-warning{{call itself}}
+ int i = 0;
+ do {
+ ++i;
+ b();
+ } while (i > 5);
+}
+
+template<class member>
+struct T {
+ member m;
+ void a() { return a(); } // expected-warning{{call itself}}
+ static void b() { return b(); } // expected-warning{{call itself}}
+};
+
+void test_T() {
+ T<int> foo;
+ foo.a(); // expected-note{{in instantiation}}
+ foo.b(); // expected-note{{in instantiation}}
+}
+
+class U {
+ U* u;
+ void Fun() { // expected-warning{{call itself}}
+ u->Fun();
+ }
+};
+
+// No warnings on templated functions
+// sum<0>() is instantiated, does recursively call itself, but never runs.
+template <int value>
+int sum() {
+ return value + sum<value/2>();
+}
+
+template<>
+int sum<1>() { return 1; }
+
+template<int x, int y>
+int calculate_value() {
+ if (x != y)
+ return sum<x - y>(); // This instantiates sum<0>() even if never called.
+ else
+ return 0;
+}
+
+int value = calculate_value<1,1>();
+
+void DoSomethingHere();
+
+// DoStuff<0,0>() is instantiated, but never called.
+template<int First, int Last>
+int DoStuff() {
+ if (First + 1 == Last) {
+ // This branch gets removed during <0, 0> instantiation in so CFG for this
+ // function goes straight to the else branch.
+ DoSomethingHere();
+ } else {
+ DoStuff<First, (First + Last)/2>();
+ DoStuff<(First + Last)/2, Last>();
+ }
+ return 0;
+}
+int stuff = DoStuff<0, 1>();
More information about the cfe-commits
mailing list