Skip to content

Commit 20e9687

Browse files
committed
Rust: Handle let statements with pattern and else branch in CFG
1 parent a935bde commit 20e9687

File tree

1 file changed

+40
-3
lines changed

1 file changed

+40
-3
lines changed

rust/ql/lib/codeql/rust/controlflow/internal/ControlFlowGraphImpl.qll

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ module CfgImpl = Make<Location, CfgInput>;
5959

6060
import CfgImpl
6161

62+
/** A trivial pattern that is always guaranteed to match. */
63+
predicate trivialPat(Pat p) { p instanceof WildcardPat or p instanceof IdentPat }
64+
6265
class AwaitExprTree extends StandardPostOrderTree instanceof AwaitExpr {
6366
override ControlFlowTree getChildNode(int i) { i = 0 and result = super.getExpr() }
6467
}
@@ -236,11 +239,43 @@ class LetExprTree extends StandardPostOrderTree instanceof LetExpr {
236239
override ControlFlowTree getChildNode(int i) { i = 0 and result = super.getExpr() }
237240
}
238241

239-
class LetStmtTree extends StandardPreOrderTree instanceof LetStmt {
242+
// We handle `let` statements with trivial patterns separately as they don't
243+
// lead to non-standard control flow. For instance, in `let a = ...` it is not
244+
// interesing to create match edges as it would carry no information.
245+
class LetStmtTreeTrivialPat extends StandardPreOrderTree instanceof LetStmt {
246+
LetStmtTreeTrivialPat() { trivialPat(super.getPat()) }
247+
240248
override ControlFlowTree getChildNode(int i) {
241-
// TODO: For now we ignore the else branch (`super.getElse`). This branch
242-
// is guaranteed to be diverging so will need special treatment in the CFG.
243249
i = 0 and result = super.getInitializer()
250+
or
251+
i = 1 and result = super.getPat()
252+
}
253+
}
254+
255+
// `let` statements with interesting patterns that we want to be reflected in
256+
// the CFG.
257+
class LetStmtTree extends PreOrderTree instanceof LetStmt {
258+
LetStmtTree() { not trivialPat(super.getPat()) }
259+
260+
final override predicate propagatesAbnormal(AstNode child) {
261+
child = super.getInitializer() or child = super.getElse()
262+
}
263+
264+
override predicate succ(AstNode pred, AstNode succ, Completion c) {
265+
// Edge to start of initializer.
266+
pred = this and first(super.getInitializer(), succ) and completionIsSimple(c)
267+
or
268+
// Edge from end of initializer to pattern.
269+
last(super.getInitializer(), pred, c) and succ = super.getPat()
270+
or
271+
// Edge from failed pattern to `else` branch.
272+
pred = super.getPat() and first(super.getElse(), succ) and c.(MatchCompletion).failed()
273+
}
274+
275+
override predicate last(AstNode node, Completion c) {
276+
// Edge out of a successfully matched pattern.
277+
node = super.getPat() and c.(MatchCompletion).succeeded()
278+
// NOTE: No edge out of the `else` branch as that is guaranteed to diverge.
244279
}
245280
}
246281

@@ -327,6 +362,8 @@ class MethodCallExprTree extends StandardPostOrderTree instanceof MethodCallExpr
327362

328363
class OffsetOfExprTree extends LeafTree instanceof OffsetOfExpr { }
329364

365+
class PatExprTree extends LeafTree instanceof Pat { }
366+
330367
class PathExprTree extends LeafTree instanceof PathExpr { }
331368

332369
class RecordExprTree extends StandardPostOrderTree instanceof RecordExpr {

0 commit comments

Comments
 (0)