Implement a warning to detect when a function will call itself recursively on 
every code path.  If a program ever calls such a function, the function will 
attempt to call itself until it runs out of stack space.

This warning searches the CFG to determine if every codepath results in a self 
call.  In addition to the test for this warning, several other tests needed to 
be fixed, and a pragma to prevent this warning where Clang really wants a stack 
overflow.

Testing with this warning has already caught several buggy functions.  Common 
mistakes include: incorrect namespaces, wrapper classes not forwarding calls 
properly, similarly named member function and data member, and failing to call 
an overload of the same function.  When run outside of template instantiations, 
all true positives.  In template instantiations, only 25% true positive.  
Therefore, this warning is disabled in template instantiations.  An example of 
such a false positive is in the test cases.

http://llvm-reviews.chandlerc.com/D1864

Files:
  test/Analysis/inlining/test-always-inline-size-option.c
  test/Analysis/misc-ps-region-store.cpp
  test/Analysis/cxx11-crashes.cpp
  test/FixIt/typo.m
  test/FixIt/fixit.c
  test/Sema/unused-expr-system-header.c
  test/Sema/warn-unused-function.c
  test/Sema/attr-deprecated.c
  test/Parser/cxx-using-declaration.cpp
  test/Parser/expressions.c
  test/CodeGen/functions.c
  test/SemaCXX/statements.cpp
  test/SemaCXX/warn-bool-conversion.cpp
  test/SemaCXX/warn-infinite-recursion.cpp
  test/SemaCXX/MicrosoftCompatibility.cpp
  test/Lexer/gnu-flags.c
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Basic/DiagnosticGroups.td
  lib/Sema/AnalysisBasedWarnings.cpp
  lib/Lex/Pragma.cpp
