UnreachableCodeChecker.cpp 9.6 KB

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