Skip to content

Commit f1bff93

Browse files
authored
Merge pull request #20203 from hvitved/rust/if-let-chain-test
Rust: Handle chained `let` expressions
2 parents b67394a + d09645b commit f1bff93

File tree

32 files changed

+5389
-4628
lines changed

32 files changed

+5389
-4628
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* [`let` chains in `if` and `while`](https://doc.rust-lang.org/edition-guide/rust-2024/let-chains.html) are now supported, as well as [`if let` guards in `match` expressions](https://rust-lang.github.io/rfcs/2294-if-let-guard.html).

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ private import codeql.rust.controlflow.CfgNodes
77
private import codeql.rust.internal.CachedStages
88

99
private predicate isPostOrder(AstNode n) {
10-
n instanceof Expr and
11-
not n instanceof LetExpr
10+
n instanceof Expr
1211
or
1312
n instanceof OrPat
1413
or

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

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,7 @@ class TypeReprTree extends LeafTree instanceof TypeRepr { }
200200
/**
201201
* Provides `ControlFlowTree`s for expressions.
202202
*
203-
* Since expressions construct values, they are modeled in post-order, except for
204-
* `LetExpr`s.
203+
* Since expressions construct values, they are modeled in post-order.
205204
*/
206205
module ExprTrees {
207206
class ArrayExprTree extends StandardPostOrderTree, ArrayExpr {
@@ -341,21 +340,15 @@ module ExprTrees {
341340
child = [super.getCondition(), super.getABranch()]
342341
}
343342

344-
private ConditionalCompletion conditionCompletion(Completion c) {
345-
if super.getCondition() instanceof LetExpr
346-
then result = c.(MatchCompletion)
347-
else result = c.(BooleanCompletion)
348-
}
349-
350343
override predicate succ(AstNode pred, AstNode succ, Completion c) {
351344
// Edges from the condition to the branches
352345
last(super.getCondition(), pred, c) and
353346
(
354-
first(super.getThen(), succ) and this.conditionCompletion(c).succeeded()
347+
first(super.getThen(), succ) and c.(ConditionalCompletion).succeeded()
355348
or
356-
first(super.getElse(), succ) and this.conditionCompletion(c).failed()
349+
first(super.getElse(), succ) and c.(ConditionalCompletion).failed()
357350
or
358-
not super.hasElse() and succ = this and this.conditionCompletion(c).failed()
351+
not super.hasElse() and succ = this and c.(ConditionalCompletion).failed()
359352
)
360353
or
361354
// An edge from the then branch to the last node
@@ -401,10 +394,7 @@ module ExprTrees {
401394
}
402395
}
403396

404-
// `LetExpr` is a pre-order tree such that the pattern itself ends up
405-
// dominating successors in the graph in the same way that patterns do in
406-
// `match` expressions.
407-
class LetExprTree extends StandardPreOrderTree, LetExpr {
397+
class LetExprTree extends StandardPostOrderTree, LetExpr {
408398
override AstNode getChildNode(int i) {
409399
i = 0 and
410400
result = this.getScrutinee()
@@ -456,21 +446,15 @@ module ExprTrees {
456446

457447
override predicate first(AstNode node) { first(super.getCondition(), node) }
458448

459-
private ConditionalCompletion conditionCompletion(Completion c) {
460-
if super.getCondition() instanceof LetExpr
461-
then result = c.(MatchCompletion)
462-
else result = c.(BooleanCompletion)
463-
}
464-
465449
override predicate succ(AstNode pred, AstNode succ, Completion c) {
466450
super.succ(pred, succ, c)
467451
or
468452
last(super.getCondition(), pred, c) and
469-
this.conditionCompletion(c).succeeded() and
453+
c.(ConditionalCompletion).succeeded() and
470454
first(this.getLoopBody(), succ)
471455
or
472456
last(super.getCondition(), pred, c) and
473-
this.conditionCompletion(c).failed() and
457+
c.(ConditionalCompletion).failed() and
474458
succ = this
475459
}
476460
}

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

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,7 @@ module ConditionalCompletionSplitting {
7171
child = parent.(LogicalNotExpr).getExpr() and
7272
childCompletion.getDual() = parentCompletion
7373
or
74-
(
75-
childCompletion = parentCompletion
76-
or
77-
// needed for `let` expressions
78-
childCompletion.(MatchCompletion).getValue() =
79-
parentCompletion.(BooleanCompletion).getValue()
80-
) and
74+
childCompletion = parentCompletion and
8175
(
8276
child = parent.(BinaryLogicalOperation).getAnOperand()
8377
or
@@ -92,6 +86,9 @@ module ConditionalCompletionSplitting {
9286
or
9387
child = parent.(PatternTrees::PostOrderPatTree).getPat(_)
9488
)
89+
or
90+
child = parent.(LetExpr).getPat() and
91+
childCompletion.(MatchCompletion).getValue() = parentCompletion.(BooleanCompletion).getValue()
9592
}
9693
}
9794

rust/ql/lib/codeql/rust/dataflow/Ssa.qll

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,9 +194,16 @@ module Ssa {
194194
ae.getRhs() = value
195195
)
196196
or
197-
exists(LetStmtCfgNode ls |
198-
ls.getPat().(IdentPatCfgNode).getName() = write and
199-
ls.getInitializer() = value
197+
exists(IdentPatCfgNode pat | pat.getName() = write |
198+
exists(LetStmtCfgNode ls |
199+
pat = ls.getPat() and
200+
ls.getInitializer() = value
201+
)
202+
or
203+
exists(LetExprCfgNode le |
204+
pat = le.getPat() and
205+
le.getScrutinee() = value
206+
)
200207
)
201208
}
202209

rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,12 @@ module LocalFlow {
241241
nodeTo.getCfgNode() = s.getPat()
242242
)
243243
or
244+
// An edge from the right-hand side of a let expression to the left-hand side.
245+
exists(LetExprCfgNode e |
246+
nodeFrom.getCfgNode() = e.getScrutinee() and
247+
nodeTo.getCfgNode() = e.getPat()
248+
)
249+
or
244250
exists(IdentPatCfgNode p |
245251
not p.isRef() and
246252
nodeFrom.getCfgNode() = p and
@@ -379,6 +385,8 @@ module RustDataFlow implements InputSig<Location> {
379385
predicate neverSkipInPathGraph(Node node) {
380386
node.(Node::Node).getCfgNode() = any(LetStmtCfgNode s).getPat()
381387
or
388+
node.(Node::Node).getCfgNode() = any(LetExprCfgNode e).getPat()
389+
or
382390
node.(Node::Node).getCfgNode() = any(AssignmentExprCfgNode a).getLhs()
383391
or
384392
exists(MatchExprCfgNode match |
@@ -899,6 +907,12 @@ module VariableCapture {
899907
v.getPat() = ls.getPat().getPat() and
900908
ls.getInitializer() = source
901909
)
910+
or
911+
exists(LetExprCfgNode le |
912+
this = le and
913+
v.getPat() = le.getPat().getPat() and
914+
le.getScrutinee() = source
915+
)
902916
}
903917

904918
CapturedVariable getVariable() { result = v }

0 commit comments

Comments
 (0)