From 983d115bc0a1039d42fb6c166af8827e3c7b2ac0 Mon Sep 17 00:00:00 2001 From: JT <547158+jntrnr@users.noreply.github.com> Date: Sat, 19 Mar 2022 08:03:57 +1300 Subject: [PATCH] Add an alias denylist for expansions (#4871) --- crates/nu-cli/src/commands.rs | 2 +- crates/nu-cli/src/completions.rs | 8 +- crates/nu-cli/src/eval_file.rs | 2 +- crates/nu-cli/src/prompt_update.rs | 2 +- crates/nu-cli/src/syntax_highlight.rs | 2 +- crates/nu-cli/src/util.rs | 1 + crates/nu-cli/src/validation.rs | 2 +- .../src/dataframe/test_dataframe.rs | 8 +- crates/nu-command/src/example_test.rs | 8 +- crates/nu-command/src/formats/from/nuon.rs | 2 +- crates/nu-command/tests/main.rs | 2 +- crates/nu-parser/src/known_external.rs | 2 +- crates/nu-parser/src/parse_keywords.rs | 135 +++++- crates/nu-parser/src/parser.rs | 394 +++++++++++++----- crates/nu-parser/tests/test_parser.rs | 50 ++- crates/nu-protocol/src/engine/engine_state.rs | 36 +- src/main.rs | 8 +- tests/shell/pipeline/commands/internal.rs | 11 + 18 files changed, 516 insertions(+), 159 deletions(-) diff --git a/crates/nu-cli/src/commands.rs b/crates/nu-cli/src/commands.rs index 850ac6e711..42de5d1764 100644 --- a/crates/nu-cli/src/commands.rs +++ b/crates/nu-cli/src/commands.rs @@ -31,7 +31,7 @@ pub fn evaluate_commands( (commands.item.as_bytes(), commands.span.start) }; - let (output, err) = parse(&mut working_set, None, input, false); + let (output, err) = parse(&mut working_set, None, input, false, &[]); if let Some(err) = err { report_error(&working_set, &err); diff --git a/crates/nu-cli/src/completions.rs b/crates/nu-cli/src/completions.rs index 8d69053de3..371f74ccf5 100644 --- a/crates/nu-cli/src/completions.rs +++ b/crates/nu-cli/src/completions.rs @@ -206,7 +206,13 @@ impl NuCompleter { let mut line = line.to_string(); line.insert(pos, 'a'); let pos = offset + pos; - let (output, _err) = parse(&mut working_set, Some("completer"), line.as_bytes(), false); + let (output, _err) = parse( + &mut working_set, + Some("completer"), + line.as_bytes(), + false, + &[], + ); for pipeline in output.pipelines.into_iter() { for expr in pipeline.expressions { diff --git a/crates/nu-cli/src/eval_file.rs b/crates/nu-cli/src/eval_file.rs index 871fa62444..e3c9d50f81 100644 --- a/crates/nu-cli/src/eval_file.rs +++ b/crates/nu-cli/src/eval_file.rs @@ -36,7 +36,7 @@ pub fn evaluate_file( let mut working_set = StateWorkingSet::new(engine_state); trace!("parsing file: {}", path); - let _ = parse(&mut working_set, Some(&path), &file, false); + let _ = parse(&mut working_set, Some(&path), &file, false, &[]); if working_set.find_decl(b"main").is_some() { let args = format!("main {}", args.join(" ")); diff --git a/crates/nu-cli/src/prompt_update.rs b/crates/nu-cli/src/prompt_update.rs index 875ca45f63..f1d5c4663c 100644 --- a/crates/nu-cli/src/prompt_update.rs +++ b/crates/nu-cli/src/prompt_update.rs @@ -104,7 +104,7 @@ fn get_prompt_string( } Value::String { val: source, .. } => { let mut working_set = StateWorkingSet::new(engine_state); - let (block, _) = parse(&mut working_set, None, source.as_bytes(), true); + let (block, _) = parse(&mut working_set, None, source.as_bytes(), true, &[]); // Use eval_subexpression to force a redirection of output, so we can use everything in prompt let ret_val = eval_subexpression( engine_state, diff --git a/crates/nu-cli/src/syntax_highlight.rs b/crates/nu-cli/src/syntax_highlight.rs index 171d90047d..0416a76601 100644 --- a/crates/nu-cli/src/syntax_highlight.rs +++ b/crates/nu-cli/src/syntax_highlight.rs @@ -17,7 +17,7 @@ impl Highlighter for NuHighlighter { let (shapes, global_span_offset) = { let mut working_set = StateWorkingSet::new(&self.engine_state); - let (block, _) = parse(&mut working_set, None, line.as_bytes(), false); + let (block, _) = parse(&mut working_set, None, line.as_bytes(), false, &[]); 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 f7f7afd833..218441f87b 100644 --- a/crates/nu-cli/src/util.rs +++ b/crates/nu-cli/src/util.rs @@ -287,6 +287,7 @@ pub fn eval_source( Some(fname), // format!("entry #{}", entry_num) source, false, + &[], ); if let Some(err) = err { report_error(&working_set, &err); diff --git a/crates/nu-cli/src/validation.rs b/crates/nu-cli/src/validation.rs index 9e306596c4..8b31932c31 100644 --- a/crates/nu-cli/src/validation.rs +++ b/crates/nu-cli/src/validation.rs @@ -9,7 +9,7 @@ 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); + let (_, err) = parse(&mut working_set, None, line.as_bytes(), false, &[]); if matches!(err, Some(ParseError::UnexpectedEof(..))) { ValidationResult::Incomplete diff --git a/crates/nu-command/src/dataframe/test_dataframe.rs b/crates/nu-command/src/dataframe/test_dataframe.rs index 7fe6066bb4..8c33690761 100644 --- a/crates/nu-command/src/dataframe/test_dataframe.rs +++ b/crates/nu-command/src/dataframe/test_dataframe.rs @@ -44,7 +44,13 @@ pub fn test_dataframe(cmds: Vec>) { let (block, delta) = { let mut working_set = StateWorkingSet::new(&*engine_state); - let (output, err) = parse(&mut working_set, None, example.example.as_bytes(), false); + let (output, err) = parse( + &mut working_set, + None, + example.example.as_bytes(), + false, + &[], + ); if let Some(err) = err { panic!("test parse error in `{}`: {:?}", example.example, err) diff --git a/crates/nu-command/src/example_test.rs b/crates/nu-command/src/example_test.rs index 765928e179..a3510bcd6b 100644 --- a/crates/nu-command/src/example_test.rs +++ b/crates/nu-command/src/example_test.rs @@ -84,7 +84,13 @@ pub fn test_examples(cmd: impl Command + 'static) { let (block, delta) = { let mut working_set = StateWorkingSet::new(&*engine_state); - let (output, err) = parse(&mut working_set, None, example.example.as_bytes(), false); + let (output, err) = parse( + &mut working_set, + None, + example.example.as_bytes(), + false, + &[], + ); if let Some(err) = err { panic!("test parse error in `{}`: {:?}", example.example, err) diff --git a/crates/nu-command/src/formats/from/nuon.rs b/crates/nu-command/src/formats/from/nuon.rs index 12a38c125b..4344f096e7 100644 --- a/crates/nu-command/src/formats/from/nuon.rs +++ b/crates/nu-command/src/formats/from/nuon.rs @@ -89,7 +89,7 @@ impl Command for FromNuon { let (lite_block, err) = nu_parser::lite_parse(&lexed); error = error.or(err); - let (mut block, err) = nu_parser::parse_block(&mut working_set, &lite_block, true); + let (mut block, err) = nu_parser::parse_block(&mut working_set, &lite_block, true, &[]); error = error.or(err); if let Some(pipeline) = block.pipelines.get(1) { diff --git a/crates/nu-command/tests/main.rs b/crates/nu-command/tests/main.rs index 62f9eb37d2..2028edcda2 100644 --- a/crates/nu-command/tests/main.rs +++ b/crates/nu-command/tests/main.rs @@ -19,7 +19,7 @@ fn quickcheck_parse(data: String) -> bool { let mut working_set = StateWorkingSet::new(&context); working_set.add_file("quickcheck".into(), data.as_bytes()); - let _ = nu_parser::parse_block(&mut working_set, &lite_block, false); + let _ = nu_parser::parse_block(&mut working_set, &lite_block, false, &[]); } } true diff --git a/crates/nu-parser/src/known_external.rs b/crates/nu-parser/src/known_external.rs index fc77a1ece5..a36a5a1ce8 100644 --- a/crates/nu-parser/src/known_external.rs +++ b/crates/nu-parser/src/known_external.rs @@ -51,7 +51,7 @@ impl Command for KnownExternal { let spans: Vec<_> = lexed.into_iter().map(|x| x.span).collect(); let mut working_set = StateWorkingSet::new(&engine_state); - let (external_call, _) = crate::parse_external_call(&mut working_set, &spans); + let (external_call, _) = crate::parse_external_call(&mut working_set, &spans, &[]); let delta = working_set.render(); engine_state.merge_delta(delta, None, ".")?; diff --git a/crates/nu-parser/src/parse_keywords.rs b/crates/nu-parser/src/parse_keywords.rs index b3516780f0..c9378fdd8c 100644 --- a/crates/nu-parser/src/parse_keywords.rs +++ b/crates/nu-parser/src/parse_keywords.rs @@ -26,7 +26,11 @@ use crate::{ ParseError, }; -pub fn parse_def_predecl(working_set: &mut StateWorkingSet, spans: &[Span]) -> Option { +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" @@ -47,7 +51,7 @@ pub fn parse_def_predecl(working_set: &mut StateWorkingSet, spans: &[Span]) -> O // 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]); + let (sig, ..) = parse_signature(working_set, spans[2], expand_aliases_denylist); let signature = sig.as_signature(); working_set.exit_scope(); @@ -70,7 +74,7 @@ pub fn parse_def_predecl(working_set: &mut StateWorkingSet, spans: &[Span]) -> O // 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]); + let (sig, ..) = parse_signature(working_set, spans[2], expand_aliases_denylist); let signature = sig.as_signature(); working_set.exit_scope(); @@ -95,6 +99,7 @@ pub fn parse_def_predecl(working_set: &mut StateWorkingSet, spans: &[Span]) -> O pub fn parse_for( working_set: &mut StateWorkingSet, spans: &[Span], + expand_aliases_denylist: &[usize], ) -> (Expression, Option) { // Checking that the function is used with the correct name // Maybe this is not necessary but it is a sanity check @@ -123,7 +128,13 @@ pub fn parse_for( } Some(decl_id) => { working_set.enter_scope(); - let (call, mut err) = parse_internal_call(working_set, spans[0], &spans[1..], decl_id); + let (call, mut err) = parse_internal_call( + working_set, + spans[0], + &spans[1..], + decl_id, + expand_aliases_denylist, + ); working_set.exit_scope(); let call_span = span(spans); @@ -251,6 +262,7 @@ fn build_usage(working_set: &StateWorkingSet, spans: &[Span]) -> String { pub fn parse_def( working_set: &mut StateWorkingSet, lite_command: &LiteCommand, + expand_aliases_denylist: &[usize], ) -> (Pipeline, Option) { let spans = &lite_command.parts[..]; @@ -285,7 +297,13 @@ pub fn parse_def( } Some(decl_id) => { working_set.enter_scope(); - let (call, mut err) = parse_internal_call(working_set, spans[0], &spans[1..], decl_id); + let (call, mut err) = parse_internal_call( + working_set, + spans[0], + &spans[1..], + decl_id, + expand_aliases_denylist, + ); working_set.exit_scope(); let call_span = span(spans); @@ -385,6 +403,7 @@ pub fn parse_def( pub fn parse_extern( working_set: &mut StateWorkingSet, lite_command: &LiteCommand, + expand_aliases_denylist: &[usize], ) -> (Pipeline, Option) { let spans = &lite_command.parts[..]; let mut error = None; @@ -420,7 +439,13 @@ pub fn parse_extern( } Some(decl_id) => { working_set.enter_scope(); - let (call, err) = parse_internal_call(working_set, spans[0], &spans[1..], decl_id); + let (call, err) = parse_internal_call( + working_set, + spans[0], + &spans[1..], + decl_id, + expand_aliases_denylist, + ); working_set.exit_scope(); error = error.or(err); @@ -486,6 +511,7 @@ pub fn parse_extern( pub fn parse_alias( working_set: &mut StateWorkingSet, spans: &[Span], + expand_aliases_denylist: &[usize], ) -> (Pipeline, Option) { let name = working_set.get_span_contents(spans[0]); @@ -495,7 +521,13 @@ pub fn parse_alias( } if let Some(decl_id) = working_set.find_decl(b"alias") { - let (call, _) = parse_internal_call(working_set, spans[0], &spans[1..], decl_id); + let (call, _) = parse_internal_call( + working_set, + spans[0], + &spans[1..], + decl_id, + expand_aliases_denylist, + ); if spans.len() >= 4 { let alias_name = working_set.get_span_contents(spans[1]); @@ -539,6 +571,7 @@ pub fn parse_alias( pub fn parse_export( working_set: &mut StateWorkingSet, lite_command: &LiteCommand, + expand_aliases_denylist: &[usize], ) -> (Pipeline, Option, Option) { let spans = &lite_command.parts[..]; let mut error = None; @@ -597,7 +630,8 @@ pub fn parse_export( comments: lite_command.comments.clone(), parts: spans[1..].to_vec(), }; - let (pipeline, err) = parse_def(working_set, &lite_command); + let (pipeline, err) = + parse_def(working_set, &lite_command, expand_aliases_denylist); error = error.or(err); let export_def_decl_id = if let Some(id) = working_set.find_decl(b"export def") { @@ -655,7 +689,8 @@ pub fn parse_export( comments: lite_command.comments.clone(), parts: spans[1..].to_vec(), }; - let (pipeline, err) = parse_def(working_set, &lite_command); + let (pipeline, err) = + parse_def(working_set, &lite_command, expand_aliases_denylist); error = error.or(err); let export_def_decl_id = if let Some(id) = working_set.find_decl(b"export def-env") @@ -738,6 +773,7 @@ pub fn parse_export( working_set, &SyntaxShape::Block(None), *block_span, + expand_aliases_denylist, ); error = error.or(err); @@ -835,6 +871,7 @@ pub fn parse_export( pub fn parse_module_block( working_set: &mut StateWorkingSet, span: Span, + expand_aliases_denylist: &[usize], ) -> (Block, Overlay, Option) { let mut error = None; @@ -851,7 +888,11 @@ pub fn parse_module_block( for pipeline in &output.block { // TODO: Should we add export env predecls as well? if pipeline.commands.len() == 1 { - parse_def_predecl(working_set, &pipeline.commands[0].parts); + parse_def_predecl( + working_set, + &pipeline.commands[0].parts, + expand_aliases_denylist, + ); } } @@ -866,12 +907,17 @@ pub fn parse_module_block( let (pipeline, err) = match name { b"def" | b"def-env" => { - let (pipeline, err) = parse_def(working_set, &pipeline.commands[0]); + let (pipeline, err) = + parse_def(working_set, &pipeline.commands[0], expand_aliases_denylist); (pipeline, err) } b"extern" => { - let (pipeline, err) = parse_extern(working_set, &pipeline.commands[0]); + let (pipeline, err) = parse_extern( + working_set, + &pipeline.commands[0], + expand_aliases_denylist, + ); (pipeline, err) } @@ -884,8 +930,11 @@ pub fn parse_module_block( // will work only if you call `use foo *; b` but not with `use foo; foo b` // since in the second case, the name of the env var would be $env."foo a". b"export" => { - let (pipe, exportable, err) = - parse_export(working_set, &pipeline.commands[0]); + let (pipe, exportable, err) = parse_export( + working_set, + &pipeline.commands[0], + expand_aliases_denylist, + ); if err.is_none() { let name_span = pipeline.commands[0].parts[2]; @@ -934,6 +983,7 @@ pub fn parse_module_block( pub fn parse_module( working_set: &mut StateWorkingSet, spans: &[Span], + expand_aliases_denylist: &[usize], ) -> (Pipeline, Option) { // 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. @@ -972,7 +1022,8 @@ pub fn parse_module( let block_span = Span { start, end }; - let (block, overlay, err) = parse_module_block(working_set, block_span); + let (block, overlay, err) = + parse_module_block(working_set, block_span, expand_aliases_denylist); error = error.or(err); let block_id = working_set.add_block(block); @@ -1021,6 +1072,7 @@ pub fn parse_module( pub fn parse_use( working_set: &mut StateWorkingSet, spans: &[Span], + expand_aliases_denylist: &[usize], ) -> (Pipeline, Option) { if working_set.get_span_contents(spans[0]) != b"use" { return ( @@ -1034,7 +1086,13 @@ pub fn parse_use( let (call, call_span, use_decl_id) = match working_set.find_decl(b"use") { Some(decl_id) => { - let (call, mut err) = parse_internal_call(working_set, spans[0], &spans[1..], decl_id); + let (call, mut err) = parse_internal_call( + working_set, + spans[0], + &spans[1..], + decl_id, + expand_aliases_denylist, + ); let decl = working_set.get_decl(decl_id); let call_span = span(spans); @@ -1124,8 +1182,11 @@ pub fn parse_use( working_set.add_file(module_filename, &contents); let span_end = working_set.next_span_start(); - let (block, overlay, err) = - parse_module_block(working_set, Span::new(span_start, span_end)); + let (block, overlay, err) = parse_module_block( + working_set, + Span::new(span_start, span_end), + expand_aliases_denylist, + ); error = error.or(err); let _ = working_set.add_block(block); @@ -1230,6 +1291,7 @@ pub fn parse_use( pub fn parse_hide( working_set: &mut StateWorkingSet, spans: &[Span], + expand_aliases_denylist: &[usize], ) -> (Pipeline, Option) { if working_set.get_span_contents(spans[0]) != b"hide" { return ( @@ -1243,7 +1305,13 @@ pub fn parse_hide( let (call, call_span, hide_decl_id) = match working_set.find_decl(b"hide") { Some(decl_id) => { - let (call, mut err) = parse_internal_call(working_set, spans[0], &spans[1..], decl_id); + let (call, mut err) = parse_internal_call( + working_set, + spans[0], + &spans[1..], + decl_id, + expand_aliases_denylist, + ); let decl = working_set.get_decl(decl_id); let call_span = span(spans); @@ -1440,6 +1508,7 @@ pub fn parse_hide( pub fn parse_let( working_set: &mut StateWorkingSet, spans: &[Span], + expand_aliases_denylist: &[usize], ) -> (Pipeline, Option) { let name = working_set.get_span_contents(spans[0]); @@ -1466,6 +1535,7 @@ pub fn parse_let( spans, &mut idx, &SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::Expression)), + expand_aliases_denylist, ); error = error.or(err); @@ -1512,7 +1582,13 @@ pub fn parse_let( } } } - let (call, err) = parse_internal_call(working_set, spans[0], &spans[1..], decl_id); + let (call, err) = parse_internal_call( + working_set, + spans[0], + &spans[1..], + decl_id, + expand_aliases_denylist, + ); return ( Pipeline { @@ -1539,6 +1615,7 @@ pub fn parse_let( pub fn parse_source( working_set: &mut StateWorkingSet, spans: &[Span], + expand_aliases_denylist: &[usize], ) -> (Pipeline, Option) { let mut error = None; let name = working_set.get_span_contents(spans[0]); @@ -1548,7 +1625,13 @@ pub fn parse_source( let cwd = working_set.get_cwd(); // 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 (call, err) = parse_internal_call(working_set, spans[0], &spans[1..], decl_id); + let (call, err) = 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") { @@ -1577,6 +1660,7 @@ pub fn parse_source( path.file_name().and_then(|x| x.to_str()), &contents, false, + expand_aliases_denylist, ); if err.is_some() { @@ -1648,6 +1732,7 @@ pub fn parse_source( pub fn parse_register( working_set: &mut StateWorkingSet, spans: &[Span], + expand_aliases_denylist: &[usize], ) -> (Pipeline, Option) { use nu_plugin::{get_signature, EncodingType, PluginDeclaration}; use nu_protocol::Signature; @@ -1679,7 +1764,13 @@ pub fn parse_register( ) } Some(decl_id) => { - let (call, mut err) = parse_internal_call(working_set, spans[0], &spans[1..], decl_id); + let (call, mut err) = parse_internal_call( + working_set, + spans[0], + &spans[1..], + decl_id, + expand_aliases_denylist, + ); let decl = working_set.get_decl(decl_id); let call_span = span(spans); diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index bb2bc547e5..bcb3031f64 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -208,6 +208,7 @@ pub fn check_name<'a>( pub fn parse_external_call( working_set: &mut StateWorkingSet, spans: &[Span], + expand_aliases_denylist: &[usize], ) -> (Expression, Option) { let mut args = vec![]; @@ -235,10 +236,17 @@ pub fn parse_external_call( new_spans.extend(&spans[1..]) } - working_set.enter_scope(); - working_set.hide_alias(&head_contents); - let (mut result, err) = parse_external_call(working_set, &new_spans); - working_set.exit_scope(); + let expand_aliases_denylist = if let Some(alias_id) = working_set.find_alias(&head_contents) + { + let mut expand_aliases_denylist = expand_aliases_denylist.to_vec(); + expand_aliases_denylist.push(alias_id); + expand_aliases_denylist + } else { + expand_aliases_denylist.to_vec() + }; + + let (mut result, err) = + parse_external_call(working_set, &new_spans, &expand_aliases_denylist); result.replace_span(working_set, expansion_span, orig_span); return (result, err); @@ -247,7 +255,7 @@ pub fn parse_external_call( let mut error = None; let head = if head_contents.starts_with(b"$") || head_contents.starts_with(b"(") { - let (arg, err) = parse_expression(working_set, &[head_span], true); + let (arg, err) = parse_expression(working_set, &[head_span], expand_aliases_denylist); error = error.or(err); Box::new(arg) } else { @@ -263,11 +271,12 @@ pub fn parse_external_call( let contents = working_set.get_span_contents(*span); if contents.starts_with(b"$") || contents.starts_with(b"(") { - let (arg, err) = parse_dollar_expr(working_set, *span); + let (arg, err) = parse_dollar_expr(working_set, *span, expand_aliases_denylist); error = error.or(err); args.push(arg); } else if contents.starts_with(b"(") { - let (arg, err) = parse_full_cell_path(working_set, None, *span); + let (arg, err) = + parse_full_cell_path(working_set, None, *span, expand_aliases_denylist); error = error.or(err); args.push(arg); } else { @@ -295,6 +304,7 @@ fn parse_long_flag( spans: &[Span], spans_idx: &mut usize, sig: &Signature, + expand_aliases_denylist: &[usize], ) -> ( Option>, Option, @@ -317,7 +327,8 @@ fn parse_long_flag( let mut span = arg_span; span.start += long_name_len + 3; //offset by long flag and '=' - let (arg, err) = parse_value(working_set, span, arg_shape); + let (arg, err) = + parse_value(working_set, span, arg_shape, expand_aliases_denylist); ( Some(Spanned { @@ -331,7 +342,8 @@ fn parse_long_flag( err, ) } else if let Some(arg) = spans.get(*spans_idx + 1) { - let (arg, err) = parse_value(working_set, *arg, arg_shape); + let (arg, err) = + parse_value(working_set, *arg, arg_shape, expand_aliases_denylist); *spans_idx += 1; ( @@ -554,6 +566,7 @@ pub fn parse_multispan_value( spans: &[Span], spans_idx: &mut usize, shape: &SyntaxShape, + expand_aliases_denylist: &[usize], ) -> (Expression, Option) { let mut error = None; @@ -568,7 +581,8 @@ pub fn parse_multispan_value( } SyntaxShape::RowCondition => { trace!("parsing: row condition"); - let (arg, err) = parse_row_condition(working_set, &spans[*spans_idx..]); + let (arg, err) = + parse_row_condition(working_set, &spans[*spans_idx..], expand_aliases_denylist); error = error.or(err); *spans_idx = spans.len() - 1; @@ -577,7 +591,12 @@ pub fn parse_multispan_value( SyntaxShape::MathExpression => { trace!("parsing: math expression"); - let (arg, err) = parse_math_expression(working_set, &spans[*spans_idx..], None); + let (arg, err) = parse_math_expression( + working_set, + &spans[*spans_idx..], + None, + expand_aliases_denylist, + ); error = error.or(err); *spans_idx = spans.len() - 1; @@ -586,7 +605,8 @@ pub fn parse_multispan_value( SyntaxShape::Expression => { trace!("parsing: expression"); - let (arg, err) = parse_expression(working_set, &spans[*spans_idx..], true); + let (arg, err) = + parse_expression(working_set, &spans[*spans_idx..], expand_aliases_denylist); error = error.or(err); *spans_idx = spans.len() - 1; @@ -595,7 +615,8 @@ pub fn parse_multispan_value( SyntaxShape::ImportPattern => { trace!("parsing: import pattern"); - let (arg, err) = parse_import_pattern(working_set, &spans[*spans_idx..]); + let (arg, err) = + parse_import_pattern(working_set, &spans[*spans_idx..], expand_aliases_denylist); error = error.or(err); *spans_idx = spans.len() - 1; @@ -649,7 +670,8 @@ pub fn parse_multispan_value( ); } let keyword_span = spans[*spans_idx - 1]; - let (expr, err) = parse_multispan_value(working_set, spans, spans_idx, arg); + let (expr, err) = + parse_multispan_value(working_set, spans, spans_idx, arg, expand_aliases_denylist); error = error.or(err); let ty = expr.ty.clone(); @@ -667,7 +689,7 @@ pub fn parse_multispan_value( // All other cases are single-span values let arg_span = spans[*spans_idx]; - let (arg, err) = parse_value(working_set, arg_span, shape); + let (arg, err) = parse_value(working_set, arg_span, shape, expand_aliases_denylist); error = error.or(err); (arg, error) @@ -680,6 +702,7 @@ pub fn parse_internal_call( command_span: Span, spans: &[Span], decl_id: usize, + expand_aliases_denylist: &[usize], ) -> (Box, Option) { trace!("parsing: internal call (decl id: {})", decl_id); @@ -706,7 +729,13 @@ pub fn parse_internal_call( let arg_span = spans[spans_idx]; // Check if we're on a long flag, if so, parse - let (long_name, arg, err) = parse_long_flag(working_set, spans, &mut spans_idx, &signature); + let (long_name, arg, err) = parse_long_flag( + working_set, + spans, + &mut spans_idx, + &signature, + expand_aliases_denylist, + ); if let Some(long_name) = long_name { // We found a long flag, like --bar error = error.or(err); @@ -729,7 +758,8 @@ pub fn parse_internal_call( for flag in short_flags { if let Some(arg_shape) = flag.arg { if let Some(arg) = spans.get(spans_idx + 1) { - let (arg, err) = parse_value(working_set, *arg, &arg_shape); + let (arg, err) = + parse_value(working_set, *arg, &arg_shape, expand_aliases_denylist); error = error.or(err); call.named.push(( @@ -793,6 +823,7 @@ pub fn parse_internal_call( &spans[..end], &mut spans_idx, &positional.shape, + expand_aliases_denylist, ); error = error.or(err); @@ -839,8 +870,8 @@ pub fn parse_internal_call( pub fn parse_call( working_set: &mut StateWorkingSet, spans: &[Span], - expand_aliases: bool, head: Span, + expand_aliases_denylist: &[usize], ) -> (Expression, Option) { trace!("parsing: call"); @@ -883,9 +914,9 @@ pub fn parse_call( name.extend(name_part); } - if expand_aliases { - // If the word is an alias, expand it and re-parse the expression - if let Some(alias_id) = working_set.find_alias(&name) { + // If the word is an alias, expand it and re-parse the expression + if let Some(alias_id) = working_set.find_alias(&name) { + if !expand_aliases_denylist.contains(&alias_id) { trace!("expanding alias"); let expansion = working_set.get_alias(alias_id); @@ -901,17 +932,15 @@ pub fn parse_call( new_spans.extend(&spans[(pos + 1)..]); } - let alias_id = working_set.hide_alias(&name); + let mut expand_aliases_denylist = expand_aliases_denylist.to_vec(); + expand_aliases_denylist.push(alias_id); + let lite_command = LiteCommand { comments: vec![], parts: new_spans.clone(), }; - let (mut result, err) = parse_builtin_commands(working_set, &lite_command); - if let Some(frame) = working_set.delta.scope.last_mut() { - if let Some(alias_id) = alias_id { - frame.aliases.insert(name.clone(), alias_id); - } - } + let (mut result, err) = + parse_builtin_commands(working_set, &lite_command, &expand_aliases_denylist); let mut result = result.expressions.remove(0); @@ -976,6 +1005,7 @@ pub fn parse_call( span(&spans[cmd_start..pos]), &spans[pos..], decl_id, + expand_aliases_denylist, ); ( Expression { @@ -992,7 +1022,8 @@ pub fn parse_call( trace!("parsing: range {:?} ", bytes); if let (Some(b'.'), Some(b'.')) = (bytes.get(0), bytes.get(1)) { trace!("-- found leading range indicator"); - let (range_expr, range_err) = parse_range(working_set, spans[0]); + let (range_expr, range_err) = + parse_range(working_set, spans[0], expand_aliases_denylist); if range_err.is_none() { trace!("-- successfully parsed range"); return (range_expr, range_err); @@ -1001,7 +1032,7 @@ pub fn parse_call( trace!("parsing: external call"); // Otherwise, try external command - parse_external_call(working_set, spans) + parse_external_call(working_set, spans, expand_aliases_denylist) } } @@ -1201,6 +1232,7 @@ pub fn parse_number(token: &[u8], span: Span) -> (Expression, Option pub fn parse_range( working_set: &mut StateWorkingSet, span: Span, + expand_aliases_denylist: &[usize], ) -> (Expression, Option) { trace!("parsing: range"); @@ -1279,7 +1311,12 @@ pub fn parse_range( None } else { let from_span = Span::new(span.start, span.start + dotdot_pos[0]); - match parse_value(working_set, from_span, &SyntaxShape::Number) { + match parse_value( + working_set, + from_span, + &SyntaxShape::Number, + expand_aliases_denylist, + ) { (expression, None) => Some(Box::new(expression)), _ => { return ( @@ -1294,7 +1331,12 @@ pub fn parse_range( None } else { let to_span = Span::new(range_op_span.end, span.end); - match parse_value(working_set, to_span, &SyntaxShape::Number) { + match parse_value( + working_set, + to_span, + &SyntaxShape::Number, + expand_aliases_denylist, + ) { (expression, None) => Some(Box::new(expression)), _ => { return ( @@ -1321,7 +1363,12 @@ pub fn parse_range( 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) { + match parse_value( + working_set, + next_span, + &SyntaxShape::Number, + expand_aliases_denylist, + ) { (expression, None) => (Some(Box::new(expression)), next_op_span), _ => { return ( @@ -1354,22 +1401,24 @@ pub fn parse_range( pub(crate) fn parse_dollar_expr( working_set: &mut StateWorkingSet, span: Span, + expand_aliases_denylist: &[usize], ) -> (Expression, Option) { trace!("parsing: dollar expression"); let contents = working_set.get_span_contents(span); if contents.starts_with(b"$\"") || contents.starts_with(b"$'") { - parse_string_interpolation(working_set, span) - } else if let (expr, None) = parse_range(working_set, span) { + parse_string_interpolation(working_set, span, 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) + parse_full_cell_path(working_set, None, span, expand_aliases_denylist) } } pub fn parse_string_interpolation( working_set: &mut StateWorkingSet, span: Span, + expand_aliases_denylist: &[usize], ) -> (Expression, Option) { #[derive(PartialEq, Eq, Debug)] enum InterpolationMode { @@ -1469,7 +1518,8 @@ pub fn parse_string_interpolation( end: b + 1, }; - let (expr, err) = parse_full_cell_path(working_set, None, span); + let (expr, err) = + parse_full_cell_path(working_set, None, span, expand_aliases_denylist); error = error.or(err); output.push(expr); } @@ -1515,7 +1565,8 @@ pub fn parse_string_interpolation( end, }; - let (expr, err) = parse_full_cell_path(working_set, None, span); + let (expr, err) = + parse_full_cell_path(working_set, None, span, expand_aliases_denylist); error = error.or(err); output.push(expr); } @@ -1692,6 +1743,7 @@ pub fn parse_full_cell_path( working_set: &mut StateWorkingSet, implicit_head: Option, span: Span, + expand_aliases_denylist: &[usize], ) -> (Expression, Option) { let full_cell_span = span; let source = working_set.get_span_contents(span); @@ -1730,7 +1782,7 @@ pub fn parse_full_cell_path( let (output, err) = lite_parse(&output); error = error.or(err); - let (output, err) = parse_block(working_set, &output, true); + let (output, err) = parse_block(working_set, &output, true, expand_aliases_denylist); error = error.or(err); let block_id = working_set.add_block(output); @@ -1748,7 +1800,8 @@ 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); + let (output, err) = + parse_table_expression(working_set, head.span, expand_aliases_denylist); error = error.or(err); tokens.next(); @@ -1756,7 +1809,7 @@ pub fn parse_full_cell_path( (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); + let (output, err) = parse_record(working_set, head.span, expand_aliases_denylist); error = error.or(err); tokens.next(); @@ -2519,6 +2572,7 @@ pub fn parse_type(_working_set: &StateWorkingSet, bytes: &[u8]) -> Type { pub fn parse_import_pattern( working_set: &mut StateWorkingSet, spans: &[Span], + expand_aliases_denylist: &[usize], ) -> (Expression, Option) { let mut error = None; @@ -2553,8 +2607,12 @@ pub fn parse_import_pattern( None, ) } else if tail.starts_with(b"[") { - let (result, err) = - parse_list_expression(working_set, *tail_span, &SyntaxShape::String); + let (result, err) = parse_list_expression( + working_set, + *tail_span, + &SyntaxShape::String, + expand_aliases_denylist, + ); error = error.or(err); let mut output = vec![]; @@ -2725,6 +2783,7 @@ pub fn expand_to_cell_path( working_set: &mut StateWorkingSet, expression: &mut Expression, var_id: VarId, + expand_aliases_denylist: &[usize], ) { if let Expression { expr: Expr::String(_), @@ -2733,7 +2792,8 @@ pub fn expand_to_cell_path( } = expression { // Re-parse the string as if it were a cell-path - let (new_expression, _err) = parse_full_cell_path(working_set, Some(var_id), *span); + let (new_expression, _err) = + parse_full_cell_path(working_set, Some(var_id), *span, expand_aliases_denylist); *expression = new_expression; } @@ -2742,9 +2802,11 @@ pub fn expand_to_cell_path( pub fn parse_row_condition( working_set: &mut StateWorkingSet, spans: &[Span], + expand_aliases_denylist: &[usize], ) -> (Expression, Option) { let var_id = working_set.add_variable(b"$it".to_vec(), span(spans), Type::Unknown); - let (expression, err) = parse_math_expression(working_set, spans, Some(var_id)); + let (expression, err) = + parse_math_expression(working_set, spans, Some(var_id), expand_aliases_denylist); let span = span(spans); let block_id = match expression.expr { @@ -2783,6 +2845,7 @@ pub fn parse_row_condition( pub fn parse_signature( working_set: &mut StateWorkingSet, span: Span, + expand_aliases_denylist: &[usize], ) -> (Expression, Option) { let bytes = working_set.get_span_contents(span); @@ -2810,7 +2873,8 @@ pub fn parse_signature( error = error.or_else(|| Some(ParseError::Unclosed("]".into(), Span { start: end, end }))); } - let (sig, err) = parse_signature_helper(working_set, Span { start, end }); + let (sig, err) = + parse_signature_helper(working_set, Span { start, end }, expand_aliases_denylist); error = error.or(err); ( @@ -2827,6 +2891,7 @@ pub fn parse_signature( pub fn parse_signature_helper( working_set: &mut StateWorkingSet, span: Span, + expand_aliases_denylist: &[usize], ) -> (Box, Option) { #[allow(clippy::enum_variant_names)] enum ParseMode { @@ -3105,8 +3170,12 @@ pub fn parse_signature_helper( } ParseMode::DefaultValueMode => { if let Some(last) = args.last_mut() { - let (expression, err) = - parse_value(working_set, span, &SyntaxShape::Any); + let (expression, err) = parse_value( + working_set, + span, + &SyntaxShape::Any, + expand_aliases_denylist, + ); error = error.or(err); //TODO check if we're replacing a custom parameter already @@ -3279,6 +3348,7 @@ pub fn parse_list_expression( working_set: &mut StateWorkingSet, span: Span, element_shape: &SyntaxShape, + expand_aliases_denylist: &[usize], ) -> (Expression, Option) { let bytes = working_set.get_span_contents(span); @@ -3314,8 +3384,13 @@ pub fn parse_list_expression( let mut spans_idx = 0; while spans_idx < arg.parts.len() { - let (arg, err) = - parse_multispan_value(working_set, &arg.parts, &mut spans_idx, element_shape); + let (arg, err) = parse_multispan_value( + working_set, + &arg.parts, + &mut spans_idx, + element_shape, + expand_aliases_denylist, + ); error = error.or(err); if let Some(ref ctype) = contained_type { @@ -3351,6 +3426,7 @@ pub fn parse_list_expression( pub fn parse_table_expression( working_set: &mut StateWorkingSet, original_span: Span, + expand_aliases_denylist: &[usize], ) -> (Expression, Option) { let bytes = working_set.get_span_contents(original_span); let mut error = None; @@ -3389,7 +3465,12 @@ pub fn parse_table_expression( ), 1 => { // List - parse_list_expression(working_set, original_span, &SyntaxShape::Any) + parse_list_expression( + working_set, + original_span, + &SyntaxShape::Any, + expand_aliases_denylist, + ) } _ => { let mut table_headers = vec![]; @@ -3398,6 +3479,7 @@ pub fn parse_table_expression( working_set, output.block[0].commands[0].parts[0], &SyntaxShape::List(Box::new(SyntaxShape::Any)), + expand_aliases_denylist, ); error = error.or(err); @@ -3415,6 +3497,7 @@ pub fn parse_table_expression( working_set, *part, &SyntaxShape::List(Box::new(SyntaxShape::Any)), + expand_aliases_denylist, ); error = error.or(err); if let Expression { @@ -3460,6 +3543,7 @@ pub fn parse_block_expression( working_set: &mut StateWorkingSet, shape: &SyntaxShape, span: Span, + expand_aliases_denylist: &[usize], ) -> (Expression, Option) { trace!("parsing: block expression"); @@ -3526,7 +3610,8 @@ pub fn parse_block_expression( start: start_point, end: end_point, }; - let (signature, err) = parse_signature_helper(working_set, signature_span); + let (signature, err) = + parse_signature_helper(working_set, signature_span, expand_aliases_denylist); error = error.or(err); (Some((signature, signature_span)), amt_to_skip) @@ -3584,7 +3669,7 @@ pub fn parse_block_expression( } } - let (mut output, err) = parse_block(working_set, &output, false); + let (mut output, err) = parse_block(working_set, &output, false, expand_aliases_denylist); error = error.or(err); if let Some(signature) = signature { @@ -3626,6 +3711,7 @@ pub fn parse_value( working_set: &mut StateWorkingSet, span: Span, shape: &SyntaxShape, + expand_aliases_denylist: &[usize], ) -> (Expression, Option) { let bytes = working_set.get_span_contents(span); @@ -3699,24 +3785,26 @@ pub fn parse_value( } match bytes[0] { - b'$' => return parse_dollar_expr(working_set, span), + b'$' => return parse_dollar_expr(working_set, span, expand_aliases_denylist), b'(' => { - if let (expr, None) = parse_range(working_set, span) { + if let (expr, None) = parse_range(working_set, span, expand_aliases_denylist) { return (expr, None); } else { - return parse_full_cell_path(working_set, None, span); + return parse_full_cell_path(working_set, None, span, expand_aliases_denylist); } } b'{' => { if !matches!(shape, SyntaxShape::Block(..)) { - if let (expr, None) = parse_full_cell_path(working_set, None, span) { + if let (expr, None) = + parse_full_cell_path(working_set, None, span, expand_aliases_denylist) + { return (expr, None); } } if matches!(shape, SyntaxShape::Block(_)) || matches!(shape, SyntaxShape::Any) { - return parse_block_expression(working_set, shape, span); + return parse_block_expression(working_set, shape, span, expand_aliases_denylist); } else if matches!(shape, SyntaxShape::Record) { - return parse_record(working_set, span); + return parse_record(working_set, span, expand_aliases_denylist); } else { return ( Expression::garbage(span), @@ -3741,7 +3829,8 @@ pub fn parse_value( match shape { SyntaxShape::Custom(shape, custom_completion) => { - let (mut expression, err) = parse_value(working_set, span, shape); + let (mut expression, err) = + parse_value(working_set, span, shape, expand_aliases_denylist); expression.custom_completion = Some(*custom_completion); (expression, err) } @@ -3750,14 +3839,14 @@ pub fn parse_value( 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), + SyntaxShape::Range => parse_range(working_set, span, expand_aliases_denylist), SyntaxShape::Filepath => parse_filepath(working_set, span), SyntaxShape::GlobPattern => parse_glob_pattern(working_set, span), SyntaxShape::String => parse_string(working_set, span), SyntaxShape::Binary => parse_binary(working_set, span), SyntaxShape::Signature => { if bytes.starts_with(b"[") { - parse_signature(working_set, span) + parse_signature(working_set, span, expand_aliases_denylist) } else { ( Expression::garbage(span), @@ -3767,7 +3856,7 @@ pub fn parse_value( } SyntaxShape::List(elem) => { if bytes.starts_with(b"[") { - parse_list_expression(working_set, span, elem) + parse_list_expression(working_set, span, elem, expand_aliases_denylist) } else { ( Expression::garbage(span), @@ -3777,7 +3866,7 @@ pub fn parse_value( } SyntaxShape::Table => { if bytes.starts_with(b"[") { - parse_table_expression(working_set, span) + parse_table_expression(working_set, span, expand_aliases_denylist) } else { ( Expression::garbage(span), @@ -3829,7 +3918,7 @@ pub fn parse_value( SyntaxShape::Any => { if bytes.starts_with(b"[") { //parse_value(working_set, span, &SyntaxShape::Table) - parse_full_cell_path(working_set, None, span) + parse_full_cell_path(working_set, None, span, expand_aliases_denylist) } else { let shapes = [ SyntaxShape::Binary, @@ -3844,7 +3933,9 @@ pub fn parse_value( SyntaxShape::String, ]; for shape in shapes.iter() { - if let (s, None) = parse_value(working_set, span, shape) { + if let (s, None) = + parse_value(working_set, span, shape, expand_aliases_denylist) + { return (s, None); } } @@ -3906,6 +3997,7 @@ pub fn parse_math_expression( working_set: &mut StateWorkingSet, spans: &[Span], lhs_row_var_id: Option, + expand_aliases_denylist: &[usize], ) -> (Expression, Option) { // As the expr_stack grows, we increase the required precedence to grow larger // If, at any time, the operator we're looking at is the same or lower precedence @@ -3923,7 +4015,12 @@ pub fn parse_math_expression( let mut last_prec = 1000000; let mut error = None; - let (lhs, err) = parse_value(working_set, spans[0], &SyntaxShape::Any); + let (lhs, err) = parse_value( + working_set, + spans[0], + &SyntaxShape::Any, + expand_aliases_denylist, + ); error = error.or(err); idx += 1; @@ -3947,7 +4044,12 @@ pub fn parse_math_expression( break; } - let (rhs, err) = parse_value(working_set, spans[idx], &SyntaxShape::Any); + let (rhs, err) = parse_value( + working_set, + spans[idx], + &SyntaxShape::Any, + expand_aliases_denylist, + ); error = error.or(err); if op_prec <= last_prec && expr_stack.len() > 1 { @@ -3965,7 +4067,7 @@ pub fn parse_math_expression( .expect("internal error: expression stack empty"); if let Some(row_var_id) = lhs_row_var_id { - expand_to_cell_path(working_set, &mut lhs, row_var_id); + expand_to_cell_path(working_set, &mut lhs, row_var_id, expand_aliases_denylist); } let (result_ty, err) = math_result_type(working_set, &mut lhs, &mut op, &mut rhs); @@ -3999,7 +4101,7 @@ pub fn parse_math_expression( .expect("internal error: expression stack empty"); if let Some(row_var_id) = lhs_row_var_id { - expand_to_cell_path(working_set, &mut lhs, row_var_id); + expand_to_cell_path(working_set, &mut lhs, row_var_id, expand_aliases_denylist); } let (result_ty, err) = math_result_type(working_set, &mut lhs, &mut op, &mut rhs); @@ -4024,7 +4126,7 @@ pub fn parse_math_expression( pub fn parse_expression( working_set: &mut StateWorkingSet, spans: &[Span], - expand_aliases: bool, + expand_aliases_denylist: &[usize], ) -> (Expression, Option) { let mut pos = 0; let mut shorthand = vec![]; @@ -4092,56 +4194,110 @@ pub fn parse_expression( .iter() .any(|x| x == bytes) { - parse_math_expression(working_set, &spans[pos..], None) + parse_math_expression(working_set, &spans[pos..], None, expand_aliases_denylist) } else { // For now, check for special parses of certain keywords match bytes { b"def" => ( - parse_call(working_set, &spans[pos..], expand_aliases, spans[0]).0, + parse_call( + working_set, + &spans[pos..], + spans[0], + expand_aliases_denylist, + ) + .0, Some(ParseError::BuiltinCommandInPipeline("def".into(), spans[0])), ), b"extern" => ( - parse_call(working_set, &spans[pos..], expand_aliases, spans[0]).0, + parse_call( + working_set, + &spans[pos..], + spans[0], + expand_aliases_denylist, + ) + .0, Some(ParseError::BuiltinCommandInPipeline( "extern".into(), spans[0], )), ), b"let" => ( - parse_call(working_set, &spans[pos..], expand_aliases, spans[0]).0, + parse_call( + working_set, + &spans[pos..], + spans[0], + expand_aliases_denylist, + ) + .0, Some(ParseError::BuiltinCommandInPipeline("let".into(), spans[0])), ), b"alias" => ( - parse_call(working_set, &spans[pos..], expand_aliases, spans[0]).0, + parse_call( + working_set, + &spans[pos..], + spans[0], + expand_aliases_denylist, + ) + .0, Some(ParseError::BuiltinCommandInPipeline( "alias".into(), spans[0], )), ), b"module" => ( - parse_call(working_set, &spans[pos..], expand_aliases, spans[0]).0, + parse_call( + working_set, + &spans[pos..], + spans[0], + expand_aliases_denylist, + ) + .0, Some(ParseError::BuiltinCommandInPipeline( "module".into(), spans[0], )), ), b"use" => ( - parse_call(working_set, &spans[pos..], expand_aliases, spans[0]).0, + parse_call( + working_set, + &spans[pos..], + spans[0], + expand_aliases_denylist, + ) + .0, Some(ParseError::BuiltinCommandInPipeline("use".into(), spans[0])), ), b"source" => ( - parse_call(working_set, &spans[pos..], expand_aliases, spans[0]).0, + parse_call( + working_set, + &spans[pos..], + spans[0], + expand_aliases_denylist, + ) + .0, Some(ParseError::BuiltinCommandInPipeline( "source".into(), spans[0], )), ), b"export" => ( - parse_call(working_set, &spans[pos..], expand_aliases, spans[0]).0, + parse_call( + working_set, + &spans[pos..], + spans[0], + expand_aliases_denylist, + ) + .0, Some(ParseError::UnexpectedKeyword("export".into(), spans[0])), ), b"hide" => ( - parse_call(working_set, &spans[pos..], expand_aliases, spans[0]).0, + parse_call( + working_set, + &spans[pos..], + spans[0], + expand_aliases_denylist, + ) + .0, Some(ParseError::BuiltinCommandInPipeline( "hide".into(), spans[0], @@ -4149,15 +4305,26 @@ pub fn parse_expression( ), #[cfg(feature = "plugin")] b"register" => ( - parse_call(working_set, &spans[pos..], expand_aliases, spans[0]).0, + parse_call( + working_set, + &spans[pos..], + spans[0], + expand_aliases_denylist, + ) + .0, Some(ParseError::BuiltinCommandInPipeline( "plugin".into(), spans[0], )), ), - b"for" => parse_for(working_set, spans), - _ => parse_call(working_set, &spans[pos..], expand_aliases, spans[0]), + b"for" => parse_for(working_set, spans, expand_aliases_denylist), + _ => parse_call( + working_set, + &spans[pos..], + spans[0], + expand_aliases_denylist, + ), } }; @@ -4240,21 +4407,22 @@ pub fn parse_variable( pub fn parse_builtin_commands( working_set: &mut StateWorkingSet, lite_command: &LiteCommand, + expand_aliases_denylist: &[usize], ) -> (Pipeline, Option) { let name = working_set.get_span_contents(lite_command.parts[0]); match name { - b"def" | b"def-env" => parse_def(working_set, lite_command), - b"extern" => parse_extern(working_set, lite_command), - b"let" => parse_let(working_set, &lite_command.parts), + b"def" | b"def-env" => parse_def(working_set, lite_command, expand_aliases_denylist), + b"extern" => parse_extern(working_set, lite_command, expand_aliases_denylist), + b"let" => parse_let(working_set, &lite_command.parts, expand_aliases_denylist), b"for" => { - let (expr, err) = parse_for(working_set, &lite_command.parts); + let (expr, err) = parse_for(working_set, &lite_command.parts, expand_aliases_denylist); (Pipeline::from_vec(vec![expr]), err) } - b"alias" => parse_alias(working_set, &lite_command.parts), - b"module" => parse_module(working_set, &lite_command.parts), - b"use" => parse_use(working_set, &lite_command.parts), - b"source" => parse_source(working_set, &lite_command.parts), + b"alias" => parse_alias(working_set, &lite_command.parts, expand_aliases_denylist), + b"module" => parse_module(working_set, &lite_command.parts, expand_aliases_denylist), + b"use" => parse_use(working_set, &lite_command.parts, expand_aliases_denylist), + b"source" => parse_source(working_set, &lite_command.parts, expand_aliases_denylist), b"export" => ( garbage_pipeline(&lite_command.parts), Some(ParseError::UnexpectedKeyword( @@ -4262,11 +4430,12 @@ pub fn parse_builtin_commands( lite_command.parts[0], )), ), - b"hide" => parse_hide(working_set, &lite_command.parts), + b"hide" => parse_hide(working_set, &lite_command.parts, expand_aliases_denylist), #[cfg(feature = "plugin")] - b"register" => parse_register(working_set, &lite_command.parts), + b"register" => parse_register(working_set, &lite_command.parts, expand_aliases_denylist), _ => { - let (expr, err) = parse_expression(working_set, &lite_command.parts, true); + let (expr, err) = + parse_expression(working_set, &lite_command.parts, expand_aliases_denylist); (Pipeline::from_vec(vec![expr]), err) } } @@ -4275,6 +4444,7 @@ pub fn parse_builtin_commands( pub fn parse_record( working_set: &mut StateWorkingSet, span: Span, + expand_aliases_denylist: &[usize], ) -> (Expression, Option) { let bytes = working_set.get_span_contents(span); @@ -4312,7 +4482,12 @@ pub fn parse_record( let mut idx = 0; while idx < tokens.len() { - let (field, err) = parse_value(working_set, tokens[idx].span, &SyntaxShape::Any); + let (field, err) = parse_value( + working_set, + tokens[idx].span, + &SyntaxShape::Any, + expand_aliases_denylist, + ); error = error.or(err); idx += 1; @@ -4331,7 +4506,12 @@ pub fn parse_record( Some(ParseError::Expected("record".into(), span)), ); } - let (value, err) = parse_value(working_set, tokens[idx].span, &SyntaxShape::Any); + let (value, err) = parse_value( + working_set, + tokens[idx].span, + &SyntaxShape::Any, + expand_aliases_denylist, + ); error = error.or(err); idx += 1; @@ -4353,6 +4533,7 @@ pub fn parse_block( working_set: &mut StateWorkingSet, lite_block: &LiteBlock, scoped: bool, + expand_aliases_denylist: &[usize], ) -> (Block, Option) { trace!("parsing block: {:?}", lite_block); @@ -4366,7 +4547,11 @@ pub fn parse_block( // that share the same block can see each other for pipeline in &lite_block.block { if pipeline.commands.len() == 1 { - if let Some(err) = parse_def_predecl(working_set, &pipeline.commands[0].parts) { + if let Some(err) = parse_def_predecl( + working_set, + &pipeline.commands[0].parts, + expand_aliases_denylist, + ) { error = error.or(Some(err)); } } @@ -4382,7 +4567,8 @@ pub fn parse_block( .commands .iter() .map(|command| { - let (expr, err) = parse_expression(working_set, &command.parts, true); + let (expr, err) = + parse_expression(working_set, &command.parts, expand_aliases_denylist); if error.is_none() { error = err; @@ -4402,8 +4588,11 @@ pub fn parse_block( expressions: output, } } else { - let (mut pipeline, err) = - parse_builtin_commands(working_set, &pipeline.commands[0]); + let (mut pipeline, err) = parse_builtin_commands( + working_set, + &pipeline.commands[0], + expand_aliases_denylist, + ); if idx == 0 { if let Some(let_decl_id) = working_set.find_decl(b"let") { @@ -4776,6 +4965,7 @@ pub fn parse( fname: Option<&str>, contents: &[u8], scoped: bool, + expand_aliases_denylist: &[usize], ) -> (Block, Option) { trace!("starting top-level parse"); @@ -4796,7 +4986,7 @@ pub fn parse( let (output, err) = lite_parse(&output); error = error.or(err); - let (mut output, err) = parse_block(working_set, &output, scoped); + let (mut output, err) = parse_block(working_set, &output, scoped, expand_aliases_denylist); error = error.or(err); let mut seen = vec![]; diff --git a/crates/nu-parser/tests/test_parser.rs b/crates/nu-parser/tests/test_parser.rs index 6960d8a077..82976a7bf3 100644 --- a/crates/nu-parser/tests/test_parser.rs +++ b/crates/nu-parser/tests/test_parser.rs @@ -46,7 +46,7 @@ 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, err) = parse(&mut working_set, None, b"3", true, &[]); assert!(err.is_none()); assert!(block.len() == 1); @@ -69,7 +69,7 @@ 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, err) = parse(&mut working_set, None, b"foo", true, &[]); assert!(err.is_none()); assert!(block.len() == 1); @@ -94,7 +94,7 @@ 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); + let (_, err) = parse(&mut working_set, None, b"foo --jazz", true, &[]); assert!(matches!(err, Some(ParseError::MissingFlagParam(..)))); } @@ -106,7 +106,7 @@ 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); + let (_, err) = parse(&mut working_set, None, b"foo -j", true, &[]); assert!(matches!(err, Some(ParseError::MissingFlagParam(..)))); } @@ -119,7 +119,7 @@ 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); + let (_, err) = parse(&mut working_set, None, b"foo -mj", true, &[]); assert!(matches!( err, Some(ParseError::ShortFlagBatchCantTakeArg(..)) @@ -133,7 +133,7 @@ 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); + let (_, err) = parse(&mut working_set, None, b"foo -mj", true, &[]); assert!(matches!(err, Some(ParseError::UnknownFlag(..)))); } @@ -144,7 +144,7 @@ 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); + let (_, err) = parse(&mut working_set, None, b"foo -j 100", true, &[]); assert!(matches!(err, Some(ParseError::ExtraPositional(..)))); } @@ -155,7 +155,7 @@ 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); + let (_, err) = parse(&mut working_set, None, b"foo", true, &[]); assert!(matches!(err, Some(ParseError::MissingPositional(..)))); } @@ -166,7 +166,7 @@ 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); + let (_, err) = parse(&mut working_set, None, b"foo", true, &[]); assert!(matches!(err, Some(ParseError::MissingRequiredFlag(..)))); } @@ -174,7 +174,7 @@ pub fn parse_call_missing_req_flag() { fn test_nothing_comparisson_eq() { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, b"2 == $nothing", true); + let (block, err) = parse(&mut working_set, None, b"2 == $nothing", true, &[]); assert!(err.is_none()); assert!(block.len() == 1); @@ -194,7 +194,7 @@ fn test_nothing_comparisson_eq() { fn test_nothing_comparisson_neq() { let engine_state = EngineState::new(); let mut working_set = StateWorkingSet::new(&engine_state); - let (block, err) = parse(&mut working_set, None, b"2 != $nothing", true); + let (block, err) = parse(&mut working_set, None, b"2 != $nothing", true, &[]); assert!(err.is_none()); assert!(block.len() == 1); @@ -219,7 +219,7 @@ 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, err) = parse(&mut working_set, None, b"0..10", true, &[]); assert!(err.is_none()); assert!(block.len() == 1); @@ -248,7 +248,7 @@ 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, err) = parse(&mut working_set, None, b"0..<10", true, &[]); assert!(err.is_none()); assert!(block.len() == 1); @@ -277,7 +277,7 @@ 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, err) = parse(&mut working_set, None, b"10..0", true, &[]); assert!(err.is_none()); assert!(block.len() == 1); @@ -306,7 +306,7 @@ 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, err) = parse(&mut working_set, None, b"(3 - 3)..<(8 + 2)", true, &[]); assert!(err.is_none()); assert!(block.len() == 1); @@ -337,7 +337,7 @@ 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, err) = parse(&mut working_set, None, b"let a = 2; $a..10", true, &[]); assert!(err.is_none()); assert!(block.len() == 2); @@ -368,7 +368,13 @@ mod range { working_set.add_decl(Box::new(Let)); - let (block, err) = parse(&mut working_set, None, b"let a = 2; $a..<($a + 10)", true); + let (block, err) = parse( + &mut working_set, + None, + b"let a = 2; $a..<($a + 10)", + true, + &[], + ); assert!(err.is_none()); assert!(block.len() == 2); @@ -397,7 +403,7 @@ 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, err) = parse(&mut working_set, None, b"0..", true, &[]); assert!(err.is_none()); assert!(block.len() == 1); @@ -426,7 +432,7 @@ 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, err) = parse(&mut working_set, None, b"..10", true, &[]); assert!(err.is_none()); assert!(block.len() == 1); @@ -455,7 +461,7 @@ 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, err) = parse(&mut working_set, None, b"-10..-3", true, &[]); assert!(err.is_none()); assert!(block.len() == 1); @@ -484,7 +490,7 @@ 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, err) = parse(&mut working_set, None, b"2.0..4.0..10.0", true, &[]); assert!(err.is_none()); assert!(block.len() == 1); @@ -513,7 +519,7 @@ 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); + let (_, err) = parse(&mut working_set, None, b"(0)..\"a\"", true, &[]); assert!(err.is_some()); } diff --git a/crates/nu-protocol/src/engine/engine_state.rs b/crates/nu-protocol/src/engine/engine_state.rs index 97eefe2dfb..c5dd87c1d0 100644 --- a/crates/nu-protocol/src/engine/engine_state.rs +++ b/crates/nu-protocol/src/engine/engine_state.rs @@ -844,10 +844,44 @@ impl<'a> StateWorkingSet<'a> { None } + pub fn use_alias(&mut self, alias_id: &AliasId) { + let mut visibility: Visibility = Visibility::new(); + + // Since we can mutate scope frames in delta, remove the id directly + for scope in self.delta.scope.iter_mut().rev() { + visibility.append(&scope.visibility); + + if !visibility.is_alias_id_visible(alias_id) { + // Hide alias only if it's not already hidden + scope.visibility.use_alias_id(alias_id); + + return; + } + } + + // We cannot mutate the permanent state => store the information in the current scope frame + let last_scope_frame = self + .delta + .scope + .last_mut() + .expect("internal error: missing required scope frame"); + + for scope in self.permanent_state.scope.iter().rev() { + visibility.append(&scope.visibility); + + if !visibility.is_alias_id_visible(alias_id) { + // Hide alias only if it's not already hidden + last_scope_frame.visibility.use_alias_id(alias_id); + + return; + } + } + } + pub fn hide_alias(&mut self, name: &[u8]) -> Option { let mut visibility: Visibility = Visibility::new(); - // // Since we can mutate scope frames in delta, remove the id directly + // Since we can mutate scope frames in delta, remove the id directly for scope in self.delta.scope.iter_mut().rev() { visibility.append(&scope.visibility); diff --git a/src/main.rs b/src/main.rs index 82c4c43397..d61c723208 100644 --- a/src/main.rs +++ b/src/main.rs @@ -279,7 +279,13 @@ fn parse_commandline_args( let mut working_set = StateWorkingSet::new(engine_state); working_set.add_decl(Box::new(Nu)); - let (output, err) = parse(&mut working_set, None, commandline_args.as_bytes(), false); + let (output, err) = parse( + &mut working_set, + None, + commandline_args.as_bytes(), + false, + &[], + ); if let Some(err) = err { report_error(&working_set, &err); diff --git a/tests/shell/pipeline/commands/internal.rs b/tests/shell/pipeline/commands/internal.rs index 3c913d31d7..27b1944cf3 100644 --- a/tests/shell/pipeline/commands/internal.rs +++ b/tests/shell/pipeline/commands/internal.rs @@ -581,6 +581,17 @@ fn block_params_override() { assert!(actual.err.contains("variable not found")); } +#[test] +fn alias_reuse() { + let actual = nu!( + cwd: ".", + r#"alias foo = echo bob; foo; foo"# + ); + + assert!(actual.out.contains("bob")); + assert!(actual.err.is_empty()); +} + #[test] fn block_params_override_correct() { let actual = nu!(