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: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -30,6 +30,10 @@ "been assigned">, InGroup>, 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; + // Constant expressions def err_expr_not_ice : Error< "expression is not an %select{integer|integral}0 constant expression">; 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 }; Index: lib/Sema/AnalysisBasedWarnings.cpp =================================================================== --- lib/Sema/AnalysisBasedWarnings.cpp +++ lib/Sema/AnalysisBasedWarnings.cpp @@ -78,6 +78,74 @@ } //===----------------------------------------------------------------------===// +// 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 &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(I->getAs()->getStmt()); + if (CE && CE->getCalleeDecl() == FD) { + if (const CXXMemberCallExpr *MCE = dyn_cast(CE)) { + if (isa(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) { + // 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; + llvm::SmallVector 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 +1838,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(D)) { + CheckRecursiveFuction(S, FD, Body, AC); + } + } + // Collect statistics about the CFG if it was built. if (S.CollectStats && AC.isCFGBuilt()) { ++NumFunctionsAnalyzed; 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/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/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/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/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/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: 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/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/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 -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/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/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,122 @@ +// 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); +} + +template +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 foo; + foo.a(); // expected-note{{in instantiation}} + foo.b(); // expected-note{{in instantiation}} +} + +// No warnings on templated fuctions +// sum<0>() is instantiated, does recursively call itself, but never runs. +template +int sum() { + return value + sum(); +} + +template<> +int sum<1>() { return 1; } + +template +int calculate_value() { + if (x != y) + return sum(); // 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 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(); + DoStuff<(First + Last)/2, Last>(); + } + return 0; +} +int stuff = DoStuff<0, 1>();