Rust: Instantiate variable capture library for data flow

This commit is contained in:
Simon Friis Vindum
2024-12-11 15:01:47 +01:00
parent e8357a648d
commit 94b037fad1
6 changed files with 387 additions and 35 deletions

View File

@@ -210,23 +210,22 @@ module Ssa {
final CfgNode getWriteAccess() { result = write }
/**
* Holds if this SSA definition assigns `value` to the underlying variable.
* Holds if this SSA definition assigns `value` to the underlying
* variable.
*
* This is either a direct assignment, `x = value`, or an assignment via
* simple pattern matching
*
* ```rb
* case value
* in Foo => x then ...
* in y => then ...
* end
* ```
* This is either the value in a direct assignment, `x = value`, or in a
* `let` statement, `let x = value`. Note that patterns on the rhs. are
* currently not supported.
*/
predicate assigns(ExprCfgNode value) {
exists(AssignmentExprCfgNode ae, BasicBlock bb, int i |
this.definesAt(_, bb, i) and
ae.getLhs() = bb.getNode(i) and
value = ae.getRhs()
exists(AssignmentExprCfgNode ae |
ae.getLhs() = write and
ae.getRhs() = value
)
or
exists(LetStmtCfgNode ls |
ls.getPat() = write and
ls.getInitializer() = value
)
}
@@ -338,4 +337,37 @@ module Ssa {
override Location getLocation() { result = this.getBasicBlock().getLocation() }
}
/**
* An SSA definition inserted at a call that may update the value of a captured
* variable. For example, in
*
* ```rb
* fn capture_mut() {
* let mut y = 0;
* (0..5).for_each(|| {
* y += x
* });
* y
* }
* ```
*
* a definition for `y` is inserted at the call to `for_each`.
*/
class CapturedCallDefinition extends Definition, SsaImpl::UncertainWriteDefinition {
CapturedCallDefinition() {
exists(Variable v, BasicBlock bb, int i |
this.definesAt(v, bb, i) and
SsaImpl::capturedCallWrite(_, bb, i, v)
)
}
/**
* Gets the immediately preceding definition. Since this update is uncertain,
* the value from the preceding definition might still be valid.
*/
final Definition getPriorDefinition() { result = SsaImpl::uncertainWriteDefinitionInput(this) }
override string toString() { result = "<captured exit> " + this.getSourceVariable() }
}
}

View File

@@ -101,11 +101,19 @@ final class ParameterPosition extends TParameterPosition {
/** Holds if this position represents the `self` position. */
predicate isSelf() { this = TSelfParameterPosition() }
/**
* Holds if this position represents a reference to a lambda itself. Only
* used for tracking flow through captured variables.
*/
predicate isLambdaSelf() { this = TLambdaSelfParameterPosition() }
/** Gets a textual representation of this position. */
string toString() {
result = this.getPosition().toString()
or
result = "self" and this.isSelf()
or
result = "lambda self" and this.isLambdaSelf()
}
ParamBase getParameterIn(ParamList ps) {
@@ -264,6 +272,26 @@ module Node {
}
}
/**
* The run-time representation of a closure itself at function entry, viewed
* as a node in a data flow graph.
*/
final class ClosureParameterNode extends ParameterNode, TLambdaSelfReferenceNode {
private CfgScope cfgScope;
ClosureParameterNode() { this = TLambdaSelfReferenceNode(cfgScope) }
final override CfgScope getCfgScope() { result = cfgScope }
override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
cfgScope = c.asCfgScope() and pos.isLambdaSelf()
}
override Location getLocation() { result = cfgScope.getLocation() }
override string toString() { result = "lambda self in " + cfgScope }
}
abstract class ArgumentNode extends Node {
abstract predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos);
}
@@ -292,6 +320,21 @@ module Node {
}
}
/**
* A data flow node that represents the run-time representation of a closure
* passed into the closure body at an invocation.
*/
final class ClosureArgumentNode extends ArgumentNode, ExprNode {
private CallExprCfgNode call_;
ClosureArgumentNode() { lambdaCallExpr(call_, _, this.asExpr()) }
override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) {
call.asCallExprCfgNode() = call_ and
pos.isLambdaSelf()
}
}
/** An SSA node. */
class SsaNode extends Node, TSsaNode {
SsaImpl::DataFlowIntegration::SsaNode node;
@@ -402,6 +445,37 @@ module Node {
final override string toString() { result = PostUpdateNode.super.toString() }
}
private class CapturePostUpdateNode extends PostUpdateNode, CaptureNode {
private CaptureNode pre;
CapturePostUpdateNode() {
VariableCapture::Flow::capturePostUpdateNode(this.getSynthesizedCaptureNode(),
pre.getSynthesizedCaptureNode())
}
override Node getPreUpdateNode() { result = pre }
final override string toString() { result = PostUpdateNode.super.toString() }
}
/**
* A synthesized data flow node representing a closure object that tracks
* captured variables.
*/
class CaptureNode extends Node, TCaptureNode {
private VariableCapture::Flow::SynthesizedCaptureNode cn;
CaptureNode() { this = TCaptureNode(cn) }
VariableCapture::Flow::SynthesizedCaptureNode getSynthesizedCaptureNode() { result = cn }
override CfgScope getCfgScope() { result = cn.getEnclosingCallable() }
override Location getLocation() { result = cn.getLocation() }
override string toString() { result = cn.toString() }
}
final class CastNode = NaNode;
}
@@ -625,6 +699,18 @@ private class StructFieldContent extends Content, TStructFieldContent {
override string toString() { result = s.toString() + "." + field_.toString() }
}
/** A captured variable. */
private class CapturedVariableContent extends Content, TCapturedVariableContent {
private Variable v;
CapturedVariableContent() { this = TCapturedVariableContent(v) }
/** Gets the captured variable. */
Variable getVariable() { result = v }
override string toString() { result = "captured " + v }
}
/**
* An element in an array.
*/
@@ -681,6 +767,26 @@ final class SingletonContentSet extends ContentSet, TSingletonContentSet {
override Content getAReadContent() { result = c }
}
class LambdaCallKind = Unit;
/** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */
private predicate lambdaCreationExpr(Expr creation, LambdaCallKind kind) {
creation instanceof ClosureExpr and exists(kind)
}
/**
* Holds if `call` is a lambda call of kind `kind` where `receiver` is the
* invoked expression.
*/
predicate lambdaCallExpr(CallExprCfgNode call, LambdaCallKind kind, ExprCfgNode receiver) {
receiver = call.getFunction() and
// All calls to complex expressions and local variable accesses are lambda call.
exists(Expr f | f = receiver.getExpr() |
f instanceof PathExpr implies f = any(Variable v).getAnAccess()
) and
exists(kind)
}
// Defines a set of aliases needed for the `RustDataFlow` module
private module Aliases {
class DataFlowCallableAlias = DataFlowCallable;
@@ -694,6 +800,8 @@ private module Aliases {
class ContentAlias = Content;
class ContentSetAlias = ContentSet;
class LambdaCallKindAlias = LambdaCallKind;
}
module RustDataFlow implements InputSig<Location> {
@@ -735,6 +843,12 @@ module RustDataFlow implements InputSig<Location> {
node instanceof Node::SsaNode
or
node instanceof Node::FlowSummaryNode
or
node instanceof Node::CaptureNode
or
node instanceof Node::ClosureParameterNode
or
node instanceof Node::ClosureArgumentNode
}
class DataFlowExpr = ExprCfgNode;
@@ -775,6 +889,8 @@ module RustDataFlow implements InputSig<Location> {
class ContentSet = ContentSetAlias;
class LambdaCallKind = LambdaCallKindAlias;
predicate forceHighPrecision(Content c) { none() }
final class ContentApprox = Content; // TODO: Implement if needed
@@ -799,12 +915,17 @@ module RustDataFlow implements InputSig<Location> {
(
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
or
exists(boolean isUseStep | SsaFlow::localFlowStep(_, nodeFrom, nodeTo, isUseStep) |
exists(SsaImpl::DefinitionExt def, boolean isUseStep |
SsaFlow::localFlowStep(def, nodeFrom, nodeTo, isUseStep) and
not def instanceof VariableCapture::CapturedSsaDefinitionExt
|
isUseStep = false
or
isUseStep = true and
not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(nodeFrom, _)
)
or
VariableCapture::localFlowStep(nodeFrom, nodeTo)
) and
model = ""
or
@@ -914,6 +1035,8 @@ module RustDataFlow implements InputSig<Location> {
c.(VariantPositionContent).getVariantCanonicalPath(0).getExtendedCanonicalPath() =
["crate::option::Option::Some", "crate::result::Result::Ok"]
)
or
VariableCapture::readStep(node1, c, node2)
)
or
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(Node::FlowSummaryNode).getSummaryNode(),
@@ -995,6 +1118,8 @@ module RustDataFlow implements InputSig<Location> {
node1.asExpr() = assignment.getRhs() and
node2.(PostUpdateNode).getPreUpdateNode().asExpr() = index.getBase()
)
or
VariableCapture::storeStep(node1, c, node2)
)
or
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(Node::FlowSummaryNode).getSummaryNode(),
@@ -1011,6 +1136,8 @@ module RustDataFlow implements InputSig<Location> {
or
FlowSummaryImpl::Private::Steps::summaryClearsContent(n.(Node::FlowSummaryNode).getSummaryNode(),
cs)
or
VariableCapture::clearsContent(n, cs.(SingletonContentSet).getContent())
}
/**
@@ -1045,6 +1172,9 @@ module RustDataFlow implements InputSig<Location> {
p.isParameterOf(c, pos) and
FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(c.asLibraryCallable(), pos)
)
or
VariableCapture::Flow::heuristicAllowInstanceParameterReturnInSelf(p.(Node::ClosureParameterNode)
.getCfgScope())
}
/**
@@ -1064,15 +1194,11 @@ module RustDataFlow implements InputSig<Location> {
.getSummaryNode(), node2.(Node::FlowSummaryNode).getSummaryNode())
}
class LambdaCallKind = Unit;
/** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */
predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) {
exists(ClosureExpr cl |
cl = creation.asExpr().getExpr() and
cl = c.asCfgScope()
) and
exists(kind)
exists(Expr e |
e = creation.asExpr().getExpr() and lambdaCreationExpr(e, kind) and e = c.asCfgScope()
)
}
/**
@@ -1098,6 +1224,141 @@ module RustDataFlow implements InputSig<Location> {
class DataFlowSecondLevelScope = Void;
}
/** Provides logic related to captured variables. */
module VariableCapture {
private import codeql.dataflow.VariableCapture as SharedVariableCapture
private predicate closureFlowStep(ExprCfgNode e1, ExprCfgNode e2) {
e1 = getALastEvalNode(e2)
or
exists(Ssa::Definition def |
def.getARead() = e2 and
def.getAnUltimateDefinition().(Ssa::WriteDefinition).assigns(e1)
)
}
private module CaptureInput implements SharedVariableCapture::InputSig<Location> {
private import rust as Ast
private import codeql.rust.controlflow.BasicBlocks as BasicBlocks
private import codeql.rust.elements.Variable as Variable
class BasicBlock extends BasicBlocks::BasicBlock {
Callable getEnclosingCallable() { result = this.getScope() }
}
class ControlFlowNode = CfgNode;
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) {
result = bb.getImmediateDominator()
}
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
class CapturedVariable extends Variable {
CapturedVariable() { this.isCaptured() }
Callable getCallable() { result = this.getEnclosingCfgScope() }
}
final class CapturedParameter extends CapturedVariable {
ParamBase p;
CapturedParameter() { p = this.getParameter() }
Node::SourceParameterNode getParameterNode() { result.getParameter().getParamBase() = p }
}
class Expr extends CfgNode {
predicate hasCfgNode(BasicBlock bb, int i) { this = bb.getNode(i) }
}
class VariableWrite extends Expr {
ExprCfgNode source;
CapturedVariable v;
VariableWrite() {
exists(AssignmentExprCfgNode assign, Variable::VariableWriteAccess write |
this = assign and
v = write.getVariable() and
assign.getLhs().getExpr() = write and
assign.getRhs() = source
)
or
exists(LetStmtCfgNode ls |
this = ls and
v.getPat() = ls.getPat().getPat() and
ls.getInitializer() = source
)
}
CapturedVariable getVariable() { result = v }
ExprCfgNode getSource() { result = source }
}
class VariableRead extends Expr instanceof ExprCfgNode {
CapturedVariable v;
VariableRead() {
exists(VariableReadAccess read | this.getExpr() = read and v = read.getVariable())
}
CapturedVariable getVariable() { result = v }
}
class ClosureExpr extends Expr instanceof ExprCfgNode {
ClosureExpr() { lambdaCreationExpr(super.getExpr(), _) }
predicate hasBody(Callable body) { body = super.getExpr() }
predicate hasAliasedAccess(Expr f) { closureFlowStep+(this, f) and not closureFlowStep(f, _) }
}
class Callable extends CfgScope {
predicate isConstructor() { none() }
}
}
class CapturedVariable = CaptureInput::CapturedVariable;
module Flow = SharedVariableCapture::Flow<Location, CaptureInput>;
private Flow::ClosureNode asClosureNode(Node n) {
result = n.(Node::CaptureNode).getSynthesizedCaptureNode()
or
result.(Flow::ExprNode).getExpr() = n.asExpr()
or
result.(Flow::VariableWriteSourceNode).getVariableWrite().getSource() = n.asExpr()
or
result.(Flow::ExprPostUpdateNode).getExpr() =
n.(Node::PostUpdateNode).getPreUpdateNode().asExpr()
or
result.(Flow::ParameterNode).getParameter().getParameterNode() = n
or
result.(Flow::ThisParameterNode).getCallable() = n.(Node::ClosureParameterNode).getCfgScope()
}
predicate storeStep(Node node1, CapturedVariableContent c, Node node2) {
Flow::storeStep(asClosureNode(node1), c.getVariable(), asClosureNode(node2))
}
predicate readStep(Node node1, CapturedVariableContent c, Node node2) {
Flow::readStep(asClosureNode(node1), c.getVariable(), asClosureNode(node2))
}
predicate localFlowStep(Node node1, Node node2) {
Flow::localFlowStep(asClosureNode(node1), asClosureNode(node2))
}
predicate clearsContent(Node node, CapturedVariableContent c) {
Flow::clearsContent(asClosureNode(node), c.getVariable())
}
class CapturedSsaDefinitionExt extends SsaImpl::DefinitionExt {
CapturedSsaDefinitionExt() { this.getSourceVariable() instanceof CapturedVariable }
}
}
import MakeImpl<Location, RustDataFlow>
/** A collection of cached types and predicates to be evaluated in the same stage. */
@@ -1112,6 +1373,8 @@ private module Cached {
TPatNode(PatCfgNode p) or
TExprPostUpdateNode(ExprCfgNode e) {
isArgumentForCall(e, _, _) or
lambdaCallExpr(_, _, e) or
lambdaCreationExpr(e.getExpr(), _) or
e =
[
any(IndexExprCfgNode i).getBase(), any(FieldExprCfgNode access).getExpr(),
@@ -1119,7 +1382,9 @@ private module Cached {
]
} or
TSsaNode(SsaImpl::DataFlowIntegration::SsaNode node) or
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn)
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) or
TLambdaSelfReferenceNode(CfgScope c) { lambdaCreationExpr(c, _) } or
TCaptureNode(VariableCapture::Flow::SynthesizedCaptureNode cn)
cached
newtype TDataFlowCall =
@@ -1156,6 +1421,7 @@ private module Cached {
or
FlowSummaryImpl::ParsePositions::isParsedParameterPosition(_, i)
} or
TLambdaSelfParameterPosition() or
TSelfParameterPosition()
cached
@@ -1211,7 +1477,8 @@ private module Cached {
} or
TStructFieldContent(StructCanonicalPath s, string field) {
field = s.getStruct().getFieldList().(RecordFieldList).getAField().getName().getText()
}
} or
TCapturedVariableContent(VariableCapture::CapturedVariable v)
cached
newtype TContentSet = TSingletonContentSet(Content c)

