Skip to content

Commit 326fa74

Browse files
committed
Update query for unreachable statements
1 parent 138a1c3 commit 326fa74

1 file changed

Lines changed: 35 additions & 15 deletions

File tree

go/ql/src/RedundantCode/UnreachableStatement.ql

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,36 @@
1414

1515
import go
1616

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()))
2247
)
2348
}
2449

@@ -63,18 +88,13 @@ predicate allowlist(Stmt s) {
6388
forall(Expr retval | retval = ret.getAnExpr() | isAllowedReturnValue(retval))
6489
)
6590
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())
7394
}
7495

75-
from Stmt s, ControlFlow::Node fst
96+
from Stmt s
7697
where
77-
fst = s.getFirstControlFlowNode() and
78-
not exists(nonGuardPredecessor(fst)) and
98+
firstUnreachableStmt(s) and
7999
not allowlist(s)
80100
select s, "This statement is unreachable."

0 commit comments

Comments
 (0)