diff --git a/crates/nu-parser/src/errors.rs b/crates/nu-parser/src/errors.rs index f55fed2da7..444873d1ae 100644 --- a/crates/nu-parser/src/errors.rs +++ b/crates/nu-parser/src/errors.rs @@ -77,6 +77,10 @@ pub enum ParseError { #[diagnostic(code(nu::parser::module_not_found), url(docsrs))] ModuleNotFound(#[label = "module not found"] Span), + #[error("Duplicate command definition within a block.")] + #[diagnostic(code(nu::parser::duplicate_command_def), url(docsrs))] + DuplicateCommandDef(#[label = "defined more than once"] Span), + #[error("Unknown command.")] #[diagnostic( code(nu::parser::unknown_command), diff --git a/crates/nu-parser/src/parse_keywords.rs b/crates/nu-parser/src/parse_keywords.rs index 3195acd785..6eb40daad0 100644 --- a/crates/nu-parser/src/parse_keywords.rs +++ b/crates/nu-parser/src/parse_keywords.rs @@ -13,7 +13,7 @@ use crate::{ ParseError, }; -pub fn parse_def_predecl(working_set: &mut StateWorkingSet, spans: &[Span]) { +pub fn parse_def_predecl(working_set: &mut StateWorkingSet, spans: &[Span]) -> Option { let name = working_set.get_span_contents(spans[0]); // handle "export def" same as "def" @@ -42,9 +42,13 @@ pub fn parse_def_predecl(working_set: &mut StateWorkingSet, spans: &[Span]) { signature.name = name; let decl = signature.predeclare(); - working_set.add_predecl(decl); + if working_set.add_predecl(decl).is_some() { + return Some(ParseError::DuplicateCommandDef(spans[1])); + } } } + + None } pub fn parse_def( @@ -98,7 +102,7 @@ pub fn parse_def( (&name, signature, block_id) { let decl_id = working_set - .find_predecl(name.as_bytes()) + .find_decl(name.as_bytes()) .expect("internal error: predeclaration failed to add definition"); let declaration = working_set.get_decl_mut(decl_id); diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index d3464869f4..79213fdf7b 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -2606,16 +2606,18 @@ pub fn parse_block( working_set.enter_scope(); } + let mut error = None; + // Pre-declare any definition so that definitions // that share the same block can see each other for pipeline in &lite_block.block { if pipeline.commands.len() == 1 { - parse_def_predecl(working_set, &pipeline.commands[0].parts); + if let Some(err) = parse_def_predecl(working_set, &pipeline.commands[0].parts) { + error = error.or(Some(err)); + } } } - let mut error = None; - let block: Block = lite_block .block .iter() diff --git a/crates/nu-protocol/src/engine/engine_state.rs b/crates/nu-protocol/src/engine/engine_state.rs index e1dcb7024d..0653ed73c9 100644 --- a/crates/nu-protocol/src/engine/engine_state.rs +++ b/crates/nu-protocol/src/engine/engine_state.rs @@ -311,17 +311,13 @@ impl<'a> StateWorkingSet<'a> { decl_id } - pub fn add_predecl(&mut self, decl: Box) { + pub fn add_predecl(&mut self, decl: Box) -> Option { let name = decl.name().as_bytes().to_vec(); self.delta.decls.push(decl); let decl_id = self.num_decls() - 1; - self.delta.predecls.insert(name, decl_id); - } - - pub fn find_predecl(&mut self, name: &[u8]) -> Option { - self.delta.predecls.get(name).copied() + self.delta.predecls.insert(name, decl_id) } pub fn merge_predecl(&mut self, name: &[u8]) -> Option {