1*0b57cec5SDimitry Andric //==- UnreachableCodeChecker.cpp - Generalized dead code checker -*- C++ -*-==// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric // This file implements a generalized unreachable code checker using a 9*0b57cec5SDimitry Andric // path-sensitive analysis. We mark any path visited, and then walk the CFG as a 10*0b57cec5SDimitry Andric // post-analysis to determine what was never visited. 11*0b57cec5SDimitry Andric // 12*0b57cec5SDimitry Andric // A similar flow-sensitive only check exists in Analysis/ReachableCode.cpp 13*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 14*0b57cec5SDimitry Andric 15*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 16*0b57cec5SDimitry Andric #include "clang/AST/ParentMap.h" 17*0b57cec5SDimitry Andric #include "clang/Basic/Builtins.h" 18*0b57cec5SDimitry Andric #include "clang/Basic/SourceManager.h" 19*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 20*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 21*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h" 22*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 23*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" 24*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" 25*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" 26*0b57cec5SDimitry Andric #include "llvm/ADT/SmallSet.h" 27*0b57cec5SDimitry Andric 28*0b57cec5SDimitry Andric using namespace clang; 29*0b57cec5SDimitry Andric using namespace ento; 30*0b57cec5SDimitry Andric 31*0b57cec5SDimitry Andric namespace { 32*0b57cec5SDimitry Andric class UnreachableCodeChecker : public Checker<check::EndAnalysis> { 33*0b57cec5SDimitry Andric public: 34*0b57cec5SDimitry Andric void checkEndAnalysis(ExplodedGraph &G, BugReporter &B, 35*0b57cec5SDimitry Andric ExprEngine &Eng) const; 36*0b57cec5SDimitry Andric private: 37*0b57cec5SDimitry Andric typedef llvm::SmallSet<unsigned, 32> CFGBlocksSet; 38*0b57cec5SDimitry Andric 39*0b57cec5SDimitry Andric static inline const Stmt *getUnreachableStmt(const CFGBlock *CB); 40*0b57cec5SDimitry Andric static void FindUnreachableEntryPoints(const CFGBlock *CB, 41*0b57cec5SDimitry Andric CFGBlocksSet &reachable, 42*0b57cec5SDimitry Andric CFGBlocksSet &visited); 43*0b57cec5SDimitry Andric static bool isInvalidPath(const CFGBlock *CB, const ParentMap &PM); 44*0b57cec5SDimitry Andric static inline bool isEmptyCFGBlock(const CFGBlock *CB); 45*0b57cec5SDimitry Andric }; 46*0b57cec5SDimitry Andric } 47*0b57cec5SDimitry Andric 48*0b57cec5SDimitry Andric void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, 49*0b57cec5SDimitry Andric BugReporter &B, 50*0b57cec5SDimitry Andric ExprEngine &Eng) const { 51*0b57cec5SDimitry Andric CFGBlocksSet reachable, visited; 52*0b57cec5SDimitry Andric 53*0b57cec5SDimitry Andric if (Eng.hasWorkRemaining()) 54*0b57cec5SDimitry Andric return; 55*0b57cec5SDimitry Andric 56*0b57cec5SDimitry Andric const Decl *D = nullptr; 57*0b57cec5SDimitry Andric CFG *C = nullptr; 58*0b57cec5SDimitry Andric ParentMap *PM = nullptr; 59*0b57cec5SDimitry Andric const LocationContext *LC = nullptr; 60*0b57cec5SDimitry Andric // Iterate over ExplodedGraph 61*0b57cec5SDimitry Andric for (ExplodedGraph::node_iterator I = G.nodes_begin(), E = G.nodes_end(); 62*0b57cec5SDimitry Andric I != E; ++I) { 63*0b57cec5SDimitry Andric const ProgramPoint &P = I->getLocation(); 64*0b57cec5SDimitry Andric LC = P.getLocationContext(); 65*0b57cec5SDimitry Andric if (!LC->inTopFrame()) 66*0b57cec5SDimitry Andric continue; 67*0b57cec5SDimitry Andric 68*0b57cec5SDimitry Andric if (!D) 69*0b57cec5SDimitry Andric D = LC->getAnalysisDeclContext()->getDecl(); 70*0b57cec5SDimitry Andric 71*0b57cec5SDimitry Andric // Save the CFG if we don't have it already 72*0b57cec5SDimitry Andric if (!C) 73*0b57cec5SDimitry Andric C = LC->getAnalysisDeclContext()->getUnoptimizedCFG(); 74*0b57cec5SDimitry Andric if (!PM) 75*0b57cec5SDimitry Andric PM = &LC->getParentMap(); 76*0b57cec5SDimitry Andric 77*0b57cec5SDimitry Andric if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { 78*0b57cec5SDimitry Andric const CFGBlock *CB = BE->getBlock(); 79*0b57cec5SDimitry Andric reachable.insert(CB->getBlockID()); 80*0b57cec5SDimitry Andric } 81*0b57cec5SDimitry Andric } 82*0b57cec5SDimitry Andric 83*0b57cec5SDimitry Andric // Bail out if we didn't get the CFG or the ParentMap. 84*0b57cec5SDimitry Andric if (!D || !C || !PM) 85*0b57cec5SDimitry Andric return; 86*0b57cec5SDimitry Andric 87*0b57cec5SDimitry Andric // Don't do anything for template instantiations. Proving that code 88*0b57cec5SDimitry Andric // in a template instantiation is unreachable means proving that it is 89*0b57cec5SDimitry Andric // unreachable in all instantiations. 90*0b57cec5SDimitry Andric if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) 91*0b57cec5SDimitry Andric if (FD->isTemplateInstantiation()) 92*0b57cec5SDimitry Andric return; 93*0b57cec5SDimitry Andric 94*0b57cec5SDimitry Andric // Find CFGBlocks that were not covered by any node 95*0b57cec5SDimitry Andric for (CFG::const_iterator I = C->begin(), E = C->end(); I != E; ++I) { 96*0b57cec5SDimitry Andric const CFGBlock *CB = *I; 97*0b57cec5SDimitry Andric // Check if the block is unreachable 98*0b57cec5SDimitry Andric if (reachable.count(CB->getBlockID())) 99*0b57cec5SDimitry Andric continue; 100*0b57cec5SDimitry Andric 101*0b57cec5SDimitry Andric // Check if the block is empty (an artificial block) 102*0b57cec5SDimitry Andric if (isEmptyCFGBlock(CB)) 103*0b57cec5SDimitry Andric continue; 104*0b57cec5SDimitry Andric 105*0b57cec5SDimitry Andric // Find the entry points for this block 106*0b57cec5SDimitry Andric if (!visited.count(CB->getBlockID())) 107*0b57cec5SDimitry Andric FindUnreachableEntryPoints(CB, reachable, visited); 108*0b57cec5SDimitry Andric 109*0b57cec5SDimitry Andric // This block may have been pruned; check if we still want to report it 110*0b57cec5SDimitry Andric if (reachable.count(CB->getBlockID())) 111*0b57cec5SDimitry Andric continue; 112*0b57cec5SDimitry Andric 113*0b57cec5SDimitry Andric // Check for false positives 114*0b57cec5SDimitry Andric if (isInvalidPath(CB, *PM)) 115*0b57cec5SDimitry Andric continue; 116*0b57cec5SDimitry Andric 117*0b57cec5SDimitry Andric // It is good practice to always have a "default" label in a "switch", even 118*0b57cec5SDimitry Andric // if we should never get there. It can be used to detect errors, for 119*0b57cec5SDimitry Andric // instance. Unreachable code directly under a "default" label is therefore 120*0b57cec5SDimitry Andric // likely to be a false positive. 121*0b57cec5SDimitry Andric if (const Stmt *label = CB->getLabel()) 122*0b57cec5SDimitry Andric if (label->getStmtClass() == Stmt::DefaultStmtClass) 123*0b57cec5SDimitry Andric continue; 124*0b57cec5SDimitry Andric 125*0b57cec5SDimitry Andric // Special case for __builtin_unreachable. 126*0b57cec5SDimitry Andric // FIXME: This should be extended to include other unreachable markers, 127*0b57cec5SDimitry Andric // such as llvm_unreachable. 128*0b57cec5SDimitry Andric if (!CB->empty()) { 129*0b57cec5SDimitry Andric bool foundUnreachable = false; 130*0b57cec5SDimitry Andric for (CFGBlock::const_iterator ci = CB->begin(), ce = CB->end(); 131*0b57cec5SDimitry Andric ci != ce; ++ci) { 132*0b57cec5SDimitry Andric if (Optional<CFGStmt> S = (*ci).getAs<CFGStmt>()) 133*0b57cec5SDimitry Andric if (const CallExpr *CE = dyn_cast<CallExpr>(S->getStmt())) { 134*0b57cec5SDimitry Andric if (CE->getBuiltinCallee() == Builtin::BI__builtin_unreachable || 135*0b57cec5SDimitry Andric CE->isBuiltinAssumeFalse(Eng.getContext())) { 136*0b57cec5SDimitry Andric foundUnreachable = true; 137*0b57cec5SDimitry Andric break; 138*0b57cec5SDimitry Andric } 139*0b57cec5SDimitry Andric } 140*0b57cec5SDimitry Andric } 141*0b57cec5SDimitry Andric if (foundUnreachable) 142*0b57cec5SDimitry Andric continue; 143*0b57cec5SDimitry Andric } 144*0b57cec5SDimitry Andric 145*0b57cec5SDimitry Andric // We found a block that wasn't covered - find the statement to report 146*0b57cec5SDimitry Andric SourceRange SR; 147*0b57cec5SDimitry Andric PathDiagnosticLocation DL; 148*0b57cec5SDimitry Andric SourceLocation SL; 149*0b57cec5SDimitry Andric if (const Stmt *S = getUnreachableStmt(CB)) { 150*0b57cec5SDimitry Andric // In macros, 'do {...} while (0)' is often used. Don't warn about the 151*0b57cec5SDimitry Andric // condition 0 when it is unreachable. 152*0b57cec5SDimitry Andric if (S->getBeginLoc().isMacroID()) 153*0b57cec5SDimitry Andric if (const auto *I = dyn_cast<IntegerLiteral>(S)) 154*0b57cec5SDimitry Andric if (I->getValue() == 0ULL) 155*0b57cec5SDimitry Andric if (const Stmt *Parent = PM->getParent(S)) 156*0b57cec5SDimitry Andric if (isa<DoStmt>(Parent)) 157*0b57cec5SDimitry Andric continue; 158*0b57cec5SDimitry Andric SR = S->getSourceRange(); 159*0b57cec5SDimitry Andric DL = PathDiagnosticLocation::createBegin(S, B.getSourceManager(), LC); 160*0b57cec5SDimitry Andric SL = DL.asLocation(); 161*0b57cec5SDimitry Andric if (SR.isInvalid() || !SL.isValid()) 162*0b57cec5SDimitry Andric continue; 163*0b57cec5SDimitry Andric } 164*0b57cec5SDimitry Andric else 165*0b57cec5SDimitry Andric continue; 166*0b57cec5SDimitry Andric 167*0b57cec5SDimitry Andric // Check if the SourceLocation is in a system header 168*0b57cec5SDimitry Andric const SourceManager &SM = B.getSourceManager(); 169*0b57cec5SDimitry Andric if (SM.isInSystemHeader(SL) || SM.isInExternCSystemHeader(SL)) 170*0b57cec5SDimitry Andric continue; 171*0b57cec5SDimitry Andric 172*0b57cec5SDimitry Andric B.EmitBasicReport(D, this, "Unreachable code", "Dead code", 173*0b57cec5SDimitry Andric "This statement is never executed", DL, SR); 174*0b57cec5SDimitry Andric } 175*0b57cec5SDimitry Andric } 176*0b57cec5SDimitry Andric 177*0b57cec5SDimitry Andric // Recursively finds the entry point(s) for this dead CFGBlock. 178*0b57cec5SDimitry Andric void UnreachableCodeChecker::FindUnreachableEntryPoints(const CFGBlock *CB, 179*0b57cec5SDimitry Andric CFGBlocksSet &reachable, 180*0b57cec5SDimitry Andric CFGBlocksSet &visited) { 181*0b57cec5SDimitry Andric visited.insert(CB->getBlockID()); 182*0b57cec5SDimitry Andric 183*0b57cec5SDimitry Andric for (CFGBlock::const_pred_iterator I = CB->pred_begin(), E = CB->pred_end(); 184*0b57cec5SDimitry Andric I != E; ++I) { 185*0b57cec5SDimitry Andric if (!*I) 186*0b57cec5SDimitry Andric continue; 187*0b57cec5SDimitry Andric 188*0b57cec5SDimitry Andric if (!reachable.count((*I)->getBlockID())) { 189*0b57cec5SDimitry Andric // If we find an unreachable predecessor, mark this block as reachable so 190*0b57cec5SDimitry Andric // we don't report this block 191*0b57cec5SDimitry Andric reachable.insert(CB->getBlockID()); 192*0b57cec5SDimitry Andric if (!visited.count((*I)->getBlockID())) 193*0b57cec5SDimitry Andric // If we haven't previously visited the unreachable predecessor, recurse 194*0b57cec5SDimitry Andric FindUnreachableEntryPoints(*I, reachable, visited); 195*0b57cec5SDimitry Andric } 196*0b57cec5SDimitry Andric } 197*0b57cec5SDimitry Andric } 198*0b57cec5SDimitry Andric 199*0b57cec5SDimitry Andric // Find the Stmt* in a CFGBlock for reporting a warning 200*0b57cec5SDimitry Andric const Stmt *UnreachableCodeChecker::getUnreachableStmt(const CFGBlock *CB) { 201*0b57cec5SDimitry Andric for (CFGBlock::const_iterator I = CB->begin(), E = CB->end(); I != E; ++I) { 202*0b57cec5SDimitry Andric if (Optional<CFGStmt> S = I->getAs<CFGStmt>()) { 203*0b57cec5SDimitry Andric if (!isa<DeclStmt>(S->getStmt())) 204*0b57cec5SDimitry Andric return S->getStmt(); 205*0b57cec5SDimitry Andric } 206*0b57cec5SDimitry Andric } 207*0b57cec5SDimitry Andric if (const Stmt *S = CB->getTerminatorStmt()) 208*0b57cec5SDimitry Andric return S; 209*0b57cec5SDimitry Andric else 210*0b57cec5SDimitry Andric return nullptr; 211*0b57cec5SDimitry Andric } 212*0b57cec5SDimitry Andric 213*0b57cec5SDimitry Andric // Determines if the path to this CFGBlock contained an element that infers this 214*0b57cec5SDimitry Andric // block is a false positive. We assume that FindUnreachableEntryPoints has 215*0b57cec5SDimitry Andric // already marked only the entry points to any dead code, so we need only to 216*0b57cec5SDimitry Andric // find the condition that led to this block (the predecessor of this block.) 217*0b57cec5SDimitry Andric // There will never be more than one predecessor. 218*0b57cec5SDimitry Andric bool UnreachableCodeChecker::isInvalidPath(const CFGBlock *CB, 219*0b57cec5SDimitry Andric const ParentMap &PM) { 220*0b57cec5SDimitry Andric // We only expect a predecessor size of 0 or 1. If it is >1, then an external 221*0b57cec5SDimitry Andric // condition has broken our assumption (for example, a sink being placed by 222*0b57cec5SDimitry Andric // another check). In these cases, we choose not to report. 223*0b57cec5SDimitry Andric if (CB->pred_size() > 1) 224*0b57cec5SDimitry Andric return true; 225*0b57cec5SDimitry Andric 226*0b57cec5SDimitry Andric // If there are no predecessors, then this block is trivially unreachable 227*0b57cec5SDimitry Andric if (CB->pred_size() == 0) 228*0b57cec5SDimitry Andric return false; 229*0b57cec5SDimitry Andric 230*0b57cec5SDimitry Andric const CFGBlock *pred = *CB->pred_begin(); 231*0b57cec5SDimitry Andric if (!pred) 232*0b57cec5SDimitry Andric return false; 233*0b57cec5SDimitry Andric 234*0b57cec5SDimitry Andric // Get the predecessor block's terminator condition 235*0b57cec5SDimitry Andric const Stmt *cond = pred->getTerminatorCondition(); 236*0b57cec5SDimitry Andric 237*0b57cec5SDimitry Andric //assert(cond && "CFGBlock's predecessor has a terminator condition"); 238*0b57cec5SDimitry Andric // The previous assertion is invalid in some cases (eg do/while). Leaving 239*0b57cec5SDimitry Andric // reporting of these situations on at the moment to help triage these cases. 240*0b57cec5SDimitry Andric if (!cond) 241*0b57cec5SDimitry Andric return false; 242*0b57cec5SDimitry Andric 243*0b57cec5SDimitry Andric // Run each of the checks on the conditions 244*0b57cec5SDimitry Andric return containsMacro(cond) || containsEnum(cond) || 245*0b57cec5SDimitry Andric containsStaticLocal(cond) || containsBuiltinOffsetOf(cond) || 246*0b57cec5SDimitry Andric containsStmt<UnaryExprOrTypeTraitExpr>(cond); 247*0b57cec5SDimitry Andric } 248*0b57cec5SDimitry Andric 249*0b57cec5SDimitry Andric // Returns true if the given CFGBlock is empty 250*0b57cec5SDimitry Andric bool UnreachableCodeChecker::isEmptyCFGBlock(const CFGBlock *CB) { 251*0b57cec5SDimitry Andric return CB->getLabel() == nullptr // No labels 252*0b57cec5SDimitry Andric && CB->size() == 0 // No statements 253*0b57cec5SDimitry Andric && !CB->getTerminatorStmt(); // No terminator 254*0b57cec5SDimitry Andric } 255*0b57cec5SDimitry Andric 256*0b57cec5SDimitry Andric void ento::registerUnreachableCodeChecker(CheckerManager &mgr) { 257*0b57cec5SDimitry Andric mgr.registerChecker<UnreachableCodeChecker>(); 258*0b57cec5SDimitry Andric } 259*0b57cec5SDimitry Andric 260*0b57cec5SDimitry Andric bool ento::shouldRegisterUnreachableCodeChecker(const LangOptions &LO) { 261*0b57cec5SDimitry Andric return true; 262*0b57cec5SDimitry Andric } 263