10b57cec5SDimitry Andric //==- UnreachableCodeChecker.cpp - Generalized dead code checker -*- C++ -*-==// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // This file implements a generalized unreachable code checker using a 90b57cec5SDimitry Andric // path-sensitive analysis. We mark any path visited, and then walk the CFG as a 100b57cec5SDimitry Andric // post-analysis to determine what was never visited. 110b57cec5SDimitry Andric // 120b57cec5SDimitry Andric // A similar flow-sensitive only check exists in Analysis/ReachableCode.cpp 130b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 140b57cec5SDimitry Andric 150b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 160b57cec5SDimitry Andric #include "clang/AST/ParentMap.h" 170b57cec5SDimitry Andric #include "clang/Basic/Builtins.h" 180b57cec5SDimitry Andric #include "clang/Basic/SourceManager.h" 190b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 200b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 210b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h" 220b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 230b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" 240b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" 250b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" 260b57cec5SDimitry Andric #include "llvm/ADT/SmallSet.h" 270b57cec5SDimitry Andric 280b57cec5SDimitry Andric using namespace clang; 290b57cec5SDimitry Andric using namespace ento; 300b57cec5SDimitry Andric 310b57cec5SDimitry Andric namespace { 320b57cec5SDimitry Andric class UnreachableCodeChecker : public Checker<check::EndAnalysis> { 330b57cec5SDimitry Andric public: 340b57cec5SDimitry Andric void checkEndAnalysis(ExplodedGraph &G, BugReporter &B, 350b57cec5SDimitry Andric ExprEngine &Eng) const; 360b57cec5SDimitry Andric private: 370b57cec5SDimitry Andric typedef llvm::SmallSet<unsigned, 32> CFGBlocksSet; 380b57cec5SDimitry Andric 390b57cec5SDimitry Andric static inline const Stmt *getUnreachableStmt(const CFGBlock *CB); 400b57cec5SDimitry Andric static void FindUnreachableEntryPoints(const CFGBlock *CB, 410b57cec5SDimitry Andric CFGBlocksSet &reachable, 420b57cec5SDimitry Andric CFGBlocksSet &visited); 430b57cec5SDimitry Andric static bool isInvalidPath(const CFGBlock *CB, const ParentMap &PM); 440b57cec5SDimitry Andric static inline bool isEmptyCFGBlock(const CFGBlock *CB); 450b57cec5SDimitry Andric }; 460b57cec5SDimitry Andric } 470b57cec5SDimitry Andric 480b57cec5SDimitry Andric void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, 490b57cec5SDimitry Andric BugReporter &B, 500b57cec5SDimitry Andric ExprEngine &Eng) const { 510b57cec5SDimitry Andric CFGBlocksSet reachable, visited; 520b57cec5SDimitry Andric 530b57cec5SDimitry Andric if (Eng.hasWorkRemaining()) 540b57cec5SDimitry Andric return; 550b57cec5SDimitry Andric 560b57cec5SDimitry Andric const Decl *D = nullptr; 570b57cec5SDimitry Andric CFG *C = nullptr; 58a7dea167SDimitry Andric const ParentMap *PM = nullptr; 590b57cec5SDimitry Andric const LocationContext *LC = nullptr; 600b57cec5SDimitry Andric // Iterate over ExplodedGraph 610b57cec5SDimitry Andric for (ExplodedGraph::node_iterator I = G.nodes_begin(), E = G.nodes_end(); 620b57cec5SDimitry Andric I != E; ++I) { 630b57cec5SDimitry Andric const ProgramPoint &P = I->getLocation(); 640b57cec5SDimitry Andric LC = P.getLocationContext(); 650b57cec5SDimitry Andric if (!LC->inTopFrame()) 660b57cec5SDimitry Andric continue; 670b57cec5SDimitry Andric 680b57cec5SDimitry Andric if (!D) 690b57cec5SDimitry Andric D = LC->getAnalysisDeclContext()->getDecl(); 700b57cec5SDimitry Andric 710b57cec5SDimitry Andric // Save the CFG if we don't have it already 720b57cec5SDimitry Andric if (!C) 730b57cec5SDimitry Andric C = LC->getAnalysisDeclContext()->getUnoptimizedCFG(); 740b57cec5SDimitry Andric if (!PM) 750b57cec5SDimitry Andric PM = &LC->getParentMap(); 760b57cec5SDimitry Andric 770b57cec5SDimitry Andric if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { 780b57cec5SDimitry Andric const CFGBlock *CB = BE->getBlock(); 790b57cec5SDimitry Andric reachable.insert(CB->getBlockID()); 800b57cec5SDimitry Andric } 810b57cec5SDimitry Andric } 820b57cec5SDimitry Andric 830b57cec5SDimitry Andric // Bail out if we didn't get the CFG or the ParentMap. 840b57cec5SDimitry Andric if (!D || !C || !PM) 850b57cec5SDimitry Andric return; 860b57cec5SDimitry Andric 870b57cec5SDimitry Andric // Don't do anything for template instantiations. Proving that code 880b57cec5SDimitry Andric // in a template instantiation is unreachable means proving that it is 890b57cec5SDimitry Andric // unreachable in all instantiations. 900b57cec5SDimitry Andric if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) 910b57cec5SDimitry Andric if (FD->isTemplateInstantiation()) 920b57cec5SDimitry Andric return; 930b57cec5SDimitry Andric 940b57cec5SDimitry Andric // Find CFGBlocks that were not covered by any node 950b57cec5SDimitry Andric for (CFG::const_iterator I = C->begin(), E = C->end(); I != E; ++I) { 960b57cec5SDimitry Andric const CFGBlock *CB = *I; 970b57cec5SDimitry Andric // Check if the block is unreachable 980b57cec5SDimitry Andric if (reachable.count(CB->getBlockID())) 990b57cec5SDimitry Andric continue; 1000b57cec5SDimitry Andric 1010b57cec5SDimitry Andric // Check if the block is empty (an artificial block) 1020b57cec5SDimitry Andric if (isEmptyCFGBlock(CB)) 1030b57cec5SDimitry Andric continue; 1040b57cec5SDimitry Andric 1050b57cec5SDimitry Andric // Find the entry points for this block 1060b57cec5SDimitry Andric if (!visited.count(CB->getBlockID())) 1070b57cec5SDimitry Andric FindUnreachableEntryPoints(CB, reachable, visited); 1080b57cec5SDimitry Andric 1090b57cec5SDimitry Andric // This block may have been pruned; check if we still want to report it 1100b57cec5SDimitry Andric if (reachable.count(CB->getBlockID())) 1110b57cec5SDimitry Andric continue; 1120b57cec5SDimitry Andric 1130b57cec5SDimitry Andric // Check for false positives 1140b57cec5SDimitry Andric if (isInvalidPath(CB, *PM)) 1150b57cec5SDimitry Andric continue; 1160b57cec5SDimitry Andric 1170b57cec5SDimitry Andric // It is good practice to always have a "default" label in a "switch", even 1180b57cec5SDimitry Andric // if we should never get there. It can be used to detect errors, for 1190b57cec5SDimitry Andric // instance. Unreachable code directly under a "default" label is therefore 1200b57cec5SDimitry Andric // likely to be a false positive. 1210b57cec5SDimitry Andric if (const Stmt *label = CB->getLabel()) 1220b57cec5SDimitry Andric if (label->getStmtClass() == Stmt::DefaultStmtClass) 1230b57cec5SDimitry Andric continue; 1240b57cec5SDimitry Andric 1250b57cec5SDimitry Andric // Special case for __builtin_unreachable. 1260b57cec5SDimitry Andric // FIXME: This should be extended to include other unreachable markers, 1270b57cec5SDimitry Andric // such as llvm_unreachable. 1280b57cec5SDimitry Andric if (!CB->empty()) { 1290b57cec5SDimitry Andric bool foundUnreachable = false; 1300b57cec5SDimitry Andric for (CFGBlock::const_iterator ci = CB->begin(), ce = CB->end(); 1310b57cec5SDimitry Andric ci != ce; ++ci) { 1320b57cec5SDimitry Andric if (Optional<CFGStmt> S = (*ci).getAs<CFGStmt>()) 1330b57cec5SDimitry Andric if (const CallExpr *CE = dyn_cast<CallExpr>(S->getStmt())) { 1340b57cec5SDimitry Andric if (CE->getBuiltinCallee() == Builtin::BI__builtin_unreachable || 1350b57cec5SDimitry Andric CE->isBuiltinAssumeFalse(Eng.getContext())) { 1360b57cec5SDimitry Andric foundUnreachable = true; 1370b57cec5SDimitry Andric break; 1380b57cec5SDimitry Andric } 1390b57cec5SDimitry Andric } 1400b57cec5SDimitry Andric } 1410b57cec5SDimitry Andric if (foundUnreachable) 1420b57cec5SDimitry Andric continue; 1430b57cec5SDimitry Andric } 1440b57cec5SDimitry Andric 1450b57cec5SDimitry Andric // We found a block that wasn't covered - find the statement to report 1460b57cec5SDimitry Andric SourceRange SR; 1470b57cec5SDimitry Andric PathDiagnosticLocation DL; 1480b57cec5SDimitry Andric SourceLocation SL; 1490b57cec5SDimitry Andric if (const Stmt *S = getUnreachableStmt(CB)) { 1500b57cec5SDimitry Andric // In macros, 'do {...} while (0)' is often used. Don't warn about the 1510b57cec5SDimitry Andric // condition 0 when it is unreachable. 1520b57cec5SDimitry Andric if (S->getBeginLoc().isMacroID()) 1530b57cec5SDimitry Andric if (const auto *I = dyn_cast<IntegerLiteral>(S)) 1540b57cec5SDimitry Andric if (I->getValue() == 0ULL) 1550b57cec5SDimitry Andric if (const Stmt *Parent = PM->getParent(S)) 1560b57cec5SDimitry Andric if (isa<DoStmt>(Parent)) 1570b57cec5SDimitry Andric continue; 1580b57cec5SDimitry Andric SR = S->getSourceRange(); 1590b57cec5SDimitry Andric DL = PathDiagnosticLocation::createBegin(S, B.getSourceManager(), LC); 1600b57cec5SDimitry Andric SL = DL.asLocation(); 1610b57cec5SDimitry Andric if (SR.isInvalid() || !SL.isValid()) 1620b57cec5SDimitry Andric continue; 1630b57cec5SDimitry Andric } 1640b57cec5SDimitry Andric else 1650b57cec5SDimitry Andric continue; 1660b57cec5SDimitry Andric 1670b57cec5SDimitry Andric // Check if the SourceLocation is in a system header 1680b57cec5SDimitry Andric const SourceManager &SM = B.getSourceManager(); 1690b57cec5SDimitry Andric if (SM.isInSystemHeader(SL) || SM.isInExternCSystemHeader(SL)) 1700b57cec5SDimitry Andric continue; 1710b57cec5SDimitry Andric 1720b57cec5SDimitry Andric B.EmitBasicReport(D, this, "Unreachable code", "Dead code", 1730b57cec5SDimitry Andric "This statement is never executed", DL, SR); 1740b57cec5SDimitry Andric } 1750b57cec5SDimitry Andric } 1760b57cec5SDimitry Andric 1770b57cec5SDimitry Andric // Recursively finds the entry point(s) for this dead CFGBlock. 1780b57cec5SDimitry Andric void UnreachableCodeChecker::FindUnreachableEntryPoints(const CFGBlock *CB, 1790b57cec5SDimitry Andric CFGBlocksSet &reachable, 1800b57cec5SDimitry Andric CFGBlocksSet &visited) { 1810b57cec5SDimitry Andric visited.insert(CB->getBlockID()); 1820b57cec5SDimitry Andric 1830b57cec5SDimitry Andric for (CFGBlock::const_pred_iterator I = CB->pred_begin(), E = CB->pred_end(); 1840b57cec5SDimitry Andric I != E; ++I) { 1850b57cec5SDimitry Andric if (!*I) 1860b57cec5SDimitry Andric continue; 1870b57cec5SDimitry Andric 1880b57cec5SDimitry Andric if (!reachable.count((*I)->getBlockID())) { 1890b57cec5SDimitry Andric // If we find an unreachable predecessor, mark this block as reachable so 1900b57cec5SDimitry Andric // we don't report this block 1910b57cec5SDimitry Andric reachable.insert(CB->getBlockID()); 1920b57cec5SDimitry Andric if (!visited.count((*I)->getBlockID())) 1930b57cec5SDimitry Andric // If we haven't previously visited the unreachable predecessor, recurse 1940b57cec5SDimitry Andric FindUnreachableEntryPoints(*I, reachable, visited); 1950b57cec5SDimitry Andric } 1960b57cec5SDimitry Andric } 1970b57cec5SDimitry Andric } 1980b57cec5SDimitry Andric 1990b57cec5SDimitry Andric // Find the Stmt* in a CFGBlock for reporting a warning 2000b57cec5SDimitry Andric const Stmt *UnreachableCodeChecker::getUnreachableStmt(const CFGBlock *CB) { 2010b57cec5SDimitry Andric for (CFGBlock::const_iterator I = CB->begin(), E = CB->end(); I != E; ++I) { 2020b57cec5SDimitry Andric if (Optional<CFGStmt> S = I->getAs<CFGStmt>()) { 2030b57cec5SDimitry Andric if (!isa<DeclStmt>(S->getStmt())) 2040b57cec5SDimitry Andric return S->getStmt(); 2050b57cec5SDimitry Andric } 2060b57cec5SDimitry Andric } 2070b57cec5SDimitry Andric if (const Stmt *S = CB->getTerminatorStmt()) 2080b57cec5SDimitry Andric return S; 2090b57cec5SDimitry Andric else 2100b57cec5SDimitry Andric return nullptr; 2110b57cec5SDimitry Andric } 2120b57cec5SDimitry Andric 2130b57cec5SDimitry Andric // Determines if the path to this CFGBlock contained an element that infers this 2140b57cec5SDimitry Andric // block is a false positive. We assume that FindUnreachableEntryPoints has 2150b57cec5SDimitry Andric // already marked only the entry points to any dead code, so we need only to 2160b57cec5SDimitry Andric // find the condition that led to this block (the predecessor of this block.) 2170b57cec5SDimitry Andric // There will never be more than one predecessor. 2180b57cec5SDimitry Andric bool UnreachableCodeChecker::isInvalidPath(const CFGBlock *CB, 2190b57cec5SDimitry Andric const ParentMap &PM) { 2200b57cec5SDimitry Andric // We only expect a predecessor size of 0 or 1. If it is >1, then an external 2210b57cec5SDimitry Andric // condition has broken our assumption (for example, a sink being placed by 2220b57cec5SDimitry Andric // another check). In these cases, we choose not to report. 2230b57cec5SDimitry Andric if (CB->pred_size() > 1) 2240b57cec5SDimitry Andric return true; 2250b57cec5SDimitry Andric 2260b57cec5SDimitry Andric // If there are no predecessors, then this block is trivially unreachable 2270b57cec5SDimitry Andric if (CB->pred_size() == 0) 2280b57cec5SDimitry Andric return false; 2290b57cec5SDimitry Andric 2300b57cec5SDimitry Andric const CFGBlock *pred = *CB->pred_begin(); 2310b57cec5SDimitry Andric if (!pred) 2320b57cec5SDimitry Andric return false; 2330b57cec5SDimitry Andric 2340b57cec5SDimitry Andric // Get the predecessor block's terminator condition 2350b57cec5SDimitry Andric const Stmt *cond = pred->getTerminatorCondition(); 2360b57cec5SDimitry Andric 2370b57cec5SDimitry Andric //assert(cond && "CFGBlock's predecessor has a terminator condition"); 2380b57cec5SDimitry Andric // The previous assertion is invalid in some cases (eg do/while). Leaving 2390b57cec5SDimitry Andric // reporting of these situations on at the moment to help triage these cases. 2400b57cec5SDimitry Andric if (!cond) 2410b57cec5SDimitry Andric return false; 2420b57cec5SDimitry Andric 2430b57cec5SDimitry Andric // Run each of the checks on the conditions 2440b57cec5SDimitry Andric return containsMacro(cond) || containsEnum(cond) || 2450b57cec5SDimitry Andric containsStaticLocal(cond) || containsBuiltinOffsetOf(cond) || 2460b57cec5SDimitry Andric containsStmt<UnaryExprOrTypeTraitExpr>(cond); 2470b57cec5SDimitry Andric } 2480b57cec5SDimitry Andric 2490b57cec5SDimitry Andric // Returns true if the given CFGBlock is empty 2500b57cec5SDimitry Andric bool UnreachableCodeChecker::isEmptyCFGBlock(const CFGBlock *CB) { 2510b57cec5SDimitry Andric return CB->getLabel() == nullptr // No labels 2520b57cec5SDimitry Andric && CB->size() == 0 // No statements 2530b57cec5SDimitry Andric && !CB->getTerminatorStmt(); // No terminator 2540b57cec5SDimitry Andric } 2550b57cec5SDimitry Andric 2560b57cec5SDimitry Andric void ento::registerUnreachableCodeChecker(CheckerManager &mgr) { 2570b57cec5SDimitry Andric mgr.registerChecker<UnreachableCodeChecker>(); 2580b57cec5SDimitry Andric } 2590b57cec5SDimitry Andric 260*5ffd83dbSDimitry Andric bool ento::shouldRegisterUnreachableCodeChecker(const CheckerManager &mgr) { 2610b57cec5SDimitry Andric return true; 2620b57cec5SDimitry Andric } 263