diff --git a/crates/nu-command/tests/commands/use_.rs b/crates/nu-command/tests/commands/use_.rs index 1d364379d..9d98e34fa 100644 --- a/crates/nu-command/tests/commands/use_.rs +++ b/crates/nu-command/tests/commands/use_.rs @@ -296,3 +296,18 @@ fn use_main_not_exported() { assert!(actual.err.contains("external_command")); } + +#[test] +fn use_sub_subname_error_if_not_from_submodule() { + let inp = r#"module spam { export def foo [] {}; export def bar [] {} }; use spam foo bar"#; + let actual = nu!(inp); + assert!(actual.err.contains("try `use [, ]`")) +} + +#[test] +fn can_use_sub_subname_from_submodule() { + let inp = + r#"module spam { export module foo { export def bar [] {"bar"} } }; use spam foo bar; bar"#; + let actual = nu!(inp); + assert_eq!(actual.out, "bar") +} diff --git a/crates/nu-protocol/src/ast/import_pattern.rs b/crates/nu-protocol/src/ast/import_pattern.rs index 6a9553fa0..2af08087a 100644 --- a/crates/nu-protocol/src/ast/import_pattern.rs +++ b/crates/nu-protocol/src/ast/import_pattern.rs @@ -10,6 +10,22 @@ pub enum ImportPatternMember { List { names: Vec<(Vec, Span)> }, } +impl ImportPatternMember { + pub fn span(&self) -> Span { + let mut spans = vec![]; + match self { + ImportPatternMember::Glob { span } => spans.push(*span), + ImportPatternMember::Name { name: _, span } => spans.push(*span), + ImportPatternMember::List { names } => { + for (_, span) in names { + spans.push(*span); + } + } + } + span(&spans) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct ImportPatternHead { pub name: Vec, @@ -46,15 +62,7 @@ impl ImportPattern { let mut spans = vec![self.head.span]; for member in &self.members { - match member { - ImportPatternMember::Glob { span } => spans.push(*span), - ImportPatternMember::Name { name: _, span } => spans.push(*span), - ImportPatternMember::List { names } => { - for (_, span) in names { - spans.push(*span); - } - } - } + spans.push(member.span()); } span(&spans) diff --git a/crates/nu-protocol/src/module.rs b/crates/nu-protocol/src/module.rs index f45dd55d9..ebb6f5621 100644 --- a/crates/nu-protocol/src/module.rs +++ b/crates/nu-protocol/src/module.rs @@ -157,6 +157,17 @@ impl Module { match head { ImportPatternMember::Name { name, span } => { + // raise errors if user wants to do something like this: + // `use a b c`: but b is not a sub-module of a. + let errors = if !rest.is_empty() && self.submodules.get(name).is_none() { + vec![ParseError::WrongImportPattern( + format!("Trying to import something but the parent `{}` is not a module, maybe you want to try `use [, ]`", String::from_utf8_lossy(name)), + rest[0].span(), + )] + } else { + vec![] + }; + if name == b"main" { if let Some(main_decl_id) = self.main { ( @@ -165,7 +176,7 @@ impl Module { vec![], vec![], ), - vec![], + errors, ) } else { ( @@ -176,7 +187,7 @@ impl Module { } else if let Some(decl_id) = self.decls.get(name) { ( ResolvedImportPattern::new(vec![(name.clone(), *decl_id)], vec![], vec![]), - vec![], + errors, ) } else if let Some(var_id) = self.constants.get(name) { match working_set.get_constant(*var_id) { @@ -186,7 +197,7 @@ impl Module { vec![], vec![(name.clone(), const_val.clone())], ), - vec![], + errors, ), Err(err) => ( ResolvedImportPattern::new(vec![], vec![], vec![]), diff --git a/crates/nu-protocol/src/parse_error.rs b/crates/nu-protocol/src/parse_error.rs index 94fd9be64..d5acc47a8 100644 --- a/crates/nu-protocol/src/parse_error.rs +++ b/crates/nu-protocol/src/parse_error.rs @@ -425,7 +425,7 @@ pub enum ParseError { MissingImportPattern(#[label = "needs an import pattern"] Span), #[error("Wrong import pattern structure.")] - #[diagnostic(code(nu::parser::missing_import_pattern))] + #[diagnostic(code(nu::parser::wrong_import_pattern))] WrongImportPattern(String, #[label = "{0}"] Span), #[error("Export not found.")]