summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/include/clang/Analysis/Analyses/ReachableCode.h10
-rw-r--r--clang/include/clang/Basic/DiagnosticGroups.td15
-rw-r--r--clang/include/clang/Basic/DiagnosticSemaKinds.td13
-rw-r--r--clang/lib/Analysis/ReachableCode.cpp62
-rw-r--r--clang/lib/Sema/AnalysisBasedWarnings.cpp17
-rw-r--r--clang/test/Sema/warn-unreachable.c2
-rw-r--r--clang/test/SemaCXX/unreachable-code.cpp2
-rw-r--r--clang/test/SemaCXX/warn-unreachable.cpp2
-rw-r--r--clang/test/SemaObjC/warn-unreachable.m2
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.
OpenPOWER on IntegriCloud