Merge pull request #20203 from hvitved/rust/if-let-chain-test

Rust: Handle chained `let` expressions
This commit is contained in:
Tom Hvitved
2025-08-14 19:51:43 +02:00
committed by GitHub
32 changed files with 5389 additions and 4628 deletions

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* [`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).

View File

@@ -7,8 +7,7 @@ private import codeql.rust.controlflow.CfgNodes
private import codeql.rust.internal.CachedStages
private predicate isPostOrder(AstNode n) {
n instanceof Expr and
not n instanceof LetExpr
n instanceof Expr
or
n instanceof OrPat
or

View File

@@ -200,8 +200,7 @@ class TypeReprTree extends LeafTree instanceof TypeRepr { }
/**
* Provides `ControlFlowTree`s for expressions.
*
* Since expressions construct values, they are modeled in post-order, except for
* `LetExpr`s.
* Since expressions construct values, they are modeled in post-order.
*/
module ExprTrees {
class ArrayExprTree extends StandardPostOrderTree, ArrayExpr {
@@ -341,21 +340,15 @@ module ExprTrees {
child = [super.getCondition(), super.getABranch()]
}
private ConditionalCompletion conditionCompletion(Completion c) {
if super.getCondition() instanceof LetExpr
then result = c.(MatchCompletion)
else result = c.(BooleanCompletion)
}
override predicate succ(AstNode pred, AstNode succ, Completion c) {
// Edges from the condition to the branches
last(super.getCondition(), pred, c) and
(
first(super.getThen(), succ) and this.conditionCompletion(c).succeeded()
first(super.getThen(), succ) and c.(ConditionalCompletion).succeeded()
or
first(super.getElse(), succ) and this.conditionCompletion(c).failed()
first(super.getElse(), succ) and c.(ConditionalCompletion).failed()
or
not super.hasElse() and succ = this and this.conditionCompletion(c).failed()
not super.hasElse() and succ = this and c.(ConditionalCompletion).failed()
)
or
// An edge from the then branch to the last node
@@ -401,10 +394,7 @@ module ExprTrees {
}
}
// `LetExpr` is a pre-order tree such that the pattern itself ends up
// dominating successors in the graph in the same way that patterns do in
// `match` expressions.
class LetExprTree extends StandardPreOrderTree, LetExpr {
class LetExprTree extends StandardPostOrderTree, LetExpr {
override AstNode getChildNode(int i) {
i = 0 and
result = this.getScrutinee()
@@ -456,21 +446,15 @@ module ExprTrees {
override predicate first(AstNode node) { first(super.getCondition(), node) }
private ConditionalCompletion conditionCompletion(Completion c) {
if super.getCondition() instanceof LetExpr
then result = c.(MatchCompletion)
else result = c.(BooleanCompletion)
}
override predicate succ(AstNode pred, AstNode succ, Completion c) {
super.succ(pred, succ, c)
or
last(super.getCondition(), pred, c) and
this.conditionCompletion(c).succeeded() and
c.(ConditionalCompletion).succeeded() and
first(this.getLoopBody(), succ)
or
last(super.getCondition(), pred, c) and
this.conditionCompletion(c).failed() and
c.(ConditionalCompletion).failed() and
succ = this
}
}

View File

@@ -71,13 +71,7 @@ module ConditionalCompletionSplitting {
child = parent.(LogicalNotExpr).getExpr() and
childCompletion.getDual() = parentCompletion
or
(
childCompletion = parentCompletion
or
// needed for `let` expressions
childCompletion.(MatchCompletion).getValue() =
parentCompletion.(BooleanCompletion).getValue()
) and
childCompletion = parentCompletion and
(
child = parent.(BinaryLogicalOperation).getAnOperand()
or
@@ -92,6 +86,9 @@ module ConditionalCompletionSplitting {
or
child = parent.(PatternTrees::PostOrderPatTree).getPat(_)
)
or
child = parent.(LetExpr).getPat() and
childCompletion.(MatchCompletion).getValue() = parentCompletion.(BooleanCompletion).getValue()
}
}

View File

@@ -194,9 +194,16 @@ module Ssa {
ae.getRhs() = value
)
or
exists(LetStmtCfgNode ls |
ls.getPat().(IdentPatCfgNode).getName() = write and
ls.getInitializer() = value
exists(IdentPatCfgNode pat | pat.getName() = write |
exists(LetStmtCfgNode ls |
pat = ls.getPat() and
ls.getInitializer() = value
)
or
exists(LetExprCfgNode le |
pat = le.getPat() and
le.getScrutinee() = value
)
)
}

View File

@@ -241,6 +241,12 @@ module LocalFlow {
nodeTo.getCfgNode() = s.getPat()
)
or
// An edge from the right-hand side of a let expression to the left-hand side.
exists(LetExprCfgNode e |
nodeFrom.getCfgNode() = e.getScrutinee() and
nodeTo.getCfgNode() = e.getPat()
)
or
exists(IdentPatCfgNode p |
not p.isRef() and
nodeFrom.getCfgNode() = p and
@@ -379,6 +385,8 @@ module RustDataFlow implements InputSig<Location> {
predicate neverSkipInPathGraph(Node node) {
node.(Node::Node).getCfgNode() = any(LetStmtCfgNode s).getPat()
or
node.(Node::Node).getCfgNode() = any(LetExprCfgNode e).getPat()
or
node.(Node::Node).getCfgNode() = any(AssignmentExprCfgNode a).getLhs()
or
exists(MatchExprCfgNode match |
@@ -899,6 +907,12 @@ module VariableCapture {
v.getPat() = ls.getPat().getPat() and
ls.getInitializer() = source
)
or
exists(LetExprCfgNode le |
this = le and
v.getPat() = le.getPat().getPat() and
le.getScrutinee() = source
)
}
CapturedVariable getVariable() { result = v }

View File

@@ -1,6 +1,6 @@
private import rust
private import codeql.rust.controlflow.ControlFlowGraph
private import codeql.rust.elements.internal.generated.ParentChild
private import codeql.rust.elements.internal.generated.ParentChild as ParentChild
private import codeql.rust.elements.internal.PathImpl::Impl as PathImpl
private import codeql.rust.elements.internal.PathExprBaseImpl::Impl as PathExprBaseImpl
private import codeql.rust.elements.internal.FormatTemplateVariableAccessImpl::Impl as FormatTemplateVariableAccessImpl
@@ -36,6 +36,38 @@ module Impl {
ClosureBodyScope() { this = any(ClosureExpr ce).getBody() }
}
/**
* A scope for conditions, which may introduce variables using `let` expressions.
*
* Such variables are only available in the body guarded by the condition.
*/
class ConditionScope extends VariableScope, Expr {
private AstNode parent;
private AstNode body;
ConditionScope() {
parent =
any(IfExpr ie |
this = ie.getCondition() and
body = ie.getThen()
)
or
parent =
any(WhileExpr we |
this = we.getCondition() and
body = we.getLoopBody()
)
}
/** Gets the parent of this condition. */
AstNode getParent() { result = parent }
/**
* Gets the body in which variables introduced in this scope are available.
*/
AstNode getBody() { result = body }
}
private Pat getAPatAncestor(Pat p) {
(p instanceof IdentPat or p instanceof OrPat) and
exists(Pat p0 | result = p0.getParentPat() |
@@ -152,8 +184,14 @@ module Impl {
/** Gets the `let` statement that introduces this variable, if any. */
LetStmt getLetStmt() { this.getPat() = result.getPat() }
/** Gets the `let` expression that introduces this variable, if any. */
LetExpr getLetExpr() { this.getPat() = result.getPat() }
/** Gets the initial value of this variable, if any. */
Expr getInitializer() { result = this.getLetStmt().getInitializer() }
Expr getInitializer() {
result = this.getLetStmt().getInitializer() or
result = this.getLetExpr().getScrutinee()
}
/** Holds if this variable is captured. */
predicate isCaptured() { this.getAnAccess().isCapture() }
@@ -193,15 +231,53 @@ module Impl {
string getName() { result = name_ }
}
pragma[nomagic]
private Element getImmediateChildAdj(Element e, int preOrd, int index) {
result = ParentChild::getImmediateChild(e, index) and
preOrd = 0 and
not exists(ConditionScope cs |
e = cs.getParent() and
result = cs.getBody()
)
or
result = e.(ConditionScope).getBody() and
preOrd = 1 and
index = 0
}
/**
* An adjusted version of `ParentChild::getImmediateChild`, which makes the following
* two adjustments:
*
* 1. For conditions like `if cond body`, instead of letting `body` be the second child
* of `if`, we make it the last child of `cond`. This ensures that variables
* introduced in the `cond` scope are available in `body`.
*
* 2. A similar adjustment is made for `while` loops: the body of the loop is made a
* child of the loop condition instead of the loop itself.
*/
pragma[nomagic]
private Element getImmediateChildAdj(Element e, int index) {
result =
rank[index + 1](Element res, int preOrd, int i |
res = getImmediateChildAdj(e, preOrd, i)
|
res order by preOrd, i
)
}
private Element getImmediateParentAdj(Element e) { e = getImmediateChildAdj(result, _) }
private AstNode getAnAncestorInVariableScope(AstNode n) {
(
n instanceof Pat or
n instanceof VariableAccessCand or
n instanceof LetStmt or
n = any(LetExpr le).getScrutinee() or
n instanceof VariableScope
) and
exists(AstNode n0 |
result = getImmediateParent(n0) or
result = getImmediateParentAdj(n0) or
result = n0.(FormatTemplateVariableAccess).getArgument().getParent().getParent()
|
n0 = n
@@ -243,14 +319,15 @@ module Impl {
this instanceof VariableScope or
this instanceof VariableAccessCand or
this instanceof LetStmt or
getImmediateChild(this, _) instanceof RelevantElement
this = any(LetExpr le).getScrutinee() or
getImmediateChildAdj(this, _) instanceof RelevantElement
}
pragma[nomagic]
private RelevantElement getChild(int index) { result = getImmediateChild(this, index) }
private RelevantElement getChild(int index) { result = getImmediateChildAdj(this, index) }
pragma[nomagic]
private RelevantElement getImmediateChildMin(int index) {
private RelevantElement getImmediateChildAdjMin(int index) {
// A child may have multiple positions for different accessors,
// so always use the first
result = this.getChild(index) and
@@ -258,16 +335,16 @@ module Impl {
}
pragma[nomagic]
RelevantElement getImmediateChild(int index) {
RelevantElement getImmediateChildAdj(int index) {
result =
rank[index + 1](Element res, int i | res = this.getImmediateChildMin(i) | res order by i)
rank[index + 1](Element res, int i | res = this.getImmediateChildAdjMin(i) | res order by i)
}
pragma[nomagic]
RelevantElement getImmediateLastChild() {
exists(int last |
result = this.getImmediateChild(last) and
not exists(this.getImmediateChild(last + 1))
result = this.getImmediateChildAdj(last) and
not exists(this.getImmediateChildAdj(last + 1))
)
}
}
@@ -288,13 +365,13 @@ module Impl {
|
// first child of a previously numbered node
result = getPreOrderNumbering(scope, parent) + 1 and
n = parent.getImmediateChild(0)
n = parent.getImmediateChildAdj(0)
or
// non-first child of a previously numbered node
exists(RelevantElement child, int i |
result = getLastPreOrderNumbering(scope, child) + 1 and
child = parent.getImmediateChild(i) and
n = parent.getImmediateChild(i + 1)
child = parent.getImmediateChildAdj(i) and
n = parent.getImmediateChildAdj(i + 1)
)
)
}
@@ -309,7 +386,7 @@ module Impl {
result = getPreOrderNumbering(scope, leaf) and
leaf != scope and
(
not exists(leaf.getImmediateChild(_))
not exists(leaf.getImmediateChildAdj(_))
or
leaf instanceof VariableScope
)
@@ -331,7 +408,7 @@ module Impl {
/**
* Holds if `v` is named `name` and is declared inside variable scope
* `scope`. The pre-order numbering of the binding site of `v`, amongst
* all nodes nester under `scope`, is `ord`.
* all nodes nested under `scope`, is `ord`.
*/
private predicate variableDeclInScope(Variable v, VariableScope scope, string name, int ord) {
name = v.getText() and
@@ -354,11 +431,13 @@ module Impl {
ord = getLastPreOrderNumbering(scope, let) + 1
)
or
exists(IfExpr ie, LetExpr let |
exists(LetExpr let, Expr scrutinee |
let.getPat() = pat and
ie.getCondition() = let and
scope = ie.getThen() and
ord = getPreOrderNumbering(scope, scope)
scrutinee = let.getScrutinee() and
scope = getEnclosingScope(scrutinee) and
// for `let` expressions, variables are bound _after_ the expression, i.e.
// not in the RHS
ord = getLastPreOrderNumbering(scope, scrutinee) + 1
)
or
exists(ForExpr fe |
@@ -366,13 +445,6 @@ module Impl {
scope = fe.getLoopBody() and
ord = getPreOrderNumbering(scope, scope)
)
or
exists(WhileExpr we, LetExpr let |
let.getPat() = pat and
we.getCondition() = let and
scope = we.getLoopBody() and
ord = getPreOrderNumbering(scope, scope)
)
)
)
}
@@ -612,7 +684,7 @@ module Impl {
or
exists(Expr mid |
assignmentExprDescendant(mid) and
getImmediateParent(e) = mid and
getImmediateParentAdj(e) = mid and
not mid instanceof DerefExpr and
not mid instanceof FieldExpr and
not mid instanceof IndexExpr

View File

@@ -328,6 +328,11 @@ private module CertainTypeInference {
let.getInitializer() = n2
)
or
exists(LetExpr let |
let.getPat() = n1 and
let.getScrutinee() = n2
)
or
n1 = n2.(ParenExpr).getExpr()
)
or
@@ -466,11 +471,6 @@ private predicate typeEquality(AstNode n1, TypePath prefix1, AstNode n2, TypePat
or
n1 = n2.(MatchExpr).getAnArm().getExpr()
or
exists(LetExpr let |
n1 = let.getScrutinee() and
n2 = let.getPat()
)
or
exists(MatchExpr me |
n1 = me.getScrutinee() and
n2 = me.getAnArm().getPat()

View File

@@ -13,13 +13,14 @@ edges
| test.rs:5:67:11:5 | { ... } | test.rs:5:5:11:5 | exit fn test_and_if_let (normal) | |
| test.rs:6:9:10:9 | if ... {...} else {...} | test.rs:5:67:11:5 | { ... } | |
| test.rs:6:12:6:12 | a | test.rs:6:12:6:31 | [boolean(false)] ... && ... | false |
| test.rs:6:12:6:12 | a | test.rs:6:17:6:31 | let ... = b | true |
| test.rs:6:12:6:12 | a | test.rs:6:31:6:31 | b | true |
| test.rs:6:12:6:31 | [boolean(false)] ... && ... | test.rs:9:13:9:17 | false | false |
| test.rs:6:12:6:31 | [boolean(true)] ... && ... | test.rs:7:13:7:13 | d | true |
| test.rs:6:17:6:31 | let ... = b | test.rs:6:31:6:31 | b | |
| test.rs:6:21:6:27 | Some(...) | test.rs:6:12:6:31 | [boolean(false)] ... && ... | no-match |
| test.rs:6:17:6:31 | [boolean(false)] let ... = b | test.rs:6:12:6:31 | [boolean(false)] ... && ... | false |
| test.rs:6:17:6:31 | [boolean(true)] let ... = b | test.rs:6:12:6:31 | [boolean(true)] ... && ... | true |
| test.rs:6:21:6:27 | Some(...) | test.rs:6:17:6:31 | [boolean(false)] let ... = b | no-match |
| test.rs:6:21:6:27 | Some(...) | test.rs:6:26:6:26 | d | match |
| test.rs:6:26:6:26 | d | test.rs:6:12:6:31 | [boolean(true)] ... && ... | match |
| test.rs:6:26:6:26 | d | test.rs:6:17:6:31 | [boolean(true)] let ... = b | match |
| test.rs:6:26:6:26 | d | test.rs:6:26:6:26 | d | |
| test.rs:6:31:6:31 | b | test.rs:6:21:6:27 | Some(...) | |
| test.rs:6:33:8:9 | { ... } | test.rs:6:9:10:9 | if ... {...} else {...} | |
@@ -40,13 +41,13 @@ edges
| test.rs:13:59:21:5 | { ... } | test.rs:13:5:21:5 | exit fn test_and_if_let2 (normal) | |
| test.rs:14:9:20:9 | if ... {...} else {...} | test.rs:13:59:21:5 | { ... } | |
| test.rs:14:12:14:12 | a | test.rs:14:12:14:25 | [boolean(false)] ... && ... | false |
| test.rs:14:12:14:12 | a | test.rs:14:17:14:25 | let ... = b | true |
| test.rs:14:12:14:12 | a | test.rs:14:25:14:25 | b | true |
| test.rs:14:12:14:25 | [boolean(false)] ... && ... | test.rs:14:12:15:16 | [boolean(false)] ... && ... | false |
| test.rs:14:12:14:25 | [boolean(true)] ... && ... | test.rs:15:16:15:16 | c | true |
| test.rs:14:12:15:16 | [boolean(false)] ... && ... | test.rs:19:13:19:17 | false | false |
| test.rs:14:12:15:16 | [boolean(true)] ... && ... | test.rs:17:13:17:13 | d | true |
| test.rs:14:17:14:25 | let ... = b | test.rs:14:25:14:25 | b | |
| test.rs:14:21:14:21 | d | test.rs:14:12:14:25 | [boolean(true)] ... && ... | match |
| test.rs:14:17:14:25 | [boolean(true)] let ... = b | test.rs:14:12:14:25 | [boolean(true)] ... && ... | true |
| test.rs:14:21:14:21 | d | test.rs:14:17:14:25 | [boolean(true)] let ... = b | match |
| test.rs:14:21:14:21 | d | test.rs:14:21:14:21 | d | |
| test.rs:14:25:14:25 | b | test.rs:14:21:14:21 | d | |
| test.rs:15:16:15:16 | c | test.rs:14:12:15:16 | [boolean(false)] ... && ... | false |

View File

@@ -118,20 +118,29 @@ dominates
| test.rs:91:17:91:22 | ExprStmt | test.rs:91:17:91:22 | ExprStmt |
| test.rs:97:5:104:5 | enter fn test_while_let | test.rs:97:5:104:5 | enter fn test_while_let |
| test.rs:97:5:104:5 | enter fn test_while_let | test.rs:99:9:103:9 | while ... { ... } |
| test.rs:97:5:104:5 | enter fn test_while_let | test.rs:99:15:99:39 | let ... = ... |
| test.rs:97:5:104:5 | enter fn test_while_let | test.rs:99:15:99:39 | [boolean(false)] let ... = ... |
| test.rs:97:5:104:5 | enter fn test_while_let | test.rs:99:24:99:24 | x |
| test.rs:97:5:104:5 | enter fn test_while_let | test.rs:99:29:99:32 | iter |
| test.rs:97:5:104:5 | enter fn test_while_let | test.rs:100:13:102:13 | if ... {...} |
| test.rs:97:5:104:5 | enter fn test_while_let | test.rs:100:17:100:17 | x |
| test.rs:97:5:104:5 | enter fn test_while_let | test.rs:101:17:101:22 | ExprStmt |
| test.rs:99:9:103:9 | while ... { ... } | test.rs:99:9:103:9 | while ... { ... } |
| test.rs:99:15:99:39 | let ... = ... | test.rs:99:9:103:9 | while ... { ... } |
| test.rs:99:15:99:39 | let ... = ... | test.rs:99:15:99:39 | let ... = ... |
| test.rs:99:15:99:39 | let ... = ... | test.rs:99:24:99:24 | x |
| test.rs:99:15:99:39 | let ... = ... | test.rs:100:13:102:13 | if ... {...} |
| test.rs:99:15:99:39 | let ... = ... | test.rs:101:17:101:22 | ExprStmt |
| test.rs:99:15:99:39 | [boolean(false)] let ... = ... | test.rs:99:15:99:39 | [boolean(false)] let ... = ... |
| test.rs:99:24:99:24 | x | test.rs:99:24:99:24 | x |
| test.rs:99:24:99:24 | x | test.rs:100:13:102:13 | if ... {...} |
| test.rs:99:24:99:24 | x | test.rs:100:17:100:17 | x |
| test.rs:99:24:99:24 | x | test.rs:101:17:101:22 | ExprStmt |
| test.rs:99:29:99:32 | iter | test.rs:99:9:103:9 | while ... { ... } |
| test.rs:99:29:99:32 | iter | test.rs:99:15:99:39 | [boolean(false)] let ... = ... |
| test.rs:99:29:99:32 | iter | test.rs:99:24:99:24 | x |
| test.rs:99:29:99:32 | iter | test.rs:99:29:99:32 | iter |
| test.rs:99:29:99:32 | iter | test.rs:100:13:102:13 | if ... {...} |
| test.rs:99:29:99:32 | iter | test.rs:100:17:100:17 | x |
| test.rs:99:29:99:32 | iter | test.rs:101:17:101:22 | ExprStmt |
| test.rs:100:13:102:13 | if ... {...} | test.rs:100:13:102:13 | if ... {...} |
| test.rs:100:17:100:17 | x | test.rs:100:13:102:13 | if ... {...} |
| test.rs:100:17:100:17 | x | test.rs:100:17:100:17 | x |
| test.rs:100:17:100:17 | x | test.rs:101:17:101:22 | ExprStmt |
| test.rs:101:17:101:22 | ExprStmt | test.rs:101:17:101:22 | ExprStmt |
| test.rs:106:5:113:5 | enter fn test_for | test.rs:106:5:113:5 | enter fn test_for |
| test.rs:106:5:113:5 | enter fn test_for | test.rs:107:9:112:9 | for ... in ... { ... } |
@@ -167,18 +176,30 @@ dominates
| test.rs:140:13:140:19 | ExprStmt | test.rs:140:13:140:19 | ExprStmt |
| test.rs:145:5:151:5 | enter fn test_if_let_else | test.rs:145:5:151:5 | enter fn test_if_let_else |
| test.rs:145:5:151:5 | enter fn test_if_let_else | test.rs:146:9:150:9 | if ... {...} else {...} |
| test.rs:145:5:151:5 | enter fn test_if_let_else | test.rs:146:12:146:26 | [boolean(false)] let ... = a |
| test.rs:145:5:151:5 | enter fn test_if_let_else | test.rs:146:21:146:21 | n |
| test.rs:145:5:151:5 | enter fn test_if_let_else | test.rs:147:13:147:13 | n |
| test.rs:145:5:151:5 | enter fn test_if_let_else | test.rs:149:13:149:13 | 0 |
| test.rs:146:9:150:9 | if ... {...} else {...} | test.rs:146:9:150:9 | if ... {...} else {...} |
| test.rs:146:12:146:26 | [boolean(false)] let ... = a | test.rs:146:12:146:26 | [boolean(false)] let ... = a |
| test.rs:146:12:146:26 | [boolean(false)] let ... = a | test.rs:149:13:149:13 | 0 |
| test.rs:146:21:146:21 | n | test.rs:146:21:146:21 | n |
| test.rs:146:21:146:21 | n | test.rs:147:13:147:13 | n |
| test.rs:147:13:147:13 | n | test.rs:147:13:147:13 | n |
| test.rs:149:13:149:13 | 0 | test.rs:149:13:149:13 | 0 |
| test.rs:153:5:158:5 | enter fn test_if_let | test.rs:153:5:158:5 | enter fn test_if_let |
| test.rs:153:5:158:5 | enter fn test_if_let | test.rs:153:5:158:5 | exit fn test_if_let (normal) |
| test.rs:153:5:158:5 | enter fn test_if_let | test.rs:154:9:156:9 | if ... {...} |
| test.rs:153:5:158:5 | enter fn test_if_let | test.rs:154:12:154:26 | [boolean(false)] let ... = a |
| test.rs:153:5:158:5 | enter fn test_if_let | test.rs:154:21:154:21 | n |
| test.rs:153:5:158:5 | enter fn test_if_let | test.rs:155:13:155:21 | ExprStmt |
| test.rs:153:5:158:5 | exit fn test_if_let (normal) | test.rs:153:5:158:5 | exit fn test_if_let (normal) |
| test.rs:154:9:156:9 | if ... {...} | test.rs:154:9:156:9 | if ... {...} |
| test.rs:154:12:154:26 | [boolean(false)] let ... = a | test.rs:154:9:156:9 | if ... {...} |
| test.rs:154:12:154:26 | [boolean(false)] let ... = a | test.rs:154:12:154:26 | [boolean(false)] let ... = a |
| test.rs:154:21:154:21 | n | test.rs:154:21:154:21 | n |
| test.rs:154:21:154:21 | n | test.rs:155:13:155:21 | ExprStmt |
| test.rs:155:13:155:21 | ExprStmt | test.rs:155:13:155:21 | ExprStmt |
| test.rs:160:5:166:5 | enter fn test_nested_if | test.rs:160:5:166:5 | enter fn test_nested_if |
| test.rs:160:5:166:5 | enter fn test_nested_if | test.rs:161:9:165:9 | if ... {...} else {...} |
| test.rs:160:5:166:5 | enter fn test_nested_if | test.rs:161:13:161:48 | [boolean(false)] if ... {...} else {...} |
@@ -847,15 +868,20 @@ postDominance
| test.rs:97:5:104:5 | enter fn test_while_let | test.rs:97:5:104:5 | enter fn test_while_let |
| test.rs:99:9:103:9 | while ... { ... } | test.rs:97:5:104:5 | enter fn test_while_let |
| test.rs:99:9:103:9 | while ... { ... } | test.rs:99:9:103:9 | while ... { ... } |
| test.rs:99:9:103:9 | while ... { ... } | test.rs:99:15:99:39 | let ... = ... |
| test.rs:99:9:103:9 | while ... { ... } | test.rs:99:15:99:39 | [boolean(false)] let ... = ... |
| test.rs:99:9:103:9 | while ... { ... } | test.rs:99:24:99:24 | x |
| test.rs:99:9:103:9 | while ... { ... } | test.rs:99:29:99:32 | iter |
| test.rs:99:9:103:9 | while ... { ... } | test.rs:100:13:102:13 | if ... {...} |
| test.rs:99:9:103:9 | while ... { ... } | test.rs:100:17:100:17 | x |
| test.rs:99:9:103:9 | while ... { ... } | test.rs:101:17:101:22 | ExprStmt |
| test.rs:99:15:99:39 | let ... = ... | test.rs:97:5:104:5 | enter fn test_while_let |
| test.rs:99:15:99:39 | let ... = ... | test.rs:99:15:99:39 | let ... = ... |
| test.rs:99:15:99:39 | let ... = ... | test.rs:100:13:102:13 | if ... {...} |
| test.rs:99:15:99:39 | [boolean(false)] let ... = ... | test.rs:99:15:99:39 | [boolean(false)] let ... = ... |
| test.rs:99:24:99:24 | x | test.rs:99:24:99:24 | x |
| test.rs:99:29:99:32 | iter | test.rs:97:5:104:5 | enter fn test_while_let |
| test.rs:99:29:99:32 | iter | test.rs:99:29:99:32 | iter |
| test.rs:99:29:99:32 | iter | test.rs:100:13:102:13 | if ... {...} |
| test.rs:100:13:102:13 | if ... {...} | test.rs:100:13:102:13 | if ... {...} |
| test.rs:100:17:100:17 | x | test.rs:99:24:99:24 | x |
| test.rs:100:17:100:17 | x | test.rs:100:17:100:17 | x |
| test.rs:101:17:101:22 | ExprStmt | test.rs:101:17:101:22 | ExprStmt |
| test.rs:106:5:113:5 | enter fn test_for | test.rs:106:5:113:5 | enter fn test_for |
| test.rs:107:9:112:9 | for ... in ... { ... } | test.rs:106:5:113:5 | enter fn test_for |
@@ -888,17 +914,29 @@ postDominance
| test.rs:145:5:151:5 | enter fn test_if_let_else | test.rs:145:5:151:5 | enter fn test_if_let_else |
| test.rs:146:9:150:9 | if ... {...} else {...} | test.rs:145:5:151:5 | enter fn test_if_let_else |
| test.rs:146:9:150:9 | if ... {...} else {...} | test.rs:146:9:150:9 | if ... {...} else {...} |
| test.rs:146:9:150:9 | if ... {...} else {...} | test.rs:146:12:146:26 | [boolean(false)] let ... = a |
| test.rs:146:9:150:9 | if ... {...} else {...} | test.rs:146:21:146:21 | n |
| test.rs:146:9:150:9 | if ... {...} else {...} | test.rs:147:13:147:13 | n |
| test.rs:146:9:150:9 | if ... {...} else {...} | test.rs:149:13:149:13 | 0 |
| test.rs:146:12:146:26 | [boolean(false)] let ... = a | test.rs:146:12:146:26 | [boolean(false)] let ... = a |
| test.rs:146:21:146:21 | n | test.rs:146:21:146:21 | n |
| test.rs:147:13:147:13 | n | test.rs:146:21:146:21 | n |
| test.rs:147:13:147:13 | n | test.rs:147:13:147:13 | n |
| test.rs:149:13:149:13 | 0 | test.rs:146:12:146:26 | [boolean(false)] let ... = a |
| test.rs:149:13:149:13 | 0 | test.rs:149:13:149:13 | 0 |
| test.rs:153:5:158:5 | enter fn test_if_let | test.rs:153:5:158:5 | enter fn test_if_let |
| test.rs:153:5:158:5 | exit fn test_if_let (normal) | test.rs:153:5:158:5 | enter fn test_if_let |
| test.rs:153:5:158:5 | exit fn test_if_let (normal) | test.rs:153:5:158:5 | exit fn test_if_let (normal) |
| test.rs:153:5:158:5 | exit fn test_if_let (normal) | test.rs:154:9:156:9 | if ... {...} |
| test.rs:153:5:158:5 | exit fn test_if_let (normal) | test.rs:154:12:154:26 | [boolean(false)] let ... = a |
| test.rs:153:5:158:5 | exit fn test_if_let (normal) | test.rs:154:21:154:21 | n |
| test.rs:153:5:158:5 | exit fn test_if_let (normal) | test.rs:155:13:155:21 | ExprStmt |
| test.rs:154:9:156:9 | if ... {...} | test.rs:154:9:156:9 | if ... {...} |
| test.rs:154:9:156:9 | if ... {...} | test.rs:154:12:154:26 | [boolean(false)] let ... = a |
| test.rs:154:12:154:26 | [boolean(false)] let ... = a | test.rs:154:12:154:26 | [boolean(false)] let ... = a |
| test.rs:154:21:154:21 | n | test.rs:154:21:154:21 | n |
| test.rs:155:13:155:21 | ExprStmt | test.rs:154:21:154:21 | n |
| test.rs:155:13:155:21 | ExprStmt | test.rs:155:13:155:21 | ExprStmt |
| test.rs:160:5:166:5 | enter fn test_nested_if | test.rs:160:5:166:5 | enter fn test_nested_if |
| test.rs:161:9:165:9 | if ... {...} else {...} | test.rs:160:5:166:5 | enter fn test_nested_if |
| test.rs:161:9:165:9 | if ... {...} else {...} | test.rs:161:9:165:9 | if ... {...} else {...} |
@@ -1462,11 +1500,13 @@ immediateDominator
| test.rs:89:13:89:14 | ExprStmt | test.rs:88:15:88:15 | b |
| test.rs:90:13:92:13 | if ... {...} | test.rs:89:13:89:14 | ExprStmt |
| test.rs:91:17:91:22 | ExprStmt | test.rs:89:13:89:14 | ExprStmt |
| test.rs:99:9:103:9 | while ... { ... } | test.rs:99:15:99:39 | let ... = ... |
| test.rs:99:15:99:39 | let ... = ... | test.rs:97:5:104:5 | enter fn test_while_let |
| test.rs:99:24:99:24 | x | test.rs:99:15:99:39 | let ... = ... |
| test.rs:100:13:102:13 | if ... {...} | test.rs:99:24:99:24 | x |
| test.rs:101:17:101:22 | ExprStmt | test.rs:99:24:99:24 | x |
| test.rs:99:9:103:9 | while ... { ... } | test.rs:99:29:99:32 | iter |
| test.rs:99:15:99:39 | [boolean(false)] let ... = ... | test.rs:99:29:99:32 | iter |
| test.rs:99:24:99:24 | x | test.rs:99:29:99:32 | iter |
| test.rs:99:29:99:32 | iter | test.rs:97:5:104:5 | enter fn test_while_let |
| test.rs:100:13:102:13 | if ... {...} | test.rs:100:17:100:17 | x |
| test.rs:100:17:100:17 | x | test.rs:99:24:99:24 | x |
| test.rs:101:17:101:22 | ExprStmt | test.rs:100:17:100:17 | x |
| test.rs:107:9:112:9 | for ... in ... { ... } | test.rs:107:13:107:13 | i |
| test.rs:107:13:107:13 | i | test.rs:106:5:113:5 | enter fn test_for |
| test.rs:108:13:110:13 | ExprStmt | test.rs:107:13:107:13 | i |
@@ -1478,11 +1518,15 @@ immediateDominator
| test.rs:139:9:141:9 | if b {...} | test.rs:137:5:143:5 | enter fn test_if_without_else |
| test.rs:140:13:140:19 | ExprStmt | test.rs:137:5:143:5 | enter fn test_if_without_else |
| test.rs:146:9:150:9 | if ... {...} else {...} | test.rs:145:5:151:5 | enter fn test_if_let_else |
| test.rs:146:12:146:26 | [boolean(false)] let ... = a | test.rs:145:5:151:5 | enter fn test_if_let_else |
| test.rs:146:21:146:21 | n | test.rs:145:5:151:5 | enter fn test_if_let_else |
| test.rs:149:13:149:13 | 0 | test.rs:145:5:151:5 | enter fn test_if_let_else |
| test.rs:147:13:147:13 | n | test.rs:146:21:146:21 | n |
| test.rs:149:13:149:13 | 0 | test.rs:146:12:146:26 | [boolean(false)] let ... = a |
| test.rs:153:5:158:5 | exit fn test_if_let (normal) | test.rs:153:5:158:5 | enter fn test_if_let |
| test.rs:154:9:156:9 | if ... {...} | test.rs:153:5:158:5 | enter fn test_if_let |
| test.rs:154:9:156:9 | if ... {...} | test.rs:154:12:154:26 | [boolean(false)] let ... = a |
| test.rs:154:12:154:26 | [boolean(false)] let ... = a | test.rs:153:5:158:5 | enter fn test_if_let |
| test.rs:154:21:154:21 | n | test.rs:153:5:158:5 | enter fn test_if_let |
| test.rs:155:13:155:21 | ExprStmt | test.rs:154:21:154:21 | n |
| test.rs:161:9:165:9 | if ... {...} else {...} | test.rs:160:5:166:5 | enter fn test_nested_if |
| test.rs:161:13:161:48 | [boolean(false)] if ... {...} else {...} | test.rs:160:5:166:5 | enter fn test_nested_if |
| test.rs:161:13:161:48 | [boolean(true)] if ... {...} else {...} | test.rs:160:5:166:5 | enter fn test_nested_if |
@@ -1721,13 +1765,20 @@ controls
| test.rs:88:15:88:15 | b | test.rs:91:17:91:22 | ExprStmt | true |
| test.rs:89:13:89:14 | ExprStmt | test.rs:90:13:92:13 | if ... {...} | false |
| test.rs:89:13:89:14 | ExprStmt | test.rs:91:17:91:22 | ExprStmt | true |
| test.rs:99:24:99:24 | x | test.rs:100:13:102:13 | if ... {...} | false |
| test.rs:99:24:99:24 | x | test.rs:100:13:102:13 | if ... {...} | true |
| test.rs:99:24:99:24 | x | test.rs:100:17:100:17 | x | true |
| test.rs:99:24:99:24 | x | test.rs:101:17:101:22 | ExprStmt | true |
| test.rs:100:17:100:17 | x | test.rs:100:13:102:13 | if ... {...} | false |
| test.rs:100:17:100:17 | x | test.rs:101:17:101:22 | ExprStmt | true |
| test.rs:108:13:110:13 | ExprStmt | test.rs:108:13:110:13 | if ... {...} | false |
| test.rs:108:13:110:13 | ExprStmt | test.rs:109:17:109:22 | ExprStmt | true |
| test.rs:129:5:135:5 | enter fn test_if_else | test.rs:131:13:131:13 | 0 | true |
| test.rs:129:5:135:5 | enter fn test_if_else | test.rs:133:13:133:13 | n | false |
| test.rs:137:5:143:5 | enter fn test_if_without_else | test.rs:140:13:140:19 | ExprStmt | true |
| test.rs:146:12:146:26 | [boolean(false)] let ... = a | test.rs:149:13:149:13 | 0 | false |
| test.rs:146:21:146:21 | n | test.rs:147:13:147:13 | n | true |
| test.rs:154:12:154:26 | [boolean(false)] let ... = a | test.rs:154:9:156:9 | if ... {...} | false |
| test.rs:154:21:154:21 | n | test.rs:155:13:155:21 | ExprStmt | true |
| test.rs:160:5:166:5 | enter fn test_nested_if | test.rs:161:22:161:32 | [boolean(false)] { ... } | true |
| test.rs:160:5:166:5 | enter fn test_nested_if | test.rs:161:22:161:32 | [boolean(true)] { ... } | true |
| test.rs:160:5:166:5 | enter fn test_nested_if | test.rs:161:24:161:24 | a | true |
@@ -1911,14 +1962,20 @@ successor
| test.rs:88:15:88:15 | b | test.rs:89:13:89:14 | ExprStmt | true |
| test.rs:89:13:89:14 | ExprStmt | test.rs:90:13:92:13 | if ... {...} | false |
| test.rs:89:13:89:14 | ExprStmt | test.rs:91:17:91:22 | ExprStmt | true |
| test.rs:99:24:99:24 | x | test.rs:100:13:102:13 | if ... {...} | false |
| test.rs:99:24:99:24 | x | test.rs:101:17:101:22 | ExprStmt | true |
| test.rs:99:15:99:39 | [boolean(false)] let ... = ... | test.rs:99:9:103:9 | while ... { ... } | false |
| test.rs:99:24:99:24 | x | test.rs:100:17:100:17 | x | true |
| test.rs:100:17:100:17 | x | test.rs:100:13:102:13 | if ... {...} | false |
| test.rs:100:17:100:17 | x | test.rs:101:17:101:22 | ExprStmt | true |
| test.rs:108:13:110:13 | ExprStmt | test.rs:108:13:110:13 | if ... {...} | false |
| test.rs:108:13:110:13 | ExprStmt | test.rs:109:17:109:22 | ExprStmt | true |
| test.rs:129:5:135:5 | enter fn test_if_else | test.rs:131:13:131:13 | 0 | true |
| test.rs:129:5:135:5 | enter fn test_if_else | test.rs:133:13:133:13 | n | false |
| test.rs:137:5:143:5 | enter fn test_if_without_else | test.rs:139:9:141:9 | if b {...} | false |
| test.rs:137:5:143:5 | enter fn test_if_without_else | test.rs:140:13:140:19 | ExprStmt | true |
| test.rs:146:12:146:26 | [boolean(false)] let ... = a | test.rs:149:13:149:13 | 0 | false |
| test.rs:146:21:146:21 | n | test.rs:147:13:147:13 | n | true |
| test.rs:154:12:154:26 | [boolean(false)] let ... = a | test.rs:154:9:156:9 | if ... {...} | false |
| test.rs:154:21:154:21 | n | test.rs:155:13:155:21 | ExprStmt | true |
| test.rs:160:5:166:5 | enter fn test_nested_if | test.rs:161:24:161:24 | a | true |
| test.rs:160:5:166:5 | enter fn test_nested_if | test.rs:161:41:161:41 | a | false |
| test.rs:161:13:161:48 | [boolean(false)] if ... {...} else {...} | test.rs:164:13:164:13 | 0 | false |
@@ -2058,10 +2115,10 @@ joinBlockPredecessor
| test.rs:88:9:94:9 | while b { ... } | test.rs:91:17:91:22 | ExprStmt | 1 |
| test.rs:88:15:88:15 | b | test.rs:86:5:95:5 | enter fn test_while | 1 |
| test.rs:88:15:88:15 | b | test.rs:90:13:92:13 | if ... {...} | 0 |
| test.rs:99:9:103:9 | while ... { ... } | test.rs:99:15:99:39 | let ... = ... | 0 |
| test.rs:99:9:103:9 | while ... { ... } | test.rs:99:15:99:39 | [boolean(false)] let ... = ... | 0 |
| test.rs:99:9:103:9 | while ... { ... } | test.rs:101:17:101:22 | ExprStmt | 1 |
| test.rs:99:15:99:39 | let ... = ... | test.rs:97:5:104:5 | enter fn test_while_let | 1 |
| test.rs:99:15:99:39 | let ... = ... | test.rs:100:13:102:13 | if ... {...} | 0 |
| test.rs:99:29:99:32 | iter | test.rs:97:5:104:5 | enter fn test_while_let | 1 |
| test.rs:99:29:99:32 | iter | test.rs:100:13:102:13 | if ... {...} | 0 |
| test.rs:107:9:112:9 | for ... in ... { ... } | test.rs:107:13:107:13 | i | 1 |
| test.rs:107:9:112:9 | for ... in ... { ... } | test.rs:109:17:109:22 | ExprStmt | 0 |
| test.rs:107:13:107:13 | i | test.rs:106:5:113:5 | enter fn test_for | 1 |
@@ -2070,10 +2127,10 @@ joinBlockPredecessor
| test.rs:130:9:134:9 | if ... {...} else {...} | test.rs:133:13:133:13 | n | 0 |
| test.rs:139:9:141:9 | if b {...} | test.rs:137:5:143:5 | enter fn test_if_without_else | 1 |
| test.rs:139:9:141:9 | if b {...} | test.rs:140:13:140:19 | ExprStmt | 0 |
| test.rs:146:9:150:9 | if ... {...} else {...} | test.rs:146:21:146:21 | n | 0 |
| test.rs:146:9:150:9 | if ... {...} else {...} | test.rs:149:13:149:13 | 0 | 1 |
| test.rs:146:9:150:9 | if ... {...} else {...} | test.rs:147:13:147:13 | n | 1 |
| test.rs:146:9:150:9 | if ... {...} else {...} | test.rs:149:13:149:13 | 0 | 0 |
| test.rs:153:5:158:5 | exit fn test_if_let (normal) | test.rs:154:9:156:9 | if ... {...} | 1 |
| test.rs:153:5:158:5 | exit fn test_if_let (normal) | test.rs:154:21:154:21 | n | 0 |
| test.rs:153:5:158:5 | exit fn test_if_let (normal) | test.rs:155:13:155:21 | ExprStmt | 0 |
| test.rs:161:9:165:9 | if ... {...} else {...} | test.rs:162:13:162:13 | 1 | 1 |
| test.rs:161:9:165:9 | if ... {...} else {...} | test.rs:164:13:164:13 | 0 | 0 |
| test.rs:161:13:161:48 | [boolean(false)] if ... {...} else {...} | test.rs:161:22:161:32 | [boolean(false)] { ... } | 1 |

View File

@@ -194,20 +194,21 @@ edges
| test.rs:97:5:104:5 | exit fn test_while_let (normal) | test.rs:97:5:104:5 | exit fn test_while_let | |
| test.rs:97:25:104:5 | { ... } | test.rs:97:5:104:5 | exit fn test_while_let (normal) | |
| test.rs:98:9:98:29 | let ... = ... | test.rs:98:24:98:24 | 1 | |
| test.rs:98:13:98:20 | mut iter | test.rs:99:15:99:39 | let ... = ... | match |
| test.rs:98:13:98:20 | mut iter | test.rs:99:29:99:32 | iter | match |
| test.rs:98:17:98:20 | iter | test.rs:98:13:98:20 | mut iter | |
| test.rs:98:24:98:24 | 1 | test.rs:98:27:98:28 | 10 | |
| test.rs:98:24:98:28 | 1..10 | test.rs:98:17:98:20 | iter | |
| test.rs:98:27:98:28 | 10 | test.rs:98:24:98:28 | 1..10 | |
| test.rs:99:9:103:9 | while ... { ... } | test.rs:97:25:104:5 | { ... } | |
| test.rs:99:15:99:39 | let ... = ... | test.rs:99:29:99:32 | iter | |
| test.rs:99:19:99:25 | Some(...) | test.rs:99:9:103:9 | while ... { ... } | no-match |
| test.rs:99:15:99:39 | [boolean(false)] let ... = ... | test.rs:99:9:103:9 | while ... { ... } | false |
| test.rs:99:15:99:39 | [boolean(true)] let ... = ... | test.rs:100:17:100:17 | x | true |
| test.rs:99:19:99:25 | Some(...) | test.rs:99:15:99:39 | [boolean(false)] let ... = ... | no-match |
| test.rs:99:19:99:25 | Some(...) | test.rs:99:24:99:24 | x | match |
| test.rs:99:24:99:24 | x | test.rs:99:15:99:39 | [boolean(true)] let ... = ... | match |
| test.rs:99:24:99:24 | x | test.rs:99:24:99:24 | x | |
| test.rs:99:24:99:24 | x | test.rs:100:17:100:17 | x | match |
| test.rs:99:29:99:32 | iter | test.rs:99:29:99:39 | iter.next() | |
| test.rs:99:29:99:39 | iter.next() | test.rs:99:19:99:25 | Some(...) | |
| test.rs:99:41:103:9 | { ... } | test.rs:99:15:99:39 | let ... = ... | |
| test.rs:99:41:103:9 | { ... } | test.rs:99:29:99:32 | iter | |
| test.rs:100:13:102:13 | if ... {...} | test.rs:99:41:103:9 | { ... } | |
| test.rs:100:17:100:17 | x | test.rs:100:22:100:22 | 5 | |
| test.rs:100:17:100:22 | ... == ... | test.rs:100:13:102:13 | if ... {...} | false |
@@ -308,14 +309,15 @@ edges
| test.rs:145:5:151:5 | exit fn test_if_let_else (normal) | test.rs:145:5:151:5 | exit fn test_if_let_else | |
| test.rs:145:25:145:25 | a | test.rs:145:25:145:25 | a | |
| test.rs:145:25:145:25 | a | test.rs:145:25:145:38 | ...: Option::<...> | match |
| test.rs:145:25:145:38 | ...: Option::<...> | test.rs:146:12:146:26 | let ... = a | |
| test.rs:145:25:145:38 | ...: Option::<...> | test.rs:146:26:146:26 | a | |
| test.rs:145:48:151:5 | { ... } | test.rs:145:5:151:5 | exit fn test_if_let_else (normal) | |
| test.rs:146:9:150:9 | if ... {...} else {...} | test.rs:145:48:151:5 | { ... } | |
| test.rs:146:12:146:26 | let ... = a | test.rs:146:26:146:26 | a | |
| test.rs:146:12:146:26 | [boolean(false)] let ... = a | test.rs:149:13:149:13 | 0 | false |
| test.rs:146:12:146:26 | [boolean(true)] let ... = a | test.rs:147:13:147:13 | n | true |
| test.rs:146:16:146:22 | Some(...) | test.rs:146:12:146:26 | [boolean(false)] let ... = a | no-match |
| test.rs:146:16:146:22 | Some(...) | test.rs:146:21:146:21 | n | match |
| test.rs:146:16:146:22 | Some(...) | test.rs:149:13:149:13 | 0 | no-match |
| test.rs:146:21:146:21 | n | test.rs:146:12:146:26 | [boolean(true)] let ... = a | match |
| test.rs:146:21:146:21 | n | test.rs:146:21:146:21 | n | |
| test.rs:146:21:146:21 | n | test.rs:147:13:147:13 | n | match |
| test.rs:146:26:146:26 | a | test.rs:146:16:146:22 | Some(...) | |
| test.rs:146:28:148:9 | { ... } | test.rs:146:9:150:9 | if ... {...} else {...} | |
| test.rs:147:13:147:13 | n | test.rs:146:28:148:9 | { ... } | |
@@ -327,13 +329,14 @@ edges
| test.rs:153:20:153:20 | a | test.rs:153:20:153:33 | ...: Option::<...> | match |
| test.rs:153:20:153:33 | ...: Option::<...> | test.rs:154:9:156:9 | ExprStmt | |
| test.rs:153:43:158:5 | { ... } | test.rs:153:5:158:5 | exit fn test_if_let (normal) | |
| test.rs:154:9:156:9 | ExprStmt | test.rs:154:12:154:26 | let ... = a | |
| test.rs:154:9:156:9 | ExprStmt | test.rs:154:26:154:26 | a | |
| test.rs:154:9:156:9 | if ... {...} | test.rs:157:9:157:9 | 0 | |
| test.rs:154:12:154:26 | let ... = a | test.rs:154:26:154:26 | a | |
| test.rs:154:16:154:22 | Some(...) | test.rs:154:9:156:9 | if ... {...} | no-match |
| test.rs:154:12:154:26 | [boolean(false)] let ... = a | test.rs:154:9:156:9 | if ... {...} | false |
| test.rs:154:12:154:26 | [boolean(true)] let ... = a | test.rs:155:13:155:21 | ExprStmt | true |
| test.rs:154:16:154:22 | Some(...) | test.rs:154:12:154:26 | [boolean(false)] let ... = a | no-match |
| test.rs:154:16:154:22 | Some(...) | test.rs:154:21:154:21 | n | match |
| test.rs:154:21:154:21 | n | test.rs:154:12:154:26 | [boolean(true)] let ... = a | match |
| test.rs:154:21:154:21 | n | test.rs:154:21:154:21 | n | |
| test.rs:154:21:154:21 | n | test.rs:155:13:155:21 | ExprStmt | match |
| test.rs:154:26:154:26 | a | test.rs:154:16:154:22 | Some(...) | |
| test.rs:155:13:155:20 | return n | test.rs:153:5:158:5 | exit fn test_if_let (normal) | return |
| test.rs:155:13:155:21 | ExprStmt | test.rs:155:20:155:20 | n | |

View File

@@ -1,2 +1,2 @@
multipleCallTargets
| main.rs:445:18:445:24 | n.len() |
| main.rs:471:18:471:24 | n.len() |

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,4 @@
#![feature(let_chains)]
// Tests for intraprocedural data flow.
fn source(i: i64) -> i64 {
@@ -21,6 +22,19 @@ fn direct() {
fn variable_usage() {
let s = source(2);
sink(s); // $ hasValueFlow=2
if let x = s {
sink(x); // $ hasValueFlow=2
};
if let x = s
&& {
sink(x); // $ hasValueFlow=2
true
}
{
sink(x); // $ hasValueFlow=2
};
}
fn if_expression(cond: bool) {
@@ -236,6 +250,18 @@ fn option_pattern_match_unqualified() {
}
}
fn option_chained_let() {
let s1 = Some(source(45));
if let Some(n) = s1
&& {
sink(n); // $ hasValueFlow=45
true
}
{
sink(n); // $ hasValueFlow=45
}
}
fn option_unwrap() {
let s1 = Some(source(19));
sink(s1.unwrap()); // $ hasValueFlow=19
@@ -558,6 +584,7 @@ fn main() {
struct_nested_match();
option_pattern_match_qualified();
option_pattern_match_unqualified();
option_chained_let();
option_unwrap();
option_unwrap_or();
option_questionmark();

View File

@@ -0,0 +1 @@
qltest_use_nightly: true

View File

@@ -80,7 +80,7 @@ async fn test_reqwest() -> Result<(), reqwest::Error> {
let mut request1 = reqwest::get("example.com").await?; // $ Alert[rust/summary/taint-sources]
sink(request1.chunk().await?.unwrap()); // $ hasTaintFlow="example.com"
while let Some(chunk) = request1.chunk().await? {
sink(chunk); // $ MISSING: hasTaintFlow="example.com"
sink(chunk); // $ hasTaintFlow="example.com"
}
Ok(())
@@ -273,7 +273,7 @@ fn test_io_stdin() -> std::io::Result<()> {
let mut reader_split = std::io::BufReader::new(std::io::stdin()).split(b','); // $ Alert[rust/summary/taint-sources]
sink(reader_split.next().unwrap().unwrap()); // $ hasTaintFlow
while let Some(chunk) = reader_split.next() {
sink(chunk.unwrap()); // $ MISSING: hasTaintFlow
sink(chunk.unwrap()); // $ hasTaintFlow
}
}

View File

@@ -62,7 +62,7 @@ async fn test_futures_rustls_futures_io() -> io::Result<()> {
let buffer = pinned.poll_fill_buf(&mut cx);
if let Poll::Ready(Ok(buf)) = buffer {
sink(&buffer); // $ hasTaintFlow=url
sink(buf); // $ MISSING: hasTaintFlow=url
sink(buf); // $ hasTaintFlow=url
}
// using the `AsyncBufRead` trait (alternative syntax)
@@ -116,7 +116,7 @@ async fn test_futures_rustls_futures_io() -> io::Result<()> {
let buffer = pinned.poll_fill_buf(&mut cx);
sink(&buffer); // $ hasTaintFlow=url
if let Poll::Ready(Ok(buf)) = buffer {
sink(buf); // $ MISSING: hasTaintFlow=url
sink(buf); // $ hasTaintFlow=url
}
}

View File

@@ -1,3 +1,3 @@
multipleCallTargets
| main.rs:87:19:87:40 | ...::from(...) |
| main.rs:106:19:106:40 | ...::from(...) |
| main.rs:89:19:89:40 | ...::from(...) |
| main.rs:111:19:111:40 | ...::from(...) |

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,5 @@
#![feature(let_chains)]
#![feature(if_let_guard)]
use std::ops::AddAssign;
fn print_str(s: &str) // s
@@ -93,13 +95,16 @@ fn let_pattern3() {
}
fn let_pattern4() {
let Some(x5): Option<&str> // x5
= Some("x5")
let x = Some("x5"); // x1
let Some(x): Option<&str> // x2
= x // $ read_access=x1
else {
let x = // x3
x; // $ read_access=x1
print_str(x.unwrap()); // $ read_access=x3
todo!()
};
print_str(x5); // $ read_access=x5
print_str(x); // $ read_access=x2
}
fn let_pattern5() {
@@ -269,6 +274,90 @@ fn match_pattern9() {
}
}
#[rustfmt::skip]
fn match_pattern10() {
let x= Some(42); // x1
if let Some(x) // x2
= x // $ read_access=x1
&&
x > 0 // $ read_access=x2
{
print_i64(x); // $ read_access=x2
} else {
let x = // x3
x; // $ read_access=x1
print_i64(x.unwrap()); // $ read_access=x3
}
}
#[rustfmt::skip]
fn match_pattern11() {
let x = Some(42); // x1
if let Some(x) // x2
= x // $ read_access=x1
&&
let Some(x) // x3
= Some(x) // $ read_access=x2
&&
x > 0 // $ read_access=x3
{
print_i64(x); // $ read_access=x3
} else {
let x = // x4
x; // $ read_access=x1
print_i64(x.unwrap()); // $ read_access=x4
}
}
#[rustfmt::skip]
fn match_pattern12() {
let x = Some(42); // x1
while let Some(x) // x2
= x // $ read_access=x1
&&
let Some(x) // x3
= Some(x) // $ read_access=x2
&&
x > 0 // $ read_access=x3
{
print_i64(x); // $ read_access=x3
break;
}
print_i64(x.unwrap()); // $ read_access=x1
}
#[rustfmt::skip]
fn match_pattern13() {
let x = Some(42); // x1
match x { // $ read_access=x1
Some(x) // x2
if let x // x3
= x // $ read_access=x2
&& x > 0 => (), // $ read_access=x3
_ => ()
}
print_i64(x.unwrap()); // $ read_access=x1
}
#[rustfmt::skip]
fn match_pattern14() {
let x = Ok(42); // x1
if let Err(x) // x2
= x // $ read_access=x1
{
print_i64(x); // $ read_access=x2
}
else if let Ok(x) // x3
= x // $ read_access=x1
{
print_i64(x); // $ read_access=x3
} else {
print_i64(x.unwrap()); // $ read_access=x1
}
}
fn param_pattern1(
a8: &str, // a8
(
@@ -417,6 +506,7 @@ fn mutate_arg() {
let y = // y
mutate_param(&mut x); // $ access=x
*y = 10; // $ read_access=y
// prints 10, not 4
print_i64(x); // $ read_access=x
@@ -428,6 +518,7 @@ fn mutate_arg() {
w, // $ read_access=w
);
**w = 11; // $ read_access=w
// prints 11, not 8
print_i64(z); // $ read_access=z
}
@@ -442,6 +533,7 @@ fn alias() {
fn capture_immut() {
let x = 100; // x
// Captures immutable value by immutable reference
let cap = || {
print_i64(x); // $ read_access=x
@@ -452,6 +544,7 @@ fn capture_immut() {
fn capture_mut() {
let mut x = 1; // x
// Captures mutable value by immutable reference
let closure1 = || {
print_i64(x); // $ read_access=x
@@ -460,6 +553,7 @@ fn capture_mut() {
print_i64(x); // $ read_access=x
let mut y = 2; // y
// Captures mutable value by mutable reference
let mut closure2 = || {
y = 3; // $ write_access=y
@@ -468,6 +562,7 @@ fn capture_mut() {
print_i64(y); // $ read_access=y
let mut z = 2; // z
// Captures mutable value by mutable reference and calls mutating method
let mut closure3 = || {
z.add_assign(1); // $ read_access=z
@@ -586,6 +681,7 @@ impl MyStruct {
fn ref_methodcall_receiver() {
let mut a = MyStruct { val: 1 }; // a
a.bar(); // $ read_access=a
// prints 3, not 1
print_i64(a.val); // $ read_access=a
}
@@ -609,6 +705,7 @@ fn macro_invocation() {
let_in_macro!(37); // $ read_access=var_in_macro
print_i64(var_from_macro); // $ read_access=var_from_macro1
let var_in_macro = 33; // var_in_macro1
// Our analysis does not currently respect the hygiene rules of Rust macros
// (https://veykril.github.io/tlborm/decl-macros/minutiae/hygiene.html), because
// all we have access to is the expanded AST
@@ -653,6 +750,11 @@ fn main() {
match_pattern7();
match_pattern8();
match_pattern9();
match_pattern10();
match_pattern11();
match_pattern12();
match_pattern13();
match_pattern14();
param_pattern1("a", ("b", "c"));
param_pattern2(Either::Left(45));
destruct_assignment();

View File

@@ -0,0 +1 @@
qltest_use_nightly: true

File diff suppressed because it is too large Load Diff

View File

@@ -11,6 +11,7 @@
| lifetime.rs:172:13:172:15 | ptr | lifetime.rs:187:12:187:21 | &my_local1 | lifetime.rs:172:13:172:15 | ptr | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:186:6:186:14 | my_local1 | my_local1 |
| lifetime.rs:255:14:255:17 | prev | lifetime.rs:251:10:251:19 | &my_local2 | lifetime.rs:255:14:255:17 | prev | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:242:7:242:15 | my_local2 | my_local2 |
| lifetime.rs:310:31:310:32 | e1 | lifetime.rs:272:30:272:32 | &e1 | lifetime.rs:310:31:310:32 | e1 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:271:6:271:7 | e1 | e1 |
| lifetime.rs:314:23:314:24 | p2 | lifetime.rs:279:28:279:30 | &v2 | lifetime.rs:314:23:314:24 | p2 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:278:6:278:7 | v2 | v2 |
| lifetime.rs:317:13:317:18 | result | lifetime.rs:289:25:289:26 | &x | lifetime.rs:317:13:317:18 | result | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:289:17:289:17 | x | x |
| lifetime.rs:411:16:411:17 | p1 | lifetime.rs:383:31:383:37 | &raw mut my_pair | lifetime.rs:411:16:411:17 | p1 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:382:11:382:17 | my_pair | my_pair |
| lifetime.rs:416:16:416:17 | p1 | lifetime.rs:383:31:383:37 | &raw mut my_pair | lifetime.rs:416:16:416:17 | p1 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:382:11:382:17 | my_pair | my_pair |
@@ -123,14 +124,22 @@ edges
| lifetime.rs:270:47:275:1 | { ... } | lifetime.rs:303:11:303:31 | get_pointer_to_enum(...) | provenance | |
| lifetime.rs:272:6:272:11 | result | lifetime.rs:270:47:275:1 | { ... } | provenance | |
| lifetime.rs:272:30:272:32 | &e1 | lifetime.rs:272:6:272:11 | result | provenance | |
| lifetime.rs:277:41:282:1 | { ... } [Pointer] | lifetime.rs:304:11:304:31 | get_pointer_in_enum(...) [Pointer] | provenance | |
| lifetime.rs:279:6:279:7 | e2 [Pointer] | lifetime.rs:277:41:282:1 | { ... } [Pointer] | provenance | |
| lifetime.rs:279:11:279:31 | ...::Pointer(...) [Pointer] | lifetime.rs:279:6:279:7 | e2 [Pointer] | provenance | |
| lifetime.rs:279:28:279:30 | &v2 | lifetime.rs:279:11:279:31 | ...::Pointer(...) [Pointer] | provenance | |
| lifetime.rs:284:46:300:1 | { ... } | lifetime.rs:305:15:305:37 | get_pointer_from_enum(...) | provenance | |
| lifetime.rs:288:2:288:7 | result | lifetime.rs:284:46:300:1 | { ... } | provenance | |
| lifetime.rs:288:2:288:7 | result | lifetime.rs:295:13:295:18 | result | provenance | |
| lifetime.rs:289:25:289:26 | &x | lifetime.rs:288:2:288:7 | result | provenance | |
| lifetime.rs:303:6:303:7 | e1 | lifetime.rs:310:31:310:32 | e1 | provenance | |
| lifetime.rs:303:11:303:31 | get_pointer_to_enum(...) | lifetime.rs:303:6:303:7 | e1 | provenance | |
| lifetime.rs:304:6:304:7 | e2 [Pointer] | lifetime.rs:313:10:313:29 | ...::Pointer(...) [Pointer] | provenance | |
| lifetime.rs:304:11:304:31 | get_pointer_in_enum(...) [Pointer] | lifetime.rs:304:6:304:7 | e2 [Pointer] | provenance | |
| lifetime.rs:305:6:305:11 | result | lifetime.rs:317:13:317:18 | result | provenance | |
| lifetime.rs:305:15:305:37 | get_pointer_from_enum(...) | lifetime.rs:305:6:305:11 | result | provenance | |
| lifetime.rs:313:10:313:29 | ...::Pointer(...) [Pointer] | lifetime.rs:313:27:313:28 | p2 | provenance | |
| lifetime.rs:313:27:313:28 | p2 | lifetime.rs:314:23:314:24 | p2 | provenance | |
| lifetime.rs:383:3:383:4 | p1 | lifetime.rs:388:15:388:16 | p1 | provenance | |
| lifetime.rs:383:3:383:4 | p1 | lifetime.rs:391:15:391:16 | p1 | provenance | |
| lifetime.rs:383:3:383:4 | p1 | lifetime.rs:399:6:399:7 | p1 | provenance | |
@@ -323,15 +332,24 @@ nodes
| lifetime.rs:270:47:275:1 | { ... } | semmle.label | { ... } |
| lifetime.rs:272:6:272:11 | result | semmle.label | result |
| lifetime.rs:272:30:272:32 | &e1 | semmle.label | &e1 |
| lifetime.rs:277:41:282:1 | { ... } [Pointer] | semmle.label | { ... } [Pointer] |
| lifetime.rs:279:6:279:7 | e2 [Pointer] | semmle.label | e2 [Pointer] |
| lifetime.rs:279:11:279:31 | ...::Pointer(...) [Pointer] | semmle.label | ...::Pointer(...) [Pointer] |
| lifetime.rs:279:28:279:30 | &v2 | semmle.label | &v2 |
| lifetime.rs:284:46:300:1 | { ... } | semmle.label | { ... } |
| lifetime.rs:288:2:288:7 | result | semmle.label | result |
| lifetime.rs:289:25:289:26 | &x | semmle.label | &x |
| lifetime.rs:295:13:295:18 | result | semmle.label | result |
| lifetime.rs:303:6:303:7 | e1 | semmle.label | e1 |
| lifetime.rs:303:11:303:31 | get_pointer_to_enum(...) | semmle.label | get_pointer_to_enum(...) |
| lifetime.rs:304:6:304:7 | e2 [Pointer] | semmle.label | e2 [Pointer] |
| lifetime.rs:304:11:304:31 | get_pointer_in_enum(...) [Pointer] | semmle.label | get_pointer_in_enum(...) [Pointer] |
| lifetime.rs:305:6:305:11 | result | semmle.label | result |
| lifetime.rs:305:15:305:37 | get_pointer_from_enum(...) | semmle.label | get_pointer_from_enum(...) |
| lifetime.rs:310:31:310:32 | e1 | semmle.label | e1 |
| lifetime.rs:313:10:313:29 | ...::Pointer(...) [Pointer] | semmle.label | ...::Pointer(...) [Pointer] |
| lifetime.rs:313:27:313:28 | p2 | semmle.label | p2 |
| lifetime.rs:314:23:314:24 | p2 | semmle.label | p2 |
| lifetime.rs:317:13:317:18 | result | semmle.label | result |
| lifetime.rs:383:3:383:4 | p1 | semmle.label | p1 |
| lifetime.rs:383:31:383:37 | &raw mut my_pair | semmle.label | &raw mut my_pair |

View File

@@ -276,7 +276,7 @@ pub fn get_pointer_to_enum() -> *const MyEnum {
pub fn get_pointer_in_enum() -> MyEnum2 {
let v2 = 2;
let e2 = MyEnum2::Pointer(&v2); // $ MISSING: Source[rust/access-after-lifetime-ended]=v2
let e2 = MyEnum2::Pointer(&v2); // $ Source[rust/access-after-lifetime-ended]=v2
e2
} // (v2 goes out of scope, so the contained pointer is dangling)
@@ -311,7 +311,7 @@ pub fn test_enums() {
println!(" v1 = {v1} (!)"); // corrupt in practice
}
if let MyEnum2::Pointer(p2) = e2 {
let v2 = unsafe { *p2 }; // $ MISSING: Alert[rust/access-after-lifetime-ended]=v2
let v2 = unsafe { *p2 }; // $ Alert[rust/access-after-lifetime-ended]=v2
println!(" v2 = {v2} (!)"); // corrupt in practice
}
let v3 = *result; // $ Alert[rust/access-after-lifetime-ended]=match_x

View File

@@ -1,6 +1,6 @@
multipleCallTargets
| main.rs:13:13:13:29 | ...::from(...) |
| main.rs:14:13:14:29 | ...::from(...) |
| main.rs:15:13:15:29 | ...::from(...) |
| unreachable.rs:165:20:165:42 | ...::from(...) |
| unreachable.rs:171:9:171:17 | ...::from(...) |
| unreachable.rs:177:17:177:25 | ...::from(...) |

View File

@@ -1,21 +1,22 @@
| main.rs:10:9:10:9 | a | Variable $@ is assigned a value that is never used. | main.rs:10:9:10:9 | a | a |
| main.rs:13:9:13:9 | d | Variable $@ is assigned a value that is never used. | main.rs:13:9:13:9 | d | d |
| main.rs:39:5:39:5 | b | Variable $@ is assigned a value that is never used. | main.rs:30:9:30:9 | b | b |
| main.rs:41:5:41:5 | c | Variable $@ is assigned a value that is never used. | main.rs:31:13:31:13 | c | c |
| main.rs:44:5:44:5 | c | Variable $@ is assigned a value that is never used. | main.rs:31:13:31:13 | c | c |
| main.rs:48:9:48:9 | d | Variable $@ is assigned a value that is never used. | main.rs:32:13:32:13 | d | d |
| main.rs:54:5:54:5 | e | Variable $@ is assigned a value that is never used. | main.rs:33:13:33:13 | e | e |
| main.rs:65:5:65:5 | f | Variable $@ is assigned a value that is never used. | main.rs:34:13:34:13 | f | f |
| main.rs:67:5:67:5 | f | Variable $@ is assigned a value that is never used. | main.rs:34:13:34:13 | f | f |
| main.rs:69:5:69:5 | g | Variable $@ is assigned a value that is never used. | main.rs:35:9:35:9 | g | g |
| main.rs:95:9:95:9 | a | Variable $@ is assigned a value that is never used. | main.rs:95:9:95:9 | a | a |
| main.rs:116:9:116:10 | is | Variable $@ is assigned a value that is never used. | main.rs:116:9:116:10 | is | is |
| main.rs:139:13:139:17 | total | Variable $@ is assigned a value that is never used. | main.rs:139:13:139:17 | total | total |
| main.rs:284:13:284:17 | total | Variable $@ is assigned a value that is never used. | main.rs:252:13:252:17 | total | total |
| main.rs:377:9:377:9 | x | Variable $@ is assigned a value that is never used. | main.rs:377:9:377:9 | x | x |
| main.rs:385:17:385:17 | x | Variable $@ is assigned a value that is never used. | main.rs:385:17:385:17 | x | x |
| main.rs:531:9:531:20 | var_in_macro | Variable $@ is assigned a value that is never used. | main.rs:531:9:531:20 | var_in_macro | var_in_macro |
| main.rs:540:9:540:9 | c | Variable $@ is assigned a value that is never used. | main.rs:540:9:540:9 | c | c |
| main.rs:11:9:11:9 | a | Variable $@ is assigned a value that is never used. | main.rs:11:9:11:9 | a | a |
| main.rs:14:9:14:9 | d | Variable $@ is assigned a value that is never used. | main.rs:14:9:14:9 | d | d |
| main.rs:40:5:40:5 | b | Variable $@ is assigned a value that is never used. | main.rs:31:9:31:9 | b | b |
| main.rs:42:5:42:5 | c | Variable $@ is assigned a value that is never used. | main.rs:32:13:32:13 | c | c |
| main.rs:45:5:45:5 | c | Variable $@ is assigned a value that is never used. | main.rs:32:13:32:13 | c | c |
| main.rs:49:9:49:9 | d | Variable $@ is assigned a value that is never used. | main.rs:33:13:33:13 | d | d |
| main.rs:55:5:55:5 | e | Variable $@ is assigned a value that is never used. | main.rs:34:13:34:13 | e | e |
| main.rs:66:5:66:5 | f | Variable $@ is assigned a value that is never used. | main.rs:35:13:35:13 | f | f |
| main.rs:68:5:68:5 | f | Variable $@ is assigned a value that is never used. | main.rs:35:13:35:13 | f | f |
| main.rs:70:5:70:5 | g | Variable $@ is assigned a value that is never used. | main.rs:36:9:36:9 | g | g |
| main.rs:96:9:96:9 | a | Variable $@ is assigned a value that is never used. | main.rs:96:9:96:9 | a | a |
| main.rs:117:9:117:10 | is | Variable $@ is assigned a value that is never used. | main.rs:117:9:117:10 | is | is |
| main.rs:140:13:140:17 | total | Variable $@ is assigned a value that is never used. | main.rs:140:13:140:17 | total | total |
| main.rs:285:13:285:17 | total | Variable $@ is assigned a value that is never used. | main.rs:253:13:253:17 | total | total |
| main.rs:322:12:322:12 | j | Variable $@ is assigned a value that is never used. | main.rs:322:12:322:12 | j | j |
| main.rs:382:9:382:9 | x | Variable $@ is assigned a value that is never used. | main.rs:382:9:382:9 | x | x |
| main.rs:390:17:390:17 | x | Variable $@ is assigned a value that is never used. | main.rs:390:17:390:17 | x | x |
| main.rs:536:9:536:20 | var_in_macro | Variable $@ is assigned a value that is never used. | main.rs:536:9:536:20 | var_in_macro | var_in_macro |
| main.rs:545:9:545:9 | c | Variable $@ is assigned a value that is never used. | main.rs:545:9:545:9 | c | c |
| more.rs:44:9:44:14 | a_ptr4 | Variable $@ is assigned a value that is never used. | more.rs:44:9:44:14 | a_ptr4 | a_ptr4 |
| more.rs:59:9:59:13 | d_ptr | Variable $@ is assigned a value that is never used. | more.rs:59:9:59:13 | d_ptr | d_ptr |
| more.rs:65:13:65:17 | f_ptr | Variable $@ is assigned a value that is never used. | more.rs:65:13:65:17 | f_ptr | f_ptr |

View File

@@ -1,22 +1,21 @@
| main.rs:29:9:29:9 | a | Variable 'a' is not used. |
| main.rs:98:13:98:13 | d | Variable 'd' is not used. |
| main.rs:147:5:147:5 | y | Variable 'y' is not used. |
| main.rs:174:9:174:9 | x | Variable 'x' is not used. |
| main.rs:254:17:254:17 | a | Variable 'a' is not used. |
| main.rs:262:20:262:22 | val | Variable 'val' is not used. |
| main.rs:276:14:276:16 | val | Variable 'val' is not used. |
| main.rs:291:22:291:24 | val | Variable 'val' is not used. |
| main.rs:298:24:298:26 | val | Variable 'val' is not used. |
| main.rs:306:13:306:15 | num | Variable 'num' is not used. |
| main.rs:321:12:321:12 | j | Variable 'j' is not used. |
| main.rs:341:25:341:25 | y | Variable 'y' is not used. |
| main.rs:344:28:344:28 | a | Variable 'a' is not used. |
| main.rs:347:9:347:9 | p | Variable 'p' is not used. |
| main.rs:365:9:365:13 | right | Variable 'right' is not used. |
| main.rs:371:9:371:14 | right2 | Variable 'right2' is not used. |
| main.rs:378:13:378:13 | y | Variable 'y' is not used. |
| main.rs:386:21:386:21 | y | Variable 'y' is not used. |
| main.rs:431:26:431:28 | val | Variable 'val' is not used. |
| main.rs:434:21:434:23 | acc | Variable 'acc' is not used. |
| main.rs:455:9:455:14 | unused | Variable 'unused' is not used. |
| main.rs:30:9:30:9 | a | Variable 'a' is not used. |
| main.rs:99:13:99:13 | d | Variable 'd' is not used. |
| main.rs:148:5:148:5 | y | Variable 'y' is not used. |
| main.rs:175:9:175:9 | x | Variable 'x' is not used. |
| main.rs:255:17:255:17 | a | Variable 'a' is not used. |
| main.rs:263:20:263:22 | val | Variable 'val' is not used. |
| main.rs:277:14:277:16 | val | Variable 'val' is not used. |
| main.rs:292:22:292:24 | val | Variable 'val' is not used. |
| main.rs:299:24:299:26 | val | Variable 'val' is not used. |
| main.rs:307:13:307:15 | num | Variable 'num' is not used. |
| main.rs:342:25:342:25 | y | Variable 'y' is not used. |
| main.rs:345:28:345:28 | a | Variable 'a' is not used. |
| main.rs:348:9:348:9 | p | Variable 'p' is not used. |
| main.rs:366:9:366:13 | right | Variable 'right' is not used. |
| main.rs:372:9:372:14 | right2 | Variable 'right2' is not used. |
| main.rs:383:13:383:13 | y | Variable 'y' is not used. |
| main.rs:391:21:391:21 | y | Variable 'y' is not used. |
| main.rs:436:26:436:28 | val | Variable 'val' is not used. |
| main.rs:439:21:439:23 | acc | Variable 'acc' is not used. |
| main.rs:460:9:460:14 | unused | Variable 'unused' is not used. |
| more.rs:24:9:24:11 | val | Variable 'val' is not used. |

View File

@@ -1,3 +1,4 @@
#![feature(let_chains)]
mod more;
mod unreachable;
@@ -318,7 +319,7 @@ fn if_lets_matches() {
No => {}
}
if let j = Yes { // $ Alert[rust/unused-variable]
if let j = Yes { // $ Alert[rust/unused-value]
}
if let k = Yes {
@@ -371,6 +372,10 @@ fn if_lets_matches() {
right2) = // $ MISSING: Alert[rust/unused-value] $ SPURIOUS: Alert[rust/unused-variable]
pair;
_ = left2;
if let Some(m) = Some(10)
&& m > 0
{}
}
fn shadowing() -> i32 {

View File

@@ -0,0 +1 @@
qltest_use_nightly: true

View File

@@ -160,7 +160,7 @@ impl<T> MyOption<T> {
}
// summary=<test::option::MyOption>::inspect;Argument[self];ReturnValue;value;dfc-generated
// MISSING: Due to `ref` pattern.
// summary=<test::option::MyOption>::inspect;Argument[self].Field[test::option::MyOption::MySome(0)];Argument[0].Parameter[0].Reference;value;dfc-generated
pub fn inspect<F: FnOnce(&T)>(self, f: F) -> Self {
if let MySome(ref x) = self {
f(x);
@@ -253,7 +253,8 @@ impl<T> MyOption<T> {
}
}
// MISSING: Reference passed to predicate
// summary=<test::option::MyOption>::filter;Argument[self].Field[test::option::MyOption::MySome(0)];Argument[0].Parameter[0].Reference;value;dfc-generated
// summary=<test::option::MyOption>::filter;Argument[self].Field[test::option::MyOption::MySome(0)];ReturnValue.Field[test::option::MyOption::MySome(0)];value;dfc-generated
pub fn filter<P>(self, predicate: P) -> Self
where
P: FnOnce(&T) -> bool,