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