Merge pull request #18270 from paldepind/rust-captured-variables

Rust: Flow through captured variables
This commit is contained in:
Simon Friis Vindum
2024-12-16 10:08:53 +01:00
committed by GitHub
12 changed files with 571 additions and 131 deletions

View File

@@ -0,0 +1,8 @@
/**
* @name Variable capture data flow inconsistencies
* @description Lists the variable capture data flow inconsistencies in the database. This query is intended for internal use.
* @kind table
* @id rust/diagnostics/variable-capture-data-flow-consistency
*/
import codeql.rust.dataflow.internal.DataFlowImpl::VariableCapture::Flow::ConsistencyChecks

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 lhs. 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
*
* ```rust
* fn capture_mut() {
* let mut y = 0;
* (0..5).for_each(|x| {
* y += x
* });
* y
* }
* ```
*
* a definition for `y` is inserted at the call to `for_each`.
*/
private 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

@@ -8,6 +8,7 @@ private import codeql.dataflow.DataFlow
private import codeql.dataflow.internal.DataFlowImpl
private import rust
private import SsaImpl as SsaImpl
private import codeql.rust.controlflow.internal.Scope as Scope
private import codeql.rust.controlflow.ControlFlowGraph
private import codeql.rust.controlflow.CfgNodes
private import codeql.rust.dataflow.Ssa
@@ -101,11 +102,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 closure itself. Only
* used for tracking flow through captured variables.
*/
predicate isClosureSelf() { this = TClosureSelfParameterPosition() }
/** Gets a textual representation of this position. */
string toString() {
result = this.getPosition().toString()
or
result = "self" and this.isSelf()
or
result = "closure self" and this.isClosureSelf()
}
ParamBase getParameterIn(ParamList ps) {
@@ -264,6 +273,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, TClosureSelfReferenceNode {
private CfgScope cfgScope;
ClosureParameterNode() { this = TClosureSelfReferenceNode(cfgScope) }
final override CfgScope getCfgScope() { result = cfgScope }
override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
cfgScope = c.asCfgScope() and pos.isClosureSelf()
}
override Location getLocation() { result = cfgScope.getLocation() }
override string toString() { result = "closure self in " + cfgScope }
}
abstract class ArgumentNode extends Node {
abstract predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos);
}
@@ -292,6 +321,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.isClosureSelf()
}
}
/** An SSA node. */
class SsaNode extends Node, TSsaNode {
SsaImpl::DataFlowIntegration::SsaNode node;
@@ -360,6 +404,24 @@ module Node {
override DataFlowCall getCall(ReturnKind kind) { result = call and kind = kind_ }
}
/**
* 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() }
}
/**
* A node associated with an object after an operation that might have
* changed its state.
@@ -402,6 +464,19 @@ 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() }
}
final class CastNode = NaNode;
}
@@ -625,6 +700,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 +768,31 @@ 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`. */
private predicate lambdaCreationExpr(Expr creation, LambdaCallKind kind) {
(
creation instanceof ClosureExpr
or
creation instanceof Scope::AsyncBlockScope
) 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 +806,8 @@ private module Aliases {
class ContentAlias = Content;
class ContentSetAlias = ContentSet;
class LambdaCallKindAlias = LambdaCallKind;
}
module RustDataFlow implements InputSig<Location> {
@@ -735,6 +849,10 @@ module RustDataFlow implements InputSig<Location> {
node instanceof Node::SsaNode
or
node instanceof Node::FlowSummaryNode
or
node instanceof Node::CaptureNode
or
node instanceof Node::ClosureParameterNode
}
class DataFlowExpr = ExprCfgNode;
@@ -775,6 +893,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 +919,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 +1039,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 +1122,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 +1140,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 +1176,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 +1198,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 +1228,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 +1377,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 +1386,9 @@ private module Cached {
]
} or
TSsaNode(SsaImpl::DataFlowIntegration::SsaNode node) or
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn)
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) or
TClosureSelfReferenceNode(CfgScope c) { lambdaCreationExpr(c, _) } or
TCaptureNode(VariableCapture::Flow::SynthesizedCaptureNode cn)
cached
newtype TDataFlowCall =
@@ -1156,6 +1425,7 @@ private module Cached {
or
FlowSummaryImpl::ParsePositions::isParsedParameterPosition(_, i)
} or
TClosureSelfParameterPosition() or
TSelfParameterPosition()
cached
@@ -1211,7 +1481,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

