forked from extern/nushell
Limited mutable variables (#7089)
This adds support for (limited) mutable variables. Mutable variables are created with mut much the same way immutable variables are made with let. Mutable variables allow mutation via the assignment operator (=). ❯ mut x = 100 ❯ $x = 200 ❯ print $x 200 Mutable variables are limited in that they're only tended to be used in the local code block. Trying to capture a local variable will result in an error: ❯ mut x = 123; {|| $x } Error: nu::parser::expected_keyword (link) × Capture of mutable variable. The intent of this limitation is to reduce some of the issues with mutable variables in general: namely they make code that's harder to reason about. By reducing the scope that a mutable variable can be used it, we can help create local reasoning about them. Mutation can occur with fields as well, as in this case: ❯ mut y = {abc: 123} ❯ $y.abc = 456 ❯ $y On a historical note: mutable variables are something that we resisted for quite a long time, leaning as much as we could on the functional style of pipelines and dataflow. That said, we've watched folks struggle to work with reduce as an approximation for patterns that would be trivial to express with local mutation. With that in mind, we're leaning towards the happy path.
This commit is contained in:
@ -1,15 +1,16 @@
|
||||
use crate::{
|
||||
lex, lite_parse,
|
||||
lite_parse::LiteCommand,
|
||||
parse_mut,
|
||||
type_check::{math_result_type, type_compatible},
|
||||
LiteBlock, ParseError, Token, TokenContents,
|
||||
};
|
||||
|
||||
use nu_protocol::{
|
||||
ast::{
|
||||
Argument, Block, Call, CellPath, Expr, Expression, FullCellPath, ImportPattern,
|
||||
ImportPatternHead, ImportPatternMember, Operator, PathMember, Pipeline, RangeInclusion,
|
||||
RangeOperator,
|
||||
Argument, Assignment, Bits, Block, Boolean, Call, CellPath, Comparison, Expr, Expression,
|
||||
FullCellPath, ImportPattern, ImportPatternHead, ImportPatternMember, Math, Operator,
|
||||
PathMember, Pipeline, RangeInclusion, RangeOperator,
|
||||
},
|
||||
engine::StateWorkingSet,
|
||||
span, BlockId, Flag, PositionalArg, Signature, Span, Spanned, SyntaxShape, Type, Unit, VarId,
|
||||
@ -633,7 +634,7 @@ pub fn parse_multispan_value(
|
||||
SyntaxShape::VarWithOptType => {
|
||||
trace!("parsing: var with opt type");
|
||||
|
||||
let (arg, err) = parse_var_with_opt_type(working_set, spans, spans_idx);
|
||||
let (arg, err) = parse_var_with_opt_type(working_set, spans, spans_idx, false);
|
||||
error = error.or(err);
|
||||
|
||||
(arg, error)
|
||||
@ -2919,6 +2920,7 @@ pub fn parse_var_with_opt_type(
|
||||
working_set: &mut StateWorkingSet,
|
||||
spans: &[Span],
|
||||
spans_idx: &mut usize,
|
||||
mutable: bool,
|
||||
) -> (Expression, Option<ParseError>) {
|
||||
let bytes = working_set.get_span_contents(spans[*spans_idx]).to_vec();
|
||||
|
||||
@ -2953,7 +2955,7 @@ pub fn parse_var_with_opt_type(
|
||||
);
|
||||
}
|
||||
|
||||
let id = working_set.add_variable(var_name, spans[*spans_idx - 1], ty.clone());
|
||||
let id = working_set.add_variable(var_name, spans[*spans_idx - 1], ty.clone(), mutable);
|
||||
|
||||
(
|
||||
Expression {
|
||||
@ -2977,7 +2979,7 @@ pub fn parse_var_with_opt_type(
|
||||
);
|
||||
}
|
||||
|
||||
let id = working_set.add_variable(var_name, spans[*spans_idx], Type::Any);
|
||||
let id = working_set.add_variable(var_name, spans[*spans_idx], Type::Any, mutable);
|
||||
(
|
||||
Expression {
|
||||
expr: Expr::VarDecl(id),
|
||||
@ -3005,6 +3007,7 @@ pub fn parse_var_with_opt_type(
|
||||
var_name,
|
||||
span(&spans[*spans_idx..*spans_idx + 1]),
|
||||
Type::Any,
|
||||
mutable,
|
||||
);
|
||||
|
||||
(
|
||||
@ -3044,7 +3047,7 @@ pub fn parse_row_condition(
|
||||
spans: &[Span],
|
||||
expand_aliases_denylist: &[usize],
|
||||
) -> (Expression, Option<ParseError>) {
|
||||
let var_id = working_set.add_variable(b"$it".to_vec(), span(spans), Type::Any);
|
||||
let var_id = working_set.add_variable(b"$it".to_vec(), span(spans), Type::Any, false);
|
||||
let (expression, err) =
|
||||
parse_math_expression(working_set, spans, Some(var_id), expand_aliases_denylist);
|
||||
let span = span(spans);
|
||||
@ -3232,7 +3235,7 @@ pub fn parse_signature_helper(
|
||||
}
|
||||
|
||||
let var_id =
|
||||
working_set.add_variable(variable_name, span, Type::Any);
|
||||
working_set.add_variable(variable_name, span, Type::Any, false);
|
||||
|
||||
if flags.len() == 1 {
|
||||
args.push(Arg::Flag(Flag {
|
||||
@ -3282,8 +3285,12 @@ pub fn parse_signature_helper(
|
||||
})
|
||||
}
|
||||
|
||||
let var_id =
|
||||
working_set.add_variable(variable_name, span, Type::Any);
|
||||
let var_id = working_set.add_variable(
|
||||
variable_name,
|
||||
span,
|
||||
Type::Any,
|
||||
false,
|
||||
);
|
||||
|
||||
if chars.len() == 1 {
|
||||
args.push(Arg::Flag(Flag {
|
||||
@ -3327,7 +3334,7 @@ pub fn parse_signature_helper(
|
||||
}
|
||||
|
||||
let var_id =
|
||||
working_set.add_variable(variable_name, span, Type::Any);
|
||||
working_set.add_variable(variable_name, span, Type::Any, false);
|
||||
|
||||
args.push(Arg::Flag(Flag {
|
||||
arg: None,
|
||||
@ -3394,7 +3401,8 @@ pub fn parse_signature_helper(
|
||||
})
|
||||
}
|
||||
|
||||
let var_id = working_set.add_variable(contents, span, Type::Any);
|
||||
let var_id =
|
||||
working_set.add_variable(contents, span, Type::Any, false);
|
||||
|
||||
// Positional arg, optional
|
||||
args.push(Arg::Positional(
|
||||
@ -3420,7 +3428,7 @@ pub fn parse_signature_helper(
|
||||
}
|
||||
|
||||
let var_id =
|
||||
working_set.add_variable(contents_vec, span, Type::Any);
|
||||
working_set.add_variable(contents_vec, span, Type::Any, false);
|
||||
|
||||
args.push(Arg::RestPositional(PositionalArg {
|
||||
desc: String::new(),
|
||||
@ -3443,7 +3451,7 @@ pub fn parse_signature_helper(
|
||||
}
|
||||
|
||||
let var_id =
|
||||
working_set.add_variable(contents_vec, span, Type::Any);
|
||||
working_set.add_variable(contents_vec, span, Type::Any, false);
|
||||
|
||||
// Positional arg, required
|
||||
args.push(Arg::Positional(
|
||||
@ -4411,33 +4419,34 @@ pub fn parse_operator(
|
||||
let contents = working_set.get_span_contents(span);
|
||||
|
||||
let operator = match contents {
|
||||
b"==" => Operator::Equal,
|
||||
b"!=" => Operator::NotEqual,
|
||||
b"<" => Operator::LessThan,
|
||||
b"<=" => Operator::LessThanOrEqual,
|
||||
b">" => Operator::GreaterThan,
|
||||
b">=" => Operator::GreaterThanOrEqual,
|
||||
b"=~" => Operator::RegexMatch,
|
||||
b"!~" => Operator::NotRegexMatch,
|
||||
b"+" => Operator::Plus,
|
||||
b"++" => Operator::Append,
|
||||
b"-" => Operator::Minus,
|
||||
b"*" => Operator::Multiply,
|
||||
b"/" => Operator::Divide,
|
||||
b"//" => Operator::FloorDivision,
|
||||
b"in" => Operator::In,
|
||||
b"not-in" => Operator::NotIn,
|
||||
b"mod" => Operator::Modulo,
|
||||
b"bit-or" => Operator::BitOr,
|
||||
b"bit-xor" => Operator::BitXor,
|
||||
b"bit-and" => Operator::BitAnd,
|
||||
b"bit-shl" => Operator::ShiftLeft,
|
||||
b"bit-shr" => Operator::ShiftRight,
|
||||
b"starts-with" => Operator::StartsWith,
|
||||
b"ends-with" => Operator::EndsWith,
|
||||
b"&&" | b"and" => Operator::And,
|
||||
b"||" | b"or" => Operator::Or,
|
||||
b"**" => Operator::Pow,
|
||||
b"=" => Operator::Assignment(Assignment::Assign),
|
||||
b"==" => Operator::Comparison(Comparison::Equal),
|
||||
b"!=" => Operator::Comparison(Comparison::NotEqual),
|
||||
b"<" => Operator::Comparison(Comparison::LessThan),
|
||||
b"<=" => Operator::Comparison(Comparison::LessThanOrEqual),
|
||||
b">" => Operator::Comparison(Comparison::GreaterThan),
|
||||
b">=" => Operator::Comparison(Comparison::GreaterThanOrEqual),
|
||||
b"=~" => Operator::Comparison(Comparison::RegexMatch),
|
||||
b"!~" => Operator::Comparison(Comparison::NotRegexMatch),
|
||||
b"+" => Operator::Math(Math::Plus),
|
||||
b"++" => Operator::Math(Math::Append),
|
||||
b"-" => Operator::Math(Math::Minus),
|
||||
b"*" => Operator::Math(Math::Multiply),
|
||||
b"/" => Operator::Math(Math::Divide),
|
||||
b"//" => Operator::Math(Math::FloorDivision),
|
||||
b"in" => Operator::Comparison(Comparison::In),
|
||||
b"not-in" => Operator::Comparison(Comparison::NotIn),
|
||||
b"mod" => Operator::Math(Math::Modulo),
|
||||
b"bit-or" => Operator::Bits(Bits::BitOr),
|
||||
b"bit-xor" => Operator::Bits(Bits::BitXor),
|
||||
b"bit-and" => Operator::Bits(Bits::BitAnd),
|
||||
b"bit-shl" => Operator::Bits(Bits::ShiftLeft),
|
||||
b"bit-shr" => Operator::Bits(Bits::ShiftRight),
|
||||
b"starts-with" => Operator::Comparison(Comparison::StartsWith),
|
||||
b"ends-with" => Operator::Comparison(Comparison::EndsWith),
|
||||
b"&&" | b"and" => Operator::Boolean(Boolean::And),
|
||||
b"||" | b"or" => Operator::Boolean(Boolean::Or),
|
||||
b"**" => Operator::Math(Math::Pow),
|
||||
_ => {
|
||||
return (
|
||||
garbage(span),
|
||||
@ -4764,6 +4773,28 @@ pub fn parse_expression(
|
||||
spans[0],
|
||||
)),
|
||||
),
|
||||
b"mut" => (
|
||||
parse_call(
|
||||
working_set,
|
||||
&spans[pos..],
|
||||
spans[0],
|
||||
expand_aliases_denylist,
|
||||
)
|
||||
.0,
|
||||
Some(ParseError::MutInPipeline(
|
||||
String::from_utf8_lossy(match spans.len() {
|
||||
1 | 2 | 3 => b"value",
|
||||
_ => working_set.get_span_contents(spans[3]),
|
||||
})
|
||||
.to_string(),
|
||||
String::from_utf8_lossy(match spans.len() {
|
||||
1 => b"variable",
|
||||
_ => working_set.get_span_contents(spans[1]),
|
||||
})
|
||||
.to_string(),
|
||||
spans[0],
|
||||
)),
|
||||
),
|
||||
b"alias" => (
|
||||
parse_call(
|
||||
working_set,
|
||||
@ -4977,6 +5008,7 @@ pub fn parse_builtin_commands(
|
||||
b"def" | b"def-env" => parse_def(working_set, lite_command, expand_aliases_denylist),
|
||||
b"extern" => parse_extern(working_set, lite_command, expand_aliases_denylist),
|
||||
b"let" => parse_let(working_set, &lite_command.parts, expand_aliases_denylist),
|
||||
b"mut" => parse_mut(working_set, &lite_command.parts, expand_aliases_denylist),
|
||||
b"for" => {
|
||||
let (expr, err) = parse_for(working_set, &lite_command.parts, expand_aliases_denylist);
|
||||
(Pipeline::from_vec(vec![expr]), err)
|
||||
@ -5229,8 +5261,8 @@ pub fn discover_captures_in_closure(
|
||||
working_set: &StateWorkingSet,
|
||||
block: &Block,
|
||||
seen: &mut Vec<VarId>,
|
||||
seen_blocks: &mut HashMap<BlockId, Vec<VarId>>,
|
||||
) -> Vec<VarId> {
|
||||
seen_blocks: &mut HashMap<BlockId, Vec<(VarId, Span)>>,
|
||||
) -> Result<Vec<(VarId, Span)>, ParseError> {
|
||||
let mut output = vec![];
|
||||
|
||||
for flag in &block.signature.named {
|
||||
@ -5256,57 +5288,71 @@ pub fn discover_captures_in_closure(
|
||||
}
|
||||
|
||||
for pipeline in &block.pipelines {
|
||||
let result = discover_captures_in_pipeline(working_set, pipeline, seen, seen_blocks);
|
||||
let result = discover_captures_in_pipeline(working_set, pipeline, seen, seen_blocks)?;
|
||||
output.extend(&result);
|
||||
}
|
||||
|
||||
output
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
fn discover_captures_in_pipeline(
|
||||
working_set: &StateWorkingSet,
|
||||
pipeline: &Pipeline,
|
||||
seen: &mut Vec<VarId>,
|
||||
seen_blocks: &mut HashMap<BlockId, Vec<VarId>>,
|
||||
) -> Vec<VarId> {
|
||||
seen_blocks: &mut HashMap<BlockId, Vec<(VarId, Span)>>,
|
||||
) -> Result<Vec<(VarId, Span)>, ParseError> {
|
||||
let mut output = vec![];
|
||||
for expr in &pipeline.expressions {
|
||||
let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks);
|
||||
let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks)?;
|
||||
output.extend(&result);
|
||||
}
|
||||
|
||||
output
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
// Closes over captured variables
|
||||
pub fn discover_captures_in_expr(
|
||||
working_set: &StateWorkingSet,
|
||||
expr: &Expression,
|
||||
seen: &mut Vec<VarId>,
|
||||
seen_blocks: &mut HashMap<BlockId, Vec<VarId>>,
|
||||
) -> Vec<VarId> {
|
||||
let mut output = vec![];
|
||||
seen_blocks: &mut HashMap<BlockId, Vec<(VarId, Span)>>,
|
||||
) -> Result<Vec<(VarId, Span)>, ParseError> {
|
||||
let mut output: Vec<(VarId, Span)> = vec![];
|
||||
match &expr.expr {
|
||||
Expr::BinaryOp(lhs, _, rhs) => {
|
||||
let lhs_result = discover_captures_in_expr(working_set, lhs, seen, seen_blocks);
|
||||
let rhs_result = discover_captures_in_expr(working_set, rhs, seen, seen_blocks);
|
||||
let lhs_result = discover_captures_in_expr(working_set, lhs, seen, seen_blocks)?;
|
||||
let rhs_result = discover_captures_in_expr(working_set, rhs, seen, seen_blocks)?;
|
||||
|
||||
output.extend(&lhs_result);
|
||||
output.extend(&rhs_result);
|
||||
}
|
||||
Expr::UnaryNot(expr) => {
|
||||
let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks);
|
||||
let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks)?;
|
||||
output.extend(&result);
|
||||
}
|
||||
Expr::Closure(block_id) => {
|
||||
let block = working_set.get_block(*block_id);
|
||||
let results = {
|
||||
let mut seen = vec![];
|
||||
discover_captures_in_closure(working_set, block, &mut seen, seen_blocks)
|
||||
let results =
|
||||
discover_captures_in_closure(working_set, block, &mut seen, seen_blocks)?;
|
||||
|
||||
for (var_id, span) in results.iter() {
|
||||
if !seen.contains(var_id) {
|
||||
if let Some(variable) = working_set.get_variable_if_possible(*var_id) {
|
||||
if variable.mutable {
|
||||
return Err(ParseError::CaptureOfMutableVar(*span));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
results
|
||||
};
|
||||
seen_blocks.insert(*block_id, results.clone());
|
||||
for var_id in results.into_iter() {
|
||||
for (var_id, span) in results.into_iter() {
|
||||
if !seen.contains(&var_id) {
|
||||
output.push(var_id)
|
||||
output.push((var_id, span))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5315,12 +5361,12 @@ pub fn discover_captures_in_expr(
|
||||
// FIXME: is this correct?
|
||||
let results = {
|
||||
let mut seen = vec![];
|
||||
discover_captures_in_closure(working_set, block, &mut seen, seen_blocks)
|
||||
discover_captures_in_closure(working_set, block, &mut seen, seen_blocks)?
|
||||
};
|
||||
seen_blocks.insert(*block_id, results.clone());
|
||||
for var_id in results.into_iter() {
|
||||
for (var_id, span) in results.into_iter() {
|
||||
if !seen.contains(&var_id) {
|
||||
output.push(var_id)
|
||||
output.push((var_id, span))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5336,7 +5382,7 @@ pub fn discover_captures_in_expr(
|
||||
None => {
|
||||
let block = working_set.get_block(block_id);
|
||||
if !block.captures.is_empty() {
|
||||
output.extend(&block.captures);
|
||||
output.extend(block.captures.iter().map(|var_id| (*var_id, call.head)));
|
||||
} else {
|
||||
let mut seen = vec![];
|
||||
seen_blocks.insert(block_id, output.clone());
|
||||
@ -5346,7 +5392,7 @@ pub fn discover_captures_in_expr(
|
||||
block,
|
||||
&mut seen,
|
||||
seen_blocks,
|
||||
);
|
||||
)?;
|
||||
output.extend(&result);
|
||||
seen_blocks.insert(block_id, result);
|
||||
}
|
||||
@ -5356,24 +5402,24 @@ pub fn discover_captures_in_expr(
|
||||
|
||||
for named in call.named_iter() {
|
||||
if let Some(arg) = &named.2 {
|
||||
let result = discover_captures_in_expr(working_set, arg, seen, seen_blocks);
|
||||
let result = discover_captures_in_expr(working_set, arg, seen, seen_blocks)?;
|
||||
output.extend(&result);
|
||||
}
|
||||
}
|
||||
|
||||
for positional in call.positional_iter() {
|
||||
let result = discover_captures_in_expr(working_set, positional, seen, seen_blocks);
|
||||
let result = discover_captures_in_expr(working_set, positional, seen, seen_blocks)?;
|
||||
output.extend(&result);
|
||||
}
|
||||
}
|
||||
Expr::CellPath(_) => {}
|
||||
Expr::DateTime(_) => {}
|
||||
Expr::ExternalCall(head, exprs) => {
|
||||
let result = discover_captures_in_expr(working_set, head, seen, seen_blocks);
|
||||
let result = discover_captures_in_expr(working_set, head, seen, seen_blocks)?;
|
||||
output.extend(&result);
|
||||
|
||||
for expr in exprs {
|
||||
let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks);
|
||||
let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks)?;
|
||||
output.extend(&result);
|
||||
}
|
||||
}
|
||||
@ -5381,7 +5427,8 @@ pub fn discover_captures_in_expr(
|
||||
Expr::Directory(_) => {}
|
||||
Expr::Float(_) => {}
|
||||
Expr::FullCellPath(cell_path) => {
|
||||
let result = discover_captures_in_expr(working_set, &cell_path.head, seen, seen_blocks);
|
||||
let result =
|
||||
discover_captures_in_expr(working_set, &cell_path.head, seen, seen_blocks)?;
|
||||
output.extend(&result);
|
||||
}
|
||||
Expr::ImportPattern(_) => {}
|
||||
@ -5391,27 +5438,27 @@ pub fn discover_captures_in_expr(
|
||||
Expr::GlobPattern(_) => {}
|
||||
Expr::Int(_) => {}
|
||||
Expr::Keyword(_, _, expr) => {
|
||||
let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks);
|
||||
let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks)?;
|
||||
output.extend(&result);
|
||||
}
|
||||
Expr::List(exprs) => {
|
||||
for expr in exprs {
|
||||
let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks);
|
||||
let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks)?;
|
||||
output.extend(&result);
|
||||
}
|
||||
}
|
||||
Expr::Operator(_) => {}
|
||||
Expr::Range(expr1, expr2, expr3, _) => {
|
||||
if let Some(expr) = expr1 {
|
||||
let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks);
|
||||
let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks)?;
|
||||
output.extend(&result);
|
||||
}
|
||||
if let Some(expr) = expr2 {
|
||||
let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks);
|
||||
let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks)?;
|
||||
output.extend(&result);
|
||||
}
|
||||
if let Some(expr) = expr3 {
|
||||
let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks);
|
||||
let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks)?;
|
||||
output.extend(&result);
|
||||
}
|
||||
}
|
||||
@ -5422,13 +5469,13 @@ pub fn discover_captures_in_expr(
|
||||
field_name,
|
||||
seen,
|
||||
seen_blocks,
|
||||
));
|
||||
)?);
|
||||
output.extend(&discover_captures_in_expr(
|
||||
working_set,
|
||||
field_value,
|
||||
seen,
|
||||
seen_blocks,
|
||||
));
|
||||
)?);
|
||||
}
|
||||
}
|
||||
Expr::Signature(sig) => {
|
||||
@ -5457,7 +5504,7 @@ pub fn discover_captures_in_expr(
|
||||
Expr::String(_) => {}
|
||||
Expr::StringInterpolation(exprs) => {
|
||||
for expr in exprs {
|
||||
let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks);
|
||||
let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks)?;
|
||||
output.extend(&result);
|
||||
}
|
||||
}
|
||||
@ -5465,41 +5512,41 @@ pub fn discover_captures_in_expr(
|
||||
let block = working_set.get_block(*block_id);
|
||||
let results = {
|
||||
let mut seen = vec![];
|
||||
discover_captures_in_closure(working_set, block, &mut seen, seen_blocks)
|
||||
discover_captures_in_closure(working_set, block, &mut seen, seen_blocks)?
|
||||
};
|
||||
seen_blocks.insert(*block_id, results.clone());
|
||||
for var_id in results.into_iter() {
|
||||
for (var_id, span) in results.into_iter() {
|
||||
if !seen.contains(&var_id) {
|
||||
output.push(var_id)
|
||||
output.push((var_id, span))
|
||||
}
|
||||
}
|
||||
}
|
||||
Expr::Table(headers, values) => {
|
||||
for header in headers {
|
||||
let result = discover_captures_in_expr(working_set, header, seen, seen_blocks);
|
||||
let result = discover_captures_in_expr(working_set, header, seen, seen_blocks)?;
|
||||
output.extend(&result);
|
||||
}
|
||||
for row in values {
|
||||
for cell in row {
|
||||
let result = discover_captures_in_expr(working_set, cell, seen, seen_blocks);
|
||||
let result = discover_captures_in_expr(working_set, cell, seen, seen_blocks)?;
|
||||
output.extend(&result);
|
||||
}
|
||||
}
|
||||
}
|
||||
Expr::ValueWithUnit(expr, _) => {
|
||||
let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks);
|
||||
let result = discover_captures_in_expr(working_set, expr, seen, seen_blocks)?;
|
||||
output.extend(&result);
|
||||
}
|
||||
Expr::Var(var_id) => {
|
||||
if (*var_id > ENV_VARIABLE_ID || *var_id == IN_VARIABLE_ID) && !seen.contains(var_id) {
|
||||
output.push(*var_id);
|
||||
output.push((*var_id, expr.span));
|
||||
}
|
||||
}
|
||||
Expr::VarDecl(var_id) => {
|
||||
seen.push(*var_id);
|
||||
}
|
||||
}
|
||||
output
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
fn wrap_expr_with_collect(working_set: &mut StateWorkingSet, expr: &Expression) -> Expression {
|
||||
@ -5604,7 +5651,10 @@ pub fn parse(
|
||||
let mut seen_blocks = HashMap::new();
|
||||
|
||||
let captures = discover_captures_in_closure(working_set, &output, &mut seen, &mut seen_blocks);
|
||||
output.captures = captures;
|
||||
match captures {
|
||||
Ok(captures) => output.captures = captures.into_iter().map(|(var_id, _)| var_id).collect(),
|
||||
Err(err) => error = Some(err),
|
||||
}
|
||||
|
||||
// Also check other blocks that might have been imported
|
||||
for (block_idx, block) in working_set.delta.blocks.iter().enumerate() {
|
||||
@ -5613,7 +5663,12 @@ pub fn parse(
|
||||
if !seen_blocks.contains_key(&block_id) {
|
||||
let captures =
|
||||
discover_captures_in_closure(working_set, block, &mut seen, &mut seen_blocks);
|
||||
seen_blocks.insert(block_id, captures);
|
||||
match captures {
|
||||
Ok(captures) => {
|
||||
seen_blocks.insert(block_id, captures);
|
||||
}
|
||||
Err(err) => error = Some(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5626,7 +5681,7 @@ pub fn parse(
|
||||
let block_captures_empty = block.captures.is_empty();
|
||||
if !captures.is_empty() && block_captures_empty {
|
||||
let block = working_set.get_block_mut(block_id);
|
||||
block.captures = captures;
|
||||
block.captures = captures.into_iter().map(|(var_id, _)| var_id).collect();
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user