Span ID Refactor (Step 2): Make Call SpanId-friendly (#13268)

<!--
if this PR closes one or more issues, you can automatically link the PR
with
them by using one of the [*linking
keywords*](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword),
e.g.
- this PR should close #xxxx
- fixes #xxxx

you can also mention related issues, PRs or discussions!
-->

# Description
<!--
Thank you for improving Nushell. Please, check our [contributing
guide](../CONTRIBUTING.md) and talk to the core team before making major
changes.

Description of your pull request goes here. **Provide examples and/or
screenshots** if your changes affect the user experience.
-->

Part of https://github.com/nushell/nushell/issues/12963, step 2.

This PR refactors Call and related argument structures to remove their
dependency on `Expression::span` which will be removed in the future.

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->

Should be none. If you see some error messages that look broken, please
report.

# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the
tests for the standard library

> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> toolkit check pr
> ```
-->

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
This commit is contained in:
Jakub Žádník 2024-07-03 09:00:52 +03:00 committed by GitHub
parent 9b63e17072
commit 0cfd5fbece
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 468 additions and 296 deletions

View file

@ -62,7 +62,7 @@ impl Command for KeybindingsList {
.collect()
} else {
call.named_iter()
.flat_map(|(argument, _, _)| get_records(argument.item.as_str(), call.head))
.flat_map(|(argument, _, _, _)| get_records(argument.item.as_str(), call.head))
.collect()
};

View file

@ -7,7 +7,7 @@ use nu_protocol::{
ast::{Argument, Call, Expr, Expression},
debugger::WithoutDebug,
engine::{Stack, StateWorkingSet},
PipelineData, Span, Type, Value,
PipelineData, Span, Spanned, Type, Value,
};
use nu_utils::IgnoreCaseExt;
use std::collections::HashMap;
@ -51,18 +51,21 @@ impl Completer for CustomCompletion {
&Call {
decl_id: self.decl_id,
head: span,
arguments: vec![
Argument::Positional(Expression::new_unknown(
Expr::String(self.line.clone()),
Span::unknown(),
Type::String,
)),
Argument::Positional(Expression::new_unknown(
Expr::Int(line_pos as i64),
Span::unknown(),
Type::Int,
)),
],
arguments: Spanned {
item: vec![
Argument::Positional(Expression::new_unknown(
Expr::String(self.line.clone()),
Span::unknown(),
Type::String,
)),
Argument::Positional(Expression::new_unknown(
Expr::Int(line_pos as i64),
Span::unknown(),
Type::Int,
)),
],
span: Span::unknown(),
},
parser_info: HashMap::new(),
},
PipelineData::empty(),

View file

@ -400,7 +400,7 @@ fn find_matching_block_end_in_expr(
}
}
Expr::Call(call) => call.arguments.iter().find_map(|arg| {
Expr::Call(call) => call.arguments.item.iter().find_map(|arg| {
arg.expr().and_then(|expr| {
find_matching_block_end_in_expr(
line,

View file

@ -49,7 +49,7 @@ impl Command for BytesBuild {
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let mut output = vec![];
for val in call.rest_iter_flattened(0, |expr| {
for val in call.rest_iter_flattened(&engine_state, 0, |expr| {
let eval_expression = get_eval_expression(engine_state);
eval_expression(engine_state, stack, expr)
})? {

View file

@ -111,11 +111,11 @@ fn get_arguments(
) -> Vec<Value> {
let mut arg_value = vec![];
let span = Span::test_data();
for arg in &call.arguments {
for arg in &call.arguments.item {
match arg {
// I think the second argument to Argument::Named is the short name, but I'm not really sure.
// Please fix it if it's wrong. :)
Argument::Named((name, short, opt_expr)) => {
Argument::Named((name, short, opt_expr, _)) => {
let arg_type = "named";
let arg_value_name = name.item.clone();
let arg_value_name_span_start = name.span.start as i64;

View file

@ -104,7 +104,7 @@ confusing the id/parent_id hierarchy. The --expr flag is helpful for investigati
collect_values,
collect_exprs,
collect_lines,
call.span(),
call.span(&engine_state),
);
let lock_err = |_| ShellError::GenericError {
@ -125,12 +125,12 @@ confusing the id/parent_id hierarchy. The --expr flag is helpful for investigati
let pipeline_data = result?;
// Collect the output
let _ = pipeline_data.into_value(call.span());
let _ = pipeline_data.into_value(call.span(&engine_state));
Ok(engine_state
.deactivate_debugger()
.map_err(lock_err)?
.report(engine_state, call.span())?
.report(engine_state, call.span(&engine_state))?
.into_pipeline_data())
}

View file

@ -177,7 +177,12 @@ impl Command for Open {
let block = engine_state.get_block(block_id);
eval_block(engine_state, stack, block, stream)
} else {
decl.run(engine_state, stack, &Call::new(call_span), stream)
decl.run(
engine_state,
stack,
&Call::new(call_span, call.arguments_span()),
stream,
)
};
output.push(command_output.map_err(|inner| {
ShellError::GenericError{

View file

@ -398,7 +398,7 @@ fn convert_to_extension(
let eval_block = get_eval_block(engine_state);
eval_block(engine_state, stack, block, input)
} else {
decl.run(engine_state, stack, &Call::new(span), input)
decl.run(engine_state, stack, &Call::new(span, span.past()), input)
}
} else {
Ok(input)

View file

@ -103,7 +103,7 @@ pub fn get_rest_for_glob_pattern(
let mut output = vec![];
let eval_expression = get_eval_expression(engine_state);
for result in call.rest_iter_flattened(starting_pos, |expr| {
for result in call.rest_iter_flattened(&engine_state, starting_pos, |expr| {
let result = eval_expression(engine_state, stack, expr);
match result {
Err(e) => Err(e),

View file

@ -141,7 +141,7 @@ fn from_csv(
} else {
return Err(ShellError::NonUtf8Custom {
msg: "separator should be a single char or a 4-byte unicode".into(),
span: call.span(),
span: call.span(&engine_state),
});
}
}

View file

@ -3,6 +3,7 @@ use nu_color_config::StyleComputer;
use nu_engine::command_prelude::*;
use nu_protocol::ast::{Expr, Expression};
use nu_protocol::engine::UNKNOWN_SPAN_ID;
use std::collections::VecDeque;
#[derive(Clone)]
@ -143,7 +144,7 @@ pub fn cal(
style_computer,
)?;
let mut table_no_index = Call::new(Span::unknown());
let mut table_no_index = Call::new(Span::unknown(), Span::unknown());
table_no_index.add_named((
Spanned {
item: "index".to_string(),
@ -155,6 +156,7 @@ pub fn cal(
Span::unknown(),
Type::Bool,
)),
UNKNOWN_SPAN_ID,
));
let cal_table_output =

View file

@ -412,7 +412,7 @@ fn display(help: &str, engine_state: &EngineState, stack: &mut Stack, span: Span
let result = decl.run(
engine_state,
stack,
&Call::new(span),
&Call::new(span, span.past()),
Value::string(item, Span::unknown()).into_pipeline_data(),
);

View file

@ -547,7 +547,7 @@ fn transform_response_using_content_type(
Some(converter_id) => engine_state.get_decl(converter_id).run(
engine_state,
stack,
&Call::new(span),
&Call::new(span, span.past()),
output,
),
None => Ok(output),

View file

@ -58,7 +58,12 @@ impl Command for IsTerminal {
_ => {
return Err(ShellError::IncompatibleParametersSingle {
msg: "Only one stream may be checked".into(),
span: Span::merge_many(call.arguments.iter().map(|arg| arg.span())),
span: Span::merge_many(
call.arguments
.item
.iter()
.map(|arg| arg.span(&engine_state)),
),
});
}
};

View file

@ -375,8 +375,12 @@ fn write_pipeline_data(
Arc::make_mut(&mut engine_state.config).use_ansi_coloring = false;
// Invoke the `table` command.
let output =
crate::Table.run(&engine_state, &mut stack, &Call::new(Span::unknown()), data)?;
let output = crate::Table.run(
&engine_state,
&mut stack,
&Call::new(Span::unknown(), Span::unknown()),
data,
)?;
// Write the output.
for value in output {

View file

@ -127,7 +127,7 @@ impl Command for Table {
return Ok(val.into_pipeline_data());
}
let cfg = parse_table_config(call, engine_state, stack)?;
let cfg = parse_table_config(engine_state, call, engine_state, stack)?;
let input = CmdInput::new(engine_state, stack, call, input);
// reset vt processing, aka ansi because illbehaved externals can break it
@ -247,6 +247,7 @@ impl TableConfig {
}
fn parse_table_config(
engine_state: &EngineState,
call: &Call,
state: &EngineState,
stack: &mut Stack,
@ -269,9 +270,9 @@ fn parse_table_config(
flatten_separator,
},
};
let theme =
get_theme_flag(call, state, stack)?.unwrap_or_else(|| get_config(state, stack).table_mode);
let index = get_index_flag(call, state, stack)?;
let theme = get_theme_flag(engine_state, call, state, stack)?
.unwrap_or_else(|| get_config(state, stack).table_mode);
let index = get_index_flag(engine_state, call, state, stack)?;
let term_width = get_width_param(width_param);
@ -285,6 +286,7 @@ fn parse_table_config(
}
fn get_index_flag(
engine_state: &EngineState,
call: &Call,
state: &EngineState,
stack: &mut Stack,
@ -308,7 +310,7 @@ fn get_index_flag(
Err(ShellError::UnsupportedInput {
msg: String::from("got a negative integer"),
input: val.to_string(),
msg_span: call.span(),
msg_span: call.span(&engine_state),
input_span: internal_span,
})
} else {
@ -319,13 +321,14 @@ fn get_index_flag(
_ => Err(ShellError::CantConvert {
to_type: String::from("index"),
from_type: String::new(),
span: call.span(),
span: call.span(&engine_state),
help: Some(String::from("supported values: [bool, int, nothing]")),
}),
}
}
fn get_theme_flag(
engine_state: &EngineState,
call: &Call,
state: &EngineState,
stack: &mut Stack,
@ -335,7 +338,7 @@ fn get_theme_flag(
TableMode::from_str(&theme).map_err(|err| ShellError::CantConvert {
to_type: String::from("theme"),
from_type: String::from("string"),
span: call.span(),
span: call.span(&engine_state),
help: Some(format!("{}, but found '{}'.", err, theme)),
})
})

View file

@ -113,7 +113,7 @@ impl CallExt for Call {
let stack = &mut stack.use_call_arg_out_dest();
let mut output = vec![];
for result in self.rest_iter_flattened(starting_pos, |expr| {
for result in self.rest_iter_flattened(&engine_state, starting_pos, |expr| {
eval_expression::<WithoutDebug>(engine_state, stack, expr)
})? {
output.push(FromValue::from_value(result)?);

View file

@ -48,7 +48,7 @@ fn nu_highlight_string(code_string: &str, engine_state: &EngineState, stack: &mu
if let Ok(output) = decl.run(
engine_state,
stack,
&Call::new(Span::unknown()),
&Call::new(Span::unknown(), Span::unknown()),
Value::string(code_string, Span::unknown()).into_pipeline_data(),
) {
let result = output.into_value(Span::unknown());
@ -241,7 +241,10 @@ fn get_documentation(
&Call {
decl_id,
head: span,
arguments: vec![],
arguments: Spanned {
item: vec![],
span: span.past(),
},
parser_info: HashMap::new(),
},
PipelineData::Value(Value::list(vals, span), None),
@ -273,7 +276,7 @@ fn get_documentation(
match decl.run(
engine_state,
stack,
&Call::new(Span::unknown()),
&Call::new(Span::unknown(), Span::unknown()),
Value::string(example.example, Span::unknown()).into_pipeline_data(),
) {
Ok(output) => {
@ -296,7 +299,7 @@ fn get_documentation(
}
if let Some(result) = &example.result {
let mut table_call = Call::new(Span::unknown());
let mut table_call = Call::new(Span::unknown(), Span::unknown());
if example.example.ends_with("--collapse") {
// collapse the result
table_call.add_named((
@ -306,6 +309,7 @@ fn get_documentation(
},
None,
None,
UNKNOWN_SPAN_ID,
))
} else {
// expand the result
@ -316,6 +320,7 @@ fn get_documentation(
},
None,
None,
UNKNOWN_SPAN_ID,
))
}
let table = engine_state
@ -368,13 +373,17 @@ fn get_ansi_color_for_component_or_default(
// Call ansi command using argument
if let Some(argument) = argument_opt {
if let Some(decl_id) = engine_state.find_decl(b"ansi", &[]) {
let arg_span = argument.span(&engine_state);
if let Ok(result) = eval_call::<WithoutDebug>(
engine_state,
caller_stack,
&Call {
decl_id,
head: span,
arguments: vec![argument],
arguments: Spanned {
item: vec![argument],
span: arg_span,
},
parser_info: HashMap::new(),
},
PipelineData::Empty,

View file

@ -26,7 +26,7 @@ pub fn eval_call<D: DebugContext>(
}
let decl = engine_state.get_decl(call.decl_id);
if !decl.is_known_external() && call.named_iter().any(|(flag, _, _)| flag.item == "help") {
if !decl.is_known_external() && call.named_iter().any(|(flag, _, _, _)| flag.item == "help") {
let help = get_full_help(decl, engine_state, caller_stack);
Ok(Value::string(help, call.head).into_pipeline_data())
} else if let Some(block_id) = decl.block_id() {
@ -100,6 +100,7 @@ pub fn eval_call<D: DebugContext>(
let mut rest_items = vec![];
for result in call.rest_iter_flattened(
&engine_state,
decl.signature().required_positional.len()
+ decl.signature().optional_positional.len(),
|expr| eval_expression::<D>(engine_state, caller_stack, expr),
@ -213,8 +214,16 @@ fn eval_external(
})?;
let command = engine_state.get_decl(decl_id);
let spans: Vec<Span> = args
.iter()
.map(|arg| match arg {
ExternalArgument::Regular(expr) | ExternalArgument::Spread(expr) => {
expr.span(&engine_state)
}
})
.collect();
let mut call = Call::new(head.span(&engine_state));
let mut call = Call::new(head.span(&engine_state), Span::concat(&spans));
call.add_positional(head.clone());

View file

@ -252,7 +252,7 @@ fn flatten_expression_into(
}
let arg_start = output.len();
for arg in &call.arguments {
for arg in &call.arguments.item {
match arg {
Argument::Positional(positional) | Argument::Unknown(positional) => {
flatten_expression_into(working_set, positional, output)

View file

@ -43,7 +43,7 @@ impl Command for KnownExternal {
let command = engine_state.get_decl(decl_id);
let mut extern_call = Call::new(head_span);
let mut extern_call = Call::new(head_span, call.arguments_span());
let extern_name = if let Some(name_bytes) = engine_state.find_decl_name(call.decl_id, &[]) {
String::from_utf8_lossy(name_bytes)
@ -78,7 +78,7 @@ impl Command for KnownExternal {
));
}
for arg in &call.arguments {
for arg in &call.arguments.item {
match arg {
Argument::Positional(positional) => extern_call.add_positional(positional.clone()),
Argument::Named(named) => {

View file

@ -21,8 +21,9 @@ pub use nu_protocol::parser_path::*;
pub use parse_keywords::*;
pub use parser::{
is_math_expression_like, parse, parse_block, parse_expression, parse_external_call,
parse_unit_value, trim_quotes, trim_quotes_str, unescape_unquote_string, DURATION_UNIT_GROUPS,
is_math_expression_like, named_arg_span, parse, parse_block, parse_expression,
parse_external_call, parse_unit_value, trim_quotes, trim_quotes_str, unescape_unquote_string,
DURATION_UNIT_GROUPS,
};
#[cfg(feature = "plugin")]

View file

@ -123,8 +123,8 @@ pub fn parse_keyword(working_set: &mut StateWorkingSet, lite_command: &LiteComma
// Apply parse keyword side effects
let cmd = working_set.get_decl(call.decl_id);
// check help flag first.
if call.named_iter().any(|(flag, _, _)| flag.item == "help") {
let call_span = call.span();
if call.named_iter().any(|(flag, _, _, _)| flag.item == "help") {
let call_span = call.span(working_set);
return Pipeline::from_vec(vec![Expression::new(
working_set,
Expr::Call(call),
@ -1056,7 +1056,7 @@ pub fn parse_alias(
// First from comments, if any are present
false => working_set.build_usage(&lite_command.comments),
// Then from the command itself
true => match alias_call.arguments.get(1) {
true => match alias_call.arguments.item.get(1) {
Some(Argument::Positional(Expression {
expr: Expr::Keyword(kw),
..
@ -1258,7 +1258,10 @@ pub fn parse_export_in_module(
let mut call = Box::new(Call {
head: spans[0],
decl_id: export_decl_id,
arguments: vec![],
arguments: Spanned {
item: vec![],
span: spans[0].past(),
},
parser_info: HashMap::new(),
});
@ -2263,13 +2266,21 @@ pub fn parse_module(
.find_decl(b"module")
.expect("internal error: missing module command");
let args_span = Span::concat(&[
module_name_or_path_expr.span(working_set),
block_expr.span(working_set),
]);
let call = Box::new(Call {
head: Span::concat(&spans[..split_id]),
decl_id: module_decl_id,
arguments: vec![
Argument::Positional(module_name_or_path_expr),
Argument::Positional(block_expr),
],
arguments: Spanned {
item: vec![
Argument::Positional(module_name_or_path_expr),
Argument::Positional(block_expr),
],
span: args_span,
},
parser_info: HashMap::new(),
});
@ -2701,7 +2712,7 @@ pub fn parse_hide(working_set: &mut StateWorkingSet, lite_command: &LiteCommand)
}
pub fn parse_overlay_new(working_set: &mut StateWorkingSet, call: Box<Call>) -> Pipeline {
let call_span = call.span();
let call_span = call.span(working_set);
let (overlay_name, _) = if let Some(expr) = call.positional_nth(0) {
match eval_constant(working_set, expr) {
@ -2750,7 +2761,7 @@ pub fn parse_overlay_new(working_set: &mut StateWorkingSet, call: Box<Call>) ->
}
pub fn parse_overlay_use(working_set: &mut StateWorkingSet, call: Box<Call>) -> Pipeline {
let call_span = call.span();
let call_span = call.span(working_set);
let (overlay_name, overlay_name_span) = if let Some(expr) = call.positional_nth(0) {
match eval_constant(working_set, expr) {
@ -2973,7 +2984,7 @@ pub fn parse_overlay_use(working_set: &mut StateWorkingSet, call: Box<Call>) ->
}
pub fn parse_overlay_hide(working_set: &mut StateWorkingSet, call: Box<Call>) -> Pipeline {
let call_span = call.span();
let call_span = call.span(working_set);
let (overlay_name, overlay_name_span) = if let Some(expr) = call.positional_nth(0) {
match eval_constant(working_set, expr) {
@ -3113,10 +3124,16 @@ pub fn parse_let(working_set: &mut StateWorkingSet, spans: &[Span]) -> Pipeline
}
}
let args_span =
Span::concat(&[lvalue.span(working_set), rvalue.span(working_set)]);
let call = Box::new(Call {
decl_id,
head: spans[0],
arguments: vec![Argument::Positional(lvalue), Argument::Positional(rvalue)],
arguments: Spanned {
item: vec![Argument::Positional(lvalue), Argument::Positional(rvalue)],
span: args_span,
},
parser_info: HashMap::new(),
});
@ -3259,10 +3276,16 @@ pub fn parse_const(working_set: &mut StateWorkingSet, spans: &[Span]) -> Pipelin
}
}
let args_span =
Span::concat(&[lvalue.span(working_set), rvalue.span(working_set)]);
let call = Box::new(Call {
decl_id,
head: spans[0],
arguments: vec![Argument::Positional(lvalue), Argument::Positional(rvalue)],
arguments: Spanned {
item: vec![Argument::Positional(lvalue), Argument::Positional(rvalue)],
span: args_span,
},
parser_info: HashMap::new(),
});
@ -3378,10 +3401,16 @@ pub fn parse_mut(working_set: &mut StateWorkingSet, spans: &[Span]) -> Pipeline
}
}
let args_span =
Span::concat(&[lvalue.span(working_set), rvalue.span(working_set)]);
let call = Box::new(Call {
decl_id,
head: spans[0],
arguments: vec![Argument::Positional(lvalue), Argument::Positional(rvalue)],
arguments: Spanned {
item: vec![Argument::Positional(lvalue), Argument::Positional(rvalue)],
span: args_span,
},
parser_info: HashMap::new(),
});
@ -3884,8 +3913,8 @@ pub fn parse_plugin_use(working_set: &mut StateWorkingSet, call: Box<Call>) -> P
let plugin_config = call
.named_iter()
.find(|(arg_name, _, _)| arg_name.item == "plugin-config")
.map(|(_, _, expr)| {
.find(|(arg_name, _, _, _)| arg_name.item == "plugin-config")
.map(|(_, _, expr, _)| {
let expr = expr
.as_ref()
.expect("--plugin-config arg should have been checked already");
@ -3961,7 +3990,7 @@ pub fn parse_plugin_use(working_set: &mut StateWorkingSet, call: Box<Call>) -> P
working_set.error(err);
}
let call_span = call.span();
let call_span = call.span(working_set);
Pipeline::from_vec(vec![Expression::new(
working_set,
@ -4147,6 +4176,6 @@ fn detect_params_in_name(
/// Run has_flag_const and push possible error to working_set
fn has_flag_const(working_set: &mut StateWorkingSet, call: &Call, name: &str) -> Result<bool, ()> {
call.has_flag_const(working_set, name).map_err(|err| {
working_set.error(err.wrap(working_set, call.span()));
working_set.error(err.wrap(working_set, call.span(working_set)));
})
}

View file

@ -160,7 +160,7 @@ pub(crate) fn check_call(
call: &Call,
) {
// Allow the call to pass if they pass in the help flag
if call.named_iter().any(|(n, _, _)| n.item == "help") {
if call.named_iter().any(|(n, _, _, _)| n.item == "help") {
return;
}
@ -180,7 +180,7 @@ pub(crate) fn check_call(
if let Some(last) = call.positional_iter().last() {
working_set.error(ParseError::MissingPositional(
argument.name.clone(),
Span::new(last.span.end, last.span.end),
Span::new(last.span(working_set).end, last.span(working_set).end),
sig.call_signature(),
));
return;
@ -199,7 +199,7 @@ pub(crate) fn check_call(
if let Some(last) = call.positional_iter().last() {
working_set.error(ParseError::MissingPositional(
missing.name.clone(),
Span::new(last.span.end, last.span.end),
Span::new(last.span(working_set).end, last.span(working_set).end),
sig.call_signature(),
))
} else {
@ -211,7 +211,10 @@ pub(crate) fn check_call(
}
} else {
for req_flag in sig.named.iter().filter(|x| x.required) {
if call.named_iter().all(|(n, _, _)| n.item != req_flag.long) {
if call
.named_iter()
.all(|(n, _, _, _)| n.item != req_flag.long)
{
working_set.error(ParseError::MissingRequiredFlag(
req_flag.long.clone(),
command,
@ -486,18 +489,19 @@ fn ensure_flag_arg_type(
arg_shape: &SyntaxShape,
long_name_span: Span,
) -> (Spanned<String>, Expression) {
let arg_span = arg.span(working_set);
if !type_compatible(&arg.ty, &arg_shape.to_type()) {
working_set.error(ParseError::TypeMismatch(
arg_shape.to_type(),
arg.ty,
arg.span,
arg_span,
));
(
Spanned {
item: arg_name,
span: long_name_span,
},
Expression::garbage(working_set, arg.span),
Expression::garbage(working_set, arg_span),
)
} else {
(
@ -916,6 +920,25 @@ pub struct ParsedInternalCall {
pub output: Type,
}
// moved from call.rs since span creation must be done at parse time now
pub fn named_arg_span(
working_set: &StateWorkingSet,
named: &Spanned<String>,
short: &Option<Spanned<String>>,
expr: &Option<Expression>,
) -> Span {
let start = named.span.start;
let end = if let Some(expr) = expr {
expr.span(&working_set).end
} else if let Some(short) = short {
short.span.end
} else {
named.span.end
};
Span::new(start, end)
}
pub fn parse_internal_call(
working_set: &mut StateWorkingSet,
command_span: Span,
@ -924,7 +947,7 @@ pub fn parse_internal_call(
) -> ParsedInternalCall {
trace!("parsing: internal call (decl id: {})", decl_id);
let mut call = Call::new(command_span);
let mut call = Call::new(command_span, Span::concat(spans));
call.decl_id = decl_id;
call.head = command_span;
let _ = working_set.add_span(call.head);
@ -1002,7 +1025,9 @@ pub fn parse_internal_call(
call.add_unknown(arg);
} else {
call.add_named((long_name, None, arg));
let named_span = named_arg_span(working_set, &long_name, &None, &arg);
let named_span_id = working_set.add_span(named_span);
call.add_named((long_name, None, arg, named_span_id));
}
spans_idx += 1;
@ -1053,27 +1078,30 @@ pub fn parse_internal_call(
if flag.long.is_empty() {
if let Some(short) = flag.short {
call.add_named((
Spanned {
item: String::new(),
span: spans[spans_idx],
},
Some(Spanned {
item: short.to_string(),
span: spans[spans_idx],
}),
Some(arg),
));
let named = Spanned {
item: String::new(),
span: spans[spans_idx],
};
let short = Some(Spanned {
item: short.to_string(),
span: spans[spans_idx],
});
let expr = Some(arg);
let named_span =
named_arg_span(working_set, &named, &short, &expr);
let named_span_id = working_set.add_span(named_span);
call.add_named((named, short, expr, named_span_id));
}
} else {
call.add_named((
Spanned {
item: flag.long.clone(),
span: spans[spans_idx],
},
None,
Some(arg),
));
let named = Spanned {
item: flag.long.clone(),
span: spans[spans_idx],
};
let short = None;
let expr = Some(arg);
let named_span = named_arg_span(working_set, &named, &short, &expr);
let named_span_id = working_set.add_span(named_span);
call.add_named((named, short, expr, named_span_id));
}
spans_idx += 1;
} else {
@ -1084,27 +1112,29 @@ pub fn parse_internal_call(
}
} else if flag.long.is_empty() {
if let Some(short) = flag.short {
call.add_named((
Spanned {
item: String::new(),
span: spans[spans_idx],
},
Some(Spanned {
item: short.to_string(),
span: spans[spans_idx],
}),
None,
));
let named = Spanned {
item: String::new(),
span: spans[spans_idx],
};
let short = Some(Spanned {
item: short.to_string(),
span: spans[spans_idx],
});
let expr = None;
let named_span = named_arg_span(working_set, &named, &short, &expr);
let named_span_id = working_set.add_span(named_span);
call.add_named((named, short, expr, named_span_id));
}
} else {
call.add_named((
Spanned {
item: flag.long.clone(),
span: spans[spans_idx],
},
None,
None,
));
let named = Spanned {
item: flag.long.clone(),
span: spans[spans_idx],
};
let short = None;
let expr = None;
let named_span = named_arg_span(working_set, &named, &short, &expr);
let named_span_id = working_set.add_span(named_span);
call.add_named((named, short, expr, named_span_id));
}
}
}
@ -1184,12 +1214,13 @@ pub fn parse_internal_call(
);
let arg = if !type_compatible(&positional.shape.to_type(), &arg.ty) {
let arg_ty = arg.ty.clone();
working_set.error(ParseError::TypeMismatch(
positional.shape.to_type(),
arg.ty,
arg.span,
arg_ty,
arg.span(working_set),
));
Expression::garbage(working_set, arg.span)
Expression::garbage(working_set, arg.span(working_set))
} else {
arg
};
@ -1311,7 +1342,7 @@ pub fn parse_call(working_set: &mut StateWorkingSet, spans: &[Span], head: Span)
trace!("parsing: alias of external call");
let mut head = head.clone();
head.span = spans[0]; // replacing the spans preserves syntax highlighting
head.span_id = working_set.add_span(spans[0]); // replacing the spans preserves syntax highlighting
let mut final_args = args.clone().into_vec();
for arg_span in &spans[1..] {
@ -3006,13 +3037,15 @@ pub fn parse_import_pattern(working_set: &mut StateWorkingSet, spans: &[Span]) -
for item in list {
match item {
ListItem::Item(expr) => {
let contents = working_set.get_span_contents(expr.span);
output.push((trim_quotes(contents).to_vec(), expr.span));
let contents =
working_set.get_span_contents(expr.span(working_set));
output
.push((trim_quotes(contents).to_vec(), expr.span(working_set)));
}
ListItem::Spread(_, spread) => {
working_set.error(ParseError::WrongImportPattern(
"cannot spread in an import pattern".into(),
spread.span,
spread.span(working_set),
))
}
}
@ -3022,7 +3055,7 @@ pub fn parse_import_pattern(working_set: &mut StateWorkingSet, spans: &[Span]) -
.members
.push(ImportPatternMember::List { names: output });
} else {
working_set.error(ParseError::ExportNotFound(result.span));
working_set.error(ParseError::ExportNotFound(result.span(working_set)));
return Expression::new(
working_set,
Expr::ImportPattern(Box::new(import_pattern)),
@ -3779,7 +3812,7 @@ pub fn parse_signature_helper(working_set: &mut StateWorkingSet, span: Span) ->
format!(
"expected default value to be `{var_type}`"
),
expression.span,
expression.span(working_set),
),
)
}
@ -3792,7 +3825,7 @@ pub fn parse_signature_helper(working_set: &mut StateWorkingSet, span: Span) ->
Some(constant)
} else {
working_set.error(ParseError::NonConstantDefaultValue(
expression.span,
expression.span(working_set),
));
None
};
@ -3806,7 +3839,7 @@ pub fn parse_signature_helper(working_set: &mut StateWorkingSet, span: Span) ->
working_set.error(ParseError::AssignmentMismatch(
"Rest parameter was given a default value".into(),
"can't have default value".into(),
expression.span,
expression.span(working_set),
))
}
Arg::Flag {
@ -3819,7 +3852,7 @@ pub fn parse_signature_helper(working_set: &mut StateWorkingSet, span: Span) ->
},
type_annotated,
} => {
let expression_span = expression.span;
let expression_span = expression.span(working_set);
*default_value = if let Ok(value) =
eval_constant(working_set, &expression)
@ -4065,7 +4098,7 @@ fn parse_table_row(
list.into_iter()
.map(|item| match item {
ListItem::Item(expr) => Ok(expr),
ListItem::Spread(_, spread) => Err(spread.span),
ListItem::Spread(_, spread) => Err(spread.span(working_set)),
})
.collect::<Result<_, _>>()
.map(|exprs| (exprs, span))
@ -4140,7 +4173,7 @@ fn parse_table_expression(working_set: &mut StateWorkingSet, span: Span) -> Expr
}
Ordering::Greater => {
let span = {
let start = list[head.len()].span.start;
let start = list[head.len()].span(working_set).start;
let end = span.end;
Span::new(start, end)
};
@ -4179,7 +4212,7 @@ fn parse_table_expression(working_set: &mut StateWorkingSet, span: Span) -> Expr
};
let ty = if working_set.parse_errors.len() == errors {
let (ty, errs) = table_type(&head, &rows);
let (ty, errs) = table_type(working_set, &head, &rows);
working_set.parse_errors.extend(errs);
ty
} else {
@ -4194,7 +4227,11 @@ fn parse_table_expression(working_set: &mut StateWorkingSet, span: Span) -> Expr
Expression::new(working_set, Expr::Table(table), span, ty)
}
fn table_type(head: &[Expression], rows: &[Vec<Expression>]) -> (Type, Vec<ParseError>) {
fn table_type(
working_set: &StateWorkingSet,
head: &[Expression],
rows: &[Vec<Expression>],
) -> (Type, Vec<ParseError>) {
let mut errors = vec![];
let mut rows = rows.to_vec();
let mut mk_ty = || -> Type {
@ -4224,7 +4261,7 @@ fn table_type(head: &[Expression], rows: &[Vec<Expression>]) -> (Type, Vec<Parse
if let Some(str) = expr.as_string() {
str
} else {
errors.push(mk_error(expr.span));
errors.push(mk_error(expr.span(&working_set)));
String::from("{ column }")
}
})
@ -5115,7 +5152,7 @@ pub fn parse_math_expression(
working_set.error(err);
}
let op_span = Span::append(lhs.span, rhs.span);
let op_span = Span::append(lhs.span(working_set), rhs.span(working_set));
expr_stack.push(Expression::new(
working_set,
Expr::BinaryOp(Box::new(lhs), Box::new(op), Box::new(rhs)),
@ -5151,7 +5188,7 @@ pub fn parse_math_expression(
working_set.error(err)
}
let binary_op_span = Span::append(lhs.span, rhs.span);
let binary_op_span = Span::append(lhs.span(working_set), rhs.span(working_set));
expr_stack.push(Expression::new(
working_set,
Expr::BinaryOp(Box::new(lhs), Box::new(op), Box::new(rhs)),
@ -5332,7 +5369,10 @@ pub fn parse_expression(working_set: &mut StateWorkingSet, spans: &[Span]) -> Ex
let expr = Expr::Call(Box::new(Call {
head: Span::unknown(),
decl_id,
arguments,
arguments: Spanned {
item: arguments,
span: Span::concat(spans),
},
parser_info: HashMap::new(),
}));
@ -6094,7 +6134,7 @@ pub fn discover_captures_in_expr(
}
}
for arg in &call.arguments {
for arg in &call.arguments.item {
match arg {
Argument::Named(named) => {
if let Some(arg) = &named.2 {
@ -6249,7 +6289,7 @@ pub fn discover_captures_in_expr(
}
Expr::Var(var_id) => {
if (*var_id > ENV_VARIABLE_ID || *var_id == IN_VARIABLE_ID) && !seen.contains(var_id) {
output.push((*var_id, expr.span));
output.push((*var_id, expr.span(&working_set)));
}
}
Expr::VarDecl(var_id) => {
@ -6294,7 +6334,7 @@ fn wrap_element_with_collect(
}
fn wrap_expr_with_collect(working_set: &mut StateWorkingSet, expr: &Expression) -> Expression {
let span = expr.span;
let span = expr.span(working_set);
if let Some(decl_id) = working_set.find_decl(b"collect") {
let mut output = vec![];
@ -6324,14 +6364,15 @@ fn wrap_expr_with_collect(working_set: &mut StateWorkingSet, expr: &Expression)
Type::Any,
)));
output.push(Argument::Named((
Spanned {
item: "keep-env".to_string(),
span: Span::new(0, 0),
},
None,
None,
)));
let named = Spanned {
item: "keep-env".to_string(),
span: Span::unknown(),
};
let short = None;
let expr = None;
let named_span = named_arg_span(working_set, &named, &short, &expr);
let named_span_id = working_set.add_span(named_span);
output.push(Argument::Named((named, short, expr, named_span_id)));
// The containing, synthetic call to `collect`.
// We don't want to have a real span as it will confuse flattening
@ -6339,8 +6380,11 @@ fn wrap_expr_with_collect(working_set: &mut StateWorkingSet, expr: &Expression)
Expression::new(
working_set,
Expr::Call(Box::new(Call {
head: Span::new(0, 0),
arguments: output,
head: Span::unknown(),
arguments: Spanned {
item: output,
span: Span::unknown(),
},
decl_id,
parser_info: HashMap::new(),
})),

View file

@ -598,9 +598,12 @@ pub fn parse_call_short_flag_batch_arg_allowed() {
if let Expr::Call(call) = &element.expr.expr {
assert_eq!(call.decl_id, 0);
assert_eq!(call.arguments.len(), 2);
matches!(call.arguments[0], Argument::Named((_, None, None)));
matches!(call.arguments[1], Argument::Named((_, None, Some(_))));
assert_eq!(call.arguments.item.len(), 2);
matches!(call.arguments.item[0], Argument::Named((_, None, None, _)));
matches!(
call.arguments.item[1],
Argument::Named((_, None, Some(_), _))
);
}
}
@ -2150,7 +2153,7 @@ mod input_types {
assert!(pipeline.elements[3].redirection.is_none());
match &pipeline.elements[3].expr.expr {
Expr::Call(call) => {
let arg = &call.arguments[0];
let arg = &call.arguments.item[0];
match arg {
Argument::Positional(a) => match &a.expr {
Expr::FullCellPath(path) => match &path.head.expr {
@ -2347,3 +2350,92 @@ mod operator {
);
}
}
#[cfg(test)]
mod test {
use nu_parser::named_arg_span;
use nu_protocol::ast::{Argument, Call, Expression};
use nu_protocol::engine::{EngineState, StateWorkingSet};
use nu_protocol::{Span, Spanned};
#[test]
fn argument_span_named() {
let engine_state = EngineState::new();
let mut working_set = StateWorkingSet::new(&engine_state);
let named_span = Span::new(2, 3);
let short_span = Span::new(5, 7);
let expr_span = Span::new(11, 13);
let _ = working_set.add_span(named_span);
let _ = working_set.add_span(short_span);
let _ = working_set.add_span(expr_span);
let named = Spanned {
item: "named".to_string(),
span: named_span,
};
let short = Spanned {
item: "short".to_string(),
span: short_span,
};
let expr = Expression::garbage(&mut working_set, expr_span);
let arg_span = named_arg_span(&working_set, &named, &None, &None);
assert_eq!(Span::new(2, 3), arg_span);
let arg_span = named_arg_span(&working_set, &named, &Some(short.clone()), &None);
assert_eq!(Span::new(2, 7), arg_span);
let arg_span = named_arg_span(&working_set, &named, &None, &Some(expr.clone()));
assert_eq!(Span::new(2, 13), arg_span);
let arg_span = named_arg_span(
&working_set,
&named,
&Some(short.clone()),
&Some(expr.clone()),
);
assert_eq!(Span::new(2, 13), arg_span);
}
#[test]
fn argument_span_positional() {
let engine_state = EngineState::new();
let mut working_set = StateWorkingSet::new(&engine_state);
let span = Span::new(2, 3);
let _ = working_set.add_span(span);
let expr = Expression::garbage(&mut working_set, span);
let arg = Argument::Positional(expr);
assert_eq!(span, arg.span(&working_set));
}
#[test]
fn argument_span_unknown() {
let engine_state = EngineState::new();
let mut working_set = StateWorkingSet::new(&engine_state);
let span = Span::new(2, 3);
let _ = working_set.add_span(span);
let expr = Expression::garbage(&mut working_set, span);
let arg = Argument::Unknown(expr);
assert_eq!(span, arg.span(&working_set));
}
#[test]
fn call_arguments_span() {
let engine_state = EngineState::new();
let mut working_set = StateWorkingSet::new(&engine_state);
let arg1_span = Span::new(2, 3);
let arg2_span = Span::new(5, 7);
let mut call = Call::new(Span::new(0, 1), Span::concat(&[arg1_span, arg2_span]));
call.add_positional(Expression::garbage(&mut working_set, arg1_span));
call.add_positional(Expression::garbage(&mut working_set, arg2_span));
assert_eq!(Span::new(2, 7), call.arguments_span());
}
}

View file

@ -34,11 +34,12 @@ impl EvaluatedCall {
stack: &mut Stack,
eval_expression_fn: fn(&EngineState, &mut Stack, &Expression) -> Result<Value, ShellError>,
) -> Result<Self, ShellError> {
let positional =
call.rest_iter_flattened(0, |expr| eval_expression_fn(engine_state, stack, expr))?;
let positional = call.rest_iter_flattened(&engine_state, 0, |expr| {
eval_expression_fn(engine_state, stack, expr)
})?;
let mut named = Vec::with_capacity(call.named_len());
for (string, _, expr) in call.named_iter() {
for (string, _, expr, _) in call.named_iter() {
let value = match expr {
None => None,
Some(expr) => Some(eval_expression_fn(engine_state, stack, expr)?),

View file

@ -4,42 +4,47 @@ use serde::{Deserialize, Serialize};
use crate::{
ast::Expression, engine::StateWorkingSet, eval_const::eval_constant, DeclId, FromValue,
ShellError, Span, Spanned, Value,
GetSpan, ShellError, Span, SpanId, Spanned, Value,
};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Argument {
Positional(Expression),
Named((Spanned<String>, Option<Spanned<String>>, Option<Expression>)),
Named(
(
Spanned<String>,
Option<Spanned<String>>,
Option<Expression>,
SpanId,
),
),
Unknown(Expression), // unknown argument used in "fall-through" signatures
Spread(Expression), // a list spread to fill in rest arguments
}
impl Argument {
/// The span for an argument
pub fn span(&self) -> Span {
pub fn span_id(&self) -> SpanId {
match self {
Argument::Positional(e) => e.span,
Argument::Named((named, short, expr)) => {
let start = named.span.start;
let end = if let Some(expr) = expr {
expr.span.end
} else if let Some(short) = short {
short.span.end
} else {
named.span.end
};
Argument::Positional(e) => e.span_id,
Argument::Named((_, _, _, span_id)) => *span_id,
Argument::Unknown(e) => e.span_id,
Argument::Spread(e) => e.span_id,
}
}
Span::new(start, end)
}
Argument::Unknown(e) => e.span,
Argument::Spread(e) => e.span,
/// The span for an argument
pub fn span(&self, state: &impl GetSpan) -> Span {
match self {
Argument::Positional(e) => e.span(state),
Argument::Named((_, _, _, span_id)) => state.get_span(*span_id),
Argument::Unknown(e) => e.span(state),
Argument::Spread(e) => e.span(state),
}
}
pub fn expr(&self) -> Option<&Expression> {
match self {
Argument::Named((_, _, expr)) => expr.as_ref(),
Argument::Named((_, _, expr, _)) => expr.as_ref(),
Argument::Positional(expr) | Argument::Unknown(expr) | Argument::Spread(expr) => {
Some(expr)
}
@ -58,17 +63,20 @@ pub struct Call {
/// identifier of the declaration to call
pub decl_id: DeclId,
pub head: Span,
pub arguments: Vec<Argument>,
pub arguments: Spanned<Vec<Argument>>,
/// this field is used by the parser to pass additional command-specific information
pub parser_info: HashMap<String, Expression>,
}
impl Call {
pub fn new(head: Span) -> Call {
pub fn new(head: Span, args_span: Span) -> Call {
Self {
decl_id: 0,
head,
arguments: vec![],
arguments: Spanned {
item: vec![],
span: args_span,
},
parser_info: HashMap::new(),
}
}
@ -80,23 +88,20 @@ impl Call {
/// If there are one or more arguments the span encompasses the start of the first argument to
/// end of the last argument
pub fn arguments_span(&self) -> Span {
let past = self.head.past();
let start = self
.arguments
.first()
.map(|a| a.span())
.unwrap_or(past)
.start;
let end = self.arguments.last().map(|a| a.span()).unwrap_or(past).end;
Span::new(start, end)
self.arguments.span
}
pub fn named_iter(
&self,
) -> impl Iterator<Item = &(Spanned<String>, Option<Spanned<String>>, Option<Expression>)> {
self.arguments.iter().filter_map(|arg| match arg {
) -> impl Iterator<
Item = &(
Spanned<String>,
Option<Spanned<String>>,
Option<Expression>,
SpanId,
),
> {
self.arguments.item.iter().filter_map(|arg| match arg {
Argument::Named(named) => Some(named),
Argument::Positional(_) => None,
Argument::Unknown(_) => None,
@ -106,9 +111,15 @@ impl Call {
pub fn named_iter_mut(
&mut self,
) -> impl Iterator<Item = &mut (Spanned<String>, Option<Spanned<String>>, Option<Expression>)>
{
self.arguments.iter_mut().filter_map(|arg| match arg {
) -> impl Iterator<
Item = &mut (
Spanned<String>,
Option<Spanned<String>>,
Option<Expression>,
SpanId,
),
> {
self.arguments.item.iter_mut().filter_map(|arg| match arg {
Argument::Named(named) => Some(named),
Argument::Positional(_) => None,
Argument::Unknown(_) => None,
@ -122,25 +133,31 @@ impl Call {
pub fn add_named(
&mut self,
named: (Spanned<String>, Option<Spanned<String>>, Option<Expression>),
named: (
Spanned<String>,
Option<Spanned<String>>,
Option<Expression>,
SpanId,
),
) {
self.arguments.push(Argument::Named(named));
self.arguments.item.push(Argument::Named(named));
}
pub fn add_positional(&mut self, positional: Expression) {
self.arguments.push(Argument::Positional(positional));
self.arguments.item.push(Argument::Positional(positional));
}
pub fn add_unknown(&mut self, unknown: Expression) {
self.arguments.push(Argument::Unknown(unknown));
self.arguments.item.push(Argument::Unknown(unknown));
}
pub fn add_spread(&mut self, args: Expression) {
self.arguments.push(Argument::Spread(args));
self.arguments.item.push(Argument::Spread(args));
}
pub fn positional_iter(&self) -> impl Iterator<Item = &Expression> {
self.arguments
.item
.iter()
.take_while(|arg| match arg {
Argument::Spread(_) => false, // Don't include positional arguments given to rest parameter
@ -168,6 +185,7 @@ impl Call {
// todo maybe rewrite to be more elegant or something
let args = self
.arguments
.item
.iter()
.filter_map(|arg| match arg {
Argument::Named(_) => None,
@ -258,9 +276,9 @@ impl Call {
) -> Result<Vec<T>, ShellError> {
let mut output = vec![];
for result in
self.rest_iter_flattened(starting_pos, |expr| eval_constant(working_set, expr))?
{
for result in self.rest_iter_flattened(&working_set, starting_pos, |expr| {
eval_constant(working_set, expr)
})? {
output.push(FromValue::from_value(result)?);
}
@ -269,6 +287,7 @@ impl Call {
pub fn rest_iter_flattened<F>(
&self,
state: &impl GetSpan,
start: usize,
mut eval: F,
) -> Result<Vec<Value>, ShellError>
@ -282,7 +301,11 @@ impl Call {
if spread {
match result {
Value::List { mut vals, .. } => output.append(&mut vals),
_ => return Err(ShellError::CannotSpreadAsList { span: expr.span }),
_ => {
return Err(ShellError::CannotSpreadAsList {
span: expr.span(state),
})
}
}
} else {
output.push(result);
@ -310,23 +333,23 @@ impl Call {
}
}
pub fn span(&self) -> Span {
pub fn span(&self, state: &impl GetSpan) -> Span {
let mut span = self.head;
for positional in self.positional_iter() {
if positional.span.end > span.end {
span.end = positional.span.end;
if positional.span(state).end > span.end {
span.end = positional.span(state).end;
}
}
for (named, _, val) in self.named_iter() {
for (named, _, val, _) in self.named_iter() {
if named.span.end > span.end {
span.end = named.span.end;
}
if let Some(val) = &val {
if val.span.end > span.end {
span.end = val.span.end;
if val.span(state).end > span.end {
span.end = val.span(state).end;
}
}
}
@ -334,77 +357,3 @@ impl Call {
span
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::engine::EngineState;
#[test]
fn argument_span_named() {
let engine_state = EngineState::new();
let mut working_set = StateWorkingSet::new(&engine_state);
let named = Spanned {
item: "named".to_string(),
span: Span::new(2, 3),
};
let short = Spanned {
item: "short".to_string(),
span: Span::new(5, 7),
};
let expr = Expression::garbage(&mut working_set, Span::new(11, 13));
let arg = Argument::Named((named.clone(), None, None));
assert_eq!(Span::new(2, 3), arg.span());
let arg = Argument::Named((named.clone(), Some(short.clone()), None));
assert_eq!(Span::new(2, 7), arg.span());
let arg = Argument::Named((named.clone(), None, Some(expr.clone())));
assert_eq!(Span::new(2, 13), arg.span());
let arg = Argument::Named((named.clone(), Some(short.clone()), Some(expr.clone())));
assert_eq!(Span::new(2, 13), arg.span());
}
#[test]
fn argument_span_positional() {
let engine_state = EngineState::new();
let mut working_set = StateWorkingSet::new(&engine_state);
let span = Span::new(2, 3);
let expr = Expression::garbage(&mut working_set, span);
let arg = Argument::Positional(expr);
assert_eq!(span, arg.span());
}
#[test]
fn argument_span_unknown() {
let engine_state = EngineState::new();
let mut working_set = StateWorkingSet::new(&engine_state);
let span = Span::new(2, 3);
let expr = Expression::garbage(&mut working_set, span);
let arg = Argument::Unknown(expr);
assert_eq!(span, arg.span());
}
#[test]
fn call_arguments_span() {
let engine_state = EngineState::new();
let mut working_set = StateWorkingSet::new(&engine_state);
let mut call = Call::new(Span::new(0, 1));
call.add_positional(Expression::garbage(&mut working_set, Span::new(2, 3)));
call.add_positional(Expression::garbage(&mut working_set, Span::new(5, 7)));
assert_eq!(Span::new(2, 7), call.arguments_span());
}
}

View file

@ -173,7 +173,7 @@ impl Expression {
Expr::Binary(_) => false,
Expr::Bool(_) => false,
Expr::Call(call) => {
for arg in &call.arguments {
for arg in &call.arguments.item {
match arg {
Argument::Positional(expr)
| Argument::Unknown(expr)
@ -366,7 +366,7 @@ impl Expression {
if replaced.contains_span(call.head) {
call.head = new_span;
}
for arg in call.arguments.iter_mut() {
for arg in call.arguments.item.iter_mut() {
match arg {
Argument::Positional(expr)
| Argument::Unknown(expr)

View file

@ -1023,16 +1023,32 @@ impl<'a> StateWorkingSet<'a> {
impl<'a> GetSpan for &'a StateWorkingSet<'a> {
fn get_span(&self, span_id: SpanId) -> Span {
let num_permanent_spans = self.permanent_state.num_spans();
if span_id.0 < num_permanent_spans {
self.permanent_state.get_span(span_id)
} else {
*self
.delta
.spans
.get(span_id.0 - num_permanent_spans)
.expect("internal error: missing span")
}
get_span(self, span_id)
}
}
impl<'a> GetSpan for &'a mut StateWorkingSet<'a> {
fn get_span(&self, span_id: SpanId) -> Span {
get_span(self, span_id)
}
}
impl<'a> GetSpan for StateWorkingSet<'a> {
fn get_span(&self, span_id: SpanId) -> Span {
get_span(self, span_id)
}
}
fn get_span(working_set: &StateWorkingSet, span_id: SpanId) -> Span {
let num_permanent_spans = working_set.permanent_state.num_spans();
if span_id.0 < num_permanent_spans {
working_set.permanent_state.get_span(span_id)
} else {
*working_set
.delta
.spans
.get(span_id.0 - num_permanent_spans)
.expect("internal error: missing span")
}
}

View file

@ -301,7 +301,7 @@ fn eval_const_call(
return Err(ShellError::NotAConstCommand { span: call.head });
}
if !decl.is_known_external() && call.named_iter().any(|(flag, _, _)| flag.item == "help") {
if !decl.is_known_external() && call.named_iter().any(|(flag, _, _, _)| flag.item == "help") {
// It would require re-implementing get_full_help() for const evaluation. Assuming that
// getting help messages at parse-time is rare enough, we can simply disallow it.
return Err(ShellError::NotAConstHelp { span: call.head });

View file

@ -567,7 +567,7 @@ impl PipelineData {
if command.block_id().is_some() {
self.write_all_and_flush(engine_state, no_newline, to_stderr)
} else {
let call = Call::new(Span::new(0, 0));
let call = Call::new(Span::unknown(), Span::unknown());
let table = command.run(engine_state, stack, &call, self)?;
table.write_all_and_flush(engine_state, no_newline, to_stderr)
}