@@ -0,0 +1,44 @@
models
edges
| main.rs:11:20:11:52 | if cond {...} else {...} | main.rs:12:10:12:16 | f(...) | provenance | |
| main.rs:11:30:11:39 | source(...) | main.rs:11:20:11:52 | if cond {...} else {...} | provenance | |
| main.rs:16:20:16:23 | ... | main.rs:18:18:18:21 | data | provenance | |
| main.rs:22:13:22:22 | source(...) | main.rs:23:13:23:13 | a | provenance | |
| main.rs:23:13:23:13 | a | main.rs:16:20:16:23 | ... | provenance | |
| main.rs:27:20:27:23 | ... | main.rs:28:9:32:9 | if cond {...} else {...} | provenance | |
| main.rs:33:13:33:22 | source(...) | main.rs:34:21:34:21 | a | provenance | |
| 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:49:5:49:5 | g [captured capt] | provenance | |
| main.rs:49:5:49:5 | g [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(...) |
| main.rs:12:10:12:16 | f(...) | semmle.label | f(...) |
| main.rs:16:20:16:23 | ... | semmle.label | ... |
| main.rs:18:18:18:21 | data | semmle.label | data |
| main.rs:22:13:22:22 | source(...) | semmle.label | source(...) |
| main.rs:23:13:23:13 | a | semmle.label | a |
| main.rs:27:20:27:23 | ... | semmle.label | ... |
| main.rs:28:9:32:9 | if cond {...} else {...} | semmle.label | if cond {...} else {...} |
| main.rs:33:13:33:22 | source(...) | semmle.label | source(...) |
| 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 |
| main.rs:49:5:49:5 | g [captured capt] | semmle.label | g [captured 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
#select
| 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

@@ -0,0 +1,12 @@
/**
* @kind path-problem
*/
import rust
import utils.InlineFlowTest
import DefaultFlowTest
import ValueFlow::PathGraph
from ValueFlow::PathNode source, ValueFlow::PathNode sink
where ValueFlow::flowPath(source, sink)
select sink, source, sink, "$@", source, source.toString()

View File

@@ -0,0 +1,57 @@
fn source(i: i64) -> i64 {
1000 + i
}
fn sink(s: i64) {
println!("{}", s);
}
fn closure_flow_out() {
let f = |cond| if cond { source(92) } else { 0 };
sink(f(true)); // $ hasValueFlow=92
}
fn closure_flow_in() {
let f = |cond, data|
if cond {
sink(data); // $ hasValueFlow=87
} else {
sink(0)
};
let a = source(87);
f(true, a);
}
fn closure_flow_through() {
let f = |cond, data|
if cond {
data
} else {
0
};
let a = source(43);
let b = f(true, a);
sink(b); // $ hasValueFlow=43
}
fn closure_captured_variable() {
let mut capt = 1;
sink(capt);
let mut f = || {
capt = source(73);
};
f();
sink(capt); // $ hasValueFlow=73
let g = || {
sink(capt); // $ hasValueFlow=73
};
g();
}
fn main() {
closure_flow_out();
closure_flow_in();
closure_flow_through();
closure_captured_variable();
}

View File

@@ -0,0 +1,2 @@
identityLocalStep
| main.rs:404:7:404:18 | phi(default_name) | Node steps to itself |

View File

@@ -427,51 +427,41 @@ localStep
| main.rs:377:13:377:19 | [post] mut_arr | main.rs:379:10:379:16 | mut_arr |
| main.rs:377:13:377:19 | mut_arr | main.rs:379:10:379:16 | mut_arr |
| main.rs:377:13:377:22 | mut_arr[1] | main.rs:377:9:377:9 | d |
| main.rs:383:9:383:9 | [SSA] f | main.rs:384:10:384:10 | f |
| main.rs:383:9:383:9 | f | main.rs:383:9:383:9 | [SSA] f |
| main.rs:383:13:383:52 | \|...\| ... | main.rs:383:9:383:9 | f |
| main.rs:383:14:383:17 | ... | main.rs:383:14:383:17 | cond |
| main.rs:383:14:383:17 | [SSA] cond | main.rs:383:23:383:26 | cond |
| main.rs:383:14:383:17 | cond | main.rs:383:14:383:17 | [SSA] cond |
| main.rs:383:28:383:41 | { ... } | main.rs:383:20:383:52 | if cond {...} else {...} |
| main.rs:383:30:383:39 | source(...) | main.rs:383:28:383:41 | { ... } |
| main.rs:383:48:383:52 | { ... } | main.rs:383:20:383:52 | if cond {...} else {...} |
| main.rs:383:50:383:50 | 0 | main.rs:383:48:383:52 | { ... } |
| main.rs:388:9:388:9 | [SSA] f | main.rs:395:5:395:5 | f |
| main.rs:388:9:388:9 | f | main.rs:388:9:388:9 | [SSA] f |
| main.rs:388:13:393:9 | \|...\| ... | main.rs:388:9:388:9 | f |
| main.rs:388:14:388:17 | ... | main.rs:388:14:388:17 | cond |
| main.rs:388:14:388:17 | [SSA] cond | main.rs:389:12:389:15 | cond |
| main.rs:388:14:388:17 | cond | main.rs:388:14:388:17 | [SSA] cond |
| main.rs:388:20:388:23 | ... | main.rs:388:20:388:23 | data |
| main.rs:388:20:388:23 | [SSA] data | main.rs:390:18:390:21 | data |
| main.rs:388:20:388:23 | data | main.rs:388:20:388:23 | [SSA] data |
| main.rs:389:17:391:9 | { ... } | main.rs:389:9:393:9 | if cond {...} else {...} |
| main.rs:391:16:393:9 | { ... } | main.rs:389:9:393:9 | if cond {...} else {...} |
| main.rs:392:13:392:19 | sink(...) | main.rs:391:16:393:9 | { ... } |
| main.rs:394:9:394:9 | [SSA] a | main.rs:395:13:395:13 | a |
| main.rs:394:9:394:9 | a | main.rs:394:9:394:9 | [SSA] a |
| main.rs:394:13:394:22 | source(...) | main.rs:394:9:394:9 | a |
| main.rs:399:9:399:9 | [SSA] f | main.rs:406:13:406:13 | f |
| main.rs:399:9:399:9 | f | main.rs:399:9:399:9 | [SSA] f |
| main.rs:399:13:404:9 | \|...\| ... | main.rs:399:9:399:9 | f |
| main.rs:399:14:399:17 | ... | main.rs:399:14:399:17 | cond |
| main.rs:399:14:399:17 | [SSA] cond | main.rs:400:12:400:15 | cond |
| main.rs:399:14:399:17 | cond | main.rs:399:14:399:17 | [SSA] cond |
| main.rs:399:20:399:23 | ... | main.rs:399:20:399:23 | data |
| main.rs:399:20:399:23 | [SSA] data | main.rs:401:13:401:16 | data |
| main.rs:399:20:399:23 | data | main.rs:399:20:399:23 | [SSA] data |
| main.rs:400:17:402:9 | { ... } | main.rs:400:9:404:9 | if cond {...} else {...} |
| main.rs:401:13:401:16 | data | main.rs:400:17:402:9 | { ... } |
| main.rs:402:16:404:9 | { ... } | main.rs:400:9:404:9 | if cond {...} else {...} |
| main.rs:403:13:403:13 | 0 | main.rs:402:16:404:9 | { ... } |
| main.rs:405:9:405:9 | [SSA] a | main.rs:406:21:406:21 | a |
| main.rs:405:9:405:9 | a | main.rs:405:9:405:9 | [SSA] a |
| main.rs:405:13:405:22 | source(...) | main.rs:405:9:405:9 | a |
| main.rs:406:9:406:9 | [SSA] b | main.rs:407:10:407:10 | b |
| main.rs:406:9:406:9 | b | main.rs:406:9:406:9 | [SSA] b |
| main.rs:406:13:406:22 | f(...) | main.rs:406:9:406:9 | b |
| main.rs:431:13:431:33 | result_questionmark(...) | main.rs:431:9:431:9 | _ |
| main.rs:386:9:386:9 | a | main.rs:386:9:386:9 | [SSA] a |
| main.rs:386:13:386:22 | source(...) | main.rs:386:9:386:9 | a |
| main.rs:387:9:387:9 | [SSA] b | main.rs:388:14:388:14 | b |
| main.rs:387:9:387:9 | b | main.rs:387:9:387:9 | [SSA] b |
| main.rs:387:13:387:14 | &a | main.rs:387:9:387:9 | b |
| main.rs:388:9:388:9 | [SSA] c | main.rs:389:10:389:10 | c |
| main.rs:388:9:388:9 | c | main.rs:388:9:388:9 | [SSA] c |
| main.rs:388:13:388:14 | * ... | main.rs:388:9:388:9 | c |
| main.rs:393:17:393:17 | 1 | main.rs:393:9:393:13 | a |
| main.rs:395:9:395:9 | [SSA] b | main.rs:396:6:396:6 | b |
| main.rs:395:9:395:9 | b | main.rs:395:9:395:9 | [SSA] b |
| main.rs:395:13:395:18 | &mut a | main.rs:395:9:395:9 | b |
| main.rs:396:10:396:19 | source(...) | main.rs:396:5:396:6 | * ... |
| main.rs:402:39:402:43 | [SSA] names | main.rs:404:23:404:27 | names |
| main.rs:402:39:402:43 | names | main.rs:402:39:402:43 | [SSA] names |
| main.rs:402:39:402:72 | ...: Vec::<...> | main.rs:402:39:402:43 | names |
| main.rs:403:7:403:18 | [SSA] default_name | main.rs:404:23:404:27 | [SSA] [input] SSA phi read(default_name) |
| main.rs:403:7:403:18 | default_name | main.rs:403:7:403:18 | [SSA] default_name |
| main.rs:403:22:403:43 | ... .to_string(...) | main.rs:403:7:403:18 | default_name |
| main.rs:404:3:410:3 | for ... in ... { ... } | main.rs:402:75:411:1 | { ... } |
| main.rs:404:7:404:18 | [SSA] SSA phi read(default_name) | main.rs:404:29:410:3 | [SSA] [input] SSA phi read(default_name) |
| main.rs:404:7:404:18 | [SSA] SSA phi read(default_name) | main.rs:408:7:408:14 | [SSA] [input] SSA phi read(default_name) |
| main.rs:404:8:404:11 | [SSA] cond | main.rs:405:8:405:11 | cond |
| main.rs:404:8:404:11 | cond | main.rs:404:8:404:11 | [SSA] cond |
| main.rs:404:14:404:17 | [SSA] name | main.rs:406:15:406:18 | name |
| main.rs:404:14:404:17 | name | main.rs:404:14:404:17 | [SSA] name |
| main.rs:404:23:404:27 | [SSA] [input] SSA phi read(default_name) | main.rs:404:7:404:18 | [SSA] SSA phi read(default_name) |
| main.rs:404:29:410:3 | [SSA] [input] SSA phi read(default_name) | main.rs:404:7:404:18 | [SSA] SSA phi read(default_name) |
| main.rs:405:5:409:5 | if cond {...} | main.rs:404:29:410:3 | { ... } |
| main.rs:406:11:406:11 | [SSA] n | main.rs:407:12:407:12 | n |
| main.rs:406:11:406:11 | n | main.rs:406:11:406:11 | [SSA] n |
| main.rs:406:15:406:62 | name.unwrap_or_else(...) | main.rs:406:11:406:11 | n |
| main.rs:406:35:406:61 | [SSA] <captured entry> default_name | main.rs:406:38:406:49 | default_name |
| main.rs:408:7:408:14 | [SSA] [input] SSA phi read(default_name) | main.rs:404:7:404:18 | [SSA] SSA phi read(default_name) |
| main.rs:434:13:434:33 | result_questionmark(...) | main.rs:434:9:434:9 | _ |
storeStep
| main.rs:94:14:94:22 | source(...) | tuple.0 | main.rs:94:13:94:26 | TupleExpr |
| main.rs:94:25:94:25 | 2 | tuple.1 | main.rs:94:13:94:26 | TupleExpr |
@@ -540,7 +530,8 @@ storeStep
| main.rs:373:27:373:27 | 2 | array[] | main.rs:373:23:373:31 | [...] |
| main.rs:373:30:373:30 | 3 | array[] | main.rs:373:23:373:31 | [...] |
| main.rs:376:18:376:27 | source(...) | array[] | main.rs:376:5:376:11 | [post] mut_arr |
| main.rs:414:27:414:27 | 0 | Some | main.rs:414:22:414:28 | Some(...) |
| main.rs:406:35:406:61 | default_name | captured default_name | main.rs:406:35:406:61 | \|...\| ... |
| main.rs:417:27:417:27 | 0 | Some | main.rs:417:22:417:28 | Some(...) |
readStep
| file://:0:0:0:0 | [summary param] self in lang:core::_::<crate::option::Option>::unwrap | Some | file://:0:0:0:0 | [summary] read: Argument[self].Variant[crate::option::Option::Some(0)] in lang:core::_::<crate::option::Option>::unwrap |
| main.rs:33:9:33:15 | Some(...) | Some | main.rs:33:14:33:14 | _ |
@@ -610,3 +601,6 @@ readStep
| main.rs:376:5:376:11 | mut_arr | array[] | main.rs:376:5:376:14 | mut_arr[1] |
| main.rs:377:13:377:19 | mut_arr | array[] | main.rs:377:13:377:22 | mut_arr[1] |
| main.rs:379:10:379:16 | mut_arr | array[] | main.rs:379:10:379:19 | mut_arr[0] |
| main.rs:404:23:404:27 | names | array[] | main.rs:404:7:404:18 | TuplePat |
| main.rs:406:35:406:61 | [post] \|...\| ... | captured default_name | main.rs:406:35:406:61 | [post] default_name |
| main.rs:406:38:406:49 | this | captured default_name | main.rs:406:38:406:49 | default_name |

View File

@@ -99,16 +99,6 @@ edges
| main.rs:377:13:377:19 | mut_arr [array[]] | main.rs:377:13:377:22 | mut_arr[1] | provenance | |
| main.rs:377:13:377:22 | mut_arr[1] | main.rs:378:10:378:10 | d | provenance | |
| main.rs:379:10:379:16 | mut_arr [array[]] | main.rs:379:10:379:19 | mut_arr[0] | provenance | |
| main.rs:383:20:383:52 | if cond {...} else {...} | main.rs:384:10:384:16 | f(...) | provenance | |
| main.rs:383:30:383:39 | source(...) | main.rs:383:20:383:52 | if cond {...} else {...} | provenance | |
| main.rs:388:20:388:23 | ... | main.rs:390:18:390:21 | data | provenance | |
| main.rs:394:13:394:22 | source(...) | main.rs:395:13:395:13 | a | provenance | |
| main.rs:395:13:395:13 | a | main.rs:388:20:388:23 | ... | provenance | |
| main.rs:399:20:399:23 | ... | main.rs:400:9:404:9 | if cond {...} else {...} | provenance | |
| main.rs:405:13:405:22 | source(...) | main.rs:406:21:406:21 | a | provenance | |
| main.rs:406:13:406:22 | f(...) | main.rs:407:10:407:10 | b | provenance | |
| main.rs:406:21:406:21 | a | main.rs:399:20:399:23 | ... | provenance | |
| main.rs:406:21:406:21 | a | main.rs:406:13:406:22 | f(...) | provenance | |
nodes
| main.rs:15:10:15:18 | source(...) | semmle.label | source(...) |
| main.rs:19:13:19:21 | source(...) | semmle.label | source(...) |
@@ -233,21 +223,7 @@ nodes
| main.rs:378:10:378:10 | d | semmle.label | d |
| main.rs:379:10:379:16 | mut_arr [array[]] | semmle.label | mut_arr [array[]] |
| main.rs:379:10:379:19 | mut_arr[0] | semmle.label | mut_arr[0] |
| main.rs:383:20:383:52 | if cond {...} else {...} | semmle.label | if cond {...} else {...} |
| main.rs:383:30:383:39 | source(...) | semmle.label | source(...) |
| main.rs:384:10:384:16 | f(...) | semmle.label | f(...) |
| main.rs:388:20:388:23 | ... | semmle.label | ... |
| main.rs:390:18:390:21 | data | semmle.label | data |
| main.rs:394:13:394:22 | source(...) | semmle.label | source(...) |
| main.rs:395:13:395:13 | a | semmle.label | a |
| main.rs:399:20:399:23 | ... | semmle.label | ... |
| main.rs:400:9:404:9 | if cond {...} else {...} | semmle.label | if cond {...} else {...} |
| main.rs:405:13:405:22 | source(...) | semmle.label | source(...) |
| main.rs:406:13:406:22 | f(...) | semmle.label | f(...) |
| main.rs:406:21:406:21 | a | semmle.label | a |
| main.rs:407:10:407:10 | b | semmle.label | b |
subpaths
| main.rs:406:21:406:21 | a | main.rs:399:20:399:23 | ... | main.rs:400:9:404:9 | if cond {...} else {...} | main.rs:406:13:406:22 | f(...) |
testFailures
#select
| main.rs:15:10:15:18 | source(...) | main.rs:15:10:15:18 | source(...) | main.rs:15:10:15:18 | source(...) | $@ | main.rs:15:10:15:18 | source(...) | source(...) |
@@ -282,6 +258,3 @@ testFailures
| main.rs:367:18:367:18 | c | main.rs:362:23:362:32 | source(...) | main.rs:367:18:367:18 | c | $@ | main.rs:362:23:362:32 | source(...) | source(...) |
| main.rs:378:10:378:10 | d | main.rs:376:18:376:27 | source(...) | main.rs:378:10:378:10 | d | $@ | main.rs:376:18:376:27 | source(...) | source(...) |
| main.rs:379:10:379:19 | mut_arr[0] | main.rs:376:18:376:27 | source(...) | main.rs:379:10:379:19 | mut_arr[0] | $@ | main.rs:376:18:376:27 | source(...) | source(...) |
| main.rs:384:10:384:16 | f(...) | main.rs:383:30:383:39 | source(...) | main.rs:384:10:384:16 | f(...) | $@ | main.rs:383:30:383:39 | source(...) | source(...) |
| main.rs:390:18:390:21 | data | main.rs:394:13:394:22 | source(...) | main.rs:390:18:390:21 | data | $@ | main.rs:394:13:394:22 | source(...) | source(...) |
| main.rs:407:10:407:10 | b | main.rs:405:13:405:22 | source(...) | main.rs:407:10:407:10 | b | $@ | main.rs:405:13:405:22 | source(...) | source(...) |

View File

@@ -379,32 +379,35 @@ fn array_assignment() {
sink(mut_arr[0]); // $ SPURIOUS: hasValueFlow=55
}
fn closure_flow_out() {
let f = |cond| if cond { source(92) } else { 0 };
sink(f(true)); // $ hasValueFlow=92
// -----------------------------------------------------------------------------
// Data flow through mutable borrows
fn read_through_borrow() {
let a = source(21);
let b = &a;
let c = *b;
sink(c); // $ MISSING: hasValueFlow=21
}
fn closure_flow_in() {
let f = |cond, data|
if cond {
sink(data); // $ hasValueFlow=87
} else {
sink(0)
};
let a = source(87);
f(true, a);
fn write_through_borrow() {
let mut a = 1;
sink(a);
let b = &mut a;
*b = source(39);
sink(a); // $ MISSING: hasValueFlow=39
}
fn closure_flow_through() {
let f = |cond, data|
if cond {
data
} else {
0
};
let a = source(43);
let b = f(true, a);
sink(b); // $ hasValueFlow=43
// Test data flow inconsistency occuring with captured variables and `continue`
// in a loop.
pub fn captured_variable_and_continue(names: Vec<(bool, Option<String>)>) {
let default_name = source(83).to_string();
for (cond, name) in names {
if cond {
let n = name.unwrap_or_else(|| default_name.to_string());
sink(n.len() as i64);
continue;
}
}
}
fn main() {
@@ -440,7 +443,7 @@ fn main() {
array_for_loop();
array_slice_pattern();
array_assignment();
closure_flow_out();
closure_flow_in();
closure_flow_through();
read_through_borrow();
write_through_borrow();
captured_variable_and_continue(vec![]);
}

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 |