fix rest parameter spans (#16176)

Closes #16071

# Description

Rest parameter variables are now fully spanned, instead of just the
first value:

```diff
def foo [...rest] {
  metadata $rest | view span $in.span.start $in.span.end
}
foo 1 2

-1
+1 2
```
This commit is contained in:
Solomon
2025-07-16 10:02:48 +00:00
committed by GitHub
parent 00e9e0e6a9
commit 5aa1ccea10
2 changed files with 18 additions and 2 deletions

View File

@ -1159,6 +1159,7 @@ fn gather_arguments(
// Arguments that didn't get consumed by required/optional // Arguments that didn't get consumed by required/optional
let mut rest = vec![]; let mut rest = vec![];
let mut rest_span: Option<Span> = None;
// If we encounter a spread, all further positionals should go to rest // If we encounter a spread, all further positionals should go to rest
let mut always_spread = false; let mut always_spread = false;
@ -1178,12 +1179,19 @@ fn gather_arguments(
} }
callee_stack.add_var(var_id, val); callee_stack.add_var(var_id, val);
} else { } else {
rest_span = Some(rest_span.map_or(val.span(), |s| s.append(val.span())));
rest.push(val); rest.push(val);
} }
} }
Argument::Spread { vals, .. } => { Argument::Spread {
vals,
span: spread_span,
..
} => {
if let Value::List { vals, .. } = vals { if let Value::List { vals, .. } = vals {
rest.extend(vals); rest.extend(vals);
// Rest variable should span the spread syntax, not the list values
rest_span = Some(rest_span.map_or(spread_span, |s| s.append(spread_span)));
// All further positional args should go to spread // All further positional args should go to spread
always_spread = true; always_spread = true;
} else if let Value::Error { error, .. } = vals { } else if let Value::Error { error, .. } = vals {
@ -1218,7 +1226,7 @@ fn gather_arguments(
// Add the collected rest of the arguments if a spread argument exists // Add the collected rest of the arguments if a spread argument exists
if let Some(rest_arg) = &block.signature.rest_positional { if let Some(rest_arg) = &block.signature.rest_positional {
let rest_span = rest.first().map(|v| v.span()).unwrap_or(call_head); let rest_span = rest_span.unwrap_or(call_head);
let var_id = expect_positional_var_id(rest_arg, rest_span)?; let var_id = expect_positional_var_id(rest_arg, rest_span)?;
callee_stack.add_var(var_id, Value::list(rest, rest_span)); callee_stack.add_var(var_id, Value::list(rest, rest_span));
} }

View File

@ -439,6 +439,14 @@ fn better_operator_spans() -> TestResult {
) )
} }
#[test]
fn call_rest_arg_span() -> TestResult {
run_test(
r#"let l = [2, 3]; def foo [...rest] { metadata $rest | view span $in.span.start $in.span.end }; foo 1 ...$l"#,
"1 ...$l",
)
}
#[test] #[test]
fn range_right_exclusive() -> TestResult { fn range_right_exclusive() -> TestResult {
run_test(r#"[1, 4, 5, 8, 9] | slice 1..<3 | math sum"#, "9") run_test(r#"[1, 4, 5, 8, 9] | slice 1..<3 | math sum"#, "9")