diff options
-rw-r--r-- | clang/include/clang/Analysis/Analyses/ReachableCode.h | 10 | ||||
-rw-r--r-- | clang/include/clang/Basic/DiagnosticGroups.td | 15 | ||||
-rw-r--r-- | clang/include/clang/Basic/DiagnosticSemaKinds.td | 13 | ||||
-rw-r--r-- | clang/lib/Analysis/ReachableCode.cpp | 62 | ||||
-rw-r--r-- | clang/lib/Sema/AnalysisBasedWarnings.cpp | 17 | ||||
-rw-r--r-- | clang/test/Sema/warn-unreachable.c | 2 | ||||
-rw-r--r-- | clang/test/SemaCXX/unreachable-code.cpp | 2 | ||||
-rw-r--r-- | clang/test/SemaCXX/warn-unreachable.cpp | 2 | ||||
-rw-r--r-- | clang/test/SemaObjC/warn-unreachable.m | 2 |
9 files changed, 94 insertions, 31 deletions
diff --git a/clang/include/clang/Analysis/Analyses/ReachableCode.h b/clang/include/clang/Analysis/Analyses/ReachableCode.h index 14967ba2981..08ee20930f1 100644 --- a/clang/include/clang/Analysis/Analyses/ReachableCode.h +++ b/clang/include/clang/Analysis/Analyses/ReachableCode.h @@ -37,11 +37,19 @@ namespace clang { namespace clang { namespace reachable_code { +/// Classifications of unreachable code. +enum UnreachableKind { + UK_TrivialReturn, + UK_Break, + UK_Other +}; + class Callback { virtual void anchor(); public: virtual ~Callback() {} - virtual void HandleUnreachable(SourceLocation L, SourceRange R1, + virtual void HandleUnreachable(UnreachableKind UK, + SourceLocation L, SourceRange R1, SourceRange R2) = 0; }; diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 97f0b3ed619..6bb101cfb63 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -424,6 +424,21 @@ def CharSubscript : DiagGroup<"char-subscripts">; def LargeByValueCopy : DiagGroup<"large-by-value-copy">; def DuplicateArgDecl : DiagGroup<"duplicate-method-arg">; +// Unreachable code warning groups. +// +// The goal is make -Wunreachable-code on by default, in -Wall, or at +// least actively used, with more noisy versions of the warning covered +// under separate flags. +// +def UnreachableCode : DiagGroup<"unreachable-code">; +def UnreachableCodeBreak : DiagGroup<"unreachable-code-break", + [UnreachableCode]>; +def UnreachableCodeTrivialReturn : DiagGroup<"unreachable-code-trivial-return", + [UnreachableCode]>; +def UnreachableCodeAggressive : DiagGroup<"unreachable-code-aggressive", + [UnreachableCodeBreak, + UnreachableCodeTrivialReturn]>; + // Aggregation warning settings. // -Widiomatic-parentheses contains warnings about 'idiomatic' diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 7ac2fc1ec97..6cc177215fb 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -359,8 +359,17 @@ def warn_suggest_noreturn_function : Warning< def warn_suggest_noreturn_block : Warning< "block could be declared with attribute 'noreturn'">, InGroup<MissingNoreturn>, DefaultIgnore; -def warn_unreachable : Warning<"will never be executed">, - InGroup<DiagGroup<"unreachable-code">>, DefaultIgnore; + +// Unreachable code. +def warn_unreachable : Warning< + "will never be executed">, + InGroup<UnreachableCode>, DefaultIgnore; +def warn_unreachable_break : Warning< + "'break' will never be executed">, + InGroup<UnreachableCodeBreak>, DefaultIgnore; +def warn_unreachable_trivial_return : Warning< + "'return' will never be executed">, + InGroup<UnreachableCodeTrivialReturn>, DefaultIgnore; /// Built-in functions. def ext_implicit_lib_function_decl : ExtWarn< diff --git a/clang/lib/Analysis/ReachableCode.cpp b/clang/lib/Analysis/ReachableCode.cpp index 994f9d2a12f..09b0efd0e68 100644 --- a/clang/lib/Analysis/ReachableCode.cpp +++ b/clang/lib/Analysis/ReachableCode.cpp @@ -59,9 +59,14 @@ static bool bodyEndsWithNoReturn(const CFGBlock::AdjacentBlock &AB) { return bodyEndsWithNoReturn(Pred); } -static bool isBreakPrecededByNoReturn(const CFGBlock *B, - const Stmt *S) { - if (!isa<BreakStmt>(S) || B->pred_empty()) +static bool isBreakPrecededByNoReturn(const CFGBlock *B, const Stmt *S, + reachable_code::UnreachableKind &UK) { + if (!isa<BreakStmt>(S)) + return false; + + UK = reachable_code::UK_Break; + + if (B->pred_empty()) return false; assert(B->empty()); @@ -131,23 +136,17 @@ static bool isTrivialExpression(const Expr *Ex) { isEnumConstant(Ex); } -static bool isTrivialReturnOrDoWhile(const CFGBlock *B, const Stmt *S) { - const Expr *Ex = dyn_cast<Expr>(S); - - if (Ex && !isTrivialExpression(Ex)) - return false; - +static bool isTrivialReturnOrDoWhile(const CFGBlock *B, const Stmt *S, + reachable_code::UnreachableKind &UK) { // Check if the block ends with a do...while() and see if 'S' is the // condition. if (const Stmt *Term = B->getTerminator()) { - if (const DoStmt *DS = dyn_cast<DoStmt>(Term)) - if (DS->getCond() == S) - return true; + if (const DoStmt *DS = dyn_cast<DoStmt>(Term)) { + const Expr *Cond = DS->getCond(); + return Cond == S && isTrivialExpression(Cond); + } } - if (B->pred_size() != 1) - return false; - // Look to see if the block ends with a 'return', and see if 'S' // is a substatement. The 'return' may not be the last element in // the block because of destructors. @@ -155,17 +154,33 @@ static bool isTrivialReturnOrDoWhile(const CFGBlock *B, const Stmt *S) { I != E; ++I) { if (Optional<CFGStmt> CS = I->getAs<CFGStmt>()) { if (const ReturnStmt *RS = dyn_cast<ReturnStmt>(CS->getStmt())) { + // Determine if we need to lock at the body of the block + // before the dead return. bool LookAtBody = false; - if (RS == S) + if (RS == S) { LookAtBody = true; + UK = reachable_code::UK_TrivialReturn; + } else { const Expr *RE = RS->getRetValue(); - if (RE && stripExprSugar(RE->IgnoreParenCasts()) == Ex) - LookAtBody = true; + if (RE) { + RE = stripExprSugar(RE->IgnoreParenCasts()); + if (RE == S) { + UK = reachable_code::UK_TrivialReturn; + LookAtBody = isTrivialExpression(RE); + } + } } - if (LookAtBody) + if (LookAtBody) { + // More than one predecessor? Restrict the heuristic + // to looking at return statements directly dominated + // by a noreturn call. + if (B->pred_size() != 1) + return false; + return bodyEndsWithNoReturn(*B->pred_begin()); + } } break; } @@ -588,19 +603,22 @@ static SourceLocation GetUnreachableLoc(const Stmt *S, void DeadCodeScan::reportDeadCode(const CFGBlock *B, const Stmt *S, clang::reachable_code::Callback &CB) { + // The kind of unreachable code found. + reachable_code::UnreachableKind UK = reachable_code::UK_Other; + // Suppress idiomatic cases of calling a noreturn function just // before executing a 'break'. If there is other code after the 'break' // in the block then don't suppress the warning. - if (isBreakPrecededByNoReturn(B, S)) + if (isBreakPrecededByNoReturn(B, S, UK)) return; // Suppress trivial 'return' statements that are dead. - if (isTrivialReturnOrDoWhile(B, S)) + if (UK == reachable_code::UK_Other && isTrivialReturnOrDoWhile(B, S, UK)) return; SourceRange R1, R2; SourceLocation Loc = GetUnreachableLoc(S, R1, R2); - CB.HandleUnreachable(Loc, R1, R2); + CB.HandleUnreachable(UK, Loc, R1, R2); } //===----------------------------------------------------------------------===// diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index dabac1e6c4c..d49dfd58459 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -65,9 +65,22 @@ namespace { public: UnreachableCodeHandler(Sema &s) : S(s) {} - void HandleUnreachable(SourceLocation L, SourceRange R1, + void HandleUnreachable(reachable_code::UnreachableKind UK, + SourceLocation L, SourceRange R1, SourceRange R2) override { - S.Diag(L, diag::warn_unreachable) << R1 << R2; + unsigned diag = diag::warn_unreachable; + switch (UK) { + case reachable_code::UK_Break: + diag = diag::warn_unreachable_break; + break; + case reachable_code::UK_TrivialReturn: + diag = diag::warn_unreachable_trivial_return; + break; + case reachable_code::UK_Other: + break; + } + + S.Diag(L, diag) << R1 << R2; } }; } diff --git a/clang/test/Sema/warn-unreachable.c b/clang/test/Sema/warn-unreachable.c index 2334c373813..663fab0f3c3 100644 --- a/clang/test/Sema/warn-unreachable.c +++ b/clang/test/Sema/warn-unreachable.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 %s -fsyntax-only -verify -fblocks -Wunreachable-code -Wno-unused-value -Wno-covered-switch-default -I %S/Inputs +// RUN: %clang_cc1 %s -fsyntax-only -verify -fblocks -Wunreachable-code-aggressive -Wno-unused-value -Wno-covered-switch-default -I %S/Inputs #include "warn-unreachable.h" diff --git a/clang/test/SemaCXX/unreachable-code.cpp b/clang/test/SemaCXX/unreachable-code.cpp index b6b8ab42093..5016c3f24be 100644 --- a/clang/test/SemaCXX/unreachable-code.cpp +++ b/clang/test/SemaCXX/unreachable-code.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -fsyntax-only -Wunreachable-code -fblocks -verify %s +// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -fsyntax-only -Wunreachable-code-aggressive -fblocks -verify %s int j; int bar(); diff --git a/clang/test/SemaCXX/warn-unreachable.cpp b/clang/test/SemaCXX/warn-unreachable.cpp index 49d26daa2f6..843720fa5fd 100644 --- a/clang/test/SemaCXX/warn-unreachable.cpp +++ b/clang/test/SemaCXX/warn-unreachable.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 %s -fcxx-exceptions -fexceptions -fsyntax-only -verify -fblocks -Wunreachable-code -Wno-unused-value +// RUN: %clang_cc1 %s -fcxx-exceptions -fexceptions -fsyntax-only -verify -fblocks -Wunreachable-code-aggressive -Wno-unused-value int &halt() __attribute__((noreturn)); int &live(); diff --git a/clang/test/SemaObjC/warn-unreachable.m b/clang/test/SemaObjC/warn-unreachable.m index e2ce8a71107..979b9053b3d 100644 --- a/clang/test/SemaObjC/warn-unreachable.m +++ b/clang/test/SemaObjC/warn-unreachable.m @@ -1,4 +1,4 @@ -// RUN: %clang %s -fsyntax-only -Xclang -verify -fblocks -Wunreachable-code -Wno-unused-value -Wno-covered-switch-default +// RUN: %clang %s -fsyntax-only -Xclang -verify -fblocks -Wunreachable-code-aggressive -Wno-unused-value -Wno-covered-switch-default // This previously triggered a warning from -Wunreachable-code because of // a busted CFG. |