|
14 | 14 |
|
15 | 15 | import go |
16 | 16 |
|
17 | | -ControlFlow::Node nonGuardPredecessor(ControlFlow::Node nd) { |
18 | | - exists(ControlFlow::Node pred | pred = nd.getAPredecessor() | |
19 | | - if pred instanceof ControlFlow::ConditionGuardNode |
20 | | - then result = nonGuardPredecessor(pred) |
21 | | - else result = pred |
| 17 | +/** |
| 18 | + * Holds if `s` is reachable, that is, the control-flow graph contains a node for it. |
| 19 | + * |
| 20 | + * The shared control-flow library does not create control-flow nodes for dead code, so an |
| 21 | + * unreachable statement has no first control-flow node. |
| 22 | + */ |
| 23 | +predicate isReachable(Stmt s) { exists(s.getFirstControlFlowNode()) } |
| 24 | + |
| 25 | +/** Gets the statement immediately preceding `s` in a statement list, if any. */ |
| 26 | +Stmt getPreviousStmt(Stmt s) { |
| 27 | + exists(BlockStmt b, int i | s = b.getStmt(i) and result = b.getStmt(i - 1)) |
| 28 | + or |
| 29 | + exists(CaseClause c, int i | s = c.getStmt(i) and result = c.getStmt(i - 1)) |
| 30 | + or |
| 31 | + exists(CommClause c, int i | s = c.getStmt(i) and result = c.getStmt(i - 1)) |
| 32 | +} |
| 33 | + |
| 34 | +/** |
| 35 | + * Holds if `s` is unreachable but the code that would precede it in the control-flow graph is |
| 36 | + * reachable, so that `s` is the first unreachable statement in a run of dead code. |
| 37 | + */ |
| 38 | +predicate firstUnreachableStmt(Stmt s) { |
| 39 | + not isReachable(s) and |
| 40 | + not s instanceof EmptyStmt and |
| 41 | + ( |
| 42 | + // a statement whose preceding statement in the same list is reachable |
| 43 | + isReachable(getPreviousStmt(s)) |
| 44 | + or |
| 45 | + // the post statement of a `for` loop whose body is entered |
| 46 | + exists(ForStmt f | s = f.getPost() and isReachable(f.getBody().getAStmt())) |
22 | 47 | ) |
23 | 48 | } |
24 | 49 |
|
@@ -63,18 +88,13 @@ predicate allowlist(Stmt s) { |
63 | 88 | forall(Expr retval | retval = ret.getAnExpr() | isAllowedReturnValue(retval)) |
64 | 89 | ) |
65 | 90 | or |
66 | | - // statements in an `if false { ... }` and similar |
67 | | - exists(IfStmt is, ControlFlow::ConditionGuardNode iffalse, Expr cond, boolean b | |
68 | | - iffalse.getCondition() = is.getCond() and |
69 | | - iffalse = s.getFirstControlFlowNode().getAPredecessor() and |
70 | | - cond.getBoolValue() = b and |
71 | | - iffalse.ensures(DataFlow::exprNode(cond), b.booleanNot()) |
72 | | - ) |
| 91 | + // statements deliberately made unreachable by a constant condition, such as the code |
| 92 | + // following `if true { return }` |
| 93 | + exists(getPreviousStmt(s).(IfStmt).getCond().getBoolValue()) |
73 | 94 | } |
74 | 95 |
|
75 | | -from Stmt s, ControlFlow::Node fst |
| 96 | +from Stmt s |
76 | 97 | where |
77 | | - fst = s.getFirstControlFlowNode() and |
78 | | - not exists(nonGuardPredecessor(fst)) and |
| 98 | + firstUnreachableStmt(s) and |
79 | 99 | not allowlist(s) |
80 | 100 | select s, "This statement is unreachable." |
0 commit comments