From cfee07e23d322f27b9ebb767a77434696ee66926 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Wed, 8 Jul 2026 20:24:06 -0700 Subject: [PATCH 1/2] Comments about existing code. --- compiler/rustc_parse/src/parser/item.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 68d2e27d1b77a..590bb61ec9db1 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -121,6 +121,8 @@ enum ReuseKind { } impl<'a> Parser<'a> { + /// Parse an item that is in a module or in a statement context + /// (and thus, not directly in an `impl`, `extern`, or `trait` block). pub fn parse_item( &mut self, force_collect: ForceCollect, @@ -151,6 +153,7 @@ impl<'a> Parser<'a> { ) } + /// Most general function for parsing an item in all contexts which items can appear. pub(super) fn parse_item_common( &mut self, attrs: AttrWrapper, @@ -190,7 +193,10 @@ impl<'a> Parser<'a> { return Ok((Some(item), Trailing::No, UsePreAttrPos::No)); } - // At this point, we have failed to parse an item. + // At this point, we have failed to parse an item, + // but we may have succeeded at parsing a modifier (`pub`, `default`, `final`) + // that precedes an item. If we did any of those, we will emit an error. + if !matches!(vis.kind, VisibilityKind::Inherited) { let vis_str = pprust::vis_to_string(&vis).trim_end().to_string(); let mut err = this.dcx().create_err(diagnostics::VisibilityNotFollowedByItem { From 69816741ad5289140df70afcdb5ec9617cc45cb4 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Wed, 8 Jul 2026 20:24:25 -0700 Subject: [PATCH 2/2] Parse `let`s as if they were items for diagnostics. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change provides a precise diagnostic in all cases of `let` appearing in a context where items but not statements are permitted, and `let` appearing after an item modifier keyword (`pub` or `final`). It provides a suggestion for replacing `let` with `const` (which should be usable interactively in rust-analyzer). It provides advice when the user might have thought `final` meant what it means in Java (immutable variable declaration). It also consolidates two existing, less powerful `let` diagnostics into the new code: one for module bodies, and one for `trait`/`impl` bodies. In order to be able to determine whether a `let` *would* parse if written properly, it adds a new parameter, `StmtWouldBeAllowed`. This is a bit intrusive and not ideal, but I couldn’t think of a better solution, other than extending `FnContext` to mean more than it currently does, which seems not necessarily wise. --- compiler/rustc_builtin_macros/src/cfg_eval.rs | 8 +- .../rustc_builtin_macros/src/source_util.rs | 8 +- compiler/rustc_expand/src/expand.rs | 8 +- compiler/rustc_expand/src/proc_macro.rs | 3 +- compiler/rustc_parse/src/diagnostics.rs | 95 +++++++ compiler/rustc_parse/src/parser/attr.rs | 3 +- compiler/rustc_parse/src/parser/item.rs | 238 ++++++++++++++---- compiler/rustc_parse/src/parser/mod.rs | 1 + .../rustc_parse/src/parser/nonterminal.rs | 9 +- compiler/rustc_parse/src/parser/stmt.rs | 3 +- compiler/rustc_parse/src/parser/tests.rs | 5 +- src/librustdoc/doctest/make.rs | 1 + src/tools/rustfmt/src/parse/macros/cfg_if.rs | 8 +- .../rustfmt/src/parse/macros/cfg_select.rs | 10 +- src/tools/rustfmt/src/parse/macros/mod.rs | 10 +- tests/ui/macros/issue-54441.rs | 2 +- tests/ui/macros/issue-54441.stderr | 6 +- tests/ui/parser/let/let-at-top-level.rs | 10 + tests/ui/parser/let/let-at-top-level.stderr | 56 +++++ tests/ui/parser/let/let-with-visibility.fixed | 46 ++++ tests/ui/parser/let/let-with-visibility.rs | 46 ++++ .../ui/parser/let/let-with-visibility.stderr | 36 +++ .../{ => let}/suggest-assoc-const.fixed | 2 +- .../parser/{ => let}/suggest-assoc-const.rs | 2 +- .../ui/parser/let/suggest-assoc-const.stderr | 20 ++ .../let/suggest-const-for-global-var.rs | 6 + .../let/suggest-const-for-global-var.stderr | 20 ++ .../let/suggest-static-for-global-var-mut.rs | 5 + .../suggest-static-for-global-var-mut.stderr | 11 + tests/ui/parser/suggest-assoc-const.stderr | 14 -- .../ui/parser/suggest-const-for-global-var.rs | 6 - .../suggest-const-for-global-var.stderr | 13 - .../suggest-static-for-global-var-mut.rs | 5 - .../suggest-static-for-global-var-mut.stderr | 11 - 34 files changed, 608 insertions(+), 119 deletions(-) create mode 100644 tests/ui/parser/let/let-at-top-level.rs create mode 100644 tests/ui/parser/let/let-at-top-level.stderr create mode 100644 tests/ui/parser/let/let-with-visibility.fixed create mode 100644 tests/ui/parser/let/let-with-visibility.rs create mode 100644 tests/ui/parser/let/let-with-visibility.stderr rename tests/ui/parser/{ => let}/suggest-assoc-const.fixed (66%) rename tests/ui/parser/{ => let}/suggest-assoc-const.rs (65%) create mode 100644 tests/ui/parser/let/suggest-assoc-const.stderr create mode 100644 tests/ui/parser/let/suggest-const-for-global-var.rs create mode 100644 tests/ui/parser/let/suggest-const-for-global-var.stderr create mode 100644 tests/ui/parser/let/suggest-static-for-global-var-mut.rs create mode 100644 tests/ui/parser/let/suggest-static-for-global-var-mut.stderr delete mode 100644 tests/ui/parser/suggest-assoc-const.stderr delete mode 100644 tests/ui/parser/suggest-const-for-global-var.rs delete mode 100644 tests/ui/parser/suggest-const-for-global-var.stderr delete mode 100644 tests/ui/parser/suggest-static-for-global-var-mut.rs delete mode 100644 tests/ui/parser/suggest-static-for-global-var-mut.stderr diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index 34ddd9427cdde..2bd7fdfa50fbe 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -9,7 +9,7 @@ use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_expand::config::StripUnconfigured; use rustc_expand::configure; use rustc_feature::Features; -use rustc_parse::parser::{AllowConstBlockItems, ForceCollect, Parser}; +use rustc_parse::parser::{AllowConstBlockItems, ForceCollect, Parser, StmtWouldBeAllowed}; use rustc_session::Session; use rustc_span::{Span, sym}; use smallvec::SmallVec; @@ -110,7 +110,11 @@ impl CfgEval<'_> { let res: PResult<'_, Option> = try { match &annotatable { Annotatable::Item(_) => parser - .parse_item(ForceCollect::Yes, AllowConstBlockItems::Yes)? + .parse_item( + ForceCollect::Yes, + AllowConstBlockItems::Yes, + StmtWouldBeAllowed::NoOrUnknown, + )? .and_then(|item| self.flat_map_item(item).pop().map(Annotatable::Item)), Annotatable::AssocItem(_, ctxt) => { parser.parse_trait_item(ForceCollect::Yes)?.flatten().and_then(|item| { diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index fe2b5e1a45920..49c119af6a82e 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -13,7 +13,7 @@ use rustc_expand::base::{ }; use rustc_expand::module::DirOwnership; use rustc_parse::lexer::StripTokens; -use rustc_parse::parser::{AllowConstBlockItems, ForceCollect}; +use rustc_parse::parser::{AllowConstBlockItems, ForceCollect, StmtWouldBeAllowed}; use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal, utf8_error}; use rustc_session::lint::builtin::INCOMPLETE_INCLUDE; use rustc_session::parse::ParseSess; @@ -168,7 +168,11 @@ pub(crate) fn expand_include<'cx>( )); let mut ret = SmallVec::new(); loop { - match p.parse_item(ForceCollect::No, AllowConstBlockItems::Yes) { + match p.parse_item( + ForceCollect::No, + AllowConstBlockItems::Yes, + StmtWouldBeAllowed::NoOrUnknown, + ) { Err(err) => { err.emit(); break; diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 187dcea4f91a5..ad9faef55c843 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -27,7 +27,7 @@ use rustc_hir::Target; use rustc_hir::def::MacroKinds; use rustc_parse::parser::{ AllowConstBlockItems, AttemptLocalParseRecovery, CommaRecoveryMode, ForceCollect, Parser, - RecoverColon, RecoverComma, Recovery, token_descr, + RecoverColon, RecoverComma, Recovery, StmtWouldBeAllowed, token_descr, }; use rustc_session::diagnostics::feature_err; use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS}; @@ -1083,7 +1083,11 @@ pub fn parse_ast_fragment<'a>( Ok(match kind { AstFragmentKind::Items => { let mut items = SmallVec::new(); - while let Some(item) = this.parse_item(ForceCollect::No, AllowConstBlockItems::Yes)? { + while let Some(item) = this.parse_item( + ForceCollect::No, + AllowConstBlockItems::Yes, + StmtWouldBeAllowed::NoOrUnknown, + )? { items.push(item); } AstFragment::Items(items) diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index 105d2d796aa80..83091d0b1f8a7 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -3,7 +3,7 @@ use rustc_ast::tokenstream::TokenStream; use rustc_data_structures::profiling::TimingGuard; use rustc_errors::ErrorGuaranteed; use rustc_middle::ty::{self, TyCtxt}; -use rustc_parse::parser::{AllowConstBlockItems, ForceCollect, Parser}; +use rustc_parse::parser::{AllowConstBlockItems, ForceCollect, Parser, StmtWouldBeAllowed}; use rustc_proc_macro as pm; use rustc_session::Session; use rustc_session::config::ProcMacroExecutionStrategy; @@ -138,6 +138,7 @@ impl MultiItemModifier for DeriveProcMacro { match parser.parse_item( ForceCollect::No, if is_stmt { AllowConstBlockItems::No } else { AllowConstBlockItems::Yes }, + StmtWouldBeAllowed::NoOrUnknown, ) { Ok(None) => break, Ok(Some(item)) => { diff --git a/compiler/rustc_parse/src/diagnostics.rs b/compiler/rustc_parse/src/diagnostics.rs index 238eebd73a0fa..718560ccb1001 100644 --- a/compiler/rustc_parse/src/diagnostics.rs +++ b/compiler/rustc_parse/src/diagnostics.rs @@ -2345,6 +2345,101 @@ pub(crate) enum AmbiguousMissingKwForItemSub { HelpMacro, } +// Emitted when an item wanted but `let` is found, and we are in a block that +// would allow a properly structured `let` statement, but something prior made it not a +// `let` statement. +#[derive(Diagnostic)] +pub(crate) enum LetWithModifier { + #[diag("a `let` statement cannot have a visibility")] + Visibility( + #[primary_span] Span, + #[suggestion( + "remove the visibility", + code = "", + applicability = "machine-applicable", + style = "short" + )] + Span, + ), + + #[diag("a `let` statement cannot be `final`")] + Final( + #[primary_span] Span, + #[suggestion( + "remove the `final`", + code = "", + applicability = "machine-applicable", + style = "short" + )] + Span, + #[subdiagnostic] Option, + ), + + #[diag("a `let` statement cannot be `default`")] + Default( + #[primary_span] Span, + #[suggestion( + "remove the `default`", + code = "", + applicability = "machine-applicable", + style = "short" + )] + Span, + ), +} + +#[derive(Subdiagnostic)] +#[note("variables in Rust are immutable by default, and `final` does not control mutability")] +pub(crate) struct FinalLetIsNotImmutability; + +/// Emitted when an item is wanted but `let` is found, and we aren’t in a block +/// that would allow a properly formatted `let`. +#[derive(Diagnostic)] +#[diag("`let` statements are not allowed outside of functions or const blocks")] +#[note("`let` cannot be used to define global variables")] +pub(crate) struct LetAsItem { + /// Points to the `let` token. + #[primary_span] + pub span: Span, + + /// May or may not be a suggestion + #[subdiagnostic] + pub advice: LetAsItemAdvice, +} + +#[derive(Subdiagnostic)] +pub(crate) enum LetAsItemAdvice { + #[suggestion( + "consider using `static` to define a global variable", + code = "static", + applicability = "maybe-incorrect", + style = "verbose" + )] + #[suggestion( + "consider using `const` to define a constant", + code = "const", + applicability = "maybe-incorrect", + style = "verbose" + )] + UseStaticOrConstSugg(#[primary_span] Span), + + /// Used when the item is an associated item. + #[suggestion( + "consider using `const` instead of `let` for associated const", + code = "const", + applicability = "maybe-incorrect", + style = "verbose" + )] + UseAssocConstSugg(#[primary_span] Span), + + #[help("consider using `static` or `const` instead of `let`")] + UseStaticOrConstNoSugg(#[primary_span] Span), + + /// Used for `let mut`. + #[help("consider using `static` and a `Mutex` instead of `let mut`")] + UseMutex, +} + #[derive(Diagnostic)] #[diag("missing parameters for function definition")] pub(crate) struct MissingFnParams { diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index fae58c29954d0..b689f3594ae04 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -10,7 +10,7 @@ use tracing::debug; use super::{ AllowConstBlockItems, AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle, - Trailing, UsePreAttrPos, + StmtWouldBeAllowed, Trailing, UsePreAttrPos, }; use crate::parser::FnContext; use crate::{diagnostics, exp}; @@ -214,6 +214,7 @@ impl<'a> Parser<'a> { FnParseMode { req_name: |_, _| true, context: FnContext::Free, req_body: true }, ForceCollect::No, AllowConstBlockItems::Yes, + StmtWouldBeAllowed::NoOrUnknown, ) { Ok(Some(item)) => { err.arg("item", item.kind.descr()); diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 590bb61ec9db1..30f3576dbd26e 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -67,7 +67,12 @@ impl<'a> Parser<'a> { // `parse_item` consumes the appropriate semicolons so any leftover is an error. loop { while self.maybe_consume_incorrect_semicolon(items.last().map(|x| &**x)) {} // Eat all bad semicolons - let Some(item) = self.parse_item(ForceCollect::No, AllowConstBlockItems::Yes)? else { + let Some(item) = self.parse_item( + ForceCollect::No, + AllowConstBlockItems::Yes, + StmtWouldBeAllowed::NoOrUnknown, + )? + else { break; }; items.push(item); @@ -76,34 +81,9 @@ impl<'a> Parser<'a> { if !self.eat(term) { let token_str = super::token_descr(&self.token); if !self.maybe_consume_incorrect_semicolon(items.last().map(|x| &**x)) { - let is_let = self.token.is_keyword(kw::Let); - let is_let_mut = is_let && self.look_ahead(1, |t| t.is_keyword(kw::Mut)); - let let_has_ident = is_let && !is_let_mut && self.is_kw_followed_by_ident(kw::Let); - let msg = format!("expected item, found {token_str}"); let mut err = self.dcx().struct_span_err(self.token.span, msg); - - let label = if is_let { - "`let` cannot be used for global variables" - } else { - "expected item" - }; - err.span_label(self.token.span, label); - - if is_let { - if is_let_mut { - err.help("consider using `static` and a `Mutex` instead of `let mut`"); - } else if let_has_ident { - err.span_suggestion_short( - self.token.span, - "consider using `static` or `const` instead of `let`", - "static", - Applicability::MaybeIncorrect, - ); - } else { - err.help("consider using `static` or `const` instead of `let`"); - } - } + err.span_label(self.token.span, "expected item"); err.note("for a full list of items that can appear in modules, see "); return Err(err); } @@ -127,10 +107,11 @@ impl<'a> Parser<'a> { &mut self, force_collect: ForceCollect, allow_const_block_items: AllowConstBlockItems, + allow_suggest_stmt: StmtWouldBeAllowed, ) -> PResult<'a, Option>> { let fn_parse_mode = FnParseMode { req_name: |_, _| true, context: FnContext::Free, req_body: true }; - self.parse_item_(fn_parse_mode, force_collect, allow_const_block_items) + self.parse_item_(fn_parse_mode, force_collect, allow_const_block_items, allow_suggest_stmt) .map(|i| i.map(Box::new)) } @@ -139,6 +120,7 @@ impl<'a> Parser<'a> { fn_parse_mode: FnParseMode, force_collect: ForceCollect, const_block_items_allowed: AllowConstBlockItems, + allow_suggest_stmt: StmtWouldBeAllowed, ) -> PResult<'a, Option> { self.recover_vcs_conflict_marker(); let attrs = self.parse_outer_attributes()?; @@ -150,6 +132,7 @@ impl<'a> Parser<'a> { fn_parse_mode, force_collect, const_block_items_allowed, + allow_suggest_stmt, ) } @@ -162,9 +145,10 @@ impl<'a> Parser<'a> { fn_parse_mode: FnParseMode, force_collect: ForceCollect, allow_const_block_items: AllowConstBlockItems, + allow_suggest_stmt: StmtWouldBeAllowed, ) -> PResult<'a, Option> { if let Some(item) = self.eat_metavar_seq(MetaVarKind::Item, |this| { - this.parse_item(ForceCollect::Yes, allow_const_block_items) + this.parse_item(ForceCollect::Yes, allow_const_block_items, allow_suggest_stmt) }) { let mut item = item.expect("an actual item"); attrs.prepend_to_nt_inner(&mut item.attrs); @@ -179,6 +163,7 @@ impl<'a> Parser<'a> { &mut attrs, mac_allowed, allow_const_block_items, + allow_suggest_stmt, lo, &vis, &mut def, @@ -253,11 +238,15 @@ impl<'a> Parser<'a> { } /// Parses one of the items allowed by the flags. + /// + /// Also parses `let`, which isn’t an item but a statement, for diagnostic purposes. + /// That branch always errors. fn parse_item_kind( &mut self, attrs: &mut AttrVec, macros_allowed: bool, allow_const_block_items: AllowConstBlockItems, + allow_suggest_stmt: StmtWouldBeAllowed, lo: Span, vis: &Visibility, def: &mut Defaultness, @@ -385,6 +374,9 @@ 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 self.token.is_keyword_case(kw::Let, case) { + // `let`, which is not an item, but might be erroneously used as if it is one. + return self.error_let_as_item(allow_suggest_stmt, fn_parse_mode, vis, def); } else if self.isnt_macro_invocation() && (self.token.is_ident_named(sym::import) || self.token.is_ident_named(sym::using) @@ -403,6 +395,7 @@ impl<'a> Parser<'a> { attrs, macros_allowed, allow_const_block_items, + allow_suggest_stmt, lo, vis, def, @@ -421,6 +414,163 @@ impl<'a> Parser<'a> { Ok(Some(info)) } + /// Parse a `let` statement appearing where an item should and report the error. + /// + /// Precondition: The `let` token is the next token. + fn error_let_as_item( + &mut self, + // Should we suggest a fix that is a `let` statement? + allow_suggest_stmt: StmtWouldBeAllowed, + // We're not parsing a fn, but we do care about whether this item is an associated item, + // which happens to be available here. + fn_parse_mode: FnParseMode, + // `vis` and `def` are tokens that we might have parsed already. + vis: &Visibility, + def: &Defaultness, + ) -> PResult<'a, Option> { + let let_token_span = self.token.span; + + // Determine whether we are parsing an associated item, in which case we should not + // mention `static` but only `const`. + // (This is a slight abuse of `FnParseMode`.) + let must_be_associated_item = match fn_parse_mode.context { + FnContext::Free => false, + FnContext::Trait => true, + FnContext::Impl => true, + }; + + // Get further information by parsing the statement (if it is a valid let statement). + let stmt: Option = if self.may_recover() { + match self.parse_stmt_without_recovery(false, ForceCollect::No, false) { + Ok(stmt) => { + // Eat the semicolon too. + self.expect_semi()?; + Some(stmt) + } + Err(e) => { + // If the statement is invalid, don't also emit that cascading error. + e.cancel(); + None + } + } + } else { + None + }; + + // Is the `let`’s pattern (if it parsed) known to be an Ident pattern, `foo` or `mut foo` + // (but not `ref foo` or any more complex pattern)? + let let_pattern_is_ident: Option = + if let Some(Stmt { kind: StmtKind::Let(ref local), .. }) = stmt { + match local.pat.kind { + PatKind::Ident(BindingMode(ByRef::No, mutability), _, _) => Some(mutability), + _ => None, + } + } else { + None + }; + + let error = match allow_suggest_stmt { + StmtWouldBeAllowed::Yes => { + // We are in a function, `const` block, or other context in which a `let` + // *would* be allowed, except that we must have parsed some item-modifier + // that prohibits it. + + if !vis.span.is_empty() { + self.dcx().create_err(diagnostics::LetWithModifier::Visibility( + vis.span, + vis.span.with_hi(let_token_span.lo()), + )) + } else { + match *def { + // We could also emit `errors::FinalNotFollowedByItem`, but that is less + // specific to the situation. + Defaultness::Final(span) => { + self.dcx().create_err(diagnostics::LetWithModifier::Final( + span, + span.with_hi(let_token_span.lo()), + if let Some(Mutability::Not) = let_pattern_is_ident { + Some(diagnostics::FinalLetIsNotImmutability) + } else { + None + }, + )) + } + Defaultness::Default(span) => { + self.dcx().create_err(diagnostics::LetWithModifier::Default( + span, + span.with_hi(let_token_span.lo()), + )) + } + _ => { + // Fallback case that should never need to be reached. + // We are in a context which allows `let` statements, and yet + // we parsed the `let` as an item. This indicates that some modifier + // token was present before the `let`, but we didn’t find it above. + self.dcx().struct_span_err( + // Ideally we would point to the token *before* the `let`, + // but we don’t know what that token is. + let_token_span.shrink_to_lo(), + "`let` statement cannot have modifiers that apply to items", + ) + } + } + } + } + StmtWouldBeAllowed::NoOrUnknown => { + let advice = match let_pattern_is_ident { + Some(Mutability::Mut) => diagnostics::LetAsItemAdvice::UseMutex, + Some(Mutability::Not) => { + // FIXME: If the `let` doesn’t already have a type, we should include + // addition of ": _" in the suggestion. + if must_be_associated_item { + diagnostics::LetAsItemAdvice::UseAssocConstSugg(let_token_span) + } else { + diagnostics::LetAsItemAdvice::UseStaticOrConstSugg(let_token_span) + } + } + None => diagnostics::LetAsItemAdvice::UseStaticOrConstNoSugg(let_token_span), + }; + + self.dcx().create_err(diagnostics::LetAsItem { span: let_token_span, advice }) + } + }; + + // FIXME: Ideally, we would have a `ItemKind::Err` that optionally defines a name but + // suppresses further errors and has no validity conditions. In lieu of that, we have to + // either synthesize some valid kind of item (here, a `static`), or abort parsing entirely. + if let Some(Stmt { kind: StmtKind::Let(box_local), .. }) = stmt + && let Local { + pat: Pat { kind: PatKind::Ident(BindingMode(_, Mutability::Not), ident, _), .. }, + ty, + kind, + .. + } = *box_local + // Guard against conditions that would provoke further errors + && let FnContext::Free = fn_parse_mode.context + && let Defaultness::Implicit = def + { + // We can pretend we parsed a static item, which will behave mostly like a global + // `let` would if that were a thing that exists. + let guar = error.emit(); + Ok(Some(ItemKind::Static(Box::new(StaticItem { + ident, + ty: ty.unwrap_or_else(|| self.mk_ty(DUMMY_SP, TyKind::Err(guar))), + safety: Safety::Default, + mutability: Mutability::Not, + expr: match kind { + LocalKind::Decl => None, + LocalKind::Init(expr) => Some(expr), + LocalKind::InitElse(expr, _block) => Some(expr), + }, + define_opaque: None, + eii_impls: ThinVec::new(), + })))) + } else { + // We are returning Err, aborting the parsing, because we don’t have a way to recover. + Err(error) + } + } + fn recover_import_as_use(&mut self) -> PResult<'a, Option> { let span = self.token.span; let token_name = super::token_descr(&self.token); @@ -1003,23 +1153,13 @@ impl<'a> Parser<'a> { } // We have to bail or we'll potentially never make progress. let non_item_span = self.token.span; - let is_let = self.token.is_keyword(kw::Let); let mut err = self.dcx().struct_span_err(non_item_span, "non-item in item list"); self.consume_block(exp!(OpenBrace), exp!(CloseBrace), ConsumeClosingDelim::Yes); - if is_let { - err.span_suggestion_verbose( - non_item_span, - "consider using `const` instead of `let` for associated const", - "const", - Applicability::MachineApplicable, - ); - } else { - err.span_label(open_brace_span, "item list starts here") - .span_label(non_item_span, "non-item starts here") - .span_label(self.prev_token.span, "item list ends here"); - } + err.span_label(open_brace_span, "item list starts here") + .span_label(non_item_span, "non-item starts here") + .span_label(self.prev_token.span, "item list ends here"); if is_unnecessary_semicolon { err.span_suggestion_verbose( semicolon_span, @@ -1251,6 +1391,7 @@ impl<'a> Parser<'a> { fn_parse_mode, force_collect, AllowConstBlockItems::DoesNotMatter, // due to `AssocItemKind::try_from` below + StmtWouldBeAllowed::NoOrUnknown, )? .map(|Item { attrs, id, span, vis, kind, tokens }| { let kind = match AssocItemKind::try_from(kind) { @@ -1510,6 +1651,7 @@ impl<'a> Parser<'a> { fn_parse_mode, force_collect, AllowConstBlockItems::DoesNotMatter, // due to `ForeignItemKind::try_from` below + StmtWouldBeAllowed::NoOrUnknown, )? .map(|Item { attrs, id, span, vis, kind, tokens }| { let kind = match ForeignItemKind::try_from(kind) { @@ -2674,6 +2816,7 @@ impl<'a> Parser<'a> { let item = self.parse_item( ForceCollect::No, AllowConstBlockItems::DoesNotMatter, // self.token != kw::Const + StmtWouldBeAllowed::NoOrUnknown, )?; let mut item = item.unwrap().span; if self.token == token::Comma { @@ -2750,6 +2893,17 @@ impl<'a> Parser<'a> { } } } + +/// Whether the context this item is being parsed in would allow a statement instead of an item. +/// +/// This information is used when reporting a parse error, to decide whether to make suggestions +/// that are statements instead of items. +#[derive(Copy, Clone, PartialEq)] +pub enum StmtWouldBeAllowed { + Yes, + NoOrUnknown, +} + enum IsMacroRulesItem { Yes { has_bang: bool }, No, diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index e2671a24177f1..359f77351b658 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -24,6 +24,7 @@ pub use diagnostics::AttemptLocalParseRecovery; // Public to use it for custom `if` expressions in rustfmt forks like https://github.com/tucant/rustfmt pub use expr::LetChainsPolicy; pub(crate) use function::{FnContext, FnParseMode, FrontMatterParsingMode, IsDotDotDot}; +pub use item::StmtWouldBeAllowed; pub use pat::{CommaRecoveryMode, RecoverColon, RecoverComma}; pub use path::PathStyle; use rustc_ast::token::{ diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 9f9545c194082..4ca2189f2f0e4 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -10,6 +10,7 @@ use crate::diagnostics::UnexpectedNonterminal; use crate::parser::pat::{CommaRecoveryMode, RecoverColon, RecoverComma}; use crate::parser::{ AllowConstBlockItems, FollowedByType, ForceCollect, ParseNtResult, Parser, PathStyle, + StmtWouldBeAllowed, }; impl<'a> Parser<'a> { @@ -126,9 +127,11 @@ impl<'a> Parser<'a> { match kind { // Note that TT is treated differently to all the others. NonterminalKind::TT => Ok(ParseNtResult::Tt(self.parse_token_tree())), - NonterminalKind::Item => match self - .parse_item(ForceCollect::Yes, AllowConstBlockItems::Yes)? - { + NonterminalKind::Item => match self.parse_item( + ForceCollect::Yes, + AllowConstBlockItems::Yes, + StmtWouldBeAllowed::NoOrUnknown, + )? { Some(item) => Ok(ParseNtResult::Item(item)), None => Err(self.dcx().create_err(UnexpectedNonterminal::Item(self.token.span))), }, diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 6339532af530c..cdc730364bed2 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -21,7 +21,7 @@ use super::pat::{PatternLocation, RecoverComma}; use super::path::PathStyle; use super::{ AllowConstBlockItems, AttrWrapper, BlockMode, FnContext, FnParseMode, ForceCollect, Parser, - Restrictions, SemiColonMode, Trailing, UsePreAttrPos, + Restrictions, SemiColonMode, StmtWouldBeAllowed, Trailing, UsePreAttrPos, }; use crate::diagnostics::{self, MalformedLoopLabel}; use crate::exp; @@ -162,6 +162,7 @@ impl<'a> Parser<'a> { FnParseMode { req_name: |_, _| true, context: FnContext::Free, req_body: true }, force_collect, AllowConstBlockItems::No, + StmtWouldBeAllowed::Yes, )? { self.mk_stmt(lo.to(item.span), StmtKind::Item(Box::new(item))) } else if self.eat(exp!(Semi)) { diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs index 5286873f3dc55..23f00bc63a96f 100644 --- a/compiler/rustc_parse/src/parser/tests.rs +++ b/compiler/rustc_parse/src/parser/tests.rs @@ -19,6 +19,7 @@ use rustc_span::{ BytePos, FileName, Pos, Span, Symbol, create_default_session_globals_then, kw, sym, }; +use super::StmtWouldBeAllowed; use crate::lexer::StripTokens; use crate::parser::{AllowConstBlockItems, ForceCollect, Parser}; use crate::{new_parser_from_source_str, source_str_to_stream, unwrap_or_emit_fatal}; @@ -2226,7 +2227,7 @@ fn parse_item_from_source_str( psess: &ParseSess, ) -> PResult<'_, Option>> { unwrap_or_emit_fatal(new_parser_from_source_str(psess, name, source, StripTokens::Nothing)) - .parse_item(ForceCollect::No, AllowConstBlockItems::Yes) + .parse_item(ForceCollect::No, AllowConstBlockItems::Yes, StmtWouldBeAllowed::NoOrUnknown) } // Produces a `rustc_span::span`. @@ -2242,7 +2243,7 @@ fn string_to_expr(source_str: String) -> Box { /// Parses a string, returns an item. fn string_to_item(source_str: String) -> Option> { with_error_checking_parse(source_str, &ParseSess::new(), |p| { - p.parse_item(ForceCollect::No, AllowConstBlockItems::Yes) + p.parse_item(ForceCollect::No, AllowConstBlockItems::Yes, StmtWouldBeAllowed::NoOrUnknown) }) } diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index ac82829fa662e..438acd528ec8a 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -569,6 +569,7 @@ fn parse_source( let parsed = parser.parse_item( rustc_parse::parser::ForceCollect::No, rustc_parse::parser::AllowConstBlockItems::No, + rustc_parse::parser::StmtWouldBeAllowed::Yes, ); let result = match parsed { diff --git a/src/tools/rustfmt/src/parse/macros/cfg_if.rs b/src/tools/rustfmt/src/parse/macros/cfg_if.rs index 495f12c8f5d5d..3e2ed76eed508 100644 --- a/src/tools/rustfmt/src/parse/macros/cfg_if.rs +++ b/src/tools/rustfmt/src/parse/macros/cfg_if.rs @@ -3,7 +3,7 @@ use std::panic::{AssertUnwindSafe, catch_unwind}; use rustc_ast::ast; use rustc_ast::token::TokenKind; use rustc_parse::exp; -use rustc_parse::parser::{AllowConstBlockItems, ForceCollect}; +use rustc_parse::parser::{AllowConstBlockItems, ForceCollect, StmtWouldBeAllowed}; use rustc_span::symbol::kw; use crate::parse::macros::build_stream_parser; @@ -61,7 +61,11 @@ fn parse_cfg_if_inner<'a>( } while parser.token != TokenKind::CloseBrace && parser.token.kind != TokenKind::Eof { - let item = match parser.parse_item(ForceCollect::No, AllowConstBlockItems::Yes) { + let item = match parser.parse_item( + ForceCollect::No, + AllowConstBlockItems::Yes, + StmtWouldBeAllowed::NoOrUnknown, + ) { Ok(Some(item_ptr)) => *item_ptr, Ok(None) => continue, Err(err) => { diff --git a/src/tools/rustfmt/src/parse/macros/cfg_select.rs b/src/tools/rustfmt/src/parse/macros/cfg_select.rs index 040447ff1898f..280417efd531f 100644 --- a/src/tools/rustfmt/src/parse/macros/cfg_select.rs +++ b/src/tools/rustfmt/src/parse/macros/cfg_select.rs @@ -3,7 +3,7 @@ use std::panic::{AssertUnwindSafe, catch_unwind}; use rustc_ast::ast; use rustc_ast::token::TokenKind; use rustc_parse::exp; -use rustc_parse::parser::{AllowConstBlockItems, ForceCollect}; +use rustc_parse::parser::{AllowConstBlockItems, ForceCollect, StmtWouldBeAllowed}; use crate::parse::macros::build_stream_parser; use crate::parse::session::ParseSess; @@ -49,9 +49,11 @@ fn parse_cfg_select_inner<'a>( } while parser.token != TokenKind::CloseBrace && parser.token.kind != TokenKind::Eof { - let item = match parser - .parse_item(ForceCollect::No, AllowConstBlockItems::DoesNotMatter) - { + let item = match parser.parse_item( + ForceCollect::No, + AllowConstBlockItems::DoesNotMatter, + StmtWouldBeAllowed::NoOrUnknown, + ) { Ok(Some(item_ptr)) => *item_ptr, Ok(None) => continue, Err(err) => { diff --git a/src/tools/rustfmt/src/parse/macros/mod.rs b/src/tools/rustfmt/src/parse/macros/mod.rs index 3d32821ce08b3..7bdd5232f8ca9 100644 --- a/src/tools/rustfmt/src/parse/macros/mod.rs +++ b/src/tools/rustfmt/src/parse/macros/mod.rs @@ -2,7 +2,9 @@ use rustc_ast::ast; use rustc_ast::token::{Delimiter, NonterminalKind, NtExprKind::*, NtPatKind::*, TokenKind}; use rustc_ast::tokenstream::TokenStream; use rustc_parse::MACRO_ARGUMENTS; -use rustc_parse::parser::{AllowConstBlockItems, ForceCollect, Parser, Recovery}; +use rustc_parse::parser::{ + AllowConstBlockItems, ForceCollect, Parser, Recovery, StmtWouldBeAllowed, +}; use rustc_session::parse::ParseSess; use rustc_span::symbol; @@ -67,7 +69,11 @@ fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option { parse_macro_arg!( Item, NonterminalKind::Item, - |parser: &mut Parser<'b>| parser.parse_item(ForceCollect::No, AllowConstBlockItems::Yes), + |parser: &mut Parser<'b>| parser.parse_item( + ForceCollect::No, + AllowConstBlockItems::Yes, + StmtWouldBeAllowed::NoOrUnknown + ), |x: Option>| x ); diff --git a/tests/ui/macros/issue-54441.rs b/tests/ui/macros/issue-54441.rs index 37ab4e636475b..792af00c47197 100644 --- a/tests/ui/macros/issue-54441.rs +++ b/tests/ui/macros/issue-54441.rs @@ -1,6 +1,6 @@ macro_rules! m { () => { - let //~ ERROR macro expansion ignores keyword `let` and any tokens following + loop //~ ERROR macro expansion ignores keyword `loop` and any tokens following }; } diff --git a/tests/ui/macros/issue-54441.stderr b/tests/ui/macros/issue-54441.stderr index f5f8b8ca2b266..e05bff3485876 100644 --- a/tests/ui/macros/issue-54441.stderr +++ b/tests/ui/macros/issue-54441.stderr @@ -1,8 +1,8 @@ -error: macro expansion ignores keyword `let` and any tokens following +error: macro expansion ignores keyword `loop` and any tokens following --> $DIR/issue-54441.rs:3:9 | -LL | let - | ^^^ +LL | loop + | ^^^^ ... LL | m!(); | ---- caused by the macro expansion here diff --git a/tests/ui/parser/let/let-at-top-level.rs b/tests/ui/parser/let/let-at-top-level.rs new file mode 100644 index 0000000000000..b4b647cd6ad32 --- /dev/null +++ b/tests/ui/parser/let/let-at-top-level.rs @@ -0,0 +1,10 @@ +let x = 1; +//~^ ERROR `let` statements are not allowed outside of functions or const blocks + +let y: i32 = 1; +//~^ ERROR `let` statements are not allowed outside of functions or const blocks + +pub let z = 1; +//~^ ERROR `let` statements are not allowed outside of functions or const blocks + +fn main() {} diff --git a/tests/ui/parser/let/let-at-top-level.stderr b/tests/ui/parser/let/let-at-top-level.stderr new file mode 100644 index 0000000000000..ebc9a64bb4562 --- /dev/null +++ b/tests/ui/parser/let/let-at-top-level.stderr @@ -0,0 +1,56 @@ +error: `let` statements are not allowed outside of functions or const blocks + --> $DIR/let-at-top-level.rs:1:1 + | +LL | let x = 1; + | ^^^ + | + = note: `let` cannot be used to define global variables +help: consider using `static` to define a global variable + | +LL - let x = 1; +LL + static x = 1; + | +help: consider using `const` to define a constant + | +LL - let x = 1; +LL + const x = 1; + | + +error: `let` statements are not allowed outside of functions or const blocks + --> $DIR/let-at-top-level.rs:4:1 + | +LL | let y: i32 = 1; + | ^^^ + | + = note: `let` cannot be used to define global variables +help: consider using `static` to define a global variable + | +LL - let y: i32 = 1; +LL + static y: i32 = 1; + | +help: consider using `const` to define a constant + | +LL - let y: i32 = 1; +LL + const y: i32 = 1; + | + +error: `let` statements are not allowed outside of functions or const blocks + --> $DIR/let-at-top-level.rs:7:5 + | +LL | pub let z = 1; + | ^^^ + | + = note: `let` cannot be used to define global variables +help: consider using `static` to define a global variable + | +LL - pub let z = 1; +LL + pub static z = 1; + | +help: consider using `const` to define a constant + | +LL - pub let z = 1; +LL + pub const z = 1; + | + +error: aborting due to 3 previous errors + diff --git a/tests/ui/parser/let/let-with-visibility.fixed b/tests/ui/parser/let/let-with-visibility.fixed new file mode 100644 index 0000000000000..9e0e8278494f8 --- /dev/null +++ b/tests/ui/parser/let/let-with-visibility.fixed @@ -0,0 +1,46 @@ +//@ run-rustfix +#![allow(unused_features)] +#![allow(unused_mut)] +#![feature(final_associated_functions)] + +fn visibility() { + let s: &str = "hello world"; + //~^ ERROR a `let` statement cannot have a visibility + + println!("{s}"); +} + +// FIXME: Make this case produce a good error like the others +// fn default() { +// default let s: &str = "hello world"; +// println!("{s}"); +// } + +fn final_imm() { + let s: &str = "hello world"; + //~^ ERROR a `let` statement cannot be `final` + + println!("{s}"); +} + +fn final_mut() { + let mut s: &str = "hello world"; + //~^ ERROR a `let` statement cannot be `final` + + println!("{s}"); +} + +fn final_ref() { + let ref s: &str = "hello world"; + //~^ ERROR a `let` statement cannot be `final` + + println!("{s}"); +} + +fn main() { + visibility(); + // default(); + final_imm(); + final_mut(); + final_ref(); +} diff --git a/tests/ui/parser/let/let-with-visibility.rs b/tests/ui/parser/let/let-with-visibility.rs new file mode 100644 index 0000000000000..4c38f0c8e1aaa --- /dev/null +++ b/tests/ui/parser/let/let-with-visibility.rs @@ -0,0 +1,46 @@ +//@ run-rustfix +#![allow(unused_features)] +#![allow(unused_mut)] +#![feature(final_associated_functions)] + +fn visibility() { + pub let s: &str = "hello world"; + //~^ ERROR a `let` statement cannot have a visibility + + println!("{s}"); +} + +// FIXME: Make this case produce a good error like the others +// fn default() { +// default let s: &str = "hello world"; +// println!("{s}"); +// } + +fn final_imm() { + final let s: &str = "hello world"; + //~^ ERROR a `let` statement cannot be `final` + + println!("{s}"); +} + +fn final_mut() { + final let mut s: &str = "hello world"; + //~^ ERROR a `let` statement cannot be `final` + + println!("{s}"); +} + +fn final_ref() { + final let ref s: &str = "hello world"; + //~^ ERROR a `let` statement cannot be `final` + + println!("{s}"); +} + +fn main() { + visibility(); + // default(); + final_imm(); + final_mut(); + final_ref(); +} diff --git a/tests/ui/parser/let/let-with-visibility.stderr b/tests/ui/parser/let/let-with-visibility.stderr new file mode 100644 index 0000000000000..2a76a95b7e4ca --- /dev/null +++ b/tests/ui/parser/let/let-with-visibility.stderr @@ -0,0 +1,36 @@ +error: a `let` statement cannot have a visibility + --> $DIR/let-with-visibility.rs:7:5 + | +LL | pub let s: &str = "hello world"; + | ^^^- + | | + | help: remove the visibility + +error: a `let` statement cannot be `final` + --> $DIR/let-with-visibility.rs:20:5 + | +LL | final let s: &str = "hello world"; + | ^^^^^- + | | + | help: remove the `final` + | + = note: variables in Rust are immutable by default, and `final` does not control mutability + +error: a `let` statement cannot be `final` + --> $DIR/let-with-visibility.rs:27:5 + | +LL | final let mut s: &str = "hello world"; + | ^^^^^- + | | + | help: remove the `final` + +error: a `let` statement cannot be `final` + --> $DIR/let-with-visibility.rs:34:5 + | +LL | final let ref s: &str = "hello world"; + | ^^^^^- + | | + | help: remove the `final` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/parser/suggest-assoc-const.fixed b/tests/ui/parser/let/suggest-assoc-const.fixed similarity index 66% rename from tests/ui/parser/suggest-assoc-const.fixed rename to tests/ui/parser/let/suggest-assoc-const.fixed index de7f2cbaaba5e..8a86bbbcb67c7 100644 --- a/tests/ui/parser/suggest-assoc-const.fixed +++ b/tests/ui/parser/let/suggest-assoc-const.fixed @@ -3,7 +3,7 @@ #![allow(dead_code)] trait Trait { const _X: i32; - //~^ ERROR non-item in item list + //~^ ERROR `let` statements are not allowed outside of functions or const blocks } fn main() { diff --git a/tests/ui/parser/suggest-assoc-const.rs b/tests/ui/parser/let/suggest-assoc-const.rs similarity index 65% rename from tests/ui/parser/suggest-assoc-const.rs rename to tests/ui/parser/let/suggest-assoc-const.rs index 6d0244130a9b5..935e962047157 100644 --- a/tests/ui/parser/suggest-assoc-const.rs +++ b/tests/ui/parser/let/suggest-assoc-const.rs @@ -3,7 +3,7 @@ #![allow(dead_code)] trait Trait { let _X: i32; - //~^ ERROR non-item in item list + //~^ ERROR `let` statements are not allowed outside of functions or const blocks } fn main() { diff --git a/tests/ui/parser/let/suggest-assoc-const.stderr b/tests/ui/parser/let/suggest-assoc-const.stderr new file mode 100644 index 0000000000000..7240fedd1966a --- /dev/null +++ b/tests/ui/parser/let/suggest-assoc-const.stderr @@ -0,0 +1,20 @@ +error: `let` statements are not allowed outside of functions or const blocks + --> $DIR/suggest-assoc-const.rs:5:5 + | +LL | trait Trait { + | - while parsing this item list starting here +LL | let _X: i32; + | ^^^ +LL | +LL | } + | - the item list ends here + | + = note: `let` cannot be used to define global variables +help: consider using `const` instead of `let` for associated const + | +LL - let _X: i32; +LL + const _X: i32; + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/parser/let/suggest-const-for-global-var.rs b/tests/ui/parser/let/suggest-const-for-global-var.rs new file mode 100644 index 0000000000000..0ebd1961e1968 --- /dev/null +++ b/tests/ui/parser/let/suggest-const-for-global-var.rs @@ -0,0 +1,6 @@ +let X: i32 = 12; +//~^ ERROR `let` statements are not allowed outside of functions or const blocks + +fn main() { + println!("{}", X); +} diff --git a/tests/ui/parser/let/suggest-const-for-global-var.stderr b/tests/ui/parser/let/suggest-const-for-global-var.stderr new file mode 100644 index 0000000000000..fa616dfe593cb --- /dev/null +++ b/tests/ui/parser/let/suggest-const-for-global-var.stderr @@ -0,0 +1,20 @@ +error: `let` statements are not allowed outside of functions or const blocks + --> $DIR/suggest-const-for-global-var.rs:1:1 + | +LL | let X: i32 = 12; + | ^^^ + | + = note: `let` cannot be used to define global variables +help: consider using `static` to define a global variable + | +LL - let X: i32 = 12; +LL + static X: i32 = 12; + | +help: consider using `const` to define a constant + | +LL - let X: i32 = 12; +LL + const X: i32 = 12; + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/parser/let/suggest-static-for-global-var-mut.rs b/tests/ui/parser/let/suggest-static-for-global-var-mut.rs new file mode 100644 index 0000000000000..e6823c1c6713c --- /dev/null +++ b/tests/ui/parser/let/suggest-static-for-global-var-mut.rs @@ -0,0 +1,5 @@ +let mut _data = vec![1,2,3]; +//~^ ERROR `let` statements are not allowed outside of functions or const blocks + +fn main() { +} diff --git a/tests/ui/parser/let/suggest-static-for-global-var-mut.stderr b/tests/ui/parser/let/suggest-static-for-global-var-mut.stderr new file mode 100644 index 0000000000000..42138fdc3f253 --- /dev/null +++ b/tests/ui/parser/let/suggest-static-for-global-var-mut.stderr @@ -0,0 +1,11 @@ +error: `let` statements are not allowed outside of functions or const blocks + --> $DIR/suggest-static-for-global-var-mut.rs:1:1 + | +LL | let mut _data = vec![1,2,3]; + | ^^^ + | + = note: `let` cannot be used to define global variables + = help: consider using `static` and a `Mutex` instead of `let mut` + +error: aborting due to 1 previous error + diff --git a/tests/ui/parser/suggest-assoc-const.stderr b/tests/ui/parser/suggest-assoc-const.stderr deleted file mode 100644 index 8cb304ced372f..0000000000000 --- a/tests/ui/parser/suggest-assoc-const.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: non-item in item list - --> $DIR/suggest-assoc-const.rs:5:5 - | -LL | let _X: i32; - | ^^^ - | -help: consider using `const` instead of `let` for associated const - | -LL - let _X: i32; -LL + const _X: i32; - | - -error: aborting due to 1 previous error - diff --git a/tests/ui/parser/suggest-const-for-global-var.rs b/tests/ui/parser/suggest-const-for-global-var.rs deleted file mode 100644 index d6216cb7ac275..0000000000000 --- a/tests/ui/parser/suggest-const-for-global-var.rs +++ /dev/null @@ -1,6 +0,0 @@ -let X: i32 = 12; -//~^ ERROR expected item, found keyword `let` - -fn main() { - println!("{}", X); -} diff --git a/tests/ui/parser/suggest-const-for-global-var.stderr b/tests/ui/parser/suggest-const-for-global-var.stderr deleted file mode 100644 index 6ac7fe8f0921b..0000000000000 --- a/tests/ui/parser/suggest-const-for-global-var.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error: expected item, found keyword `let` - --> $DIR/suggest-const-for-global-var.rs:1:1 - | -LL | let X: i32 = 12; - | ^^^ - | | - | `let` cannot be used for global variables - | help: consider using `static` or `const` instead of `let` - | - = 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-static-for-global-var-mut.rs b/tests/ui/parser/suggest-static-for-global-var-mut.rs deleted file mode 100644 index c63b09bb7a7f7..0000000000000 --- a/tests/ui/parser/suggest-static-for-global-var-mut.rs +++ /dev/null @@ -1,5 +0,0 @@ -let mut _data = vec![1,2,3]; -//~^ ERROR expected item, found keyword `let` - -fn main() { -} diff --git a/tests/ui/parser/suggest-static-for-global-var-mut.stderr b/tests/ui/parser/suggest-static-for-global-var-mut.stderr deleted file mode 100644 index 4b00d1a24f317..0000000000000 --- a/tests/ui/parser/suggest-static-for-global-var-mut.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: expected item, found keyword `let` - --> $DIR/suggest-static-for-global-var-mut.rs:1:1 - | -LL | let mut _data = vec![1,2,3]; - | ^^^ `let` cannot be used for global variables - | - = help: consider using `static` and a `Mutex` instead of `let mut` - = note: for a full list of items that can appear in modules, see - -error: aborting due to 1 previous error -