blob: e3d758f20a52ab0de4057cdaeab5d5a2a70d935f [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();
Tom Carebc42c532010-08-03 01:55:07 +000036 void VisitEndAnalysis(ExplodedGraph &G,
37 BugReporter &B,
38 GRExprEngine &Eng);
Tom Carec4b5bd82010-07-23 23:04:53 +000039private:
Tom Care7bce3a12010-07-27 23:30:21 +000040 typedef bool (*ExplodedNodeHeuristic)(const ExplodedNode &EN);
41
Tom Carec4b5bd82010-07-23 23:04:53 +000042 static SourceLocation GetUnreachableLoc(const CFGBlock &b, SourceRange &R);
43 void FindUnreachableEntryPoints(const CFGBlock *CB);
44 void MarkSuccessorsReachable(const CFGBlock *CB);
Tom Care7bce3a12010-07-27 23:30:21 +000045 static const Expr *getConditon(const Stmt *S);
46 static bool isInvalidPath(const CFGBlock *CB, const ParentMap &PM);
Tom Carec4b5bd82010-07-23 23:04:53 +000047
48 llvm::SmallPtrSet<const CFGBlock*, DEFAULT_CFGBLOCKS> reachable;
49 llvm::SmallPtrSet<const CFGBlock*, DEFAULT_CFGBLOCKS> visited;
50};
51}
52
53void *UnreachableCodeChecker::getTag() {
54 static int x = 0;
55 return &x;
56}
57
58void clang::RegisterUnreachableCodeChecker(GRExprEngine &Eng) {
59 Eng.registerCheck(new UnreachableCodeChecker());
60}
61
62void UnreachableCodeChecker::VisitEndAnalysis(ExplodedGraph &G,
63 BugReporter &B,
Tom Carebc42c532010-08-03 01:55:07 +000064 GRExprEngine &Eng) {
Tom Carec4b5bd82010-07-23 23:04:53 +000065 // Bail out if we didn't cover all paths
Tom Carebc42c532010-08-03 01:55:07 +000066 if (Eng.hasWorkRemaining())
Tom Carec4b5bd82010-07-23 23:04:53 +000067 return;
68
69 CFG *C = 0;
Tom Care7bce3a12010-07-27 23:30:21 +000070 ParentMap *PM = 0;
Tom Carec4b5bd82010-07-23 23:04:53 +000071 // Iterate over ExplodedGraph
72 for (ExplodedGraph::node_iterator I = G.nodes_begin(); I != G.nodes_end();
73 ++I) {
74 const ProgramPoint &P = I->getLocation();
Tom Care7bce3a12010-07-27 23:30:21 +000075 const LocationContext *LC = P.getLocationContext();
Tom Carec4b5bd82010-07-23 23:04:53 +000076
77 // Save the CFG if we don't have it already
78 if (!C)
Tom Care7bce3a12010-07-27 23:30:21 +000079 C = LC->getCFG();
80 if (!PM)
81 PM = &LC->getParentMap();
Tom Carec4b5bd82010-07-23 23:04:53 +000082
83 if (const BlockEntrance *BE = dyn_cast<BlockEntrance>(&P)) {
84 const CFGBlock *CB = BE->getBlock();
85 reachable.insert(CB);
86 }
87 }
88
Tom Care7bce3a12010-07-27 23:30:21 +000089 // Bail out if we didn't get the CFG or the ParentMap.
90 if (!C || !PM)
Tom Carec4b5bd82010-07-23 23:04:53 +000091 return;
92
Jordy Rose5e04bdd2010-07-27 03:39:53 +000093 ASTContext &Ctx = B.getContext();
94
Tom Carec4b5bd82010-07-23 23:04:53 +000095 // Find CFGBlocks that were not covered by any node
96 for (CFG::const_iterator I = C->begin(); I != C->end(); ++I) {
97 const CFGBlock *CB = *I;
98 // Check if the block is unreachable
Tom Care7bce3a12010-07-27 23:30:21 +000099 if (reachable.count(CB))
100 continue;
Tom Carec4b5bd82010-07-23 23:04:53 +0000101
Tom Care7bce3a12010-07-27 23:30:21 +0000102 // Find the entry points for this block
103 FindUnreachableEntryPoints(CB);
Jordy Rose5e04bdd2010-07-27 03:39:53 +0000104
Tom Care7bce3a12010-07-27 23:30:21 +0000105 // This block may have been pruned; check if we still want to report it
106 if (reachable.count(CB))
107 continue;
108
109 // Check for false positives
110 if (CB->size() > 0 && isInvalidPath(CB, *PM))
111 continue;
112
113 // We found a statement that wasn't covered
114 SourceRange S;
115 SourceLocation SL = GetUnreachableLoc(*CB, S);
116 if (S.isInvalid() || SL.isInvalid())
117 continue;
118
119 // Special case for __builtin_unreachable.
120 // FIXME: This should be extended to include other unreachable markers,
121 // such as llvm_unreachable.
122 if (!CB->empty()) {
123 const Stmt *First = CB->front();
124 if (const CallExpr *CE = dyn_cast<CallExpr>(First)) {
125 if (CE->isBuiltinCall(Ctx) == Builtin::BI__builtin_unreachable)
126 continue;
Jordy Rose5e04bdd2010-07-27 03:39:53 +0000127 }
Tom Carec4b5bd82010-07-23 23:04:53 +0000128 }
Tom Care7bce3a12010-07-27 23:30:21 +0000129
130 B.EmitBasicReport("Unreachable code", "This statement is never executed",
131 SL, S);
Tom Carec4b5bd82010-07-23 23:04:53 +0000132 }
133}
134
135// Recursively finds the entry point(s) for this dead CFGBlock.
136void UnreachableCodeChecker::FindUnreachableEntryPoints(const CFGBlock *CB) {
137 bool allPredecessorsReachable = true;
138
139 visited.insert(CB);
140
141 for (CFGBlock::const_pred_iterator I = CB->pred_begin(); I != CB->pred_end();
142 ++I) {
143 // Recurse over all unreachable blocks
144 if (!reachable.count(*I) && !visited.count(*I)) {
145 FindUnreachableEntryPoints(*I);
146 allPredecessorsReachable = false;
147 }
148 }
149
150 // If at least one predecessor is unreachable, mark this block as reachable
151 // so we don't report this block.
152 if (!allPredecessorsReachable) {
153 reachable.insert(CB);
154 }
155}
156
157// Find the SourceLocation and SourceRange to report this CFGBlock
158SourceLocation UnreachableCodeChecker::GetUnreachableLoc(const CFGBlock &b,
159 SourceRange &R) {
160 const Stmt *S = 0;
161 unsigned sn = 0;
162 R = SourceRange();
163
164 if (sn < b.size())
165 S = b[sn].getStmt();
166 else if (b.getTerminator())
167 S = b.getTerminator();
168 else
169 return SourceLocation();
170
171 R = S->getSourceRange();
172 return S->getLocStart();
173}
Tom Care7bce3a12010-07-27 23:30:21 +0000174
175// Returns the Expr* condition if this is a conditional statement, or 0
176// otherwise
177const Expr *UnreachableCodeChecker::getConditon(const Stmt *S) {
178 if (const IfStmt *IS = dyn_cast<IfStmt>(S)) {
179 return IS->getCond();
180 }
181 else if (const SwitchStmt *SS = dyn_cast<SwitchStmt>(S)) {
182 return SS->getCond();
183 }
184 else if (const WhileStmt *WS = dyn_cast<WhileStmt>(S)) {
185 return WS->getCond();
186 }
187 else if (const DoStmt *DS = dyn_cast<DoStmt>(S)) {
188 return DS->getCond();
189 }
190 else if (const ForStmt *FS = dyn_cast<ForStmt>(S)) {
191 return FS->getCond();
192 }
193 else if (const ConditionalOperator *CO = dyn_cast<ConditionalOperator>(S)) {
194 return CO->getCond();
195 }
196
197 return 0;
198}
199
200// Traverse the predecessor Stmt*s from this CFGBlock to find any signs of a
201// path that is a false positive.
202bool UnreachableCodeChecker::isInvalidPath(const CFGBlock *CB,
203 const ParentMap &PM) {
204
205 // Start at the end of the block and work up the path.
206 const Stmt *S = CB->back().getStmt();
207 while (S) {
208 // Find any false positives in the conditions on this path.
209 if (const Expr *cond = getConditon(S)) {
210 if (containsMacro(cond) || containsEnum(cond)
211 || containsStaticLocal(cond) || containsBuiltinOffsetOf(cond)
212 || containsStmt<SizeOfAlignOfExpr>(cond))
213 return true;
214 }
215 // Get the previous statement.
216 S = PM.getParent(S);
217 }
218
219 return false;
220}