[-Wunreachable-code] Prune out unreachable warnings where a 'break' is preceded by a call to a 'noreturn' function.

For example:

	unreachable();
    break;

This code is idiomatic and defensive.  The fact that 'break' is
unreachable here is not interesting.  This occurs frequently
in LLVM/Clang itself.

llvm-svn: 202328
This commit is contained in:
Ted Kremenek 2014-02-27 00:24:08 +00:00
parent f353919e8e
commit cc893386bc
2 changed files with 64 additions and 7 deletions

View File

@ -49,7 +49,8 @@ public:
const Stmt *findDeadCode(const CFGBlock *Block);
void reportDeadCode(const Stmt *S,
void reportDeadCode(const CFGBlock *B,
const Stmt *S,
clang::reachable_code::Callback &CB);
};
}
@ -153,7 +154,7 @@ unsigned DeadCodeScan::scanBackwards(const clang::CFGBlock *Start,
}
if (isDeadCodeRoot(Block)) {
reportDeadCode(S, CB);
reportDeadCode(Block, S, CB);
count += clang::reachable_code::ScanReachableFromBlock(Block, Reachable);
}
else {
@ -170,11 +171,11 @@ unsigned DeadCodeScan::scanBackwards(const clang::CFGBlock *Start,
llvm::array_pod_sort(DeferredLocs.begin(), DeferredLocs.end(), SrcCmp);
for (DeferredLocsTy::iterator I = DeferredLocs.begin(),
E = DeferredLocs.end(); I != E; ++I) {
const CFGBlock *block = I->first;
if (Reachable[block->getBlockID()])
const CFGBlock *Block = I->first;
if (Reachable[Block->getBlockID()])
continue;
reportDeadCode(I->second, CB);
count += clang::reachable_code::ScanReachableFromBlock(block, Reachable);
reportDeadCode(Block, I->second, CB);
count += clang::reachable_code::ScanReachableFromBlock(Block, Reachable);
}
}
@ -246,8 +247,43 @@ static SourceLocation GetUnreachableLoc(const Stmt *S,
return S->getLocStart();
}
void DeadCodeScan::reportDeadCode(const Stmt *S,
static bool bodyEndsWithNoReturn(const CFGBlock *B) {
for (CFGBlock::const_reverse_iterator I = B->rbegin(), E = B->rend();
I != E; ++I) {
if (Optional<CFGStmt> CS = I->getAs<CFGStmt>()) {
if (const CallExpr *CE = dyn_cast<CallExpr>(CS->getStmt())) {
QualType CalleeType = CE->getCallee()->getType();
if (getFunctionExtInfo(*CalleeType).getNoReturn())
return true;
}
break;
}
}
return false;
}
static bool isBreakPrecededByNoReturn(const CFGBlock *B,
const Stmt *S) {
if (!isa<BreakStmt>(S) || B->pred_empty())
return false;
assert(B->empty());
assert(B->pred_size() == 1);
const CFGBlock::AdjacentBlock &AB = *B->pred_begin();
const CFGBlock *Pred = AB.getPossiblyUnreachableBlock();
assert(!AB.isReachable() && Pred);
return bodyEndsWithNoReturn(Pred);
}
void DeadCodeScan::reportDeadCode(const CFGBlock *B,
const Stmt *S,
clang::reachable_code::Callback &CB) {
// 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))
return;
SourceRange R1, R2;
SourceLocation Loc = GetUnreachableLoc(S, R1, R2);
CB.HandleUnreachable(Loc, R1, R2);

View File

@ -143,3 +143,24 @@ void test_mul_and_zero(int x) {
if (x * 0) calledFun(); // expected-warning {{will never be executed}}
if (0 * x) calledFun(); // expected-warning {{will never be executed}}
}
void raze() __attribute__((noreturn));
void warn_here();
int test_break_preceded_by_noreturn(int i) {
switch (i) {
case 1:
raze();
break; // no-warning
case 2:
raze();
break; // no-warning
warn_here(); // expected-warning {{will never be executed}}
case 3:
return 1;
break; // expected-warning {{will never be executed}}
default:
break;
}
return i;
}