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() .collect()
} else { } else {
call.named_iter() 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() .collect()
}; };

View file

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

View file

@ -49,7 +49,7 @@ impl Command for BytesBuild {
_input: PipelineData, _input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let mut output = vec![]; 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); let eval_expression = get_eval_expression(engine_state);
eval_expression(engine_state, stack, expr) eval_expression(engine_state, stack, expr)
})? { })? {

View file

@ -111,11 +111,11 @@ fn get_arguments(
) -> Vec<Value> { ) -> Vec<Value> {
let mut arg_value = vec![]; let mut arg_value = vec![];
let span = Span::test_data(); let span = Span::test_data();
for arg in &call.arguments { for arg in &call.arguments.item {
match arg { match arg {
// I think the second argument to Argument::Named is the short name, but I'm not really sure. // 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. :) // 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_type = "named";
let arg_value_name = name.item.clone(); let arg_value_name = name.item.clone();
let arg_value_name_span_start = name.span.start as i64; 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_values,
collect_exprs, collect_exprs,
collect_lines, collect_lines,
call.span(), call.span(&engine_state),
); );
let lock_err = |_| ShellError::GenericError { 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?; let pipeline_data = result?;
// Collect the output // Collect the output
let _ = pipeline_data.into_value(call.span()); let _ = pipeline_data.into_value(call.span(&engine_state));
Ok(engine_state Ok(engine_state
.deactivate_debugger() .deactivate_debugger()
.map_err(lock_err)? .map_err(lock_err)?
.report(engine_state, call.span())? .report(engine_state, call.span(&engine_state))?
.into_pipeline_data()) .into_pipeline_data())
} }

View file

@ -177,7 +177,12 @@ impl Command for Open {
let block = engine_state.get_block(block_id); let block = engine_state.get_block(block_id);
eval_block(engine_state, stack, block, stream) eval_block(engine_state, stack, block, stream)
} else { } 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| { output.push(command_output.map_err(|inner| {
ShellError::GenericError{ ShellError::GenericError{

View file

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

View file

@ -103,7 +103,7 @@ pub fn get_rest_for_glob_pattern(
let mut output = vec![]; let mut output = vec![];
let eval_expression = get_eval_expression(engine_state); 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); let result = eval_expression(engine_state, stack, expr);
match result { match result {
Err(e) => Err(e), Err(e) => Err(e),

View file

@ -141,7 +141,7 @@ fn from_csv(
} else { } else {
return Err(ShellError::NonUtf8Custom { return Err(ShellError::NonUtf8Custom {
msg: "separator should be a single char or a 4-byte unicode".into(), 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_engine::command_prelude::*;
use nu_protocol::ast::{Expr, Expression}; use nu_protocol::ast::{Expr, Expression};
use nu_protocol::engine::UNKNOWN_SPAN_ID;
use std::collections::VecDeque; use std::collections::VecDeque;
#[derive(Clone)] #[derive(Clone)]
@ -143,7 +144,7 @@ pub fn cal(
style_computer, 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(( table_no_index.add_named((
Spanned { Spanned {
item: "index".to_string(), item: "index".to_string(),
@ -155,6 +156,7 @@ pub fn cal(
Span::unknown(), Span::unknown(),
Type::Bool, Type::Bool,
)), )),
UNKNOWN_SPAN_ID,
)); ));
let cal_table_output = 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( let result = decl.run(
engine_state, engine_state,
stack, stack,
&Call::new(span), &Call::new(span, span.past()),
Value::string(item, Span::unknown()).into_pipeline_data(), 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( Some(converter_id) => engine_state.get_decl(converter_id).run(
engine_state, engine_state,
stack, stack,
&Call::new(span), &Call::new(span, span.past()),
output, output,
), ),
None => Ok(output), None => Ok(output),

View file

@ -58,7 +58,12 @@ impl Command for IsTerminal {
_ => { _ => {
return Err(ShellError::IncompatibleParametersSingle { return Err(ShellError::IncompatibleParametersSingle {
msg: "Only one stream may be checked".into(), 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; Arc::make_mut(&mut engine_state.config).use_ansi_coloring = false;
// Invoke the `table` command. // Invoke the `table` command.
let output = let output = crate::Table.run(
crate::Table.run(&engine_state, &mut stack, &Call::new(Span::unknown()), data)?; &engine_state,
&mut stack,
&Call::new(Span::unknown(), Span::unknown()),
data,
)?;
// Write the output. // Write the output.
for value in output { for value in output {

View file

@ -127,7 +127,7 @@ impl Command for Table {
return Ok(val.into_pipeline_data()); 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); let input = CmdInput::new(engine_state, stack, call, input);
// reset vt processing, aka ansi because illbehaved externals can break it // reset vt processing, aka ansi because illbehaved externals can break it
@ -247,6 +247,7 @@ impl TableConfig {
} }
fn parse_table_config( fn parse_table_config(
engine_state: &EngineState,
call: &Call, call: &Call,
state: &EngineState, state: &EngineState,
stack: &mut Stack, stack: &mut Stack,
@ -269,9 +270,9 @@ fn parse_table_config(
flatten_separator, flatten_separator,
}, },
}; };
let theme = let theme = get_theme_flag(engine_state, call, state, stack)?
get_theme_flag(call, state, stack)?.unwrap_or_else(|| get_config(state, stack).table_mode); .unwrap_or_else(|| get_config(state, stack).table_mode);
let index = get_index_flag(call, state, stack)?; let index = get_index_flag(engine_state, call, state, stack)?;
let term_width = get_width_param(width_param); let term_width = get_width_param(width_param);
@ -285,6 +286,7 @@ fn parse_table_config(
} }
fn get_index_flag( fn get_index_flag(
engine_state: &EngineState,
call: &Call, call: &Call,
state: &EngineState, state: &EngineState,
stack: &mut Stack, stack: &mut Stack,
@ -308,7 +310,7 @@ fn get_index_flag(
Err(ShellError::UnsupportedInput { Err(ShellError::UnsupportedInput {
msg: String::from("got a negative integer"), msg: String::from("got a negative integer"),
input: val.to_string(), input: val.to_string(),
msg_span: call.span(), msg_span: call.span(&engine_state),
input_span: internal_span, input_span: internal_span,
}) })
} else { } else {
@ -319,13 +321,14 @@ fn get_index_flag(
_ => Err(ShellError::CantConvert { _ => Err(ShellError::CantConvert {
to_type: String::from("index"), to_type: String::from("index"),
from_type: String::new(), from_type: String::new(),
span: call.span(), span: call.span(&engine_state),
help: Some(String::from("supported values: [bool, int, nothing]")), help: Some(String::from("supported values: [bool, int, nothing]")),
}), }),
} }
} }
fn get_theme_flag( fn get_theme_flag(
engine_state: &EngineState,
call: &Call, call: &Call,
state: &EngineState, state: &EngineState,
stack: &mut Stack, stack: &mut Stack,
@ -335,7 +338,7 @@ fn get_theme_flag(
TableMode::from_str(&theme).map_err(|err| ShellError::CantConvert { TableMode::from_str(&theme).map_err(|err| ShellError::CantConvert {
to_type: String::from("theme"), to_type: String::from("theme"),
from_type: String::from("string"), from_type: String::from("string"),
span: call.span(), span: call.span(&engine_state),
help: Some(format!("{}, but found '{}'.", err, theme)), 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 stack = &mut stack.use_call_arg_out_dest();
let mut output = vec![]; 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) eval_expression::<WithoutDebug>(engine_state, stack, expr)
})? { })? {
output.push(FromValue::from_value(result)?); 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( if let Ok(output) = decl.run(
engine_state, engine_state,
stack, stack,
&Call::new(Span::unknown()), &Call::new(Span::unknown(), Span::unknown()),
Value::string(code_string, Span::unknown()).into_pipeline_data(), Value::string(code_string, Span::unknown()).into_pipeline_data(),
) { ) {
let result = output.into_value(Span::unknown()); let result = output.into_value(Span::unknown());
@ -241,7 +241,10 @@ fn get_documentation(
&Call { &Call {
decl_id, decl_id,
head: span, head: span,
arguments: vec![], arguments: Spanned {
item: vec![],
span: span.past(),
},
parser_info: HashMap::new(), parser_info: HashMap::new(),
}, },
PipelineData::Value(Value::list(vals, span), None), PipelineData::Value(Value::list(vals, span), None),
@ -273,7 +276,7 @@ fn get_documentation(
match decl.run( match decl.run(
engine_state, engine_state,
stack, stack,
&Call::new(Span::unknown()), &Call::new(Span::unknown(), Span::unknown()),
Value::string(example.example, Span::unknown()).into_pipeline_data(), Value::string(example.example, Span::unknown()).into_pipeline_data(),
) { ) {
Ok(output) => { Ok(output) => {
@ -296,7 +299,7 @@ fn get_documentation(
} }
if let Some(result) = &example.result { 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") { if example.example.ends_with("--collapse") {
// collapse the result // collapse the result
table_call.add_named(( table_call.add_named((
@ -306,6 +309,7 @@ fn get_documentation(
}, },
None, None,
None, None,
UNKNOWN_SPAN_ID,
)) ))
} else { } else {
// expand the result // expand the result
@ -316,6 +320,7 @@ fn get_documentation(
}, },
None, None,
None, None,
UNKNOWN_SPAN_ID,
)) ))
} }
let table = engine_state let table = engine_state
@ -368,13 +373,17 @@ fn get_ansi_color_for_component_or_default(
// Call ansi command using argument // Call ansi command using argument
if let Some(argument) = argument_opt { if let Some(argument) = argument_opt {
if let Some(decl_id) = engine_state.find_decl(b"ansi", &[]) { 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>( if let Ok(result) = eval_call::<WithoutDebug>(
engine_state, engine_state,
caller_stack, caller_stack,
&Call { &Call {
decl_id, decl_id,
head: span, head: span,
arguments: vec![argument], arguments: Spanned {
item: vec![argument],
span: arg_span,
},
parser_info: HashMap::new(), parser_info: HashMap::new(),
}, },
PipelineData::Empty, PipelineData::Empty,

View file

@ -26,7 +26,7 @@ pub fn eval_call<D: DebugContext>(
} }
let decl = engine_state.get_decl(call.decl_id); 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); let help = get_full_help(decl, engine_state, caller_stack);
Ok(Value::string(help, call.head).into_pipeline_data()) Ok(Value::string(help, call.head).into_pipeline_data())
} else if let Some(block_id) = decl.block_id() { } else if let Some(block_id) = decl.block_id() {
@ -100,6 +100,7 @@ pub fn eval_call<D: DebugContext>(
let mut rest_items = vec![]; let mut rest_items = vec![];
for result in call.rest_iter_flattened( for result in call.rest_iter_flattened(
&engine_state,
decl.signature().required_positional.len() decl.signature().required_positional.len()
+ decl.signature().optional_positional.len(), + decl.signature().optional_positional.len(),
|expr| eval_expression::<D>(engine_state, caller_stack, expr), |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 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()); call.add_positional(head.clone());

View file

@ -252,7 +252,7 @@ fn flatten_expression_into(
} }
let arg_start = output.len(); let arg_start = output.len();
for arg in &call.arguments { for arg in &call.arguments.item {
match arg { match arg {
Argument::Positional(positional) | Argument::Unknown(positional) => { Argument::Positional(positional) | Argument::Unknown(positional) => {
flatten_expression_into(working_set, positional, output) 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 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, &[]) { let extern_name = if let Some(name_bytes) = engine_state.find_decl_name(call.decl_id, &[]) {
String::from_utf8_lossy(name_bytes) 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 { match arg {
Argument::Positional(positional) => extern_call.add_positional(positional.clone()), Argument::Positional(positional) => extern_call.add_positional(positional.clone()),
Argument::Named(named) => { Argument::Named(named) => {

View file

@ -21,8 +21,9 @@ pub use nu_protocol::parser_path::*;
pub use parse_keywords::*; pub use parse_keywords::*;
pub use parser::{ pub use parser::{
is_math_expression_like, parse, parse_block, parse_expression, parse_external_call, is_math_expression_like, named_arg_span, parse, parse_block, parse_expression,
parse_unit_value, trim_quotes, trim_quotes_str, unescape_unquote_string, DURATION_UNIT_GROUPS, parse_external_call, parse_unit_value, trim_quotes, trim_quotes_str, unescape_unquote_string,
DURATION_UNIT_GROUPS,
}; };
#[cfg(feature = "plugin")] #[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 // Apply parse keyword side effects
let cmd = working_set.get_decl(call.decl_id); let cmd = working_set.get_decl(call.decl_id);
// check help flag first. // check help flag first.
if call.named_iter().any(|(flag, _, _)| flag.item == "help") { if call.named_iter().any(|(flag, _, _, _)| flag.item == "help") {
let call_span = call.span(); let call_span = call.span(working_set);
return Pipeline::from_vec(vec![Expression::new( return Pipeline::from_vec(vec![Expression::new(
working_set, working_set,
Expr::Call(call), Expr::Call(call),
@ -1056,7 +1056,7 @@ pub fn parse_alias(
// First from comments, if any are present // First from comments, if any are present
false => working_set.build_usage(&lite_command.comments), false => working_set.build_usage(&lite_command.comments),
// Then from the command itself // Then from the command itself
true => match alias_call.arguments.get(1) { true => match alias_call.arguments.item.get(1) {
Some(Argument::Positional(Expression { Some(Argument::Positional(Expression {
expr: Expr::Keyword(kw), expr: Expr::Keyword(kw),
.. ..
@ -1258,7 +1258,10 @@ pub fn parse_export_in_module(
let mut call = Box::new(Call { let mut call = Box::new(Call {
head: spans[0], head: spans[0],
decl_id: export_decl_id, decl_id: export_decl_id,
arguments: vec![], arguments: Spanned {
item: vec![],
span: spans[0].past(),
},
parser_info: HashMap::new(), parser_info: HashMap::new(),
}); });
@ -2263,13 +2266,21 @@ pub fn parse_module(
.find_decl(b"module") .find_decl(b"module")
.expect("internal error: missing module command"); .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 { let call = Box::new(Call {
head: Span::concat(&spans[..split_id]), head: Span::concat(&spans[..split_id]),
decl_id: module_decl_id, decl_id: module_decl_id,
arguments: vec![ arguments: Spanned {
Argument::Positional(module_name_or_path_expr), item: vec![
Argument::Positional(block_expr), Argument::Positional(module_name_or_path_expr),
], Argument::Positional(block_expr),
],
span: args_span,
},
parser_info: HashMap::new(), 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 { 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) { let (overlay_name, _) = if let Some(expr) = call.positional_nth(0) {
match eval_constant(working_set, expr) { 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 { 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) { let (overlay_name, overlay_name_span) = if let Some(expr) = call.positional_nth(0) {
match eval_constant(working_set, expr) { 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 { 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) { let (overlay_name, overlay_name_span) = if let Some(expr) = call.positional_nth(0) {
match eval_constant(working_set, expr) { 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 { let call = Box::new(Call {
decl_id, decl_id,
head: spans[0], 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(), 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 { let call = Box::new(Call {
decl_id, decl_id,
head: spans[0], 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(), 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 { let call = Box::new(Call {
decl_id, decl_id,
head: spans[0], 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(), 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 let plugin_config = call
.named_iter() .named_iter()
.find(|(arg_name, _, _)| arg_name.item == "plugin-config") .find(|(arg_name, _, _, _)| arg_name.item == "plugin-config")
.map(|(_, _, expr)| { .map(|(_, _, expr, _)| {
let expr = expr let expr = expr
.as_ref() .as_ref()
.expect("--plugin-config arg should have been checked already"); .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); working_set.error(err);
} }
let call_span = call.span(); let call_span = call.span(working_set);
Pipeline::from_vec(vec![Expression::new( Pipeline::from_vec(vec![Expression::new(
working_set, working_set,
@ -4147,6 +4176,6 @@ fn detect_params_in_name(
/// Run has_flag_const and push possible error to working_set /// 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, ()> { fn has_flag_const(working_set: &mut StateWorkingSet, call: &Call, name: &str) -> Result<bool, ()> {
call.has_flag_const(working_set, name).map_err(|err| { 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, call: &Call,
) { ) {
// Allow the call to pass if they pass in the help flag // 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; return;
} }
@ -180,7 +180,7 @@ pub(crate) fn check_call(
if let Some(last) = call.positional_iter().last() { if let Some(last) = call.positional_iter().last() {
working_set.error(ParseError::MissingPositional( working_set.error(ParseError::MissingPositional(
argument.name.clone(), 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(), sig.call_signature(),
)); ));
return; return;
@ -199,7 +199,7 @@ pub(crate) fn check_call(
if let Some(last) = call.positional_iter().last() { if let Some(last) = call.positional_iter().last() {
working_set.error(ParseError::MissingPositional( working_set.error(ParseError::MissingPositional(
missing.name.clone(), 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(), sig.call_signature(),
)) ))
} else { } else {
@ -211,7 +211,10 @@ pub(crate) fn check_call(
} }
} else { } else {
for req_flag in sig.named.iter().filter(|x| x.required) { 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( working_set.error(ParseError::MissingRequiredFlag(
req_flag.long.clone(), req_flag.long.clone(),
command, command,
@ -486,18 +489,19 @@ fn ensure_flag_arg_type(
arg_shape: &SyntaxShape, arg_shape: &SyntaxShape,
long_name_span: Span, long_name_span: Span,
) -> (Spanned<String>, Expression) { ) -> (Spanned<String>, Expression) {
let arg_span = arg.span(working_set);
if !type_compatible(&arg.ty, &arg_shape.to_type()) { if !type_compatible(&arg.ty, &arg_shape.to_type()) {
working_set.error(ParseError::TypeMismatch( working_set.error(ParseError::TypeMismatch(
arg_shape.to_type(), arg_shape.to_type(),
arg.ty, arg.ty,
arg.span, arg_span,
)); ));
( (
Spanned { Spanned {
item: arg_name, item: arg_name,
span: long_name_span, span: long_name_span,
}, },
Expression::garbage(working_set, arg.span), Expression::garbage(working_set, arg_span),
) )
} else { } else {
( (
@ -916,6 +920,25 @@ pub struct ParsedInternalCall {
pub output: Type, 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( pub fn parse_internal_call(
working_set: &mut StateWorkingSet, working_set: &mut StateWorkingSet,
command_span: Span, command_span: Span,
@ -924,7 +947,7 @@ pub fn parse_internal_call(
) -> ParsedInternalCall { ) -> ParsedInternalCall {
trace!("parsing: internal call (decl id: {})", decl_id); 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.decl_id = decl_id;
call.head = command_span; call.head = command_span;
let _ = working_set.add_span(call.head); let _ = working_set.add_span(call.head);
@ -1002,7 +1025,9 @@ pub fn parse_internal_call(
call.add_unknown(arg); call.add_unknown(arg);
} else { } 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; spans_idx += 1;
@ -1053,27 +1078,30 @@ pub fn parse_internal_call(
if flag.long.is_empty() { if flag.long.is_empty() {
if let Some(short) = flag.short { if let Some(short) = flag.short {
call.add_named(( let named = Spanned {
Spanned { item: String::new(),
item: String::new(), span: spans[spans_idx],
span: spans[spans_idx], };
}, let short = Some(Spanned {
Some(Spanned { item: short.to_string(),
item: short.to_string(), span: spans[spans_idx],
span: spans[spans_idx], });
}), let expr = Some(arg);
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 { } else {
call.add_named(( let named = Spanned {
Spanned { item: flag.long.clone(),
item: flag.long.clone(), span: spans[spans_idx],
span: spans[spans_idx], };
}, let short = None;
None, let expr = Some(arg);
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; spans_idx += 1;
} else { } else {
@ -1084,27 +1112,29 @@ pub fn parse_internal_call(
} }
} else if flag.long.is_empty() { } else if flag.long.is_empty() {
if let Some(short) = flag.short { if let Some(short) = flag.short {
call.add_named(( let named = Spanned {
Spanned { item: String::new(),
item: String::new(), span: spans[spans_idx],
span: spans[spans_idx], };
}, let short = Some(Spanned {
Some(Spanned { item: short.to_string(),
item: short.to_string(), span: spans[spans_idx],
span: spans[spans_idx], });
}), let expr = None;
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 { } else {
call.add_named(( let named = Spanned {
Spanned { item: flag.long.clone(),
item: flag.long.clone(), span: spans[spans_idx],
span: spans[spans_idx], };
}, let short = None;
None, let expr = None;
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 = if !type_compatible(&positional.shape.to_type(), &arg.ty) {
let arg_ty = arg.ty.clone();
working_set.error(ParseError::TypeMismatch( working_set.error(ParseError::TypeMismatch(
positional.shape.to_type(), positional.shape.to_type(),
arg.ty, arg_ty,
arg.span, arg.span(working_set),
)); ));
Expression::garbage(working_set, arg.span) Expression::garbage(working_set, arg.span(working_set))
} else { } else {
arg arg
}; };
@ -1311,7 +1342,7 @@ pub fn parse_call(working_set: &mut StateWorkingSet, spans: &[Span], head: Span)
trace!("parsing: alias of external call"); trace!("parsing: alias of external call");
let mut head = head.clone(); 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(); let mut final_args = args.clone().into_vec();
for arg_span in &spans[1..] { 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 { for item in list {
match item { match item {
ListItem::Item(expr) => { ListItem::Item(expr) => {
let contents = working_set.get_span_contents(expr.span); let contents =
output.push((trim_quotes(contents).to_vec(), expr.span)); working_set.get_span_contents(expr.span(working_set));
output
.push((trim_quotes(contents).to_vec(), expr.span(working_set)));
} }
ListItem::Spread(_, spread) => { ListItem::Spread(_, spread) => {
working_set.error(ParseError::WrongImportPattern( working_set.error(ParseError::WrongImportPattern(
"cannot spread in an import pattern".into(), "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 .members
.push(ImportPatternMember::List { names: output }); .push(ImportPatternMember::List { names: output });
} else { } else {
working_set.error(ParseError::ExportNotFound(result.span)); working_set.error(ParseError::ExportNotFound(result.span(working_set)));
return Expression::new( return Expression::new(
working_set, working_set,
Expr::ImportPattern(Box::new(import_pattern)), Expr::ImportPattern(Box::new(import_pattern)),
@ -3779,7 +3812,7 @@ pub fn parse_signature_helper(working_set: &mut StateWorkingSet, span: Span) ->
format!( format!(
"expected default value to be `{var_type}`" "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) Some(constant)
} else { } else {
working_set.error(ParseError::NonConstantDefaultValue( working_set.error(ParseError::NonConstantDefaultValue(
expression.span, expression.span(working_set),
)); ));
None None
}; };
@ -3806,7 +3839,7 @@ pub fn parse_signature_helper(working_set: &mut StateWorkingSet, span: Span) ->
working_set.error(ParseError::AssignmentMismatch( working_set.error(ParseError::AssignmentMismatch(
"Rest parameter was given a default value".into(), "Rest parameter was given a default value".into(),
"can't have default value".into(), "can't have default value".into(),
expression.span, expression.span(working_set),
)) ))
} }
Arg::Flag { Arg::Flag {
@ -3819,7 +3852,7 @@ pub fn parse_signature_helper(working_set: &mut StateWorkingSet, span: Span) ->
}, },
type_annotated, type_annotated,
} => { } => {
let expression_span = expression.span; let expression_span = expression.span(working_set);
*default_value = if let Ok(value) = *default_value = if let Ok(value) =
eval_constant(working_set, &expression) eval_constant(working_set, &expression)
@ -4065,7 +4098,7 @@ fn parse_table_row(
list.into_iter() list.into_iter()
.map(|item| match item { .map(|item| match item {
ListItem::Item(expr) => Ok(expr), ListItem::Item(expr) => Ok(expr),
ListItem::Spread(_, spread) => Err(spread.span), ListItem::Spread(_, spread) => Err(spread.span(working_set)),
}) })
.collect::<Result<_, _>>() .collect::<Result<_, _>>()
.map(|exprs| (exprs, span)) .map(|exprs| (exprs, span))
@ -4140,7 +4173,7 @@ fn parse_table_expression(working_set: &mut StateWorkingSet, span: Span) -> Expr
} }
Ordering::Greater => { Ordering::Greater => {
let span = { let span = {
let start = list[head.len()].span.start; let start = list[head.len()].span(working_set).start;
let end = span.end; let end = span.end;
Span::new(start, 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 = 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); working_set.parse_errors.extend(errs);
ty ty
} else { } 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) 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 errors = vec![];
let mut rows = rows.to_vec(); let mut rows = rows.to_vec();
let mut mk_ty = || -> Type { 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() { if let Some(str) = expr.as_string() {
str str
} else { } else {
errors.push(mk_error(expr.span)); errors.push(mk_error(expr.span(&working_set)));
String::from("{ column }") String::from("{ column }")
} }
}) })
@ -5115,7 +5152,7 @@ pub fn parse_math_expression(
working_set.error(err); 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( expr_stack.push(Expression::new(
working_set, working_set,
Expr::BinaryOp(Box::new(lhs), Box::new(op), Box::new(rhs)), Expr::BinaryOp(Box::new(lhs), Box::new(op), Box::new(rhs)),
@ -5151,7 +5188,7 @@ pub fn parse_math_expression(
working_set.error(err) 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( expr_stack.push(Expression::new(
working_set, working_set,
Expr::BinaryOp(Box::new(lhs), Box::new(op), Box::new(rhs)), 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 { let expr = Expr::Call(Box::new(Call {
head: Span::unknown(), head: Span::unknown(),
decl_id, decl_id,
arguments, arguments: Spanned {
item: arguments,
span: Span::concat(spans),
},
parser_info: HashMap::new(), 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 { match arg {
Argument::Named(named) => { Argument::Named(named) => {
if let Some(arg) = &named.2 { if let Some(arg) = &named.2 {
@ -6249,7 +6289,7 @@ pub fn discover_captures_in_expr(
} }
Expr::Var(var_id) => { Expr::Var(var_id) => {
if (*var_id > ENV_VARIABLE_ID || *var_id == IN_VARIABLE_ID) && !seen.contains(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) => { 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 { 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") { if let Some(decl_id) = working_set.find_decl(b"collect") {
let mut output = vec![]; let mut output = vec![];
@ -6324,14 +6364,15 @@ fn wrap_expr_with_collect(working_set: &mut StateWorkingSet, expr: &Expression)
Type::Any, Type::Any,
))); )));
output.push(Argument::Named(( let named = Spanned {
Spanned { item: "keep-env".to_string(),
item: "keep-env".to_string(), span: Span::unknown(),
span: Span::new(0, 0), };
}, let short = None;
None, let expr = None;
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`. // The containing, synthetic call to `collect`.
// We don't want to have a real span as it will confuse flattening // 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( Expression::new(
working_set, working_set,
Expr::Call(Box::new(Call { Expr::Call(Box::new(Call {
head: Span::new(0, 0), head: Span::unknown(),
arguments: output, arguments: Spanned {
item: output,
span: Span::unknown(),
},
decl_id, decl_id,
parser_info: HashMap::new(), 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 { if let Expr::Call(call) = &element.expr.expr {
assert_eq!(call.decl_id, 0); assert_eq!(call.decl_id, 0);
assert_eq!(call.arguments.len(), 2); assert_eq!(call.arguments.item.len(), 2);
matches!(call.arguments[0], Argument::Named((_, None, None))); matches!(call.arguments.item[0], Argument::Named((_, None, None, _)));
matches!(call.arguments[1], Argument::Named((_, None, Some(_)))); matches!(
call.arguments.item[1],
Argument::Named((_, None, Some(_), _))
);
} }
} }
@ -2150,7 +2153,7 @@ mod input_types {
assert!(pipeline.elements[3].redirection.is_none()); assert!(pipeline.elements[3].redirection.is_none());
match &pipeline.elements[3].expr.expr { match &pipeline.elements[3].expr.expr {
Expr::Call(call) => { Expr::Call(call) => {
let arg = &call.arguments[0]; let arg = &call.arguments.item[0];
match arg { match arg {
Argument::Positional(a) => match &a.expr { Argument::Positional(a) => match &a.expr {
Expr::FullCellPath(path) => match &path.head.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, stack: &mut Stack,
eval_expression_fn: fn(&EngineState, &mut Stack, &Expression) -> Result<Value, ShellError>, eval_expression_fn: fn(&EngineState, &mut Stack, &Expression) -> Result<Value, ShellError>,
) -> Result<Self, ShellError> { ) -> Result<Self, ShellError> {
let positional = let positional = call.rest_iter_flattened(&engine_state, 0, |expr| {
call.rest_iter_flattened(0, |expr| eval_expression_fn(engine_state, stack, expr))?; eval_expression_fn(engine_state, stack, expr)
})?;
let mut named = Vec::with_capacity(call.named_len()); 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 { let value = match expr {
None => None, None => None,
Some(expr) => Some(eval_expression_fn(engine_state, stack, expr)?), Some(expr) => Some(eval_expression_fn(engine_state, stack, expr)?),

View file

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

View file

@ -1023,16 +1023,32 @@ impl<'a> StateWorkingSet<'a> {
impl<'a> GetSpan for &'a StateWorkingSet<'a> { impl<'a> GetSpan for &'a StateWorkingSet<'a> {
fn get_span(&self, span_id: SpanId) -> Span { fn get_span(&self, span_id: SpanId) -> Span {
let num_permanent_spans = self.permanent_state.num_spans(); get_span(self, span_id)
if span_id.0 < num_permanent_spans { }
self.permanent_state.get_span(span_id) }
} else {
*self impl<'a> GetSpan for &'a mut StateWorkingSet<'a> {
.delta fn get_span(&self, span_id: SpanId) -> Span {
.spans get_span(self, span_id)
.get(span_id.0 - num_permanent_spans) }
.expect("internal error: missing span") }
}
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 }); 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 // 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. // getting help messages at parse-time is rare enough, we can simply disallow it.
return Err(ShellError::NotAConstHelp { span: call.head }); return Err(ShellError::NotAConstHelp { span: call.head });

View file

@ -567,7 +567,7 @@ impl PipelineData {
if command.block_id().is_some() { if command.block_id().is_some() {
self.write_all_and_flush(engine_state, no_newline, to_stderr) self.write_all_and_flush(engine_state, no_newline, to_stderr)
} else { } 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)?; let table = command.run(engine_state, stack, &call, self)?;
table.write_all_and_flush(engine_state, no_newline, to_stderr) table.write_all_and_flush(engine_state, no_newline, to_stderr)
} }