Error on use path item1 item2, if item1 is not a module (#11183)

# Description
Fixes: #11143

# User-Facing Changes
Take the following as example:
```nushell
module foo { export def bar [] {}; export def baz [] {} }
```

`use foo bar baz` will be error:
```
❯ use foo c d
Error: nu::parser::wrong_import_pattern

  × Wrong import pattern structure.
   ╭─[entry #2:1:1]
 1 │ use foo c d
   ·           ┬
   ·           ╰── Trying to import something but the parent `c` is not a module, maybe you want to try `use <module> [<name1>, <name2>]`
   ╰────
```

# Tests + Formatting
Done
This commit is contained in:
WindSoilder 2023-12-05 18:38:45 +08:00 committed by GitHub
parent 2ffe30ecf0
commit fb3350ebc3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 47 additions and 13 deletions

View file

@ -296,3 +296,18 @@ fn use_main_not_exported() {
assert!(actual.err.contains("external_command")); 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 <module> [<name1>, <name2>]`"))
}
#[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")
}

View file

@ -10,6 +10,22 @@ pub enum ImportPatternMember {
List { names: Vec<(Vec<u8>, Span)> }, List { names: Vec<(Vec<u8>, 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)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ImportPatternHead { pub struct ImportPatternHead {
pub name: Vec<u8>, pub name: Vec<u8>,
@ -46,15 +62,7 @@ impl ImportPattern {
let mut spans = vec![self.head.span]; let mut spans = vec![self.head.span];
for member in &self.members { for member in &self.members {
match member { spans.push(member.span());
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) span(&spans)

View file

@ -157,6 +157,17 @@ impl Module {
match head { match head {
ImportPatternMember::Name { name, span } => { 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 <module> [<name1>, <name2>]`", String::from_utf8_lossy(name)),
rest[0].span(),
)]
} else {
vec![]
};
if name == b"main" { if name == b"main" {
if let Some(main_decl_id) = self.main { if let Some(main_decl_id) = self.main {
( (
@ -165,7 +176,7 @@ impl Module {
vec![], vec![],
vec![], vec![],
), ),
vec![], errors,
) )
} else { } else {
( (
@ -176,7 +187,7 @@ impl Module {
} else if let Some(decl_id) = self.decls.get(name) { } else if let Some(decl_id) = self.decls.get(name) {
( (
ResolvedImportPattern::new(vec![(name.clone(), *decl_id)], vec![], vec![]), ResolvedImportPattern::new(vec![(name.clone(), *decl_id)], vec![], vec![]),
vec![], errors,
) )
} else if let Some(var_id) = self.constants.get(name) { } else if let Some(var_id) = self.constants.get(name) {
match working_set.get_constant(*var_id) { match working_set.get_constant(*var_id) {
@ -186,7 +197,7 @@ impl Module {
vec![], vec![],
vec![(name.clone(), const_val.clone())], vec![(name.clone(), const_val.clone())],
), ),
vec![], errors,
), ),
Err(err) => ( Err(err) => (
ResolvedImportPattern::new(vec![], vec![], vec![]), ResolvedImportPattern::new(vec![], vec![], vec![]),

View file

@ -425,7 +425,7 @@ pub enum ParseError {
MissingImportPattern(#[label = "needs an import pattern"] Span), MissingImportPattern(#[label = "needs an import pattern"] Span),
#[error("Wrong import pattern structure.")] #[error("Wrong import pattern structure.")]
#[diagnostic(code(nu::parser::missing_import_pattern))] #[diagnostic(code(nu::parser::wrong_import_pattern))]
WrongImportPattern(String, #[label = "{0}"] Span), WrongImportPattern(String, #[label = "{0}"] Span),
#[error("Export not found.")] #[error("Export not found.")]