Index: test/Analysis/inlining/test-always-inline-size-option.c
===================================================================
--- test/Analysis/inlining/test-always-inline-size-option.c
+++ test/Analysis/inlining/test-always-inline-size-option.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-inline-max-stack-depth=3 -analyzer-config ipa-always-inline-size=3 -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-inline-max-stack-depth=3 -analyzer-config ipa-always-inline-size=3 -Wno-infinite-recursion -verify %s
 
 void clang_analyzer_eval(int);
 int nested5() {
Index: test/Analysis/misc-ps-region-store.cpp
===================================================================
--- test/Analysis/misc-ps-region-store.cpp
+++ test/Analysis/misc-ps-region-store.cpp
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -triple i386-apple-darwin9 -analyze -analyzer-checker=core,alpha.core,debug.ExprInspection -analyzer-store=region -verify -fblocks -analyzer-opt-analyze-nested-blocks %s -fexceptions -fcxx-exceptions
-// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -analyze -analyzer-checker=core,alpha.core,debug.ExprInspection -analyzer-store=region -verify -fblocks -analyzer-opt-analyze-nested-blocks %s -fexceptions -fcxx-exceptions
+// RUN: %clang_cc1 -triple i386-apple-darwin9 -analyze -analyzer-checker=core,alpha.core,debug.ExprInspection -analyzer-store=region -verify -fblocks -analyzer-opt-analyze-nested-blocks %s -fexceptions -fcxx-exceptions -Wno-infinite-recursion
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -analyze -analyzer-checker=core,alpha.core,debug.ExprInspection -analyzer-store=region -verify -fblocks -analyzer-opt-analyze-nested-blocks %s -fexceptions -fcxx-exceptions -Wno-infinite-recursion
 
 void clang_analyzer_warnIfReached();
 
Index: test/Analysis/cxx11-crashes.cpp
===================================================================
--- test/Analysis/cxx11-crashes.cpp
+++ test/Analysis/cxx11-crashes.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core -std=c++11 -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core -std=c++11 -verify -Wno-infinite-recursion %s
 // expected-no-diagnostics
 
 // radar://11485149, PR12871
Index: test/FixIt/typo.m
===================================================================
--- test/FixIt/typo.m
+++ test/FixIt/typo.m
@@ -147,7 +147,8 @@
 @end
 #endif
 
-void f(A *a) {
+void f(A *a);
+void test_f(A *a) {
   f(a) // expected-error{{expected ';' after expression}}
   [a methodA] // expected-error{{expected ';' after expression}}
   [A methodA] // expected-error{{expected ';' after expression}}
Index: test/FixIt/fixit.c
===================================================================
--- test/FixIt/fixit.c
+++ test/FixIt/fixit.c
@@ -65,7 +65,6 @@
 
 void removeUnusedLabels(char c) {
   L0 /*removed comment*/:        c++; // expected-warning {{unused label}}
-  removeUnusedLabels(c);
   L1: // expected-warning {{unused label}}
   c++;
   /*preserved comment*/ L2  :        c++; // expected-warning {{unused label}}
Index: test/Sema/unused-expr-system-header.c
===================================================================
--- test/Sema/unused-expr-system-header.c
+++ test/Sema/unused-expr-system-header.c
@@ -1,6 +1,7 @@
 // RUN: %clang_cc1 -isystem %S/Inputs -fsyntax-only -verify %s
 #include <unused-expr-system-header.h>
-void f(int i1, int i2) {
+void f(int i1, int i2);
+void g(int i1, int i2) {
   POSSIBLY_BAD_MACRO(5);
   STATEMENT_EXPR_MACRO(5);
   COMMA_MACRO_1(i1 == i2, f(i1, i2)); // expected-warning {{comparison result unused}} \
Index: test/Sema/warn-unused-function.c
===================================================================
--- test/Sema/warn-unused-function.c
+++ test/Sema/warn-unused-function.c
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -fsyntax-only -Wused-but-marked-unused -Wunused-function -Wunneeded-internal-declaration -verify %s
-// RUN: %clang_cc1 -fsyntax-only -verify -Wunused %s
-// RUN: %clang_cc1 -fsyntax-only -verify -Wall %s
+// RUN: %clang_cc1 -fsyntax-only -Wused-but-marked-unused -Wunused-function -Wunneeded-internal-declaration -Wno-infinite-recursion -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wno-infinite-recursion -verify -Wunused %s
+// RUN: %clang_cc1 -fsyntax-only -Wno-infinite-recursion -verify -Wall %s
 
 void foo() {}
 static void f2() {} 
Index: test/Sema/attr-deprecated.c
===================================================================
--- test/Sema/attr-deprecated.c
+++ test/Sema/attr-deprecated.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -verify -fsyntax-only
+// RUN: %clang_cc1 %s -Wno-infinite-recursion -verify -fsyntax-only
 
 int f() __attribute__((deprecated)); // expected-note 2 {{declared here}}
 void g() __attribute__((deprecated));
Index: test/Parser/cxx-using-declaration.cpp
===================================================================
--- test/Parser/cxx-using-declaration.cpp
+++ test/Parser/cxx-using-declaration.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wno-infinite-recursion -verify %s
 
 namespace A {
     int VA;
Index: test/Parser/expressions.c
===================================================================
--- test/Parser/expressions.c
+++ test/Parser/expressions.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wno-infinite-recursion -verify %s
 
 void test1() {
   if (sizeof (int){ 1}) {}   // sizeof compound literal
Index: test/CodeGen/functions.c
===================================================================
--- test/CodeGen/functions.c
+++ test/CodeGen/functions.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -triple i386-unknown-unknown -emit-llvm -o - -verify | FileCheck %s
+// RUN: %clang_cc1 %s -Wno-infinite-recursion -triple i386-unknown-unknown -emit-llvm -o - -verify | FileCheck %s
 
 int g();
 
Index: test/SemaCXX/statements.cpp
===================================================================
--- test/SemaCXX/statements.cpp
+++ test/SemaCXX/statements.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -fsyntax-only -pedantic -verify
+// RUN: %clang_cc1 %s -fsyntax-only -Wno-infinite-recursion -pedantic -verify
 
 void foo() { 
   return foo();
Index: test/SemaCXX/warn-bool-conversion.cpp
===================================================================
--- test/SemaCXX/warn-bool-conversion.cpp
+++ test/SemaCXX/warn-bool-conversion.cpp
@@ -2,7 +2,8 @@
 
 int* j = false; // expected-warning{{initialization of pointer of type 'int *' to null from a constant boolean expression}}
 
-void foo(int* i, int *j=(false)) // expected-warning{{initialization of pointer of type 'int *' to null from a constant boolean expression}}
+void foo(int* i, int *j=(false)); // expected-warning{{initialization of pointer of type 'int *' to null from a constant boolean expression}}
+void bar()
 {
   foo(false); // expected-warning{{initialization of pointer of type 'int *' to null from a constant boolean expression}}
   foo((int*)false); // no-warning: explicit cast
Index: test/SemaCXX/warn-infinite-recursion.cpp
===================================================================
--- test/SemaCXX/warn-infinite-recursion.cpp
+++ test/SemaCXX/warn-infinite-recursion.cpp
@@ -0,0 +1,92 @@
+// RUN: %clang_cc1 %s -fsyntax-only -verify
+
+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);
+}
+
+// No warnings on templates
+// sum<0>() is instantiated, does recursively call itself, but never runs.
+template <int value>
+int sum() {
+  return value + sum<value/2>;
+}
+
+template<>
+int sum<0>() { 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>();
Index: test/SemaCXX/MicrosoftCompatibility.cpp
===================================================================
--- test/SemaCXX/MicrosoftCompatibility.cpp
+++ test/SemaCXX/MicrosoftCompatibility.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -triple i686-pc-win32 -fsyntax-only -std=c++11 -Wmicrosoft -verify -fms-compatibility -fexceptions -fcxx-exceptions
+// RUN: %clang_cc1 %s -triple i686-pc-win32 -fsyntax-only -std=c++11 -Wmicrosoft -Wno-infinite-recursion -verify -fms-compatibility -fexceptions -fcxx-exceptions
 
 
 typedef unsigned short char16_t;
Index: test/Lexer/gnu-flags.c
===================================================================
--- test/Lexer/gnu-flags.c
+++ test/Lexer/gnu-flags.c
@@ -18,14 +18,15 @@
 
 
 #if ALL || ZEROARGS
-// expected-warning@+9 {{must specify at least one argument for '...' parameter of variadic macro}}
+// expected-warning@+10 {{must specify at least one argument for '...' parameter of variadic macro}}
 // expected-note@+4 {{macro 'efoo' defined here}}
 // expected-warning@+3 {{token pasting of ',' and __VA_ARGS__ is a GNU extension}}
 #endif
 
 #define efoo(format, args...) foo(format , ##args)
 
-void foo( const char* c )
+void foo( const char* c );
+void foo_test()
 {
   efoo("6");
 }
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -30,6 +30,10 @@
   "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>;
+
 // Constant expressions
 def err_expr_not_ice : Error<
   "expression is not an %select{integer|integral}0 constant expression">;
Index: include/clang/Basic/DiagnosticGroups.td
===================================================================
--- include/clang/Basic/DiagnosticGroups.td
+++ include/clang/Basic/DiagnosticGroups.td
@@ -161,6 +161,7 @@
 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">;
Index: lib/Sema/AnalysisBasedWarnings.cpp
===================================================================
--- lib/Sema/AnalysisBasedWarnings.cpp
+++ lib/Sema/AnalysisBasedWarnings.cpp
@@ -78,6 +78,69 @@
 }
 
 //===----------------------------------------------------------------------===//
+// Check for infinite self-recursion in functions
+//===----------------------------------------------------------------------===//
+
+enum RecursiveState {
+  Unknown,
+  AfterFunctionCall,
+  Reachable,
+};
+
+static void CheckForFunctionCall(Sema &S, const FunctionDecl *FD,
+                                 CFGBlock& Block, unsigned exitID,
+                                 llvm::SmallVectorImpl<RecursiveState> &states,
+                                 RecursiveState state) {
+  unsigned ID = Block.getBlockID();
+
+  if (states[ID] < state)
+    states[ID] = state;
+  else
+    return;
+
+  if (state == Reachable) {
+    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() == FD) {
+        if (const CXXMemberCallExpr *MCE = dyn_cast<CXXMemberCallExpr>(CE)) {
+          if (isa<CXXThisExpr>(MCE->getImplicitObjectArgument())) {
+            state = AfterFunctionCall;
+            break;
+          }
+        } else {
+          state = AfterFunctionCall;
+          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 CheckRecursiveFuction(Sema &S, const FunctionDecl *FD,
+                                  const Stmt *Body,
+                                  AnalysisDeclContext &AC) {
+  if (FD->isTemplateInstantiation()) return;
+  CFG *cfg = AC.getCFG();
+  if (cfg == 0) return;
+  llvm::SmallVector<RecursiveState, 16> states(cfg->getNumBlockIDs(), Unknown);
+  CheckForFunctionCall(S, FD, cfg->getEntry(), cfg->getExit().getBlockID(),
+                       states, Reachable);
+
+  // Check that the exit block is reachable.  This prevents triggering the
+  // warning on 
+  if (states[cfg->getExit().getBlockID()] == AfterFunctionCall)
+    S.Diag(Body->getLocStart(), diag::warn_infinite_recursive_function);
+}
+
+//===----------------------------------------------------------------------===//
 // Check for missing return value.
 //===----------------------------------------------------------------------===//
 
@@ -1770,6 +1833,16 @@
                                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)) {
+      CheckRecursiveFuction(S, FD, Body, AC);
+    }
+  }
+
   // Collect statistics about the CFG if it was built.
   if (S.CollectStats && AC.isCFGBuilt()) {
     ++NumFunctionsAnalyzed;
Index: lib/Lex/Pragma.cpp
===================================================================
--- lib/Lex/Pragma.cpp
+++ lib/Lex/Pragma.cpp
@@ -926,12 +926,20 @@
 #ifdef _MSC_VER
     #pragma warning(disable : 4717)
 #endif
+// Disable Clang's warning about infinite recursion.
+#ifdef __clang__
+    #pragma clang diagnostic push
+    #pragma clang diagnostic ignored "-Winfinite-recursion"
+#endif
   void DebugOverflowStack() {
     DebugOverflowStack();
   }
 #ifdef _MSC_VER
     #pragma warning(default : 4717)
 #endif
+#ifdef __clang__
+    #pragma clang diagnostic pop
+#endif
 
 };
 
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to