From b7ed8601084e58e6919d62c3b32d81c1766ce576 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 7 Jun 2022 00:13:50 -0700 Subject: [PATCH] recover `import` instead of `use` in item --- compiler/rustc_parse/src/parser/item.rs | 69 +++++++++++++------ .../did_you_mean/use_instead_of_import.fixed | 15 ++++ .../ui/did_you_mean/use_instead_of_import.rs | 15 ++++ .../did_you_mean/use_instead_of_import.stderr | 14 ++++ 4 files changed, 93 insertions(+), 20 deletions(-) create mode 100644 src/test/ui/did_you_mean/use_instead_of_import.fixed create mode 100644 src/test/ui/did_you_mean/use_instead_of_import.rs create mode 100644 src/test/ui/did_you_mean/use_instead_of_import.stderr diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 6720399aacb..48c3c467bec 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -204,25 +204,7 @@ impl<'a> Parser<'a> { let mut def = || mem::replace(def, Defaultness::Final); let info = if self.eat_keyword(kw::Use) { - // USE ITEM - let tree = self.parse_use_tree()?; - - // If wildcard or glob-like brace syntax doesn't have `;`, - // the user may not know `*` or `{}` should be the last. - if let Err(mut e) = self.expect_semi() { - match tree.kind { - UseTreeKind::Glob => { - e.note("the wildcard token must be last on the path"); - } - UseTreeKind::Nested(..) => { - e.note("glob-like brace syntax must be last on the path"); - } - _ => (), - } - return Err(e); - } - - (Ident::empty(), ItemKind::Use(tree)) + self.parse_use_item()? } else if self.check_fn_front_matter(def_final) { // FUNCTION ITEM let (ident, sig, generics, body) = self.parse_fn(attrs, fn_parse_mode, lo, vis)?; @@ -288,7 +270,12 @@ impl<'a> Parser<'a> { } else if let IsMacroRulesItem::Yes { has_bang } = self.is_macro_rules_item() { // MACRO_RULES ITEM self.parse_item_macro_rules(vis, has_bang)? - } else if vis.kind.is_pub() && self.isnt_macro_invocation() { + } else if self.isnt_macro_invocation() + && (self.token.is_ident_named(Symbol::intern("import")) + || self.token.is_ident_named(Symbol::intern("using"))) + { + return self.recover_import_as_use(); + } else if self.isnt_macro_invocation() && vis.kind.is_pub() { self.recover_missing_kw_before_item()?; return Ok(None); } else if macros_allowed && self.check_path() { @@ -300,6 +287,48 @@ impl<'a> Parser<'a> { Ok(Some(info)) } + fn recover_import_as_use(&mut self) -> PResult<'a, Option<(Ident, ItemKind)>> { + let span = self.token.span; + let token_name = super::token_descr(&self.token); + let snapshot = self.create_snapshot_for_diagnostic(); + self.bump(); + match self.parse_use_item() { + Ok(u) => { + self.struct_span_err(span, format!("expected item, found {token_name}")) + .span_suggestion_short( + span, + "items are imported using the `use` keyword", + "use".to_owned(), + Applicability::MachineApplicable, + ) + .emit(); + Ok(Some(u)) + } + Err(e) => { + e.cancel(); + self.restore_snapshot(snapshot); + Ok(None) + } + } + } + + fn parse_use_item(&mut self) -> PResult<'a, (Ident, ItemKind)> { + let tree = self.parse_use_tree()?; + if let Err(mut e) = self.expect_semi() { + match tree.kind { + UseTreeKind::Glob => { + e.note("the wildcard token must be last on the path"); + } + UseTreeKind::Nested(..) => { + e.note("glob-like brace syntax must be last on the path"); + } + _ => (), + } + return Err(e); + } + Ok((Ident::empty(), ItemKind::Use(tree))) + } + /// When parsing a statement, would the start of a path be an item? pub(super) fn is_path_start_item(&mut self) -> bool { self.is_kw_followed_by_ident(kw::Union) // no: `union::b`, yes: `union U { .. }` diff --git a/src/test/ui/did_you_mean/use_instead_of_import.fixed b/src/test/ui/did_you_mean/use_instead_of_import.fixed new file mode 100644 index 00000000000..87d453e1565 --- /dev/null +++ b/src/test/ui/did_you_mean/use_instead_of_import.fixed @@ -0,0 +1,15 @@ +// run-rustfix + +use std::{ + //~^ ERROR expected item, found `import` + io::Write, + rc::Rc, +}; + +pub use std::io; +//~^ ERROR expected item, found `using` + +fn main() { + let x = Rc::new(1); + let _ = write!(io::stdout(), "{:?}", x); +} diff --git a/src/test/ui/did_you_mean/use_instead_of_import.rs b/src/test/ui/did_you_mean/use_instead_of_import.rs new file mode 100644 index 00000000000..59e83732328 --- /dev/null +++ b/src/test/ui/did_you_mean/use_instead_of_import.rs @@ -0,0 +1,15 @@ +// run-rustfix + +import std::{ + //~^ ERROR expected item, found `import` + io::Write, + rc::Rc, +}; + +pub using std::io; +//~^ ERROR expected item, found `using` + +fn main() { + let x = Rc::new(1); + let _ = write!(io::stdout(), "{:?}", x); +} diff --git a/src/test/ui/did_you_mean/use_instead_of_import.stderr b/src/test/ui/did_you_mean/use_instead_of_import.stderr new file mode 100644 index 00000000000..b22954af80f --- /dev/null +++ b/src/test/ui/did_you_mean/use_instead_of_import.stderr @@ -0,0 +1,14 @@ +error: expected item, found `import` + --> $DIR/use_instead_of_import.rs:3:1 + | +LL | import std::{ + | ^^^^^^ help: items are imported using the `use` keyword + +error: expected item, found `using` + --> $DIR/use_instead_of_import.rs:9:5 + | +LL | pub using std::io; + | ^^^^^ help: items are imported using the `use` keyword + +error: aborting due to 2 previous errors +