Coverage Report

Created: 2023-09-21 18:56

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