1 | //===-- ReachableCode.cpp - Code Reachability Analysis --------------------===// |
---|---|
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 | // |
9 | // This file implements a flow-sensitive, path-insensitive analysis of |
10 | // determining reachable blocks within a CFG. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "clang/Analysis/Analyses/ReachableCode.h" |
15 | #include "clang/AST/Attr.h" |
16 | #include "clang/AST/DynamicRecursiveASTVisitor.h" |
17 | #include "clang/AST/Expr.h" |
18 | #include "clang/AST/ExprCXX.h" |
19 | #include "clang/AST/ExprObjC.h" |
20 | #include "clang/AST/ParentMap.h" |
21 | #include "clang/AST/StmtCXX.h" |
22 | #include "clang/Analysis/AnalysisDeclContext.h" |
23 | #include "clang/Analysis/CFG.h" |
24 | #include "clang/Basic/Builtins.h" |
25 | #include "clang/Basic/SourceManager.h" |
26 | #include "clang/Lex/Preprocessor.h" |
27 | #include "llvm/ADT/BitVector.h" |
28 | #include <optional> |
29 | |
30 | using namespace clang; |
31 | |
32 | //===----------------------------------------------------------------------===// |
33 | // Core Reachability Analysis routines. |
34 | //===----------------------------------------------------------------------===// |
35 | |
36 | static bool isEnumConstant(const Expr *Ex) { |
37 | const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Val: Ex); |
38 | if (!DR) |
39 | return false; |
40 | return isa<EnumConstantDecl>(Val: DR->getDecl()); |
41 | } |
42 | |
43 | static bool isTrivialExpression(const Expr *Ex) { |
44 | Ex = Ex->IgnoreParenCasts(); |
45 | return isa<IntegerLiteral>(Val: Ex) || isa<StringLiteral>(Val: Ex) || |
46 | isa<CXXBoolLiteralExpr>(Val: Ex) || isa<ObjCBoolLiteralExpr>(Val: Ex) || |
47 | isa<CharacterLiteral>(Val: Ex) || |
48 | isEnumConstant(Ex); |
49 | } |
50 | |
51 | static bool isTrivialDoWhile(const CFGBlock *B, const Stmt *S) { |
52 | // Check if the block ends with a do...while() and see if 'S' is the |
53 | // condition. |
54 | if (const Stmt *Term = B->getTerminatorStmt()) { |
55 | if (const DoStmt *DS = dyn_cast<DoStmt>(Val: Term)) { |
56 | const Expr *Cond = DS->getCond()->IgnoreParenCasts(); |
57 | return Cond == S && isTrivialExpression(Ex: Cond); |
58 | } |
59 | } |
60 | return false; |
61 | } |
62 | |
63 | static bool isBuiltinUnreachable(const Stmt *S) { |
64 | if (const auto *DRE = dyn_cast<DeclRefExpr>(S)) |
65 | if (const auto *FDecl = dyn_cast<FunctionDecl>(DRE->getDecl())) |
66 | return FDecl->getIdentifier() && |
67 | FDecl->getBuiltinID() == Builtin::BI__builtin_unreachable; |
68 | return false; |
69 | } |
70 | |
71 | static bool isBuiltinAssumeFalse(const CFGBlock *B, const Stmt *S, |
72 | ASTContext &C) { |
73 | if (B->empty()) { |
74 | // Happens if S is B's terminator and B contains nothing else |
75 | // (e.g. a CFGBlock containing only a goto). |
76 | return false; |
77 | } |
78 | if (std::optional<CFGStmt> CS = B->back().getAs<CFGStmt>()) { |
79 | if (const auto *CE = dyn_cast<CallExpr>(Val: CS->getStmt())) { |
80 | return CE->getCallee()->IgnoreCasts() == S && CE->isBuiltinAssumeFalse(Ctx: C); |
81 | } |
82 | } |
83 | return false; |
84 | } |
85 | |
86 | static bool isDeadReturn(const CFGBlock *B, const Stmt *S) { |
87 | // Look to see if the current control flow ends with a 'return', and see if |
88 | // 'S' is a substatement. The 'return' may not be the last element in the |
89 | // block, or may be in a subsequent block because of destructors. |
90 | const CFGBlock *Current = B; |
91 | while (true) { |
92 | for (const CFGElement &CE : llvm::reverse(C: *Current)) { |
93 | if (std::optional<CFGStmt> CS = CE.getAs<CFGStmt>()) { |
94 | if (const ReturnStmt *RS = dyn_cast<ReturnStmt>(Val: CS->getStmt())) { |
95 | if (RS == S) |
96 | return true; |
97 | if (const Expr *RE = RS->getRetValue()) { |
98 | RE = RE->IgnoreParenCasts(); |
99 | if (RE == S) |
100 | return true; |
101 | ParentMap PM(const_cast<Expr *>(RE)); |
102 | // If 'S' is in the ParentMap, it is a subexpression of |
103 | // the return statement. |
104 | return PM.getParent(S); |
105 | } |
106 | } |
107 | break; |
108 | } |
109 | } |
110 | // Note also that we are restricting the search for the return statement |
111 | // to stop at control-flow; only part of a return statement may be dead, |
112 | // without the whole return statement being dead. |
113 | if (Current->getTerminator().isTemporaryDtorsBranch()) { |
114 | // Temporary destructors have a predictable control flow, thus we want to |
115 | // look into the next block for the return statement. |
116 | // We look into the false branch, as we know the true branch only contains |
117 | // the call to the destructor. |
118 | assert(Current->succ_size() == 2); |
119 | Current = *(Current->succ_begin() + 1); |
120 | } else if (!Current->getTerminatorStmt() && Current->succ_size() == 1) { |
121 | // If there is only one successor, we're not dealing with outgoing control |
122 | // flow. Thus, look into the next block. |
123 | Current = *Current->succ_begin(); |
124 | if (Current->pred_size() > 1) { |
125 | // If there is more than one predecessor, we're dealing with incoming |
126 | // control flow - if the return statement is in that block, it might |
127 | // well be reachable via a different control flow, thus it's not dead. |
128 | return false; |
129 | } |
130 | } else { |
131 | // We hit control flow or a dead end. Stop searching. |
132 | return false; |
133 | } |
134 | } |
135 | llvm_unreachable("Broke out of infinite loop."); |
136 | } |
137 | |
138 | static SourceLocation getTopMostMacro(SourceLocation Loc, SourceManager &SM) { |
139 | assert(Loc.isMacroID()); |
140 | SourceLocation Last; |
141 | do { |
142 | Last = Loc; |
143 | Loc = SM.getImmediateMacroCallerLoc(Loc); |
144 | } while (Loc.isMacroID()); |
145 | return Last; |
146 | } |
147 | |
148 | /// Returns true if the statement is expanded from a configuration macro. |
149 | static bool isExpandedFromConfigurationMacro(const Stmt *S, |
150 | Preprocessor &PP, |
151 | bool IgnoreYES_NO = false) { |
152 | // FIXME: This is not very precise. Here we just check to see if the |
153 | // value comes from a macro, but we can do much better. This is likely |
154 | // to be over conservative. This logic is factored into a separate function |
155 | // so that we can refine it later. |
156 | SourceLocation L = S->getBeginLoc(); |
157 | if (L.isMacroID()) { |
158 | SourceManager &SM = PP.getSourceManager(); |
159 | if (IgnoreYES_NO) { |
160 | // The Objective-C constant 'YES' and 'NO' |
161 | // are defined as macros. Do not treat them |
162 | // as configuration values. |
163 | SourceLocation TopL = getTopMostMacro(Loc: L, SM); |
164 | StringRef MacroName = PP.getImmediateMacroName(Loc: TopL); |
165 | if (MacroName == "YES"|| MacroName == "NO") |
166 | return false; |
167 | } else if (!PP.getLangOpts().CPlusPlus) { |
168 | // Do not treat C 'false' and 'true' macros as configuration values. |
169 | SourceLocation TopL = getTopMostMacro(Loc: L, SM); |
170 | StringRef MacroName = PP.getImmediateMacroName(Loc: TopL); |
171 | if (MacroName == "false"|| MacroName == "true") |
172 | return false; |
173 | } |
174 | return true; |
175 | } |
176 | return false; |
177 | } |
178 | |
179 | static bool isConfigurationValue(const ValueDecl *D, Preprocessor &PP); |
180 | |
181 | /// Returns true if the statement represents a configuration value. |
182 | /// |
183 | /// A configuration value is something usually determined at compile-time |
184 | /// to conditionally always execute some branch. Such guards are for |
185 | /// "sometimes unreachable" code. Such code is usually not interesting |
186 | /// to report as unreachable, and may mask truly unreachable code within |
187 | /// those blocks. |
188 | static bool isConfigurationValue(const Stmt *S, |
189 | Preprocessor &PP, |
190 | SourceRange *SilenceableCondVal = nullptr, |
191 | bool IncludeIntegers = true, |
192 | bool WrappedInParens = false) { |
193 | if (!S) |
194 | return false; |
195 | |
196 | if (const auto *Ex = dyn_cast<Expr>(Val: S)) |
197 | S = Ex->IgnoreImplicit(); |
198 | |
199 | if (const auto *Ex = dyn_cast<Expr>(Val: S)) |
200 | S = Ex->IgnoreCasts(); |
201 | |
202 | // Special case looking for the sigil '()' around an integer literal. |
203 | if (const ParenExpr *PE = dyn_cast<ParenExpr>(Val: S)) |
204 | if (!PE->getBeginLoc().isMacroID()) |
205 | return isConfigurationValue(PE->getSubExpr(), PP, SilenceableCondVal, |
206 | IncludeIntegers, true); |
207 | |
208 | if (const Expr *Ex = dyn_cast<Expr>(Val: S)) |
209 | S = Ex->IgnoreCasts(); |
210 | |
211 | bool IgnoreYES_NO = false; |
212 | |
213 | switch (S->getStmtClass()) { |
214 | case Stmt::CallExprClass: { |
215 | const FunctionDecl *Callee = |
216 | dyn_cast_or_null<FunctionDecl>(Val: cast<CallExpr>(Val: S)->getCalleeDecl()); |
217 | return Callee ? Callee->isConstexpr() : false; |
218 | } |
219 | case Stmt::DeclRefExprClass: |
220 | return isConfigurationValue(D: cast<DeclRefExpr>(Val: S)->getDecl(), PP); |
221 | case Stmt::ObjCBoolLiteralExprClass: |
222 | IgnoreYES_NO = true; |
223 | [[fallthrough]]; |
224 | case Stmt::CXXBoolLiteralExprClass: |
225 | case Stmt::IntegerLiteralClass: { |
226 | const Expr *E = cast<Expr>(Val: S); |
227 | if (IncludeIntegers) { |
228 | if (SilenceableCondVal && !SilenceableCondVal->getBegin().isValid()) |
229 | *SilenceableCondVal = E->getSourceRange(); |
230 | return WrappedInParens || |
231 | isExpandedFromConfigurationMacro(E, PP, IgnoreYES_NO); |
232 | } |
233 | return false; |
234 | } |
235 | case Stmt::MemberExprClass: |
236 | return isConfigurationValue(D: cast<MemberExpr>(Val: S)->getMemberDecl(), PP); |
237 | case Stmt::UnaryExprOrTypeTraitExprClass: |
238 | return true; |
239 | case Stmt::BinaryOperatorClass: { |
240 | const BinaryOperator *B = cast<BinaryOperator>(Val: S); |
241 | // Only include raw integers (not enums) as configuration |
242 | // values if they are used in a logical or comparison operator |
243 | // (not arithmetic). |
244 | IncludeIntegers &= (B->isLogicalOp() || B->isComparisonOp()); |
245 | return isConfigurationValue(B->getLHS(), PP, SilenceableCondVal, |
246 | IncludeIntegers) || |
247 | isConfigurationValue(B->getRHS(), PP, SilenceableCondVal, |
248 | IncludeIntegers); |
249 | } |
250 | case Stmt::UnaryOperatorClass: { |
251 | const UnaryOperator *UO = cast<UnaryOperator>(Val: S); |
252 | if (UO->getOpcode() != UO_LNot && UO->getOpcode() != UO_Minus) |
253 | return false; |
254 | bool SilenceableCondValNotSet = |
255 | SilenceableCondVal && SilenceableCondVal->getBegin().isInvalid(); |
256 | bool IsSubExprConfigValue = |
257 | isConfigurationValue(UO->getSubExpr(), PP, SilenceableCondVal, |
258 | IncludeIntegers, WrappedInParens); |
259 | // Update the silenceable condition value source range only if the range |
260 | // was set directly by the child expression. |
261 | if (SilenceableCondValNotSet && |
262 | SilenceableCondVal->getBegin().isValid() && |
263 | *SilenceableCondVal == |
264 | UO->getSubExpr()->IgnoreCasts()->getSourceRange()) |
265 | *SilenceableCondVal = UO->getSourceRange(); |
266 | return IsSubExprConfigValue; |
267 | } |
268 | default: |
269 | return false; |
270 | } |
271 | } |
272 | |
273 | static bool isConfigurationValue(const ValueDecl *D, Preprocessor &PP) { |
274 | if (const EnumConstantDecl *ED = dyn_cast<EnumConstantDecl>(Val: D)) |
275 | return isConfigurationValue(ED->getInitExpr(), PP); |
276 | if (const VarDecl *VD = dyn_cast<VarDecl>(Val: D)) { |
277 | // As a heuristic, treat globals as configuration values. Note |
278 | // that we only will get here if Sema evaluated this |
279 | // condition to a constant expression, which means the global |
280 | // had to be declared in a way to be a truly constant value. |
281 | // We could generalize this to local variables, but it isn't |
282 | // clear if those truly represent configuration values that |
283 | // gate unreachable code. |
284 | if (!VD->hasLocalStorage()) |
285 | return true; |
286 | |
287 | // As a heuristic, locals that have been marked 'const' explicitly |
288 | // can be treated as configuration values as well. |
289 | return VD->getType().isLocalConstQualified(); |
290 | } |
291 | return false; |
292 | } |
293 | |
294 | /// Returns true if we should always explore all successors of a block. |
295 | static bool shouldTreatSuccessorsAsReachable(const CFGBlock *B, |
296 | Preprocessor &PP) { |
297 | if (const Stmt *Term = B->getTerminatorStmt()) { |
298 | if (isa<SwitchStmt>(Val: Term)) |
299 | return true; |
300 | // Specially handle '||' and '&&'. |
301 | if (isa<BinaryOperator>(Val: Term)) { |
302 | return isConfigurationValue(S: Term, PP); |
303 | } |
304 | // Do not treat constexpr if statement successors as unreachable in warnings |
305 | // since the point of these statements is to determine branches at compile |
306 | // time. |
307 | if (const auto *IS = dyn_cast<IfStmt>(Val: Term); |
308 | IS != nullptr && IS->isConstexpr()) |
309 | return true; |
310 | } |
311 | |
312 | const Stmt *Cond = B->getTerminatorCondition(/* stripParens */ StripParens: false); |
313 | return isConfigurationValue(S: Cond, PP); |
314 | } |
315 | |
316 | static unsigned scanFromBlock(const CFGBlock *Start, |
317 | llvm::BitVector &Reachable, |
318 | Preprocessor *PP, |
319 | bool IncludeSometimesUnreachableEdges) { |
320 | unsigned count = 0; |
321 | |
322 | // Prep work queue |
323 | SmallVector<const CFGBlock*, 32> WL; |
324 | |
325 | // The entry block may have already been marked reachable |
326 | // by the caller. |
327 | if (!Reachable[Start->getBlockID()]) { |
328 | ++count; |
329 | Reachable[Start->getBlockID()] = true; |
330 | } |
331 | |
332 | WL.push_back(Elt: Start); |
333 | |
334 | // Find the reachable blocks from 'Start'. |
335 | while (!WL.empty()) { |
336 | const CFGBlock *item = WL.pop_back_val(); |
337 | |
338 | // There are cases where we want to treat all successors as reachable. |
339 | // The idea is that some "sometimes unreachable" code is not interesting, |
340 | // and that we should forge ahead and explore those branches anyway. |
341 | // This allows us to potentially uncover some "always unreachable" code |
342 | // within the "sometimes unreachable" code. |
343 | // Look at the successors and mark then reachable. |
344 | std::optional<bool> TreatAllSuccessorsAsReachable; |
345 | if (!IncludeSometimesUnreachableEdges) |
346 | TreatAllSuccessorsAsReachable = false; |
347 | |
348 | for (CFGBlock::const_succ_iterator I = item->succ_begin(), |
349 | E = item->succ_end(); I != E; ++I) { |
350 | const CFGBlock *B = *I; |
351 | if (!B) do { |
352 | const CFGBlock *UB = I->getPossiblyUnreachableBlock(); |
353 | if (!UB) |
354 | break; |
355 | |
356 | if (!TreatAllSuccessorsAsReachable) { |
357 | assert(PP); |
358 | TreatAllSuccessorsAsReachable = |
359 | shouldTreatSuccessorsAsReachable(B: item, PP&: *PP); |
360 | } |
361 | |
362 | if (*TreatAllSuccessorsAsReachable) { |
363 | B = UB; |
364 | break; |
365 | } |
366 | } |
367 | while (false); |
368 | |
369 | if (B) { |
370 | unsigned blockID = B->getBlockID(); |
371 | if (!Reachable[blockID]) { |
372 | Reachable.set(blockID); |
373 | WL.push_back(Elt: B); |
374 | ++count; |
375 | } |
376 | } |
377 | } |
378 | } |
379 | return count; |
380 | } |
381 | |
382 | static unsigned scanMaybeReachableFromBlock(const CFGBlock *Start, |
383 | Preprocessor &PP, |
384 | llvm::BitVector &Reachable) { |
385 | return scanFromBlock(Start, Reachable, PP: &PP, IncludeSometimesUnreachableEdges: true); |
386 | } |
387 | |
388 | //===----------------------------------------------------------------------===// |
389 | // Dead Code Scanner. |
390 | //===----------------------------------------------------------------------===// |
391 | |
392 | namespace { |
393 | class DeadCodeScan { |
394 | llvm::BitVector Visited; |
395 | llvm::BitVector &Reachable; |
396 | SmallVector<const CFGBlock *, 10> WorkList; |
397 | Preprocessor &PP; |
398 | ASTContext &C; |
399 | |
400 | typedef SmallVector<std::pair<const CFGBlock *, const Stmt *>, 12> |
401 | DeferredLocsTy; |
402 | |
403 | DeferredLocsTy DeferredLocs; |
404 | |
405 | public: |
406 | DeadCodeScan(llvm::BitVector &reachable, Preprocessor &PP, ASTContext &C) |
407 | : Visited(reachable.size()), |
408 | Reachable(reachable), |
409 | PP(PP), C(C) {} |
410 | |
411 | void enqueue(const CFGBlock *block); |
412 | unsigned scanBackwards(const CFGBlock *Start, |
413 | clang::reachable_code::Callback &CB); |
414 | |
415 | bool isDeadCodeRoot(const CFGBlock *Block); |
416 | |
417 | const Stmt *findDeadCode(const CFGBlock *Block); |
418 | |
419 | void reportDeadCode(const CFGBlock *B, |
420 | const Stmt *S, |
421 | clang::reachable_code::Callback &CB); |
422 | }; |
423 | } |
424 | |
425 | void DeadCodeScan::enqueue(const CFGBlock *block) { |
426 | unsigned blockID = block->getBlockID(); |
427 | if (Reachable[blockID] || Visited[blockID]) |
428 | return; |
429 | Visited[blockID] = true; |
430 | WorkList.push_back(Elt: block); |
431 | } |
432 | |
433 | bool DeadCodeScan::isDeadCodeRoot(const clang::CFGBlock *Block) { |
434 | bool isDeadRoot = true; |
435 | |
436 | for (CFGBlock::const_pred_iterator I = Block->pred_begin(), |
437 | E = Block->pred_end(); I != E; ++I) { |
438 | if (const CFGBlock *PredBlock = *I) { |
439 | unsigned blockID = PredBlock->getBlockID(); |
440 | if (Visited[blockID]) { |
441 | isDeadRoot = false; |
442 | continue; |
443 | } |
444 | if (!Reachable[blockID]) { |
445 | isDeadRoot = false; |
446 | Visited[blockID] = true; |
447 | WorkList.push_back(Elt: PredBlock); |
448 | continue; |
449 | } |
450 | } |
451 | } |
452 | |
453 | return isDeadRoot; |
454 | } |
455 | |
456 | // Check if the given `DeadStmt` is a coroutine statement and is a substmt of |
457 | // the coroutine statement. `Block` is the CFGBlock containing the `DeadStmt`. |
458 | static bool isInCoroutineStmt(const Stmt *DeadStmt, const CFGBlock *Block) { |
459 | // The coroutine statement, co_return, co_await, or co_yield. |
460 | const Stmt *CoroStmt = nullptr; |
461 | // Find the first coroutine statement after the DeadStmt in the block. |
462 | bool AfterDeadStmt = false; |
463 | for (CFGBlock::const_iterator I = Block->begin(), E = Block->end(); I != E; |
464 | ++I) |
465 | if (std::optional<CFGStmt> CS = I->getAs<CFGStmt>()) { |
466 | const Stmt *S = CS->getStmt(); |
467 | if (S == DeadStmt) |
468 | AfterDeadStmt = true; |
469 | if (AfterDeadStmt && |
470 | // For simplicity, we only check simple coroutine statements. |
471 | (llvm::isa<CoreturnStmt>(Val: S) || llvm::isa<CoroutineSuspendExpr>(Val: S))) { |
472 | CoroStmt = S; |
473 | break; |
474 | } |
475 | } |
476 | if (!CoroStmt) |
477 | return false; |
478 | struct Checker : DynamicRecursiveASTVisitor { |
479 | const Stmt *DeadStmt; |
480 | bool CoroutineSubStmt = false; |
481 | Checker(const Stmt *S) : DeadStmt(S) { |
482 | // Statements captured in the CFG can be implicit. |
483 | ShouldVisitImplicitCode = true; |
484 | } |
485 | |
486 | bool VisitStmt(Stmt *S) override { |
487 | if (S == DeadStmt) |
488 | CoroutineSubStmt = true; |
489 | return true; |
490 | } |
491 | }; |
492 | Checker checker(DeadStmt); |
493 | checker.TraverseStmt(const_cast<Stmt *>(CoroStmt)); |
494 | return checker.CoroutineSubStmt; |
495 | } |
496 | |
497 | static bool isValidDeadStmt(const Stmt *S, const clang::CFGBlock *Block) { |
498 | if (S->getBeginLoc().isInvalid()) |
499 | return false; |
500 | if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(Val: S)) |
501 | return BO->getOpcode() != BO_Comma; |
502 | // Coroutine statements are never considered dead statements, because removing |
503 | // them may change the function semantic if it is the only coroutine statement |
504 | // of the coroutine. |
505 | return !isInCoroutineStmt(DeadStmt: S, Block); |
506 | } |
507 | |
508 | const Stmt *DeadCodeScan::findDeadCode(const clang::CFGBlock *Block) { |
509 | for (CFGBlock::const_iterator I = Block->begin(), E = Block->end(); I!=E; ++I) |
510 | if (std::optional<CFGStmt> CS = I->getAs<CFGStmt>()) { |
511 | const Stmt *S = CS->getStmt(); |
512 | if (isValidDeadStmt(S, Block)) |
513 | return S; |
514 | } |
515 | |
516 | CFGTerminator T = Block->getTerminator(); |
517 | if (T.isStmtBranch()) { |
518 | const Stmt *S = T.getStmt(); |
519 | if (S && isValidDeadStmt(S, Block)) |
520 | return S; |
521 | } |
522 | |
523 | return nullptr; |
524 | } |
525 | |
526 | static int SrcCmp(const std::pair<const CFGBlock *, const Stmt *> *p1, |
527 | const std::pair<const CFGBlock *, const Stmt *> *p2) { |
528 | if (p1->second->getBeginLoc() < p2->second->getBeginLoc()) |
529 | return -1; |
530 | if (p2->second->getBeginLoc() < p1->second->getBeginLoc()) |
531 | return 1; |
532 | return 0; |
533 | } |
534 | |
535 | unsigned DeadCodeScan::scanBackwards(const clang::CFGBlock *Start, |
536 | clang::reachable_code::Callback &CB) { |
537 | |
538 | unsigned count = 0; |
539 | enqueue(block: Start); |
540 | |
541 | while (!WorkList.empty()) { |
542 | const CFGBlock *Block = WorkList.pop_back_val(); |
543 | |
544 | // It is possible that this block has been marked reachable after |
545 | // it was enqueued. |
546 | if (Reachable[Block->getBlockID()]) |
547 | continue; |
548 | |
549 | // Look for any dead code within the block. |
550 | const Stmt *S = findDeadCode(Block); |
551 | |
552 | if (!S) { |
553 | // No dead code. Possibly an empty block. Look at dead predecessors. |
554 | for (CFGBlock::const_pred_iterator I = Block->pred_begin(), |
555 | E = Block->pred_end(); I != E; ++I) { |
556 | if (const CFGBlock *predBlock = *I) |
557 | enqueue(block: predBlock); |
558 | } |
559 | continue; |
560 | } |
561 | |
562 | // Specially handle macro-expanded code. |
563 | if (S->getBeginLoc().isMacroID()) { |
564 | count += scanMaybeReachableFromBlock(Start: Block, PP, Reachable); |
565 | continue; |
566 | } |
567 | |
568 | if (isDeadCodeRoot(Block)) { |
569 | reportDeadCode(B: Block, S, CB); |
570 | count += scanMaybeReachableFromBlock(Start: Block, PP, Reachable); |
571 | } |
572 | else { |
573 | // Record this statement as the possibly best location in a |
574 | // strongly-connected component of dead code for emitting a |
575 | // warning. |
576 | DeferredLocs.push_back(Elt: std::make_pair(x&: Block, y&: S)); |
577 | } |
578 | } |
579 | |
580 | // If we didn't find a dead root, then report the dead code with the |
581 | // earliest location. |
582 | if (!DeferredLocs.empty()) { |
583 | llvm::array_pod_sort(Start: DeferredLocs.begin(), End: DeferredLocs.end(), Compare: SrcCmp); |
584 | for (const auto &I : DeferredLocs) { |
585 | const CFGBlock *Block = I.first; |
586 | if (Reachable[Block->getBlockID()]) |
587 | continue; |
588 | reportDeadCode(B: Block, S: I.second, CB); |
589 | count += scanMaybeReachableFromBlock(Start: Block, PP, Reachable); |
590 | } |
591 | } |
592 | |
593 | return count; |
594 | } |
595 | |
596 | static SourceLocation GetUnreachableLoc(const Stmt *S, |
597 | SourceRange &R1, |
598 | SourceRange &R2) { |
599 | R1 = R2 = SourceRange(); |
600 | |
601 | if (const Expr *Ex = dyn_cast<Expr>(Val: S)) |
602 | S = Ex->IgnoreParenImpCasts(); |
603 | |
604 | switch (S->getStmtClass()) { |
605 | case Expr::BinaryOperatorClass: { |
606 | const BinaryOperator *BO = cast<BinaryOperator>(Val: S); |
607 | return BO->getOperatorLoc(); |
608 | } |
609 | case Expr::UnaryOperatorClass: { |
610 | const UnaryOperator *UO = cast<UnaryOperator>(Val: S); |
611 | R1 = UO->getSubExpr()->getSourceRange(); |
612 | return UO->getOperatorLoc(); |
613 | } |
614 | case Expr::CompoundAssignOperatorClass: { |
615 | const CompoundAssignOperator *CAO = cast<CompoundAssignOperator>(Val: S); |
616 | R1 = CAO->getLHS()->getSourceRange(); |
617 | R2 = CAO->getRHS()->getSourceRange(); |
618 | return CAO->getOperatorLoc(); |
619 | } |
620 | case Expr::BinaryConditionalOperatorClass: |
621 | case Expr::ConditionalOperatorClass: { |
622 | const AbstractConditionalOperator *CO = |
623 | cast<AbstractConditionalOperator>(Val: S); |
624 | return CO->getQuestionLoc(); |
625 | } |
626 | case Expr::MemberExprClass: { |
627 | const MemberExpr *ME = cast<MemberExpr>(Val: S); |
628 | R1 = ME->getSourceRange(); |
629 | return ME->getMemberLoc(); |
630 | } |
631 | case Expr::ArraySubscriptExprClass: { |
632 | const ArraySubscriptExpr *ASE = cast<ArraySubscriptExpr>(Val: S); |
633 | R1 = ASE->getLHS()->getSourceRange(); |
634 | R2 = ASE->getRHS()->getSourceRange(); |
635 | return ASE->getRBracketLoc(); |
636 | } |
637 | case Expr::CStyleCastExprClass: { |
638 | const CStyleCastExpr *CSC = cast<CStyleCastExpr>(Val: S); |
639 | R1 = CSC->getSubExpr()->getSourceRange(); |
640 | return CSC->getLParenLoc(); |
641 | } |
642 | case Expr::CXXFunctionalCastExprClass: { |
643 | const CXXFunctionalCastExpr *CE = cast <CXXFunctionalCastExpr>(Val: S); |
644 | R1 = CE->getSubExpr()->getSourceRange(); |
645 | return CE->getBeginLoc(); |
646 | } |
647 | case Stmt::CXXTryStmtClass: { |
648 | return cast<CXXTryStmt>(Val: S)->getHandler(i: 0)->getCatchLoc(); |
649 | } |
650 | case Expr::ObjCBridgedCastExprClass: { |
651 | const ObjCBridgedCastExpr *CSC = cast<ObjCBridgedCastExpr>(Val: S); |
652 | R1 = CSC->getSubExpr()->getSourceRange(); |
653 | return CSC->getLParenLoc(); |
654 | } |
655 | default: ; |
656 | } |
657 | R1 = S->getSourceRange(); |
658 | return S->getBeginLoc(); |
659 | } |
660 | |
661 | void DeadCodeScan::reportDeadCode(const CFGBlock *B, |
662 | const Stmt *S, |
663 | clang::reachable_code::Callback &CB) { |
664 | // Classify the unreachable code found, or suppress it in some cases. |
665 | reachable_code::UnreachableKind UK = reachable_code::UK_Other; |
666 | |
667 | if (isa<BreakStmt>(Val: S)) { |
668 | UK = reachable_code::UK_Break; |
669 | } else if (isTrivialDoWhile(B, S) || isBuiltinUnreachable(S) || |
670 | isBuiltinAssumeFalse(B, S, C)) { |
671 | return; |
672 | } |
673 | else if (isDeadReturn(B, S)) { |
674 | UK = reachable_code::UK_Return; |
675 | } |
676 | |
677 | const auto *AS = dyn_cast<AttributedStmt>(Val: S); |
678 | bool HasFallThroughAttr = |
679 | AS && hasSpecificAttr<FallThroughAttr>(AS->getAttrs()); |
680 | |
681 | SourceRange SilenceableCondVal; |
682 | |
683 | if (UK == reachable_code::UK_Other) { |
684 | // Check if the dead code is part of the "loop target" of |
685 | // a for/for-range loop. This is the block that contains |
686 | // the increment code. |
687 | if (const Stmt *LoopTarget = B->getLoopTarget()) { |
688 | SourceLocation Loc = LoopTarget->getBeginLoc(); |
689 | SourceRange R1(Loc, Loc), R2; |
690 | |
691 | if (const ForStmt *FS = dyn_cast<ForStmt>(Val: LoopTarget)) { |
692 | const Expr *Inc = FS->getInc(); |
693 | Loc = Inc->getBeginLoc(); |
694 | R2 = Inc->getSourceRange(); |
695 | } |
696 | |
697 | CB.HandleUnreachable(UK: reachable_code::UK_Loop_Increment, L: Loc, |
698 | ConditionVal: SourceRange(), R1: SourceRange(Loc, Loc), R2, |
699 | HasFallThroughAttr); |
700 | return; |
701 | } |
702 | |
703 | // Check if the dead block has a predecessor whose branch has |
704 | // a configuration value that *could* be modified to |
705 | // silence the warning. |
706 | CFGBlock::const_pred_iterator PI = B->pred_begin(); |
707 | if (PI != B->pred_end()) { |
708 | if (const CFGBlock *PredBlock = PI->getPossiblyUnreachableBlock()) { |
709 | const Stmt *TermCond = |
710 | PredBlock->getTerminatorCondition(/* strip parens */ StripParens: false); |
711 | isConfigurationValue(S: TermCond, PP, SilenceableCondVal: &SilenceableCondVal); |
712 | } |
713 | } |
714 | } |
715 | |
716 | SourceRange R1, R2; |
717 | SourceLocation Loc = GetUnreachableLoc(S, R1, R2); |
718 | CB.HandleUnreachable(UK, L: Loc, ConditionVal: SilenceableCondVal, R1, R2, HasFallThroughAttr); |
719 | } |
720 | |
721 | //===----------------------------------------------------------------------===// |
722 | // Reachability APIs. |
723 | //===----------------------------------------------------------------------===// |
724 | |
725 | namespace clang { namespace reachable_code { |
726 | |
727 | void Callback::anchor() { } |
728 | |
729 | unsigned ScanReachableFromBlock(const CFGBlock *Start, |
730 | llvm::BitVector &Reachable) { |
731 | return scanFromBlock(Start, Reachable, /* SourceManager* */ PP: nullptr, IncludeSometimesUnreachableEdges: false); |
732 | } |
733 | |
734 | void FindUnreachableCode(AnalysisDeclContext &AC, Preprocessor &PP, |
735 | Callback &CB) { |
736 | |
737 | CFG *cfg = AC.getCFG(); |
738 | if (!cfg) |
739 | return; |
740 | |
741 | // Scan for reachable blocks from the entrance of the CFG. |
742 | // If there are no unreachable blocks, we're done. |
743 | llvm::BitVector reachable(cfg->getNumBlockIDs()); |
744 | unsigned numReachable = |
745 | scanMaybeReachableFromBlock(Start: &cfg->getEntry(), PP, Reachable&: reachable); |
746 | if (numReachable == cfg->getNumBlockIDs()) |
747 | return; |
748 | |
749 | // If there aren't explicit EH edges, we should include the 'try' dispatch |
750 | // blocks as roots. |
751 | if (!AC.getCFGBuildOptions().AddEHEdges) { |
752 | for (const CFGBlock *B : cfg->try_blocks()) |
753 | numReachable += scanMaybeReachableFromBlock(Start: B, PP, Reachable&: reachable); |
754 | if (numReachable == cfg->getNumBlockIDs()) |
755 | return; |
756 | } |
757 | |
758 | // There are some unreachable blocks. We need to find the root blocks that |
759 | // contain code that should be considered unreachable. |
760 | for (const CFGBlock *block : *cfg) { |
761 | // A block may have been marked reachable during this loop. |
762 | if (reachable[block->getBlockID()]) |
763 | continue; |
764 | |
765 | DeadCodeScan DS(reachable, PP, AC.getASTContext()); |
766 | numReachable += DS.scanBackwards(Start: block, CB); |
767 | |
768 | if (numReachable == cfg->getNumBlockIDs()) |
769 | return; |
770 | } |
771 | } |
772 | |
773 | }} // end namespace clang::reachable_code |
774 |
Definitions
- isEnumConstant
- isTrivialExpression
- isTrivialDoWhile
- isBuiltinUnreachable
- isBuiltinAssumeFalse
- isDeadReturn
- getTopMostMacro
- isExpandedFromConfigurationMacro
- isConfigurationValue
- isConfigurationValue
- shouldTreatSuccessorsAsReachable
- scanFromBlock
- scanMaybeReachableFromBlock
- DeadCodeScan
- DeadCodeScan
- enqueue
- isDeadCodeRoot
- isInCoroutineStmt
- isValidDeadStmt
- findDeadCode
- SrcCmp
- scanBackwards
- GetUnreachableLoc
- reportDeadCode
- anchor
- ScanReachableFromBlock
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more