blob: e95e6a832d05524ef0bd6317cfd6b14ce0234dcd [file] [log] [blame]
Tom Carec4b5bd82010-07-23 23:04:53 +00001//==- UnreachableCodeChecker.cpp - Generalized dead code checker -*- C++ -*-==//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9// This file implements a generalized unreachable code checker using a
10// path-sensitive analysis. We mark any path visited, and then walk the CFG as a
11// post-analysis to determine what was never visited.
12//
Jordy Rose5e04bdd2010-07-27 03:39:53 +000013// A similar flow-sensitive only check exists in Analysis/ReachableCode.cpp
Tom Carec4b5bd82010-07-23 23:04:53 +000014//===----------------------------------------------------------------------===//
15
Tom Care7bce3a12010-07-27 23:30:21 +000016#include "clang/AST/ParentMap.h"
17#include "clang/Basic/Builtins.h"
Tom Carec4b5bd82010-07-23 23:04:53 +000018#include "clang/Checker/PathSensitive/CheckerVisitor.h"
19#include "clang/Checker/PathSensitive/ExplodedGraph.h"
20#include "clang/Checker/PathSensitive/SVals.h"
Tom Care7bce3a12010-07-27 23:30:21 +000021#include "clang/Checker/PathSensitive/CheckerHelpers.h"
Tom Carec4b5bd82010-07-23 23:04:53 +000022#include "clang/Checker/BugReporter/BugReporter.h"
23#include "GRExprEngineExperimentalChecks.h"
Tom Carec4b5bd82010-07-23 23:04:53 +000024#include "llvm/ADT/SmallPtrSet.h"
25
26// The number of CFGBlock pointers we want to reserve memory for. This is used
27// once for each function we analyze.
28#define DEFAULT_CFGBLOCKS 256
29
30using namespace clang;
31
32namespace {
33class UnreachableCodeChecker : public CheckerVisitor<UnreachableCodeChecker> {
34public:
35 static void *getTag();
36 void VisitEndAnalysis(ExplodedGraph &G, BugReporter &B,
37 bool hasWorkRemaining);
38private:
Tom Care7bce3a12010-07-27 23:30:21 +000039 typedef bool (*ExplodedNodeHeuristic)(const ExplodedNode &EN);
40
Tom Carec4b5bd82010-07-23 23:04:53 +000041 static SourceLocation GetUnreachableLoc(const CFGBlock &b, SourceRange &R);
42 void FindUnreachableEntryPoints(const CFGBlock *CB);
43 void MarkSuccessorsReachable(const CFGBlock *CB);
Tom Care7bce3a12010-07-27 23:30:21 +000044 static const Expr *getConditon(const Stmt *S);
45 static bool isInvalidPath(const CFGBlock *CB, const ParentMap &PM);
Tom Carec4b5bd82010-07-23 23:04:53 +000046
47 llvm::SmallPtrSet<const CFGBlock*, DEFAULT_CFGBLOCKS> reachable;
48 llvm::SmallPtrSet<const CFGBlock*, DEFAULT_CFGBLOCKS> visited;
49};
50}
51
52void *UnreachableCodeChecker::getTag() {
53 static int x = 0;
54 return &x;
55}
56
57void clang::RegisterUnreachableCodeChecker(GRExprEngine &Eng) {
58 Eng.registerCheck(new UnreachableCodeChecker());
59}
60
61void UnreachableCodeChecker::VisitEndAnalysis(ExplodedGraph &G,
62 BugReporter &B,
63 bool hasWorkRemaining) {
64 // Bail out if we didn't cover all paths
65 if (hasWorkRemaining)
66 return;
67
68 CFG *C = 0;
Tom Care7bce3a12010-07-27 23:30:21 +000069 ParentMap *PM = 0;
Tom Carec4b5bd82010-07-23 23:04:53 +000070 // Iterate over ExplodedGraph
71 for (ExplodedGraph::node_iterator I = G.nodes_begin(); I != G.nodes_end();
72 ++I) {
73 const ProgramPoint &P = I->getLocation();
Tom Care7bce3a12010-07-27 23:30:21 +000074 const LocationContext *LC = P.getLocationContext();
Tom Carec4b5bd82010-07-23 23:04:53 +000075
76 // Save the CFG if we don't have it already
77 if (!C)
Tom Care7bce3a12010-07-27 23:30:21 +000078 C = LC->getCFG();
79 if (!PM)
80 PM = &LC->getParentMap();
Tom Carec4b5bd82010-07-23 23:04:53 +000081
82 if (const BlockEntrance *BE = dyn_cast<BlockEntrance>(&P)) {
83 const CFGBlock *CB = BE->getBlock();
84 reachable.insert(CB);
85 }
86 }
87
Tom Care7bce3a12010-07-27 23:30:21 +000088 // Bail out if we didn't get the CFG or the ParentMap.
89 if (!C || !PM)
Tom Carec4b5bd82010-07-23 23:04:53 +000090 return;
91
Jordy Rose5e04bdd2010-07-27 03:39:53 +000092 ASTContext &Ctx = B.getContext();
93
Tom Carec4b5bd82010-07-23 23:04:53 +000094 // Find CFGBlocks that were not covered by any node
95 for (CFG::const_iterator I = C->begin(); I != C->end(); ++I) {
96 const CFGBlock *CB = *I;
97 // Check if the block is unreachable
Tom Care7bce3a12010-07-27 23:30:21 +000098 if (reachable.count(CB))
99 continue;
Tom Carec4b5bd82010-07-23 23:04:53 +0000100
Tom Care7bce3a12010-07-27 23:30:21 +0000101 // Find the entry points for this block
102 FindUnreachableEntryPoints(CB);
Jordy Rose5e04bdd2010-07-27 03:39:53 +0000103
Tom Care7bce3a12010-07-27 23:30:21 +0000104 // This block may have been pruned; check if we still want to report it
105 if (reachable.count(CB))
106 continue;
107
108 // Check for false positives
109 if (CB->size() > 0 && isInvalidPath(CB, *PM))
110 continue;
111
112 // We found a statement that wasn't covered
113 SourceRange S;
114 SourceLocation SL = GetUnreachableLoc(*CB, S);
115 if (S.isInvalid() || SL.isInvalid())
116 continue;
117
118 // Special case for __builtin_unreachable.
119 // FIXME: This should be extended to include other unreachable markers,
120 // such as llvm_unreachable.
121 if (!CB->empty()) {
122 const Stmt *First = CB->front();
123 if (const CallExpr *CE = dyn_cast<CallExpr>(First)) {
124 if (CE->isBuiltinCall(Ctx) == Builtin::BI__builtin_unreachable)
125 continue;
Jordy Rose5e04bdd2010-07-27 03:39:53 +0000126 }
Tom Carec4b5bd82010-07-23 23:04:53 +0000127 }
Tom Care7bce3a12010-07-27 23:30:21 +0000128
129 B.EmitBasicReport("Unreachable code", "This statement is never executed",
130 SL, S);
Tom Carec4b5bd82010-07-23 23:04:53 +0000131 }
132}
133
134// Recursively finds the entry point(s) for this dead CFGBlock.
135void UnreachableCodeChecker::FindUnreachableEntryPoints(const CFGBlock *CB) {
136 bool allPredecessorsReachable = true;
137
138 visited.insert(CB);
139
140 for (CFGBlock::const_pred_iterator I = CB->pred_begin(); I != CB->pred_end();
141 ++I) {
142 // Recurse over all unreachable blocks
143 if (!reachable.count(*I) && !visited.count(*I)) {
144 FindUnreachableEntryPoints(*I);
145 allPredecessorsReachable = false;
146 }
147 }
148
149 // If at least one predecessor is unreachable, mark this block as reachable
150 // so we don't report this block.
151 if (!allPredecessorsReachable) {
152 reachable.insert(CB);
153 }
154}
155
156// Find the SourceLocation and SourceRange to report this CFGBlock
157SourceLocation UnreachableCodeChecker::GetUnreachableLoc(const CFGBlock &b,
158 SourceRange &R) {
159 const Stmt *S = 0;
160 unsigned sn = 0;
161 R = SourceRange();
162
163 if (sn < b.size())
164 S = b[sn].getStmt();
165 else if (b.getTerminator())
166 S = b.getTerminator();
167 else
168 return SourceLocation();
169
170 R = S->getSourceRange();
171 return S->getLocStart();
172}
Tom Care7bce3a12010-07-27 23:30:21 +0000173
174// Returns the Expr* condition if this is a conditional statement, or 0
175// otherwise
176const Expr *UnreachableCodeChecker::getConditon(const Stmt *S) {
177 if (const IfStmt *IS = dyn_cast<IfStmt>(S)) {
178 return IS->getCond();
179 }
180 else if (const SwitchStmt *SS = dyn_cast<SwitchStmt>(S)) {
181 return SS->getCond();
182 }
183 else if (const WhileStmt *WS = dyn_cast<WhileStmt>(S)) {
184 return WS->getCond();
185 }
186 else if (const DoStmt *DS = dyn_cast<DoStmt>(S)) {
187 return DS->getCond();
188 }
189 else if (const ForStmt *FS = dyn_cast<ForStmt>(S)) {
190 return FS->getCond();
191 }
192 else if (const ConditionalOperator *CO = dyn_cast<ConditionalOperator>(S)) {
193 return CO->getCond();
194 }
195
196 return 0;
197}
198
199// Traverse the predecessor Stmt*s from this CFGBlock to find any signs of a
200// path that is a false positive.
201bool UnreachableCodeChecker::isInvalidPath(const CFGBlock *CB,
202 const ParentMap &PM) {
203
204 // Start at the end of the block and work up the path.
205 const Stmt *S = CB->back().getStmt();
206 while (S) {
207 // Find any false positives in the conditions on this path.
208 if (const Expr *cond = getConditon(S)) {
209 if (containsMacro(cond) || containsEnum(cond)
210 || containsStaticLocal(cond) || containsBuiltinOffsetOf(cond)
211 || containsStmt<SizeOfAlignOfExpr>(cond))
212 return true;
213 }
214 // Get the previous statement.
215 S = PM.getParent(S);
216 }
217
218 return false;
219}