View File

@@ -335,7 +335,7 @@ private module Cached {
/**
* Holds if `v` is written at index `i` in basic block `bb`, and the corresponding
* AST write access is `write`.
* write access node in the CFG is `write`.
*/
cached
predicate variableWriteActual(BasicBlock bb, int i, Variable v, CfgNode write) {

View File

@@ -10,6 +10,9 @@ edges
| main.rs:34:13:34:22 | f(...) | main.rs:35:10:35:10 | b | provenance | |
| main.rs:34:21:34:21 | a | main.rs:27:20:27:23 | ... | provenance | |
| main.rs:34:21:34:21 | a | main.rs:34:13:34:22 | f(...) | provenance | |
| main.rs:42:16:42:25 | source(...) | main.rs:44:5:44:5 | [post] f [captured capt] | provenance | |
| main.rs:44:5:44:5 | [post] f [captured capt] | main.rs:45:10:45:13 | capt | provenance | |
| main.rs:44:5:44:5 | [post] f [captured capt] | main.rs:47:14:47:17 | capt | provenance | |
nodes
| main.rs:11:20:11:52 | if cond {...} else {...} | semmle.label | if cond {...} else {...} |
| main.rs:11:30:11:39 | source(...) | semmle.label | source(...) |
@@ -24,6 +27,10 @@ nodes
| main.rs:34:13:34:22 | f(...) | semmle.label | f(...) |
| main.rs:34:21:34:21 | a | semmle.label | a |
| main.rs:35:10:35:10 | b | semmle.label | b |
| main.rs:42:16:42:25 | source(...) | semmle.label | source(...) |
| main.rs:44:5:44:5 | [post] f [captured capt] | semmle.label | [post] f [captured capt] |
| main.rs:45:10:45:13 | capt | semmle.label | capt |
| main.rs:47:14:47:17 | capt | semmle.label | capt |
subpaths
| main.rs:34:21:34:21 | a | main.rs:27:20:27:23 | ... | main.rs:28:9:32:9 | if cond {...} else {...} | main.rs:34:13:34:22 | f(...) |
testFailures
@@ -31,3 +38,5 @@ testFailures
| main.rs:12:10:12:16 | f(...) | main.rs:11:30:11:39 | source(...) | main.rs:12:10:12:16 | f(...) | $@ | main.rs:11:30:11:39 | source(...) | source(...) |
| main.rs:18:18:18:21 | data | main.rs:22:13:22:22 | source(...) | main.rs:18:18:18:21 | data | $@ | main.rs:22:13:22:22 | source(...) | source(...) |
| main.rs:35:10:35:10 | b | main.rs:33:13:33:22 | source(...) | main.rs:35:10:35:10 | b | $@ | main.rs:33:13:33:22 | source(...) | source(...) |
| main.rs:45:10:45:13 | capt | main.rs:42:16:42:25 | source(...) | main.rs:45:10:45:13 | capt | $@ | main.rs:42:16:42:25 | source(...) | source(...) |
| main.rs:47:14:47:17 | capt | main.rs:42:16:42:25 | source(...) | main.rs:47:14:47:17 | capt | $@ | main.rs:42:16:42:25 | source(...) | source(...) |

View File

@@ -42,9 +42,9 @@ fn closure_captured_variable() {
capt = source(73);
};
f();
sink(capt); // $ MISSING: hasValueFlow=73
sink(capt); // $ hasValueFlow=73
let g = || {
sink(capt); // $ MISSING: hasValueFlow=73
sink(capt); // $ hasValueFlow=73
};
g();
}

View File

@@ -120,12 +120,12 @@ definition
| variables.rs:418:9:418:13 | y | variables.rs:418:13:418:13 | y |
| variables.rs:420:9:420:20 | closure2 | variables.rs:420:13:420:20 | closure2 |
| variables.rs:421:9:421:9 | y | variables.rs:418:13:418:13 | y |
| variables.rs:423:5:423:14 | closure2(...) | variables.rs:418:13:418:13 | y |
| variables.rs:423:5:423:14 | <captured exit> y | variables.rs:418:13:418:13 | y |
| variables.rs:428:9:428:20 | closure3 | variables.rs:428:13:428:20 | closure3 |
| variables.rs:436:9:436:13 | i | variables.rs:436:13:436:13 | i |
| variables.rs:437:9:437:13 | block | variables.rs:437:9:437:13 | block |
| variables.rs:438:9:438:9 | i | variables.rs:436:13:436:13 | i |
| variables.rs:441:5:441:15 | await block | variables.rs:436:13:436:13 | i |
| variables.rs:441:5:441:15 | <captured exit> i | variables.rs:436:13:436:13 | i |
| variables.rs:445:8:445:8 | b | variables.rs:445:8:445:8 | b |
| variables.rs:446:9:446:13 | x | variables.rs:446:13:446:13 | x |
| variables.rs:449:5:457:5 | phi | variables.rs:446:13:446:13 | x |
@@ -248,10 +248,10 @@ read
| variables.rs:412:9:412:16 | closure1 | variables.rs:412:9:412:16 | closure1 | variables.rs:415:5:415:12 | closure1 |
| variables.rs:412:20:414:5 | <captured entry> x | variables.rs:410:13:410:13 | x | variables.rs:413:19:413:19 | x |
| variables.rs:420:9:420:20 | closure2 | variables.rs:420:13:420:20 | closure2 | variables.rs:423:5:423:12 | closure2 |
| variables.rs:423:5:423:14 | closure2(...) | variables.rs:418:13:418:13 | y | variables.rs:424:15:424:15 | y |
| variables.rs:423:5:423:14 | <captured exit> y | variables.rs:418:13:418:13 | y | variables.rs:424:15:424:15 | y |
| variables.rs:428:9:428:20 | closure3 | variables.rs:428:13:428:20 | closure3 | variables.rs:431:5:431:12 | closure3 |
| variables.rs:437:9:437:13 | block | variables.rs:437:9:437:13 | block | variables.rs:441:5:441:9 | block |
| variables.rs:441:5:441:15 | await block | variables.rs:436:13:436:13 | i | variables.rs:442:15:442:15 | i |
| variables.rs:441:5:441:15 | <captured exit> i | variables.rs:436:13:436:13 | i | variables.rs:442:15:442:15 | i |
| variables.rs:445:8:445:8 | b | variables.rs:445:8:445:8 | b | variables.rs:449:8:449:8 | b |
| variables.rs:446:9:446:13 | x | variables.rs:446:13:446:13 | x | variables.rs:447:15:447:15 | x |
| variables.rs:446:9:446:13 | x | variables.rs:446:13:446:13 | x | variables.rs:448:15:448:15 | x |
@@ -363,10 +363,10 @@ firstRead
| variables.rs:412:9:412:16 | closure1 | variables.rs:412:9:412:16 | closure1 | variables.rs:415:5:415:12 | closure1 |
| variables.rs:412:20:414:5 | <captured entry> x | variables.rs:410:13:410:13 | x | variables.rs:413:19:413:19 | x |
| variables.rs:420:9:420:20 | closure2 | variables.rs:420:13:420:20 | closure2 | variables.rs:423:5:423:12 | closure2 |
| variables.rs:423:5:423:14 | closure2(...) | variables.rs:418:13:418:13 | y | variables.rs:424:15:424:15 | y |
| variables.rs:423:5:423:14 | <captured exit> y | variables.rs:418:13:418:13 | y | variables.rs:424:15:424:15 | y |
| variables.rs:428:9:428:20 | closure3 | variables.rs:428:13:428:20 | closure3 | variables.rs:431:5:431:12 | closure3 |
| variables.rs:437:9:437:13 | block | variables.rs:437:9:437:13 | block | variables.rs:441:5:441:9 | block |
| variables.rs:441:5:441:15 | await block | variables.rs:436:13:436:13 | i | variables.rs:442:15:442:15 | i |
| variables.rs:441:5:441:15 | <captured exit> i | variables.rs:436:13:436:13 | i | variables.rs:442:15:442:15 | i |
| variables.rs:445:8:445:8 | b | variables.rs:445:8:445:8 | b | variables.rs:449:8:449:8 | b |
| variables.rs:446:9:446:13 | x | variables.rs:446:13:446:13 | x | variables.rs:447:15:447:15 | x |
| variables.rs:449:5:457:5 | phi | variables.rs:446:13:446:13 | x | variables.rs:458:15:458:15 | x |
@@ -471,10 +471,10 @@ lastRead
| variables.rs:412:9:412:16 | closure1 | variables.rs:412:9:412:16 | closure1 | variables.rs:415:5:415:12 | closure1 |
| variables.rs:412:20:414:5 | <captured entry> x | variables.rs:410:13:410:13 | x | variables.rs:413:19:413:19 | x |
| variables.rs:420:9:420:20 | closure2 | variables.rs:420:13:420:20 | closure2 | variables.rs:423:5:423:12 | closure2 |
| variables.rs:423:5:423:14 | closure2(...) | variables.rs:418:13:418:13 | y | variables.rs:424:15:424:15 | y |
| variables.rs:423:5:423:14 | <captured exit> y | variables.rs:418:13:418:13 | y | variables.rs:424:15:424:15 | y |
| variables.rs:428:9:428:20 | closure3 | variables.rs:428:13:428:20 | closure3 | variables.rs:431:5:431:12 | closure3 |
| variables.rs:437:9:437:13 | block | variables.rs:437:9:437:13 | block | variables.rs:441:5:441:9 | block |
| variables.rs:441:5:441:15 | await block | variables.rs:436:13:436:13 | i | variables.rs:442:15:442:15 | i |
| variables.rs:441:5:441:15 | <captured exit> i | variables.rs:436:13:436:13 | i | variables.rs:442:15:442:15 | i |
| variables.rs:445:8:445:8 | b | variables.rs:445:8:445:8 | b | variables.rs:449:8:449:8 | b |
| variables.rs:446:9:446:13 | x | variables.rs:446:13:446:13 | x | variables.rs:448:15:448:15 | x |
| variables.rs:449:5:457:5 | phi | variables.rs:446:13:446:13 | x | variables.rs:458:15:458:15 | x |
@@ -592,10 +592,54 @@ ultimateDef
| variables.rs:449:5:457:5 | phi | variables.rs:450:9:450:9 | x |
| variables.rs:449:5:457:5 | phi | variables.rs:454:9:454:9 | x |
assigns
| variables.rs:16:9:16:10 | x1 | variables.rs:16:14:16:16 | "a" |
| variables.rs:21:9:21:14 | x2 | variables.rs:21:18:21:18 | 4 |
| variables.rs:23:5:23:6 | x2 | variables.rs:23:10:23:10 | 5 |
| variables.rs:28:9:28:13 | x | variables.rs:28:17:28:17 | 1 |
| variables.rs:30:5:30:5 | x | variables.rs:30:9:30:9 | 2 |
| variables.rs:35:9:35:10 | x3 | variables.rs:35:14:35:14 | 1 |
| variables.rs:37:9:37:10 | x3 | variables.rs:38:9:38:14 | ... + ... |
| variables.rs:43:9:43:10 | x4 | variables.rs:43:14:43:16 | "a" |
| variables.rs:46:13:46:14 | x4 | variables.rs:46:18:46:20 | "b" |
| variables.rs:75:9:75:10 | p1 | variables.rs:75:14:75:37 | Point {...} |
| variables.rs:85:9:85:10 | s1 | variables.rs:85:14:85:41 | Some(...) |
| variables.rs:102:9:102:10 | s1 | variables.rs:102:14:102:41 | Some(...) |
| variables.rs:111:9:111:10 | x6 | variables.rs:111:14:111:20 | Some(...) |
| variables.rs:112:9:112:10 | y1 | variables.rs:112:14:112:15 | 10 |
| variables.rs:128:9:128:15 | numbers | variables.rs:128:19:128:35 | TupleExpr |
| variables.rs:155:9:155:10 | p2 | variables.rs:155:14:155:37 | Point {...} |
| variables.rs:169:9:169:11 | msg | variables.rs:169:15:169:38 | ...::Hello {...} |
| variables.rs:189:9:189:14 | either | variables.rs:189:18:189:33 | ...::Left(...) |
| variables.rs:203:9:203:10 | tv | variables.rs:203:14:203:36 | ...::Second(...) |
| variables.rs:219:9:219:14 | either | variables.rs:219:18:219:33 | ...::Left(...) |
| variables.rs:229:9:229:14 | either | variables.rs:229:18:229:33 | ...::Left(...) |
| variables.rs:253:9:253:10 | fv | variables.rs:253:14:253:35 | ...::Second(...) |
| variables.rs:315:9:315:23 | example_closure | variables.rs:316:9:317:9 | \|...\| x |
| variables.rs:318:9:318:10 | n1 | variables.rs:319:9:319:26 | example_closure(...) |
| variables.rs:323:9:323:26 | immutable_variable | variables.rs:324:9:325:9 | \|...\| x |
| variables.rs:326:9:326:10 | n2 | variables.rs:327:9:327:29 | immutable_variable(...) |
| variables.rs:332:9:332:9 | v | variables.rs:332:13:332:41 | &... |
| variables.rs:350:9:350:13 | ref_i | variables.rs:351:9:351:14 | &mut i |
| variables.rs:373:9:373:9 | y | variables.rs:374:9:374:28 | mutate_param(...) |
| variables.rs:380:9:380:9 | w | variables.rs:381:9:381:19 | &mut ... |
| variables.rs:393:9:393:9 | y | variables.rs:394:9:394:14 | &mut x |
| variables.rs:400:9:400:9 | x | variables.rs:400:13:400:15 | 100 |
| variables.rs:402:9:402:11 | cap | variables.rs:402:15:404:5 | \|...\| ... |
| variables.rs:410:9:410:13 | x | variables.rs:410:17:410:17 | 1 |
| variables.rs:412:9:412:16 | closure1 | variables.rs:412:20:414:5 | \|...\| ... |
| variables.rs:418:9:418:13 | y | variables.rs:418:17:418:17 | 2 |
| variables.rs:420:9:420:20 | closure2 | variables.rs:420:24:422:5 | \|...\| ... |
| variables.rs:421:9:421:9 | y | variables.rs:421:13:421:13 | 3 |
| variables.rs:428:9:428:20 | closure3 | variables.rs:428:24:430:5 | \|...\| ... |
| variables.rs:436:9:436:13 | i | variables.rs:436:22:436:22 | 0 |
| variables.rs:437:9:437:13 | block | variables.rs:437:17:439:5 | { ... } |
| variables.rs:438:9:438:9 | i | variables.rs:438:13:438:13 | 1 |
| variables.rs:446:9:446:13 | x | variables.rs:446:17:446:17 | 1 |
| variables.rs:450:9:450:9 | x | variables.rs:450:13:450:13 | 2 |
| variables.rs:454:9:454:9 | x | variables.rs:454:13:454:13 | 3 |
| variables.rs:462:9:462:9 | x | variables.rs:462:13:462:13 | 1 |
| variables.rs:491:13:491:17 | f | variables.rs:491:21:494:9 | \|...\| ... |
| variables.rs:510:9:510:13 | a | variables.rs:510:17:510:25 | [...] |
| variables.rs:514:5:514:5 | a | variables.rs:514:9:514:17 | [...] |
| variables.rs:519:9:519:9 | x | variables.rs:519:13:519:14 | 16 |
| variables.rs:523:9:523:9 | z | variables.rs:523:13:523:14 | 17 |