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