diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 3d46415507def..fb666550e9302 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -661,11 +661,11 @@ impl TokenStream { if attr_style == AttrStyle::Inner { vec![ TokenTree::token_joint(token::Pound, span), - TokenTree::token_joint_hidden(token::Not, span), + TokenTree::token_alone(token::Not, span), body, ] } else { - vec![TokenTree::token_joint_hidden(token::Pound, span), body] + vec![TokenTree::token_alone(token::Pound, span), body] } } } diff --git a/compiler/rustc_ast_pretty/src/pprust/mod.rs b/compiler/rustc_ast_pretty/src/pprust/mod.rs index 83b7e13905aee..cbb18cc119486 100644 --- a/compiler/rustc_ast_pretty/src/pprust/mod.rs +++ b/compiler/rustc_ast_pretty/src/pprust/mod.rs @@ -76,6 +76,14 @@ pub fn attribute_to_string(attr: &ast::Attribute) -> String { State::new().attribute_to_string(attr) } +pub fn local_to_string(local: &ast::Local) -> String { + State::new().local_to_string(local) +} + +pub fn stmt_to_string(stmt: &ast::Stmt) -> String { + State::new().stmt_to_string(stmt) +} + pub fn to_string(f: impl FnOnce(&mut State<'_>)) -> String { State::to_string(f) } diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index f02fe4cf0a728..7e8ed3f23b1b8 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -2075,4 +2075,8 @@ impl<'a> State<'a> { pub(crate) fn attribute_to_string(&self, attr: &ast::Attribute) -> String { Self::to_string(|s| s.print_attribute(attr)) } + + pub(crate) fn local_to_string(&self, local: &ast::Local) -> String { + Self::to_string(|s| s.print_local_decl(local)) + } } diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 62c8f9f5dacb1..38a99c2ec18c0 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -15,7 +15,7 @@ use std::ops::Range; /// for the attribute target. This allows us to perform cfg-expansion on /// a token stream before we invoke a derive proc-macro. /// -/// This wrapper prevents direct access to the underlying `ast::AttrVec>`. +/// This wrapper prevents direct access to the underlying `ast::AttrVec`. /// Parsing code can only get access to the underlying attributes /// by passing an `AttrWrapper` to `collect_tokens_trailing_tokens`. /// This makes it difficult to accidentally construct an AST node diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 53757c38e8b04..cff72a88640e4 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1,7 +1,8 @@ use super::diagnostics::{dummy_arg, ConsumeClosingDelim}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{ - AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, Trailing, TrailingToken, + AttemptLocalParseRecovery, AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, + Trailing, TrailingToken, }; use crate::errors::{self, MacroExpandsToAdtField}; use crate::fluent_generated as fluent; @@ -40,6 +41,7 @@ impl<'a> Parser<'a> { let mod_kind = if self.eat(&token::Semi) { ModKind::Unloaded } else { + // mod blocks are parsed recursively self.expect(&token::OpenDelim(Delimiter::Brace))?; let (inner_attrs, items, inner_span) = self.parse_mod(&token::CloseDelim(Delimiter::Brace))?; @@ -72,21 +74,10 @@ impl<'a> Parser<'a> { } if !self.eat(term) { - let token_str = super::token_descr(&self.token); + // The last token should be `term`: either EOF or `}`. If it's not that means that we've had an error + // parsing an item if !self.maybe_consume_incorrect_semicolon(items.last().map(|x| &**x)) { - let msg = format!("expected item, found {token_str}"); - let mut err = self.dcx().struct_span_err(self.token.span, msg); - let span = self.token.span; - if self.is_kw_followed_by_ident(kw::Let) { - err.span_label( - span, - "consider using `const` or `static` instead of `let` for global variables", - ); - } else { - err.span_label(span, "expected item") - .note("for a full list of items that can appear in modules, see "); - }; - return Err(err); + self.fallback_incorrect_item()?; } } @@ -94,6 +85,73 @@ impl<'a> Parser<'a> { let mod_spans = ModSpans { inner_span: lo.to(self.prev_token.span), inject_use_span }; Ok((attrs, items, mod_spans)) } + + fn fallback_incorrect_item(&mut self) -> PResult<'a, ()> { + let token_str = super::token_descr(&self.token); + let mut err = self + .dcx() + .struct_span_err(self.token.span, format!("expected item, found {token_str}")); + + let stmt = match self.parse_full_stmt(AttemptLocalParseRecovery::No) { + Ok(stmt) => stmt, + Err(e) => { + // We don't really care about an error parsing this statement. + e.cancel(); + return Err(err); + } + }; + + if let Some(stmt) = stmt { + let span = stmt.span; + match &stmt.kind { + StmtKind::Let(_) => { + err.span_label(span, "unexpected `let` binding outside of a function") + .help(format!("consider using `const` or `static` instead of `let` for global variables, or put it inside of a function: fn foo() {{ {} }}", + pprust::stmt_to_string(&stmt))); + } + StmtKind::Semi(expr) | StmtKind::Expr(expr) => { + match expr.kind { + ExprKind::Err(_) => { + err.span_label(expr.span, "error parsing this expression"); + } + ExprKind::Array(_) => { + // If it's an array it's possible that it's an attribute + // that is missing a `#`. + err.span_label(span, "unexpected array") + .help( + format!("If you wanted an array consider putting it inside a function: fn foo() {{ {} }}", + pprust::expr_to_string(expr))) + .span_help(span.shrink_to_lo(), "If you wanted an attribute consider adding `#`"); + } + ExprKind::Unary(UnOp::Not, _) => { + // This might be an inner attribute wihtout #... + err.span_label(span, "unexpected expression"); + } + + _ => { + err.span_label(span, "unexpected expression").help(format!( + "consider putting it inside a function: fn foo() {{ {} }}", + pprust::expr_to_string(expr) + )); + } + }; + } + StmtKind::MacCall(_) => { + err.span_label(span, "unexpected macro call at this position"); + } + StmtKind::Item(_) => { + err.span_label(span, "unexpected item at this position"); + } + StmtKind::Empty => { + unreachable!("should have been handled by maybe_consume_incorrect_semicolon"); + } + }; + } + + err.note("for a full list of items that can appear in modules, see "); + + return Err(err); + } } pub(super) type ItemInfo = (Ident, ItemKind); @@ -2289,10 +2347,18 @@ impl<'a> Parser<'a> { vis: &Visibility, case: Case, ) -> PResult<'a, (Ident, FnSig, Generics, Option>)> { + // Let's have an example function + // const unsafe fn <'a, T> foo(arg: &'a T) -> i32 + // where T: Send { + // 1+1 + // } + let fn_span = self.token.span; - let header = self.parse_fn_front_matter(vis, case)?; // `const ... fn` + let header = self.parse_fn_front_matter(vis, case)?; // `const unsafe` let ident = self.parse_ident()?; // `foo` - let mut generics = self.parse_generics()?; // `<'a, T, ...>` + let mut generics = self.parse_generics()?; // `<'a, T>` + + // (arg: &'a T) -> i32 let decl = match self.parse_fn_decl( fn_parse_mode.req_name, AllowPlus::Yes, @@ -2309,10 +2375,10 @@ impl<'a> Parser<'a> { } } }; - generics.where_clause = self.parse_where_clause()?; // `where T: Ord` + generics.where_clause = self.parse_where_clause()?; // `where T: Send` let mut sig_hi = self.prev_token.span; - let body = self.parse_fn_body(attrs, &ident, &mut sig_hi, fn_parse_mode.req_body)?; // `;` or `{ ... }`. + let body = self.parse_fn_body(attrs, &ident, &mut sig_hi, fn_parse_mode.req_body)?; // `{ 1+1 }` but could also be `;` let fn_sig_span = sig_lo.to(sig_hi); Ok((ident, FnSig { header, decl, span: fn_sig_span }, generics, body)) } diff --git a/tests/ui/fn/keyword-order.stderr b/tests/ui/fn/keyword-order.stderr index 97d8f91b1ee3b..702b2f667c256 100644 --- a/tests/ui/fn/keyword-order.stderr +++ b/tests/ui/fn/keyword-order.stderr @@ -10,7 +10,9 @@ error: expected item, found keyword `pub` --> $DIR/keyword-order.rs:3:9 | LL | default pub const async unsafe extern fn err() {} - | ^^^ expected item + | ^^^-------------------------------------- + | | + | unexpected item at this position | = note: for a full list of items that can appear in modules, see diff --git a/tests/ui/impl-trait/precise-capturing/unexpected-token.stderr b/tests/ui/impl-trait/precise-capturing/unexpected-token.stderr index 989c479b2484e..f8fbad0d0f4e8 100644 --- a/tests/ui/impl-trait/precise-capturing/unexpected-token.stderr +++ b/tests/ui/impl-trait/precise-capturing/unexpected-token.stderr @@ -8,9 +8,7 @@ error: expected item, found `>` --> $DIR/unexpected-token.rs:5:29 | LL | fn hello() -> impl use<'a {}> Sized {} - | ^ expected item - | - = note: for a full list of items that can appear in modules, see + | ^ error: aborting due to 2 previous errors diff --git a/tests/ui/parser/default-unmatched.stderr b/tests/ui/parser/default-unmatched.stderr index de142411d6912..563f3c9046216 100644 --- a/tests/ui/parser/default-unmatched.stderr +++ b/tests/ui/parser/default-unmatched.stderr @@ -10,9 +10,7 @@ error: expected item, found reserved keyword `do` --> $DIR/default-unmatched.rs:3:13 | LL | default do - | ^^ expected item - | - = note: for a full list of items that can appear in modules, see + | ^^ error: aborting due to 2 previous errors diff --git a/tests/ui/parser/help-set-edition-ice-122130.stderr b/tests/ui/parser/help-set-edition-ice-122130.stderr index fe4d212f2db65..2c26d4afb7fd5 100644 --- a/tests/ui/parser/help-set-edition-ice-122130.stderr +++ b/tests/ui/parser/help-set-edition-ice-122130.stderr @@ -13,9 +13,7 @@ error: expected item, found `"owned_box"` --> $DIR/help-set-edition-ice-122130.rs:2:9 | LL | s#[c"owned_box"] - | ^^^^^^^^^^^ expected item - | - = note: for a full list of items that can appear in modules, see + | ^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/parser/impl-parsing.stderr b/tests/ui/parser/impl-parsing.stderr index a57cc075ccc90..be56fa9dcb441 100644 --- a/tests/ui/parser/impl-parsing.stderr +++ b/tests/ui/parser/impl-parsing.stderr @@ -34,9 +34,7 @@ error: expected item, found keyword `unsafe` --> $DIR/impl-parsing.rs:9:9 | LL | default unsafe FAIL - | ^^^^^^ expected item - | - = note: for a full list of items that can appear in modules, see + | ^^^^^^ error: aborting due to 6 previous errors diff --git a/tests/ui/parser/issues/issue-101477-enum.stderr b/tests/ui/parser/issues/issue-101477-enum.stderr index 94130671f1c9a..870fca9bd2159 100644 --- a/tests/ui/parser/issues/issue-101477-enum.stderr +++ b/tests/ui/parser/issues/issue-101477-enum.stderr @@ -10,9 +10,7 @@ error: expected item, found `==` --> $DIR/issue-101477-enum.rs:6:7 | LL | B == 2 - | ^^ expected item - | - = note: for a full list of items that can appear in modules, see + | ^^ error: aborting due to 2 previous errors diff --git a/tests/ui/parser/issues/issue-113110-non-item-at-module-root.stderr b/tests/ui/parser/issues/issue-113110-non-item-at-module-root.stderr index a47dd41036997..709420524b8f4 100644 --- a/tests/ui/parser/issues/issue-113110-non-item-at-module-root.stderr +++ b/tests/ui/parser/issues/issue-113110-non-item-at-module-root.stderr @@ -2,8 +2,9 @@ error: expected item, found `5` --> $DIR/issue-113110-non-item-at-module-root.rs:1:2 | LL | 5 - | ^ expected item + | ^ unexpected expression | + = help: consider putting it inside a function: fn foo() { 5 } = note: for a full list of items that can appear in modules, see error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-17904-2.stderr b/tests/ui/parser/issues/issue-17904-2.stderr index 211ffcedd582e..f47946c8195e5 100644 --- a/tests/ui/parser/issues/issue-17904-2.stderr +++ b/tests/ui/parser/issues/issue-17904-2.stderr @@ -2,9 +2,7 @@ error: expected item, found keyword `where` --> $DIR/issue-17904-2.rs:1:24 | LL | struct Bar { x: T } where T: Copy - | ^^^^^ expected item - | - = note: for a full list of items that can appear in modules, see + | ^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-43196.stderr b/tests/ui/parser/issues/issue-43196.stderr index 15bbb158cd1ab..d1ef1b7f0094c 100644 --- a/tests/ui/parser/issues/issue-43196.stderr +++ b/tests/ui/parser/issues/issue-43196.stderr @@ -10,9 +10,7 @@ error: expected item, found `|` --> $DIR/issue-43196.rs:5:1 | LL | | - | ^ expected item - | - = note: for a full list of items that can appear in modules, see + | ^ error: aborting due to 2 previous errors diff --git a/tests/ui/parser/issues/issue-62913.stderr b/tests/ui/parser/issues/issue-62913.stderr index c33e46837287f..9830e0ffadd55 100644 --- a/tests/ui/parser/issues/issue-62913.stderr +++ b/tests/ui/parser/issues/issue-62913.stderr @@ -16,8 +16,9 @@ error: expected item, found `"\u\"` --> $DIR/issue-62913.rs:1:1 | LL | "\u\" - | ^^^^^^ expected item + | ^^^^^^ unexpected expression | + = help: consider putting it inside a function: fn foo() { "\u\" } = note: for a full list of items that can appear in modules, see error: aborting due to 3 previous errors diff --git a/tests/ui/parser/issues/issue-68890.stderr b/tests/ui/parser/issues/issue-68890.stderr index 43ed69dcf249a..753ae561dd0f7 100644 --- a/tests/ui/parser/issues/issue-68890.stderr +++ b/tests/ui/parser/issues/issue-68890.stderr @@ -18,9 +18,7 @@ error: expected item, found `)` --> $DIR/issue-68890.rs:1:21 | LL | enum e{A((?'a a+?+l))} - | ^ expected item - | - = note: for a full list of items that can appear in modules, see + | ^ error: aborting due to 3 previous errors diff --git a/tests/ui/parser/shebang/shebang-doc-comment.stderr b/tests/ui/parser/shebang/shebang-doc-comment.stderr index 92fefded55a50..35ec52839270b 100644 --- a/tests/ui/parser/shebang/shebang-doc-comment.stderr +++ b/tests/ui/parser/shebang/shebang-doc-comment.stderr @@ -2,8 +2,16 @@ error: expected item, found `[` --> $DIR/shebang-doc-comment.rs:2:1 | LL | [allow(unused_variables)] - | ^ expected item + | ^------------------------ + | | + | unexpected array | + = help: If you wanted an array consider putting it inside a function: fn foo() { [allow(unused_variables)] } +help: If you wanted an attribute consider adding `#` + --> $DIR/shebang-doc-comment.rs:2:1 + | +LL | [allow(unused_variables)] + | ^ = note: for a full list of items that can appear in modules, see error: aborting due to 1 previous error diff --git a/tests/ui/parser/suggest-const-for-global-var.stderr b/tests/ui/parser/suggest-const-for-global-var.stderr index 235e621d8827b..265087331e39a 100644 --- a/tests/ui/parser/suggest-const-for-global-var.stderr +++ b/tests/ui/parser/suggest-const-for-global-var.stderr @@ -2,7 +2,12 @@ error: expected item, found keyword `let` --> $DIR/suggest-const-for-global-var.rs:1:1 | LL | let X: i32 = 12; - | ^^^ consider using `const` or `static` instead of `let` for global variables + | ^^^------------- + | | + | unexpected `let` binding outside of a function + | + = help: consider using `const` or `static` instead of `let` for global variables, or put it inside of a function: fn foo() { let X: i32 = 12; } + = note: for a full list of items that can appear in modules, see error: aborting due to 1 previous error diff --git a/tests/ui/parser/virtual-structs.stderr b/tests/ui/parser/virtual-structs.stderr index 7b45e77ba9c97..a30ddc410d0eb 100644 --- a/tests/ui/parser/virtual-structs.stderr +++ b/tests/ui/parser/virtual-structs.stderr @@ -2,9 +2,7 @@ error: expected item, found reserved keyword `virtual` --> $DIR/virtual-structs.rs:3:1 | LL | virtual struct SuperStruct { - | ^^^^^^^ expected item - | - = note: for a full list of items that can appear in modules, see + | ^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/pub/pub-restricted-error-fn.stderr b/tests/ui/pub/pub-restricted-error-fn.stderr index ca5d8e1b58eb8..d32a80faf132f 100644 --- a/tests/ui/pub/pub-restricted-error-fn.stderr +++ b/tests/ui/pub/pub-restricted-error-fn.stderr @@ -10,9 +10,7 @@ error: expected item, found `(` --> $DIR/pub-restricted-error-fn.rs:1:12 | LL | pub(crate) () fn foo() {} - | ^ expected item - | - = note: for a full list of items that can appear in modules, see + | ^ error: aborting due to 2 previous errors