From aded2c1937c93399062456a7ab525a1bb2a3f9c0 Mon Sep 17 00:00:00 2001 From: JT <547158+jntrnr@users.noreply.github.com> Date: Fri, 7 Apr 2023 12:35:45 +1200 Subject: [PATCH] Refactor to support multiple parse errors (#8765) # Description This is a pretty heavy refactor of the parser to support multiple parser errors. It has a few issues we should address before landing: - [x] In some cases, error quality has gotten worse `1 / "bob"` for example - [x] if/else isn't currently parsing correctly - probably others # User-Facing Changes This may have error quality degradation as we adjust to the new error reporting mechanism. # 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 -A clippy::needless_collect` to check that you're using the standard code style - `cargo test --workspace` to check that all tests pass - `cargo run -- crates/nu-utils/standard_library/tests.nu` 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. --- crates/nu-cli/src/commands.rs | 6 +- crates/nu-cli/src/completions/completer.rs | 2 +- crates/nu-cli/src/config_files.rs | 6 +- crates/nu-cli/src/reedline_config.rs | 2 +- crates/nu-cli/src/repl.rs | 2 +- crates/nu-cli/src/syntax_highlight.rs | 5 +- crates/nu-cli/src/util.rs | 26 +- crates/nu-cli/src/validation.rs | 14 +- crates/nu-cli/tests/completions.rs | 4 +- .../tests/support/completions_helpers.rs | 4 +- crates/nu-cmd-lang/src/example_support.rs | 4 +- .../src/dataframe/test_dataframe.rs | 4 +- crates/nu-command/src/debug/ast.rs | 6 +- crates/nu-command/src/formats/from/nuon.rs | 7 +- crates/nu-command/src/hook.rs | 6 +- .../nu-command/src/strings/format/command.rs | 4 +- crates/nu-command/src/system/nu_check.rs | 54 +- crates/nu-explore/src/nu_common/command.rs | 4 +- crates/nu-parser/src/eval.rs | 3 +- crates/nu-parser/src/lex.rs | 3 +- crates/nu-parser/src/lib.rs | 2 - crates/nu-parser/src/lite_parser.rs | 4 +- crates/nu-parser/src/parse_keywords.rs | 2301 ++++++------ crates/nu-parser/src/parse_patterns.rs | 167 +- crates/nu-parser/src/parser.rs | 3101 +++++++---------- crates/nu-parser/src/type_check.rs | 3 +- crates/nu-parser/tests/test_lex.rs | 4 +- crates/nu-parser/tests/test_parser.rs | 240 +- .../tests/test_parser_unicode_escapes.rs | 4 +- crates/nu-protocol/src/engine/engine_state.rs | 8 +- crates/nu-protocol/src/lib.rs | 2 + .../src/parse_error.rs} | 2 +- src/command.rs | 6 +- src/config_files.rs | 3 +- src/ide.rs | 6 +- src/run.rs | 12 +- src/test_bins.rs | 6 +- 37 files changed, 2658 insertions(+), 3379 deletions(-) rename crates/{nu-parser/src/errors.rs => nu-protocol/src/parse_error.rs} (99%) diff --git a/crates/nu-cli/src/commands.rs b/crates/nu-cli/src/commands.rs index 5d78b64918..5d5242f8c4 100644 --- a/crates/nu-cli/src/commands.rs +++ b/crates/nu-cli/src/commands.rs @@ -34,9 +34,9 @@ pub fn evaluate_commands( let mut working_set = StateWorkingSet::new(engine_state); - let (output, err) = parse(&mut working_set, None, commands.item.as_bytes(), false, &[]); - if let Some(err) = err { - report_error(&working_set, &err); + let output = parse(&mut working_set, None, commands.item.as_bytes(), false, &[]); + if let Some(err) = working_set.parse_errors.first() { + report_error(&working_set, err); std::process::exit(1); } diff --git a/crates/nu-cli/src/completions/completer.rs b/crates/nu-cli/src/completions/completer.rs index 1cbab139c3..033673262c 100644 --- a/crates/nu-cli/src/completions/completer.rs +++ b/crates/nu-cli/src/completions/completer.rs @@ -120,7 +120,7 @@ impl NuCompleter { let pos = offset + pos; let config = self.engine_state.get_config(); - let (output, _err) = parse(&mut working_set, Some("completer"), &new_line, false, &[]); + let output = parse(&mut working_set, Some("completer"), &new_line, false, &[]); for pipeline in output.pipelines.into_iter() { for pipeline_element in pipeline.elements { diff --git a/crates/nu-cli/src/config_files.rs b/crates/nu-cli/src/config_files.rs index 509014eb4b..fb5826c083 100644 --- a/crates/nu-cli/src/config_files.rs +++ b/crates/nu-cli/src/config_files.rs @@ -1,14 +1,12 @@ use crate::util::eval_source; use nu_command::util::report_error; #[cfg(feature = "plugin")] -use nu_parser::ParseError; -#[cfg(feature = "plugin")] use nu_path::canonicalize_with; use nu_protocol::engine::{EngineState, Stack, StateWorkingSet}; -#[cfg(feature = "plugin")] -use nu_protocol::Spanned; use nu_protocol::{HistoryFileFormat, PipelineData}; #[cfg(feature = "plugin")] +use nu_protocol::{ParseError, Spanned}; +#[cfg(feature = "plugin")] use nu_utils::utils::perf; use std::path::PathBuf; diff --git a/crates/nu-cli/src/reedline_config.rs b/crates/nu-cli/src/reedline_config.rs index 111ac886a7..80b8c5fbec 100644 --- a/crates/nu-cli/src/reedline_config.rs +++ b/crates/nu-cli/src/reedline_config.rs @@ -97,7 +97,7 @@ pub(crate) fn add_menus( { let (block, _) = { let mut working_set = StateWorkingSet::new(&engine_state); - let (output, _) = parse( + let output = parse( &mut working_set, Some(name), // format!("entry #{}", entry_num) definition.as_bytes(), diff --git a/crates/nu-cli/src/repl.rs b/crates/nu-cli/src/repl.rs index d42af49480..5c49151eb6 100644 --- a/crates/nu-cli/src/repl.rs +++ b/crates/nu-cli/src/repl.rs @@ -789,7 +789,7 @@ pub fn eval_string_with_input( ) -> Result { let (block, delta) = { let mut working_set = StateWorkingSet::new(engine_state); - let (output, _) = parse(&mut working_set, None, source.as_bytes(), false, &[]); + let output = parse(&mut working_set, None, source.as_bytes(), false, &[]); (output, working_set.render()) }; diff --git a/crates/nu-cli/src/syntax_highlight.rs b/crates/nu-cli/src/syntax_highlight.rs index 51ca55376b..157b445b01 100644 --- a/crates/nu-cli/src/syntax_highlight.rs +++ b/crates/nu-cli/src/syntax_highlight.rs @@ -18,10 +18,7 @@ impl Highlighter for NuHighlighter { trace!("highlighting: {}", line); let mut working_set = StateWorkingSet::new(&self.engine_state); - let block = { - let (block, _) = parse(&mut working_set, None, line.as_bytes(), false, &[]); - block - }; + let block = parse(&mut working_set, None, line.as_bytes(), false, &[]); let (shapes, global_span_offset) = { let shapes = flatten_block(&working_set, &block); (shapes, self.engine_state.next_span_start()) diff --git a/crates/nu-cli/src/util.rs b/crates/nu-cli/src/util.rs index 22872edfcb..85b0f0b3e7 100644 --- a/crates/nu-cli/src/util.rs +++ b/crates/nu-cli/src/util.rs @@ -113,7 +113,8 @@ fn gather_env_vars( span, }) = parts.get(0) { - let bytes = engine_state.get_span_contents(span); + let mut working_set = StateWorkingSet::new(engine_state); + let bytes = working_set.get_span_contents(*span); if bytes.len() < 2 { report_capture_error( @@ -125,9 +126,12 @@ fn gather_env_vars( continue; } - let (bytes, parse_error) = unescape_unquote_string(bytes, *span); + let (bytes, err) = unescape_unquote_string(bytes, *span); + if let Some(err) = err { + working_set.error(err); + } - if parse_error.is_some() { + if working_set.parse_errors.first().is_some() { report_capture_error( engine_state, &String::from_utf8_lossy(contents), @@ -153,7 +157,8 @@ fn gather_env_vars( span, }) = parts.get(2) { - let bytes = engine_state.get_span_contents(span); + let mut working_set = StateWorkingSet::new(engine_state); + let bytes = working_set.get_span_contents(*span); if bytes.len() < 2 { report_capture_error( @@ -165,9 +170,12 @@ fn gather_env_vars( continue; } - let (bytes, parse_error) = unescape_unquote_string(bytes, *span); + let (bytes, err) = unescape_unquote_string(bytes, *span); + if let Some(err) = err { + working_set.error(err); + } - if parse_error.is_some() { + if working_set.parse_errors.first().is_some() { report_capture_error( engine_state, &String::from_utf8_lossy(contents), @@ -209,16 +217,16 @@ pub fn eval_source( let (block, delta) = { let mut working_set = StateWorkingSet::new(engine_state); - let (output, err) = parse( + let output = parse( &mut working_set, Some(fname), // format!("entry #{}", entry_num) source, false, &[], ); - if let Some(err) = err { + if let Some(err) = working_set.parse_errors.first() { set_last_exit_code(stack, 1); - report_error(&working_set, &err); + report_error(&working_set, err); return false; } diff --git a/crates/nu-cli/src/validation.rs b/crates/nu-cli/src/validation.rs index a53e617b09..8f51c4c553 100644 --- a/crates/nu-cli/src/validation.rs +++ b/crates/nu-cli/src/validation.rs @@ -1,5 +1,8 @@ -use nu_parser::{parse, ParseError}; -use nu_protocol::engine::{EngineState, StateWorkingSet}; +use nu_parser::parse; +use nu_protocol::{ + engine::{EngineState, StateWorkingSet}, + ParseError, +}; use reedline::{ValidationResult, Validator}; use std::sync::Arc; @@ -10,9 +13,12 @@ pub struct NuValidator { impl Validator for NuValidator { fn validate(&self, line: &str) -> ValidationResult { let mut working_set = StateWorkingSet::new(&self.engine_state); - let (_, err) = parse(&mut working_set, None, line.as_bytes(), false, &[]); + parse(&mut working_set, None, line.as_bytes(), false, &[]); - if matches!(err, Some(ParseError::UnexpectedEof(..))) { + if matches!( + working_set.parse_errors.first(), + Some(ParseError::UnexpectedEof(..)) + ) { ValidationResult::Incomplete } else { ValidationResult::Complete diff --git a/crates/nu-cli/tests/completions.rs b/crates/nu-cli/tests/completions.rs index 78c3b07143..727bdff6bb 100644 --- a/crates/nu-cli/tests/completions.rs +++ b/crates/nu-cli/tests/completions.rs @@ -663,8 +663,8 @@ fn run_external_completion(block: &str, input: &str) -> Vec { let (dir, _, mut engine_state, mut stack) = new_engine(); let (_, delta) = { let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, block.as_bytes(), false, &[]); - assert!(err.is_none()); + let block = parse(&mut working_set, None, block.as_bytes(), false, &[]); + assert!(working_set.parse_errors.is_empty()); (block, working_set.render()) }; diff --git a/crates/nu-cli/tests/support/completions_helpers.rs b/crates/nu-cli/tests/support/completions_helpers.rs index 2ae8b2b121..0911c67a52 100644 --- a/crates/nu-cli/tests/support/completions_helpers.rs +++ b/crates/nu-cli/tests/support/completions_helpers.rs @@ -146,9 +146,9 @@ pub fn merge_input( let (block, delta) = { let mut working_set = StateWorkingSet::new(engine_state); - let (block, err) = parse(&mut working_set, None, input, false, &[]); + let block = parse(&mut working_set, None, input, false, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); (block, working_set.render()) }; diff --git a/crates/nu-cmd-lang/src/example_support.rs b/crates/nu-cmd-lang/src/example_support.rs index 59f150c3ee..610ae1b440 100644 --- a/crates/nu-cmd-lang/src/example_support.rs +++ b/crates/nu-cmd-lang/src/example_support.rs @@ -126,9 +126,9 @@ fn eval_pipeline_without_terminal_expression( pub fn parse(contents: &str, engine_state: &EngineState) -> (Block, StateDelta) { let mut working_set = StateWorkingSet::new(engine_state); - let (output, err) = nu_parser::parse(&mut working_set, None, contents.as_bytes(), false, &[]); + let output = nu_parser::parse(&mut working_set, None, contents.as_bytes(), false, &[]); - if let Some(err) = err { + if let Some(err) = working_set.parse_errors.first() { panic!("test parse error in `{contents}`: {err:?}") } diff --git a/crates/nu-command/src/dataframe/test_dataframe.rs b/crates/nu-command/src/dataframe/test_dataframe.rs index 697ce7bd5f..fb457dad77 100644 --- a/crates/nu-command/src/dataframe/test_dataframe.rs +++ b/crates/nu-command/src/dataframe/test_dataframe.rs @@ -50,7 +50,7 @@ pub fn test_dataframe(cmds: Vec>) { let (block, delta) = { let mut working_set = StateWorkingSet::new(&engine_state); - let (output, err) = parse( + let output = parse( &mut working_set, None, example.example.as_bytes(), @@ -58,7 +58,7 @@ pub fn test_dataframe(cmds: Vec>) { &[], ); - if let Some(err) = err { + if let Some(err) = working_set.parse_errors.first() { panic!("test parse error in `{}`: {:?}", example.example, err) } diff --git a/crates/nu-command/src/debug/ast.rs b/crates/nu-command/src/debug/ast.rs index ac7d062c34..72081d0937 100644 --- a/crates/nu-command/src/debug/ast.rs +++ b/crates/nu-command/src/debug/ast.rs @@ -41,8 +41,10 @@ impl Command for Ast { let pipeline: Spanned = call.req(engine_state, stack, 0)?; let mut working_set = StateWorkingSet::new(engine_state); - let (block_output, error_output) = - parse(&mut working_set, None, pipeline.item.as_bytes(), false, &[]); + let block_output = parse(&mut working_set, None, pipeline.item.as_bytes(), false, &[]); + + let error_output = working_set.parse_errors.first(); + let block_value = Value::String { val: format!("{block_output:#?}"), span: pipeline.span, diff --git a/crates/nu-command/src/formats/from/nuon.rs b/crates/nu-command/src/formats/from/nuon.rs index 584e87c941..ee0abe23f7 100644 --- a/crates/nu-command/src/formats/from/nuon.rs +++ b/crates/nu-command/src/formats/from/nuon.rs @@ -64,10 +64,9 @@ impl Command for FromNuon { let engine_state = engine_state.clone(); let mut working_set = StateWorkingSet::new(&engine_state); - let mut error = None; - let (mut block, err) = + + let mut block = nu_parser::parse(&mut working_set, None, string_input.as_bytes(), false, &[]); - error = error.or(err); if let Some(pipeline) = block.pipelines.get(1) { if let Some(element) = pipeline.elements.get(0) { @@ -146,7 +145,7 @@ impl Command for FromNuon { } }; - if let Some(err) = error { + if let Some(err) = working_set.parse_errors.first() { return Err(ShellError::GenericError( "error when parsing nuon text".into(), "could not parse nuon text".into(), diff --git a/crates/nu-command/src/hook.rs b/crates/nu-command/src/hook.rs index 3d57386959..390e2474bb 100644 --- a/crates/nu-command/src/hook.rs +++ b/crates/nu-command/src/hook.rs @@ -166,10 +166,10 @@ pub fn eval_hook( vars.push((var_id, val)); } - let (output, err) = + let output = parse(&mut working_set, Some("hook"), val.as_bytes(), false, &[]); - if let Some(err) = err { - report_error(&working_set, &err); + if let Some(err) = working_set.parse_errors.first() { + report_error(&working_set, err); return Err(ShellError::UnsupportedConfigValue( "valid source code".into(), diff --git a/crates/nu-command/src/strings/format/command.rs b/crates/nu-command/src/strings/format/command.rs index e3363e159c..43b547f900 100644 --- a/crates/nu-command/src/strings/format/command.rs +++ b/crates/nu-command/src/strings/format/command.rs @@ -290,8 +290,8 @@ fn format_record( } } FormatOperation::ValueNeedEval(_col_name, span) => { - let (exp, may_parse_err) = parse_expression(working_set, &[*span], &[], false); - match may_parse_err { + let exp = parse_expression(working_set, &[*span], &[], false); + match working_set.parse_errors.first() { None => { let parsed_result = eval_expression(engine_state, stack, &exp); if let Ok(val) = parsed_result { diff --git a/crates/nu-command/src/system/nu_check.rs b/crates/nu-command/src/system/nu_check.rs index f9ea8e2203..0239183126 100644 --- a/crates/nu-command/src/system/nu_check.rs +++ b/crates/nu-command/src/system/nu_check.rs @@ -260,9 +260,14 @@ fn heuristic_parse_file( call: &Call, is_debug: bool, ) -> Result { - let (filename, err) = unescape_unquote_string(path.as_bytes(), call.head); - if err.is_none() { - if let Ok(contents) = std::fs::read(&path) { + let starting_error_count = working_set.parse_errors.len(); + let bytes = working_set.get_span_contents(call.head); + let (filename, err) = unescape_unquote_string(bytes, call.head); + if let Some(err) = err { + working_set.error(err); + } + if starting_error_count == working_set.parse_errors.len() { + if let Ok(contents) = std::fs::read(path) { match parse_script( working_set, Some(filename.as_str()), @@ -314,13 +319,17 @@ fn parse_module( let end = working_set.next_span_start(); let new_span = Span::new(start, end); - let (_, _, _, err) = parse_module_block(working_set, new_span, filename.as_bytes(), &[]); + let starting_error_count = working_set.parse_errors.len(); + parse_module_block(working_set, new_span, filename.as_bytes(), &[]); - if err.is_some() { + if starting_error_count != working_set.parse_errors.len() { if is_debug { let msg = format!( r#"Found : {}"#, - err.expect("Unable to parse content as module") + working_set + .parse_errors + .first() + .expect("Unable to parse content as module") ); Err(ShellError::GenericError( "Failed to parse content".to_string(), @@ -344,9 +353,16 @@ fn parse_script( is_debug: bool, span: Span, ) -> Result { - let (_, err) = parse(working_set, filename, contents, false, &[]); - if err.is_some() { - let msg = format!(r#"Found : {}"#, err.expect("Unable to parse content")); + let starting_error_count = working_set.parse_errors.len(); + parse(working_set, filename, contents, false, &[]); + if starting_error_count != working_set.parse_errors.len() { + let msg = format!( + r#"Found : {}"#, + working_set + .parse_errors + .first() + .expect("Unable to parse content") + ); if is_debug { Err(ShellError::GenericError( "Failed to parse content".to_string(), @@ -369,9 +385,14 @@ fn parse_file_script( call: &Call, is_debug: bool, ) -> Result { - let (filename, err) = unescape_unquote_string(path.as_bytes(), call.head); - if err.is_none() { - if let Ok(contents) = std::fs::read(&path) { + let starting_error_count = working_set.parse_errors.len(); + let bytes = working_set.get_span_contents(call.head); + let (filename, err) = unescape_unquote_string(bytes, call.head); + if let Some(err) = err { + working_set.error(err) + } + if starting_error_count == working_set.parse_errors.len() { + if let Ok(contents) = std::fs::read(path) { parse_script( working_set, Some(filename.as_str()), @@ -393,8 +414,13 @@ fn parse_file_module( call: &Call, is_debug: bool, ) -> Result { - let (filename, err) = unescape_unquote_string(path.as_bytes(), call.head); - if err.is_none() { + let starting_error_count = working_set.parse_errors.len(); + let bytes = working_set.get_span_contents(call.head); + let (filename, err) = unescape_unquote_string(bytes, call.head); + if let Some(err) = err { + working_set.error(err); + } + if starting_error_count == working_set.parse_errors.len() { if let Ok(contents) = std::fs::read(path) { parse_module(working_set, Some(filename), &contents, is_debug, call.head) } else { diff --git a/crates/nu-explore/src/nu_common/command.rs b/crates/nu-explore/src/nu_common/command.rs index 006e1eae80..bd71f2fba5 100644 --- a/crates/nu-explore/src/nu_common/command.rs +++ b/crates/nu-explore/src/nu_common/command.rs @@ -55,7 +55,7 @@ fn eval_source2( ) -> Result { let (mut block, delta) = { let mut working_set = StateWorkingSet::new(engine_state); - let (output, err) = parse( + let output = parse( &mut working_set, Some(fname), // format!("entry #{}", entry_num) source, @@ -63,7 +63,7 @@ fn eval_source2( &[], ); - if let Some(err) = err { + if let Some(err) = working_set.parse_errors.first() { return Err(ShellError::IOError(err.to_string())); } diff --git a/crates/nu-parser/src/eval.rs b/crates/nu-parser/src/eval.rs index e3cc6ec9e3..2ecc1214f8 100644 --- a/crates/nu-parser/src/eval.rs +++ b/crates/nu-parser/src/eval.rs @@ -1,8 +1,7 @@ -use crate::ParseError; use nu_protocol::{ ast::{Expr, Expression}, engine::StateWorkingSet, - Span, Value, + ParseError, Span, Value, }; /// Evaluate a constant value at parse time diff --git a/crates/nu-parser/src/lex.rs b/crates/nu-parser/src/lex.rs index 8d51687a73..a0d7a1af85 100644 --- a/crates/nu-parser/src/lex.rs +++ b/crates/nu-parser/src/lex.rs @@ -1,5 +1,4 @@ -use crate::ParseError; -use nu_protocol::Span; +use nu_protocol::{ParseError, Span}; #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum TokenContents { diff --git a/crates/nu-parser/src/lib.rs b/crates/nu-parser/src/lib.rs index ff980a2fc2..ebc555c6c1 100644 --- a/crates/nu-parser/src/lib.rs +++ b/crates/nu-parser/src/lib.rs @@ -1,5 +1,4 @@ mod deparse; -mod errors; mod eval; mod flatten; mod known_external; @@ -11,7 +10,6 @@ mod parser; mod type_check; pub use deparse::{escape_for_script_arg, escape_quote_string}; -pub use errors::ParseError; pub use flatten::{ flatten_block, flatten_expression, flatten_pipeline, flatten_pipeline_element, FlatShape, }; diff --git a/crates/nu-parser/src/lite_parser.rs b/crates/nu-parser/src/lite_parser.rs index 5d017a356a..8c1f563d9a 100644 --- a/crates/nu-parser/src/lite_parser.rs +++ b/crates/nu-parser/src/lite_parser.rs @@ -1,8 +1,8 @@ /// Lite parsing converts a flat stream of tokens from the lexer to a syntax element structure that /// can be parsed. -use crate::{ParseError, Token, TokenContents}; +use crate::{Token, TokenContents}; -use nu_protocol::{ast::Redirection, Span}; +use nu_protocol::{ast::Redirection, ParseError, Span}; #[derive(Debug)] pub struct LiteCommand { diff --git a/crates/nu-parser/src/parse_keywords.rs b/crates/nu-parser/src/parse_keywords.rs index f259106f89..afe229962c 100644 --- a/crates/nu-parser/src/parse_keywords.rs +++ b/crates/nu-parser/src/parse_keywords.rs @@ -6,8 +6,8 @@ use nu_protocol::{ ImportPatternMember, PathMember, Pipeline, PipelineElement, }, engine::{StateWorkingSet, DEFAULT_OVERLAY_NAME}, - span, Alias, BlockId, Exportable, Module, PositionalArg, Span, Spanned, SyntaxShape, Type, - VarId, + span, Alias, BlockId, Exportable, Module, ParseError, PositionalArg, Span, Spanned, + SyntaxShape, Type, VarId, }; use std::collections::{HashMap, HashSet}; use std::path::{Path, PathBuf}; @@ -27,7 +27,7 @@ use crate::{ parse_import_pattern, parse_internal_call, parse_multispan_value, parse_signature, parse_string, parse_value, parse_var_with_opt_type, trim_quotes, ParsedInternalCall, }, - unescape_unquote_string, ParseError, Token, TokenContents, + unescape_unquote_string, Token, TokenContents, }; /// These parser keywords can be aliased @@ -84,8 +84,8 @@ pub fn parse_keyword( lite_command: &LiteCommand, expand_aliases_denylist: &[usize], is_subexpression: bool, -) -> (Pipeline, Option) { - let (call_expr, err) = parse_call( +) -> Pipeline { + let call_expr = parse_call( working_set, &lite_command.parts, lite_command.parts[0], @@ -93,9 +93,9 @@ pub fn parse_keyword( is_subexpression, ); - if err.is_some() { - return (Pipeline::from_vec(vec![call_expr]), err); - } + // if err.is_some() { + // return (Pipeline::from_vec(vec![call_expr]), err); + // } if let Expression { expr: Expr::Call(call), @@ -109,10 +109,10 @@ pub fn parse_keyword( "overlay hide" => parse_overlay_hide(working_set, call), "overlay new" => parse_overlay_new(working_set, call), "overlay use" => parse_overlay_use(working_set, call, expand_aliases_denylist), - _ => (Pipeline::from_vec(vec![call_expr]), err), + _ => Pipeline::from_vec(vec![call_expr]), } } else { - (Pipeline::from_vec(vec![call_expr]), err) + Pipeline::from_vec(vec![call_expr]) } } @@ -120,7 +120,7 @@ pub fn parse_def_predecl( working_set: &mut StateWorkingSet, spans: &[Span], expand_aliases_denylist: &[usize], -) -> Option { +) { let name = working_set.get_span_contents(spans[0]); // handle "export def" same as "def" @@ -131,8 +131,12 @@ pub fn parse_def_predecl( }; if (name == b"def" || name == b"def-env") && spans.len() >= 4 { - let (name_expr, ..) = parse_string(working_set, spans[1], expand_aliases_denylist); - let name = name_expr.as_string(); + let starting_error_count = working_set.parse_errors.len(); + // let name_expr = parse_string(working_set, spans[1], expand_aliases_denylist); + // let name = name_expr.as_string(); + let name = working_set.get_span_contents(spans[1]); + let name = trim_quotes(name); + let name = String::from_utf8_lossy(name).to_string(); working_set.enter_scope(); // FIXME: because parse_signature will update the scope with the variables it sees @@ -141,27 +145,30 @@ pub fn parse_def_predecl( // The second time is when we actually parse the body itworking_set. // We can't reuse the first time because the variables that are created during parse_signature // are lost when we exit the scope below. - let (sig, ..) = parse_signature(working_set, spans[2], expand_aliases_denylist); + let sig = parse_signature(working_set, spans[2], expand_aliases_denylist); + working_set.parse_errors.truncate(starting_error_count); + let signature = sig.as_signature(); working_set.exit_scope(); - if let (Some(name), Some(mut signature)) = (name, signature) { - if name.contains('#') - || name.contains('^') - || name.parse::().is_ok() - || name.parse::().is_ok() - { - return Some(ParseError::CommandDefNotValid(spans[1])); - } + if name.contains('#') + || name.contains('^') + || name.parse::().is_ok() + || name.parse::().is_ok() + { + working_set.error(ParseError::CommandDefNotValid(spans[1])); + return; + } + if let Some(mut signature) = signature { signature.name = name; let decl = signature.predeclare(); if working_set.add_predecl(decl).is_some() { - return Some(ParseError::DuplicateCommandDef(spans[1])); + working_set.error(ParseError::DuplicateCommandDef(spans[1])); } } } else if name == b"extern" && spans.len() == 3 { - let (name_expr, ..) = parse_string(working_set, spans[1], expand_aliases_denylist); + let name_expr = parse_string(working_set, spans[1], expand_aliases_denylist); let name = name_expr.as_string(); working_set.enter_scope(); @@ -171,7 +178,7 @@ pub fn parse_def_predecl( // The second time is when we actually parse the body itworking_set. // We can't reuse the first time because the variables that are created during parse_signature // are lost when we exit the scope below. - let (sig, ..) = parse_signature(working_set, spans[2], expand_aliases_denylist); + let sig = parse_signature(working_set, spans[2], expand_aliases_denylist); let signature = sig.as_signature(); working_set.exit_scope(); @@ -180,7 +187,8 @@ pub fn parse_def_predecl( || name.parse::().is_ok() || name.parse::().is_ok() { - return Some(ParseError::CommandDefNotValid(spans[1])); + working_set.error(ParseError::CommandDefNotValid(spans[1])); + return; } signature.name = name.clone(); @@ -192,29 +200,26 @@ pub fn parse_def_predecl( }; if working_set.add_predecl(Box::new(decl)).is_some() { - return Some(ParseError::DuplicateCommandDef(spans[1])); + working_set.error(ParseError::DuplicateCommandDef(spans[1])); + return; } } } - - None } pub fn parse_for( working_set: &mut StateWorkingSet, spans: &[Span], expand_aliases_denylist: &[usize], -) -> (Expression, Option) { +) -> Expression { // Checking that the function is used with the correct name // Maybe this is not necessary but it is a sanity check if working_set.get_span_contents(spans[0]) != b"for" { - return ( - garbage(spans[0]), - Some(ParseError::UnknownState( - "internal error: Wrong call name for 'for' function".into(), - span(spans), - )), - ); + working_set.error(ParseError::UnknownState( + "internal error: Wrong call name for 'for' function".into(), + span(spans), + )); + return garbage(spans[0]); } // Parsing the spans and checking that they match the register signature @@ -222,21 +227,15 @@ pub fn parse_for( // Also, by creating a call, it can be checked if it matches the declaration signature let (call, call_span) = match working_set.find_decl(b"for", &Type::Any) { None => { - return ( - garbage(spans[0]), - Some(ParseError::UnknownState( - "internal error: for declaration not found".into(), - span(spans), - )), - ) + working_set.error(ParseError::UnknownState( + "internal error: for declaration not found".into(), + span(spans), + )); + return garbage(spans[0]); } Some(decl_id) => { working_set.enter_scope(); - let ParsedInternalCall { - call, - error: mut err, - output, - } = parse_internal_call( + let ParsedInternalCall { call, output } = parse_internal_call( working_set, spans[0], &spans[1..], @@ -269,17 +268,15 @@ pub fn parse_for( } } - err = check_call(call_span, &sig, &call).or(err); - if err.is_some() || call.has_flag("help") { - return ( - Expression { - expr: Expr::Call(call), - span: call_span, - ty: output, - custom_completion: None, - }, - err, - ); + let starting_error_count = working_set.parse_errors.len(); + check_call(working_set, call_span, &sig, &call); + if starting_error_count != working_set.parse_errors.len() || call.has_flag("help") { + return Expression { + expr: Expr::Call(call), + span: call_span, + ty: output, + custom_completion: None, + }; } (call, call_span) @@ -290,7 +287,6 @@ pub fn parse_for( let var_decl = call.positional_nth(0).expect("for call already checked"); let block = call.positional_nth(2).expect("for call already checked"); - let error = None; if let (Some(var_id), Some(block_id)) = (&var_decl.as_var(), block.as_block()) { let block = working_set.get_block_mut(block_id); @@ -306,15 +302,12 @@ pub fn parse_for( ); } - ( - Expression { - expr: Expr::Call(call), - span: call_span, - ty: Type::Any, - custom_completion: None, - }, - error, - ) + Expression { + expr: Expr::Call(call), + span: call_span, + ty: Type::Any, + custom_completion: None, + } } pub fn parse_def( @@ -322,7 +315,7 @@ pub fn parse_def( lite_command: &LiteCommand, module_name: Option<&[u8]>, expand_aliases_denylist: &[usize], -) -> (Pipeline, Option) { +) -> Pipeline { let spans = &lite_command.parts[..]; let (usage, extra_usage) = working_set.build_usage(&lite_command.comments); @@ -340,13 +333,11 @@ pub fn parse_def( let def_call = working_set.get_span_contents(name_span).to_vec(); if def_call != b"def" && def_call != b"def-env" { - return ( - garbage_pipeline(spans), - Some(ParseError::UnknownState( - "internal error: Wrong call name for def function".into(), - span(spans), - )), - ); + working_set.error(ParseError::UnknownState( + "internal error: Wrong call name for def function".into(), + span(spans), + )); + return garbage_pipeline(spans); } // Parsing the spans and checking that they match the register signature @@ -354,28 +345,27 @@ pub fn parse_def( // Also, by creating a call, it can be checked if it matches the declaration signature let (call, call_span) = match working_set.find_decl(&def_call, &Type::Any) { None => { - return ( - garbage_pipeline(spans), - Some(ParseError::UnknownState( - "internal error: def declaration not found".into(), - span(spans), - )), - ) + working_set.error(ParseError::UnknownState( + "internal error: def declaration not found".into(), + span(spans), + )); + return garbage_pipeline(spans); } Some(decl_id) => { working_set.enter_scope(); let (command_spans, rest_spans) = spans.split_at(split_id); - let ParsedInternalCall { - call, - error: mut err, - output, - } = parse_internal_call( + let starting_error_count = working_set.parse_errors.len(); + let ParsedInternalCall { call, output } = parse_internal_call( working_set, span(command_spans), rest_spans, decl_id, expand_aliases_denylist, ); + // This is to preserve the order of the errors so that + // the check errors below come first + let mut new_errors = working_set.parse_errors[starting_error_count..].to_vec(); + working_set.parse_errors.truncate(starting_error_count); working_set.exit_scope(); @@ -402,17 +392,16 @@ pub fn parse_def( } } - err = check_call(call_span, &sig, &call).or(err); - if err.is_some() || call.has_flag("help") { - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: call_span, - ty: output, - custom_completion: None, - }]), - err, - ); + let starting_error_count = working_set.parse_errors.len(); + check_call(working_set, call_span, &sig, &call); + working_set.parse_errors.append(&mut new_errors); + if starting_error_count != working_set.parse_errors.len() || call.has_flag("help") { + return Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: call_span, + ty: output, + custom_completion: None, + }]); } (call, call_span) @@ -424,38 +413,32 @@ pub fn parse_def( let sig = call.positional_nth(1).expect("def call already checked"); let block = call.positional_nth(2).expect("def call already checked"); - let mut error = None; - let name = if let Some(name) = name_expr.as_string() { if let Some(mod_name) = module_name { if name.as_bytes() == mod_name { let name_expr_span = name_expr.span; - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: call_span, - ty: Type::Any, - custom_completion: None, - }]), - Some(ParseError::NamedAsModule( - "command".to_string(), - name, - name_expr_span, - )), - ); + working_set.error(ParseError::NamedAsModule( + "command".to_string(), + name, + name_expr_span, + )); + return Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: call_span, + ty: Type::Any, + custom_completion: None, + }]); } } name } else { - return ( - garbage_pipeline(spans), - Some(ParseError::UnknownState( - "Could not get string from string expression".into(), - name_expr.span, - )), - ); + working_set.error(ParseError::UnknownState( + "Could not get string from string expression".into(), + name_expr.span, + )); + return garbage_pipeline(spans); }; if let (Some(mut signature), Some(block_id)) = (sig.as_signature(), block.as_block()) { @@ -508,27 +491,22 @@ pub fn parse_def( block.signature = signature; block.redirect_env = def_call == b"def-env"; } else { - error = error.or_else(|| { - Some(ParseError::InternalError( - "Predeclaration failed to add declaration".into(), - name_expr.span, - )) - }); + working_set.error(ParseError::InternalError( + "Predeclaration failed to add declaration".into(), + name_expr.span, + )); }; } // It's OK if it returns None: The decl was already merged in previous parse pass. working_set.merge_predecl(name.as_bytes()); - ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: call_span, - ty: Type::Any, - custom_completion: None, - }]), - error, - ) + Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: call_span, + ty: Type::Any, + custom_completion: None, + }]) } pub fn parse_extern( @@ -536,9 +514,8 @@ pub fn parse_extern( lite_command: &LiteCommand, module_name: Option<&[u8]>, expand_aliases_denylist: &[usize], -) -> (Pipeline, Option) { +) -> Pipeline { let spans = &lite_command.parts; - let mut error = None; let (usage, extra_usage) = working_set.build_usage(&lite_command.comments); @@ -554,13 +531,11 @@ pub fn parse_extern( let extern_call = working_set.get_span_contents(name_span).to_vec(); if extern_call != b"extern" { - return ( - garbage_pipeline(spans), - Some(ParseError::UnknownState( - "internal error: Wrong call name for extern function".into(), - span(spans), - )), - ); + working_set.error(ParseError::UnknownState( + "internal error: Wrong call name for extern function".into(), + span(spans), + )); + return garbage_pipeline(spans); } // Parsing the spans and checking that they match the register signature @@ -568,22 +543,18 @@ pub fn parse_extern( // Also, by creating a call, it can be checked if it matches the declaration signature let (call, call_span) = match working_set.find_decl(&extern_call, &Type::Any) { None => { - return ( - garbage_pipeline(spans), - Some(ParseError::UnknownState( - "internal error: def declaration not found".into(), - span(spans), - )), - ) + working_set.error(ParseError::UnknownState( + "internal error: def declaration not found".into(), + span(spans), + )); + return garbage_pipeline(spans); } Some(decl_id) => { working_set.enter_scope(); let (command_spans, rest_spans) = spans.split_at(split_id); - let ParsedInternalCall { - call, error: err, .. - } = parse_internal_call( + let ParsedInternalCall { call, .. } = parse_internal_call( working_set, span(command_spans), rest_spans, @@ -592,8 +563,6 @@ pub fn parse_extern( ); working_set.exit_scope(); - error = error.or(err); - let call_span = span(spans); //let decl = working_set.get_decl(decl_id); //let sig = decl.signature(); @@ -609,19 +578,17 @@ pub fn parse_extern( if let Some(mod_name) = module_name { if name.as_bytes() == mod_name { let name_expr_span = name_expr.span; - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: call_span, - ty: Type::Any, - custom_completion: None, - }]), - Some(ParseError::NamedAsModule( - "known external".to_string(), - name.clone(), - name_expr_span, - )), - ); + working_set.error(ParseError::NamedAsModule( + "known external".to_string(), + name.clone(), + name_expr_span, + )); + return Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: call_span, + ty: Type::Any, + custom_completion: None, + }]); } } @@ -651,36 +618,29 @@ pub fn parse_extern( *declaration = Box::new(decl); } else { - error = error.or_else(|| { - Some(ParseError::InternalError( - "Predeclaration failed to add declaration".into(), - spans[split_id], - )) - }); + working_set.error(ParseError::InternalError( + "Predeclaration failed to add declaration".into(), + spans[split_id], + )); }; } if let Some(name) = name_expr.as_string() { // It's OK if it returns None: The decl was already merged in previous parse pass. working_set.merge_predecl(name.as_bytes()); } else { - error = error.or_else(|| { - Some(ParseError::UnknownState( - "Could not get string from string expression".into(), - name_expr.span, - )) - }); + working_set.error(ParseError::UnknownState( + "Could not get string from string expression".into(), + name_expr.span, + )); } } - ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: call_span, - ty: Type::Any, - custom_completion: None, - }]), - error, - ) + Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: call_span, + ty: Type::Any, + custom_completion: None, + }]) } pub fn parse_alias( @@ -688,7 +648,7 @@ pub fn parse_alias( lite_command: &LiteCommand, module_name: Option<&[u8]>, expand_aliases_denylist: &[usize], -) -> (Pipeline, Option) { +) -> Pipeline { let spans = &lite_command.parts; let (name_span, split_id) = @@ -701,22 +661,22 @@ pub fn parse_alias( let name = working_set.get_span_contents(name_span); if name != b"alias" { - return ( - garbage_pipeline(spans), - Some(ParseError::InternalError( - "Alias statement unparsable".into(), - span(spans), - )), - ); + working_set.error(ParseError::InternalError( + "Alias statement unparsable".into(), + span(spans), + )); + return garbage_pipeline(spans); } - if let Some((span, err)) = check_name(working_set, spans) { - return (Pipeline::from_vec(vec![garbage(*span)]), Some(err)); + if let Some(span) = check_name(working_set, spans) { + return Pipeline::from_vec(vec![garbage(*span)]); } if let Some(decl_id) = working_set.find_decl(b"alias", &Type::Any) { let (command_spans, rest_spans) = spans.split_at(split_id); + let original_starting_error_count = working_set.parse_errors.len(); + let ParsedInternalCall { call: alias_call, output, @@ -728,6 +688,9 @@ pub fn parse_alias( decl_id, expand_aliases_denylist, ); + working_set + .parse_errors + .truncate(original_starting_error_count); let has_help_flag = alias_call.has_flag("help"); @@ -739,19 +702,17 @@ pub fn parse_alias( }]); if has_help_flag { - return (alias_pipeline, None); + return alias_pipeline; } let alias_name_expr = if let Some(expr) = alias_call.positional_nth(0) { expr } else { - return ( - garbage_pipeline(spans), - Some(ParseError::UnknownState( - "Missing positional after call check".to_string(), - span(spans), - )), - ); + working_set.error(ParseError::UnknownState( + "Missing positional after call check".to_string(), + span(spans), + )); + return garbage_pipeline(spans); }; let alias_name = if let Some(name) = alias_name_expr.as_string() { @@ -760,38 +721,31 @@ pub fn parse_alias( || name.parse::().is_ok() || name.parse::().is_ok() { - return ( - garbage_pipeline(spans), - Some(ParseError::AliasNotValid(alias_name_expr.span)), - ); + working_set.error(ParseError::AliasNotValid(alias_name_expr.span)); + return garbage_pipeline(spans); } else { name } } else { - return ( - garbage_pipeline(spans), - Some(ParseError::AliasNotValid(alias_name_expr.span)), - ); + working_set.error(ParseError::AliasNotValid(alias_name_expr.span)); + return garbage_pipeline(spans); }; if spans.len() >= split_id + 3 { if let Some(mod_name) = module_name { if alias_name.as_bytes() == mod_name { - return ( - alias_pipeline, - Some(ParseError::NamedAsModule( - "alias".to_string(), - alias_name, - spans[split_id], - )), - ); + working_set.error(ParseError::NamedAsModule( + "alias".to_string(), + alias_name, + spans[split_id], + )); + + return alias_pipeline; } if alias_name == "main" { - return ( - alias_pipeline, - Some(ParseError::ExportMainAliasNotAllowed(spans[split_id])), - ); + working_set.error(ParseError::ExportMainAliasNotAllowed(spans[split_id])); + return alias_pipeline; } } @@ -809,26 +763,27 @@ pub fn parse_alias( ) { // TODO: Maybe we need to implement a Display trait for Expression? - let (expr, _) = parse_expression( + let starting_error_count = working_set.parse_errors.len(); + let expr = parse_expression( working_set, replacement_spans, expand_aliases_denylist, false, ); + working_set.parse_errors.truncate(starting_error_count); let msg = format!("{:?}", expr.expr); let msg_parts: Vec<&str> = msg.split('(').collect(); - return ( - alias_pipeline, - Some(ParseError::CantAliasExpression( - msg_parts[0].to_string(), - replacement_spans[0], - )), - ); + working_set.error(ParseError::CantAliasExpression( + msg_parts[0].to_string(), + replacement_spans[0], + )); + return alias_pipeline; } - let (expr, err) = parse_call( + let starting_error_count = working_set.parse_errors.len(); + let expr = parse_call( working_set, replacement_spans, replacement_spans[0], @@ -836,11 +791,16 @@ pub fn parse_alias( false, // TODO: Should this be set properly??? ); - if let Some(e) = err { - if let ParseError::MissingPositional(..) = e { - // ignore missing required positional - } else { - return (garbage_pipeline(replacement_spans), Some(e)); + if starting_error_count != working_set.parse_errors.len() { + if let Some(e) = working_set.parse_errors.get(starting_error_count) { + if let ParseError::MissingPositional(..) = e { + working_set + .parse_errors + .truncate(original_starting_error_count); + // ignore missing required positional + } else { + return garbage_pipeline(replacement_spans); + } } } @@ -854,17 +814,15 @@ pub fn parse_alias( if cmd.is_parser_keyword() && !ALIASABLE_PARSER_KEYWORDS.contains(&cmd.name().as_bytes()) { - return ( - alias_pipeline, - Some(ParseError::CantAliasKeyword( - ALIASABLE_PARSER_KEYWORDS - .iter() - .map(|bytes| String::from_utf8_lossy(bytes).to_string()) - .collect::>() - .join(", "), - rhs_call.head, - )), - ); + working_set.error(ParseError::CantAliasKeyword( + ALIASABLE_PARSER_KEYWORDS + .iter() + .map(|bytes| String::from_utf8_lossy(bytes).to_string()) + .collect::>() + .join(", "), + rhs_call.head, + )); + return alias_pipeline; } (Some(cmd.clone_box()), expr) @@ -874,13 +832,11 @@ pub fn parse_alias( .. } => (None, expr), _ => { - return ( - alias_pipeline, - Some(ParseError::InternalError( - "Parsed call not a call".into(), - expr.span, - )), - ) + working_set.error(ParseError::InternalError( + "Parsed call not a call".into(), + expr.span, + )); + return alias_pipeline; } }; @@ -893,26 +849,23 @@ pub fn parse_alias( working_set.add_decl(Box::new(decl)); } - let err = if spans.len() < 4 { - Some(ParseError::IncorrectValue( + if spans.len() < 4 { + working_set.error(ParseError::IncorrectValue( "Incomplete alias".into(), span(&spans[..split_id]), "incomplete alias".into(), - )) - } else { - None - }; + )); + } - return (alias_pipeline, err); + return alias_pipeline; } - ( - garbage_pipeline(spans), - Some(ParseError::InternalError( - "Alias statement unparsable".into(), - span(spans), - )), - ) + working_set.error(ParseError::InternalError( + "Alias statement unparsable".into(), + span(spans), + )); + + garbage_pipeline(spans) } pub fn parse_old_alias( @@ -920,7 +873,7 @@ pub fn parse_old_alias( lite_command: &LiteCommand, module_name: Option<&[u8]>, expand_aliases_denylist: &[usize], -) -> (Pipeline, Option) { +) -> Pipeline { let spans = &lite_command.parts; // if the call is "alias", turn it into "print $nu.scope.aliases" @@ -958,17 +911,14 @@ pub fn parse_old_alias( redirect_stderr: false, parser_info: HashMap::new(), })); - return ( - Pipeline::from_vec(vec![Expression { - expr: print_call, - span: spans[0], - ty: Type::Any, - custom_completion: None, - }]), - None, - ); + return Pipeline::from_vec(vec![Expression { + expr: print_call, + span: spans[0], + ty: Type::Any, + custom_completion: None, + }]); } - return (Pipeline::from_vec(vec![expr]), None); + return Pipeline::from_vec(vec![expr]); } let (name_span, split_id) = @@ -981,8 +931,8 @@ pub fn parse_old_alias( let name = working_set.get_span_contents(name_span); if name == b"old-alias" { - if let Some((span, err)) = check_name(working_set, spans) { - return (Pipeline::from_vec(vec![garbage(*span)]), Some(err)); + if let Some(span) = check_name(working_set, spans) { + return Pipeline::from_vec(vec![garbage(*span)]); } if let Some(decl_id) = working_set.find_decl(b"alias", &Type::Any) { @@ -997,15 +947,12 @@ pub fn parse_old_alias( ); if call.has_flag("help") { - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: span(spans), - ty: output, - custom_completion: None, - }]), - None, - ); + return Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: span(spans), + ty: output, + custom_completion: None, + }]); } if spans.len() >= split_id + 3 { @@ -1022,31 +969,27 @@ pub fn parse_old_alias( if let Some(mod_name) = module_name { if alias_name == mod_name { - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: span(spans), - ty: output, - custom_completion: None, - }]), - Some(ParseError::NamedAsModule( - "alias".to_string(), - String::from_utf8_lossy(&alias_name).to_string(), - spans[split_id], - )), - ); + working_set.error(ParseError::NamedAsModule( + "alias".to_string(), + String::from_utf8_lossy(&alias_name).to_string(), + spans[split_id], + )); + return Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: span(spans), + ty: output, + custom_completion: None, + }]); } if &alias_name == b"main" { - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: span(spans), - ty: output, - custom_completion: None, - }]), - Some(ParseError::ExportMainAliasNotAllowed(spans[split_id])), - ); + working_set.error(ParseError::ExportMainAliasNotAllowed(spans[split_id])); + return Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: span(spans), + ty: output, + custom_completion: None, + }]); } } @@ -1060,44 +1003,35 @@ pub fn parse_old_alias( || checked_name.parse::().is_ok() || checked_name.parse::().is_ok() { - return ( - Pipeline::from_vec(vec![garbage(name_span)]), - Some(ParseError::AliasNotValid(name_span)), - ); + working_set.error(ParseError::AliasNotValid(name_span)); + return Pipeline::from_vec(vec![garbage(name_span)]); } working_set.add_alias(alias_name, replacement, lite_command.comments.clone()); } - let err = if spans.len() < 4 { - Some(ParseError::IncorrectValue( + if spans.len() < 4 { + working_set.error(ParseError::IncorrectValue( "Incomplete alias".into(), span(&spans[..split_id]), "incomplete alias".into(), )) - } else { - None - }; + } - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: span(spans), - ty: Type::Any, - custom_completion: None, - }]), - err, - ); + return Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: span(spans), + ty: Type::Any, + custom_completion: None, + }]); } } - ( - garbage_pipeline(spans), - Some(ParseError::InternalError( - "Alias statement unparsable".into(), - span(spans), - )), - ) + working_set.error(ParseError::InternalError( + "Alias statement unparsable".into(), + span(spans), + )); + garbage_pipeline(spans) } // This one will trigger if `export` appears during eval, e.g., in a script @@ -1105,7 +1039,7 @@ pub fn parse_export_in_block( working_set: &mut StateWorkingSet, lite_command: &LiteCommand, expand_aliases_denylist: &[usize], -) -> (Pipeline, Option) { +) -> Pipeline { let call_span = span(&lite_command.parts); let full_name = if lite_command.parts.len() > 1 { @@ -1121,12 +1055,7 @@ pub fn parse_export_in_block( }; if let Some(decl_id) = working_set.find_decl(&full_name, &Type::Any) { - let ParsedInternalCall { - call, - error: mut err, - output, - .. - } = parse_internal_call( + let ParsedInternalCall { call, output, .. } = parse_internal_call( working_set, if full_name == b"export" { lite_command.parts[0] @@ -1143,41 +1072,35 @@ pub fn parse_export_in_block( ); let decl = working_set.get_decl(decl_id); - err = check_call(call_span, &decl.signature(), &call).or(err); - if err.is_some() || call.has_flag("help") { - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: call_span, - ty: output, - custom_completion: None, - }]), - err, - ); + let starting_error_count = working_set.parse_errors.len(); + check_call(working_set, call_span, &decl.signature(), &call); + if starting_error_count != working_set.parse_errors.len() || call.has_flag("help") { + return Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: call_span, + ty: output, + custom_completion: None, + }]); } } else { - return ( - garbage_pipeline(&lite_command.parts), - Some(ParseError::UnknownState( - format!( - "internal error: '{}' declaration not found", - String::from_utf8_lossy(&full_name) - ), - span(&lite_command.parts), - )), - ); + working_set.error(ParseError::UnknownState( + format!( + "internal error: '{}' declaration not found", + String::from_utf8_lossy(&full_name) + ), + span(&lite_command.parts), + )); + return garbage_pipeline(&lite_command.parts); }; if &full_name == b"export" { // export by itself is meaningless - return ( - garbage_pipeline(&lite_command.parts), - Some(ParseError::UnexpectedKeyword( - "export".into(), - lite_command.parts[0], - )), - ); + working_set.error(ParseError::UnexpectedKeyword( + "export".into(), + lite_command.parts[0], + )); + return garbage_pipeline(&lite_command.parts); } match full_name.as_slice() { @@ -1189,18 +1112,19 @@ pub fn parse_export_in_block( parse_def(working_set, lite_command, None, expand_aliases_denylist) } b"export use" => { - let (pipeline, _, err) = + let (pipeline, _) = parse_use(working_set, &lite_command.parts, expand_aliases_denylist); - (pipeline, err) + pipeline } b"export extern" => parse_extern(working_set, lite_command, None, expand_aliases_denylist), - _ => ( - garbage_pipeline(&lite_command.parts), - Some(ParseError::UnexpectedKeyword( + _ => { + working_set.error(ParseError::UnexpectedKeyword( String::from_utf8_lossy(&full_name).to_string(), lite_command.parts[0], - )), - ), + )); + + garbage_pipeline(&lite_command.parts) + } } } @@ -1210,45 +1134,35 @@ pub fn parse_export_in_module( lite_command: &LiteCommand, module_name: &[u8], expand_aliases_denylist: &[usize], -) -> (Pipeline, Vec, Option) { +) -> (Pipeline, Vec) { let spans = &lite_command.parts[..]; - let mut error = None; let export_span = if let Some(sp) = spans.get(0) { if working_set.get_span_contents(*sp) != b"export" { - return ( - garbage_pipeline(spans), - vec![], - Some(ParseError::UnknownState( - "expected export statement".into(), - span(spans), - )), - ); + working_set.error(ParseError::UnknownState( + "expected export statement".into(), + span(spans), + )); + return (garbage_pipeline(spans), vec![]); } *sp } else { - return ( - garbage_pipeline(spans), - vec![], - Some(ParseError::UnknownState( - "got empty input for parsing export statement".into(), - span(spans), - )), - ); + working_set.error(ParseError::UnknownState( + "got empty input for parsing export statement".into(), + span(spans), + )); + return (garbage_pipeline(spans), vec![]); }; let export_decl_id = if let Some(id) = working_set.find_decl(b"export", &Type::Any) { id } else { - return ( - garbage_pipeline(spans), - vec![], - Some(ParseError::InternalError( - "missing export command".into(), - export_span, - )), - ); + working_set.error(ParseError::InternalError( + "missing export command".into(), + export_span, + )); + return (garbage_pipeline(spans), vec![]); }; let mut call = Box::new(Call { @@ -1268,26 +1182,22 @@ pub fn parse_export_in_module( comments: lite_command.comments.clone(), parts: spans[1..].to_vec(), }; - let (pipeline, err) = parse_def( + let pipeline = parse_def( working_set, &lite_command, Some(module_name), expand_aliases_denylist, ); - error = error.or(err); let export_def_decl_id = if let Some(id) = working_set.find_decl(b"export def", &Type::Any) { id } else { - return ( - garbage_pipeline(spans), - vec![], - Some(ParseError::InternalError( - "missing 'export def' command".into(), - export_span, - )), - ); + working_set.error(ParseError::InternalError( + "missing 'export def' command".into(), + export_span, + )); + return (garbage_pipeline(spans), vec![]); }; // Trying to warp the 'def' call into the 'export def' in a very clumsy way @@ -1304,12 +1214,10 @@ pub fn parse_export_in_module( call.head = span(&spans[0..=1]); call.decl_id = export_def_decl_id; } else { - error = error.or_else(|| { - Some(ParseError::InternalError( - "unexpected output from parsing a definition".into(), - span(&spans[1..]), - )) - }); + working_set.error(ParseError::InternalError( + "unexpected output from parsing a definition".into(), + span(&spans[1..]), + )); }; let mut result = vec![]; @@ -1324,12 +1232,10 @@ pub fn parse_export_in_module( id: decl_id, }); } else { - error = error.or_else(|| { - Some(ParseError::InternalError( - "failed to find added declaration".into(), - span(&spans[1..]), - )) - }); + working_set.error(ParseError::InternalError( + "failed to find added declaration".into(), + span(&spans[1..]), + )); } } @@ -1340,26 +1246,22 @@ pub fn parse_export_in_module( comments: lite_command.comments.clone(), parts: spans[1..].to_vec(), }; - let (pipeline, err) = parse_def( + let pipeline = parse_def( working_set, &lite_command, Some(module_name), expand_aliases_denylist, ); - error = error.or(err); let export_def_decl_id = if let Some(id) = working_set.find_decl(b"export def-env", &Type::Any) { id } else { - return ( - garbage_pipeline(spans), - vec![], - Some(ParseError::InternalError( - "missing 'export def-env' command".into(), - export_span, - )), - ); + working_set.error(ParseError::InternalError( + "missing 'export def-env' command".into(), + export_span, + )); + return (garbage_pipeline(spans), vec![]); }; // Trying to warp the 'def' call into the 'export def' in a very clumsy way @@ -1376,12 +1278,10 @@ pub fn parse_export_in_module( call.head = span(&spans[0..=1]); call.decl_id = export_def_decl_id; } else { - error = error.or_else(|| { - Some(ParseError::InternalError( - "unexpected output from parsing a definition".into(), - span(&spans[1..]), - )) - }); + working_set.error(ParseError::InternalError( + "unexpected output from parsing a definition".into(), + span(&spans[1..]), + )); }; let mut result = vec![]; @@ -1398,12 +1298,10 @@ pub fn parse_export_in_module( id: decl_id, }); } else { - error = error.or_else(|| { - Some(ParseError::InternalError( - "failed to find added declaration".into(), - span(&spans[1..]), - )) - }); + working_set.error(ParseError::InternalError( + "failed to find added declaration".into(), + span(&spans[1..]), + )); } result @@ -1413,26 +1311,22 @@ pub fn parse_export_in_module( comments: lite_command.comments.clone(), parts: spans[1..].to_vec(), }; - let (pipeline, err) = parse_extern( + let pipeline = parse_extern( working_set, &lite_command, Some(module_name), expand_aliases_denylist, ); - error = error.or(err); let export_def_decl_id = if let Some(id) = working_set.find_decl(b"export extern", &Type::Any) { id } else { - return ( - garbage_pipeline(spans), - vec![], - Some(ParseError::InternalError( - "missing 'export extern' command".into(), - export_span, - )), - ); + working_set.error(ParseError::InternalError( + "missing 'export extern' command".into(), + export_span, + )); + return (garbage_pipeline(spans), vec![]); }; // Trying to warp the 'def' call into the 'export def' in a very clumsy way @@ -1449,12 +1343,10 @@ pub fn parse_export_in_module( call.head = span(&spans[0..=1]); call.decl_id = export_def_decl_id; } else { - error = error.or_else(|| { - Some(ParseError::InternalError( - "unexpected output from parsing a definition".into(), - span(&spans[1..]), - )) - }); + working_set.error(ParseError::InternalError( + "unexpected output from parsing a definition".into(), + span(&spans[1..]), + )); }; let mut result = vec![]; @@ -1471,12 +1363,10 @@ pub fn parse_export_in_module( id: decl_id, }); } else { - error = error.or_else(|| { - Some(ParseError::InternalError( - "failed to find added declaration".into(), - span(&spans[1..]), - )) - }); + working_set.error(ParseError::InternalError( + "failed to find added declaration".into(), + span(&spans[1..]), + )); } result @@ -1486,26 +1376,22 @@ pub fn parse_export_in_module( comments: lite_command.comments.clone(), parts: spans[1..].to_vec(), }; - let (pipeline, err) = parse_old_alias( + let pipeline = parse_old_alias( working_set, &lite_command, Some(module_name), expand_aliases_denylist, ); - error = error.or(err); let export_alias_decl_id = if let Some(id) = working_set.find_decl(b"export old-alias", &Type::Any) { id } else { - return ( - garbage_pipeline(spans), - vec![], - Some(ParseError::InternalError( - "missing 'export old-alias' command".into(), - export_span, - )), - ); + working_set.error(ParseError::InternalError( + "missing 'export old-alias' command".into(), + export_span, + )); + return (garbage_pipeline(spans), vec![]); }; // Trying to warp the 'old-alias' call into the 'export old-alias' in a very clumsy way @@ -1522,12 +1408,10 @@ pub fn parse_export_in_module( call.head = span(&spans[0..=1]); call.decl_id = export_alias_decl_id; } else { - error = error.or_else(|| { - Some(ParseError::InternalError( - "unexpected output from parsing a definition".into(), - span(&spans[1..]), - )) - }); + working_set.error(ParseError::InternalError( + "unexpected output from parsing a definition".into(), + span(&spans[1..]), + )); }; let mut result = vec![]; @@ -1544,12 +1428,10 @@ pub fn parse_export_in_module( id: alias_id, }); } else { - error = error.or_else(|| { - Some(ParseError::InternalError( - "failed to find added alias".into(), - span(&spans[1..]), - )) - }); + working_set.error(ParseError::InternalError( + "failed to find added alias".into(), + span(&spans[1..]), + )); } result @@ -1559,26 +1441,22 @@ pub fn parse_export_in_module( comments: lite_command.comments.clone(), parts: spans[1..].to_vec(), }; - let (pipeline, err) = parse_alias( + let pipeline = parse_alias( working_set, &lite_command, Some(module_name), expand_aliases_denylist, ); - error = error.or(err); let export_alias_decl_id = if let Some(id) = working_set.find_decl(b"export alias", &Type::Any) { id } else { - return ( - garbage_pipeline(spans), - vec![], - Some(ParseError::InternalError( - "missing 'export alias' command".into(), - export_span, - )), - ); + working_set.error(ParseError::InternalError( + "missing 'export alias' command".into(), + export_span, + )); + return (garbage_pipeline(spans), vec![]); }; // Trying to warp the 'alias' call into the 'export alias' in a very clumsy way @@ -1595,12 +1473,10 @@ pub fn parse_export_in_module( call.head = span(&spans[0..=1]); call.decl_id = export_alias_decl_id; } else { - error = error.or_else(|| { - Some(ParseError::InternalError( - "unexpected output from parsing a definition".into(), - span(&spans[1..]), - )) - }); + working_set.error(ParseError::InternalError( + "unexpected output from parsing a definition".into(), + span(&spans[1..]), + )); }; let mut result = vec![]; @@ -1617,12 +1493,10 @@ pub fn parse_export_in_module( id: alias_id, }); } else { - error = error.or_else(|| { - Some(ParseError::InternalError( - "failed to find added alias".into(), - span(&spans[1..]), - )) - }); + working_set.error(ParseError::InternalError( + "failed to find added alias".into(), + span(&spans[1..]), + )); } result @@ -1632,22 +1506,18 @@ pub fn parse_export_in_module( comments: lite_command.comments.clone(), parts: spans[1..].to_vec(), }; - let (pipeline, exportables, err) = + let (pipeline, exportables) = parse_use(working_set, &lite_command.parts, expand_aliases_denylist); - error = error.or(err); let export_use_decl_id = if let Some(id) = working_set.find_decl(b"export use", &Type::Any) { id } else { - return ( - garbage_pipeline(spans), - vec![], - Some(ParseError::InternalError( - "missing 'export use' command".into(), - export_span, - )), - ); + working_set.error(ParseError::InternalError( + "missing 'export use' command".into(), + export_span, + )); + return (garbage_pipeline(spans), vec![]); }; // Trying to warp the 'use' call into the 'export use' in a very clumsy way @@ -1664,36 +1534,30 @@ pub fn parse_export_in_module( call.head = span(&spans[0..=1]); call.decl_id = export_use_decl_id; } else { - error = error.or_else(|| { - Some(ParseError::InternalError( - "unexpected output from parsing a definition".into(), - span(&spans[1..]), - )) - }); + working_set.error(ParseError::InternalError( + "unexpected output from parsing a definition".into(), + span(&spans[1..]), + )); }; exportables } _ => { - error = error.or_else(|| { - Some(ParseError::Expected( - // TODO: Fill in more keywords as they come - "def, def-env, alias, use, or env keyword".into(), - spans[1], - )) - }); + working_set.error(ParseError::Expected( + // TODO: Fill in more keywords as they come + "def, def-env, alias, use, or env keyword".into(), + spans[1], + )); vec![] } } } else { - error = error.or_else(|| { - Some(ParseError::MissingPositional( - "def, def-env, alias, or env keyword".into(), // TODO: keep filling more keywords as they come - Span::new(export_span.end, export_span.end), - "'def', `def-env`, `alias`, or 'env' keyword.".to_string(), - )) - }); + working_set.error(ParseError::MissingPositional( + "def, def-env, alias, or env keyword".into(), // TODO: keep filling more keywords as they come + Span::new(export_span.end, export_span.end), + "'def', `def-env`, `alias`, or 'env' keyword.".to_string(), + )); vec![] }; @@ -1706,7 +1570,6 @@ pub fn parse_export_in_module( custom_completion: None, }]), exportables, - error, ) } @@ -1714,37 +1577,27 @@ pub fn parse_export_env( working_set: &mut StateWorkingSet, spans: &[Span], expand_aliases_denylist: &[usize], -) -> (Pipeline, Option, Option) { +) -> (Pipeline, Option) { if !spans.is_empty() && working_set.get_span_contents(spans[0]) != b"export-env" { - return ( - garbage_pipeline(spans), - None, - Some(ParseError::UnknownState( - "internal error: Wrong call name for 'export-env' command".into(), - span(spans), - )), - ); + working_set.error(ParseError::UnknownState( + "internal error: Wrong call name for 'export-env' command".into(), + span(spans), + )); + return (garbage_pipeline(spans), None); } if spans.len() < 2 { - return ( - garbage_pipeline(spans), - None, - Some(ParseError::MissingPositional( - "block".into(), - span(spans), - "export-env ".into(), - )), - ); + working_set.error(ParseError::MissingPositional( + "block".into(), + span(spans), + "export-env ".into(), + )); + return (garbage_pipeline(spans), None); } let call = match working_set.find_decl(b"export-env", &Type::Any) { Some(decl_id) => { - let ParsedInternalCall { - call, - error: mut err, - output, - } = parse_internal_call( + let ParsedInternalCall { call, output } = parse_internal_call( working_set, spans[0], &[spans[1]], @@ -1755,8 +1608,9 @@ pub fn parse_export_env( let call_span = span(spans); - err = check_call(call_span, &decl.signature(), &call).or(err); - if err.is_some() || call.has_flag("help") { + let starting_error_count = working_set.parse_errors.len(); + check_call(working_set, call_span, &decl.signature(), &call); + if starting_error_count != working_set.parse_errors.len() || call.has_flag("help") { return ( Pipeline::from_vec(vec![Expression { expr: Expr::Call(call), @@ -1765,21 +1619,17 @@ pub fn parse_export_env( custom_completion: None, }]), None, - err, ); } call } None => { - return ( - garbage_pipeline(spans), - None, - Some(ParseError::UnknownState( - "internal error: 'export-env' declaration not found".into(), - span(spans), - )), - ) + working_set.error(ParseError::UnknownState( + "internal error: 'export-env' declaration not found".into(), + span(spans), + )); + return (garbage_pipeline(spans), None); } }; @@ -1787,24 +1637,18 @@ pub fn parse_export_env( if let Some(block_id) = block.as_block() { block_id } else { - return ( - garbage_pipeline(spans), - None, - Some(ParseError::UnknownState( - "internal error: 'export-env' block is not a block".into(), - block.span, - )), - ); + working_set.error(ParseError::UnknownState( + "internal error: 'export-env' block is not a block".into(), + block.span, + )); + return (garbage_pipeline(spans), None); } } else { - return ( - garbage_pipeline(spans), - None, - Some(ParseError::UnknownState( - "internal error: 'export-env' block is missing".into(), - span(spans), - )), - ); + working_set.error(ParseError::UnknownState( + "internal error: 'export-env' block is missing".into(), + span(spans), + )); + return (garbage_pipeline(spans), None); }; let pipeline = Pipeline::from_vec(vec![Expression { @@ -1814,7 +1658,7 @@ pub fn parse_export_env( custom_completion: None, }]); - (pipeline, Some(block_id), None) + (pipeline, Some(block_id)) } fn collect_first_comments(tokens: &[Token]) -> Vec { @@ -1852,20 +1696,22 @@ pub fn parse_module_block( span: Span, module_name: &[u8], expand_aliases_denylist: &[usize], -) -> (Block, Module, Vec, Option) { - let mut error = None; - +) -> (Block, Module, Vec) { working_set.enter_scope(); let source = working_set.get_span_contents(span); let (output, err) = lex(source, span.start, &[], &[], false); - error = error.or(err); + if let Some(err) = err { + working_set.error(err) + } let module_comments = collect_first_comments(&output); let (output, err) = lite_parse(&output); - error = error.or(err); + if let Some(err) = err { + working_set.error(err) + } for pipeline in &output.block { if pipeline.commands.len() == 1 { @@ -1886,82 +1732,67 @@ pub fn parse_module_block( LiteElement::Command(_, command) => { let name = working_set.get_span_contents(command.parts[0]); - let (pipeline, err) = match name { + match name { b"def" | b"def-env" => { - let (pipeline, err) = parse_def( + parse_def( working_set, command, None, // using commands named as the module locally is OK expand_aliases_denylist, - ); - - (pipeline, err) + ) } b"extern" => { - let (pipeline, err) = parse_extern( - working_set, - command, - None, - expand_aliases_denylist, - ); - - (pipeline, err) + parse_extern(working_set, command, None, expand_aliases_denylist) } b"old-alias" => { - let (pipeline, err) = parse_old_alias( + parse_old_alias( working_set, command, None, // using aliases named as the module locally is OK expand_aliases_denylist, - ); - - (pipeline, err) + ) } b"alias" => { - let (pipeline, err) = parse_alias( + parse_alias( working_set, command, None, // using aliases named as the module locally is OK expand_aliases_denylist, - ); - - (pipeline, err) + ) } b"use" => { - let (pipeline, _, err) = + let (pipeline, _) = parse_use(working_set, &command.parts, expand_aliases_denylist); - (pipeline, err) + pipeline } b"export" => { - let (pipe, exportables, err) = parse_export_in_module( + let (pipe, exportables) = parse_export_in_module( working_set, command, module_name, expand_aliases_denylist, ); - if err.is_none() { - for exportable in exportables { - match exportable { - Exportable::Decl { name, id } => { - if &name == b"main" { - module.main = Some(id); - } else { - module.add_decl(name, id); - } - } - Exportable::Alias { name, id } => { - module.add_alias(name, id); + for exportable in exportables { + match exportable { + Exportable::Decl { name, id } => { + if &name == b"main" { + module.main = Some(id); + } else { + module.add_decl(name, id); } } + Exportable::Alias { name, id } => { + module.add_alias(name, id); + } } } - (pipe, err) + pipe } b"export-env" => { - let (pipe, maybe_env_block, err) = parse_export_env( + let (pipe, maybe_env_block) = parse_export_env( working_set, &command.parts, expand_aliases_denylist, @@ -1971,21 +1802,17 @@ pub fn parse_module_block( module.add_env_block(block_id); } - (pipe, err) + pipe } - _ => ( - garbage_pipeline(&command.parts), - Some(ParseError::ExpectedKeyword( + _ => { + working_set.error(ParseError::ExpectedKeyword( "def or export keyword".into(), command.parts[0], - )), - ), - }; - if error.is_none() { - error = err; - } + )); - pipeline + garbage_pipeline(&command.parts) + } + } } LiteElement::Redirection(_, _, command) => garbage_pipeline(&command.parts), LiteElement::SeparateRedirection { @@ -1993,7 +1820,7 @@ pub fn parse_module_block( } => garbage_pipeline(&command.parts), } } else { - error = Some(ParseError::Expected("not a pipeline".into(), span)); + working_set.error(ParseError::Expected("not a pipeline".into(), span)); garbage_pipeline(&[span]) } }) @@ -2001,26 +1828,24 @@ pub fn parse_module_block( working_set.exit_scope(); - (block, module, module_comments, error) + (block, module, module_comments) } pub fn parse_module( working_set: &mut StateWorkingSet, lite_command: &LiteCommand, expand_aliases_denylist: &[usize], -) -> (Pipeline, Option) { +) -> Pipeline { // TODO: Currently, module is closing over its parent scope (i.e., defs in the parent scope are // visible and usable in this module's scope). We want to disable that for files. let spans = &lite_command.parts; let mut module_comments = lite_command.comments.clone(); - let mut error = None; let bytes = working_set.get_span_contents(spans[0]); if bytes == b"module" && spans.len() >= 3 { - let (module_name_expr, err) = parse_string(working_set, spans[1], expand_aliases_denylist); - error = error.or(err); + let module_name_expr = parse_string(working_set, spans[1], expand_aliases_denylist); let module_name = module_name_expr .as_string() @@ -2034,27 +1859,24 @@ pub fn parse_module( if block_bytes.starts_with(b"{") { start += 1; } else { - return ( - garbage_pipeline(spans), - Some(ParseError::Expected("block".into(), block_span)), - ); + working_set.error(ParseError::Expected("block".into(), block_span)); + return garbage_pipeline(spans); } if block_bytes.ends_with(b"}") { end -= 1; } else { - error = error.or_else(|| Some(ParseError::Unclosed("}".into(), Span::new(end, end)))); + working_set.error(ParseError::Unclosed("}".into(), Span::new(end, end))); } let block_span = Span::new(start, end); - let (block, module, inner_comments, err) = parse_module_block( + let (block, module, inner_comments) = parse_module_block( working_set, block_span, module_name.as_bytes(), expand_aliases_denylist, ); - error = error.or(err); let block_id = working_set.add_block(block); @@ -2084,23 +1906,19 @@ pub fn parse_module( parser_info: HashMap::new(), }); - ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: span(spans), - ty: Type::Any, - custom_completion: None, - }]), - error, - ) + Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: span(spans), + ty: Type::Any, + custom_completion: None, + }]) } else { - ( - garbage_pipeline(spans), - Some(ParseError::UnknownState( - "Expected structure: module {}".into(), - span(spans), - )), - ) + working_set.error(ParseError::UnknownState( + "Expected structure: module {}".into(), + span(spans), + )); + + garbage_pipeline(spans) } } @@ -2108,7 +1926,7 @@ pub fn parse_use( working_set: &mut StateWorkingSet, spans: &[Span], expand_aliases_denylist: &[usize], -) -> (Pipeline, Vec, Option) { +) -> (Pipeline, Vec) { let (name_span, split_id) = if spans.len() > 1 && working_set.get_span_contents(spans[0]) == b"export" { (spans[1], 2) @@ -2118,36 +1936,26 @@ pub fn parse_use( let use_call = working_set.get_span_contents(name_span).to_vec(); if use_call != b"use" { - return ( - garbage_pipeline(spans), - vec![], - Some(ParseError::UnknownState( - "internal error: Wrong call name for 'use' command".into(), - span(spans), - )), - ); + working_set.error(ParseError::UnknownState( + "internal error: Wrong call name for 'use' command".into(), + span(spans), + )); + return (garbage_pipeline(spans), vec![]); } if working_set.get_span_contents(name_span) != b"use" { - return ( - garbage_pipeline(spans), - vec![], - Some(ParseError::UnknownState( - "internal error: Wrong call name for 'use' command".into(), - span(spans), - )), - ); + working_set.error(ParseError::UnknownState( + "internal error: Wrong call name for 'use' command".into(), + span(spans), + )); + return (garbage_pipeline(spans), vec![]); } let (call, call_span, args_spans) = match working_set.find_decl(b"use", &Type::Any) { Some(decl_id) => { let (command_spans, rest_spans) = spans.split_at(split_id); - let ParsedInternalCall { - call, - error: mut err, - output, - } = parse_internal_call( + let ParsedInternalCall { call, output } = parse_internal_call( working_set, span(command_spans), rest_spans, @@ -2158,8 +1966,9 @@ pub fn parse_use( let call_span = span(spans); - err = check_call(call_span, &decl.signature(), &call).or(err); - if err.is_some() || call.has_flag("help") { + let starting_error_count = working_set.parse_errors.len(); + check_call(working_set, call_span, &decl.signature(), &call); + if starting_error_count != working_set.parse_errors.len() || call.has_flag("help") { return ( Pipeline::from_vec(vec![Expression { expr: Expr::Call(call), @@ -2168,29 +1977,22 @@ pub fn parse_use( custom_completion: None, }]), vec![], - err, ); } (call, call_span, rest_spans) } None => { - return ( - garbage_pipeline(spans), - vec![], - Some(ParseError::UnknownState( - "internal error: 'use' declaration not found".into(), - span(spans), - )), - ) + working_set.error(ParseError::UnknownState( + "internal error: 'use' declaration not found".into(), + span(spans), + )); + return (garbage_pipeline(spans), vec![]); } }; - let mut error = None; - - let (import_pattern_expr, err) = + let import_pattern_expr = parse_import_pattern(working_set, args_spans, expand_aliases_denylist); - error = error.or(err); let import_pattern = if let Expression { expr: Expr::ImportPattern(import_pattern), @@ -2199,14 +2001,11 @@ pub fn parse_use( { import_pattern.clone() } else { - return ( - garbage_pipeline(spans), - vec![], - Some(ParseError::UnknownState( - "internal error: Import pattern positional is not import pattern".into(), - import_pattern_expr.span, - )), - ); + working_set.error(ParseError::UnknownState( + "internal error: Import pattern positional is not import pattern".into(), + import_pattern_expr.span, + )); + return (garbage_pipeline(spans), vec![]); }; let cwd = working_set.get_cwd(); @@ -2219,10 +2018,14 @@ pub fn parse_use( // It could be a file // TODO: Do not close over when loading module from file? + let starting_error_count = working_set.parse_errors.len(); let (module_filename, err) = unescape_unquote_string(&import_pattern.head.name, import_pattern.head.span); + if let Some(err) = err { + working_set.error(err); + } - if err.is_none() { + if starting_error_count == working_set.parse_errors.len() { if let Some(module_path) = find_in_dirs(&module_filename, working_set, &cwd, LIB_DIRS_VAR) { @@ -2242,6 +2045,10 @@ pub fn parse_use( let msg = files.join("\nuses "); + working_set.error(ParseError::CyclicalModuleImport( + msg, + import_pattern.head.span, + )); return ( Pipeline::from_vec(vec![Expression { expr: Expr::Call(call), @@ -2250,16 +2057,13 @@ pub fn parse_use( custom_completion: None, }]), vec![], - Some(ParseError::CyclicalModuleImport( - msg, - import_pattern.head.span, - )), ); } let module_name = if let Some(stem) = module_path.file_stem() { stem.to_string_lossy().to_string() } else { + working_set.error(ParseError::ModuleNotFound(import_pattern.head.span)); return ( Pipeline::from_vec(vec![Expression { expr: Expr::Call(call), @@ -2268,7 +2072,6 @@ pub fn parse_use( custom_completion: None, }]), vec![], - Some(ParseError::ModuleNotFound(import_pattern.head.span)), ); }; @@ -2292,13 +2095,12 @@ pub fn parse_use( working_set.parsed_module_files.push(module_path); // Parse the module - let (block, module, module_comments, err) = parse_module_block( + let (block, module, module_comments) = parse_module_block( working_set, Span::new(span_start, span_end), module_name.as_bytes(), expand_aliases_denylist, ); - error = error.or(err); // Remove the file from the stack of parsed module files working_set.parsed_module_files.pop(); @@ -2323,6 +2125,7 @@ pub fn parse_use( module, ) } else { + working_set.error(ParseError::ModuleNotFound(import_pattern.head.span)); return ( Pipeline::from_vec(vec![Expression { expr: Expr::Call(call), @@ -2331,10 +2134,11 @@ pub fn parse_use( custom_completion: None, }]), vec![], - Some(ParseError::ModuleNotFound(import_pattern.head.span)), ); } } else { + println!("MODULE NOT FOUND"); + working_set.error(ParseError::ModuleNotFound(import_pattern.head.span)); return ( Pipeline::from_vec(vec![Expression { expr: Expr::Call(call), @@ -2343,15 +2147,11 @@ pub fn parse_use( custom_completion: None, }]), vec![], - Some(ParseError::ModuleNotFound(import_pattern.head.span)), ); } } else { - return ( - garbage_pipeline(spans), - vec![], - Some(ParseError::NonUtf8(import_pattern.head.span)), - ); + working_set.error(ParseError::NonUtf8(import_pattern.head.span)); + return (garbage_pipeline(spans), vec![]); } }; @@ -2371,14 +2171,14 @@ pub fn parse_use( if let Some(id) = &module.main { decl_output.push((import_pattern.head.name.clone(), *id)); } else { - error = error.or(Some(ParseError::ExportNotFound(*span))); + working_set.error(ParseError::ExportNotFound(*span)); } } else if let Some(id) = module.get_decl_id(name) { decl_output.push((name.clone(), id)); } else if let Some(id) = module.get_alias_id(name) { alias_output.push((name.clone(), id)); } else { - error = error.or(Some(ParseError::ExportNotFound(*span))); + working_set.error(ParseError::ExportNotFound(*span)); } (decl_output, alias_output) @@ -2392,14 +2192,14 @@ pub fn parse_use( if let Some(id) = &module.main { decl_output.push((import_pattern.head.name.clone(), *id)); } else { - error = error.or(Some(ParseError::ExportNotFound(*span))); + working_set.error(ParseError::ExportNotFound(*span)); } } else if let Some(id) = module.get_decl_id(name) { decl_output.push((name.clone(), id)); } else if let Some(id) = module.get_alias_id(name) { alias_output.push((name.clone(), id)); } else { - error = error.or(Some(ParseError::ExportNotFound(*span))); + working_set.error(ParseError::ExportNotFound(*span)); break; } } @@ -2448,7 +2248,6 @@ pub fn parse_use( custom_completion: None, }]), exportables, - error, ) } @@ -2456,24 +2255,18 @@ pub fn parse_hide( working_set: &mut StateWorkingSet, spans: &[Span], expand_aliases_denylist: &[usize], -) -> (Pipeline, Option) { +) -> Pipeline { if working_set.get_span_contents(spans[0]) != b"hide" { - return ( - garbage_pipeline(spans), - Some(ParseError::UnknownState( - "internal error: Wrong call name for 'hide' command".into(), - span(spans), - )), - ); + working_set.error(ParseError::UnknownState( + "internal error: Wrong call name for 'hide' command".into(), + span(spans), + )); + return garbage_pipeline(spans); } let (call, args_spans) = match working_set.find_decl(b"hide", &Type::Any) { Some(decl_id) => { - let ParsedInternalCall { - call, - error: mut err, - output, - } = parse_internal_call( + let ParsedInternalCall { call, output } = parse_internal_call( working_set, spans[0], &spans[1..], @@ -2484,37 +2277,30 @@ pub fn parse_hide( let call_span = span(spans); - err = check_call(call_span, &decl.signature(), &call).or(err); - if err.is_some() || call.has_flag("help") { - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: call_span, - ty: output, - custom_completion: None, - }]), - err, - ); + let starting_error_count = working_set.parse_errors.len(); + check_call(working_set, call_span, &decl.signature(), &call); + if starting_error_count != working_set.parse_errors.len() || call.has_flag("help") { + return Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: call_span, + ty: output, + custom_completion: None, + }]); } (call, &spans[1..]) } None => { - return ( - garbage_pipeline(spans), - Some(ParseError::UnknownState( - "internal error: 'hide' declaration not found".into(), - span(spans), - )), - ) + working_set.error(ParseError::UnknownState( + "internal error: 'hide' declaration not found".into(), + span(spans), + )); + return garbage_pipeline(spans); } }; - let mut error = None; - - let (import_pattern_expr, err) = + let import_pattern_expr = parse_import_pattern(working_set, args_spans, expand_aliases_denylist); - error = error.or(err); let import_pattern = if let Expression { expr: Expr::ImportPattern(import_pattern), @@ -2523,21 +2309,18 @@ pub fn parse_hide( { import_pattern.clone() } else { - return ( - garbage_pipeline(spans), - Some(ParseError::UnknownState( - "internal error: Import pattern positional is not import pattern".into(), - import_pattern_expr.span, - )), - ); + working_set.error(ParseError::UnknownState( + "internal error: Import pattern positional is not import pattern".into(), + import_pattern_expr.span, + )); + return garbage_pipeline(spans); }; let bytes = working_set.get_span_contents(spans[0]); if bytes == b"hide" && spans.len() >= 2 { for span in spans[1..].iter() { - let (_, err) = parse_string(working_set, *span, expand_aliases_denylist); - error = error.or(err); + parse_string(working_set, *span, expand_aliases_denylist); } // module used only internally, not saved anywhere @@ -2564,10 +2347,8 @@ pub fn parse_hide( (false, Module::new(b"tmp".to_vec())) } } else { - return ( - garbage_pipeline(spans), - Some(ParseError::ModuleNotFound(spans[1])), - ); + working_set.error(ParseError::ModuleNotFound(spans[1])); + return garbage_pipeline(spans); }; // This kind of inverts the import pattern matching found in parse_use() @@ -2591,7 +2372,7 @@ pub fn parse_hide( if module.main.is_some() { decls.push(import_pattern.head.name.clone()); } else { - error = error.or(Some(ParseError::ExportNotFound(*span))); + working_set.error(ParseError::ExportNotFound(*span)); } } else if let Some(item) = module.alias_name_with_head(name, &import_pattern.head.name) @@ -2602,7 +2383,7 @@ pub fn parse_hide( { decls.push(item); } else { - error = error.or(Some(ParseError::ExportNotFound(*span))); + working_set.error(ParseError::ExportNotFound(*span)); } (aliases, decls) @@ -2616,7 +2397,7 @@ pub fn parse_hide( if module.main.is_some() { decls.push(import_pattern.head.name.clone()); } else { - error = error.or(Some(ParseError::ExportNotFound(*span))); + working_set.error(ParseError::ExportNotFound(*span)); break; } } else if let Some(item) = @@ -2628,7 +2409,7 @@ pub fn parse_hide( { decls.push(item); } else { - error = error.or(Some(ParseError::ExportNotFound(*span))); + working_set.error(ParseError::ExportNotFound(*span)); break; } } @@ -2661,30 +2442,22 @@ pub fn parse_hide( let mut call = call; call.set_parser_info("import_pattern".to_string(), import_pattern_expr); - ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: span(spans), - ty: Type::Any, - custom_completion: None, - }]), - error, - ) + Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: span(spans), + ty: Type::Any, + custom_completion: None, + }]) } else { - ( - garbage_pipeline(spans), - Some(ParseError::UnknownState( - "Expected structure: hide ".into(), - span(spans), - )), - ) + working_set.error(ParseError::UnknownState( + "Expected structure: hide ".into(), + span(spans), + )); + garbage_pipeline(spans) } } -pub fn parse_overlay_new( - working_set: &mut StateWorkingSet, - call: Box, -) -> (Pipeline, Option) { +pub fn parse_overlay_new(working_set: &mut StateWorkingSet, call: Box) -> Pipeline { let call_span = call.span(); let (overlay_name, _) = if let Some(expr) = call.positional_nth(0) { @@ -2692,21 +2465,21 @@ pub fn parse_overlay_new( Ok(val) => match value_as_string(val, expr.span) { Ok(s) => (s, expr.span), Err(err) => { - return (garbage_pipeline(&[call_span]), Some(err)); + working_set.error(err); + return garbage_pipeline(&[call_span]); } }, Err(err) => { - return (garbage_pipeline(&[call_span]), Some(err)); + working_set.error(err); + return garbage_pipeline(&[call_span]); } } } else { - return ( - garbage_pipeline(&[call_span]), - Some(ParseError::UnknownState( - "internal error: Missing required positional after call parsing".into(), - call_span, - )), - ); + working_set.error(ParseError::UnknownState( + "internal error: Missing required positional after call parsing".into(), + call_span, + )); + return garbage_pipeline(&[call_span]); }; let pipeline = Pipeline::from_vec(vec![Expression { @@ -2730,14 +2503,14 @@ pub fn parse_overlay_new( false, ); - (pipeline, None) + pipeline } pub fn parse_overlay_use( working_set: &mut StateWorkingSet, call: Box, expand_aliases_denylist: &[usize], -) -> (Pipeline, Option) { +) -> Pipeline { let call_span = call.span(); let (overlay_name, overlay_name_span) = if let Some(expr) = call.positional_nth(0) { @@ -2745,21 +2518,21 @@ pub fn parse_overlay_use( Ok(val) => match value_as_string(val, expr.span) { Ok(s) => (s, expr.span), Err(err) => { - return (garbage_pipeline(&[call_span]), Some(err)); + working_set.error(err); + return garbage_pipeline(&[call_span]); } }, Err(err) => { - return (garbage_pipeline(&[call_span]), Some(err)); + working_set.error(err); + return garbage_pipeline(&[call_span]); } } } else { - return ( - garbage_pipeline(&[call_span]), - Some(ParseError::UnknownState( - "internal error: Missing required positional after call parsing".into(), - call_span, - )), - ); + working_set.error(ParseError::UnknownState( + "internal error: Missing required positional after call parsing".into(), + call_span, + )); + return garbage_pipeline(&[call_span]); }; let new_name = if let Some(kw_expression) = call.positional_nth(1) { @@ -2770,18 +2543,22 @@ pub fn parse_overlay_use( item: s, span: new_name_expression.span, }), - Err(err) => return (garbage_pipeline(&[call_span]), Some(err)), + Err(err) => { + working_set.error(err); + return garbage_pipeline(&[call_span]); + } }, - Err(err) => return (garbage_pipeline(&[call_span]), Some(err)), + Err(err) => { + working_set.error(err); + return garbage_pipeline(&[call_span]); + } } } else { - return ( - garbage_pipeline(&[call_span]), - Some(ParseError::ExpectedKeyword( - "as keyword".to_string(), - kw_expression.span, - )), - ); + working_set.error(ParseError::ExpectedKeyword( + "as keyword".to_string(), + kw_expression.span, + )); + return garbage_pipeline(&[call_span]); } } else { None @@ -2799,159 +2576,148 @@ pub fn parse_overlay_use( let cwd = working_set.get_cwd(); - let mut error = None; + let (final_overlay_name, origin_module, origin_module_id, is_module_updated) = + if let Some(overlay_frame) = working_set.find_overlay(overlay_name.as_bytes()) { + // Activate existing overlay - let (final_overlay_name, origin_module, origin_module_id, is_module_updated) = if let Some( - overlay_frame, - ) = - working_set.find_overlay(overlay_name.as_bytes()) - { - // Activate existing overlay - - // First, check for errors - if has_prefix && !overlay_frame.prefixed { - return ( - pipeline, - Some(ParseError::OverlayPrefixMismatch( + // First, check for errors + if has_prefix && !overlay_frame.prefixed { + working_set.error(ParseError::OverlayPrefixMismatch( overlay_name, "without".to_string(), overlay_name_span, - )), - ); - } + )); + return pipeline; + } - if !has_prefix && overlay_frame.prefixed { - return ( - pipeline, - Some(ParseError::OverlayPrefixMismatch( + if !has_prefix && overlay_frame.prefixed { + working_set.error(ParseError::OverlayPrefixMismatch( overlay_name, "with".to_string(), overlay_name_span, - )), - ); - } - - if let Some(new_name) = new_name { - if new_name.item != overlay_name { - return ( - pipeline, - Some(ParseError::CantAddOverlayHelp( - format!("Cannot add overlay as '{}' because it already exists under the name '{}'", new_name.item, overlay_name), - new_name.span, - )), - ); + )); + return pipeline; } - } - let module_id = overlay_frame.origin; - - if let Some(new_module_id) = working_set.find_module(overlay_name.as_bytes()) { - if !do_reload && (module_id == new_module_id) { - ( - overlay_name, - Module::new(working_set.get_module(module_id).name.clone()), - module_id, - false, - ) - } else { - // The origin module of an overlay changed => update it - ( - overlay_name, - working_set.get_module(new_module_id).clone(), - new_module_id, - true, - ) + if let Some(new_name) = new_name { + if new_name.item != overlay_name { + working_set.error(ParseError::CantAddOverlayHelp( + format!( + "Cannot add overlay as '{}' because it already exists under the name '{}'", + new_name.item, overlay_name + ), + new_name.span, + )); + return pipeline; + } } - } else { - let module_name = overlay_name.as_bytes().to_vec(); - (overlay_name, Module::new(module_name), module_id, true) - } - } else { - // Create a new overlay from a module - if let Some(module_id) = - // the name is a module - working_set.find_module(overlay_name.as_bytes()) - { - ( - new_name.map(|spanned| spanned.item).unwrap_or(overlay_name), - working_set.get_module(module_id).clone(), - module_id, - true, - ) - } else { - // try if the name is a file - if let Ok(module_filename) = - String::from_utf8(trim_quotes(overlay_name.as_bytes()).to_vec()) - { - if let Some(module_path) = - find_in_dirs(&module_filename, working_set, &cwd, LIB_DIRS_VAR) - { - let overlay_name = if let Some(stem) = module_path.file_stem() { - stem.to_string_lossy().to_string() - } else { - return ( - pipeline, - Some(ParseError::ModuleOrOverlayNotFound(overlay_name_span)), - ); - }; - if let Ok(contents) = std::fs::read(&module_path) { - let span_start = working_set.next_span_start(); - working_set.add_file(module_filename, &contents); - let span_end = working_set.next_span_start(); + let module_id = overlay_frame.origin; - // Change currently parsed directory - let prev_currently_parsed_cwd = if let Some(parent) = module_path.parent() { - let prev = working_set.currently_parsed_cwd.clone(); - - working_set.currently_parsed_cwd = Some(parent.into()); - - prev - } else { - working_set.currently_parsed_cwd.clone() - }; - - let (block, module, module_comments, err) = parse_module_block( - working_set, - Span::new(span_start, span_end), - overlay_name.as_bytes(), - expand_aliases_denylist, - ); - error = error.or(err); - - // Restore the currently parsed directory back - working_set.currently_parsed_cwd = prev_currently_parsed_cwd; - - let _ = working_set.add_block(block); - let module_id = - working_set.add_module(&overlay_name, module.clone(), module_comments); - - ( - new_name.map(|spanned| spanned.item).unwrap_or(overlay_name), - module, - module_id, - true, - ) - } else { - return ( - pipeline, - Some(ParseError::ModuleOrOverlayNotFound(overlay_name_span)), - ); - } + if let Some(new_module_id) = working_set.find_module(overlay_name.as_bytes()) { + if !do_reload && (module_id == new_module_id) { + ( + overlay_name, + Module::new(working_set.get_module(module_id).name.clone()), + module_id, + false, + ) } else { - return ( - pipeline, - Some(ParseError::ModuleOrOverlayNotFound(overlay_name_span)), - ); + // The origin module of an overlay changed => update it + ( + overlay_name, + working_set.get_module(new_module_id).clone(), + new_module_id, + true, + ) } } else { - return ( - garbage_pipeline(&[call_span]), - Some(ParseError::NonUtf8(overlay_name_span)), - ); + let module_name = overlay_name.as_bytes().to_vec(); + (overlay_name, Module::new(module_name), module_id, true) } - } - }; + } else { + // Create a new overlay from a module + if let Some(module_id) = + // the name is a module + working_set.find_module(overlay_name.as_bytes()) + { + ( + new_name.map(|spanned| spanned.item).unwrap_or(overlay_name), + working_set.get_module(module_id).clone(), + module_id, + true, + ) + } else { + // try if the name is a file + if let Ok(module_filename) = + String::from_utf8(trim_quotes(overlay_name.as_bytes()).to_vec()) + { + if let Some(module_path) = + find_in_dirs(&module_filename, working_set, &cwd, LIB_DIRS_VAR) + { + let overlay_name = if let Some(stem) = module_path.file_stem() { + stem.to_string_lossy().to_string() + } else { + working_set + .error(ParseError::ModuleOrOverlayNotFound(overlay_name_span)); + return pipeline; + }; + + if let Ok(contents) = std::fs::read(&module_path) { + let span_start = working_set.next_span_start(); + working_set.add_file(module_filename, &contents); + let span_end = working_set.next_span_start(); + + // Change currently parsed directory + let prev_currently_parsed_cwd = + if let Some(parent) = module_path.parent() { + let prev = working_set.currently_parsed_cwd.clone(); + + working_set.currently_parsed_cwd = Some(parent.into()); + + prev + } else { + working_set.currently_parsed_cwd.clone() + }; + + let (block, module, module_comments) = parse_module_block( + working_set, + Span::new(span_start, span_end), + overlay_name.as_bytes(), + expand_aliases_denylist, + ); + + // Restore the currently parsed directory back + working_set.currently_parsed_cwd = prev_currently_parsed_cwd; + + let _ = working_set.add_block(block); + let module_id = working_set.add_module( + &overlay_name, + module.clone(), + module_comments, + ); + + ( + new_name.map(|spanned| spanned.item).unwrap_or(overlay_name), + module, + module_id, + true, + ) + } else { + working_set + .error(ParseError::ModuleOrOverlayNotFound(overlay_name_span)); + return pipeline; + } + } else { + working_set.error(ParseError::ModuleOrOverlayNotFound(overlay_name_span)); + return pipeline; + } + } else { + working_set.error(ParseError::NonUtf8(overlay_name_span)); + return garbage_pipeline(&[call_span]); + } + } + }; let (decls_to_lay, aliases_to_lay) = if is_module_updated { if has_prefix { @@ -2990,20 +2756,15 @@ pub fn parse_overlay_use( }, ); - let pipeline = Pipeline::from_vec(vec![Expression { + Pipeline::from_vec(vec![Expression { expr: Expr::Call(call), span: call_span, ty: Type::Any, custom_completion: None, - }]); - - (pipeline, error) + }]) } -pub fn parse_overlay_hide( - working_set: &mut StateWorkingSet, - call: Box, -) -> (Pipeline, Option) { +pub fn parse_overlay_hide(working_set: &mut StateWorkingSet, call: Box) -> Pipeline { let call_span = call.span(); let (overlay_name, overlay_name_span) = if let Some(expr) = call.positional_nth(0) { @@ -3011,11 +2772,13 @@ pub fn parse_overlay_hide( Ok(val) => match value_as_string(val, expr.span) { Ok(s) => (s, expr.span), Err(err) => { - return (garbage_pipeline(&[call_span]), Some(err)); + working_set.error(err); + return garbage_pipeline(&[call_span]); } }, Err(err) => { - return (garbage_pipeline(&[call_span]), Some(err)); + working_set.error(err); + return garbage_pipeline(&[call_span]); } } } else { @@ -3035,49 +2798,44 @@ pub fn parse_overlay_hide( }]); if overlay_name == DEFAULT_OVERLAY_NAME { - return ( - pipeline, - Some(ParseError::CantHideDefaultOverlay( - overlay_name, - overlay_name_span, - )), - ); + working_set.error(ParseError::CantHideDefaultOverlay( + overlay_name, + overlay_name_span, + )); + + return pipeline; } if !working_set .unique_overlay_names() .contains(&overlay_name.as_bytes().to_vec()) { - return ( - pipeline, - Some(ParseError::ActiveOverlayNotFound(overlay_name_span)), - ); + working_set.error(ParseError::ActiveOverlayNotFound(overlay_name_span)); + return pipeline; } if working_set.num_overlays() < 2 { - return ( - pipeline, - Some(ParseError::CantRemoveLastOverlay(overlay_name_span)), - ); + working_set.error(ParseError::CantRemoveLastOverlay(overlay_name_span)); + return pipeline; } working_set.remove_overlay(overlay_name.as_bytes(), keep_custom); - (pipeline, None) + pipeline } pub fn parse_let_or_const( working_set: &mut StateWorkingSet, spans: &[Span], expand_aliases_denylist: &[usize], -) -> (Pipeline, Option) { +) -> Pipeline { let name = working_set.get_span_contents(spans[0]); if name == b"let" || name == b"const" { let is_const = &name == b"const"; - if let Some((span, err)) = check_name(working_set, spans) { - return (Pipeline::from_vec(vec![garbage(*span)]), Some(err)); + if let Some(span) = check_name(working_set, spans) { + return Pipeline::from_vec(vec![garbage(*span)]); } if let Some(decl_id) = @@ -3092,10 +2850,8 @@ pub fn parse_let_or_const( for span in spans.iter().enumerate() { let item = working_set.get_span_contents(*span.1); if item == b"=" && spans.len() > (span.0 + 1) { - let mut error = None; - let mut idx = span.0; - let (rvalue, err) = parse_multispan_value( + let rvalue = parse_multispan_value( working_set, spans, &mut idx, @@ -3105,33 +2861,30 @@ pub fn parse_let_or_const( ), expand_aliases_denylist, ); - error = error.or(err); if idx < (spans.len() - 1) { - error = error.or(Some(ParseError::ExtraPositional( - call_signature, - spans[idx + 1], - ))); + working_set + .error(ParseError::ExtraPositional(call_signature, spans[idx + 1])); } let mut idx = 0; - let (lvalue, err) = parse_var_with_opt_type( + let lvalue = parse_var_with_opt_type( working_set, &spans[1..(span.0)], &mut idx, false, ); - error = error.or(err); let var_name = String::from_utf8_lossy(working_set.get_span_contents(lvalue.span)) .to_string(); if ["in", "nu", "env", "nothing"].contains(&var_name.as_str()) { - error = if is_const { - error.or(Some(ParseError::ConstBuiltinVar(var_name, lvalue.span))) + if is_const { + working_set + .error(ParseError::ConstBuiltinVar(var_name, lvalue.span)) } else { - error.or(Some(ParseError::LetBuiltinVar(var_name, lvalue.span))) + working_set.error(ParseError::LetBuiltinVar(var_name, lvalue.span)) }; } @@ -3146,7 +2899,7 @@ pub fn parse_let_or_const( Ok(val) => { working_set.add_constant(var_id, val); } - Err(err) => error = error.or(Some(err)), + Err(err) => working_set.error(err), } } } @@ -3163,23 +2916,16 @@ pub fn parse_let_or_const( parser_info: HashMap::new(), }); - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: nu_protocol::span(spans), - ty: Type::Any, - custom_completion: None, - }]), - error, - ); + return Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: nu_protocol::span(spans), + ty: Type::Any, + custom_completion: None, + }]); } } } - let ParsedInternalCall { - call, - error: err, - output, - } = parse_internal_call( + let ParsedInternalCall { call, output } = parse_internal_call( working_set, spans[0], &spans[1..], @@ -3187,36 +2933,33 @@ pub fn parse_let_or_const( expand_aliases_denylist, ); - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: nu_protocol::span(spans), - ty: output, - custom_completion: None, - }]), - err, - ); + return Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: nu_protocol::span(spans), + ty: output, + custom_completion: None, + }]); } } - ( - garbage_pipeline(spans), - Some(ParseError::UnknownState( - "internal error: let or const statement unparsable".into(), - span(spans), - )), - ) + + working_set.error(ParseError::UnknownState( + "internal error: let or const statement unparsable".into(), + span(spans), + )); + + garbage_pipeline(spans) } pub fn parse_mut( working_set: &mut StateWorkingSet, spans: &[Span], expand_aliases_denylist: &[usize], -) -> (Pipeline, Option) { +) -> Pipeline { let name = working_set.get_span_contents(spans[0]); if name == b"mut" { - if let Some((span, err)) = check_name(working_set, spans) { - return (Pipeline::from_vec(vec![garbage(*span)]), Some(err)); + if let Some(span) = check_name(working_set, spans) { + return Pipeline::from_vec(vec![garbage(*span)]); } if let Some(decl_id) = working_set.find_decl(b"mut", &Type::Any) { @@ -3229,10 +2972,8 @@ pub fn parse_mut( for span in spans.iter().enumerate() { let item = working_set.get_span_contents(*span.1); if item == b"=" && spans.len() > (span.0 + 1) { - let mut error = None; - let mut idx = span.0; - let (rvalue, err) = parse_multispan_value( + let rvalue = parse_multispan_value( working_set, spans, &mut idx, @@ -3242,31 +2983,26 @@ pub fn parse_mut( ), expand_aliases_denylist, ); - error = error.or(err); if idx < (spans.len() - 1) { - error = error.or(Some(ParseError::ExtraPositional( - call_signature, - spans[idx + 1], - ))); + working_set + .error(ParseError::ExtraPositional(call_signature, spans[idx + 1])); } let mut idx = 0; - let (lvalue, err) = parse_var_with_opt_type( + let lvalue = parse_var_with_opt_type( working_set, &spans[1..(span.0)], &mut idx, true, ); - error = error.or(err); let var_name = String::from_utf8_lossy(working_set.get_span_contents(lvalue.span)) .to_string(); if ["in", "nu", "env", "nothing"].contains(&var_name.as_str()) { - error = - error.or(Some(ParseError::MutBuiltinVar(var_name, lvalue.span))); + working_set.error(ParseError::MutBuiltinVar(var_name, lvalue.span)); } let var_id = lvalue.as_var(); @@ -3288,23 +3024,16 @@ pub fn parse_mut( parser_info: HashMap::new(), }); - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: nu_protocol::span(spans), - ty: Type::Any, - custom_completion: None, - }]), - error, - ); + return Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: nu_protocol::span(spans), + ty: Type::Any, + custom_completion: None, + }]); } } } - let ParsedInternalCall { - call, - error: err, - output, - } = parse_internal_call( + let ParsedInternalCall { call, output } = parse_internal_call( working_set, spans[0], &spans[1..], @@ -3312,32 +3041,27 @@ pub fn parse_mut( expand_aliases_denylist, ); - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: nu_protocol::span(spans), - ty: output, - custom_completion: None, - }]), - err, - ); + return Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: nu_protocol::span(spans), + ty: output, + custom_completion: None, + }]); } } - ( - garbage_pipeline(spans), - Some(ParseError::UnknownState( - "internal error: mut statement unparsable".into(), - span(spans), - )), - ) + working_set.error(ParseError::UnknownState( + "internal error: mut statement unparsable".into(), + span(spans), + )); + + garbage_pipeline(spans) } pub fn parse_source( working_set: &mut StateWorkingSet, spans: &[Span], expand_aliases_denylist: &[usize], -) -> (Pipeline, Option) { - let mut error = None; +) -> Pipeline { let name = working_set.get_span_contents(spans[0]); if name == b"source" || name == b"source-env" { @@ -3348,69 +3072,55 @@ pub fn parse_source( // Is this the right call to be using here? // Some of the others (`parse_let`) use it, some of them (`parse_hide`) don't. - let ParsedInternalCall { - call, - error: err, - output, - } = parse_internal_call( + let ParsedInternalCall { call, output } = parse_internal_call( working_set, spans[0], &spans[1..], decl_id, expand_aliases_denylist, ); - error = error.or(err); - if error.is_some() || call.has_flag("help") { - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: span(spans), - ty: output, - custom_completion: None, - }]), - error, - ); + if call.has_flag("help") { + return Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: span(spans), + ty: output, + custom_completion: None, + }]); } // Command and one file name if spans.len() >= 2 { - let (expr, err) = parse_value( + let expr = parse_value( working_set, spans[1], &SyntaxShape::Any, expand_aliases_denylist, ); - error = error.or(err); - let val = match eval_constant(working_set, &expr) { Ok(val) => val, Err(err) => { - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: span(&spans[1..]), - ty: Type::Any, - custom_completion: None, - }]), - Some(err), - ); + working_set.error(err); + return Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: span(&spans[1..]), + ty: Type::Any, + custom_completion: None, + }]); } }; let filename = match value_as_string(val, spans[1]) { Ok(s) => s, Err(err) => { - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: span(&spans[1..]), - ty: Type::Any, - custom_completion: None, - }]), - Some(err), - ); + working_set.error(err); + return Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: span(&spans[1..]), + ty: Type::Any, + custom_completion: None, + }]); } }; @@ -3429,7 +3139,7 @@ pub fn parse_source( // This will load the defs from the file into the // working set, if it was a successful parse. - let (block, err) = parse( + let block = parse( working_set, path.file_name().and_then(|x| x.to_str()), &contents, @@ -3440,106 +3150,76 @@ pub fn parse_source( // Restore the currently parsed directory back working_set.currently_parsed_cwd = prev_currently_parsed_cwd; - if err.is_some() { - // Unsuccessful parse of file - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: span(&spans[1..]), - ty: Type::Any, - custom_completion: None, - }]), - // Return the file parse error - err, - ); - } else { - // Save the block into the working set - let block_id = working_set.add_block(block); + // Save the block into the working set + let block_id = working_set.add_block(block); - let mut call_with_block = call; + let mut call_with_block = call; - // FIXME: Adding this expression to the positional creates a syntax highlighting error - // after writing `source example.nu` - call_with_block.set_parser_info( - "block_id".to_string(), - Expression { - expr: Expr::Int(block_id as i64), - span: spans[1], - ty: Type::Any, - custom_completion: None, - }, - ); + // FIXME: Adding this expression to the positional creates a syntax highlighting error + // after writing `source example.nu` + call_with_block.set_parser_info( + "block_id".to_string(), + Expression { + expr: Expr::Int(block_id as i64), + span: spans[1], + ty: Type::Any, + custom_completion: None, + }, + ); - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call_with_block), - span: span(spans), - ty: Type::Any, - custom_completion: None, - }]), - None, - ); - } + return Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call_with_block), + span: span(spans), + ty: Type::Any, + custom_completion: None, + }]); } } else { - error = error.or(Some(ParseError::SourcedFileNotFound(filename, spans[1]))); + working_set.error(ParseError::SourcedFileNotFound(filename, spans[1])); } } - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: span(spans), - ty: Type::Any, - custom_completion: None, - }]), - error, - ); + return Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: span(spans), + ty: Type::Any, + custom_completion: None, + }]); } } - ( - garbage_pipeline(spans), - Some(ParseError::UnknownState( - "internal error: source statement unparsable".into(), - span(spans), - )), - ) + working_set.error(ParseError::UnknownState( + "internal error: source statement unparsable".into(), + span(spans), + )); + garbage_pipeline(spans) } pub fn parse_where_expr( working_set: &mut StateWorkingSet, spans: &[Span], expand_aliases_denylist: &[usize], -) -> (Expression, Option) { +) -> Expression { trace!("parsing: where"); if !spans.is_empty() && working_set.get_span_contents(spans[0]) != b"where" { - return ( - garbage(span(spans)), - Some(ParseError::UnknownState( - "internal error: Wrong call name for 'where' command".into(), - span(spans), - )), - ); + working_set.error(ParseError::UnknownState( + "internal error: Wrong call name for 'where' command".into(), + span(spans), + )); + return garbage(span(spans)); } if spans.len() < 2 { - return ( - garbage(span(spans)), - Some(ParseError::MissingPositional( - "row condition".into(), - span(spans), - "where ".into(), - )), - ); + working_set.error(ParseError::MissingPositional( + "row condition".into(), + span(spans), + "where ".into(), + )); + return garbage(span(spans)); } let call = match working_set.find_decl(b"where", &Type::Any) { Some(decl_id) => { - let ParsedInternalCall { - call, - error: mut err, - output, - } = parse_internal_call( + let ParsedInternalCall { call, output } = parse_internal_call( working_set, spans[0], &spans[1..], @@ -3550,50 +3230,43 @@ pub fn parse_where_expr( let call_span = span(spans); - err = check_call(call_span, &decl.signature(), &call).or(err); - if err.is_some() || call.has_flag("help") { - return ( - Expression { - expr: Expr::Call(call), - span: call_span, - ty: output, - custom_completion: None, - }, - err, - ); + let starting_error_count = working_set.parse_errors.len(); + check_call(working_set, call_span, &decl.signature(), &call); + if starting_error_count != working_set.parse_errors.len() || call.has_flag("help") { + return Expression { + expr: Expr::Call(call), + span: call_span, + ty: output, + custom_completion: None, + }; } call } None => { - return ( - garbage(span(spans)), - Some(ParseError::UnknownState( - "internal error: 'where' declaration not found".into(), - span(spans), - )), - ) + working_set.error(ParseError::UnknownState( + "internal error: 'where' declaration not found".into(), + span(spans), + )); + return garbage(span(spans)); } }; - ( - Expression { - expr: Expr::Call(call), - span: span(spans), - ty: Type::Any, - custom_completion: None, - }, - None, - ) + Expression { + expr: Expr::Call(call), + span: span(spans), + ty: Type::Any, + custom_completion: None, + } } pub fn parse_where( working_set: &mut StateWorkingSet, spans: &[Span], expand_aliases_denylist: &[usize], -) -> (Pipeline, Option) { - let (expression, err) = parse_where_expr(working_set, spans, expand_aliases_denylist); - (Pipeline::from_vec(vec![expression]), err) +) -> Pipeline { + let expression = parse_where_expr(working_set, spans, expand_aliases_denylist); + Pipeline::from_vec(vec![expression]) } #[cfg(feature = "plugin")] @@ -3601,7 +3274,7 @@ pub fn parse_register( working_set: &mut StateWorkingSet, spans: &[Span], expand_aliases_denylist: &[usize], -) -> (Pipeline, Option) { +) -> Pipeline { use nu_plugin::{get_signature, PluginDeclaration}; use nu_protocol::{engine::Stack, PluginSignature}; @@ -3610,13 +3283,11 @@ pub fn parse_register( // Checking that the function is used with the correct name // Maybe this is not necessary but it is a sanity check if working_set.get_span_contents(spans[0]) != b"register" { - return ( - garbage_pipeline(spans), - Some(ParseError::UnknownState( - "internal error: Wrong call name for parse plugin function".into(), - span(spans), - )), - ); + working_set.error(ParseError::UnknownState( + "internal error: Wrong call name for parse plugin function".into(), + span(spans), + )); + return garbage_pipeline(spans); } // Parsing the spans and checking that they match the register signature @@ -3624,20 +3295,14 @@ pub fn parse_register( // Also, by creating a call, it can be checked if it matches the declaration signature let (call, call_span) = match working_set.find_decl(b"register", &Type::Any) { None => { - return ( - garbage_pipeline(spans), - Some(ParseError::UnknownState( - "internal error: Register declaration not found".into(), - span(spans), - )), - ) + working_set.error(ParseError::UnknownState( + "internal error: Register declaration not found".into(), + span(spans), + )); + return garbage_pipeline(spans); } Some(decl_id) => { - let ParsedInternalCall { - call, - error: mut err, - output, - } = parse_internal_call( + let ParsedInternalCall { call, output } = parse_internal_call( working_set, spans[0], &spans[1..], @@ -3648,17 +3313,15 @@ pub fn parse_register( let call_span = span(spans); - err = check_call(call_span, &decl.signature(), &call).or(err); - if err.is_some() || call.has_flag("help") { - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: call_span, - ty: output, - custom_completion: None, - }]), - err, - ); + let starting_error_count = working_set.parse_errors.len(); + check_call(working_set, call_span, &decl.signature(), &call); + if starting_error_count != working_set.parse_errors.len() || call.has_flag("help") { + return Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: call_span, + ty: output, + custom_completion: None, + }]); } (call, call_span) @@ -3672,27 +3335,24 @@ pub fn parse_register( .positional_nth(0) .map(|expr| { let name_expr = working_set.get_span_contents(expr.span); - let (name, err) = unescape_unquote_string(name_expr, expr.span); - if let Some(err) = err { - Err(err) - } else { - let path = if let Some(p) = find_in_dirs(&name, working_set, &cwd, PLUGIN_DIRS_VAR) - { - p - } else { - return Err(ParseError::RegisteredFileNotFound(name, expr.span)); - }; + working_set.error(err) + } - if path.exists() & path.is_file() { - Ok((path, expr.span)) - } else { - Err(ParseError::RegisteredFileNotFound( - format!("{path:?}"), - expr.span, - )) - } + let path = if let Some(p) = find_in_dirs(&name, working_set, &cwd, PLUGIN_DIRS_VAR) { + p + } else { + return Err(ParseError::RegisteredFileNotFound(name, expr.span)); + }; + + if path.exists() & path.is_file() { + Ok((path, expr.span)) + } else { + Err(ParseError::RegisteredFileNotFound( + format!("{path:?}"), + expr.span, + )) } }) .expect("required positional has being checked"); @@ -3737,15 +3397,13 @@ pub fn parse_register( Some(path) => match path { Ok(path) => Some(path), Err(err) => { - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: call_span, - ty: Type::Any, - custom_completion: None, - }]), - Some(err), - ); + working_set.error(err); + return Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: call_span, + ty: Type::Any, + custom_completion: None, + }]); } }, }; @@ -3814,15 +3472,16 @@ pub fn parse_register( } .err(); - ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: call_span, - ty: Type::Nothing, - custom_completion: None, - }]), - error, - ) + if let Some(err) = error { + working_set.error(err); + } + + Pipeline::from_vec(vec![Expression { + expr: Expr::Call(call), + span: call_span, + ty: Type::Nothing, + custom_completion: None, + }]) } pub fn find_dirs_var(working_set: &StateWorkingSet, var_name: &str) -> Option { diff --git a/crates/nu-parser/src/parse_patterns.rs b/crates/nu-parser/src/parse_patterns.rs index 134d493e7e..22a92d7ffc 100644 --- a/crates/nu-parser/src/parse_patterns.rs +++ b/crates/nu-parser/src/parse_patterns.rs @@ -1,13 +1,13 @@ use nu_protocol::{ ast::{Expr, Expression, MatchPattern, Pattern}, engine::StateWorkingSet, - Span, SyntaxShape, Type, VarId, + ParseError, Span, SyntaxShape, Type, VarId, }; use crate::{ lex, lite_parse, parser::{is_variable, parse_value}, - LiteElement, ParseError, + LiteElement, }; pub fn garbage(span: Span) -> MatchPattern { @@ -17,29 +17,20 @@ pub fn garbage(span: Span) -> MatchPattern { } } -pub fn parse_match_pattern( - working_set: &mut StateWorkingSet, - span: Span, -) -> (Expression, Option) { +pub fn parse_match_pattern(working_set: &mut StateWorkingSet, span: Span) -> Expression { working_set.enter_scope(); - let (output, err) = parse_pattern(working_set, span); + let output = parse_pattern(working_set, span); working_set.exit_scope(); - ( - Expression { - expr: Expr::MatchPattern(Box::new(output)), - span, - ty: Type::Any, - custom_completion: None, - }, - err, - ) + Expression { + expr: Expr::MatchPattern(Box::new(output)), + span, + ty: Type::Any, + custom_completion: None, + } } -pub fn parse_pattern( - working_set: &mut StateWorkingSet, - span: Span, -) -> (MatchPattern, Option) { +pub fn parse_pattern(working_set: &mut StateWorkingSet, span: Span) -> MatchPattern { let bytes = working_set.get_span_contents(span); if bytes.starts_with(b"$") { @@ -52,23 +43,18 @@ pub fn parse_pattern( // List pattern parse_list_pattern(working_set, span) } else if bytes == b"_" { - ( - MatchPattern { - pattern: Pattern::IgnoreValue, - span, - }, - None, - ) + MatchPattern { + pattern: Pattern::IgnoreValue, + span, + } } else { // Literal value - let (value, error) = parse_value(working_set, span, &SyntaxShape::Any, &[]); - ( - MatchPattern { - pattern: Pattern::Value(value), - span, - }, - error, - ) + let value = parse_value(working_set, span, &SyntaxShape::Any, &[]); + + MatchPattern { + pattern: Pattern::Value(value), + span, + } } } @@ -88,34 +74,21 @@ fn parse_variable_pattern_helper(working_set: &mut StateWorkingSet, span: Span) } } -pub fn parse_variable_pattern( - working_set: &mut StateWorkingSet, - span: Span, -) -> (MatchPattern, Option) { +pub fn parse_variable_pattern(working_set: &mut StateWorkingSet, span: Span) -> MatchPattern { if let Some(var_id) = parse_variable_pattern_helper(working_set, span) { - ( - MatchPattern { - pattern: Pattern::Variable(var_id), - span, - }, - None, - ) + MatchPattern { + pattern: Pattern::Variable(var_id), + span, + } } else { - ( - garbage(span), - Some(ParseError::Expected("valid variable name".into(), span)), - ) + working_set.error(ParseError::Expected("valid variable name".into(), span)); + garbage(span) } } -pub fn parse_list_pattern( - working_set: &mut StateWorkingSet, - span: Span, -) -> (MatchPattern, Option) { +pub fn parse_list_pattern(working_set: &mut StateWorkingSet, span: Span) -> MatchPattern { let bytes = working_set.get_span_contents(span); - let mut error = None; - let mut start = span.start; let mut end = span.end; @@ -125,17 +98,21 @@ pub fn parse_list_pattern( if bytes.ends_with(b"]") { end -= 1; } else { - error = error.or_else(|| Some(ParseError::Unclosed("]".into(), Span::new(end, end)))); + working_set.error(ParseError::Unclosed("]".into(), Span::new(end, end))); } let inner_span = Span::new(start, end); let source = working_set.get_span_contents(inner_span); let (output, err) = lex(source, inner_span.start, &[b'\n', b'\r', b','], &[], true); - error = error.or(err); + if let Some(err) = err { + working_set.error(err); + } let (output, err) = lite_parse(&output); - error = error.or(err); + if let Some(err) = err { + working_set.error(err); + } let mut args = vec![]; @@ -167,14 +144,13 @@ pub fn parse_list_pattern( break; } else { args.push(garbage(command.parts[spans_idx])); - error = error.or(Some(ParseError::Expected( + working_set.error(ParseError::Expected( "valid variable name".into(), command.parts[spans_idx], - ))); + )); } } else { - let (arg, err) = parse_pattern(working_set, command.parts[spans_idx]); - error = error.or(err); + let arg = parse_pattern(working_set, command.parts[spans_idx]); args.push(arg); }; @@ -185,47 +161,41 @@ pub fn parse_list_pattern( } } - ( - MatchPattern { - pattern: Pattern::List(args), - span, - }, - error, - ) + MatchPattern { + pattern: Pattern::List(args), + span, + } } -pub fn parse_record_pattern( - working_set: &mut StateWorkingSet, - span: Span, -) -> (MatchPattern, Option) { - let bytes = working_set.get_span_contents(span); +pub fn parse_record_pattern(working_set: &mut StateWorkingSet, span: Span) -> MatchPattern { + let mut bytes = working_set.get_span_contents(span); - let mut error = None; let mut start = span.start; let mut end = span.end; if bytes.starts_with(b"{") { start += 1; } else { - error = error.or_else(|| { - Some(ParseError::Expected( - "{".into(), - Span::new(start, start + 1), - )) - }); + working_set.error(ParseError::Expected( + "{".into(), + Span::new(start, start + 1), + )); + bytes = working_set.get_span_contents(span); } if bytes.ends_with(b"}") { end -= 1; } else { - error = error.or_else(|| Some(ParseError::Unclosed("}".into(), Span::new(end, end)))); + working_set.error(ParseError::Unclosed("}".into(), Span::new(end, end))); } let inner_span = Span::new(start, end); let source = working_set.get_span_contents(inner_span); let (tokens, err) = lex(source, start, &[b'\n', b'\r', b','], &[b':'], true); - error = error.or(err); + if let Some(err) = err { + working_set.error(err); + } let mut output = vec![]; let mut idx = 0; @@ -236,8 +206,7 @@ pub fn parse_record_pattern( // If this is a variable, treat it as both the name of the field and the pattern let field = String::from_utf8_lossy(&bytes[1..]).to_string(); - let (pattern, err) = parse_variable_pattern(working_set, tokens[idx].span); - error = error.or(err); + let pattern = parse_variable_pattern(working_set, tokens[idx].span); (field, pattern) } else { @@ -245,22 +214,17 @@ pub fn parse_record_pattern( idx += 1; if idx == tokens.len() { - return ( - garbage(span), - Some(ParseError::Expected("record".into(), span)), - ); + working_set.error(ParseError::Expected("record".into(), span)); + return garbage(span); } let colon = working_set.get_span_contents(tokens[idx].span); idx += 1; if idx == tokens.len() || colon != b":" { //FIXME: need better error - return ( - garbage(span), - Some(ParseError::Expected("record".into(), span)), - ); + working_set.error(ParseError::Expected("record".into(), span)); + return garbage(span); } - let (pattern, err) = parse_pattern(working_set, tokens[idx].span); - error = error.or(err); + let pattern = parse_pattern(working_set, tokens[idx].span); (field, pattern) }; @@ -269,11 +233,8 @@ pub fn parse_record_pattern( output.push((field, pattern)); } - ( - MatchPattern { - pattern: Pattern::Record(output), - span, - }, - error, - ) + MatchPattern { + pattern: Pattern::Record(output), + span, + } } diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 70377a23d8..982cea32b2 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -5,7 +5,7 @@ use crate::{ parse_mut, parse_patterns::{parse_match_pattern, parse_pattern}, type_check::{math_result_type, type_compatible}, - ParseError, Token, TokenContents, + Token, TokenContents, }; use nu_engine::DIR_VAR_PARSER_INFO; @@ -16,8 +16,8 @@ use nu_protocol::{ Operator, PathMember, Pattern, Pipeline, PipelineElement, RangeInclusion, RangeOperator, }, engine::StateWorkingSet, - span, BlockId, Flag, PositionalArg, Signature, Span, Spanned, SyntaxShape, Type, Unit, VarId, - ENV_VARIABLE_ID, IN_VARIABLE_ID, + span, BlockId, Flag, ParseError, PositionalArg, Signature, Span, Spanned, SyntaxShape, Type, + Unit, VarId, ENV_VARIABLE_ID, IN_VARIABLE_ID, }; use crate::parse_keywords::{ @@ -89,32 +89,46 @@ pub fn is_math_expression_like( return true; } - if parse_number(bytes, span).1.is_none() { - return true; - } + let starting_error_count = working_set.parse_errors.len(); - if parse_filesize(working_set, span).1.is_none() { + // Number + parse_number(working_set, span); + if working_set.parse_errors.len() == starting_error_count { return true; } + working_set.parse_errors.truncate(starting_error_count); - if parse_duration(working_set, span).1.is_none() { + // Filesize + parse_filesize(working_set, span); + if working_set.parse_errors.len() == starting_error_count { return true; } + working_set.parse_errors.truncate(starting_error_count); - if parse_datetime(working_set, span).1.is_none() { + parse_duration(working_set, span); + if working_set.parse_errors.len() == starting_error_count { return true; } + working_set.parse_errors.truncate(starting_error_count); - if parse_binary(working_set, span).1.is_none() { + parse_datetime(working_set, span); + if working_set.parse_errors.len() == starting_error_count { return true; } + working_set.parse_errors.truncate(starting_error_count); - if parse_range(working_set, span, expand_aliases_denylist) - .1 - .is_none() - { + parse_binary(working_set, span); + if working_set.parse_errors.len() == starting_error_count { return true; } + working_set.parse_errors.truncate(starting_error_count); + + parse_range(working_set, span, expand_aliases_denylist); + + if working_set.parse_errors.len() == starting_error_count { + return true; + } + working_set.parse_errors.truncate(starting_error_count); false } @@ -153,10 +167,10 @@ pub fn trim_quotes_str(s: &str) -> &str { } } -pub fn check_call(command: Span, sig: &Signature, call: &Call) -> Option { +pub fn check_call(working_set: &mut StateWorkingSet, command: Span, sig: &Signature, call: &Call) { // Allow the call to pass if they pass in the help flag if call.named_iter().any(|(n, _, _)| n.item == "help") { - return None; + return; } if call.positional_len() < sig.required_positional.len() { @@ -173,30 +187,32 @@ pub fn check_call(command: Span, sig: &Signature, call: &Call) -> Option Option( - working_set: &mut StateWorkingSet, - spans: &'a [Span], -) -> Option<(&'a Span, ParseError)> { +pub fn check_name<'a>(working_set: &mut StateWorkingSet, spans: &'a [Span]) -> Option<&'a Span> { let command_len = if !spans.is_empty() { if working_set.get_span_contents(spans[0]) == b"export" { 2 @@ -235,28 +247,24 @@ pub fn check_name<'a>( if working_set.get_span_contents(spans[command_len]) == b"=" { let name = String::from_utf8_lossy(working_set.get_span_contents(span(&spans[..command_len]))); - Some(( - &spans[command_len], - ParseError::AssignmentMismatch( - format!("{name} missing name"), - "missing name".into(), - spans[command_len], - ), - )) + working_set.error(ParseError::AssignmentMismatch( + format!("{name} missing name"), + "missing name".into(), + spans[command_len], + )); + Some(&spans[command_len]) } else { None } } else if working_set.get_span_contents(spans[command_len + 1]) != b"=" { let name = String::from_utf8_lossy(working_set.get_span_contents(span(&spans[..command_len]))); - Some(( - &spans[command_len + 1], - ParseError::AssignmentMismatch( - format!("{name} missing sign"), - "missing equal sign".into(), - spans[command_len + 1], - ), - )) + working_set.error(ParseError::AssignmentMismatch( + format!("{name} missing sign"), + "missing equal sign".into(), + spans[command_len + 1], + )); + Some(&spans[command_len + 1]) } else { None } @@ -266,43 +274,36 @@ fn parse_external_arg( working_set: &mut StateWorkingSet, span: Span, expand_aliases_denylist: &[usize], -) -> (Expression, Option) { +) -> Expression { let contents = working_set.get_span_contents(span); - let mut error = None; - if contents.starts_with(b"$") || contents.starts_with(b"(") { - let (arg, err) = parse_dollar_expr(working_set, span, expand_aliases_denylist); - error = error.or(err); - (arg, error) + parse_dollar_expr(working_set, span, expand_aliases_denylist) } else if contents.starts_with(b"[") { - let (arg, err) = parse_list_expression( + parse_list_expression( working_set, span, &SyntaxShape::Any, expand_aliases_denylist, - ); - error = error.or(err); - (arg, error) + ) } else { // Eval stage trims the quotes, so we don't have to do the same thing when parsing. let contents = if contents.starts_with(b"\"") { let (contents, err) = unescape_string(contents, span); - error = error.or(err); + if let Some(err) = err { + working_set.error(err) + } String::from_utf8_lossy(&contents).to_string() } else { String::from_utf8_lossy(contents).to_string() }; - ( - Expression { - expr: Expr::String(contents), - span, - ty: Type::String, - custom_completion: None, - }, - error, - ) + Expression { + expr: Expr::String(contents), + span, + ty: Type::String, + custom_completion: None, + } } } @@ -311,7 +312,7 @@ pub fn parse_external_call( spans: &[Span], expand_aliases_denylist: &[usize], is_subexpression: bool, -) -> (Expression, Option) { +) -> Expression { trace!("parse external"); let mut args = vec![]; @@ -326,16 +327,15 @@ pub fn parse_external_call( let head_contents = working_set.get_span_contents(head_span).to_vec(); - let mut error = None; - let head = if head_contents.starts_with(b"$") || head_contents.starts_with(b"(") { // the expression is inside external_call, so it's a subexpression - let (arg, err) = parse_expression(working_set, &[head_span], expand_aliases_denylist, true); - error = error.or(err); + let arg = parse_expression(working_set, &[head_span], expand_aliases_denylist, true); Box::new(arg) } else { let (contents, err) = unescape_unquote_string(&head_contents, head_span); - error = error.or(err); + if let Some(err) = err { + working_set.error(err) + } Box::new(Expression { expr: Expr::String(contents), @@ -346,19 +346,16 @@ pub fn parse_external_call( }; for span in &spans[1..] { - let (arg, err) = parse_external_arg(working_set, *span, expand_aliases_denylist); - error = error.or(err); + let arg = parse_external_arg(working_set, *span, expand_aliases_denylist); args.push(arg); } - ( - Expression { - expr: Expr::ExternalCall(head, args, is_subexpression), - span: span(spans), - ty: Type::Any, - custom_completion: None, - }, - error, - ) + + Expression { + expr: Expr::ExternalCall(head, args, is_subexpression), + span: span(spans), + ty: Type::Any, + custom_completion: None, + } } fn parse_long_flag( @@ -367,11 +364,7 @@ fn parse_long_flag( spans_idx: &mut usize, sig: &Signature, expand_aliases_denylist: &[usize], -) -> ( - Option>, - Option, - Option, -) { +) -> (Option>, Option) { let arg_span = spans[*spans_idx]; let arg_contents = working_set.get_span_contents(arg_span); @@ -389,7 +382,7 @@ fn parse_long_flag( let mut span = arg_span; span.start += long_name_len + 3; //offset by long flag and '=' - let (arg, err) = + let arg = parse_value(working_set, span, arg_shape, expand_aliases_denylist); ( @@ -398,10 +391,9 @@ fn parse_long_flag( span: Span::new(arg_span.start, arg_span.start + long_name_len + 2), }), Some(arg), - err, ) } else if let Some(arg) = spans.get(*spans_idx + 1) { - let (arg, err) = + let arg = parse_value(working_set, *arg, arg_shape, expand_aliases_denylist); *spans_idx += 1; @@ -411,19 +403,18 @@ fn parse_long_flag( span: arg_span, }), Some(arg), - err, ) } else { + working_set.error(ParseError::MissingFlagParam( + arg_shape.to_string(), + arg_span, + )); ( Some(Spanned { item: long_name, span: arg_span, }), None, - Some(ParseError::MissingFlagParam( - arg_shape.to_string(), - arg_span, - )), ) } } else { @@ -434,36 +425,35 @@ fn parse_long_flag( span: arg_span, }), None, - None, ) } } else { + working_set.error(ParseError::UnknownFlag( + sig.name.clone(), + long_name.clone(), + arg_span, + sig.clone().formatted_flags(), + )); ( Some(Spanned { item: long_name.clone(), span: arg_span, }), None, - Some(ParseError::UnknownFlag( - sig.name.clone(), - long_name.clone(), - arg_span, - sig.clone().formatted_flags(), - )), ) } } else { + working_set.error(ParseError::NonUtf8(arg_span)); ( Some(Spanned { item: "--".into(), span: arg_span, }), None, - Some(ParseError::NonUtf8(arg_span)), ) } } else { - (None, None, None) + (None, None) } } @@ -473,8 +463,7 @@ fn parse_short_flags( spans_idx: &mut usize, positional_idx: usize, sig: &Signature, -) -> (Option>, Option) { - let mut error = None; +) -> Option> { let arg_span = spans[*spans_idx]; let arg_contents = working_set.get_span_contents(arg_span); @@ -493,7 +482,8 @@ fn parse_short_flags( if let Some(flag) = sig.get_short_flag(short_flag_char) { // If we require an arg and are in a batch of short flags, error if !found_short_flags.is_empty() && flag.arg.is_some() { - error = error.or(Some(ParseError::ShortFlagBatchCantTakeArg(short_flag_span))) + working_set.error(ParseError::ShortFlagBatchCantTakeArg(short_flag_span)); + break; } found_short_flags.push(flag); } else { @@ -502,61 +492,55 @@ fn parse_short_flags( } if found_short_flags.is_empty() { + let arg_contents = working_set.get_span_contents(arg_span); + // check to see if we have a negative number if let Some(positional) = sig.get_positional(positional_idx) { if positional.shape == SyntaxShape::Int || positional.shape == SyntaxShape::Number { if String::from_utf8_lossy(arg_contents).parse::().is_ok() { - return (None, None); + return None; } else if let Some(first) = unmatched_short_flags.first() { let contents = working_set.get_span_contents(*first); - error = error.or_else(|| { - Some(ParseError::UnknownFlag( - sig.name.clone(), - format!("-{}", String::from_utf8_lossy(contents)), - *first, - sig.clone().formatted_flags(), - )) - }); - } - } else if let Some(first) = unmatched_short_flags.first() { - let contents = working_set.get_span_contents(*first); - error = error.or_else(|| { - Some(ParseError::UnknownFlag( + working_set.error(ParseError::UnknownFlag( sig.name.clone(), format!("-{}", String::from_utf8_lossy(contents)), *first, sig.clone().formatted_flags(), - )) - }); - } - } else if let Some(first) = unmatched_short_flags.first() { - let contents = working_set.get_span_contents(*first); - error = error.or_else(|| { - Some(ParseError::UnknownFlag( + )); + } + } else if let Some(first) = unmatched_short_flags.first() { + let contents = working_set.get_span_contents(*first); + working_set.error(ParseError::UnknownFlag( sig.name.clone(), format!("-{}", String::from_utf8_lossy(contents)), *first, sig.clone().formatted_flags(), - )) - }); + )); + } + } else if let Some(first) = unmatched_short_flags.first() { + let contents = working_set.get_span_contents(*first); + working_set.error(ParseError::UnknownFlag( + sig.name.clone(), + format!("-{}", String::from_utf8_lossy(contents)), + *first, + sig.clone().formatted_flags(), + )); } } else if !unmatched_short_flags.is_empty() { if let Some(first) = unmatched_short_flags.first() { let contents = working_set.get_span_contents(*first); - error = error.or_else(|| { - Some(ParseError::UnknownFlag( - sig.name.clone(), - format!("-{}", String::from_utf8_lossy(contents)), - *first, - sig.clone().formatted_flags(), - )) - }); + working_set.error(ParseError::UnknownFlag( + sig.name.clone(), + format!("-{}", String::from_utf8_lossy(contents)), + *first, + sig.clone().formatted_flags(), + )); } } - (Some(found_short_flags), error) + Some(found_short_flags) } else { - (None, None) + None } } @@ -631,106 +615,99 @@ pub fn parse_multispan_value( spans_idx: &mut usize, shape: &SyntaxShape, expand_aliases_denylist: &[usize], -) -> (Expression, Option) { - let mut error = None; - +) -> Expression { match shape { SyntaxShape::VarWithOptType => { trace!("parsing: var with opt type"); - let (arg, err) = parse_var_with_opt_type(working_set, spans, spans_idx, false); - error = error.or(err); - - (arg, error) + parse_var_with_opt_type(working_set, spans, spans_idx, false) } SyntaxShape::RowCondition => { trace!("parsing: row condition"); - let (arg, err) = + let arg = parse_row_condition(working_set, &spans[*spans_idx..], expand_aliases_denylist); - error = error.or(err); *spans_idx = spans.len() - 1; - (arg, error) + arg } SyntaxShape::MathExpression => { trace!("parsing: math expression"); - let (arg, err) = parse_math_expression( + let arg = parse_math_expression( working_set, &spans[*spans_idx..], None, expand_aliases_denylist, ); - error = error.or(err); *spans_idx = spans.len() - 1; - (arg, error) + arg } SyntaxShape::OneOf(shapes) => { // handle for `if` command. - let block_then_exp = shapes.as_slice() == [SyntaxShape::Block, SyntaxShape::Expression]; - let mut err = None; + //let block_then_exp = shapes.as_slice() == [SyntaxShape::Block, SyntaxShape::Expression]; for shape in shapes.iter() { - let (s, option_err) = parse_multispan_value( + let starting_error_count = working_set.parse_errors.len(); + let s = parse_multispan_value( working_set, spans, spans_idx, shape, expand_aliases_denylist, ); - match option_err { - None => return (s, None), - e => { - // `if` is parsing block first and then expression. - // when we're writing something like `else if $a`, parsing as a - // block will result to error(because it's not a block) - // - // If parse as a expression also failed, user is more likely concerned - // about expression failure rather than "expect block failure"". - if block_then_exp { - match &err { - Some(ParseError::Expected(expected, _)) => { - if expected.starts_with("block") { - err = e - } - } - _ => err = err.or(e), - } - } else { - err = err.or(e) - } - } + + if starting_error_count == working_set.parse_errors.len() { + return s; + } else if let Some(ParseError::Expected(..)) = working_set.parse_errors.last() { + working_set.parse_errors.truncate(starting_error_count); + continue; } + // `if` is parsing block first and then expression. + // when we're writing something like `else if $a`, parsing as a + // block will result to error(because it's not a block) + // + // If parse as a expression also failed, user is more likely concerned + // about expression failure rather than "expect block failure"". + + // FIXME FIXME FIXME + // if block_then_exp { + // match &err { + // Some(ParseError::Expected(expected, _)) => { + // if expected.starts_with("block") { + // err = e + // } + // } + // _ => err = err.or(e), + // } + // } else { + // err = err.or(e) + // } } let span = spans[*spans_idx]; - if err.is_some() { - (Expression::garbage(span), err) - } else { - ( - Expression::garbage(span), - Some(ParseError::Expected( - format!("one of a list of accepted shapes: {shapes:?}"), - span, - )), - ) + if working_set.parse_errors.is_empty() { + working_set.error(ParseError::Expected( + format!("one of a list of accepted shapes: {shapes:?}"), + span, + )); } + + Expression::garbage(span) } SyntaxShape::Expression => { trace!("parsing: expression"); // is it subexpression? // Not sure, but let's make it not, so the behavior is the same as previous version of nushell. - let (arg, err) = parse_expression( + let arg = parse_expression( working_set, &spans[*spans_idx..], expand_aliases_denylist, false, ); - error = error.or(err); *spans_idx = spans.len() - 1; - (arg, error) + arg } SyntaxShape::Keyword(keyword, arg) => { trace!( @@ -747,7 +724,7 @@ pub fn parse_multispan_value( // We won't often override the current error, but as this is a strong indicator // go ahead and override the current error and tell the user about the missing // keyword/literal. - error = Some(ParseError::ExpectedKeyword( + working_set.error(ParseError::ExpectedKeyword( String::from_utf8_lossy(keyword).into(), arg_span, )) @@ -755,51 +732,39 @@ pub fn parse_multispan_value( *spans_idx += 1; if *spans_idx >= spans.len() { - error = error.or_else(|| { - Some(ParseError::KeywordMissingArgument( - arg.to_string(), - String::from_utf8_lossy(keyword).into(), - Span::new(spans[*spans_idx - 1].end, spans[*spans_idx - 1].end), - )) - }); - return ( - Expression { - expr: Expr::Keyword( - keyword.clone(), - spans[*spans_idx - 1], - Box::new(Expression::garbage(arg_span)), - ), - span: arg_span, - ty: Type::Any, - custom_completion: None, - }, - error, - ); + working_set.error(ParseError::KeywordMissingArgument( + arg.to_string(), + String::from_utf8_lossy(keyword).into(), + Span::new(spans[*spans_idx - 1].end, spans[*spans_idx - 1].end), + )); + return Expression { + expr: Expr::Keyword( + keyword.clone(), + spans[*spans_idx - 1], + Box::new(Expression::garbage(arg_span)), + ), + span: arg_span, + ty: Type::Any, + custom_completion: None, + }; } let keyword_span = spans[*spans_idx - 1]; - let (expr, err) = + let expr = parse_multispan_value(working_set, spans, spans_idx, arg, expand_aliases_denylist); - error = error.or(err); let ty = expr.ty.clone(); - ( - Expression { - expr: Expr::Keyword(keyword.clone(), keyword_span, Box::new(expr)), - span: arg_span, - ty, - custom_completion: None, - }, - error, - ) + Expression { + expr: Expr::Keyword(keyword.clone(), keyword_span, Box::new(expr)), + span: arg_span, + ty, + custom_completion: None, + } } _ => { // All other cases are single-span values let arg_span = spans[*spans_idx]; - let (arg, err) = parse_value(working_set, arg_span, shape, expand_aliases_denylist); - error = error.or(err); - - (arg, error) + parse_value(working_set, arg_span, shape, expand_aliases_denylist) } } } @@ -807,7 +772,6 @@ pub fn parse_multispan_value( pub struct ParsedInternalCall { pub call: Box, pub output: Type, - pub error: Option, } fn attach_parser_info_builtin(working_set: &StateWorkingSet, name: &str, call: &mut Call) { @@ -838,8 +802,6 @@ pub fn parse_internal_call( ) -> ParsedInternalCall { trace!("parsing: internal call (decl id: {})", decl_id); - let mut error = None; - let mut call = Call::new(command_span); call.decl_id = decl_id; call.head = command_span; @@ -871,13 +833,13 @@ pub fn parse_internal_call( // Skip positionals passed to aliased call positional_idx = call.positional_len(); } else { + working_set.error(ParseError::UnknownState( + "Alias does not point to internal call.".to_string(), + command_span, + )); return ParsedInternalCall { call: Box::new(call), output: Type::Any, - error: Some(ParseError::UnknownState( - "Alias does not point to internal call.".to_string(), - command_span, - )), }; } } @@ -891,8 +853,9 @@ pub fn parse_internal_call( while spans_idx < spans.len() { let arg_span = spans[spans_idx]; + let starting_error_count = working_set.parse_errors.len(); // Check if we're on a long flag, if so, parse - let (long_name, arg, err) = parse_long_flag( + let (long_name, arg) = parse_long_flag( working_set, spans, &mut spans_idx, @@ -902,20 +865,21 @@ pub fn parse_internal_call( if let Some(long_name) = long_name { // We found a long flag, like --bar - if matches!(err, Some(ParseError::UnknownFlag(_, _, _, _))) + if working_set.parse_errors[starting_error_count..] + .iter() + .any(|x| matches!(x, ParseError::UnknownFlag(_, _, _, _))) && signature.allows_unknown_args { - let (arg, arg_err) = parse_value( + working_set.parse_errors.truncate(starting_error_count); + let arg = parse_value( working_set, arg_span, &SyntaxShape::Any, expand_aliases_denylist, ); - error = error.or(arg_err); call.add_unknown(arg); } else { - error = error.or(err); call.add_named((long_name, None, arg)); } @@ -923,8 +887,10 @@ pub fn parse_internal_call( continue; } + let starting_error_count = working_set.parse_errors.len(); + // Check if we're on a short flag or group of short flags, if so, parse - let (short_flags, err) = parse_short_flags( + let short_flags = parse_short_flags( working_set, spans, &mut spans_idx, @@ -946,10 +912,13 @@ pub fn parse_internal_call( }) } - if matches!(err, Some(ParseError::UnknownFlag(_, _, _, _))) + if working_set.parse_errors[starting_error_count..] + .iter() + .any(|x| matches!(x, ParseError::UnknownFlag(_, _, _, _))) && signature.allows_unknown_args { - let (arg, arg_err) = parse_value( + working_set.parse_errors.truncate(starting_error_count); + let arg = parse_value( working_set, arg_span, &SyntaxShape::Any, @@ -957,15 +926,12 @@ pub fn parse_internal_call( ); call.add_unknown(arg); - error = error.or(arg_err); } else { - error = error.or(err); for flag in short_flags { if let Some(arg_shape) = flag.arg { if let Some(arg) = spans.get(spans_idx + 1) { - let (arg, err) = + let arg = parse_value(working_set, *arg, &arg_shape, expand_aliases_denylist); - error = error.or(err); if flag.long.is_empty() { if let Some(short) = flag.short { @@ -993,12 +959,10 @@ pub fn parse_internal_call( } spans_idx += 1; } else { - error = error.or_else(|| { - Some(ParseError::MissingFlagParam( - arg_shape.to_string(), - arg_span, - )) - }) + working_set.error(ParseError::MissingFlagParam( + arg_shape.to_string(), + arg_span, + )) } } else if flag.long.is_empty() { if let Some(short) = flag.short { @@ -1042,34 +1006,29 @@ pub fn parse_internal_call( }; if spans[..end].is_empty() || spans_idx == end { - error = error.or_else(|| { - Some(ParseError::MissingPositional( - positional.name.clone(), - Span::new(spans[spans_idx].end, spans[spans_idx].end), - signature.call_signature(), - )) - }); + working_set.error(ParseError::MissingPositional( + positional.name.clone(), + Span::new(spans[spans_idx].end, spans[spans_idx].end), + signature.call_signature(), + )); positional_idx += 1; continue; } - let (arg, err) = parse_multispan_value( + let arg = parse_multispan_value( working_set, &spans[..end], &mut spans_idx, &positional.shape, expand_aliases_denylist, ); - error = error.or(err); let arg = if !type_compatible(&positional.shape.to_type(), &arg.ty) { - error = error.or_else(|| { - Some(ParseError::TypeMismatch( - positional.shape.to_type(), - arg.ty, - arg.span, - )) - }); + working_set.error(ParseError::TypeMismatch( + positional.shape.to_type(), + arg.ty, + arg.span, + )); Expression::garbage(arg.span) } else { arg @@ -1077,7 +1036,7 @@ pub fn parse_internal_call( call.add_positional(arg); positional_idx += 1; } else if signature.allows_unknown_args { - let (arg, arg_err) = parse_value( + let arg = parse_value( working_set, arg_span, &SyntaxShape::Any, @@ -1085,23 +1044,18 @@ pub fn parse_internal_call( ); call.add_unknown(arg); - error = error.or(arg_err); } else { call.add_positional(Expression::garbage(arg_span)); - error = error.or_else(|| { - Some(ParseError::ExtraPositional( - signature.call_signature(), - arg_span, - )) - }) + working_set.error(ParseError::ExtraPositional( + signature.call_signature(), + arg_span, + )) } - error = error.or(err); spans_idx += 1; } - let err = check_call(command_span, &signature, &call); - error = error.or(err); + check_call(working_set, command_span, &signature, &call); if signature.creates_scope { working_set.exit_scope(); @@ -1110,7 +1064,6 @@ pub fn parse_internal_call( ParsedInternalCall { call: Box::new(call), output, - error, } } @@ -1120,17 +1073,15 @@ pub fn parse_call( head: Span, expand_aliases_denylist: &[usize], is_subexpression: bool, -) -> (Expression, Option) { +) -> Expression { trace!("parsing: call"); if spans.is_empty() { - return ( - garbage(head), - Some(ParseError::UnknownState( - "Encountered command with zero spans".into(), - span(spans), - )), - ); + working_set.error(ParseError::UnknownState( + "Encountered command with zero spans".into(), + span(spans), + )); + return garbage(head); } let mut pos = 0; @@ -1184,7 +1135,7 @@ pub fn parse_call( parts: new_spans.clone(), }; - let (mut result, err) = parse_builtin_commands( + let mut result = parse_builtin_commands( working_set, &lite_command, &expand_aliases_denylist, @@ -1197,7 +1148,7 @@ pub fn parse_call( if let PipelineElement::Expression(_, mut result) = result { result.replace_span(working_set, expansion_span, orig_span); - return (result, err); + return result; } else { panic!("Internal error: first element of pipeline not an expression") } @@ -1242,13 +1193,11 @@ pub fn parse_call( if test_equal == [b'='] { trace!("incomplete statement"); - return ( - garbage(span(spans)), - Some(ParseError::UnknownState( - "Incomplete statement".into(), - span(spans), - )), - ); + working_set.error(ParseError::UnknownState( + "Incomplete statement".into(), + span(spans), + )); + return garbage(span(spans)); } } @@ -1265,28 +1214,22 @@ pub fn parse_call( { trace!("parsing: alias of external call"); - let mut error = None; let mut final_args = args.clone(); for arg_span in spans.iter().skip(1) { - let (arg, err) = - parse_external_arg(working_set, *arg_span, expand_aliases_denylist); - error = error.or(err); + let arg = parse_external_arg(working_set, *arg_span, expand_aliases_denylist); final_args.push(arg); } let mut head = head.clone(); head.span = spans[0]; // replacing the spans preserves syntax highlighting - return ( - Expression { - expr: Expr::ExternalCall(head, final_args, *is_subexpression), - span: span(spans), - ty: ty.clone(), - custom_completion: *custom_completion, - }, - error, - ); + return Expression { + expr: Expr::ExternalCall(head, final_args, *is_subexpression), + span: span(spans), + ty: ty.clone(), + custom_completion: *custom_completion, + }; } else { trace!("parsing: alias of internal call"); parse_internal_call( @@ -1308,27 +1251,26 @@ pub fn parse_call( ) }; - ( - Expression { - expr: Expr::Call(parsed_call.call), - span: span(spans), - ty: parsed_call.output, - custom_completion: None, - }, - parsed_call.error, - ) + Expression { + expr: Expr::Call(parsed_call.call), + span: span(spans), + ty: parsed_call.output, + custom_completion: None, + } } else { // We might be parsing left-unbounded range ("..10") let bytes = working_set.get_span_contents(spans[0]); trace!("parsing: range {:?} ", bytes); if let (Some(b'.'), Some(b'.')) = (bytes.first(), bytes.get(1)) { trace!("-- found leading range indicator"); - let (range_expr, range_err) = - parse_range(working_set, spans[0], expand_aliases_denylist); - if range_err.is_none() { + let starting_error_count = working_set.parse_errors.len(); + + let range_expr = parse_range(working_set, spans[0], expand_aliases_denylist); + if working_set.parse_errors.len() == starting_error_count { trace!("-- successfully parsed range"); - return (range_expr, range_err); + return range_expr; } + working_set.parse_errors.truncate(starting_error_count); } trace!("parsing: external call"); @@ -1342,19 +1284,15 @@ pub fn parse_call( } } -pub fn parse_binary( - working_set: &mut StateWorkingSet, - span: Span, -) -> (Expression, Option) { - let (hex_value, err) = parse_binary_with_base(working_set, span, 16, 2, b"0x[", b"]"); - if err.is_some() { - let (octal_value, err) = parse_binary_with_base(working_set, span, 8, 3, b"0o[", b"]"); - if err.is_some() { - return parse_binary_with_base(working_set, span, 2, 8, b"0b[", b"]"); - } - return (octal_value, err); +pub fn parse_binary(working_set: &mut StateWorkingSet, span: Span) -> Expression { + let contents = working_set.get_span_contents(span); + if contents.starts_with(b"0x[") { + parse_binary_with_base(working_set, span, 16, 2, b"0x[", b"]") + } else if contents.starts_with(b"0o[") { + parse_binary_with_base(working_set, span, 8, 3, b"0o[", b"]") + } else { + parse_binary_with_base(working_set, span, 2, 8, b"0b[", b"]") } - (hex_value, err) } fn parse_binary_with_base( @@ -1364,7 +1302,7 @@ fn parse_binary_with_base( min_digits_per_byte: usize, prefix: &[u8], suffix: &[u8], -) -> (Expression, Option) { +) -> Expression { let token = working_set.get_span_contents(span); if let Some(token) = token.strip_prefix(prefix) { @@ -1376,6 +1314,9 @@ fn parse_binary_with_base( &[], true, ); + if let Some(err) = err { + working_set.error(err); + } let mut binary_value = vec![]; for token in lexed { @@ -1390,10 +1331,8 @@ fn parse_binary_with_base( | TokenContents::OutGreaterThan | TokenContents::ErrGreaterThan | TokenContents::OutErrGreaterThan => { - return ( - garbage(span), - Some(ParseError::Expected("binary".into(), span)), - ); + working_set.error(ParseError::Expected("binary".into(), span)); + return garbage(span); } TokenContents::Comment | TokenContents::Semicolon | TokenContents::Eol => {} } @@ -1415,33 +1354,27 @@ fn parse_binary_with_base( match decode_with_base(&str, base, min_digits_per_byte) { Ok(v) => { - return ( - Expression { - expr: Expr::Binary(v), - span, - ty: Type::Binary, - custom_completion: None, - }, - err, - ) + return Expression { + expr: Expr::Binary(v), + span, + ty: Type::Binary, + custom_completion: None, + } } Err(x) => { - return ( - garbage(span), - Some(ParseError::IncorrectValue( - "not a binary value".into(), - span, - x.to_string(), - )), - ) + working_set.error(ParseError::IncorrectValue( + "not a binary value".into(), + span, + x.to_string(), + )); + return garbage(span); } } } } - ( - garbage(span), - Some(ParseError::Expected("binary".into(), span)), - ) + + working_set.error(ParseError::Expected("binary".into(), span)); + garbage(span) } fn decode_with_base(s: &str, base: u32, digits_per_byte: usize) -> Result, ParseIntError> { @@ -1462,107 +1395,107 @@ fn strip_underscores(token: &[u8]) -> String { .collect() } -pub fn parse_int(token: &[u8], span: Span) -> (Expression, Option) { - fn extract_int(token: &str, span: Span, radix: u32) -> (Expression, Option) { +pub fn parse_int(working_set: &mut StateWorkingSet, span: Span) -> Expression { + let token = working_set.get_span_contents(span); + + fn extract_int( + working_set: &mut StateWorkingSet, + token: &str, + span: Span, + radix: u32, + ) -> Expression { if let Ok(num) = i64::from_str_radix(token, radix) { - ( - Expression { - expr: Expr::Int(num), - span, - ty: Type::Int, - custom_completion: None, - }, - None, - ) + Expression { + expr: Expr::Int(num), + span, + ty: Type::Int, + custom_completion: None, + } } else { - ( - garbage(span), - Some(ParseError::InvalidLiteral( - format!("invalid digits for radix {}", radix), - "int".into(), - span, - )), - ) + working_set.error(ParseError::InvalidLiteral( + format!("invalid digits for radix {}", radix), + "int".into(), + span, + )); + + garbage(span) } } let token = strip_underscores(token); if token.is_empty() { - return ( - garbage(span), - Some(ParseError::Expected("int".into(), span)), - ); + working_set.error(ParseError::Expected("int".into(), span)); + return garbage(span); } if let Some(num) = token.strip_prefix("0b") { - extract_int(num, span, 2) + extract_int(working_set, num, span, 2) } else if let Some(num) = token.strip_prefix("0o") { - extract_int(num, span, 8) + extract_int(working_set, num, span, 8) } else if let Some(num) = token.strip_prefix("0x") { - extract_int(num, span, 16) + extract_int(working_set, num, span, 16) } else if let Ok(num) = token.parse::() { - ( - Expression { - expr: Expr::Int(num), - span, - ty: Type::Int, - custom_completion: None, - }, - None, - ) + Expression { + expr: Expr::Int(num), + span, + ty: Type::Int, + custom_completion: None, + } } else { - ( - garbage(span), - Some(ParseError::Expected("int".into(), span)), - ) + working_set.error(ParseError::Expected("int".into(), span)); + garbage(span) } } -pub fn parse_float(token: &[u8], span: Span) -> (Expression, Option) { +pub fn parse_float(working_set: &mut StateWorkingSet, span: Span) -> Expression { + let token = working_set.get_span_contents(span); let token = strip_underscores(token); if let Ok(x) = token.parse::() { - ( - Expression { - expr: Expr::Float(x), - span, - ty: Type::Float, - custom_completion: None, - }, - None, - ) + Expression { + expr: Expr::Float(x), + span, + ty: Type::Float, + custom_completion: None, + } } else { - ( - garbage(span), - Some(ParseError::Expected("float".into(), span)), - ) + working_set.error(ParseError::Expected("float".into(), span)); + + garbage(span) } } -pub fn parse_number(token: &[u8], span: Span) -> (Expression, Option) { - match parse_int(token, span) { - (x, None) => { - return (x, None); - } - (_, Some(ParseError::Expected(_, _))) => {} - (x, e) => return (x, e), - } - if let (x, None) = parse_float(token, span) { - return (x, None); +pub fn parse_number(working_set: &mut StateWorkingSet, span: Span) -> Expression { + let starting_error_count = working_set.parse_errors.len(); + + let result = parse_int(working_set, span); + if starting_error_count == working_set.parse_errors.len() { + return result; + } else if !matches!( + working_set.parse_errors.last(), + Some(ParseError::Expected(_, _)) + ) { + } else { + working_set.parse_errors.truncate(starting_error_count); } - ( - garbage(span), - Some(ParseError::Expected("number".into(), span)), - ) + let result = parse_float(working_set, span); + + if starting_error_count == working_set.parse_errors.len() { + return result; + } + working_set.parse_errors.truncate(starting_error_count); + + working_set.error(ParseError::Expected("number".into(), span)); + garbage(span) } pub fn parse_range( working_set: &mut StateWorkingSet, span: Span, expand_aliases_denylist: &[usize], -) -> (Expression, Option) { +) -> Expression { trace!("parsing: range"); // Range follows the following syntax: [][][] @@ -1577,35 +1510,32 @@ pub fn parse_range( let token = if let Ok(s) = String::from_utf8(contents.into()) { s } else { - return (garbage(span), Some(ParseError::NonUtf8(span))); + working_set.error(ParseError::NonUtf8(span)); + return garbage(span); }; if !token.contains("..") { - return ( - garbage(span), - Some(ParseError::Expected( - "at least one range bound set".into(), - span, - )), - ); + working_set.error(ParseError::Expected( + "at least one range bound set".into(), + span, + )); + return garbage(span); } // First, figure out what exact operators are used and determine their positions let dotdot_pos: Vec<_> = token.match_indices("..").map(|(pos, _)| pos).collect(); - let (next_op_pos, range_op_pos) = - match dotdot_pos.len() { - 1 => (None, dotdot_pos[0]), - 2 => (Some(dotdot_pos[0]), dotdot_pos[1]), - _ => return ( - garbage(span), - Some(ParseError::Expected( - "one range operator ('..' or '..<') and optionally one next operator ('..')" - .into(), - span, - )), - ), - }; + let (next_op_pos, range_op_pos) = match dotdot_pos.len() { + 1 => (None, dotdot_pos[0]), + 2 => (Some(dotdot_pos[0]), dotdot_pos[1]), + _ => { + working_set.error(ParseError::Expected( + "one range operator ('..' or '..<') and optionally one next operator ('..')".into(), + span, + )); + return garbage(span); + } + }; let (inclusion, range_op_str, range_op_span) = if let Some(pos) = token.find("..<") { if pos == range_op_pos { @@ -1616,13 +1546,11 @@ pub fn parse_range( ); (RangeInclusion::RightExclusive, "..<", op_span) } else { - return ( - garbage(span), - Some(ParseError::Expected( - "inclusive operator preceding second range bound".into(), - span, - )), - ); + working_set.error(ParseError::Expected( + "inclusive operator preceding second range bound".into(), + span, + )); + return garbage(span); } } else { let op_str = ".."; @@ -1641,72 +1569,49 @@ pub fn parse_range( None } else { let from_span = Span::new(span.start, span.start + dotdot_pos[0]); - match parse_value( + Some(Box::new(parse_value( working_set, from_span, &SyntaxShape::Number, expand_aliases_denylist, - ) { - (expression, None) => Some(Box::new(expression)), - _ => { - return ( - garbage(span), - Some(ParseError::Expected("number".into(), span)), - ) - } - } + ))) }; let to = if token.ends_with(range_op_str) { None } else { let to_span = Span::new(range_op_span.end, span.end); - match parse_value( + Some(Box::new(parse_value( working_set, to_span, &SyntaxShape::Number, expand_aliases_denylist, - ) { - (expression, None) => Some(Box::new(expression)), - _ => { - return ( - garbage(span), - Some(ParseError::Expected("number".into(), span)), - ) - } - } + ))) }; trace!("-- from: {:?} to: {:?}", from, to); if let (None, None) = (&from, &to) { - return ( - garbage(span), - Some(ParseError::Expected( - "at least one range bound set".into(), - span, - )), - ); + working_set.error(ParseError::Expected( + "at least one range bound set".into(), + span, + )); + return garbage(span); } let (next, next_op_span) = if let Some(pos) = next_op_pos { let next_op_span = Span::new(span.start + pos, span.start + pos + "..".len()); let next_span = Span::new(next_op_span.end, range_op_span.start); - match parse_value( - working_set, - next_span, - &SyntaxShape::Number, - expand_aliases_denylist, - ) { - (expression, None) => (Some(Box::new(expression)), next_op_span), - _ => { - return ( - garbage(span), - Some(ParseError::Expected("number".into(), span)), - ) - } - } + ( + Some(Box::new(parse_value( + working_set, + next_span, + &SyntaxShape::Number, + expand_aliases_denylist, + ))), + next_op_span, + ) } else { (None, span) }; @@ -1717,22 +1622,19 @@ pub fn parse_range( next_op_span, }; - ( - Expression { - expr: Expr::Range(from, next, to, range_op), - span, - ty: Type::Range, - custom_completion: None, - }, - None, - ) + Expression { + expr: Expr::Range(from, next, to, range_op), + span, + ty: Type::Range, + custom_completion: None, + } } pub(crate) fn parse_dollar_expr( working_set: &mut StateWorkingSet, span: Span, expand_aliases_denylist: &[usize], -) -> (Expression, Option) { +) -> Expression { trace!("parsing: dollar expression"); let contents = working_set.get_span_contents(span); @@ -1744,10 +1646,16 @@ pub(crate) fn parse_dollar_expr( Span::new(span.start + 2, span.end), expand_aliases_denylist, ) - } else if let (expr, None) = parse_range(working_set, span, expand_aliases_denylist) { - (expr, None) } else { - parse_full_cell_path(working_set, None, span, expand_aliases_denylist) + let starting_error_count = working_set.parse_errors.len(); + + let expr = parse_range(working_set, span, expand_aliases_denylist); + if starting_error_count == working_set.parse_errors.len() { + expr + } else { + working_set.parse_errors.truncate(starting_error_count); + parse_full_cell_path(working_set, None, span, expand_aliases_denylist) + } } } @@ -1756,13 +1664,21 @@ pub fn parse_paren_expr( span: Span, shape: &SyntaxShape, expand_aliases_denylist: &[usize], -) -> (Expression, Option) { - if let (expr, None) = parse_range(working_set, span, expand_aliases_denylist) { - (expr, None) - } else if matches!(shape, SyntaxShape::Signature) { - return parse_signature(working_set, span, expand_aliases_denylist); +) -> Expression { + let starting_error_count = working_set.parse_errors.len(); + + let expr = parse_range(working_set, span, expand_aliases_denylist); + + if starting_error_count == working_set.parse_errors.len() { + expr } else { - parse_full_cell_path(working_set, None, span, expand_aliases_denylist) + working_set.parse_errors.truncate(starting_error_count); + + if matches!(shape, SyntaxShape::Signature) { + parse_signature(working_set, span, expand_aliases_denylist) + } else { + parse_full_cell_path(working_set, None, span, expand_aliases_denylist) + } } } @@ -1771,27 +1687,28 @@ pub fn parse_numberlike_expr( span: Span, shape: &SyntaxShape, expand_aliases_denylist: &[usize], -) -> (Expression, Option) { - let bytes = working_set.get_span_contents(span); +) -> Expression { match shape { SyntaxShape::Binary => parse_binary(working_set, span), - SyntaxShape::Number => parse_number(bytes, span), - SyntaxShape::Decimal => parse_float(bytes, span), - SyntaxShape::Int => parse_int(bytes, span), + SyntaxShape::Number => parse_number(working_set, span), + SyntaxShape::Decimal => parse_float(working_set, span), + SyntaxShape::Int => parse_int(working_set, span), SyntaxShape::Duration => parse_duration(working_set, span), SyntaxShape::DateTime => parse_datetime(working_set, span), SyntaxShape::Filesize => parse_filesize(working_set, span), SyntaxShape::Range => parse_range(working_set, span, expand_aliases_denylist), SyntaxShape::CellPath => parse_simple_cell_path(working_set, span, expand_aliases_denylist), - SyntaxShape::String => ( - garbage(span), - Some(ParseError::Mismatch( + SyntaxShape::String => { + working_set.error(ParseError::Mismatch( "string".into(), "number-like value (hint: use quotes or backticks)".into(), span, - )), - ), + )); + garbage(span) + } SyntaxShape::Any => { + let bytes = working_set.get_span_contents(span); + if bytes == b"0b" { // FIXME: having to work around this filesize that also looks like a binary value parse_filesize(working_set, span) @@ -1804,7 +1721,7 @@ pub fn parse_numberlike_expr( || bytes.starts_with(b"0b") || bytes.starts_with(b"0o") { - parse_int(bytes, span) + parse_int(working_set, span) } else { for shape in &[ SyntaxShape::Range, @@ -1815,28 +1732,31 @@ pub fn parse_numberlike_expr( SyntaxShape::DateTime, //FIXME requires 3 failed conversion attempts before failing SyntaxShape::Number, ] { - let (result, err) = - parse_value(working_set, span, shape, expand_aliases_denylist); - if err.is_none() { - return (result, err); + let starting_error_count = working_set.parse_errors.len(); + + let result = parse_value(working_set, span, shape, expand_aliases_denylist); + + if starting_error_count == working_set.parse_errors.len() { + return result; + } else { + working_set.parse_errors.truncate(starting_error_count); } } - ( - garbage(span), - Some(ParseError::Expected( - "number-like value (int, float, date, etc)".into(), - span, - )), - ) + + working_set.error(ParseError::Expected( + "number-like value (int, float, date, etc)".into(), + span, + )); + garbage(span) } } - _ => ( - garbage(span), - Some(ParseError::Expected( + _ => { + working_set.error(ParseError::Expected( "number-like value (int, float, date, etc)".into(), span, - )), - ), + )); + garbage(span) + } } } @@ -1845,7 +1765,7 @@ pub fn parse_brace_expr( span: Span, shape: &SyntaxShape, expand_aliases_denylist: &[usize], -) -> (Expression, Option) { +) -> Expression { // Try to detect what kind of value we're about to parse // FIXME: In the future, we should work over the token stream so we only have to do this once // before parsing begins @@ -1855,13 +1775,11 @@ pub fn parse_brace_expr( // and then revisit the parsing. if span.end <= (span.start + 1) { - return ( - Expression::garbage(span), - Some(ParseError::Expected( - format!("non-block value: {shape}"), - span, - )), - ); + working_set.error(ParseError::Expected( + format!("non-block value: {shape}"), + span, + )); + return Expression::garbage(span); } let bytes = working_set.get_span_contents(Span::new(span.start + 1, span.end - 1)); @@ -1905,13 +1823,12 @@ pub fn parse_brace_expr( } else if matches!(shape, SyntaxShape::MatchBlock) { parse_match_block_expression(working_set, span, expand_aliases_denylist) } else { - ( - Expression::garbage(span), - Some(ParseError::Expected( - format!("non-block value: {shape}"), - span, - )), - ) + working_set.error(ParseError::Expected( + format!("non-block value: {shape}"), + span, + )); + + Expression::garbage(span) } } @@ -1919,13 +1836,12 @@ pub fn parse_string_interpolation( working_set: &mut StateWorkingSet, span: Span, expand_aliases_denylist: &[usize], -) -> (Expression, Option) { +) -> Expression { #[derive(PartialEq, Eq, Debug)] enum InterpolationMode { String, Expression, } - let mut error = None; let contents = working_set.get_span_contents(span); @@ -1982,14 +1898,14 @@ pub fn parse_string_interpolation( let span = Span::new(token_start, b); let str_contents = working_set.get_span_contents(span); - let str_contents = if double_quote { - let (str_contents, err) = unescape_string(str_contents, span); - error = error.or(err); - - str_contents + let (str_contents, err) = if double_quote { + unescape_string(str_contents, span) } else { - str_contents.to_vec() + (str_contents.to_vec(), None) }; + if let Some(err) = err { + working_set.error(err); + } output.push(Expression { expr: Expr::String(String::from_utf8_lossy(&str_contents).to_string()), @@ -2034,9 +1950,8 @@ pub fn parse_string_interpolation( if token_start < b { let span = Span::new(token_start, b + 1); - let (expr, err) = + let expr = parse_full_cell_path(working_set, None, span, expand_aliases_denylist); - error = error.or(err); output.push(expr); } @@ -2054,14 +1969,14 @@ pub fn parse_string_interpolation( let span = Span::new(token_start, end); let str_contents = working_set.get_span_contents(span); - let str_contents = if double_quote { - let (str_contents, err) = unescape_string(str_contents, span); - error = error.or(err); - - str_contents + let (str_contents, err) = if double_quote { + unescape_string(str_contents, span) } else { - str_contents.to_vec() + (str_contents.to_vec(), None) }; + if let Some(err) = err { + working_set.error(err); + } output.push(Expression { expr: Expr::String(String::from_utf8_lossy(&str_contents).to_string()), @@ -2075,91 +1990,65 @@ pub fn parse_string_interpolation( if token_start < end { let span = Span::new(token_start, end); - let (expr, err) = - parse_full_cell_path(working_set, None, span, expand_aliases_denylist); - error = error.or(err); + let expr = parse_full_cell_path(working_set, None, span, expand_aliases_denylist); output.push(expr); } } } - ( - Expression { - expr: Expr::StringInterpolation(output), - span, - ty: Type::String, - custom_completion: None, - }, - error, - ) + Expression { + expr: Expr::StringInterpolation(output), + span, + ty: Type::String, + custom_completion: None, + } } -pub fn parse_variable_expr( - working_set: &mut StateWorkingSet, - span: Span, -) -> (Expression, Option) { +pub fn parse_variable_expr(working_set: &mut StateWorkingSet, span: Span) -> Expression { let contents = working_set.get_span_contents(span); if contents == b"$nothing" { - return ( - Expression { - expr: Expr::Nothing, - span, - ty: Type::Nothing, - custom_completion: None, - }, - None, - ); + return Expression { + expr: Expr::Nothing, + span, + ty: Type::Nothing, + custom_completion: None, + }; } else if contents == b"$nu" { - return ( - Expression { - expr: Expr::Var(nu_protocol::NU_VARIABLE_ID), - span, - ty: Type::Any, - custom_completion: None, - }, - None, - ); + return Expression { + expr: Expr::Var(nu_protocol::NU_VARIABLE_ID), + span, + ty: Type::Any, + custom_completion: None, + }; } else if contents == b"$in" { - return ( - Expression { - expr: Expr::Var(nu_protocol::IN_VARIABLE_ID), - span, - ty: Type::Any, - custom_completion: None, - }, - None, - ); + return Expression { + expr: Expr::Var(nu_protocol::IN_VARIABLE_ID), + span, + ty: Type::Any, + custom_completion: None, + }; } else if contents == b"$env" { - return ( - Expression { - expr: Expr::Var(nu_protocol::ENV_VARIABLE_ID), - span, - ty: Type::Any, - custom_completion: None, - }, - None, - ); + return Expression { + expr: Expr::Var(nu_protocol::ENV_VARIABLE_ID), + span, + ty: Type::Any, + custom_completion: None, + }; } - let (id, err) = parse_variable(working_set, span); + let id = parse_variable(working_set, span); - if err.is_none() { - if let Some(id) = id { - ( - Expression { - expr: Expr::Var(id), - span, - ty: working_set.get_variable(id).ty.clone(), - custom_completion: None, - }, - None, - ) - } else { - (garbage(span), Some(ParseError::VariableNotFound(span))) + if let Some(id) = id { + Expression { + expr: Expr::Var(id), + span, + ty: working_set.get_variable(id).ty.clone(), + custom_completion: None, } } else { - (garbage(span), err) + working_set.error(ParseError::VariableNotFound(span)); + garbage(span) } } @@ -2168,7 +2057,7 @@ pub fn parse_cell_path( tokens: impl Iterator, expect_dot: bool, expand_aliases_denylist: &[usize], -) -> (Vec, Option) { +) -> Vec { enum TokenType { Dot, // . QuestionOrDot, // ? or . @@ -2182,7 +2071,6 @@ pub fn parse_cell_path( TokenType::PathMember }; - let mut error = None; let mut tail = vec![]; for path_element in tokens { @@ -2191,10 +2079,8 @@ pub fn parse_cell_path( match expected_token { TokenType::Dot => { if bytes.len() != 1 || bytes[0] != b'.' { - return ( - tail, - Some(ParseError::Expected('.'.into(), path_element.span)), - ); + working_set.error(ParseError::Expected('.'.into(), path_element.span)); + return tail; } expected_token = TokenType::PathMember; } @@ -2214,30 +2100,29 @@ pub fn parse_cell_path( } expected_token = TokenType::Dot; } else { - return ( - tail, - Some(ParseError::Expected(". or ?".into(), path_element.span)), - ); + working_set.error(ParseError::Expected(". or ?".into(), path_element.span)); + return tail; } } TokenType::PathMember => { - match parse_int(bytes, path_element.span) { - ( - Expression { - expr: Expr::Int(val), - span, - .. - }, - None, - ) => tail.push(PathMember::Int { + let starting_error_count = working_set.parse_errors.len(); + + let expr = parse_int(working_set, path_element.span); + working_set.parse_errors.truncate(starting_error_count); + + match expr { + Expression { + expr: Expr::Int(val), + span, + .. + } => tail.push(PathMember::Int { val: val as usize, span, optional: false, }), _ => { - let (result, err) = + let result = parse_string(working_set, path_element.span, expand_aliases_denylist); - error = error.or(err); match result { Expression { expr: Expr::String(string), @@ -2251,10 +2136,11 @@ pub fn parse_cell_path( }); } _ => { - return ( - tail, - Some(ParseError::Expected("string".into(), path_element.span)), - ); + working_set.error(ParseError::Expected( + "string".into(), + path_element.span, + )); + return tail; } } } @@ -2264,34 +2150,31 @@ pub fn parse_cell_path( } } - (tail, error) + tail } pub fn parse_simple_cell_path( working_set: &mut StateWorkingSet, span: Span, expand_aliases_denylist: &[usize], -) -> (Expression, Option) { +) -> Expression { let source = working_set.get_span_contents(span); - let mut error = None; let (tokens, err) = lex(source, span.start, &[b'\n', b'\r'], &[b'.', b'?'], true); - error = error.or(err); + if let Some(err) = err { + working_set.error(err) + } let tokens = tokens.into_iter().peekable(); - let (cell_path, err) = parse_cell_path(working_set, tokens, false, expand_aliases_denylist); - error = error.or(err); + let cell_path = parse_cell_path(working_set, tokens, false, expand_aliases_denylist); - ( - Expression { - expr: Expr::CellPath(CellPath { members: cell_path }), - span, - ty: Type::CellPath, - custom_completion: None, - }, - error, - ) + Expression { + expr: Expr::CellPath(CellPath { members: cell_path }), + span, + ty: Type::CellPath, + custom_completion: None, + } } pub fn parse_full_cell_path( @@ -2299,14 +2182,15 @@ pub fn parse_full_cell_path( implicit_head: Option, span: Span, expand_aliases_denylist: &[usize], -) -> (Expression, Option) { +) -> Expression { trace!("parsing: full cell path"); let full_cell_span = span; let source = working_set.get_span_contents(span); - let mut error = None; let (tokens, err) = lex(source, span.start, &[b'\n', b'\r'], &[b'.', b'?'], true); - error = error.or(err); + if let Some(err) = err { + working_set.error(err) + } let mut tokens = tokens.into_iter().peekable(); if let Some(head) = tokens.peek() { @@ -2324,8 +2208,7 @@ pub fn parse_full_cell_path( if bytes.ends_with(b")") { end -= 1; } else { - error = - error.or_else(|| Some(ParseError::Unclosed(")".into(), Span::new(end, end)))); + working_set.error(ParseError::Unclosed(")".into(), Span::new(end, end))); } let span = Span::new(start, end); @@ -2333,12 +2216,13 @@ pub fn parse_full_cell_path( let source = working_set.get_span_contents(span); let (output, err) = lex(source, span.start, &[b'\n', b'\r'], &[], true); - error = error.or(err); + if let Some(err) = err { + working_set.error(err) + } // Creating a Type scope to parse the new block. This will keep track of // the previous input type found in that block - let (output, err) = - parse_block(working_set, &output, true, expand_aliases_denylist, true); + let output = parse_block(working_set, &output, true, expand_aliases_denylist, true); working_set .type_scope .add_type(working_set.type_scope.get_last_output()); @@ -2363,8 +2247,6 @@ pub fn parse_full_cell_path( }) .unwrap_or_else(|| working_set.type_scope.get_last_output()); - error = error.or(err); - let block_id = working_set.add_block(output); tokens.next(); @@ -2380,17 +2262,14 @@ pub fn parse_full_cell_path( } else if bytes.starts_with(b"[") { trace!("parsing: table head of full cell path"); - let (output, err) = - parse_table_expression(working_set, head.span, expand_aliases_denylist); - error = error.or(err); + let output = parse_table_expression(working_set, head.span, expand_aliases_denylist); tokens.next(); (output, true) } else if bytes.starts_with(b"{") { trace!("parsing: record head of full cell path"); - let (output, err) = parse_record(working_set, head.span, expand_aliases_denylist); - error = error.or(err); + let output = parse_record(working_set, head.span, expand_aliases_denylist); tokens.next(); @@ -2398,8 +2277,7 @@ pub fn parse_full_cell_path( } else if bytes.starts_with(b"$") { trace!("parsing: $variable head of full cell path"); - let (out, err) = parse_variable_expr(working_set, head.span); - error = error.or(err); + let out = parse_variable_expr(working_set, head.span); tokens.next(); @@ -2416,176 +2294,140 @@ pub fn parse_full_cell_path( false, ) } else { - return ( - garbage(span), - Some(ParseError::Mismatch( - "variable or subexpression".into(), - String::from_utf8_lossy(bytes).to_string(), - span, - )), - ); + working_set.error(ParseError::Mismatch( + "variable or subexpression".into(), + String::from_utf8_lossy(bytes).to_string(), + span, + )); + return garbage(span); }; - let (tail, err) = parse_cell_path(working_set, tokens, expect_dot, expand_aliases_denylist); - error = error.or(err); + let tail = parse_cell_path(working_set, tokens, expect_dot, expand_aliases_denylist); - ( - Expression { - // FIXME: Get the type of the data at the tail using follow_cell_path() (or something) - ty: if !tail.is_empty() { - // Until the aforementioned fix is implemented, this is necessary to allow mutable list upserts - // such as $a.1 = 2 to work correctly. - Type::Any - } else { - head.ty.clone() - }, - expr: Expr::FullCellPath(Box::new(FullCellPath { head, tail })), - span: full_cell_span, - custom_completion: None, + Expression { + // FIXME: Get the type of the data at the tail using follow_cell_path() (or something) + ty: if !tail.is_empty() { + // Until the aforementioned fix is implemented, this is necessary to allow mutable list upserts + // such as $a.1 = 2 to work correctly. + Type::Any + } else { + head.ty.clone() }, - error, - ) + expr: Expr::FullCellPath(Box::new(FullCellPath { head, tail })), + span: full_cell_span, + custom_completion: None, + } } else { - (garbage(span), error) + garbage(span) } } -pub fn parse_directory( - working_set: &mut StateWorkingSet, - span: Span, -) -> (Expression, Option) { +pub fn parse_directory(working_set: &mut StateWorkingSet, span: Span) -> Expression { let bytes = working_set.get_span_contents(span); let (token, err) = unescape_unquote_string(bytes, span); trace!("parsing: directory"); if err.is_none() { trace!("-- found {}", token); - ( - Expression { - expr: Expr::Directory(token), - span, - ty: Type::String, - custom_completion: None, - }, - None, - ) + + Expression { + expr: Expr::Directory(token), + span, + ty: Type::String, + custom_completion: None, + } } else { - ( - garbage(span), - Some(ParseError::Expected("directory".into(), span)), - ) + working_set.error(ParseError::Expected("directory".into(), span)); + + garbage(span) } } -pub fn parse_filepath( - working_set: &mut StateWorkingSet, - span: Span, -) -> (Expression, Option) { +pub fn parse_filepath(working_set: &mut StateWorkingSet, span: Span) -> Expression { let bytes = working_set.get_span_contents(span); let (token, err) = unescape_unquote_string(bytes, span); trace!("parsing: filepath"); if err.is_none() { trace!("-- found {}", token); - ( - Expression { - expr: Expr::Filepath(token), - span, - ty: Type::String, - custom_completion: None, - }, - None, - ) + + Expression { + expr: Expr::Filepath(token), + span, + ty: Type::String, + custom_completion: None, + } } else { - ( - garbage(span), - Some(ParseError::Expected("filepath".into(), span)), - ) + working_set.error(ParseError::Expected("filepath".into(), span)); + + garbage(span) } } - /// Parse a datetime type, eg '2022-02-02' -pub fn parse_datetime( - working_set: &mut StateWorkingSet, - span: Span, -) -> (Expression, Option) { +pub fn parse_datetime(working_set: &mut StateWorkingSet, span: Span) -> Expression { trace!("parsing: datetime"); let bytes = working_set.get_span_contents(span); if bytes.is_empty() || !bytes[0].is_ascii_digit() { - return ( - garbage(span), - Some(ParseError::Expected("datetime".into(), span)), - ); + working_set.error(ParseError::Expected("datetime".into(), span)); + return garbage(span); } let token = String::from_utf8_lossy(bytes).to_string(); if let Ok(datetime) = chrono::DateTime::parse_from_rfc3339(&token) { - return ( - Expression { - expr: Expr::DateTime(datetime), - span, - ty: Type::Date, - custom_completion: None, - }, - None, - ); + return Expression { + expr: Expr::DateTime(datetime), + span, + ty: Type::Date, + custom_completion: None, + }; } // Just the date let just_date = token.clone() + "T00:00:00+00:00"; if let Ok(datetime) = chrono::DateTime::parse_from_rfc3339(&just_date) { - return ( - Expression { - expr: Expr::DateTime(datetime), - span, - ty: Type::Date, - custom_completion: None, - }, - None, - ); + return Expression { + expr: Expr::DateTime(datetime), + span, + ty: Type::Date, + custom_completion: None, + }; } // Date and time, assume UTC let datetime = token + "+00:00"; if let Ok(datetime) = chrono::DateTime::parse_from_rfc3339(&datetime) { - return ( - Expression { - expr: Expr::DateTime(datetime), - span, - ty: Type::Date, - custom_completion: None, - }, - None, - ); + return Expression { + expr: Expr::DateTime(datetime), + span, + ty: Type::Date, + custom_completion: None, + }; } - ( - garbage(span), - Some(ParseError::Expected("datetime".into(), span)), - ) + working_set.error(ParseError::Expected("datetime".into(), span)); + + garbage(span) } /// Parse a duration type, eg '10day' -pub fn parse_duration( - working_set: &StateWorkingSet, - span: Span, -) -> (Expression, Option) { +pub fn parse_duration(working_set: &mut StateWorkingSet, span: Span) -> Expression { trace!("parsing: duration"); let bytes = working_set.get_span_contents(span); match parse_duration_bytes(bytes, span) { - Some(expression) => (expression, None), - None => ( - garbage(span), - Some(ParseError::Expected( + Some(expression) => expression, + None => { + working_set.error(ParseError::Expected( "duration with valid units".into(), span, - )), - ), + )); + + garbage(span) + } } } @@ -2706,24 +2548,22 @@ pub fn parse_duration_bytes(num_with_unit_bytes: &[u8], span: Span) -> Option (Expression, Option) { +pub fn parse_filesize(working_set: &mut StateWorkingSet, span: Span) -> Expression { trace!("parsing: filesize"); let bytes = working_set.get_span_contents(span); //todo: parse_filesize_bytes should distinguish between not-that-type and syntax error in units match parse_filesize_bytes(bytes, span) { - Some(expression) => (expression, None), - None => ( - garbage(span), - Some(ParseError::Expected( + Some(expression) => expression, + None => { + working_set.error(ParseError::Expected( "filesize with valid units".into(), span, - )), - ), + )); + + garbage(span) + } } } @@ -2807,38 +2647,32 @@ pub fn parse_filesize_bytes(num_with_unit_bytes: &[u8], span: Span) -> Option (Expression, Option) { +pub fn parse_glob_pattern(working_set: &mut StateWorkingSet, span: Span) -> Expression { let bytes = working_set.get_span_contents(span); let (token, err) = unescape_unquote_string(bytes, span); trace!("parsing: glob pattern"); if err.is_none() { trace!("-- found {}", token); - ( - Expression { - expr: Expr::GlobPattern(token), - span, - ty: Type::String, - custom_completion: None, - }, - None, - ) + + Expression { + expr: Expr::GlobPattern(token), + span, + ty: Type::String, + custom_completion: None, + } } else { - ( - garbage(span), - Some(ParseError::Expected("glob pattern string".into(), span)), - ) + working_set.error(ParseError::Expected("glob pattern string".into(), span)); + + garbage(span) } } pub fn unescape_string(bytes: &[u8], span: Span) -> (Vec, Option) { let mut output = Vec::new(); + let mut error = None; let mut idx = 0; - let mut err = None; 'us_loop: while idx < bytes.len() { if bytes[idx] == b'\\' { @@ -2943,11 +2777,11 @@ pub fn unescape_string(bytes: &[u8], span: Span) -> (Vec, Option cur_idx += 1; } _ => { - err = Some(ParseError::InvalidLiteral( + error = error.or(Some(ParseError::InvalidLiteral( "missing '}' for unicode escape '\\u{X...}'".into(), "string".into(), Span::new(span.start + idx, span.end), - )); + ))); break 'us_loop; } } @@ -2976,20 +2810,20 @@ pub fn unescape_string(bytes: &[u8], span: Span) -> (Vec, Option } } // fall through -- escape not accepted above, must be error. - err = Some(ParseError::InvalidLiteral( + error = error.or(Some(ParseError::InvalidLiteral( "invalid unicode escape '\\u{X...}', must be 1-6 hex digits, max value 10FFFF".into(), "string".into(), Span::new(span.start + idx, span.end), - )); + ))); break 'us_loop; } _ => { - err = Some(ParseError::InvalidLiteral( + error = error.or(Some(ParseError::InvalidLiteral( "unrecognized escape after '\\'".into(), "string".into(), Span::new(span.start + idx, span.end), - )); + ))); break 'us_loop; } } @@ -2999,7 +2833,7 @@ pub fn unescape_string(bytes: &[u8], span: Span) -> (Vec, Option } } - (output, err) + (output, error) } pub fn unescape_unquote_string(bytes: &[u8], span: Span) -> (String, Option) { @@ -3035,16 +2869,14 @@ pub fn parse_string( working_set: &mut StateWorkingSet, span: Span, expand_aliases_denylist: &[usize], -) -> (Expression, Option) { +) -> Expression { trace!("parsing: string"); let bytes = working_set.get_span_contents(span); if bytes.is_empty() { - return ( - Expression::garbage(span), - Some(ParseError::Expected("String".into(), span)), - ); + working_set.error(ParseError::Expected("String".into(), span)); + return Expression::garbage(span); } // Check for bare word interpolation @@ -3053,22 +2885,19 @@ pub fn parse_string( } let (s, err) = unescape_unquote_string(bytes, span); + if let Some(err) = err { + working_set.error(err); + } - ( - Expression { - expr: Expr::String(s), - span, - ty: Type::String, - custom_completion: None, - }, - err, - ) + Expression { + expr: Expr::String(s), + span, + ty: Type::String, + custom_completion: None, + } } -pub fn parse_string_strict( - working_set: &mut StateWorkingSet, - span: Span, -) -> (Expression, Option) { +pub fn parse_string_strict(working_set: &mut StateWorkingSet, span: Span) -> Expression { trace!("parsing: string, with required delimiters"); let bytes = working_set.get_span_contents(span); @@ -3081,10 +2910,12 @@ pub fn parse_string_strict( bytes }; if bytes.starts_with(b"\"") && (bytes.len() == 1 || !bytes.ends_with(b"\"")) { - return (garbage(span), Some(ParseError::Unclosed("\"".into(), span))); + working_set.error(ParseError::Unclosed("\"".into(), span)); + return garbage(span); } if bytes.starts_with(b"\'") && (bytes.len() == 1 || !bytes.ends_with(b"\'")) { - return (garbage(span), Some(ParseError::Unclosed("\'".into(), span))); + working_set.error(ParseError::Unclosed("\'".into(), span)); + return garbage(span); } } @@ -3104,47 +2935,36 @@ pub fn parse_string_strict( trace!("-- found {}", token); if quoted { - ( - Expression { - expr: Expr::String(token), - span, - ty: Type::String, - custom_completion: None, - }, - None, - ) + Expression { + expr: Expr::String(token), + span, + ty: Type::String, + custom_completion: None, + } } else if token.contains(' ') { - ( - garbage(span), - Some(ParseError::Expected("string".into(), span)), - ) + working_set.error(ParseError::Expected("string".into(), span)); + + garbage(span) } else { - ( - Expression { - expr: Expr::String(token), - span, - ty: Type::String, - custom_completion: None, - }, - None, - ) + Expression { + expr: Expr::String(token), + span, + ty: Type::String, + custom_completion: None, + } } } else { - ( - garbage(span), - Some(ParseError::Expected("string".into(), span)), - ) + working_set.error(ParseError::Expected("string".into(), span)); + garbage(span) } } //TODO: Handle error case for unknown shapes pub fn parse_shape_name( - working_set: &StateWorkingSet, + working_set: &mut StateWorkingSet, bytes: &[u8], span: Span, -) -> (SyntaxShape, Option) { - let mut error = None; - +) -> SyntaxShape { let result = match bytes { b"any" => SyntaxShape::Any, b"binary" => SyntaxShape::Binary, @@ -3166,11 +2986,7 @@ pub fn parse_shape_name( b"int" => SyntaxShape::Int, b"import-pattern" => SyntaxShape::ImportPattern, b"keyword" => SyntaxShape::Keyword(vec![], Box::new(SyntaxShape::Any)), - _ if bytes.starts_with(b"list") => { - let (sig, err) = parse_list_shape(working_set, bytes, span); - error = error.or(err); - sig - } + _ if bytes.starts_with(b"list") => parse_list_shape(working_set, bytes, span), b"math" => SyntaxShape::MathExpression, b"nothing" => SyntaxShape::Nothing, b"number" => SyntaxShape::Number, @@ -3190,40 +3006,38 @@ pub fn parse_shape_name( let shape_span = Span::new(span.start, span.start + split[0].len()); let cmd_span = Span::new(span.start + split[0].len() + 1, span.end); - let (shape, err) = parse_shape_name(working_set, split[0], shape_span); + let shape = parse_shape_name(working_set, split[0], shape_span); let command_name = trim_quotes(split[1]); if command_name.is_empty() { - let err = ParseError::Expected("a command name".into(), cmd_span); - return (SyntaxShape::Any, Some(err)); + working_set.error(ParseError::Expected("a command name".into(), cmd_span)); + return SyntaxShape::Any; } let decl_id = working_set.find_decl(command_name, &Type::Any); if let Some(decl_id) = decl_id { - return (SyntaxShape::Custom(Box::new(shape), decl_id), err); + return SyntaxShape::Custom(Box::new(shape), decl_id); } else { - return (shape, Some(ParseError::UnknownCommand(cmd_span))); + working_set.error(ParseError::UnknownCommand(cmd_span)); + return shape; } } else { - return (SyntaxShape::Any, Some(ParseError::UnknownType(span))); + working_set.error(ParseError::UnknownType(span)); + return SyntaxShape::Any; } } }; - (result, error) + result } -fn parse_list_shape( - working_set: &StateWorkingSet, - bytes: &[u8], - span: Span, -) -> (SyntaxShape, Option) { +fn parse_list_shape(working_set: &mut StateWorkingSet, bytes: &[u8], span: Span) -> SyntaxShape { assert!(bytes.starts_with(b"list")); if bytes == b"list" { - (SyntaxShape::List(Box::new(SyntaxShape::Any)), None) + SyntaxShape::List(Box::new(SyntaxShape::Any)) } else if bytes.starts_with(b"list<") { let start = span.start + 5; @@ -3236,15 +3050,15 @@ fn parse_list_shape( let angle_start = bytes.split(|it| it == &b'>').collect::>()[0].len() + 1; let span = Span::new(span.start + angle_start, span.end); - let err = ParseError::LabeledError( + working_set.error(ParseError::LabeledError( "Extra characters in the parameter name".into(), "extra characters".into(), span, - ); - return (SyntaxShape::Any, Some(err)); + )); + return SyntaxShape::Any; } else { - let err = ParseError::Unclosed(">".into(), span); - return (SyntaxShape::List(Box::new(SyntaxShape::Any)), Some(err)); + working_set.error(ParseError::Unclosed(">".into(), span)); + return SyntaxShape::List(Box::new(SyntaxShape::Any)); }; let inner_span = Span::new(start, end); @@ -3252,21 +3066,20 @@ fn parse_list_shape( let inner_text = String::from_utf8_lossy(working_set.get_span_contents(inner_span)); // remove any extra whitespace, for example `list< string >` becomes `list` - let inner_bytes = inner_text.trim().as_bytes(); + let inner_bytes = inner_text.trim().as_bytes().to_vec(); // list<> if inner_bytes.is_empty() { - (SyntaxShape::List(Box::new(SyntaxShape::Any)), None) + SyntaxShape::List(Box::new(SyntaxShape::Any)) } else { - let (inner_sig, err) = parse_shape_name(working_set, inner_bytes, inner_span); + let inner_sig = parse_shape_name(working_set, &inner_bytes, inner_span); - (SyntaxShape::List(Box::new(inner_sig)), err) + SyntaxShape::List(Box::new(inner_sig)) } } else { - ( - SyntaxShape::List(Box::new(SyntaxShape::Any)), - Some(ParseError::UnknownType(span)), - ) + working_set.error(ParseError::UnknownType(span)); + + SyntaxShape::List(Box::new(SyntaxShape::Any)) } } @@ -3298,35 +3111,32 @@ pub fn parse_import_pattern( working_set: &mut StateWorkingSet, spans: &[Span], expand_aliases_denylist: &[usize], -) -> (Expression, Option) { - let mut error = None; - +) -> Expression { let head_span = if let Some(head_span) = spans.get(0) { head_span } else { - return ( - garbage(span(spans)), - Some(ParseError::WrongImportPattern(span(spans))), - ); + working_set.error(ParseError::WrongImportPattern(span(spans))); + return garbage(span(spans)); }; - let (head_expr, err) = parse_value( + let head_expr = parse_value( working_set, *head_span, &SyntaxShape::Any, expand_aliases_denylist, ); - error = error.or(err); let (maybe_module_id, head_name) = match eval_constant(working_set, &head_expr) { Ok(val) => match value_as_string(val, head_expr.span) { Ok(s) => (working_set.find_module(s.as_bytes()), s.into_bytes()), Err(err) => { - return (garbage(span(spans)), error.or(Some(err))); + working_set.error(err); + return garbage(span(spans)); } }, Err(err) => { - return (garbage(span(spans)), error.or(Some(err))); + working_set.error(err); + return garbage(span(spans)); } }; @@ -3347,13 +3157,12 @@ pub fn parse_import_pattern( None, ) } else if tail.starts_with(b"[") { - let (result, err) = parse_list_expression( + let result = parse_list_expression( working_set, *tail_span, &SyntaxShape::String, expand_aliases_denylist, ); - error = error.or(err); let mut output = vec![]; @@ -3426,15 +3235,16 @@ pub fn parse_import_pattern( ) }; - ( - Expression { - expr: Expr::ImportPattern(import_pattern), - span: span(&spans[1..]), - ty: Type::List(Box::new(Type::String)), - custom_completion: None, - }, - error.or(err), - ) + if let Some(err) = err { + working_set.error(err); + } + + Expression { + expr: Expr::ImportPattern(import_pattern), + span: span(&spans[1..]), + ty: Type::List(Box::new(Type::String)), + custom_completion: None, + } } pub fn parse_var_with_opt_type( @@ -3442,7 +3252,7 @@ pub fn parse_var_with_opt_type( spans: &[Span], spans_idx: &mut usize, mutable: bool, -) -> (Expression, Option) { +) -> Expression { let bytes = working_set.get_span_contents(spans[*spans_idx]).to_vec(); if bytes.contains(&b' ') @@ -3450,10 +3260,8 @@ pub fn parse_var_with_opt_type( || bytes.contains(&b'\'') || bytes.contains(&b'`') { - return ( - garbage(spans[*spans_idx]), - Some(ParseError::VariableNotValid(spans[*spans_idx])), - ); + working_set.error(ParseError::VariableNotValid(spans[*spans_idx])); + return garbage(spans[*spans_idx]); } if bytes.ends_with(b":") { @@ -3467,61 +3275,51 @@ pub fn parse_var_with_opt_type( let var_name = bytes[0..(bytes.len() - 1)].to_vec(); if !is_variable(&var_name) { - return ( - garbage(spans[*spans_idx]), - Some(ParseError::Expected( - "valid variable name".into(), - spans[*spans_idx], - )), - ); + working_set.error(ParseError::Expected( + "valid variable name".into(), + spans[*spans_idx], + )); + return garbage(spans[*spans_idx]); } let id = working_set.add_variable(var_name, spans[*spans_idx - 1], ty.clone(), mutable); - ( - Expression { - expr: Expr::VarDecl(id), - span: span(&spans[*spans_idx - 1..*spans_idx + 1]), - ty, - custom_completion: None, - }, - None, - ) + Expression { + expr: Expr::VarDecl(id), + span: span(&spans[*spans_idx - 1..*spans_idx + 1]), + ty, + custom_completion: None, + } } else { let var_name = bytes[0..(bytes.len() - 1)].to_vec(); if !is_variable(&var_name) { - return ( - garbage(spans[*spans_idx]), - Some(ParseError::Expected( - "valid variable name".into(), - spans[*spans_idx], - )), - ); + working_set.error(ParseError::Expected( + "valid variable name".into(), + spans[*spans_idx], + )); + return garbage(spans[*spans_idx]); } let id = working_set.add_variable(var_name, spans[*spans_idx], Type::Any, mutable); - ( - Expression { - expr: Expr::VarDecl(id), - span: spans[*spans_idx], - ty: Type::Any, - custom_completion: None, - }, - Some(ParseError::MissingType(spans[*spans_idx])), - ) + + working_set.error(ParseError::MissingType(spans[*spans_idx])); + Expression { + expr: Expr::VarDecl(id), + span: spans[*spans_idx], + ty: Type::Any, + custom_completion: None, + } } } else { let var_name = bytes; if !is_variable(&var_name) { - return ( - garbage(spans[*spans_idx]), - Some(ParseError::Expected( - "valid variable name".into(), - spans[*spans_idx], - )), - ); + working_set.error(ParseError::Expected( + "valid variable name".into(), + spans[*spans_idx], + )); + return garbage(spans[*spans_idx]); } let id = working_set.add_variable( @@ -3531,15 +3329,12 @@ pub fn parse_var_with_opt_type( mutable, ); - ( - Expression { - expr: Expr::VarDecl(id), - span: span(&spans[*spans_idx..*spans_idx + 1]), - ty: Type::Any, - custom_completion: None, - }, - None, - ) + Expression { + expr: Expr::VarDecl(id), + span: span(&spans[*spans_idx..*spans_idx + 1]), + ty: Type::Any, + custom_completion: None, + } } } @@ -3557,7 +3352,7 @@ pub fn expand_to_cell_path( } = expression { // Re-parse the string as if it were a cell-path - let (new_expression, _err) = + let new_expression = parse_full_cell_path(working_set, Some(var_id), *span, expand_aliases_denylist); *expression = new_expression; @@ -3568,9 +3363,9 @@ pub fn parse_row_condition( working_set: &mut StateWorkingSet, spans: &[Span], expand_aliases_denylist: &[usize], -) -> (Expression, Option) { +) -> Expression { let var_id = working_set.add_variable(b"$it".to_vec(), span(spans), Type::Any, false); - let (expression, err) = + let expression = parse_math_expression(working_set, spans, Some(var_id), expand_aliases_denylist); let span = span(spans); @@ -3599,25 +3394,21 @@ pub fn parse_row_condition( } }; - ( - Expression { - ty: Type::Bool, - span, - expr: Expr::RowCondition(block_id), - custom_completion: None, - }, - err, - ) + Expression { + ty: Type::Bool, + span, + expr: Expr::RowCondition(block_id), + custom_completion: None, + } } pub fn parse_signature( working_set: &mut StateWorkingSet, span: Span, expand_aliases_denylist: &[usize], -) -> (Expression, Option) { +) -> Expression { let bytes = working_set.get_span_contents(span); - let mut error = None; let mut start = span.start; let mut end = span.end; @@ -3629,40 +3420,34 @@ pub fn parse_signature( has_paren = true; start += 1; } else { - error = error.or_else(|| { - Some(ParseError::Expected( - "[ or (".into(), - Span::new(start, start + 1), - )) - }); + working_set.error(ParseError::Expected( + "[ or (".into(), + Span::new(start, start + 1), + )); + return garbage(span); } if (has_paren && bytes.ends_with(b")")) || (!has_paren && bytes.ends_with(b"]")) { end -= 1; } else { - error = error.or_else(|| Some(ParseError::Unclosed("] or )".into(), Span::new(end, end)))); + working_set.error(ParseError::Unclosed("] or )".into(), Span::new(end, end))); } - let (sig, err) = - parse_signature_helper(working_set, Span::new(start, end), expand_aliases_denylist); - error = error.or(err); + let sig = parse_signature_helper(working_set, Span::new(start, end), expand_aliases_denylist); - ( - Expression { - expr: Expr::Signature(sig), - span, - ty: Type::Signature, - custom_completion: None, - }, - error, - ) + Expression { + expr: Expr::Signature(sig), + span, + ty: Type::Signature, + custom_completion: None, + } } pub fn parse_signature_helper( working_set: &mut StateWorkingSet, span: Span, expand_aliases_denylist: &[usize], -) -> (Box, Option) { +) -> Box { #[allow(clippy::enum_variant_names)] enum ParseMode { ArgMode, @@ -3678,7 +3463,6 @@ pub fn parse_signature_helper( Flag(Flag), } - let mut error = None; let source = working_set.get_span_contents(span); let (output, err) = lex_signature( @@ -3688,8 +3472,9 @@ pub fn parse_signature_helper( &[b':', b'=', b','], false, ); - - error = error.or(err); + if let Some(err) = err { + working_set.error(err); + } let mut args: Vec = vec![]; let mut parse_mode = ParseMode::ArgMode; @@ -3701,7 +3486,7 @@ pub fn parse_signature_helper( span, } => { let span = *span; - let contents = working_set.get_span_contents(span); + let contents = working_set.get_span_contents(span).to_vec(); // The : symbol separates types if contents == b":" { @@ -3710,14 +3495,12 @@ pub fn parse_signature_helper( parse_mode = ParseMode::TypeMode; } ParseMode::AfterCommaArgMode => { - error = error.or_else(|| { - Some(ParseError::Expected("parameter or flag".into(), span)) - }); + working_set + .error(ParseError::Expected("parameter or flag".into(), span)); } ParseMode::TypeMode | ParseMode::DefaultValueMode => { // We're seeing two types for the same thing for some reason, error - error = - error.or_else(|| Some(ParseError::Expected("type".into(), span))); + working_set.error(ParseError::Expected("type".into(), span)); } } } @@ -3728,15 +3511,12 @@ pub fn parse_signature_helper( parse_mode = ParseMode::DefaultValueMode; } ParseMode::AfterCommaArgMode => { - error = error.or_else(|| { - Some(ParseError::Expected("parameter or flag".into(), span)) - }); + working_set + .error(ParseError::Expected("parameter or flag".into(), span)); } ParseMode::DefaultValueMode => { // We're seeing two default values for some reason, error - error = error.or_else(|| { - Some(ParseError::Expected("default value".into(), span)) - }); + working_set.error(ParseError::Expected("default value".into(), span)); } } } @@ -3745,18 +3525,14 @@ pub fn parse_signature_helper( match parse_mode { ParseMode::ArgMode => parse_mode = ParseMode::AfterCommaArgMode, ParseMode::AfterCommaArgMode => { - error = error.or_else(|| { - Some(ParseError::Expected("parameter or flag".into(), span)) - }); + working_set + .error(ParseError::Expected("parameter or flag".into(), span)); } ParseMode::TypeMode => { - error = - error.or_else(|| Some(ParseError::Expected("type".into(), span))); + working_set.error(ParseError::Expected("type".into(), span)); } ParseMode::DefaultValueMode => { - error = error.or_else(|| { - Some(ParseError::Expected("default value".into(), span)) - }); + working_set.error(ParseError::Expected("default value".into(), span)); } } } else { @@ -3779,12 +3555,10 @@ pub fn parse_signature_helper( }); if !is_variable(&variable_name) { - error = error.or_else(|| { - Some(ParseError::Expected( - "valid variable name for this long flag".into(), - span, - )) - }) + working_set.error(ParseError::Expected( + "valid variable name for this long flag".into(), + span, + )) } let var_id = @@ -3802,23 +3576,19 @@ pub fn parse_signature_helper( default_value: None, })); } else if flags.len() >= 3 { - error = error.or_else(|| { - Some(ParseError::Expected( - "only one short flag alternative".into(), - span, - )) - }); + working_set.error(ParseError::Expected( + "only one short flag alternative".into(), + span, + )); } else { let short_flag = &flags[1]; let short_flag = if !short_flag.starts_with(b"-") || !short_flag.ends_with(b")") { - error = error.or_else(|| { - Some(ParseError::Expected( - "short flag alternative for the long flag".into(), - span, - )) - }); + working_set.error(ParseError::Expected( + "short flag alternative for the long flag".into(), + span, + )); short_flag } else { // Obtain the flag's name by removing the starting - and trailing ) @@ -3840,12 +3610,10 @@ pub fn parse_signature_helper( }); if !is_variable(&variable_name) { - error = error.or_else(|| { - Some(ParseError::Expected( - "valid variable name for this short flag".into(), - span, - )) - }) + working_set.error(ParseError::Expected( + "valid variable name for this short flag".into(), + span, + )) } let var_id = working_set.add_variable( @@ -3866,9 +3634,8 @@ pub fn parse_signature_helper( default_value: None, })); } else { - error = error.or_else(|| { - Some(ParseError::Expected("short flag".into(), span)) - }); + working_set + .error(ParseError::Expected("short flag".into(), span)); } } parse_mode = ParseMode::ArgMode; @@ -3880,9 +3647,8 @@ pub fn parse_signature_helper( let chars: Vec = short_flag.chars().collect(); if chars.len() > 1 { - error = error.or_else(|| { - Some(ParseError::Expected("short flag".into(), span)) - }); + working_set + .error(ParseError::Expected("short flag".into(), span)); } let mut encoded_var_name = vec![0u8; 4]; @@ -3890,12 +3656,10 @@ pub fn parse_signature_helper( let variable_name = encoded_var_name[0..len].to_vec(); if !is_variable(&variable_name) { - error = error.or_else(|| { - Some(ParseError::Expected( - "valid variable name for this short flag".into(), - span, - )) - }) + working_set.error(ParseError::Expected( + "valid variable name for this short flag".into(), + span, + )) } let var_id = @@ -3916,16 +3680,16 @@ pub fn parse_signature_helper( // This is the same as the short flag in --b(-a) else if contents.starts_with(b"(-") { if matches!(parse_mode, ParseMode::AfterCommaArgMode) { - error = error.or_else(|| { - Some(ParseError::Expected("parameter or flag".into(), span)) - }); + working_set.error(ParseError::Expected( + "parameter or flag".into(), + span, + )); } let short_flag = &contents[2..]; let short_flag = if !short_flag.ends_with(b")") { - error = error.or_else(|| { - Some(ParseError::Expected("short flag".into(), span)) - }); + working_set + .error(ParseError::Expected("short flag".into(), span)); short_flag } else { &short_flag[..(short_flag.len() - 1)] @@ -3938,29 +3702,24 @@ pub fn parse_signature_helper( match args.last_mut() { Some(Arg::Flag(flag)) => { if flag.short.is_some() { - error = error.or_else(|| { - Some(ParseError::Expected( - "one short flag".into(), - span, - )) - }); + working_set.error(ParseError::Expected( + "one short flag".into(), + span, + )); } else { flag.short = Some(chars[0]); } } _ => { - error = error.or_else(|| { - Some(ParseError::Expected( - "unknown flag".into(), - span, - )) - }); + working_set.error(ParseError::Expected( + "unknown flag".into(), + span, + )); } } } else { - error = error.or_else(|| { - Some(ParseError::Expected("short flag".into(), span)) - }); + working_set + .error(ParseError::Expected("short flag".into(), span)); } } // Positional arg, optional @@ -3969,13 +3728,10 @@ pub fn parse_signature_helper( let name = String::from_utf8_lossy(&contents).to_string(); if !is_variable(&contents) { - error = error.or_else(|| { - Some(ParseError::Expected( - "valid variable name for this optional parameter" - .into(), - span, - )) - }) + working_set.error(ParseError::Expected( + "valid variable name for this optional parameter".into(), + span, + )) } let var_id = @@ -3999,12 +3755,10 @@ pub fn parse_signature_helper( let contents_vec: Vec = contents.to_vec(); if !is_variable(&contents_vec) { - error = error.or_else(|| { - Some(ParseError::Expected( - "valid variable name for this rest parameter".into(), - span, - )) - }) + working_set.error(ParseError::Expected( + "valid variable name for this rest parameter".into(), + span, + )) } let var_id = @@ -4021,16 +3775,14 @@ pub fn parse_signature_helper( } // Normal param else { - let name = String::from_utf8_lossy(contents).to_string(); + let name = String::from_utf8_lossy(&contents).to_string(); let contents_vec = contents.to_vec(); if !is_variable(&contents_vec) { - error = error.or_else(|| { - Some(ParseError::Expected( - "valid variable name for this parameter".into(), - span, - )) - }) + working_set.error(ParseError::Expected( + "valid variable name for this parameter".into(), + span, + )) } let var_id = @@ -4052,9 +3804,7 @@ pub fn parse_signature_helper( } ParseMode::TypeMode => { if let Some(last) = args.last_mut() { - let (syntax_shape, err) = - parse_shape_name(working_set, contents, span); - error = error.or(err); + let syntax_shape = parse_shape_name(working_set, &contents, span); //TODO check if we're replacing a custom parameter already match last { Arg::Positional(PositionalArg { shape, var_id, .. }, ..) => { @@ -4080,13 +3830,12 @@ pub fn parse_signature_helper( } ParseMode::DefaultValueMode => { if let Some(last) = args.last_mut() { - let (expression, err) = parse_value( + let expression = parse_value( working_set, span, &SyntaxShape::Any, expand_aliases_denylist, ); - error = error.or(err); //TODO check if we're replacing a custom parameter already match last { @@ -4118,9 +3867,7 @@ pub fn parse_signature_helper( expression.ty.clone(), ); } else { - error = error.or_else(|| { - Some( - ParseError::AssignmentMismatch( + working_set.error(ParseError::AssignmentMismatch( "Default value wrong type" .into(), format!( @@ -4129,29 +3876,28 @@ pub fn parse_signature_helper( expression.span, ), ) - }) } } else { - error = error.or_else(|| { - Some(ParseError::AssignmentMismatch( + working_set.error(ParseError::AssignmentMismatch( "Default value wrong type".into(), format!( "expected default value to be `{var_type}`", ), expression.span, )) - }) } } t => { if t != &expression.ty { - error = error.or_else(|| { - Some(ParseError::AssignmentMismatch( + working_set.error( + ParseError::AssignmentMismatch( "Default value wrong type".into(), - format!("expected default value to be `{t}`"), + format!( + "expected default value to be `{t}`" + ), expression.span, - )) - }) + ), + ) } } } @@ -4160,13 +3906,11 @@ pub fn parse_signature_helper( *required = false; } Arg::RestPositional(..) => { - error = error.or_else(|| { - Some(ParseError::AssignmentMismatch( - "Rest parameter was given a default value".into(), - "can't have default value".into(), - expression.span, - )) - }) + working_set.error(ParseError::AssignmentMismatch( + "Rest parameter was given a default value".into(), + "can't have default value".into(), + expression.span, + )) } Arg::Flag(Flag { arg, @@ -4192,16 +3936,16 @@ pub fn parse_signature_helper( } t => { if t != &expression_ty { - error = error.or_else(|| { - Some(ParseError::AssignmentMismatch( + working_set.error( + ParseError::AssignmentMismatch( "Default value is the wrong type" .into(), format!( "default value should be {t}" ), expression_span, - )) - }) + ), + ) } } } @@ -4257,12 +4001,10 @@ pub fn parse_signature_helper( Arg::Positional(positional, required) => { if required { if !sig.optional_positional.is_empty() { - error = error.or_else(|| { - Some(ParseError::RequiredAfterOptional( - positional.name.clone(), - span, - )) - }) + working_set.error(ParseError::RequiredAfterOptional( + positional.name.clone(), + span, + )) } sig.required_positional.push(positional) } else { @@ -4272,7 +4014,7 @@ pub fn parse_signature_helper( Arg::Flag(flag) => sig.named.push(flag), Arg::RestPositional(positional) => { if positional.name.is_empty() { - error = error.or(Some(ParseError::RestNeedsName(span))) + working_set.error(ParseError::RestNeedsName(span)) } else if sig.rest_positional.is_none() { sig.rest_positional = Some(PositionalArg { name: positional.name, @@ -4280,13 +4022,13 @@ pub fn parse_signature_helper( }) } else { // Too many rest params - error = error.or(Some(ParseError::MultipleRestParams(span))) + working_set.error(ParseError::MultipleRestParams(span)) } } } } - (Box::new(sig), error) + Box::new(sig) } pub fn parse_list_expression( @@ -4294,11 +4036,9 @@ pub fn parse_list_expression( span: Span, element_shape: &SyntaxShape, expand_aliases_denylist: &[usize], -) -> (Expression, Option) { +) -> Expression { let bytes = working_set.get_span_contents(span); - let mut error = None; - let mut start = span.start; let mut end = span.end; @@ -4308,17 +4048,21 @@ pub fn parse_list_expression( if bytes.ends_with(b"]") { end -= 1; } else { - error = error.or_else(|| Some(ParseError::Unclosed("]".into(), Span::new(end, end)))); + working_set.error(ParseError::Unclosed("]".into(), Span::new(end, end))); } let inner_span = Span::new(start, end); let source = working_set.get_span_contents(inner_span); let (output, err) = lex(source, inner_span.start, &[b'\n', b'\r', b','], &[], true); - error = error.or(err); + if let Some(err) = err { + working_set.error(err) + } let (output, err) = lite_parse(&output); - error = error.or(err); + if let Some(err) = err { + working_set.error(err) + } let mut args = vec![]; @@ -4330,14 +4074,13 @@ pub fn parse_list_expression( if let LiteElement::Command(_, command) = arg { while spans_idx < command.parts.len() { - let (arg, err) = parse_multispan_value( + let arg = parse_multispan_value( working_set, &command.parts, &mut spans_idx, element_shape, expand_aliases_denylist, ); - error = error.or(err); if let Some(ref ctype) = contained_type { if *ctype != arg.ty { @@ -4355,28 +4098,24 @@ pub fn parse_list_expression( } } - ( - Expression { - expr: Expr::List(args), - span, - ty: Type::List(Box::new(if let Some(ty) = contained_type { - ty - } else { - Type::Any - })), - custom_completion: None, - }, - error, - ) + Expression { + expr: Expr::List(args), + span, + ty: Type::List(Box::new(if let Some(ty) = contained_type { + ty + } else { + Type::Any + })), + custom_completion: None, + } } pub fn parse_table_expression( working_set: &mut StateWorkingSet, original_span: Span, expand_aliases_denylist: &[usize], -) -> (Expression, Option) { +) -> Expression { let bytes = working_set.get_span_contents(original_span); - let mut error = None; let mut start = original_span.start; let mut end = original_span.end; @@ -4387,7 +4126,7 @@ pub fn parse_table_expression( if bytes.ends_with(b"]") { end -= 1; } else { - error = error.or_else(|| Some(ParseError::Unclosed("]".into(), Span::new(end, end)))); + working_set.error(ParseError::Unclosed("]".into(), Span::new(end, end))); } let inner_span = Span::new(start, end); @@ -4395,21 +4134,22 @@ pub fn parse_table_expression( let source = working_set.get_span_contents(inner_span); let (output, err) = lex(source, start, &[b'\n', b'\r', b','], &[], true); - error = error.or(err); + if let Some(err) = err { + working_set.error(err); + } let (output, err) = lite_parse(&output); - error = error.or(err); + if let Some(err) = err { + working_set.error(err); + } match output.block.len() { - 0 => ( - Expression { - expr: Expr::List(vec![]), - span: original_span, - ty: Type::List(Box::new(Type::Any)), - custom_completion: None, - }, - None, - ), + 0 => Expression { + expr: Expr::List(vec![]), + span: original_span, + ty: Type::List(Box::new(Type::Any)), + custom_completion: None, + }, 1 => { // List parse_list_expression( @@ -4428,13 +4168,12 @@ pub fn parse_table_expression( } => { let mut table_headers = vec![]; - let (headers, err) = parse_value( + let headers = parse_value( working_set, command.parts[0], &SyntaxShape::List(Box::new(SyntaxShape::Any)), expand_aliases_denylist, ); - error = error.or(err); if let Expression { expr: Expr::List(headers), @@ -4452,13 +4191,12 @@ pub fn parse_table_expression( } => { let mut rows = vec![]; for part in &command.parts { - let (values, err) = parse_value( + let values = parse_value( working_set, *part, &SyntaxShape::List(Box::new(SyntaxShape::Any)), expand_aliases_denylist, ); - error = error.or(err); if let Expression { expr: Expr::List(values), span, @@ -4466,20 +4204,15 @@ pub fn parse_table_expression( } = values { match values.len().cmp(&table_headers.len()) { - std::cmp::Ordering::Less => { - error = error.or(Some(ParseError::MissingColumns( - table_headers.len(), - span, - ))) - } + std::cmp::Ordering::Less => working_set.error( + ParseError::MissingColumns(table_headers.len(), span), + ), std::cmp::Ordering::Equal => {} std::cmp::Ordering::Greater => { - error = error.or_else(|| { - Some(ParseError::ExtraColumns( - table_headers.len(), - values[table_headers.len()].span, - )) - }) + working_set.error(ParseError::ExtraColumns( + table_headers.len(), + values[table_headers.len()].span, + )) } } @@ -4487,15 +4220,12 @@ pub fn parse_table_expression( } } - ( - Expression { - expr: Expr::Table(table_headers, rows), - span: original_span, - ty: Type::Table(vec![]), //FIXME - custom_completion: None, - }, - error, - ) + Expression { + expr: Expr::Table(table_headers, rows), + span: original_span, + ty: Type::Table(vec![]), //FIXME + custom_completion: None, + } } } } @@ -4508,11 +4238,10 @@ pub fn parse_block_expression( working_set: &mut StateWorkingSet, span: Span, expand_aliases_denylist: &[usize], -) -> (Expression, Option) { +) -> Expression { trace!("parsing: block expression"); let bytes = working_set.get_span_contents(span); - let mut error = None; let mut start = span.start; let mut end = span.end; @@ -4520,15 +4249,13 @@ pub fn parse_block_expression( if bytes.starts_with(b"{") { start += 1; } else { - return ( - garbage(span), - Some(ParseError::Expected("block".into(), span)), - ); + working_set.error(ParseError::Expected("block".into(), span)); + return garbage(span); } if bytes.ends_with(b"}") { end -= 1; } else { - error = error.or_else(|| Some(ParseError::Unclosed("}".into(), Span::new(end, end)))); + working_set.error(ParseError::Unclosed("}".into(), Span::new(end, end))); } let inner_span = Span::new(start, end); @@ -4536,7 +4263,9 @@ pub fn parse_block_expression( let source = working_set.get_span_contents(inner_span); let (output, err) = lex(source, start, &[], &[], false); - error = error.or(err); + if let Some(err) = err { + working_set.error(err); + } working_set.enter_scope(); @@ -4546,25 +4275,22 @@ pub fn parse_block_expression( contents: TokenContents::Pipe, span, }) => { - error = error.or_else(|| { - Some(ParseError::Expected( - "block but found closure".into(), - *span, - )) - }); + working_set.error(ParseError::Expected( + "block but found closure".into(), + *span, + )); (None, 0) } _ => (None, 0), }; - let (mut output, err) = parse_block( + let mut output = parse_block( working_set, &output[amt_to_skip..], false, expand_aliases_denylist, false, ); - error = error.or(err); if let Some(signature) = signature { output.signature = signature.0; @@ -4590,24 +4316,20 @@ pub fn parse_block_expression( let block_id = working_set.add_block(output); - ( - Expression { - expr: Expr::Block(block_id), - span, - ty: Type::Block, - custom_completion: None, - }, - error, - ) + Expression { + expr: Expr::Block(block_id), + span, + ty: Type::Block, + custom_completion: None, + } } pub fn parse_match_block_expression( working_set: &mut StateWorkingSet, span: Span, expand_aliases_denylist: &[usize], -) -> (Expression, Option) { +) -> Expression { let bytes = working_set.get_span_contents(span); - let mut error = None; let mut start = span.start; let mut end = span.end; @@ -4615,15 +4337,13 @@ pub fn parse_match_block_expression( if bytes.starts_with(b"{") { start += 1; } else { - return ( - garbage(span), - Some(ParseError::Expected("closure".into(), span)), - ); + working_set.error(ParseError::Expected("closure".into(), span)); + return garbage(span); } if bytes.ends_with(b"}") { end -= 1; } else { - error = error.or_else(|| Some(ParseError::Unclosed("}".into(), Span::new(end, end)))); + working_set.error(ParseError::Unclosed("}".into(), Span::new(end, end))); } let inner_span = Span::new(start, end); @@ -4631,7 +4351,9 @@ pub fn parse_match_block_expression( let source = working_set.get_span_contents(inner_span); let (output, err) = lex(source, start, &[b' ', b'\r', b'\n', b',', b'|'], &[], false); - error = error.or(err); + if let Some(err) = err { + working_set.error(err); + } let mut position = 0; @@ -4643,17 +4365,16 @@ pub fn parse_match_block_expression( working_set.enter_scope(); // First parse the pattern - let (mut pattern, err) = parse_pattern(working_set, output[position].span); - error = error.or(err); + let mut pattern = parse_pattern(working_set, output[position].span); position += 1; if position >= output.len() { - error = error.or(Some(ParseError::Mismatch( + working_set.error(ParseError::Mismatch( "=>".into(), "end of input".into(), Span::new(output[position - 1].span.end, output[position - 1].span.end), - ))); + )); working_set.exit_scope(); break; @@ -4670,27 +4391,26 @@ pub fn parse_match_block_expression( position += 1; if position >= output.len() { - error = error.or(Some(ParseError::Mismatch( + working_set.error(ParseError::Mismatch( "pattern".into(), "end of input".into(), Span::new(output[position - 1].span.end, output[position - 1].span.end), - ))); + )); working_set.exit_scope(); break; } - let (pattern, err) = parse_pattern(working_set, output[position].span); - error = error.or(err); + let pattern = parse_pattern(working_set, output[position].span); or_pattern.push(pattern); position += 1; if position >= output.len() { - error = error.or(Some(ParseError::Mismatch( + working_set.error(ParseError::Mismatch( "=>".into(), "end of input".into(), Span::new(output[position - 1].span.end, output[position - 1].span.end), - ))); + )); working_set.exit_scope(); break; @@ -4718,50 +4438,46 @@ pub fn parse_match_block_expression( // Then the `=>` arrow if connector != b"=>" { - error = error.or(Some(ParseError::Mismatch( + working_set.error(ParseError::Mismatch( "=>".into(), "end of input".into(), Span::new(output[position - 1].span.end, output[position - 1].span.end), - ))); + )); } else { position += 1; } // Finally, the value/expression/block that we will run to produce the result if position >= output.len() { - error = error.or(Some(ParseError::Mismatch( + working_set.error(ParseError::Mismatch( "match result".into(), "end of input".into(), Span::new(output[position - 1].span.end, output[position - 1].span.end), - ))); + )); working_set.exit_scope(); break; } - let (result, err) = parse_multispan_value( + let result = parse_multispan_value( working_set, &[output[position].span], &mut 0, &SyntaxShape::OneOf(vec![SyntaxShape::Block, SyntaxShape::Expression]), expand_aliases_denylist, ); - error = error.or(err); position += 1; working_set.exit_scope(); output_matches.push((pattern, result)); } - ( - Expression { - expr: Expr::MatchBlock(output_matches), - span, - ty: Type::Any, - custom_completion: None, - }, - error, - ) + Expression { + expr: Expr::MatchBlock(output_matches), + span, + ty: Type::Any, + custom_completion: None, + } } pub fn parse_closure_expression( @@ -4770,11 +4486,10 @@ pub fn parse_closure_expression( span: Span, expand_aliases_denylist: &[usize], require_pipe: bool, -) -> (Expression, Option) { +) -> Expression { trace!("parsing: closure expression"); let bytes = working_set.get_span_contents(span); - let mut error = None; let mut start = span.start; let mut end = span.end; @@ -4782,15 +4497,13 @@ pub fn parse_closure_expression( if bytes.starts_with(b"{") { start += 1; } else { - return ( - garbage(span), - Some(ParseError::Expected("closure".into(), span)), - ); + working_set.error(ParseError::Expected("closure".into(), span)); + return garbage(span); } if bytes.ends_with(b"}") { end -= 1; } else { - error = error.or_else(|| Some(ParseError::Unclosed("}".into(), Span::new(end, end)))); + working_set.error(ParseError::Unclosed("}".into(), Span::new(end, end))); } let inner_span = Span::new(start, end); @@ -4798,7 +4511,9 @@ pub fn parse_closure_expression( let source = working_set.get_span_contents(inner_span); let (output, err) = lex(source, start, &[], &[], false); - error = error.or(err); + if let Some(err) = err { + working_set.error(err); + } working_set.enter_scope(); @@ -4833,9 +4548,8 @@ pub fn parse_closure_expression( }; let signature_span = Span::new(start_point, end_point); - let (signature, err) = + let signature = parse_signature_helper(working_set, signature_span, expand_aliases_denylist); - error = error.or(err); (Some((signature, signature_span)), amt_to_skip) } @@ -4848,9 +4562,9 @@ pub fn parse_closure_expression( ), _ => { if require_pipe { - error = error.or(Some(ParseError::ClosureMissingPipe(span))); + working_set.error(ParseError::ClosureMissingPipe(span)); working_set.exit_scope(); - return (garbage(span), error); + return garbage(span); } else { (None, 0) } @@ -4861,43 +4575,38 @@ pub fn parse_closure_expression( if let SyntaxShape::Closure(Some(v)) = shape { if let Some((sig, sig_span)) = &signature { if sig.num_positionals() > v.len() { - error = error.or_else(|| { - Some(ParseError::Expected( - format!( - "{} closure parameter{}", - v.len(), - if v.len() > 1 { "s" } else { "" } - ), - *sig_span, - )) - }); + working_set.error(ParseError::Expected( + format!( + "{} closure parameter{}", + v.len(), + if v.len() > 1 { "s" } else { "" } + ), + *sig_span, + )); } for (expected, PositionalArg { name, shape, .. }) in v.iter().zip(sig.required_positional.iter()) { if expected != shape && *shape != SyntaxShape::Any { - error = error.or_else(|| { - Some(ParseError::ParameterMismatchType( - name.to_owned(), - expected.to_string(), - shape.to_string(), - *sig_span, - )) - }); + working_set.error(ParseError::ParameterMismatchType( + name.to_owned(), + expected.to_string(), + shape.to_string(), + *sig_span, + )); } } } } - let (mut output, err) = parse_block( + let mut output = parse_block( working_set, &output[amt_to_skip..], false, expand_aliases_denylist, false, ); - error = error.or(err); if let Some(signature) = signature { output.signature = signature.0; @@ -4923,15 +4632,12 @@ pub fn parse_closure_expression( let block_id = working_set.add_block(output); - ( - Expression { - expr: Expr::Closure(block_id), - span, - ty: Type::Closure, - custom_completion: None, - }, - error, - ) + Expression { + expr: Expr::Closure(block_id), + span, + ty: Type::Closure, + custom_completion: None, + } } pub fn parse_value( @@ -4939,61 +4645,49 @@ pub fn parse_value( span: Span, shape: &SyntaxShape, expand_aliases_denylist: &[usize], -) -> (Expression, Option) { +) -> Expression { let bytes = working_set.get_span_contents(span); if bytes.is_empty() { - return (garbage(span), Some(ParseError::IncompleteParser(span))); + working_set.error(ParseError::IncompleteParser(span)); + return garbage(span); } // Check for reserved keyword values match bytes { b"true" => { if matches!(shape, SyntaxShape::Boolean) || matches!(shape, SyntaxShape::Any) { - return ( - Expression { - expr: Expr::Bool(true), - span, - ty: Type::Bool, - custom_completion: None, - }, - None, - ); + return Expression { + expr: Expr::Bool(true), + span, + ty: Type::Bool, + custom_completion: None, + }; } else { - return ( - Expression::garbage(span), - Some(ParseError::Expected("non-boolean value".into(), span)), - ); + working_set.error(ParseError::Expected("non-boolean value".into(), span)); + return Expression::garbage(span); } } b"false" => { if matches!(shape, SyntaxShape::Boolean) || matches!(shape, SyntaxShape::Any) { - return ( - Expression { - expr: Expr::Bool(false), - span, - ty: Type::Bool, - custom_completion: None, - }, - None, - ); + return Expression { + expr: Expr::Bool(false), + span, + ty: Type::Bool, + custom_completion: None, + }; } else { - return ( - Expression::garbage(span), - Some(ParseError::Expected("non-boolean value".into(), span)), - ); + working_set.error(ParseError::Expected("non-boolean value".into(), span)); + return Expression::garbage(span); } } b"null" => { - return ( - Expression { - expr: Expr::Nothing, - span, - ty: Type::Nothing, - custom_completion: None, - }, - None, - ); + return Expression { + expr: Expr::Nothing, + span, + ty: Type::Nothing, + custom_completion: None, + }; } b"-inf" | b"inf" | b"NaN" => { return parse_numberlike_expr(working_set, span, shape, expand_aliases_denylist); @@ -5015,10 +4709,8 @@ pub fn parse_value( | SyntaxShape::Table | SyntaxShape::Signature => {} _ => { - return ( - Expression::garbage(span), - Some(ParseError::Expected("non-[] value".into(), span)), - ); + working_set.error(ParseError::Expected("non-[] value".into(), span)); + return Expression::garbage(span); } }, x if x.is_ascii_digit() => { @@ -5040,10 +4732,9 @@ pub fn parse_value( match shape { SyntaxShape::Custom(shape, custom_completion) => { - let (mut expression, err) = - parse_value(working_set, span, shape, expand_aliases_denylist); + let mut expression = parse_value(working_set, span, shape, expand_aliases_denylist); expression.custom_completion = Some(*custom_completion); - (expression, err) + expression } SyntaxShape::Range => parse_range(working_set, span, expand_aliases_denylist), SyntaxShape::Filepath => parse_filepath(working_set, span), @@ -5056,62 +4747,56 @@ pub fn parse_value( if bytes.starts_with(b"[") { parse_signature(working_set, span, expand_aliases_denylist) } else { - ( - Expression::garbage(span), - Some(ParseError::Expected("signature".into(), span)), - ) + working_set.error(ParseError::Expected("signature".into(), span)); + + Expression::garbage(span) } } SyntaxShape::List(elem) => { if bytes.starts_with(b"[") { parse_list_expression(working_set, span, elem, expand_aliases_denylist) } else { - ( - Expression::garbage(span), - Some(ParseError::Expected("list".into(), span)), - ) + working_set.error(ParseError::Expected("list".into(), span)); + + Expression::garbage(span) } } SyntaxShape::Table => { if bytes.starts_with(b"[") { parse_table_expression(working_set, span, expand_aliases_denylist) } else { - ( - Expression::garbage(span), - Some(ParseError::Expected("table".into(), span)), - ) + working_set.error(ParseError::Expected("table".into(), span)); + + Expression::garbage(span) } } SyntaxShape::CellPath => parse_simple_cell_path(working_set, span, expand_aliases_denylist), SyntaxShape::Boolean => { // Redundant, though we catch bad boolean parses here if bytes == b"true" || bytes == b"false" { - ( - Expression { - expr: Expr::Bool(true), - span, - ty: Type::Bool, - custom_completion: None, - }, - None, - ) + Expression { + expr: Expr::Bool(true), + span, + ty: Type::Bool, + custom_completion: None, + } } else { - ( - garbage(span), - Some(ParseError::Expected("bool".into(), span)), - ) + working_set.error(ParseError::Expected("bool".into(), span)); + + Expression::garbage(span) } } // Be sure to return ParseError::Expected(..) if invoked for one of these shapes, but lex // stream doesn't start with '{'} -- parsing in SyntaxShape::Any arm depends on this error variant. - SyntaxShape::Block | SyntaxShape::Closure(..) | SyntaxShape::Record => ( - garbage(span), - Some(ParseError::Expected( + SyntaxShape::Block | SyntaxShape::Closure(..) | SyntaxShape::Record => { + working_set.error(ParseError::Expected( "block, closure or record".into(), span, - )), - ), + )); + + Expression::garbage(span) + } SyntaxShape::Any => { if bytes.starts_with(b"[") { @@ -5126,38 +4811,37 @@ pub fn parse_value( SyntaxShape::String, ]; for shape in shapes.iter() { - let (s, e) = parse_value(working_set, span, shape, expand_aliases_denylist); - match (s, e) { - (s, None) => { - return (s, None); - } - (_, Some(ParseError::Expected(_, _))) => { - // value didn't parse as this shape, try other options - continue; - } - (s, e) => { - // value did parse, but had syntax issues, don't try any more options. - return (s, e); + let starting_error_count = working_set.parse_errors.len(); + + let s = parse_value(working_set, span, shape, expand_aliases_denylist); + + if starting_error_count == working_set.parse_errors.len() { + return s; + } else { + match working_set.parse_errors.first() { + Some(ParseError::Expected(_, _)) => { + working_set.parse_errors.truncate(starting_error_count); + continue; + } + _ => { + return s; + } } } } - ( - garbage(span), - Some(ParseError::Expected("any shape".into(), span)), - ) + working_set.error(ParseError::Expected("any shape".into(), span)); + + garbage(span) } } - x => ( - garbage(span), - Some(ParseError::Expected(x.to_type().to_string(), span)), - ), + x => { + working_set.error(ParseError::Expected(x.to_type().to_string(), span)); + garbage(span) + } } } -pub fn parse_operator( - working_set: &mut StateWorkingSet, - span: Span, -) -> (Expression, Option) { +pub fn parse_operator(working_set: &mut StateWorkingSet, span: Span) -> Expression { let contents = working_set.get_span_contents(span); let operator = match contents { @@ -5197,124 +4881,103 @@ pub fn parse_operator( b"**" => Operator::Math(Math::Pow), // WARNING: not actual operators below! Error handling only pow @ (b"^" | b"pow") => { - return ( - garbage(span), - Some(ParseError::UnknownOperator( - match pow { - b"^" => "^", - b"pow" => "pow", - _ => unreachable!(), - }, - "Use '**' for exponentiation or 'bit-xor' for bitwise XOR.", - span, - )), - ); + working_set.error(ParseError::UnknownOperator( + match pow { + b"^" => "^", + b"pow" => "pow", + _ => unreachable!(), + }, + "Use '**' for exponentiation or 'bit-xor' for bitwise XOR.", + span, + )); + return garbage(span); } equality @ (b"is" | b"===") => { - return ( - garbage(span), - Some(ParseError::UnknownOperator( - match equality { - b"is" => "is", - b"===" => "===", - _ => unreachable!(), - }, - "Did you mean '=='?", - span, - )), - ); + working_set.error(ParseError::UnknownOperator( + match equality { + b"is" => "is", + b"===" => "===", + _ => unreachable!(), + }, + "Did you mean '=='?", + span, + )); + return garbage(span); } b"contains" => { - return ( - garbage(span), - Some(ParseError::UnknownOperator( - "contains", - "Did you mean '$string =~ $pattern' or '$element in $container'?", - span, - )), - ); + working_set.error(ParseError::UnknownOperator( + "contains", + "Did you mean '$string =~ $pattern' or '$element in $container'?", + span, + )); + return garbage(span); } b"%" => { - return ( - garbage(span), - Some(ParseError::UnknownOperator( - "%", - "Did you mean 'mod'?", - span, - )), - ); + working_set.error(ParseError::UnknownOperator( + "%", + "Did you mean 'mod'?", + span, + )); + return garbage(span); } b"&" => { - return ( - garbage(span), - Some(ParseError::UnknownOperator( - "&", - "Did you mean 'bit-and'?", - span, - )), - ); + working_set.error(ParseError::UnknownOperator( + "&", + "Did you mean 'bit-and'?", + span, + )); + return garbage(span); } b"<<" => { - return ( - garbage(span), - Some(ParseError::UnknownOperator( - "<<", - "Did you mean 'bit-shl'?", - span, - )), - ); + working_set.error(ParseError::UnknownOperator( + "<<", + "Did you mean 'bit-shl'?", + span, + )); + return garbage(span); } b">>" => { - return ( - garbage(span), - Some(ParseError::UnknownOperator( - ">>", - "Did you mean 'bit-shr'?", - span, - )), - ); + working_set.error(ParseError::UnknownOperator( + ">>", + "Did you mean 'bit-shr'?", + span, + )); + return garbage(span); } bits @ (b"bits-and" | b"bits-xor" | b"bits-or" | b"bits-shl" | b"bits-shr") => { - return ( - garbage(span), - Some(ParseError::UnknownOperator( - match bits { - b"bits-and" => "bits-and", - b"bits-xor" => "bits-xor", - b"bits-or" => "bits-or", - b"bits-shl" => "bits-shl", - b"bits-shr" => "bits-shr", - _ => unreachable!(), - }, - match bits { - b"bits-and" => "Did you mean 'bit-and'?", - b"bits-xor" => "Did you mean 'bit-xor'?", - b"bits-or" => "Did you mean 'bit-or'?", - b"bits-shl" => "Did you mean 'bit-shl'?", - b"bits-shr" => "Did you mean 'bit-shr'?", - _ => unreachable!(), - }, - span, - )), - ); + working_set.error(ParseError::UnknownOperator( + match bits { + b"bits-and" => "bits-and", + b"bits-xor" => "bits-xor", + b"bits-or" => "bits-or", + b"bits-shl" => "bits-shl", + b"bits-shr" => "bits-shr", + _ => unreachable!(), + }, + match bits { + b"bits-and" => "Did you mean 'bit-and'?", + b"bits-xor" => "Did you mean 'bit-xor'?", + b"bits-or" => "Did you mean 'bit-or'?", + b"bits-shl" => "Did you mean 'bit-shl'?", + b"bits-shr" => "Did you mean 'bit-shr'?", + _ => unreachable!(), + }, + span, + )); + return garbage(span); } _ => { - return ( - garbage(span), - Some(ParseError::Expected("operator".into(), span)), - ); + working_set.error(ParseError::Expected("operator".into(), span)); + return garbage(span); } }; - ( - Expression { - expr: Expr::Operator(operator), - span, - ty: Type::Any, - custom_completion: None, - }, - None, - ) + Expression { + expr: Expr::Operator(operator), + span, + ty: Type::Any, + custom_completion: None, + } } pub fn parse_math_expression( @@ -5322,7 +4985,7 @@ pub fn parse_math_expression( spans: &[Span], lhs_row_var_id: Option, expand_aliases_denylist: &[usize], -) -> (Expression, Option) { +) -> Expression { trace!("parsing: math expression"); // As the expr_stack grows, we increase the required precedence to grow larger @@ -5340,8 +5003,6 @@ pub fn parse_math_expression( let mut idx = 0; let mut last_prec = 1000000; - let mut error = None; - let first_span = working_set.get_span_contents(spans[0]); if first_span == b"if" || first_span == b"match" { @@ -5349,49 +5010,41 @@ pub fn parse_math_expression( if spans.len() > 1 { return parse_call(working_set, spans, spans[0], expand_aliases_denylist, false); } else { - return ( - garbage(spans[0]), - Some(ParseError::Expected( - "expression".into(), - Span::new(spans[0].end, spans[0].end), - )), - ); + working_set.error(ParseError::Expected( + "expression".into(), + Span::new(spans[0].end, spans[0].end), + )); + return garbage(spans[0]); } } else if first_span == b"not" { if spans.len() > 1 { - let (remainder, err) = parse_math_expression( + let remainder = parse_math_expression( working_set, &spans[1..], lhs_row_var_id, expand_aliases_denylist, ); - return ( - Expression { - expr: Expr::UnaryNot(Box::new(remainder)), - span: span(spans), - ty: Type::Bool, - custom_completion: None, - }, - err, - ); + return Expression { + expr: Expr::UnaryNot(Box::new(remainder)), + span: span(spans), + ty: Type::Bool, + custom_completion: None, + }; } else { - return ( - garbage(spans[0]), - Some(ParseError::Expected( - "expression".into(), - Span::new(spans[0].end, spans[0].end), - )), - ); + working_set.error(ParseError::Expected( + "expression".into(), + Span::new(spans[0].end, spans[0].end), + )); + return garbage(spans[0]); } } - let (mut lhs, err) = parse_value( + let mut lhs = parse_value( working_set, spans[0], &SyntaxShape::Any, expand_aliases_denylist, ); - error = error.or(err); idx += 1; if idx >= spans.len() { @@ -5404,8 +5057,7 @@ pub fn parse_math_expression( expr_stack.push(lhs); while idx < spans.len() { - let (op, err) = parse_operator(working_set, spans[idx]); - error = error.or(err); + let op = parse_operator(working_set, spans[idx]); let op_prec = op.precedence(); @@ -5413,7 +5065,7 @@ pub fn parse_math_expression( if idx == spans.len() { // Handle broken math expr `1 +` etc - error = error.or(Some(ParseError::IncompleteMathExpression(spans[idx - 1]))); + working_set.error(ParseError::IncompleteMathExpression(spans[idx - 1])); expr_stack.push(Expression::garbage(spans[idx - 1])); expr_stack.push(Expression::garbage(spans[idx - 1])); @@ -5421,13 +5073,12 @@ pub fn parse_math_expression( break; } - let (rhs, err) = parse_value( + let rhs = parse_value( working_set, spans[idx], &SyntaxShape::Any, expand_aliases_denylist, ); - error = error.or(err); while op_prec <= last_prec && expr_stack.len() > 1 { // Collapse the right associated operations first @@ -5456,7 +5107,9 @@ pub fn parse_math_expression( } let (result_ty, err) = math_result_type(working_set, &mut lhs, &mut op, &mut rhs); - error = error.or(err); + if let Some(err) = err { + working_set.error(err); + } let op_span = span(&[lhs.span, rhs.span]); expr_stack.push(Expression { @@ -5490,7 +5143,9 @@ pub fn parse_math_expression( } let (result_ty, err) = math_result_type(working_set, &mut lhs, &mut op, &mut rhs); - error = error.or(err); + if let Some(err) = err { + working_set.error(err) + } let binary_op_span = span(&[lhs.span, rhs.span]); expr_stack.push(Expression { @@ -5501,11 +5156,9 @@ pub fn parse_math_expression( }); } - let output = expr_stack + expr_stack .pop() - .expect("internal error: expression stack empty"); - - (output, error) + .expect("internal error: expression stack empty") } pub fn parse_expression( @@ -5513,7 +5166,7 @@ pub fn parse_expression( spans: &[Span], expand_aliases_denylist: &[usize], is_subexpression: bool, -) -> (Expression, Option) { +) -> Expression { trace!("parsing: expression"); let mut pos = 0; @@ -5528,6 +5181,8 @@ pub fn parse_expression( if !name.starts_with(b"^") && split.len() == 2 && !split[0].is_empty() { let point = split[0].len() + 1; + let starting_error_count = working_set.parse_errors.len(); + let lhs = parse_string_strict( working_set, Span::new(spans[pos].start, spans[pos].start + point - 1), @@ -5541,21 +5196,19 @@ pub fn parse_expression( parse_string_strict(working_set, rhs_span) } } else { - ( - Expression { - expr: Expr::String(String::new()), - span: Span::unknown(), - ty: Type::Nothing, - custom_completion: None, - }, - None, - ) + Expression { + expr: Expr::String(String::new()), + span: Span::unknown(), + ty: Type::Nothing, + custom_completion: None, + } }; - if lhs.1.is_none() && rhs.1.is_none() { - shorthand.push((lhs.0, rhs.0)); + if starting_error_count == working_set.parse_errors.len() { + shorthand.push((lhs, rhs)); pos += 1; } else { + working_set.parse_errors.truncate(starting_error_count); break; } } else { @@ -5564,14 +5217,11 @@ pub fn parse_expression( } if pos == spans.len() { - return ( - garbage(span(spans)), - Some(ParseError::UnknownCommand(spans[0])), - ); + working_set.error(ParseError::UnknownCommand(spans[0])); + return garbage(span(spans)); } - let (output, err) = if is_math_expression_like(working_set, spans[pos], expand_aliases_denylist) - { + let output = if is_math_expression_like(working_set, spans[pos], expand_aliases_denylist) { parse_math_expression(working_set, &spans[pos..], None, expand_aliases_denylist) } else { let bytes = working_set.get_span_contents(spans[pos]).to_vec(); @@ -5579,22 +5229,13 @@ pub fn parse_expression( // For now, check for special parses of certain keywords match bytes.as_slice() { b"def" | b"extern" | b"for" | b"module" | b"use" | b"source" | b"old-alias" - | b"alias" | b"export" | b"hide" => ( - parse_call( - working_set, - &spans[pos..], - spans[0], - expand_aliases_denylist, - is_subexpression, - ) - .0, - Some(ParseError::BuiltinCommandInPipeline( + | b"alias" | b"export" | b"hide" => { + working_set.error(ParseError::BuiltinCommandInPipeline( String::from_utf8(bytes) .expect("builtin commands bytes should be able to convert to string"), spans[0], - )), - ), - b"let" | b"const" | b"mut" => ( + )); + parse_call( working_set, &spans[pos..], @@ -5602,8 +5243,9 @@ pub fn parse_expression( expand_aliases_denylist, is_subexpression, ) - .0, - Some(ParseError::AssignInPipeline( + } + b"let" | b"const" | b"mut" => { + working_set.error(ParseError::AssignInPipeline( String::from_utf8(bytes) .expect("builtin commands bytes should be able to convert to string"), String::from_utf8_lossy(match spans.len() { @@ -5617,8 +5259,15 @@ pub fn parse_expression( }) .to_string(), spans[0], - )), - ), + )); + parse_call( + working_set, + &spans[pos..], + spans[0], + expand_aliases_denylist, + is_subexpression, + ) + } b"overlay" => { if spans.len() > 1 && working_set.get_span_contents(spans[1]) == b"list" { // whitelist 'overlay list' @@ -5630,25 +5279,28 @@ pub fn parse_expression( is_subexpression, ) } else { - ( - parse_call( - working_set, - &spans[pos..], - spans[0], - expand_aliases_denylist, - is_subexpression, - ) - .0, - Some(ParseError::BuiltinCommandInPipeline( - "overlay".into(), - spans[0], - )), + working_set.error(ParseError::BuiltinCommandInPipeline( + "overlay".into(), + spans[0], + )); + + parse_call( + working_set, + &spans[pos..], + spans[0], + expand_aliases_denylist, + is_subexpression, ) } } b"where" => parse_where_expr(working_set, &spans[pos..], expand_aliases_denylist), #[cfg(feature = "plugin")] - b"register" => ( + b"register" => { + working_set.error(ParseError::BuiltinCommandInPipeline( + "plugin".into(), + spans[0], + )); + parse_call( working_set, &spans[pos..], @@ -5656,12 +5308,7 @@ pub fn parse_expression( expand_aliases_denylist, is_subexpression, ) - .0, - Some(ParseError::BuiltinCommandInPipeline( - "plugin".into(), - spans[0], - )), - ), + } _ => parse_call( working_set, @@ -5713,27 +5360,21 @@ pub fn parse_expression( parser_info: HashMap::new(), })); - ( - Expression { - expr, - custom_completion: None, - span: span(spans), - ty, - }, - err, - ) + Expression { + expr, + custom_completion: None, + span: span(spans), + ty, + } } else { - (output, err) + output } } else { - (output, err) + output } } -pub fn parse_variable( - working_set: &mut StateWorkingSet, - span: Span, -) -> (Option, Option) { +pub fn parse_variable(working_set: &mut StateWorkingSet, span: Span) -> Option { let bytes = working_set.get_span_contents(span); if is_variable(bytes) { @@ -5741,15 +5382,14 @@ pub fn parse_variable( let input = working_set.get_variable(var_id).ty.clone(); working_set.type_scope.add_type(input); - (Some(var_id), None) + Some(var_id) } else { - (None, None) + None } } else { - ( - None, - Some(ParseError::Expected("valid variable name".into(), span)), - ) + working_set.error(ParseError::Expected("valid variable name".into(), span)); + + None } } @@ -5758,7 +5398,7 @@ pub fn parse_builtin_commands( lite_command: &LiteCommand, expand_aliases_denylist: &[usize], is_subexpression: bool, -) -> (Pipeline, Option) { +) -> Pipeline { if !is_math_expression_like(working_set, lite_command.parts[0], expand_aliases_denylist) && !is_unaliasable_parser_keyword(working_set, &lite_command.parts) { @@ -5768,7 +5408,7 @@ pub fn parse_builtin_commands( if cmd.is_alias() { // Parse keywords that can be aliased. Note that we check for "unaliasable" keywords // because alias can have any name, therefore, we can't check for "aliasable" keywords. - let (call_expr, err) = parse_call( + let call_expr = parse_call( working_set, &lite_command.parts, lite_command.parts[0], @@ -5776,26 +5416,20 @@ pub fn parse_builtin_commands( is_subexpression, ); - if err.is_none() { - if let Expression { - expr: Expr::Call(call), - .. - } = call_expr - { - // Apply parse keyword side effects - let cmd = working_set.get_decl(call.decl_id); - match cmd.name() { - "overlay hide" => return parse_overlay_hide(working_set, call), - "overlay new" => return parse_overlay_new(working_set, call), - "overlay use" => { - return parse_overlay_use( - working_set, - call, - expand_aliases_denylist, - ) - } - _ => { /* this alias is not a parser keyword */ } + if let Expression { + expr: Expr::Call(call), + .. + } = call_expr + { + // Apply parse keyword side effects + let cmd = working_set.get_decl(call.decl_id); + match cmd.name() { + "overlay hide" => return parse_overlay_hide(working_set, call), + "overlay new" => return parse_overlay_new(working_set, call), + "overlay use" => { + return parse_overlay_use(working_set, call, expand_aliases_denylist) } + _ => { /* this alias is not a parser keyword */ } } } } @@ -5812,16 +5446,16 @@ pub fn parse_builtin_commands( } b"mut" => parse_mut(working_set, &lite_command.parts, expand_aliases_denylist), b"for" => { - let (expr, err) = parse_for(working_set, &lite_command.parts, expand_aliases_denylist); - (Pipeline::from_vec(vec![expr]), err) + let expr = parse_for(working_set, &lite_command.parts, expand_aliases_denylist); + Pipeline::from_vec(vec![expr]) } b"old-alias" => parse_old_alias(working_set, lite_command, None, expand_aliases_denylist), b"alias" => parse_alias(working_set, lite_command, None, expand_aliases_denylist), b"module" => parse_module(working_set, lite_command, expand_aliases_denylist), b"use" => { - let (pipeline, _, err) = + let (pipeline, _) = parse_use(working_set, &lite_command.parts, expand_aliases_denylist); - (pipeline, err) + pipeline } b"overlay" => parse_keyword( working_set, @@ -5838,14 +5472,14 @@ pub fn parse_builtin_commands( #[cfg(feature = "plugin")] b"register" => parse_register(working_set, &lite_command.parts, expand_aliases_denylist), _ => { - let (expr, err) = parse_expression( + let expr = parse_expression( working_set, &lite_command.parts, expand_aliases_denylist, is_subexpression, ); - (Pipeline::from_vec(vec![expr]), err) + Pipeline::from_vec(vec![expr]) } } } @@ -5854,72 +5488,66 @@ pub fn parse_record( working_set: &mut StateWorkingSet, span: Span, expand_aliases_denylist: &[usize], -) -> (Expression, Option) { +) -> Expression { let bytes = working_set.get_span_contents(span); - let mut error = None; let mut start = span.start; let mut end = span.end; if bytes.starts_with(b"{") { start += 1; } else { - error = error.or_else(|| { - Some(ParseError::Expected( - "{".into(), - Span::new(start, start + 1), - )) - }); + working_set.error(ParseError::Expected( + "{".into(), + Span::new(start, start + 1), + )); + return garbage(span); } if bytes.ends_with(b"}") { end -= 1; } else { - error = error.or_else(|| Some(ParseError::Unclosed("}".into(), Span::new(end, end)))); + working_set.error(ParseError::Unclosed("}".into(), Span::new(end, end))); } let inner_span = Span::new(start, end); let source = working_set.get_span_contents(inner_span); let (tokens, err) = lex(source, start, &[b'\n', b'\r', b','], &[b':'], true); - error = error.or(err); + if let Some(err) = err { + working_set.error(err); + } let mut output = vec![]; let mut idx = 0; let mut field_types = Some(vec![]); while idx < tokens.len() { - let (field, err) = parse_value( + let field = parse_value( working_set, tokens[idx].span, &SyntaxShape::Any, expand_aliases_denylist, ); - error = error.or(err); idx += 1; if idx == tokens.len() { - return ( - garbage(span), - Some(ParseError::Expected("record".into(), span)), - ); + working_set.error(ParseError::Expected("record".into(), span)); + return garbage(span); } let colon = working_set.get_span_contents(tokens[idx].span); idx += 1; if idx == tokens.len() || colon != b":" { //FIXME: need better error - return ( - garbage(span), - Some(ParseError::Expected("record".into(), span)), - ); + working_set.error(ParseError::Expected("record".into(), span)); + return garbage(span); } - let (value, err) = parse_value( + let value = parse_value( working_set, tokens[idx].span, &SyntaxShape::Any, expand_aliases_denylist, ); - error = error.or(err); idx += 1; if let Some(field) = field.as_string() { @@ -5934,19 +5562,16 @@ pub fn parse_record( output.push((field, value)); } - ( - Expression { - expr: Expr::Record(output), - span, - ty: (if let Some(fields) = field_types { - Type::Record(fields) - } else { - Type::Any - }), - custom_completion: None, - }, - error, - ) + Expression { + expr: Expr::Record(output), + span, + ty: (if let Some(fields) = field_types { + Type::Record(fields) + } else { + Type::Any + }), + custom_completion: None, + } } pub fn parse_block( @@ -5955,11 +5580,11 @@ pub fn parse_block( scoped: bool, expand_aliases_denylist: &[usize], is_subexpression: bool, -) -> (Block, Option) { - let mut error = None; - +) -> Block { let (lite_block, err) = lite_parse(tokens); - error = error.or(err); + if let Some(err) = err { + working_set.error(err); + } trace!("parsing block: {:?}", lite_block); @@ -5977,13 +5602,7 @@ pub fn parse_block( | LiteElement::Redirection(_, _, command) | LiteElement::SeparateRedirection { out: (_, command), .. - } => { - if let Some(err) = - parse_def_predecl(working_set, &command.parts, expand_aliases_denylist) - { - error = error.or(Some(err)); - } - } + } => parse_def_predecl(working_set, &command.parts, expand_aliases_denylist), } } } @@ -6000,7 +5619,7 @@ pub fn parse_block( .map(|command| match command { LiteElement::Command(span, command) => { trace!("parsing: pipeline element: command"); - let (expr, err) = parse_expression( + let expr = parse_expression( working_set, &command.parts, expand_aliases_denylist, @@ -6008,15 +5627,11 @@ pub fn parse_block( ); working_set.type_scope.add_type(expr.ty.clone()); - if error.is_none() { - error = err; - } - PipelineElement::Expression(*span, expr) } LiteElement::Redirection(span, redirection, command) => { trace!("parsing: pipeline element: redirection"); - let (expr, err) = parse_string( + let expr = parse_string( working_set, command.parts[0], expand_aliases_denylist, @@ -6024,10 +5639,6 @@ pub fn parse_block( working_set.type_scope.add_type(expr.ty.clone()); - if error.is_none() { - error = err; - } - PipelineElement::Redirection(*span, redirection.clone(), expr) } LiteElement::SeparateRedirection { @@ -6035,7 +5646,7 @@ pub fn parse_block( err: (err_span, err_command), } => { trace!("parsing: pipeline element: separate redirection"); - let (out_expr, out_err) = parse_string( + let out_expr = parse_string( working_set, out_command.parts[0], expand_aliases_denylist, @@ -6043,11 +5654,7 @@ pub fn parse_block( working_set.type_scope.add_type(out_expr.ty.clone()); - if error.is_none() { - error = out_err; - } - - let (err_expr, err_err) = parse_string( + let err_expr = parse_string( working_set, err_command.parts[0], expand_aliases_denylist, @@ -6055,10 +5662,6 @@ pub fn parse_block( working_set.type_scope.add_type(err_expr.ty.clone()); - if error.is_none() { - error = err_err; - } - PipelineElement::SeparateRedirection { out: (*out_span, out_expr), err: (*err_span, err_expr), @@ -6089,7 +5692,7 @@ pub fn parse_block( | LiteElement::SeparateRedirection { out: (_, command), .. } => { - let (mut pipeline, err) = parse_builtin_commands( + let mut pipeline = parse_builtin_commands( working_set, command, expand_aliases_denylist, @@ -6144,10 +5747,6 @@ pub fn parse_block( } } - if error.is_none() { - error = err; - } - pipeline } } @@ -6160,7 +5759,7 @@ pub fn parse_block( } working_set.type_scope.exit_scope(); - (block, error) + block } pub fn discover_captures_in_closure( @@ -6621,9 +6220,7 @@ pub fn parse( contents: &[u8], scoped: bool, expand_aliases_denylist: &[usize], -) -> (Block, Option) { - let mut error = None; - +) -> Block { let span_offset = working_set.next_span_start(); let name = match fname { @@ -6634,11 +6231,11 @@ pub fn parse( working_set.add_file(name, contents); let (output, err) = lex(contents, span_offset, &[], &[], false); - error = error.or(err); + if let Some(err) = err { + working_set.error(err) + } - let (mut output, err) = - parse_block(working_set, &output, scoped, expand_aliases_denylist, false); - error = error.or(err); + let mut output = parse_block(working_set, &output, scoped, expand_aliases_denylist, false); let mut seen = vec![]; let mut seen_blocks = HashMap::new(); @@ -6646,10 +6243,11 @@ pub fn parse( let captures = discover_captures_in_closure(working_set, &output, &mut seen, &mut seen_blocks); match captures { Ok(captures) => output.captures = captures.into_iter().map(|(var_id, _)| var_id).collect(), - Err(err) => error = Some(err), + Err(err) => working_set.error(err), } // Also check other blocks that might have been imported + let mut errors = vec![]; for (block_idx, block) in working_set.delta.blocks.iter().enumerate() { let block_id = block_idx + working_set.permanent_state.num_blocks(); @@ -6660,10 +6258,15 @@ pub fn parse( Ok(captures) => { seen_blocks.insert(block_id, captures); } - Err(err) => error = Some(err), + Err(err) => { + errors.push(err); + } } } } + for err in errors { + working_set.error(err) + } for (block_id, captures) in seen_blocks.into_iter() { // In theory, we should only be updating captures where we have new information @@ -6678,5 +6281,5 @@ pub fn parse( } } - (output, error) + output } diff --git a/crates/nu-parser/src/type_check.rs b/crates/nu-parser/src/type_check.rs index 68d47410a8..88e5b3b0ac 100644 --- a/crates/nu-parser/src/type_check.rs +++ b/crates/nu-parser/src/type_check.rs @@ -1,8 +1,7 @@ -use crate::ParseError; use nu_protocol::{ ast::{Bits, Boolean, Comparison, Expr, Expression, Math, Operator}, engine::StateWorkingSet, - Type, + ParseError, Type, }; pub fn type_compatible(lhs: &Type, rhs: &Type) -> bool { diff --git a/crates/nu-parser/tests/test_lex.rs b/crates/nu-parser/tests/test_lex.rs index ef843f9131..07470a310e 100644 --- a/crates/nu-parser/tests/test_lex.rs +++ b/crates/nu-parser/tests/test_lex.rs @@ -1,5 +1,5 @@ -use nu_parser::{lex, lex_signature, ParseError, Token, TokenContents}; -use nu_protocol::Span; +use nu_parser::{lex, lex_signature, Token, TokenContents}; +use nu_protocol::{ParseError, Span}; #[test] fn lex_basic() { diff --git a/crates/nu-parser/tests/test_parser.rs b/crates/nu-parser/tests/test_parser.rs index d1a83eecc8..00e7843727 100644 --- a/crates/nu-parser/tests/test_parser.rs +++ b/crates/nu-parser/tests/test_parser.rs @@ -1,11 +1,10 @@ -use nu_parser::ParseError; use nu_parser::*; use nu_protocol::ast::{Call, PathMember}; use nu_protocol::Span; use nu_protocol::{ ast::{Expr, Expression, PipelineElement}, engine::{Command, EngineState, Stack, StateWorkingSet}, - PipelineData, ShellError, Signature, SyntaxShape, + ParseError, PipelineData, ShellError, Signature, SyntaxShape, }; #[cfg(test)] @@ -53,7 +52,9 @@ fn test_int( let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, test, true, &[]); + let block = parse(&mut working_set, None, test, true, &[]); + + let err = working_set.parse_errors.first(); if let Some(err_pat) = expected_err { if let Some(parse_err) = err { @@ -265,9 +266,9 @@ fn test_parse_any() { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, test, true, &[]); + let block = parse(&mut working_set, None, test, true, &[]); - match (block, err) { + match (block, working_set.parse_errors.first()) { (_, Some(e)) => { println!("test: {test:?}, error: {e:#?}"); } @@ -281,9 +282,9 @@ pub fn parse_int() { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, b"3", true, &[]); + let block = parse(&mut working_set, None, b"3", true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; assert_eq!(expressions.len(), 1); @@ -304,9 +305,9 @@ pub fn parse_int_with_underscores() { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, b"420_69_2023", true, &[]); + let block = parse(&mut working_set, None, b"420_69_2023", true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; assert_eq!(expressions.len(), 1); @@ -334,9 +335,9 @@ pub fn parse_cell_path() { false, ); - let (block, err) = parse(&mut working_set, None, b"$foo.bar.baz", true, &[]); + let block = parse(&mut working_set, None, b"$foo.bar.baz", true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; assert_eq!(expressions.len(), 1); @@ -388,12 +389,9 @@ pub fn parse_cell_path_optional() { false, ); - let (block, err) = parse(&mut working_set, None, b"$foo.bar?.baz", true, &[]); + let block = parse(&mut working_set, None, b"$foo.bar?.baz", true, &[]); - if let Some(err) = err { - dbg!(err); - panic!(); - } + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; @@ -439,9 +437,9 @@ pub fn parse_binary_with_hex_format() { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, b"0x[13]", true, &[]); + let block = parse(&mut working_set, None, b"0x[13]", true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; assert_eq!(expressions.len(), 1); @@ -457,9 +455,9 @@ pub fn parse_binary_with_incomplete_hex_format() { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, b"0x[3]", true, &[]); + let block = parse(&mut working_set, None, b"0x[3]", true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; assert_eq!(expressions.len(), 1); @@ -475,9 +473,9 @@ pub fn parse_binary_with_binary_format() { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, b"0b[1010 1000]", true, &[]); + let block = parse(&mut working_set, None, b"0b[1010 1000]", true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; assert_eq!(expressions.len(), 1); @@ -493,9 +491,9 @@ pub fn parse_binary_with_incomplete_binary_format() { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, b"0b[10]", true, &[]); + let block = parse(&mut working_set, None, b"0b[10]", true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; assert_eq!(expressions.len(), 1); @@ -511,9 +509,9 @@ pub fn parse_binary_with_octal_format() { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, b"0o[250]", true, &[]); + let block = parse(&mut working_set, None, b"0o[250]", true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; assert_eq!(expressions.len(), 1); @@ -529,9 +527,9 @@ pub fn parse_binary_with_incomplete_octal_format() { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, b"0o[2]", true, &[]); + let block = parse(&mut working_set, None, b"0o[2]", true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; assert_eq!(expressions.len(), 1); @@ -547,9 +545,9 @@ pub fn parse_binary_with_invalid_octal_format() { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, b"0b[90]", true, &[]); + let block = parse(&mut working_set, None, b"0b[90]", true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; assert_eq!(expressions.len(), 1); @@ -567,9 +565,9 @@ pub fn parse_binary_with_multi_byte_char() { // found using fuzzing, Rust can panic if you slice into this string let contents = b"0x[\xEF\xBF\xBD]"; - let (block, err) = parse(&mut working_set, None, contents, true, &[]); + let block = parse(&mut working_set, None, contents, true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; assert_eq!(expressions.len(), 1); @@ -588,9 +586,9 @@ pub fn parse_call() { let sig = Signature::build("foo").named("--jazz", SyntaxShape::Int, "jazz!!", Some('j')); working_set.add_decl(sig.predeclare()); - let (block, err) = parse(&mut working_set, None, b"foo", true, &[]); + let block = parse(&mut working_set, None, b"foo", true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; @@ -616,8 +614,11 @@ pub fn parse_call_missing_flag_arg() { let sig = Signature::build("foo").named("jazz", SyntaxShape::Int, "jazz!!", Some('j')); working_set.add_decl(sig.predeclare()); - let (_, err) = parse(&mut working_set, None, b"foo --jazz", true, &[]); - assert!(matches!(err, Some(ParseError::MissingFlagParam(..)))); + parse(&mut working_set, None, b"foo --jazz", true, &[]); + assert!(matches!( + working_set.parse_errors.first(), + Some(ParseError::MissingFlagParam(..)) + )); } #[test] @@ -628,8 +629,11 @@ pub fn parse_call_missing_short_flag_arg() { let sig = Signature::build("foo").named("--jazz", SyntaxShape::Int, "jazz!!", Some('j')); working_set.add_decl(sig.predeclare()); - let (_, err) = parse(&mut working_set, None, b"foo -j", true, &[]); - assert!(matches!(err, Some(ParseError::MissingFlagParam(..)))); + parse(&mut working_set, None, b"foo -j", true, &[]); + assert!(matches!( + working_set.parse_errors.first(), + Some(ParseError::MissingFlagParam(..)) + )); } #[test] @@ -641,9 +645,9 @@ pub fn parse_call_too_many_shortflag_args() { .named("--jazz", SyntaxShape::Int, "jazz!!", Some('j')) .named("--math", SyntaxShape::Int, "math!!", Some('m')); working_set.add_decl(sig.predeclare()); - let (_, err) = parse(&mut working_set, None, b"foo -mj", true, &[]); + parse(&mut working_set, None, b"foo -mj", true, &[]); assert!(matches!( - err, + working_set.parse_errors.first(), Some(ParseError::ShortFlagBatchCantTakeArg(..)) )); } @@ -655,8 +659,11 @@ pub fn parse_call_unknown_shorthand() { let sig = Signature::build("foo").switch("--jazz", "jazz!!", Some('j')); working_set.add_decl(sig.predeclare()); - let (_, err) = parse(&mut working_set, None, b"foo -mj", true, &[]); - assert!(matches!(err, Some(ParseError::UnknownFlag(..)))); + parse(&mut working_set, None, b"foo -mj", true, &[]); + assert!(matches!( + working_set.parse_errors.first(), + Some(ParseError::UnknownFlag(..)) + )); } #[test] @@ -666,8 +673,11 @@ pub fn parse_call_extra_positional() { let sig = Signature::build("foo").switch("--jazz", "jazz!!", Some('j')); working_set.add_decl(sig.predeclare()); - let (_, err) = parse(&mut working_set, None, b"foo -j 100", true, &[]); - assert!(matches!(err, Some(ParseError::ExtraPositional(..)))); + parse(&mut working_set, None, b"foo -j 100", true, &[]); + assert!(matches!( + working_set.parse_errors.first(), + Some(ParseError::ExtraPositional(..)) + )); } #[test] @@ -677,8 +687,11 @@ pub fn parse_call_missing_req_positional() { let sig = Signature::build("foo").required("jazz", SyntaxShape::Int, "jazz!!"); working_set.add_decl(sig.predeclare()); - let (_, err) = parse(&mut working_set, None, b"foo", true, &[]); - assert!(matches!(err, Some(ParseError::MissingPositional(..)))); + parse(&mut working_set, None, b"foo", true, &[]); + assert!(matches!( + working_set.parse_errors.first(), + Some(ParseError::MissingPositional(..)) + )); } #[test] @@ -688,17 +701,20 @@ pub fn parse_call_missing_req_flag() { let sig = Signature::build("foo").required_named("--jazz", SyntaxShape::Int, "jazz!!", None); working_set.add_decl(sig.predeclare()); - let (_, err) = parse(&mut working_set, None, b"foo", true, &[]); - assert!(matches!(err, Some(ParseError::MissingRequiredFlag(..)))); + parse(&mut working_set, None, b"foo", true, &[]); + assert!(matches!( + working_set.parse_errors.first(), + Some(ParseError::MissingRequiredFlag(..)) + )); } #[test] fn test_nothing_comparison_eq() { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, b"2 == null", true, &[]); + let block = parse(&mut working_set, None, b"2 == null", true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; @@ -719,9 +735,9 @@ fn test_nothing_comparison_eq() { fn test_nothing_comparison_neq() { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, b"2 != null", true, &[]); + let block = parse(&mut working_set, None, b"2 != null", true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; @@ -746,9 +762,9 @@ mod string { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, b"\"hello nushell\"", true, &[]); + let block = parse(&mut working_set, None, b"\"hello nushell\"", true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; assert_eq!(expressions.len(), 1); @@ -769,9 +785,9 @@ mod string { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, b"$\"hello (39 + 3)\"", true, &[]); + let block = parse(&mut working_set, None, b"$\"hello (39 + 3)\"", true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; @@ -801,9 +817,9 @@ mod string { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, b"$\"hello \\(39 + 3)\"", true, &[]); + let block = parse(&mut working_set, None, b"$\"hello \\(39 + 3)\"", true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; @@ -832,7 +848,7 @@ mod string { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse( + let block = parse( &mut working_set, None, b"$\"hello \\\\(39 + 3)\"", @@ -840,7 +856,7 @@ mod string { &[], ); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; @@ -871,7 +887,7 @@ mod string { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse( + let block = parse( &mut working_set, None, b"$\"\\(1 + 3)\\(7 - 5)\"", @@ -879,7 +895,7 @@ mod string { &[], ); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; @@ -914,7 +930,7 @@ mod string { false, ); - let (_block, err) = parse( + parse( &mut working_set, None, br#" @@ -924,7 +940,7 @@ mod string { &[], ); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); } #[test] @@ -939,7 +955,7 @@ mod string { false, ); - let (_block, err) = parse( + parse( &mut working_set, None, br#" @@ -949,7 +965,7 @@ mod string { &[], ); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); } } } @@ -963,9 +979,9 @@ mod range { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, b"0..10", true, &[]); + let block = parse(&mut working_set, None, b"0..10", true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; @@ -995,9 +1011,9 @@ mod range { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, b"0..<10", true, &[]); + let block = parse(&mut working_set, None, b"0..<10", true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; @@ -1027,9 +1043,9 @@ mod range { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, b"10..0", true, &[]); + let block = parse(&mut working_set, None, b"10..0", true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; @@ -1059,9 +1075,9 @@ mod range { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, b"(3 - 3)..<(8 + 2)", true, &[]); + let block = parse(&mut working_set, None, b"(3 - 3)..<(8 + 2)", true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; @@ -1093,9 +1109,9 @@ mod range { working_set.add_decl(Box::new(Let)); - let (block, err) = parse(&mut working_set, None, b"let a = 2; $a..10", true, &[]); + let block = parse(&mut working_set, None, b"let a = 2; $a..10", true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 2); let expressions = &block[1]; @@ -1127,7 +1143,7 @@ mod range { working_set.add_decl(Box::new(Let)); - let (block, err) = parse( + let block = parse( &mut working_set, None, b"let a = 2; $a..<($a + 10)", @@ -1135,7 +1151,7 @@ mod range { &[], ); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 2); let expressions = &block[1]; @@ -1165,9 +1181,9 @@ mod range { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, b"0..", true, &[]); + let block = parse(&mut working_set, None, b"0..", true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; @@ -1197,9 +1213,9 @@ mod range { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, b"..10", true, &[]); + let block = parse(&mut working_set, None, b"..10", true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; @@ -1229,9 +1245,9 @@ mod range { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, b"-10..-3", true, &[]); + let block = parse(&mut working_set, None, b"-10..-3", true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; @@ -1261,9 +1277,9 @@ mod range { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, b"2.0..4.0..10.0", true, &[]); + let block = parse(&mut working_set, None, b"2.0..4.0..10.0", true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; @@ -1293,9 +1309,9 @@ mod range { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (_, err) = parse(&mut working_set, None, b"(0)..\"a\"", true, &[]); + parse(&mut working_set, None, b"(0)..\"a\"", true, &[]); - assert!(err.is_some()); + assert!(!working_set.parse_errors.is_empty()); } } @@ -1616,9 +1632,9 @@ mod input_types { let mut working_set = StateWorkingSet::new(&engine_state); let input = r#"ls | to-custom | group-by name other"#; - let (block, err) = parse(&mut working_set, None, input.as_bytes(), true, &[]); + let block = parse(&mut working_set, None, input.as_bytes(), true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; @@ -1680,9 +1696,9 @@ mod input_types { let input = r#"let a = (ls | to-custom | group-by name other); let b = (1+3); $a | agg sum"#; - let (block, err) = parse(&mut working_set, None, input.as_bytes(), true, &[]); + let block = parse(&mut working_set, None, input.as_bytes(), true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 3); let expressions = &block[2]; @@ -1711,9 +1727,9 @@ mod input_types { let mut working_set = StateWorkingSet::new(&engine_state); let input = r#"let a = (ls | to-custom | group-by name other); ($a + $a) | agg sum"#; - let (block, err) = parse(&mut working_set, None, input.as_bytes(), true, &[]); + let block = parse(&mut working_set, None, input.as_bytes(), true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 2); let expressions = &block[1]; @@ -1743,9 +1759,9 @@ mod input_types { let input = r#" let a = (ls | to-custom | group-by name other); [1 2 3] | to-custom; [1 2 3] | to-custom"#; - let (block, err) = parse(&mut working_set, None, input.as_bytes(), true, &[]); + let block = parse(&mut working_set, None, input.as_bytes(), true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 3); let expressions = &block[1]; @@ -1787,9 +1803,9 @@ mod input_types { let mut working_set = StateWorkingSet::new(&engine_state); let input = r#"ls | group-by name"#; - let (block, err) = parse(&mut working_set, None, input.as_bytes(), true, &[]); + let block = parse(&mut working_set, None, input.as_bytes(), true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; @@ -1832,7 +1848,7 @@ mod input_types { let (block, delta) = { let mut working_set = StateWorkingSet::new(&engine_state); let input = r#"ls | to-custom | group-by name other | agg ("b" | min)"#; - let (block, _) = parse(&mut working_set, None, input.as_bytes(), true, &[]); + let block = parse(&mut working_set, None, input.as_bytes(), true, &[]); (block, working_set.render()) }; @@ -1893,9 +1909,9 @@ mod input_types { let mut working_set = StateWorkingSet::new(&engine_state); let input = r#"[[a b]; [1 2] [3 4]] | to-custom | with-column [ ("a" | min) ("b" | min) ] | collect"#; - let (block, err) = parse(&mut working_set, None, input.as_bytes(), true, &[]); + let block = parse(&mut working_set, None, input.as_bytes(), true, &[]); - assert!(err.is_none()); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 1); let expressions = &block[0]; @@ -1947,9 +1963,9 @@ mod input_types { ]; for input in inputs { - let (block, err) = parse(&mut working_set, None, input.as_bytes(), true, &[]); + let block = parse(&mut working_set, None, input.as_bytes(), true, &[]); - assert!(err.is_none(), "testing: {input}"); + assert!(working_set.parse_errors.is_empty()); assert_eq!(block.len(), 2, "testing: {input}"); } } @@ -1960,7 +1976,7 @@ mod input_types { add_declarations(&mut engine_state); let mut working_set = StateWorkingSet::new(&engine_state); - let (_, err) = parse( + parse( &mut working_set, None, b"if false { 'a' } else { $foo }", @@ -1968,9 +1984,10 @@ mod input_types { &[], ); - let err = err.unwrap(); - - assert!(matches!(err, ParseError::VariableNotFound(_))); + assert!(matches!( + working_set.parse_errors.first(), + Some(ParseError::VariableNotFound(_)) + )); } #[test] @@ -1979,7 +1996,7 @@ mod input_types { add_declarations(&mut engine_state); let mut working_set = StateWorkingSet::new(&engine_state); - let (_, err) = parse( + parse( &mut working_set, None, b"if false { 'a' } else $foo { 'b' }", @@ -1987,8 +2004,9 @@ mod input_types { &[], ); - let err = err.unwrap(); - - assert!(matches!(err, ParseError::VariableNotFound(_))); + assert!(matches!( + working_set.parse_errors.first(), + Some(ParseError::VariableNotFound(_)) + )); } } diff --git a/crates/nu-parser/tests/test_parser_unicode_escapes.rs b/crates/nu-parser/tests/test_parser_unicode_escapes.rs index 50d4e10c21..0ee8e12750 100644 --- a/crates/nu-parser/tests/test_parser_unicode_escapes.rs +++ b/crates/nu-parser/tests/test_parser_unicode_escapes.rs @@ -14,9 +14,9 @@ pub fn do_test(test: &[u8], expected: &str, error_contains: Option<&str>) { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, test, true, &[]); + let block = parse(&mut working_set, None, test, true, &[]); - match err { + match working_set.parse_errors.first() { None => { assert_eq!(block.len(), 1); let expressions = &block[0]; diff --git a/crates/nu-protocol/src/engine/engine_state.rs b/crates/nu-protocol/src/engine/engine_state.rs index 2b4dda8c10..f6758aaadf 100644 --- a/crates/nu-protocol/src/engine/engine_state.rs +++ b/crates/nu-protocol/src/engine/engine_state.rs @@ -2,11 +2,11 @@ use fancy_regex::Regex; use lru::LruCache; use super::{Command, EnvVars, OverlayFrame, ScopeFrame, Stack, Visibility, DEFAULT_OVERLAY_NAME}; -use crate::Value; use crate::{ ast::Block, AliasId, BlockId, Config, DeclId, Example, Module, ModuleId, OverlayId, ShellError, Signature, Span, Type, VarId, Variable, }; +use crate::{ParseError, Value}; use core::panic; use std::borrow::Borrow; use std::num::NonZeroUsize; @@ -1035,6 +1035,7 @@ pub struct StateWorkingSet<'a> { pub currently_parsed_cwd: Option, /// All previously parsed module files. Used to protect against circular imports. pub parsed_module_files: Vec, + pub parse_errors: Vec, } /// A temporary placeholder for expression types. It is used to keep track of the input types @@ -1218,9 +1219,14 @@ impl<'a> StateWorkingSet<'a> { type_scope: TypeScope::default(), currently_parsed_cwd: permanent_state.currently_parsed_cwd.clone(), parsed_module_files: vec![], + parse_errors: vec![], } } + pub fn error(&mut self, parse_error: ParseError) { + self.parse_errors.push(parse_error) + } + pub fn num_files(&self) -> usize { self.delta.num_files() + self.permanent_state.num_files() } diff --git a/crates/nu-protocol/src/lib.rs b/crates/nu-protocol/src/lib.rs index 6211ca5a2c..6e47d44bf3 100644 --- a/crates/nu-protocol/src/lib.rs +++ b/crates/nu-protocol/src/lib.rs @@ -8,6 +8,7 @@ mod exportable; mod id; mod lev_distance; mod module; +mod parse_error; mod pipeline_data; #[cfg(feature = "plugin")] mod plugin_signature; @@ -28,6 +29,7 @@ pub use example::*; pub use exportable::*; pub use id::*; pub use module::*; +pub use parse_error::ParseError; pub use pipeline_data::*; #[cfg(feature = "plugin")] pub use plugin_signature::*; diff --git a/crates/nu-parser/src/errors.rs b/crates/nu-protocol/src/parse_error.rs similarity index 99% rename from crates/nu-parser/src/errors.rs rename to crates/nu-protocol/src/parse_error.rs index af347107c6..cbe81525d9 100644 --- a/crates/nu-parser/src/errors.rs +++ b/crates/nu-protocol/src/parse_error.rs @@ -1,5 +1,5 @@ +use crate::{Span, Type}; use miette::Diagnostic; -use nu_protocol::{Span, Type}; use thiserror::Error; #[derive(Clone, Debug, Error, Diagnostic)] diff --git a/src/command.rs b/src/command.rs index 5772f3d4c6..382c9abd9b 100644 --- a/src/command.rs +++ b/src/command.rs @@ -64,15 +64,15 @@ pub(crate) fn parse_commandline_args( let mut working_set = StateWorkingSet::new(engine_state); working_set.add_decl(Box::new(Nu)); - let (output, err) = parse( + let output = parse( &mut working_set, None, commandline_args.as_bytes(), false, &[], ); - if let Some(err) = err { - report_error(&working_set, &err); + if let Some(err) = working_set.parse_errors.first() { + report_error(&working_set, err); std::process::exit(1); } diff --git a/src/config_files.rs b/src/config_files.rs index 9ba13227ee..f061cb0542 100644 --- a/src/config_files.rs +++ b/src/config_files.rs @@ -3,10 +3,9 @@ use log::info; use nu_cli::read_plugin_file; use nu_cli::{eval_config_contents, eval_source}; use nu_command::util::report_error; -use nu_parser::ParseError; use nu_path::canonicalize_with; use nu_protocol::engine::{EngineState, Stack, StateWorkingSet}; -use nu_protocol::{PipelineData, Spanned}; +use nu_protocol::{ParseError, PipelineData, Spanned}; use nu_utils::{get_default_config, get_default_env}; use std::fs::File; use std::io::Write; diff --git a/src/ide.rs b/src/ide.rs index 8c50934322..927bbbe649 100644 --- a/src/ide.rs +++ b/src/ide.rs @@ -22,7 +22,7 @@ fn find_id( location: &Value, ) -> Option<(Id, usize, Span)> { let offset = working_set.next_span_start(); - let (block, _) = parse(working_set, Some(file_path), file, false, &[]); + let block = parse(working_set, Some(file_path), file, false, &[]); let flattened = flatten_block(working_set, &block); @@ -78,9 +78,9 @@ pub fn check(engine_state: &mut EngineState, file_path: &String) { if let Ok(contents) = file { let offset = working_set.next_span_start(); - let (block, err) = parse(&mut working_set, Some(file_path), &contents, false, &[]); + let block = parse(&mut working_set, Some(file_path), &contents, false, &[]); - if let Some(err) = err { + for err in &working_set.parse_errors { let mut span = err.span(); span.start -= offset; span.end -= offset; diff --git a/src/run.rs b/src/run.rs index fb6eb0827a..3358b913a3 100644 --- a/src/run.rs +++ b/src/run.rs @@ -62,21 +62,21 @@ fn load_standard_library( working_set.add_file(name.clone(), content); let end = working_set.next_span_start(); - let (_, module, comments, parse_error) = parse_module_block( + let (_, module, comments) = parse_module_block( &mut working_set, Span::new(start, end), name.as_bytes(), &[], ); - if let Some(err) = parse_error { - report_error(&working_set, &err); + if let Some(err) = working_set.parse_errors.first() { + report_error(&working_set, err); } - let (_, parse_error) = parse(&mut working_set, Some(&name), content, true, &[]); + parse(&mut working_set, Some(&name), content, true, &[]); - if let Some(err) = parse_error { - report_error(&working_set, &err); + if let Some(err) = working_set.parse_errors.first() { + report_error(&working_set, err); } // TODO: change this when #8505 is merged diff --git a/src/test_bins.rs b/src/test_bins.rs index 65cfa2fe6d..0cc8261f89 100644 --- a/src/test_bins.rs +++ b/src/test_bins.rs @@ -224,7 +224,7 @@ pub fn nu_repl() { // Eval the REPL line let (block, delta) = { let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse( + let block = parse( &mut working_set, Some(&format!("line{i}")), line.as_bytes(), @@ -232,8 +232,8 @@ pub fn nu_repl() { &[], ); - if let Some(err) = err { - outcome_err(&engine_state, &err); + if let Some(err) = working_set.parse_errors.first() { + outcome_err(&engine_state, err); } (block, working_set.render()) };