Fix panic on double def; Tests; Double def error

* Fixes a panic with defining two commands with the same name caused by
  declaration not found after predeclaration.
* Adds a new error if a custom command is defined more than once in one
  block.
* Add some tests
This commit is contained in:
Jakub Žádník 2021-10-01 23:16:27 +03:00
parent 2af8116f50
commit 25b05dec9e
4 changed files with 18 additions and 12 deletions

View file

@ -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),

View file

@ -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<ParseError> {
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);

View file

@ -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()

View file

@ -311,17 +311,13 @@ impl<'a> StateWorkingSet<'a> {
decl_id
}
pub fn add_predecl(&mut self, decl: Box<dyn Command>) {
pub fn add_predecl(&mut self, decl: Box<dyn Command>) -> Option<DeclId> {
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<DeclId> {
self.delta.predecls.get(name).copied()
self.delta.predecls.insert(name, decl_id)
}
pub fn merge_predecl(&mut self, name: &[u8]) -> Option<DeclId> {