From e3622c1a449b927ada555dc2adecbdf93d345499 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Wed, 17 Jun 2026 20:30:09 -0400 Subject: [PATCH 01/10] Don't escape U+FF9E and U+FF9F in `escape_debug_ext` --- library/core/src/char/methods.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 8c5f9d472bfa8..cdab082d64485 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -486,8 +486,11 @@ impl char { '\r' => EscapeDebug::backslash(ascii::Char::SmallR), '\0' => EscapeDebug::backslash(ascii::Char::Digit0), - // ASCII fast path - '\x20'..='\x7E' => EscapeDebug::printable(self), + // ASCII fast path, + // plus U+FF9E HALFWIDTH KATAKANA VOICED SOUND MARK + // and U+FF9F HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK + // which should not be escaped despite being grapheme extenders. + '\x20'..='\x7E' | '\u{FF9E}' | '\u{FF9F}' => EscapeDebug::printable(self), _ if self.is_control() || self.is_private_use() From f3d6ac3a69cc7e3ffe5d1e39c45394aa60e05a61 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Thu, 18 Jun 2026 23:28:58 -0400 Subject: [PATCH 02/10] Add note to doc comment of `escape_grapheme_extender` field --- library/core/src/char/methods.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index cdab082d64485..8f836967d4e0d 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -2427,6 +2427,10 @@ impl char { pub(crate) struct EscapeDebugExtArgs { /// Escape Grapheme Extender codepoints? + /// (Note that this excludes + /// U+FF9E HALFWIDTH KATAKANA VOICED SOUND MARK + /// and U+FF9F HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK, + /// which are never escaped.) pub(crate) escape_grapheme_extender: bool, /// Escape single quotes? From 903bac5a5941d8f9fd027c7c6cabaca06f79283d Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Thu, 18 Jun 2026 23:31:55 -0400 Subject: [PATCH 03/10] Add tests --- library/coretests/tests/char.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/coretests/tests/char.rs b/library/coretests/tests/char.rs index e0921e2f39af5..4981ab0c9b809 100644 --- a/library/coretests/tests/char.rs +++ b/library/coretests/tests/char.rs @@ -299,6 +299,8 @@ fn test_escape_debug() { assert_eq!(string('\u{200b}'), "\\u{200b}"); // zero width space assert_eq!(string('\u{e000}'), "\\u{e000}"); // private use 1 assert_eq!(string('\u{100000}'), "\\u{100000}"); // private use 2 + assert_eq!(string('\u{ff9e}'), "\u{ff9e}"); // halfwidth dakuten + assert_eq!(string('\u{ff9f}'), "\u{ff9f}"); // halfwidth handakuten } #[test] From 4be2c446a7cf1ed665a31234a248b2bc91daa62e Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Fri, 19 Jun 2026 14:32:22 -0400 Subject: [PATCH 04/10] Add link to additional background info --- library/core/src/char/methods.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 8f836967d4e0d..788c25aafeef0 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -2427,10 +2427,13 @@ impl char { pub(crate) struct EscapeDebugExtArgs { /// Escape Grapheme Extender codepoints? - /// (Note that this excludes + /// + /// Note that this excludes /// U+FF9E HALFWIDTH KATAKANA VOICED SOUND MARK /// and U+FF9F HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK, - /// which are never escaped.) + /// which are never escaped, as graphically + /// they are not combining. See + /// for background on these characters. pub(crate) escape_grapheme_extender: bool, /// Escape single quotes? From f79fa636bfc99bf24d8df5124d26fbbab04b5f8c Mon Sep 17 00:00:00 2001 From: Adwin White Date: Fri, 24 Jul 2026 16:08:58 +0800 Subject: [PATCH 05/10] add missing resolving vars --- .../query/type_op/implied_outlives_bounds.rs | 1 + ..._outlives_bounds_not_resolving_vars_ice.rs | 19 ++++++++++++ ...lives_bounds_not_resolving_vars_ice.stderr | 29 +++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.rs create mode 100644 tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.stderr diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index a98f8b9a9af88..e55f87c6076e6 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -95,6 +95,7 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( continue; } + let arg = ocx.infcx.resolve_vars_if_possible(arg); // From the full set of obligations, just filter down to the region relationships. for obligation in wf::unnormalized_obligations(ocx.infcx, param_env, arg, DUMMY_SP, CRATE_DEF_ID) diff --git a/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.rs b/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.rs new file mode 100644 index 0000000000000..a1e60c38fbbc8 --- /dev/null +++ b/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.rs @@ -0,0 +1,19 @@ +//@ compile-flags: -Znext-solver + +// Regression test for https://github.com/rust-lang/rust-clippy/issues/17439 + +#![feature(inherent_associated_types)] + +struct Foo(T); + +impl<'a> Foo { + type Assoc = &'a (); +} + +fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} +//~^ ERROR: higher-ranked subtype error +//~| ERROR: higher-ranked subtype error +//~| ERROR: lifetime bound not satisfied [E0478] +//~| ERROR: lifetime bound not satisfied [E0478] + +fn main() {} diff --git a/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.stderr b/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.stderr new file mode 100644 index 0000000000000..6a79474f60915 --- /dev/null +++ b/tests/ui/traits/next-solver/implied_outlives_bounds_not_resolving_vars_ice.stderr @@ -0,0 +1,29 @@ +error[E0478]: lifetime bound not satisfied + --> $DIR/implied_outlives_bounds_not_resolving_vars_ice.rs:13:11 + | +LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0478]: lifetime bound not satisfied + --> $DIR/implied_outlives_bounds_not_resolving_vars_ice.rs:13:11 + | +LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: higher-ranked subtype error + --> $DIR/implied_outlives_bounds_not_resolving_vars_ice.rs:13:1 + | +LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: higher-ranked subtype error + --> $DIR/implied_outlives_bounds_not_resolving_vars_ice.rs:13:8 + | +LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} + | ^ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0478`. From 0c1fefd9de92de4659c3d8430a74df1511af3ad2 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Mon, 27 Jul 2026 21:08:59 -0700 Subject: [PATCH 06/10] Split function parsing out of `item.rs` to a new module. `item.rs` is approaching the tidy-enforced length limit, and this will make room for new additions. --- .../rustc_parse/src/parser/diagnostics.rs | 7 +- compiler/rustc_parse/src/parser/function.rs | 1018 +++++++++++++++++ compiler/rustc_parse/src/parser/item.rs | 1015 +--------------- compiler/rustc_parse/src/parser/mod.rs | 3 +- compiler/rustc_parse/src/parser/ty.rs | 5 +- 5 files changed, 1032 insertions(+), 1016 deletions(-) create mode 100644 compiler/rustc_parse/src/parser/function.rs diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 3064c0b22592d..1c99c2c2dae1e 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -43,9 +43,8 @@ use crate::diagnostics::{ UseEqInstead, WrapType, }; use crate::exp; -use crate::parser::FnContext; use crate::parser::attr::InnerAttrPolicy; -use crate::parser::item::IsDotDotDot; +use crate::parser::{FnContext, IsDotDotDot}; /// Creates a placeholder argument. pub(super) fn dummy_arg(ident: Ident, guar: ErrorGuaranteed) -> Param { @@ -2240,7 +2239,7 @@ impl<'a> Parser<'a> { pat: Box, require_name: bool, first_param: bool, - fn_parse_mode: &crate::parser::item::FnParseMode, + fn_parse_mode: &crate::parser::FnParseMode, ) -> Option { // If we find a pattern followed by an identifier, it could be an (incorrect) // C-style parameter declaration. @@ -2265,7 +2264,7 @@ impl<'a> Parser<'a> { { let maybe_emit_anon_params_note = |this: &mut Self, err: &mut Diag<'_>| { let ed = this.token.span.with_neighbor(this.prev_token.span).edition(); - if matches!(fn_parse_mode.context, crate::parser::item::FnContext::Trait) + if matches!(fn_parse_mode.context, crate::parser::FnContext::Trait) && (fn_parse_mode.req_name)(ed, IsDotDotDot::No) { err.note("anonymous parameters are removed in the 2018 edition (see RFC 1685)"); diff --git a/compiler/rustc_parse/src/parser/function.rs b/compiler/rustc_parse/src/parser/function.rs new file mode 100644 index 0000000000000..c7eb84f553614 --- /dev/null +++ b/compiler/rustc_parse/src/parser/function.rs @@ -0,0 +1,1018 @@ +use ast::token::IdentIsRaw; +use rustc_ast as ast; +use rustc_ast::ast::*; +use rustc_ast::token::{self, InvisibleOrigin, MetaVarKind, TokenKind}; +use rustc_ast::tokenstream::TokenTree; +use rustc_ast::util::case::Case; +use rustc_ast_pretty::pprust; +use rustc_errors::{Applicability, PResult}; +use rustc_session::lint::builtin::VARARGS_WITHOUT_PATTERN; +use rustc_span::edition::Edition; +use rustc_span::{ErrorGuaranteed, Ident, Span, kw, respan, sym}; +use thin_vec::ThinVec; +use tracing::debug; + +use super::diagnostics::dummy_arg; +use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; +use super::{ + ExpKeywordPair, FollowedByType, ForceCollect, Parser, Recovered, Trailing, UsePreAttrPos, +}; +use crate::diagnostics::{self, FnPointerCannotBeAsync, FnPointerCannotBeConst}; +use crate::exp; + +/// The parsing configuration used to parse a parameter list (see `parse_fn_params`). +/// +/// The function decides if, per-parameter `p`, `p` must have a pattern or just a type. +/// +/// This function pointer accepts an edition, because in edition 2015, trait declarations +/// were allowed to omit parameter names. In 2018, they became required. It also accepts an +/// `IsDotDotDot` parameter, as `extern` function declarations and function pointer types are +/// allowed to omit the name of the `...` but regular function items are not. +type ReqName = fn(Edition, IsDotDotDot) -> bool; + +#[derive(Copy, Clone, PartialEq)] +pub(crate) enum IsDotDotDot { + Yes, + No, +} + +/// Parsing configuration for functions. +/// +/// The syntax of function items is slightly different within trait definitions, +/// impl blocks, and modules. It is still parsed using the same code, just with +/// different flags set, so that even when the input is wrong and produces a parse +/// error, it still gets into the AST and the rest of the parser and +/// type checker can run. +#[derive(Clone, Copy)] +pub(crate) struct FnParseMode { + /// A function pointer that decides if, per-parameter `p`, `p` must have a + /// pattern or just a type. This field affects parsing of the parameters list. + /// + /// ```text + /// fn foo(alef: A) -> X { X::new() } + /// -----^^ affects parsing this part of the function signature + /// | + /// if req_name returns false, then this name is optional + /// + /// fn bar(A) -> X; + /// ^ + /// | + /// if req_name returns true, this is an error + /// ``` + /// + /// Calling this function pointer should only return false if: + /// + /// * The item is being parsed inside of a trait definition. + /// Within an impl block or a module, it should always evaluate + /// to true. + /// * The span is from Edition 2015. In particular, you can get a + /// 2015 span inside a 2021 crate using macros. + /// + /// Or if `IsDotDotDot::Yes`, this function will also return `false` if the item being parsed + /// is inside an `extern` block. + pub(super) req_name: ReqName, + /// The context in which this function is parsed, used for diagnostics. + /// This indicates the fn is a free function or method and so on. + pub(super) context: FnContext, + /// If this flag is set to `true`, then plain, semicolon-terminated function + /// prototypes are not allowed here. + /// + /// ```text + /// fn foo(alef: A) -> X { X::new() } + /// ^^^^^^^^^^^^ + /// | + /// this is always allowed + /// + /// fn bar(alef: A, bet: B) -> X; + /// ^ + /// | + /// if req_body is set to true, this is an error + /// ``` + /// + /// This field should only be set to false if the item is inside of a trait + /// definition or extern block. Within an impl block or a module, it should + /// always be set to true. + pub(super) req_body: bool, +} + +/// The context in which a function is parsed. +/// FIXME(estebank, xizheyin): Use more variants. +#[derive(Clone, Copy, PartialEq, Eq)] +pub(crate) enum FnContext { + /// Free context. + Free, + /// A Trait context. + Trait, + /// An Impl block. + Impl, +} + +/// Parsing of functions and methods. +impl<'a> Parser<'a> { + /// Parse a function starting from the front matter (`const ...`) to the body `{ ... }` or `;`. + pub(super) fn parse_fn( + &mut self, + attrs: &mut AttrVec, + fn_parse_mode: FnParseMode, + sig_lo: Span, + vis: &Visibility, + case: Case, + ) -> PResult<'a, (Ident, FnSig, Generics, Option>, Option>)> { + let fn_span = self.token.span; + let header = self.parse_fn_front_matter(vis, case, FrontMatterParsingMode::Function)?; // `const ... fn` + let ident = self.parse_ident()?; // `foo` + let mut generics = self.parse_generics()?; // `<'a, T, ...>` + let decl = match self.parse_fn_decl(&fn_parse_mode, AllowPlus::Yes, RecoverReturnSign::Yes) + { + Ok(decl) => decl, + Err(old_err) => { + // If we see `for Ty ...` then user probably meant `impl` item. + if self.token.is_keyword(kw::For) { + old_err.cancel(); + return Err(self.dcx().create_err(diagnostics::FnTypoWithImpl { fn_span })); + } else { + return Err(old_err); + } + } + }; + + // Store the end of function parameters to give better diagnostics + // inside `parse_fn_body()`. + let fn_params_end = self.prev_token.span.shrink_to_hi(); + + let contract = self.parse_contract()?; + + generics.where_clause = self.parse_where_clause()?; // `where T: Ord` + + // `fn_params_end` is needed only when it's followed by a where clause. + let fn_params_end = + if generics.where_clause.has_where_token { Some(fn_params_end) } else { None }; + + let mut sig_hi = self.prev_token.span; + // Either `;` or `{ ... }`. + let body = + self.parse_fn_body(attrs, &ident, &mut sig_hi, fn_parse_mode.req_body, fn_params_end)?; + let fn_sig_span = sig_lo.to(sig_hi); + Ok((ident, FnSig { header, decl, span: fn_sig_span }, generics, contract, body)) + } + + /// Provide diagnostics when function body is not found + fn error_fn_body_not_found( + &mut self, + ident_span: Span, + req_body: bool, + fn_params_end: Option, + ) -> PResult<'a, ErrorGuaranteed> { + let expected: &[_] = + if req_body { &[exp!(OpenBrace)] } else { &[exp!(Semi), exp!(OpenBrace)] }; + match self.expected_one_of_not_found(&[], expected) { + Ok(error_guaranteed) => Ok(error_guaranteed), + Err(mut err) => { + if self.token == token::CloseBrace { + // The enclosing `mod`, `trait` or `impl` is being closed, so keep the `fn` in + // the AST for typechecking. + err.span_label(ident_span, "while parsing this `fn`"); + Ok(err.emit()) + } else if self.token == token::RArrow + && let Some(fn_params_end) = fn_params_end + { + // Instead of a function body, the parser has encountered a right arrow + // preceded by a where clause. + + // Find whether token behind the right arrow is a function trait and + // store its span. + let fn_trait_span = + [sym::FnOnce, sym::FnMut, sym::Fn].into_iter().find_map(|symbol| { + if self.prev_token.is_ident_named(symbol) { + Some(self.prev_token.span) + } else { + None + } + }); + + // Parse the return type (along with the right arrow) and store its span. + // If there's a parse error, cancel it and return the existing error + // as we are primarily concerned with the + // expected-function-body-but-found-something-else error here. + let arrow_span = self.token.span; + let ty_span = match self.parse_ret_ty( + AllowPlus::Yes, + RecoverQPath::Yes, + RecoverReturnSign::Yes, + ) { + Ok(ty_span) => ty_span.span().shrink_to_hi(), + Err(parse_error) => { + parse_error.cancel(); + return Err(err); + } + }; + let ret_ty_span = arrow_span.to(ty_span); + + if let Some(fn_trait_span) = fn_trait_span { + // Typo'd Fn* trait bounds such as + // fn foo() where F: FnOnce -> () {} + err.subdiagnostic(diagnostics::FnTraitMissingParen { span: fn_trait_span }); + } else if let Ok(snippet) = self.psess.source_map().span_to_snippet(ret_ty_span) + { + // If token behind right arrow is not a Fn* trait, the programmer + // probably misplaced the return type after the where clause like + // `fn foo() where T: Default -> u8 {}` + err.primary_message( + "return type should be specified after the function parameters", + ); + err.subdiagnostic(diagnostics::MisplacedReturnType { + fn_params_end, + snippet, + ret_ty_span, + }); + } + Err(err) + } else { + Err(err) + } + } + } + } + + /// Parse the "body" of a function. + /// This can either be `;` when there's no body, + /// or e.g. a block when the function is a provided one. + fn parse_fn_body( + &mut self, + attrs: &mut AttrVec, + ident: &Ident, + sig_hi: &mut Span, + req_body: bool, + fn_params_end: Option, + ) -> PResult<'a, Option>> { + let has_semi = if req_body { + self.token == TokenKind::Semi + } else { + // Only include `;` in list of expected tokens if body is not required + self.check(exp!(Semi)) + }; + let (inner_attrs, body) = if has_semi { + // Include the trailing semicolon in the span of the signature + self.expect_semi()?; + *sig_hi = self.prev_token.span; + (AttrVec::new(), None) + } else if self.check(exp!(OpenBrace)) || self.token.is_metavar_block() { + let prev_in_fn_body = self.in_fn_body; + self.in_fn_body = true; + let res = self.parse_block_common(self.token.span, BlockCheckMode::Default, None).map( + |(attrs, mut body)| { + if let Some(guar) = self.fn_body_missing_semi_guar.take() { + body.stmts.push(self.mk_stmt( + body.span, + StmtKind::Expr(self.mk_expr(body.span, ExprKind::Err(guar))), + )); + } + (attrs, Some(body)) + }, + ); + self.in_fn_body = prev_in_fn_body; + res? + } else if self.token == token::Eq { + // Recover `fn foo() = $expr;`. + self.bump(); // `=` + let eq_sp = self.prev_token.span; + let _ = self.parse_expr()?; + self.expect_semi()?; // `;` + let span = eq_sp.to(self.prev_token.span); + let guar = self.dcx().emit_err(diagnostics::FunctionBodyEqualsExpr { + span, + sugg: diagnostics::FunctionBodyEqualsExprSugg { + eq: eq_sp, + semi: self.prev_token.span, + }, + }); + (AttrVec::new(), Some(self.mk_block_err(span, guar))) + } else { + self.error_fn_body_not_found(ident.span, req_body, fn_params_end)?; + (AttrVec::new(), None) + }; + attrs.extend(inner_attrs); + Ok(body) + } + + /// Is the current token the start of an `FnHeader` / not a valid parse? + /// + /// `check_pub` adds additional `pub` to the checks in case users place it + /// wrongly, can be used to ensure `pub` never comes after `default`. + pub(super) fn check_fn_front_matter(&mut self, check_pub: bool, case: Case) -> bool { + const ALL_QUALS: &[ExpKeywordPair] = &[ + exp!(Pub), + exp!(Gen), + exp!(Const), + exp!(Async), + exp!(Unsafe), + exp!(Safe), + exp!(Extern), + ]; + + // We use an over-approximation here. + // `const const`, `fn const` won't parse, but we're not stepping over other syntax either. + // `pub` is added in case users got confused with the ordering like `async pub fn`, + // only if it wasn't preceded by `default` as `default pub` is invalid. + let quals: &[_] = if check_pub { + ALL_QUALS + } else { + &[exp!(Gen), exp!(Const), exp!(Async), exp!(Unsafe), exp!(Safe), exp!(Extern)] + }; + self.check_keyword_case(exp!(Fn), case) // Definitely an `fn`. + // `$qual fn` or `$qual $qual`: + || quals.iter().any(|&exp| self.check_keyword_case(exp, case)) + && self.look_ahead(1, |t| { + // `$qual fn`, e.g. `const fn` or `async fn`. + t.is_keyword_case(kw::Fn, case) + // Two qualifiers `$qual $qual` is enough, e.g. `async unsafe`. + || ( + ( + t.is_non_raw_ident_where(|i| + quals.iter().any(|exp| exp.kw == i.name) + // Rule out 2015 `const async: T = val`. + && i.is_reserved() + ) + || case == Case::Insensitive + && t.is_non_raw_ident_where(|i| quals.iter().any(|exp| { + exp.kw.as_str() == i.name.as_str().to_lowercase() + })) + ) + // Rule out `unsafe extern {`. + && !self.is_unsafe_foreign_mod() + // Rule out `async gen {` and `async gen move {` + && !self.is_async_gen_block() + // Rule out `const unsafe auto` and `const unsafe trait` and `const unsafe impl` + && !self.is_keyword_ahead(2, &[kw::Auto, kw::Trait, kw::Impl]) + ) + }) + // `extern ABI fn` + || self.check_keyword_case(exp!(Extern), case) + // Use `tree_look_ahead` because `ABI` might be a metavariable, + // i.e. an invisible-delimited sequence, and `tree_look_ahead` + // will consider that a single element when looking ahead. + && self.look_ahead(1, |t| t.can_begin_string_literal()) + && (self.tree_look_ahead(2, |tt| { + match tt { + TokenTree::Token(t, _) => t.is_keyword_case(kw::Fn, case), + TokenTree::Delimited(..) => false, + } + }) == Some(true) || + // This branch is only for better diagnostics; `pub`, `unsafe`, etc. are not + // allowed here. + (self.may_recover() + && self.tree_look_ahead(2, |tt| { + match tt { + TokenTree::Token(t, _) => + ALL_QUALS.iter().any(|exp| { + t.is_keyword(exp.kw) + }), + TokenTree::Delimited(..) => false, + } + }) == Some(true) + && self.tree_look_ahead(3, |tt| { + match tt { + TokenTree::Token(t, _) => t.is_keyword_case(kw::Fn, case), + TokenTree::Delimited(..) => false, + } + }) == Some(true) + ) + ) + } + + /// Parses all the "front matter" (or "qualifiers") for a `fn` declaration, + /// up to and including the `fn` keyword. The formal grammar is: + /// + /// ```text + /// Extern = "extern" StringLit? ; + /// FnQual = "const"? "async"? "unsafe"? Extern? ; + /// FnFrontMatter = FnQual "fn" ; + /// ``` + /// + /// `vis` represents the visibility that was already parsed, if any. Use + /// `Visibility::Inherited` when no visibility is known. + /// + /// If `parsing_mode` is `FrontMatterParsingMode::FunctionPtrType`, we error on `const` and `async` qualifiers, + /// which are not allowed in function pointer types. + pub(super) fn parse_fn_front_matter( + &mut self, + orig_vis: &Visibility, + case: Case, + parsing_mode: FrontMatterParsingMode, + ) -> PResult<'a, FnHeader> { + let sp_start = self.token.span; + let constness = self.parse_constness(case); + if parsing_mode == FrontMatterParsingMode::FunctionPtrType + && let Const::Yes(const_span) = constness + { + self.dcx().emit_err(FnPointerCannotBeConst { + span: const_span, + suggestion: const_span.until(self.token.span), + }); + } + + let async_start_sp = self.token.span; + let coroutine_kind = self.parse_coroutine_kind(case); + if parsing_mode == FrontMatterParsingMode::FunctionPtrType + && let Some(ast::CoroutineKind::Async { span: async_span, .. }) = coroutine_kind + { + self.dcx().emit_err(FnPointerCannotBeAsync { + span: async_span, + suggestion: async_span.until(self.token.span), + }); + } + // FIXME(gen_blocks): emit a similar error for `gen fn()` + + let unsafe_start_sp = self.token.span; + let safety = self.parse_safety(case); + + let ext_start_sp = self.token.span; + let ext = self.parse_extern(case); + + if let Some(CoroutineKind::Async { span, .. }) = coroutine_kind { + if span.is_rust_2015() { + self.dcx().emit_err(diagnostics::AsyncFnIn2015 { + span, + help: diagnostics::HelpUseLatestEdition::new(), + }); + } + } + + match coroutine_kind { + Some(CoroutineKind::Gen { span, .. }) | Some(CoroutineKind::AsyncGen { span, .. }) => { + self.psess.gated_spans.gate(sym::gen_blocks, span); + } + Some(CoroutineKind::Async { .. }) | None => {} + } + + if !self.eat_keyword_case(exp!(Fn), case) { + // It is possible for `expect_one_of` to recover given the contents of + // `self.expected_token_types`, therefore, do not use `self.unexpected()` which doesn't + // account for this. + match self.expect_one_of(&[], &[]) { + Ok(Recovered::Yes(_)) => {} + Ok(Recovered::No) => unreachable!(), + Err(mut err) => { + // Qualifier keywords ordering check + enum WrongKw { + Duplicated(Span), + Misplaced(Span), + /// `MisplacedDisallowedQualifier` is only used instead of `Misplaced`, + /// when the misplaced keyword is disallowed by the current `FrontMatterParsingMode`. + /// In this case, we avoid generating the suggestion to swap around the keywords, + /// as we already generated a suggestion to remove the keyword earlier. + MisplacedDisallowedQualifier, + } + + // We may be able to recover + let mut recover_constness = constness; + let mut recover_coroutine_kind = coroutine_kind; + let mut recover_safety = safety; + // This will allow the machine fix to directly place the keyword in the correct place or to indicate + // that the keyword is already present and the second instance should be removed. + let wrong_kw = if self.check_keyword(exp!(Const)) { + match constness { + Const::Yes(sp) => Some(WrongKw::Duplicated(sp)), + Const::No => { + recover_constness = Const::Yes(self.token.span); + match parsing_mode { + FrontMatterParsingMode::Function => { + Some(WrongKw::Misplaced(async_start_sp)) + } + FrontMatterParsingMode::FunctionPtrType => { + self.dcx().emit_err(FnPointerCannotBeConst { + span: self.token.span, + suggestion: self + .token + .span + .with_lo(self.prev_token.span.hi()), + }); + Some(WrongKw::MisplacedDisallowedQualifier) + } + } + } + } + } else if self.check_keyword(exp!(Async)) { + match coroutine_kind { + Some(CoroutineKind::Async { span, .. }) => { + Some(WrongKw::Duplicated(span)) + } + Some(CoroutineKind::AsyncGen { span, .. }) => { + Some(WrongKw::Duplicated(span)) + } + Some(CoroutineKind::Gen { .. }) => { + recover_coroutine_kind = Some(CoroutineKind::AsyncGen { + span: self.token.span, + closure_id: DUMMY_NODE_ID, + return_impl_trait_id: DUMMY_NODE_ID, + }); + // FIXME(gen_blocks): This span is wrong, didn't want to think about it. + Some(WrongKw::Misplaced(unsafe_start_sp)) + } + None => { + recover_coroutine_kind = Some(CoroutineKind::Async { + span: self.token.span, + closure_id: DUMMY_NODE_ID, + return_impl_trait_id: DUMMY_NODE_ID, + }); + match parsing_mode { + FrontMatterParsingMode::Function => { + Some(WrongKw::Misplaced(async_start_sp)) + } + FrontMatterParsingMode::FunctionPtrType => { + self.dcx().emit_err(FnPointerCannotBeAsync { + span: self.token.span, + suggestion: self + .token + .span + .with_lo(self.prev_token.span.hi()), + }); + Some(WrongKw::MisplacedDisallowedQualifier) + } + } + } + } + } else if self.check_keyword(exp!(Unsafe)) { + match safety { + Safety::Unsafe(sp) => Some(WrongKw::Duplicated(sp)), + Safety::Safe(sp) => { + recover_safety = Safety::Unsafe(self.token.span); + Some(WrongKw::Misplaced(sp)) + } + Safety::Default => { + recover_safety = Safety::Unsafe(self.token.span); + Some(WrongKw::Misplaced(ext_start_sp)) + } + } + } else if self.check_keyword(exp!(Safe)) { + match safety { + Safety::Safe(sp) => Some(WrongKw::Duplicated(sp)), + Safety::Unsafe(sp) => { + recover_safety = Safety::Safe(self.token.span); + Some(WrongKw::Misplaced(sp)) + } + Safety::Default => { + recover_safety = Safety::Safe(self.token.span); + Some(WrongKw::Misplaced(ext_start_sp)) + } + } + } else { + None + }; + + // The keyword is already present, suggest removal of the second instance + if let Some(WrongKw::Duplicated(original_sp)) = wrong_kw { + let original_kw = self + .span_to_snippet(original_sp) + .expect("Span extracted directly from keyword should always work"); + + err.span_suggestion_verbose( + self.token_uninterpolated_span(), + format!("`{original_kw}` already used earlier, remove this one"), + "", + Applicability::MachineApplicable, + ) + .span_note(original_sp, format!("`{original_kw}` first seen here")); + } + // The keyword has not been seen yet, suggest correct placement in the function front matter + else if let Some(WrongKw::Misplaced(correct_pos_sp)) = wrong_kw { + let correct_pos_sp = correct_pos_sp.to(self.prev_token.span); + if let Ok(current_qual) = self.span_to_snippet(correct_pos_sp) { + let misplaced_qual_sp = self.token_uninterpolated_span(); + let misplaced_qual = self.span_to_snippet(misplaced_qual_sp).unwrap(); + + err.span_suggestion_verbose( + correct_pos_sp.to(misplaced_qual_sp), + format!("`{misplaced_qual}` must come before `{current_qual}`"), + format!("{misplaced_qual} {current_qual}"), + Applicability::MachineApplicable, + ).note("keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`"); + } + } + // Recover incorrect visibility order such as `async pub` + else if self.check_keyword(exp!(Pub)) { + let sp = sp_start.to(self.prev_token.span); + if let Ok(snippet) = self.span_to_snippet(sp) { + let current_vis = match self.parse_visibility(FollowedByType::No) { + Ok(v) => v, + Err(d) => { + d.cancel(); + return Err(err); + } + }; + let vs = pprust::vis_to_string(¤t_vis); + let vs = vs.trim_end(); + + // There was no explicit visibility + if matches!(orig_vis.kind, VisibilityKind::Inherited) { + err.span_suggestion_verbose( + sp_start.to(self.prev_token.span), + format!("visibility `{vs}` must come before `{snippet}`"), + format!("{vs} {snippet}"), + Applicability::MachineApplicable, + ); + } + // There was an explicit visibility + else { + err.span_suggestion_verbose( + current_vis.span, + "there is already a visibility modifier, remove one", + "", + Applicability::MachineApplicable, + ) + .span_note(orig_vis.span, "explicit visibility first seen here"); + } + } + } + + // FIXME(gen_blocks): add keyword recovery logic for genness + + if let Some(wrong_kw) = wrong_kw + && self.may_recover() + && self.look_ahead(1, |tok| tok.is_keyword_case(kw::Fn, case)) + { + // Advance past the misplaced keyword and `fn` + self.bump(); + self.bump(); + // When we recover from a `MisplacedDisallowedQualifier`, we already emitted an error for the disallowed qualifier + // So we don't emit another error that the qualifier is unexpected. + if matches!(wrong_kw, WrongKw::MisplacedDisallowedQualifier) { + err.cancel(); + } else { + err.emit(); + } + return Ok(FnHeader { + constness: recover_constness, + safety: recover_safety, + coroutine_kind: recover_coroutine_kind, + ext, + }); + } + + return Err(err); + } + } + } + + Ok(FnHeader { constness, safety, coroutine_kind, ext }) + } + + /// Parses the parameter list and result type of a function declaration. + pub(super) fn parse_fn_decl( + &mut self, + fn_parse_mode: &FnParseMode, + ret_allow_plus: AllowPlus, + recover_return_sign: RecoverReturnSign, + ) -> PResult<'a, Box> { + Ok(Box::new(FnDecl { + inputs: self.parse_fn_params(fn_parse_mode)?, + output: self.parse_ret_ty(ret_allow_plus, RecoverQPath::Yes, recover_return_sign)?, + })) + } + + /// Parses the parameter list of a function, including the `(` and `)` delimiters. + pub(super) fn parse_fn_params( + &mut self, + fn_parse_mode: &FnParseMode, + ) -> PResult<'a, ThinVec> { + let mut first_param = true; + // Parse the arguments, starting out with `self` being allowed... + if self.token != TokenKind::OpenParen + // might be typo'd trait impl, handled elsewhere + && !self.token.is_keyword(kw::For) + { + // recover from missing argument list, e.g. `fn main -> () {}` + self.dcx().emit_err(diagnostics::MissingFnParams { + span: self.prev_token.span.shrink_to_hi(), + }); + return Ok(ThinVec::new()); + } + + let (mut params, _) = self.parse_paren_comma_seq(|p| { + p.recover_vcs_conflict_marker(); + let snapshot = p.create_snapshot_for_diagnostic(); + let param = p.parse_param_general(fn_parse_mode, first_param, true).or_else(|e| { + let guar = e.emit(); + // When parsing a param failed, we should check to make the span of the param + // not contain '(' before it. + // For example when parsing `*mut Self` in function `fn oof(*mut Self)`. + let lo = if let TokenKind::OpenParen = p.prev_token.kind { + p.prev_token.span.shrink_to_hi() + } else { + p.prev_token.span + }; + p.restore_snapshot(snapshot); + // Skip every token until next possible arg or end. + p.eat_to_tokens(&[exp!(Comma), exp!(CloseParen)]); + // Create a placeholder argument for proper arg count (issue #34264). + Ok(dummy_arg(Ident::new(sym::dummy, lo.to(p.prev_token.span)), guar)) + }); + // ...now that we've parsed the first argument, `self` is no longer allowed. + first_param = false; + param + })?; + // Replace duplicated recovered params with `_` pattern to avoid unnecessary errors. + self.deduplicate_recovered_params_names(&mut params); + Ok(params) + } + + /// Parses a single function parameter. + /// + /// - `self` is syntactically allowed when `first_param` holds. + /// - `recover_arg_parse` is used to recover from a failed argument parse. + pub(super) fn parse_param_general( + &mut self, + fn_parse_mode: &FnParseMode, + first_param: bool, + recover_arg_parse: bool, + ) -> PResult<'a, Param> { + let lo = self.token.span; + let attrs = self.parse_outer_attributes()?; + self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| { + // Possibly parse `self`. Recover if we parsed it and it wasn't allowed here. + if let Some(mut param) = this.parse_self_param()? { + param.attrs = attrs; + let res = if first_param { Ok(param) } else { this.recover_bad_self_param(param) }; + return Ok((res?, Trailing::No, UsePreAttrPos::No)); + } + + let is_dot_dot_dot = if this.token.kind == token::DotDotDot { + IsDotDotDot::Yes + } else { + IsDotDotDot::No + }; + let is_name_required = (fn_parse_mode.req_name)( + this.token.span.with_neighbor(this.prev_token.span).edition(), + is_dot_dot_dot, + ); + let is_name_required = if is_name_required && is_dot_dot_dot == IsDotDotDot::Yes { + this.psess.buffer_lint( + VARARGS_WITHOUT_PATTERN, + this.token.span, + ast::CRATE_NODE_ID, + diagnostics::VarargsWithoutPattern { span: this.token.span }, + ); + false + } else { + is_name_required + }; + let (pat, ty) = if is_name_required || this.is_named_param() { + debug!("parse_param_general parse_pat (is_name_required:{})", is_name_required); + let (pat, colon) = this.parse_fn_param_pat_colon()?; + if !colon { + let mut err = this.unexpected().unwrap_err(); + let pat_span = pat.span; + return if let Some(ident) = this.parameter_without_type( + &mut err, + pat, + is_name_required, + first_param, + fn_parse_mode, + ) { + let guar = err.emit(); + let mut arg = dummy_arg(ident, guar); + arg.span = pat_span; + Ok((arg, Trailing::No, UsePreAttrPos::No)) + } else { + Err(err) + }; + } + + this.eat_incorrect_doc_comment_for_param_type(); + (pat, this.parse_ty_for_param()?) + } else { + debug!("parse_param_general ident_to_pat"); + let parser_snapshot_before_ty = this.create_snapshot_for_diagnostic(); + this.eat_incorrect_doc_comment_for_param_type(); + let mut ty = this.parse_ty_for_param(); + + if let Ok(t) = &ty { + // Check for trailing angle brackets + if let TyKind::Path(_, Path { segments, .. }) = &t.kind + && let Some(segment) = segments.last() + && let Some(guar) = + this.check_trailing_angle_brackets(segment, &[exp!(CloseParen)]) + { + return Ok(( + dummy_arg(segment.ident, guar), + Trailing::No, + UsePreAttrPos::No, + )); + } + + if this.token != token::Comma && this.token != token::CloseParen { + // This wasn't actually a type, but a pattern looking like a type, + // so we are going to rollback and re-parse for recovery. + ty = this.unexpected_any(); + } + } + match ty { + Ok(ty) => { + let pat = this.mk_pat(ty.span, PatKind::Missing); + (Box::new(pat), ty) + } + // If this is a C-variadic argument and we hit an error, return the error. + Err(err) if this.token == token::DotDotDot => return Err(err), + Err(err) if this.unmatched_angle_bracket_count > 0 => return Err(err), + Err(err) if recover_arg_parse => { + // Recover from attempting to parse the argument as a type without pattern. + err.cancel(); + this.restore_snapshot(parser_snapshot_before_ty); + this.recover_arg_parse()? + } + Err(err) => return Err(err), + } + }; + + let span = lo.to(this.prev_token.span); + + Ok(( + Param { attrs, id: ast::DUMMY_NODE_ID, is_placeholder: false, pat, span, ty }, + Trailing::No, + UsePreAttrPos::No, + )) + }) + } + + /// Returns the parsed optional self parameter and whether a self shortcut was used. + fn parse_self_param(&mut self) -> PResult<'a, Option> { + // Extract an identifier *after* having confirmed that the token is one. + let expect_self_ident = |this: &mut Self| match this.token.ident() { + Some((ident, IdentIsRaw::No)) => { + this.bump(); + ident + } + _ => unreachable!(), + }; + // is lifetime `n` tokens ahead? + let is_lifetime = |this: &Self, n| this.look_ahead(n, |t| t.is_lifetime()); + // Is `self` `n` tokens ahead? + let is_isolated_self = |this: &Self, n| { + this.is_keyword_ahead(n, &[kw::SelfLower]) + && this.look_ahead(n + 1, |t| t != &token::PathSep) + }; + // Is `pin const self` `n` tokens ahead? + let is_isolated_pin_const_self = |this: &Self, n| { + this.look_ahead(n, |token| token.is_ident_named(sym::pin)) + && this.is_keyword_ahead(n + 1, &[kw::Const]) + && is_isolated_self(this, n + 2) + }; + // Is `mut self` `n` tokens ahead? + let is_isolated_mut_self = + |this: &Self, n| this.is_keyword_ahead(n, &[kw::Mut]) && is_isolated_self(this, n + 1); + // Is `pin mut self` `n` tokens ahead? + let is_isolated_pin_mut_self = |this: &Self, n| { + this.look_ahead(n, |token| token.is_ident_named(sym::pin)) + && is_isolated_mut_self(this, n + 1) + }; + // Parse `self` or `self: TYPE`. We already know the current token is `self`. + let parse_self_possibly_typed = |this: &mut Self, m| { + let eself_ident = expect_self_ident(this); + let eself_hi = this.prev_token.span; + let eself = if this.eat(exp!(Colon)) { + SelfKind::Explicit(this.parse_ty()?, m) + } else { + SelfKind::Value(m) + }; + Ok((eself, eself_ident, eself_hi)) + }; + let expect_self_ident_not_typed = + |this: &mut Self, modifier: &SelfKind, modifier_span: Span| { + let eself_ident = expect_self_ident(this); + + // Recover `: Type` after a qualified self + if this.may_recover() && this.eat_noexpect(&token::Colon) { + let snap = this.create_snapshot_for_diagnostic(); + match this.parse_ty() { + Ok(ty) => { + this.dcx().emit_err(diagnostics::IncorrectTypeOnSelf { + span: ty.span, + move_self_modifier: diagnostics::MoveSelfModifier { + removal_span: modifier_span, + insertion_span: ty.span.shrink_to_lo(), + modifier: modifier.to_ref_suggestion(), + }, + }); + } + Err(diag) => { + diag.cancel(); + this.restore_snapshot(snap); + } + } + } + eself_ident + }; + // Recover for the grammar `*self`, `*const self`, and `*mut self`. + let recover_self_ptr = |this: &mut Self| { + this.dcx().emit_err(diagnostics::SelfArgumentPointer { span: this.token.span }); + + Ok((SelfKind::Value(Mutability::Not), expect_self_ident(this), this.prev_token.span)) + }; + + // Parse optional `self` parameter of a method. + // Only a limited set of initial token sequences is considered `self` parameters; anything + // else is parsed as a normal function parameter list, so some lookahead is required. + let eself_lo = self.token.span; + let (eself, eself_ident, eself_hi) = match self.token.uninterpolate().kind { + token::And => { + let has_lifetime = is_lifetime(self, 1); + let skip_lifetime_count = has_lifetime as usize; + let eself = if is_isolated_self(self, skip_lifetime_count + 1) { + // `&{'lt} self` + self.bump(); // & + let lifetime = has_lifetime.then(|| self.expect_lifetime()); + SelfKind::Region(lifetime, Mutability::Not) + } else if is_isolated_mut_self(self, skip_lifetime_count + 1) { + // `&{'lt} mut self` + self.bump(); // & + let lifetime = has_lifetime.then(|| self.expect_lifetime()); + self.bump(); // mut + SelfKind::Region(lifetime, Mutability::Mut) + } else if is_isolated_pin_const_self(self, skip_lifetime_count + 1) { + // `&{'lt} pin const self` + self.bump(); // & + let lifetime = has_lifetime.then(|| self.expect_lifetime()); + self.psess.gated_spans.gate(sym::pin_ergonomics, self.token.span); + self.bump(); // pin + self.bump(); // const + SelfKind::Pinned(lifetime, Mutability::Not) + } else if is_isolated_pin_mut_self(self, skip_lifetime_count + 1) { + // `&{'lt} pin mut self` + self.bump(); // & + let lifetime = has_lifetime.then(|| self.expect_lifetime()); + self.psess.gated_spans.gate(sym::pin_ergonomics, self.token.span); + self.bump(); // pin + self.bump(); // mut + SelfKind::Pinned(lifetime, Mutability::Mut) + } else { + // `¬_self` + return Ok(None); + }; + let hi = self.token.span; + let self_ident = expect_self_ident_not_typed(self, &eself, eself_lo.until(hi)); + (eself, self_ident, hi) + } + // `*self` + token::Star if is_isolated_self(self, 1) => { + self.bump(); + recover_self_ptr(self)? + } + // `*mut self` and `*const self` + token::Star + if self.look_ahead(1, |t| t.is_mutability()) && is_isolated_self(self, 2) => + { + self.bump(); + self.bump(); + recover_self_ptr(self)? + } + // `self` and `self: TYPE` + token::Ident(..) if is_isolated_self(self, 0) => { + parse_self_possibly_typed(self, Mutability::Not)? + } + // `mut self` and `mut self: TYPE` + token::Ident(..) if is_isolated_mut_self(self, 0) => { + self.bump(); + parse_self_possibly_typed(self, Mutability::Mut)? + } + _ => return Ok(None), + }; + + let eself = respan(eself_lo.to(eself_hi), eself); + Ok(Some(Param::from_self(AttrVec::default(), eself, eself_ident))) + } + + fn is_named_param(&self) -> bool { + let offset = match &self.token.kind { + token::OpenInvisible(origin) => match origin { + InvisibleOrigin::MetaVar(MetaVarKind::Pat(_)) => { + return self.check_noexpect_past_close_delim(&token::Colon); + } + _ => 0, + }, + token::And | token::AndAnd => 1, + _ if self.token.is_keyword(kw::Mut) => 1, + _ => 0, + }; + + self.look_ahead(offset, |t| t.is_ident()) + && self.look_ahead(offset + 1, |t| t == &token::Colon) + } + + pub(super) fn recover_self_param(&mut self) -> bool { + matches!( + self.parse_outer_attributes() + .and_then(|_| self.parse_self_param()) + .map_err(|e| e.cancel()), + Ok(Some(_)) + ) + } +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub(crate) enum FrontMatterParsingMode { + /// Parse the front matter of a function declaration + Function, + /// Parse the front matter of a function pointet type. + /// For function pointer types, the `const` and `async` keywords are not permitted. + FunctionPtrType, +} diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 1b06c80f83687..68d2e27d1b77a 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -4,28 +4,25 @@ use std::mem; use ast::token::IdentIsRaw; use rustc_ast as ast; use rustc_ast::ast::*; -use rustc_ast::token::{self, Delimiter, InvisibleOrigin, MetaVarKind, TokenKind}; +use rustc_ast::token::{self, Delimiter, MetaVarKind, TokenKind}; use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; use rustc_ast::util::case::Case; use rustc_ast_pretty::pprust; use rustc_errors::codes::*; use rustc_errors::{Applicability, PResult, StashKey, msg, struct_span_code_err}; -use rustc_session::lint::builtin::VARARGS_WITHOUT_PATTERN; use rustc_span::edit_distance::edit_distance; use rustc_span::edition::Edition; -use rustc_span::{DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, respan, sym}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use tracing::debug; -use super::diagnostics::{ConsumeClosingDelim, dummy_arg}; -use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; +use super::diagnostics::ConsumeClosingDelim; use super::{ - AllowConstBlockItems, AttrWrapper, ExpKeywordPair, ExpTokenPair, FollowedByType, ForceCollect, - Parser, PathStyle, Recovered, Trailing, UsePreAttrPos, + AllowConstBlockItems, AttrWrapper, ExpTokenPair, FnContext, FnParseMode, FollowedByType, + ForceCollect, IsDotDotDot, Parser, PathStyle, Recovered, Trailing, UsePreAttrPos, }; use crate::diagnostics::{ - self, FnPointerCannotBeAsync, FnPointerCannotBeConst, MacroExpandsToAdtField, - UseDoubleColonSuggestion, UseRegularStructSuggestion, + self, MacroExpandsToAdtField, UseDoubleColonSuggestion, UseRegularStructSuggestion, }; use crate::exp; @@ -1566,7 +1563,7 @@ impl<'a> Parser<'a> { } } - fn is_unsafe_foreign_mod(&self) -> bool { + pub(super) fn is_unsafe_foreign_mod(&self) -> bool { // Look for `unsafe`. if !self.token.is_keyword(kw::Unsafe) { return false; @@ -2687,282 +2684,6 @@ impl<'a> Parser<'a> { } Ok(true) } -} - -/// The parsing configuration used to parse a parameter list (see `parse_fn_params`). -/// -/// The function decides if, per-parameter `p`, `p` must have a pattern or just a type. -/// -/// This function pointer accepts an edition, because in edition 2015, trait declarations -/// were allowed to omit parameter names. In 2018, they became required. It also accepts an -/// `IsDotDotDot` parameter, as `extern` function declarations and function pointer types are -/// allowed to omit the name of the `...` but regular function items are not. -type ReqName = fn(Edition, IsDotDotDot) -> bool; - -#[derive(Copy, Clone, PartialEq)] -pub(crate) enum IsDotDotDot { - Yes, - No, -} - -/// Parsing configuration for functions. -/// -/// The syntax of function items is slightly different within trait definitions, -/// impl blocks, and modules. It is still parsed using the same code, just with -/// different flags set, so that even when the input is wrong and produces a parse -/// error, it still gets into the AST and the rest of the parser and -/// type checker can run. -#[derive(Clone, Copy)] -pub(crate) struct FnParseMode { - /// A function pointer that decides if, per-parameter `p`, `p` must have a - /// pattern or just a type. This field affects parsing of the parameters list. - /// - /// ```text - /// fn foo(alef: A) -> X { X::new() } - /// -----^^ affects parsing this part of the function signature - /// | - /// if req_name returns false, then this name is optional - /// - /// fn bar(A) -> X; - /// ^ - /// | - /// if req_name returns true, this is an error - /// ``` - /// - /// Calling this function pointer should only return false if: - /// - /// * The item is being parsed inside of a trait definition. - /// Within an impl block or a module, it should always evaluate - /// to true. - /// * The span is from Edition 2015. In particular, you can get a - /// 2015 span inside a 2021 crate using macros. - /// - /// Or if `IsDotDotDot::Yes`, this function will also return `false` if the item being parsed - /// is inside an `extern` block. - pub(super) req_name: ReqName, - /// The context in which this function is parsed, used for diagnostics. - /// This indicates the fn is a free function or method and so on. - pub(super) context: FnContext, - /// If this flag is set to `true`, then plain, semicolon-terminated function - /// prototypes are not allowed here. - /// - /// ```text - /// fn foo(alef: A) -> X { X::new() } - /// ^^^^^^^^^^^^ - /// | - /// this is always allowed - /// - /// fn bar(alef: A, bet: B) -> X; - /// ^ - /// | - /// if req_body is set to true, this is an error - /// ``` - /// - /// This field should only be set to false if the item is inside of a trait - /// definition or extern block. Within an impl block or a module, it should - /// always be set to true. - pub(super) req_body: bool, -} - -/// The context in which a function is parsed. -/// FIXME(estebank, xizheyin): Use more variants. -#[derive(Clone, Copy, PartialEq, Eq)] -pub(crate) enum FnContext { - /// Free context. - Free, - /// A Trait context. - Trait, - /// An Impl block. - Impl, -} - -/// Parsing of functions and methods. -impl<'a> Parser<'a> { - /// Parse a function starting from the front matter (`const ...`) to the body `{ ... }` or `;`. - fn parse_fn( - &mut self, - attrs: &mut AttrVec, - fn_parse_mode: FnParseMode, - sig_lo: Span, - vis: &Visibility, - case: Case, - ) -> PResult<'a, (Ident, FnSig, Generics, Option>, Option>)> { - let fn_span = self.token.span; - let header = self.parse_fn_front_matter(vis, case, FrontMatterParsingMode::Function)?; // `const ... fn` - let ident = self.parse_ident()?; // `foo` - let mut generics = self.parse_generics()?; // `<'a, T, ...>` - let decl = match self.parse_fn_decl(&fn_parse_mode, AllowPlus::Yes, RecoverReturnSign::Yes) - { - Ok(decl) => decl, - Err(old_err) => { - // If we see `for Ty ...` then user probably meant `impl` item. - if self.token.is_keyword(kw::For) { - old_err.cancel(); - return Err(self.dcx().create_err(diagnostics::FnTypoWithImpl { fn_span })); - } else { - return Err(old_err); - } - } - }; - - // Store the end of function parameters to give better diagnostics - // inside `parse_fn_body()`. - let fn_params_end = self.prev_token.span.shrink_to_hi(); - - let contract = self.parse_contract()?; - - generics.where_clause = self.parse_where_clause()?; // `where T: Ord` - - // `fn_params_end` is needed only when it's followed by a where clause. - let fn_params_end = - if generics.where_clause.has_where_token { Some(fn_params_end) } else { None }; - - let mut sig_hi = self.prev_token.span; - // Either `;` or `{ ... }`. - let body = - self.parse_fn_body(attrs, &ident, &mut sig_hi, fn_parse_mode.req_body, fn_params_end)?; - let fn_sig_span = sig_lo.to(sig_hi); - Ok((ident, FnSig { header, decl, span: fn_sig_span }, generics, contract, body)) - } - - /// Provide diagnostics when function body is not found - fn error_fn_body_not_found( - &mut self, - ident_span: Span, - req_body: bool, - fn_params_end: Option, - ) -> PResult<'a, ErrorGuaranteed> { - let expected: &[_] = - if req_body { &[exp!(OpenBrace)] } else { &[exp!(Semi), exp!(OpenBrace)] }; - match self.expected_one_of_not_found(&[], expected) { - Ok(error_guaranteed) => Ok(error_guaranteed), - Err(mut err) => { - if self.token == token::CloseBrace { - // The enclosing `mod`, `trait` or `impl` is being closed, so keep the `fn` in - // the AST for typechecking. - err.span_label(ident_span, "while parsing this `fn`"); - Ok(err.emit()) - } else if self.token == token::RArrow - && let Some(fn_params_end) = fn_params_end - { - // Instead of a function body, the parser has encountered a right arrow - // preceded by a where clause. - - // Find whether token behind the right arrow is a function trait and - // store its span. - let fn_trait_span = - [sym::FnOnce, sym::FnMut, sym::Fn].into_iter().find_map(|symbol| { - if self.prev_token.is_ident_named(symbol) { - Some(self.prev_token.span) - } else { - None - } - }); - - // Parse the return type (along with the right arrow) and store its span. - // If there's a parse error, cancel it and return the existing error - // as we are primarily concerned with the - // expected-function-body-but-found-something-else error here. - let arrow_span = self.token.span; - let ty_span = match self.parse_ret_ty( - AllowPlus::Yes, - RecoverQPath::Yes, - RecoverReturnSign::Yes, - ) { - Ok(ty_span) => ty_span.span().shrink_to_hi(), - Err(parse_error) => { - parse_error.cancel(); - return Err(err); - } - }; - let ret_ty_span = arrow_span.to(ty_span); - - if let Some(fn_trait_span) = fn_trait_span { - // Typo'd Fn* trait bounds such as - // fn foo() where F: FnOnce -> () {} - err.subdiagnostic(diagnostics::FnTraitMissingParen { span: fn_trait_span }); - } else if let Ok(snippet) = self.psess.source_map().span_to_snippet(ret_ty_span) - { - // If token behind right arrow is not a Fn* trait, the programmer - // probably misplaced the return type after the where clause like - // `fn foo() where T: Default -> u8 {}` - err.primary_message( - "return type should be specified after the function parameters", - ); - err.subdiagnostic(diagnostics::MisplacedReturnType { - fn_params_end, - snippet, - ret_ty_span, - }); - } - Err(err) - } else { - Err(err) - } - } - } - } - - /// Parse the "body" of a function. - /// This can either be `;` when there's no body, - /// or e.g. a block when the function is a provided one. - fn parse_fn_body( - &mut self, - attrs: &mut AttrVec, - ident: &Ident, - sig_hi: &mut Span, - req_body: bool, - fn_params_end: Option, - ) -> PResult<'a, Option>> { - let has_semi = if req_body { - self.token == TokenKind::Semi - } else { - // Only include `;` in list of expected tokens if body is not required - self.check(exp!(Semi)) - }; - let (inner_attrs, body) = if has_semi { - // Include the trailing semicolon in the span of the signature - self.expect_semi()?; - *sig_hi = self.prev_token.span; - (AttrVec::new(), None) - } else if self.check(exp!(OpenBrace)) || self.token.is_metavar_block() { - let prev_in_fn_body = self.in_fn_body; - self.in_fn_body = true; - let res = self.parse_block_common(self.token.span, BlockCheckMode::Default, None).map( - |(attrs, mut body)| { - if let Some(guar) = self.fn_body_missing_semi_guar.take() { - body.stmts.push(self.mk_stmt( - body.span, - StmtKind::Expr(self.mk_expr(body.span, ExprKind::Err(guar))), - )); - } - (attrs, Some(body)) - }, - ); - self.in_fn_body = prev_in_fn_body; - res? - } else if self.token == token::Eq { - // Recover `fn foo() = $expr;`. - self.bump(); // `=` - let eq_sp = self.prev_token.span; - let _ = self.parse_expr()?; - self.expect_semi()?; // `;` - let span = eq_sp.to(self.prev_token.span); - let guar = self.dcx().emit_err(diagnostics::FunctionBodyEqualsExpr { - span, - sugg: diagnostics::FunctionBodyEqualsExprSugg { - eq: eq_sp, - semi: self.prev_token.span, - }, - }); - (AttrVec::new(), Some(self.mk_block_err(span, guar))) - } else { - self.error_fn_body_not_found(ident.span, req_body, fn_params_end)?; - (AttrVec::new(), None) - }; - attrs.extend(inner_attrs); - Ok(body) - } fn check_impl_frontmatter(&mut self, look_ahead: usize) -> bool { const ALL_QUALS: &[Symbol] = &[kw::Const, kw::Unsafe]; @@ -2992,718 +2713,6 @@ impl<'a> Parser<'a> { self.is_keyword_ahead(i, &[kw::Impl]) } - /// Is the current token the start of an `FnHeader` / not a valid parse? - /// - /// `check_pub` adds additional `pub` to the checks in case users place it - /// wrongly, can be used to ensure `pub` never comes after `default`. - pub(super) fn check_fn_front_matter(&mut self, check_pub: bool, case: Case) -> bool { - const ALL_QUALS: &[ExpKeywordPair] = &[ - exp!(Pub), - exp!(Gen), - exp!(Const), - exp!(Async), - exp!(Unsafe), - exp!(Safe), - exp!(Extern), - ]; - - // We use an over-approximation here. - // `const const`, `fn const` won't parse, but we're not stepping over other syntax either. - // `pub` is added in case users got confused with the ordering like `async pub fn`, - // only if it wasn't preceded by `default` as `default pub` is invalid. - let quals: &[_] = if check_pub { - ALL_QUALS - } else { - &[exp!(Gen), exp!(Const), exp!(Async), exp!(Unsafe), exp!(Safe), exp!(Extern)] - }; - self.check_keyword_case(exp!(Fn), case) // Definitely an `fn`. - // `$qual fn` or `$qual $qual`: - || quals.iter().any(|&exp| self.check_keyword_case(exp, case)) - && self.look_ahead(1, |t| { - // `$qual fn`, e.g. `const fn` or `async fn`. - t.is_keyword_case(kw::Fn, case) - // Two qualifiers `$qual $qual` is enough, e.g. `async unsafe`. - || ( - ( - t.is_non_raw_ident_where(|i| - quals.iter().any(|exp| exp.kw == i.name) - // Rule out 2015 `const async: T = val`. - && i.is_reserved() - ) - || case == Case::Insensitive - && t.is_non_raw_ident_where(|i| quals.iter().any(|exp| { - exp.kw.as_str() == i.name.as_str().to_lowercase() - })) - ) - // Rule out `unsafe extern {`. - && !self.is_unsafe_foreign_mod() - // Rule out `async gen {` and `async gen move {` - && !self.is_async_gen_block() - // Rule out `const unsafe auto` and `const unsafe trait` and `const unsafe impl` - && !self.is_keyword_ahead(2, &[kw::Auto, kw::Trait, kw::Impl]) - ) - }) - // `extern ABI fn` - || self.check_keyword_case(exp!(Extern), case) - // Use `tree_look_ahead` because `ABI` might be a metavariable, - // i.e. an invisible-delimited sequence, and `tree_look_ahead` - // will consider that a single element when looking ahead. - && self.look_ahead(1, |t| t.can_begin_string_literal()) - && (self.tree_look_ahead(2, |tt| { - match tt { - TokenTree::Token(t, _) => t.is_keyword_case(kw::Fn, case), - TokenTree::Delimited(..) => false, - } - }) == Some(true) || - // This branch is only for better diagnostics; `pub`, `unsafe`, etc. are not - // allowed here. - (self.may_recover() - && self.tree_look_ahead(2, |tt| { - match tt { - TokenTree::Token(t, _) => - ALL_QUALS.iter().any(|exp| { - t.is_keyword(exp.kw) - }), - TokenTree::Delimited(..) => false, - } - }) == Some(true) - && self.tree_look_ahead(3, |tt| { - match tt { - TokenTree::Token(t, _) => t.is_keyword_case(kw::Fn, case), - TokenTree::Delimited(..) => false, - } - }) == Some(true) - ) - ) - } - - /// Parses all the "front matter" (or "qualifiers") for a `fn` declaration, - /// up to and including the `fn` keyword. The formal grammar is: - /// - /// ```text - /// Extern = "extern" StringLit? ; - /// FnQual = "const"? "async"? "unsafe"? Extern? ; - /// FnFrontMatter = FnQual "fn" ; - /// ``` - /// - /// `vis` represents the visibility that was already parsed, if any. Use - /// `Visibility::Inherited` when no visibility is known. - /// - /// If `parsing_mode` is `FrontMatterParsingMode::FunctionPtrType`, we error on `const` and `async` qualifiers, - /// which are not allowed in function pointer types. - pub(super) fn parse_fn_front_matter( - &mut self, - orig_vis: &Visibility, - case: Case, - parsing_mode: FrontMatterParsingMode, - ) -> PResult<'a, FnHeader> { - let sp_start = self.token.span; - let constness = self.parse_constness(case); - if parsing_mode == FrontMatterParsingMode::FunctionPtrType - && let Const::Yes(const_span) = constness - { - self.dcx().emit_err(FnPointerCannotBeConst { - span: const_span, - suggestion: const_span.until(self.token.span), - }); - } - - let async_start_sp = self.token.span; - let coroutine_kind = self.parse_coroutine_kind(case); - if parsing_mode == FrontMatterParsingMode::FunctionPtrType - && let Some(ast::CoroutineKind::Async { span: async_span, .. }) = coroutine_kind - { - self.dcx().emit_err(FnPointerCannotBeAsync { - span: async_span, - suggestion: async_span.until(self.token.span), - }); - } - // FIXME(gen_blocks): emit a similar error for `gen fn()` - - let unsafe_start_sp = self.token.span; - let safety = self.parse_safety(case); - - let ext_start_sp = self.token.span; - let ext = self.parse_extern(case); - - if let Some(CoroutineKind::Async { span, .. }) = coroutine_kind { - if span.is_rust_2015() { - self.dcx().emit_err(diagnostics::AsyncFnIn2015 { - span, - help: diagnostics::HelpUseLatestEdition::new(), - }); - } - } - - match coroutine_kind { - Some(CoroutineKind::Gen { span, .. }) | Some(CoroutineKind::AsyncGen { span, .. }) => { - self.psess.gated_spans.gate(sym::gen_blocks, span); - } - Some(CoroutineKind::Async { .. }) | None => {} - } - - if !self.eat_keyword_case(exp!(Fn), case) { - // It is possible for `expect_one_of` to recover given the contents of - // `self.expected_token_types`, therefore, do not use `self.unexpected()` which doesn't - // account for this. - match self.expect_one_of(&[], &[]) { - Ok(Recovered::Yes(_)) => {} - Ok(Recovered::No) => unreachable!(), - Err(mut err) => { - // Qualifier keywords ordering check - enum WrongKw { - Duplicated(Span), - Misplaced(Span), - /// `MisplacedDisallowedQualifier` is only used instead of `Misplaced`, - /// when the misplaced keyword is disallowed by the current `FrontMatterParsingMode`. - /// In this case, we avoid generating the suggestion to swap around the keywords, - /// as we already generated a suggestion to remove the keyword earlier. - MisplacedDisallowedQualifier, - } - - // We may be able to recover - let mut recover_constness = constness; - let mut recover_coroutine_kind = coroutine_kind; - let mut recover_safety = safety; - // This will allow the machine fix to directly place the keyword in the correct place or to indicate - // that the keyword is already present and the second instance should be removed. - let wrong_kw = if self.check_keyword(exp!(Const)) { - match constness { - Const::Yes(sp) => Some(WrongKw::Duplicated(sp)), - Const::No => { - recover_constness = Const::Yes(self.token.span); - match parsing_mode { - FrontMatterParsingMode::Function => { - Some(WrongKw::Misplaced(async_start_sp)) - } - FrontMatterParsingMode::FunctionPtrType => { - self.dcx().emit_err(FnPointerCannotBeConst { - span: self.token.span, - suggestion: self - .token - .span - .with_lo(self.prev_token.span.hi()), - }); - Some(WrongKw::MisplacedDisallowedQualifier) - } - } - } - } - } else if self.check_keyword(exp!(Async)) { - match coroutine_kind { - Some(CoroutineKind::Async { span, .. }) => { - Some(WrongKw::Duplicated(span)) - } - Some(CoroutineKind::AsyncGen { span, .. }) => { - Some(WrongKw::Duplicated(span)) - } - Some(CoroutineKind::Gen { .. }) => { - recover_coroutine_kind = Some(CoroutineKind::AsyncGen { - span: self.token.span, - closure_id: DUMMY_NODE_ID, - return_impl_trait_id: DUMMY_NODE_ID, - }); - // FIXME(gen_blocks): This span is wrong, didn't want to think about it. - Some(WrongKw::Misplaced(unsafe_start_sp)) - } - None => { - recover_coroutine_kind = Some(CoroutineKind::Async { - span: self.token.span, - closure_id: DUMMY_NODE_ID, - return_impl_trait_id: DUMMY_NODE_ID, - }); - match parsing_mode { - FrontMatterParsingMode::Function => { - Some(WrongKw::Misplaced(async_start_sp)) - } - FrontMatterParsingMode::FunctionPtrType => { - self.dcx().emit_err(FnPointerCannotBeAsync { - span: self.token.span, - suggestion: self - .token - .span - .with_lo(self.prev_token.span.hi()), - }); - Some(WrongKw::MisplacedDisallowedQualifier) - } - } - } - } - } else if self.check_keyword(exp!(Unsafe)) { - match safety { - Safety::Unsafe(sp) => Some(WrongKw::Duplicated(sp)), - Safety::Safe(sp) => { - recover_safety = Safety::Unsafe(self.token.span); - Some(WrongKw::Misplaced(sp)) - } - Safety::Default => { - recover_safety = Safety::Unsafe(self.token.span); - Some(WrongKw::Misplaced(ext_start_sp)) - } - } - } else if self.check_keyword(exp!(Safe)) { - match safety { - Safety::Safe(sp) => Some(WrongKw::Duplicated(sp)), - Safety::Unsafe(sp) => { - recover_safety = Safety::Safe(self.token.span); - Some(WrongKw::Misplaced(sp)) - } - Safety::Default => { - recover_safety = Safety::Safe(self.token.span); - Some(WrongKw::Misplaced(ext_start_sp)) - } - } - } else { - None - }; - - // The keyword is already present, suggest removal of the second instance - if let Some(WrongKw::Duplicated(original_sp)) = wrong_kw { - let original_kw = self - .span_to_snippet(original_sp) - .expect("Span extracted directly from keyword should always work"); - - err.span_suggestion_verbose( - self.token_uninterpolated_span(), - format!("`{original_kw}` already used earlier, remove this one"), - "", - Applicability::MachineApplicable, - ) - .span_note(original_sp, format!("`{original_kw}` first seen here")); - } - // The keyword has not been seen yet, suggest correct placement in the function front matter - else if let Some(WrongKw::Misplaced(correct_pos_sp)) = wrong_kw { - let correct_pos_sp = correct_pos_sp.to(self.prev_token.span); - if let Ok(current_qual) = self.span_to_snippet(correct_pos_sp) { - let misplaced_qual_sp = self.token_uninterpolated_span(); - let misplaced_qual = self.span_to_snippet(misplaced_qual_sp).unwrap(); - - err.span_suggestion_verbose( - correct_pos_sp.to(misplaced_qual_sp), - format!("`{misplaced_qual}` must come before `{current_qual}`"), - format!("{misplaced_qual} {current_qual}"), - Applicability::MachineApplicable, - ).note("keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`"); - } - } - // Recover incorrect visibility order such as `async pub` - else if self.check_keyword(exp!(Pub)) { - let sp = sp_start.to(self.prev_token.span); - if let Ok(snippet) = self.span_to_snippet(sp) { - let current_vis = match self.parse_visibility(FollowedByType::No) { - Ok(v) => v, - Err(d) => { - d.cancel(); - return Err(err); - } - }; - let vs = pprust::vis_to_string(¤t_vis); - let vs = vs.trim_end(); - - // There was no explicit visibility - if matches!(orig_vis.kind, VisibilityKind::Inherited) { - err.span_suggestion_verbose( - sp_start.to(self.prev_token.span), - format!("visibility `{vs}` must come before `{snippet}`"), - format!("{vs} {snippet}"), - Applicability::MachineApplicable, - ); - } - // There was an explicit visibility - else { - err.span_suggestion_verbose( - current_vis.span, - "there is already a visibility modifier, remove one", - "", - Applicability::MachineApplicable, - ) - .span_note(orig_vis.span, "explicit visibility first seen here"); - } - } - } - - // FIXME(gen_blocks): add keyword recovery logic for genness - - if let Some(wrong_kw) = wrong_kw - && self.may_recover() - && self.look_ahead(1, |tok| tok.is_keyword_case(kw::Fn, case)) - { - // Advance past the misplaced keyword and `fn` - self.bump(); - self.bump(); - // When we recover from a `MisplacedDisallowedQualifier`, we already emitted an error for the disallowed qualifier - // So we don't emit another error that the qualifier is unexpected. - if matches!(wrong_kw, WrongKw::MisplacedDisallowedQualifier) { - err.cancel(); - } else { - err.emit(); - } - return Ok(FnHeader { - constness: recover_constness, - safety: recover_safety, - coroutine_kind: recover_coroutine_kind, - ext, - }); - } - - return Err(err); - } - } - } - - Ok(FnHeader { constness, safety, coroutine_kind, ext }) - } - - /// Parses the parameter list and result type of a function declaration. - pub(super) fn parse_fn_decl( - &mut self, - fn_parse_mode: &FnParseMode, - ret_allow_plus: AllowPlus, - recover_return_sign: RecoverReturnSign, - ) -> PResult<'a, Box> { - Ok(Box::new(FnDecl { - inputs: self.parse_fn_params(fn_parse_mode)?, - output: self.parse_ret_ty(ret_allow_plus, RecoverQPath::Yes, recover_return_sign)?, - })) - } - - /// Parses the parameter list of a function, including the `(` and `)` delimiters. - pub(super) fn parse_fn_params( - &mut self, - fn_parse_mode: &FnParseMode, - ) -> PResult<'a, ThinVec> { - let mut first_param = true; - // Parse the arguments, starting out with `self` being allowed... - if self.token != TokenKind::OpenParen - // might be typo'd trait impl, handled elsewhere - && !self.token.is_keyword(kw::For) - { - // recover from missing argument list, e.g. `fn main -> () {}` - self.dcx().emit_err(diagnostics::MissingFnParams { - span: self.prev_token.span.shrink_to_hi(), - }); - return Ok(ThinVec::new()); - } - - let (mut params, _) = self.parse_paren_comma_seq(|p| { - p.recover_vcs_conflict_marker(); - let snapshot = p.create_snapshot_for_diagnostic(); - let param = p.parse_param_general(fn_parse_mode, first_param, true).or_else(|e| { - let guar = e.emit(); - // When parsing a param failed, we should check to make the span of the param - // not contain '(' before it. - // For example when parsing `*mut Self` in function `fn oof(*mut Self)`. - let lo = if let TokenKind::OpenParen = p.prev_token.kind { - p.prev_token.span.shrink_to_hi() - } else { - p.prev_token.span - }; - p.restore_snapshot(snapshot); - // Skip every token until next possible arg or end. - p.eat_to_tokens(&[exp!(Comma), exp!(CloseParen)]); - // Create a placeholder argument for proper arg count (issue #34264). - Ok(dummy_arg(Ident::new(sym::dummy, lo.to(p.prev_token.span)), guar)) - }); - // ...now that we've parsed the first argument, `self` is no longer allowed. - first_param = false; - param - })?; - // Replace duplicated recovered params with `_` pattern to avoid unnecessary errors. - self.deduplicate_recovered_params_names(&mut params); - Ok(params) - } - - /// Parses a single function parameter. - /// - /// - `self` is syntactically allowed when `first_param` holds. - /// - `recover_arg_parse` is used to recover from a failed argument parse. - pub(super) fn parse_param_general( - &mut self, - fn_parse_mode: &FnParseMode, - first_param: bool, - recover_arg_parse: bool, - ) -> PResult<'a, Param> { - let lo = self.token.span; - let attrs = self.parse_outer_attributes()?; - self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| { - // Possibly parse `self`. Recover if we parsed it and it wasn't allowed here. - if let Some(mut param) = this.parse_self_param()? { - param.attrs = attrs; - let res = if first_param { Ok(param) } else { this.recover_bad_self_param(param) }; - return Ok((res?, Trailing::No, UsePreAttrPos::No)); - } - - let is_dot_dot_dot = if this.token.kind == token::DotDotDot { - IsDotDotDot::Yes - } else { - IsDotDotDot::No - }; - let is_name_required = (fn_parse_mode.req_name)( - this.token.span.with_neighbor(this.prev_token.span).edition(), - is_dot_dot_dot, - ); - let is_name_required = if is_name_required && is_dot_dot_dot == IsDotDotDot::Yes { - this.psess.buffer_lint( - VARARGS_WITHOUT_PATTERN, - this.token.span, - ast::CRATE_NODE_ID, - diagnostics::VarargsWithoutPattern { span: this.token.span }, - ); - false - } else { - is_name_required - }; - let (pat, ty) = if is_name_required || this.is_named_param() { - debug!("parse_param_general parse_pat (is_name_required:{})", is_name_required); - let (pat, colon) = this.parse_fn_param_pat_colon()?; - if !colon { - let mut err = this.unexpected().unwrap_err(); - let pat_span = pat.span; - return if let Some(ident) = this.parameter_without_type( - &mut err, - pat, - is_name_required, - first_param, - fn_parse_mode, - ) { - let guar = err.emit(); - let mut arg = dummy_arg(ident, guar); - arg.span = pat_span; - Ok((arg, Trailing::No, UsePreAttrPos::No)) - } else { - Err(err) - }; - } - - this.eat_incorrect_doc_comment_for_param_type(); - (pat, this.parse_ty_for_param()?) - } else { - debug!("parse_param_general ident_to_pat"); - let parser_snapshot_before_ty = this.create_snapshot_for_diagnostic(); - this.eat_incorrect_doc_comment_for_param_type(); - let mut ty = this.parse_ty_for_param(); - - if let Ok(t) = &ty { - // Check for trailing angle brackets - if let TyKind::Path(_, Path { segments, .. }) = &t.kind - && let Some(segment) = segments.last() - && let Some(guar) = - this.check_trailing_angle_brackets(segment, &[exp!(CloseParen)]) - { - return Ok(( - dummy_arg(segment.ident, guar), - Trailing::No, - UsePreAttrPos::No, - )); - } - - if this.token != token::Comma && this.token != token::CloseParen { - // This wasn't actually a type, but a pattern looking like a type, - // so we are going to rollback and re-parse for recovery. - ty = this.unexpected_any(); - } - } - match ty { - Ok(ty) => { - let pat = this.mk_pat(ty.span, PatKind::Missing); - (Box::new(pat), ty) - } - // If this is a C-variadic argument and we hit an error, return the error. - Err(err) if this.token == token::DotDotDot => return Err(err), - Err(err) if this.unmatched_angle_bracket_count > 0 => return Err(err), - Err(err) if recover_arg_parse => { - // Recover from attempting to parse the argument as a type without pattern. - err.cancel(); - this.restore_snapshot(parser_snapshot_before_ty); - this.recover_arg_parse()? - } - Err(err) => return Err(err), - } - }; - - let span = lo.to(this.prev_token.span); - - Ok(( - Param { attrs, id: ast::DUMMY_NODE_ID, is_placeholder: false, pat, span, ty }, - Trailing::No, - UsePreAttrPos::No, - )) - }) - } - - /// Returns the parsed optional self parameter and whether a self shortcut was used. - fn parse_self_param(&mut self) -> PResult<'a, Option> { - // Extract an identifier *after* having confirmed that the token is one. - let expect_self_ident = |this: &mut Self| match this.token.ident() { - Some((ident, IdentIsRaw::No)) => { - this.bump(); - ident - } - _ => unreachable!(), - }; - // is lifetime `n` tokens ahead? - let is_lifetime = |this: &Self, n| this.look_ahead(n, |t| t.is_lifetime()); - // Is `self` `n` tokens ahead? - let is_isolated_self = |this: &Self, n| { - this.is_keyword_ahead(n, &[kw::SelfLower]) - && this.look_ahead(n + 1, |t| t != &token::PathSep) - }; - // Is `pin const self` `n` tokens ahead? - let is_isolated_pin_const_self = |this: &Self, n| { - this.look_ahead(n, |token| token.is_ident_named(sym::pin)) - && this.is_keyword_ahead(n + 1, &[kw::Const]) - && is_isolated_self(this, n + 2) - }; - // Is `mut self` `n` tokens ahead? - let is_isolated_mut_self = - |this: &Self, n| this.is_keyword_ahead(n, &[kw::Mut]) && is_isolated_self(this, n + 1); - // Is `pin mut self` `n` tokens ahead? - let is_isolated_pin_mut_self = |this: &Self, n| { - this.look_ahead(n, |token| token.is_ident_named(sym::pin)) - && is_isolated_mut_self(this, n + 1) - }; - // Parse `self` or `self: TYPE`. We already know the current token is `self`. - let parse_self_possibly_typed = |this: &mut Self, m| { - let eself_ident = expect_self_ident(this); - let eself_hi = this.prev_token.span; - let eself = if this.eat(exp!(Colon)) { - SelfKind::Explicit(this.parse_ty()?, m) - } else { - SelfKind::Value(m) - }; - Ok((eself, eself_ident, eself_hi)) - }; - let expect_self_ident_not_typed = - |this: &mut Self, modifier: &SelfKind, modifier_span: Span| { - let eself_ident = expect_self_ident(this); - - // Recover `: Type` after a qualified self - if this.may_recover() && this.eat_noexpect(&token::Colon) { - let snap = this.create_snapshot_for_diagnostic(); - match this.parse_ty() { - Ok(ty) => { - this.dcx().emit_err(diagnostics::IncorrectTypeOnSelf { - span: ty.span, - move_self_modifier: diagnostics::MoveSelfModifier { - removal_span: modifier_span, - insertion_span: ty.span.shrink_to_lo(), - modifier: modifier.to_ref_suggestion(), - }, - }); - } - Err(diag) => { - diag.cancel(); - this.restore_snapshot(snap); - } - } - } - eself_ident - }; - // Recover for the grammar `*self`, `*const self`, and `*mut self`. - let recover_self_ptr = |this: &mut Self| { - this.dcx().emit_err(diagnostics::SelfArgumentPointer { span: this.token.span }); - - Ok((SelfKind::Value(Mutability::Not), expect_self_ident(this), this.prev_token.span)) - }; - - // Parse optional `self` parameter of a method. - // Only a limited set of initial token sequences is considered `self` parameters; anything - // else is parsed as a normal function parameter list, so some lookahead is required. - let eself_lo = self.token.span; - let (eself, eself_ident, eself_hi) = match self.token.uninterpolate().kind { - token::And => { - let has_lifetime = is_lifetime(self, 1); - let skip_lifetime_count = has_lifetime as usize; - let eself = if is_isolated_self(self, skip_lifetime_count + 1) { - // `&{'lt} self` - self.bump(); // & - let lifetime = has_lifetime.then(|| self.expect_lifetime()); - SelfKind::Region(lifetime, Mutability::Not) - } else if is_isolated_mut_self(self, skip_lifetime_count + 1) { - // `&{'lt} mut self` - self.bump(); // & - let lifetime = has_lifetime.then(|| self.expect_lifetime()); - self.bump(); // mut - SelfKind::Region(lifetime, Mutability::Mut) - } else if is_isolated_pin_const_self(self, skip_lifetime_count + 1) { - // `&{'lt} pin const self` - self.bump(); // & - let lifetime = has_lifetime.then(|| self.expect_lifetime()); - self.psess.gated_spans.gate(sym::pin_ergonomics, self.token.span); - self.bump(); // pin - self.bump(); // const - SelfKind::Pinned(lifetime, Mutability::Not) - } else if is_isolated_pin_mut_self(self, skip_lifetime_count + 1) { - // `&{'lt} pin mut self` - self.bump(); // & - let lifetime = has_lifetime.then(|| self.expect_lifetime()); - self.psess.gated_spans.gate(sym::pin_ergonomics, self.token.span); - self.bump(); // pin - self.bump(); // mut - SelfKind::Pinned(lifetime, Mutability::Mut) - } else { - // `¬_self` - return Ok(None); - }; - let hi = self.token.span; - let self_ident = expect_self_ident_not_typed(self, &eself, eself_lo.until(hi)); - (eself, self_ident, hi) - } - // `*self` - token::Star if is_isolated_self(self, 1) => { - self.bump(); - recover_self_ptr(self)? - } - // `*mut self` and `*const self` - token::Star - if self.look_ahead(1, |t| t.is_mutability()) && is_isolated_self(self, 2) => - { - self.bump(); - self.bump(); - recover_self_ptr(self)? - } - // `self` and `self: TYPE` - token::Ident(..) if is_isolated_self(self, 0) => { - parse_self_possibly_typed(self, Mutability::Not)? - } - // `mut self` and `mut self: TYPE` - token::Ident(..) if is_isolated_mut_self(self, 0) => { - self.bump(); - parse_self_possibly_typed(self, Mutability::Mut)? - } - _ => return Ok(None), - }; - - let eself = respan(eself_lo.to(eself_hi), eself); - Ok(Some(Param::from_self(AttrVec::default(), eself, eself_ident))) - } - - fn is_named_param(&self) -> bool { - let offset = match &self.token.kind { - token::OpenInvisible(origin) => match origin { - InvisibleOrigin::MetaVar(MetaVarKind::Pat(_)) => { - return self.check_noexpect_past_close_delim(&token::Colon); - } - _ => 0, - }, - token::And | token::AndAnd => 1, - _ if self.token.is_keyword(kw::Mut) => 1, - _ => 0, - }; - - self.look_ahead(offset, |t| t.is_ident()) - && self.look_ahead(offset + 1, |t| t == &token::Colon) - } - - fn recover_self_param(&mut self) -> bool { - matches!( - self.parse_outer_attributes() - .and_then(|_| self.parse_self_param()) - .map_err(|e| e.cancel()), - Ok(Some(_)) - ) - } - /// Try to recover from over-parsing in const item when a semicolon is missing. /// /// This detects cases where we parsed too much because a semicolon was missing @@ -3735,17 +2744,7 @@ impl<'a> Parser<'a> { } } } - enum IsMacroRulesItem { Yes { has_bang: bool }, No, } - -#[derive(Copy, Clone, PartialEq, Eq)] -pub(super) enum FrontMatterParsingMode { - /// Parse the front matter of a function declaration - Function, - /// Parse the front matter of a function pointet type. - /// For function pointer types, the `const` and `async` keywords are not permitted. - FunctionPtrType, -} diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index ef62316abf941..e2671a24177f1 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -2,6 +2,7 @@ pub mod attr; mod attr_wrapper; mod diagnostics; mod expr; +mod function; mod generics; mod item; mod nonterminal; @@ -22,7 +23,7 @@ use attr_wrapper::{AttrWrapper, UsePreAttrPos}; 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 item::{FnContext, FnParseMode}; +pub(crate) use function::{FnContext, FnParseMode, FrontMatterParsingMode, IsDotDotDot}; pub use pat::{CommaRecoveryMode, RecoverColon, RecoverComma}; pub use path::PathStyle; use rustc_ast::token::{ diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 2b12ee68d6497..b975e01796b89 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -18,8 +18,7 @@ use crate::diagnostics::{ HelpUseLatestEdition, InvalidCVariadicType, InvalidDynKeyword, LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime, NestedCVariadicType, ReturnTypesUseThinArrow, }; -use crate::parser::item::FrontMatterParsingMode; -use crate::parser::{FnContext, FnParseMode}; +use crate::parser::{FnContext, FnParseMode, FrontMatterParsingMode}; use crate::{exp, maybe_recover_from_interpolated_ty_qpath}; /// Signals whether parsing a type should allow `+`. @@ -881,7 +880,7 @@ impl<'a> Parser<'a> { if self.may_recover() && self.token == TokenKind::Lt { self.recover_fn_ptr_with_generics(lo, &mut params, param_insertion_point)?; } - let mode = crate::parser::item::FnParseMode { + let mode = crate::parser::FnParseMode { req_name: |_, _| false, context: FnContext::Free, req_body: false, From 842907ba8532a53b84e6c6261cdcddd40d845adf Mon Sep 17 00:00:00 2001 From: mu001999 Date: Tue, 28 Jul 2026 13:12:15 +0800 Subject: [PATCH 07/10] Add regression tests for fixed dead-code issues --- ...llow-derived-inherent-impl-issue-102190.rs | 19 ++++++++++ ...generate-inherent-impl-with-input-spans.rs | 35 +++++++++++++++++++ .../expect-on-assoc-fn-issue-148861.rs | 25 +++++++++++++ .../struct-used-as-pattern-issue-56750.rs | 17 +++++++++ 4 files changed, 96 insertions(+) create mode 100644 tests/ui/lint/dead-code/allow-derived-inherent-impl-issue-102190.rs create mode 100644 tests/ui/lint/dead-code/auxiliary/generate-inherent-impl-with-input-spans.rs create mode 100644 tests/ui/lint/dead-code/expect-on-assoc-fn-issue-148861.rs create mode 100644 tests/ui/lint/dead-code/struct-used-as-pattern-issue-56750.rs diff --git a/tests/ui/lint/dead-code/allow-derived-inherent-impl-issue-102190.rs b/tests/ui/lint/dead-code/allow-derived-inherent-impl-issue-102190.rs new file mode 100644 index 0000000000000..dc300f793c35a --- /dev/null +++ b/tests/ui/lint/dead-code/allow-derived-inherent-impl-issue-102190.rs @@ -0,0 +1,19 @@ +//! Regression test for + +//@ check-pass +//@ proc-macro: generate-inherent-impl-with-input-spans.rs + +#![deny(dead_code)] + +extern crate generate_inherent_impl_with_input_spans; + +use generate_inherent_impl_with_input_spans::Test; + +#[allow(dead_code)] +#[derive(Test)] +#[repr(i8)] +pub(crate) enum MyEnum { + MyValue, +} + +fn main() {} diff --git a/tests/ui/lint/dead-code/auxiliary/generate-inherent-impl-with-input-spans.rs b/tests/ui/lint/dead-code/auxiliary/generate-inherent-impl-with-input-spans.rs new file mode 100644 index 0000000000000..97cda6163c50e --- /dev/null +++ b/tests/ui/lint/dead-code/auxiliary/generate-inherent-impl-with-input-spans.rs @@ -0,0 +1,35 @@ +extern crate proc_macro; + +use proc_macro::{Delimiter, Group, Ident, TokenStream, TokenTree}; + +#[proc_macro_derive(Test)] +pub fn derive(input: TokenStream) -> TokenStream { + let input = input.into_iter().collect::>(); + let input_span = input[0].span(); + + let output = "impl MyEnum { + pub(crate) fn false_positive(self) -> i8 { + self as i8 + } + }" + .parse::() + .unwrap() + .into_iter() + .collect::>(); + + let TokenTree::Group(body) = &output[2] else { unreachable!() }; + let mut body = body.stream().into_iter().collect::>(); + + // Preserve the visibility tokens from the input, as `quote!` does when interpolating `#vis`. + body[0] = input[0].clone(); + body[1] = input[1].clone(); + + // Give the return type the input's span, matching `Ident::new("i8", input.span())`. + body[7] = TokenTree::Ident(Ident::new("i8", input_span)); + + let mut generated_body = + Group::new(Delimiter::Brace, body.into_iter().collect::()); + generated_body.set_span(output[2].span()); + + vec![output[0].clone(), output[1].clone(), generated_body.into()].into_iter().collect() +} diff --git a/tests/ui/lint/dead-code/expect-on-assoc-fn-issue-148861.rs b/tests/ui/lint/dead-code/expect-on-assoc-fn-issue-148861.rs new file mode 100644 index 0000000000000..5eab8938e5ee3 --- /dev/null +++ b/tests/ui/lint/dead-code/expect-on-assoc-fn-issue-148861.rs @@ -0,0 +1,25 @@ +//! Regression test for + +//@ check-pass + +#![deny(dead_code)] + +use std::any::Any; + +pub trait Foo: Any {} + +impl Foo for Box {} + +impl dyn Foo { + #[expect(dead_code)] + fn downcast_ref(&self) -> Option<&T> { + let this = self as &dyn Any; + if let Some(boxed) = this.downcast_ref::>() { + boxed.downcast_ref::() + } else { + this.downcast_ref::() + } + } +} + +fn main() {} diff --git a/tests/ui/lint/dead-code/struct-used-as-pattern-issue-56750.rs b/tests/ui/lint/dead-code/struct-used-as-pattern-issue-56750.rs new file mode 100644 index 0000000000000..65e625e20043b --- /dev/null +++ b/tests/ui/lint/dead-code/struct-used-as-pattern-issue-56750.rs @@ -0,0 +1,17 @@ +//! Regression test for + +//@ check-pass + +#![deny(dead_code)] + +use std::mem; + +fn main() { + struct Value { + a: i32, + b: i32, + } + + let Value { a, b } = unsafe { mem::zeroed() }; + println!("{a} {b}"); +} From 715f44b04b0fe2e1ea17ecd6ad8f23fdcc10930a Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 29 Jul 2026 14:49:11 +0300 Subject: [PATCH 08/10] tests: Remove `-Zthreads` options from tests in `ui/parallel-rustc` --- tests/run-make/mir-opt-bisect-limit/rmake.rs | 1 - tests/ui/README.md | 2 +- .../cache-after-waiting-issue-111528.rs | 4 +- .../cache-after-waiting-issue-111528.stderr | 2 +- .../cycle_crash-issue-135870.rs | 3 - .../cycle_crash-issue-135870.stderr | 2 +- ...default-trait-shadow-cycle-issue-151358.rs | 3 - .../export-symbols-deadlock-issue-118205-2.rs | 5 +- .../export-symbols-deadlock-issue-118205.rs | 4 +- .../parallel-rustc/fn-sig-cycle-ice-153391.rs | 5 +- .../fn-sig-cycle-ice-153391.stderr | 28 ++++---- tests/ui/parallel-rustc/hello_world.rs | 9 --- .../read-stolen-value-issue-111520.rs | 4 +- .../recursive-struct-oncelock-issue-151226.rs | 3 - ...ursive-struct-oncelock-issue-151226.stderr | 2 +- .../recursive-trait-fn-sig-issue-142064.rs | 3 - ...recursive-trait-fn-sig-issue-142064.stderr | 66 ++++++++--------- .../trait-solver-global-cache.rs} | 4 +- .../trait-solver-global-cache.stderr} | 4 +- .../ty-variance-issue-124423.rs | 3 - .../ty-variance-issue-124423.stderr | 70 +++++++++---------- .../ty-variance-issue-127971.rs | 3 - .../ty-variance-issue-127971.stderr | 22 +++--- .../undefined-function-issue-120760.rs | 4 +- .../undefined-function-issue-120760.stderr | 24 +++---- .../unexpected-type-issue-120601.rs | 6 +- .../unexpected-type-issue-120601.stderr | 20 +++--- 27 files changed, 133 insertions(+), 173 deletions(-) delete mode 100644 tests/ui/parallel-rustc/hello_world.rs rename tests/ui/{traits/next-solver/global-cache-and-parallel-frontend.rs => parallel-rustc/trait-solver-global-cache.rs} (88%) rename tests/ui/{traits/next-solver/global-cache-and-parallel-frontend.stderr => parallel-rustc/trait-solver-global-cache.stderr} (88%) diff --git a/tests/run-make/mir-opt-bisect-limit/rmake.rs b/tests/run-make/mir-opt-bisect-limit/rmake.rs index fa77cb7d8c35c..112805bdc4bcc 100644 --- a/tests/run-make/mir-opt-bisect-limit/rmake.rs +++ b/tests/run-make/mir-opt-bisect-limit/rmake.rs @@ -82,7 +82,6 @@ fn compile_case(dump_dir: &str, extra_flags: &[&str]) -> (bool, usize, usize, Co .arg("--emit=mir") .arg("-Zmir-opt-level=2") .arg("-Copt-level=2") - .arg("-Zthreads=1") .arg("-Zdump-mir=Inline") .arg(format!("-Zdump-mir-dir={dump_dir}")); diff --git a/tests/ui/README.md b/tests/ui/README.md index 8c159b0e0447f..3b50c3f82d575 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -1051,7 +1051,7 @@ Broad category of tests about panics in general, often but not necessarily using ## `tests/ui/parallel-rustc/` -Efforts towards a [Parallel Rustc Front-end](https://github.com/rust-lang/rust/issues/113349). Includes `-Zthreads=`. +Regression tests for [Parallel Rustc Front-end](https://github.com/rust-lang/rust/issues/113349). ## `tests/ui/parser/` diff --git a/tests/ui/parallel-rustc/cache-after-waiting-issue-111528.rs b/tests/ui/parallel-rustc/cache-after-waiting-issue-111528.rs index a357040a4e4df..0326f1b8b379e 100644 --- a/tests/ui/parallel-rustc/cache-after-waiting-issue-111528.rs +++ b/tests/ui/parallel-rustc/cache-after-waiting-issue-111528.rs @@ -1,8 +1,6 @@ // Test for #111528, the ice issue cause waiting on a query that panicked -// -//@ compile-flags: -Z threads=16 + //@ build-fail -//@ compare-output-by-lines #![crate_type = "rlib"] #![allow(warnings)] diff --git a/tests/ui/parallel-rustc/cache-after-waiting-issue-111528.stderr b/tests/ui/parallel-rustc/cache-after-waiting-issue-111528.stderr index 80f63733fb32b..7963165e31b0a 100644 --- a/tests/ui/parallel-rustc/cache-after-waiting-issue-111528.stderr +++ b/tests/ui/parallel-rustc/cache-after-waiting-issue-111528.stderr @@ -1,5 +1,5 @@ error: symbol `fail` is already defined - --> $DIR/cache-after-waiting-issue-111528.rs:14:1 + --> $DIR/cache-after-waiting-issue-111528.rs:12:1 | LL | pub fn b() { | ^^^^^^^^^^ diff --git a/tests/ui/parallel-rustc/cycle_crash-issue-135870.rs b/tests/ui/parallel-rustc/cycle_crash-issue-135870.rs index d199bc14842a0..8069dcb8e6e98 100644 --- a/tests/ui/parallel-rustc/cycle_crash-issue-135870.rs +++ b/tests/ui/parallel-rustc/cycle_crash-issue-135870.rs @@ -1,7 +1,4 @@ // Test for #135870, which causes a deadlock bug -// -//@ compile-flags: -Z threads=2 -//@ compare-output-by-lines const FOO: usize = FOO; //~ ERROR cycle detected diff --git a/tests/ui/parallel-rustc/cycle_crash-issue-135870.stderr b/tests/ui/parallel-rustc/cycle_crash-issue-135870.stderr index b1ecc0f3cf9ee..158c5fee8ae7e 100644 --- a/tests/ui/parallel-rustc/cycle_crash-issue-135870.stderr +++ b/tests/ui/parallel-rustc/cycle_crash-issue-135870.stderr @@ -1,5 +1,5 @@ error[E0391]: cycle detected when checking if `FOO` is a trivial const - --> $DIR/cycle_crash-issue-135870.rs:6:1 + --> $DIR/cycle_crash-issue-135870.rs:3:1 | LL | const FOO: usize = FOO; | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/parallel-rustc/default-trait-shadow-cycle-issue-151358.rs b/tests/ui/parallel-rustc/default-trait-shadow-cycle-issue-151358.rs index 61fb67174fde4..21ec2f6b2af29 100644 --- a/tests/ui/parallel-rustc/default-trait-shadow-cycle-issue-151358.rs +++ b/tests/ui/parallel-rustc/default-trait-shadow-cycle-issue-151358.rs @@ -1,9 +1,6 @@ // Test for #151358, assertion failed: !worker_thread.is_null() //~^ ERROR internal compiler error: query cycle when printing cycle detected //~^^ ERROR cycle detected when getting the resolver for lowering -// -//@ compile-flags: -Z threads=2 -//@ compare-output-by-lines trait Default {} use std::num::NonZero; diff --git a/tests/ui/parallel-rustc/export-symbols-deadlock-issue-118205-2.rs b/tests/ui/parallel-rustc/export-symbols-deadlock-issue-118205-2.rs index 523b1b2d1f3fe..7168e4c96f4b5 100644 --- a/tests/ui/parallel-rustc/export-symbols-deadlock-issue-118205-2.rs +++ b/tests/ui/parallel-rustc/export-symbols-deadlock-issue-118205-2.rs @@ -1,9 +1,8 @@ // Test for #118205, which causes a deadlock bug -// -//@ compile-flags:-C extra-filename=-1 -Z threads=16 + +//@ compile-flags:-C extra-filename=-1 //@ no-prefer-dynamic //@ build-pass -//@ compare-output-by-lines #![crate_name = "crateresolve1"] #![crate_type = "lib"] diff --git a/tests/ui/parallel-rustc/export-symbols-deadlock-issue-118205.rs b/tests/ui/parallel-rustc/export-symbols-deadlock-issue-118205.rs index 65f99c3064399..d309341091dbd 100644 --- a/tests/ui/parallel-rustc/export-symbols-deadlock-issue-118205.rs +++ b/tests/ui/parallel-rustc/export-symbols-deadlock-issue-118205.rs @@ -1,8 +1,6 @@ // Test for #118205, which causes a deadlock bug -// -//@ compile-flags: -Z threads=16 + //@ build-pass -//@ compare-output-by-lines pub static GLOBAL: isize = 3; diff --git a/tests/ui/parallel-rustc/fn-sig-cycle-ice-153391.rs b/tests/ui/parallel-rustc/fn-sig-cycle-ice-153391.rs index 0108ada8c08c3..4300026dd66af 100644 --- a/tests/ui/parallel-rustc/fn-sig-cycle-ice-153391.rs +++ b/tests/ui/parallel-rustc/fn-sig-cycle-ice-153391.rs @@ -1,9 +1,6 @@ // Regression test for #153391. -// + //@ edition:2024 -//@ compile-flags: -Z threads=16 -//@ compare-output-by-lines -//@ ignore-test (#142063) trait A { fn g() -> B; diff --git a/tests/ui/parallel-rustc/fn-sig-cycle-ice-153391.stderr b/tests/ui/parallel-rustc/fn-sig-cycle-ice-153391.stderr index 4d348cf22f4ef..225eb8a71cbfb 100644 --- a/tests/ui/parallel-rustc/fn-sig-cycle-ice-153391.stderr +++ b/tests/ui/parallel-rustc/fn-sig-cycle-ice-153391.stderr @@ -1,16 +1,5 @@ error[E0782]: expected a type, found a trait - --> $DIR/fn-sig-cycle-ice-153391.rs:8:15 - | -LL | fn g() -> B; - | ^ - | -help: `B` is dyn-incompatible, use `impl B` to return an opaque type, as long as you return a single underlying type - | -LL | fn g() -> impl B; - | ++++ - -error[E0782]: expected a type, found a trait - --> $DIR/fn-sig-cycle-ice-153391.rs:13:23 + --> $DIR/fn-sig-cycle-ice-153391.rs:11:23 | LL | fn bar(&self, x: &A); | ^ @@ -26,6 +15,21 @@ help: you can also use an opaque type, but users won't be able to specify the ty LL | fn bar(&self, x: &impl A); | ++++ +error[E0782]: expected a type, found a trait + --> $DIR/fn-sig-cycle-ice-153391.rs:6:15 + | +LL | fn g() -> B; + | ^ + | +help: use `impl B` to return an opaque type, as long as you return a single underlying type + | +LL | fn g() -> impl B; + | ++++ +help: alternatively, you can return an owned trait object + | +LL | fn g() -> Box; + | +++++++ + + error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0782`. diff --git a/tests/ui/parallel-rustc/hello_world.rs b/tests/ui/parallel-rustc/hello_world.rs deleted file mode 100644 index 57891b92da038..0000000000000 --- a/tests/ui/parallel-rustc/hello_world.rs +++ /dev/null @@ -1,9 +0,0 @@ -// Test for the basic function of parallel front end -// -//@ compile-flags: -Z threads=8 -//@ run-pass -//@ compare-output-by-lines - -fn main() { - println!("Hello world!"); -} diff --git a/tests/ui/parallel-rustc/read-stolen-value-issue-111520.rs b/tests/ui/parallel-rustc/read-stolen-value-issue-111520.rs index a6b37e6291375..0763a6991e13a 100644 --- a/tests/ui/parallel-rustc/read-stolen-value-issue-111520.rs +++ b/tests/ui/parallel-rustc/read-stolen-value-issue-111520.rs @@ -1,8 +1,6 @@ // Test for #111520, which causes an ice bug cause of reading stolen value -// -//@ compile-flags: -Z threads=16 + //@ run-pass -//@ compare-output-by-lines #[repr(transparent)] struct Sched { diff --git a/tests/ui/parallel-rustc/recursive-struct-oncelock-issue-151226.rs b/tests/ui/parallel-rustc/recursive-struct-oncelock-issue-151226.rs index 69cbd578325c5..32b570af445e4 100644 --- a/tests/ui/parallel-rustc/recursive-struct-oncelock-issue-151226.rs +++ b/tests/ui/parallel-rustc/recursive-struct-oncelock-issue-151226.rs @@ -1,7 +1,4 @@ // Test for #151226, Unable to verify registry association -// -//@ compile-flags: -Z threads=2 -//@ compare-output-by-lines struct A(std::sync::OnceLock); //~^ ERROR recursive type `A` has infinite size diff --git a/tests/ui/parallel-rustc/recursive-struct-oncelock-issue-151226.stderr b/tests/ui/parallel-rustc/recursive-struct-oncelock-issue-151226.stderr index 2a51ee930779c..be0ffb5f11c1d 100644 --- a/tests/ui/parallel-rustc/recursive-struct-oncelock-issue-151226.stderr +++ b/tests/ui/parallel-rustc/recursive-struct-oncelock-issue-151226.stderr @@ -1,5 +1,5 @@ error[E0072]: recursive type `A` has infinite size - --> $DIR/recursive-struct-oncelock-issue-151226.rs:6:1 + --> $DIR/recursive-struct-oncelock-issue-151226.rs:3:1 | LL | struct A(std::sync::OnceLock); | ^^^^^^^^^^^ ------------------------- recursive without indirection diff --git a/tests/ui/parallel-rustc/recursive-trait-fn-sig-issue-142064.rs b/tests/ui/parallel-rustc/recursive-trait-fn-sig-issue-142064.rs index 5ae2f936333f6..752613de186d2 100644 --- a/tests/ui/parallel-rustc/recursive-trait-fn-sig-issue-142064.rs +++ b/tests/ui/parallel-rustc/recursive-trait-fn-sig-issue-142064.rs @@ -1,7 +1,4 @@ // Test for #142064, internal error: entered unreachable code -// -//@ compile-flags: -Zthreads=2 -//@ compare-output-by-lines #![crate_type = "rlib"] trait A { fn foo() -> A; } diff --git a/tests/ui/parallel-rustc/recursive-trait-fn-sig-issue-142064.stderr b/tests/ui/parallel-rustc/recursive-trait-fn-sig-issue-142064.stderr index 0b49389003f74..c26df459d2564 100644 --- a/tests/ui/parallel-rustc/recursive-trait-fn-sig-issue-142064.stderr +++ b/tests/ui/parallel-rustc/recursive-trait-fn-sig-issue-142064.stderr @@ -1,5 +1,5 @@ warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/recursive-trait-fn-sig-issue-142064.rs:7:23 + --> $DIR/recursive-trait-fn-sig-issue-142064.rs:4:23 | LL | trait A { fn foo() -> A; } | ^ @@ -13,7 +13,7 @@ LL | trait A { fn foo() -> dyn A; } | +++ warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/recursive-trait-fn-sig-issue-142064.rs:7:23 + --> $DIR/recursive-trait-fn-sig-issue-142064.rs:4:23 | LL | trait A { fn foo() -> A; } | ^ @@ -26,42 +26,15 @@ help: if this is a dyn-compatible trait, use `dyn` LL | trait A { fn foo() -> dyn A; } | +++ -warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/recursive-trait-fn-sig-issue-142064.rs:13:23 - | -LL | trait B { fn foo() -> A; } - | ^ - | - = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! - = note: for more information, see -help: if this is a dyn-compatible trait, use `dyn` - | -LL | trait B { fn foo() -> dyn A; } - | +++ - -warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/recursive-trait-fn-sig-issue-142064.rs:13:23 - | -LL | trait B { fn foo() -> A; } - | ^ - | - = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! - = note: for more information, see - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: if this is a dyn-compatible trait, use `dyn` - | -LL | trait B { fn foo() -> dyn A; } - | +++ - error[E0038]: the trait `A` is not dyn compatible - --> $DIR/recursive-trait-fn-sig-issue-142064.rs:7:23 + --> $DIR/recursive-trait-fn-sig-issue-142064.rs:4:23 | LL | trait A { fn foo() -> A; } | ^ `A` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit - --> $DIR/recursive-trait-fn-sig-issue-142064.rs:7:14 + --> $DIR/recursive-trait-fn-sig-issue-142064.rs:4:14 | LL | trait A { fn foo() -> A; } | - ^^^ ...because associated function `foo` has no `self` parameter @@ -81,15 +54,42 @@ LL - trait A { fn foo() -> A; } LL + trait A { fn foo() -> Self; } | +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/recursive-trait-fn-sig-issue-142064.rs:10:23 + | +LL | trait B { fn foo() -> A; } + | ^ + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see +help: if this is a dyn-compatible trait, use `dyn` + | +LL | trait B { fn foo() -> dyn A; } + | +++ + +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/recursive-trait-fn-sig-issue-142064.rs:10:23 + | +LL | trait B { fn foo() -> A; } + | ^ + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: if this is a dyn-compatible trait, use `dyn` + | +LL | trait B { fn foo() -> dyn A; } + | +++ + error[E0038]: the trait `A` is not dyn compatible - --> $DIR/recursive-trait-fn-sig-issue-142064.rs:13:23 + --> $DIR/recursive-trait-fn-sig-issue-142064.rs:10:23 | LL | trait B { fn foo() -> A; } | ^ `A` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit - --> $DIR/recursive-trait-fn-sig-issue-142064.rs:7:14 + --> $DIR/recursive-trait-fn-sig-issue-142064.rs:4:14 | LL | trait A { fn foo() -> A; } | - ^^^ ...because associated function `foo` has no `self` parameter diff --git a/tests/ui/traits/next-solver/global-cache-and-parallel-frontend.rs b/tests/ui/parallel-rustc/trait-solver-global-cache.rs similarity index 88% rename from tests/ui/traits/next-solver/global-cache-and-parallel-frontend.rs rename to tests/ui/parallel-rustc/trait-solver-global-cache.rs index ceef87d76abb6..faa847b1ea3d0 100644 --- a/tests/ui/traits/next-solver/global-cache-and-parallel-frontend.rs +++ b/tests/ui/parallel-rustc/trait-solver-global-cache.rs @@ -1,8 +1,6 @@ -//@ compile-flags: -Zthreads=16 - // original issue: https://github.com/rust-lang/rust/issues/129112 // Previously, the "next" solver asserted that each successful solution is only obtained once. -// This test exhibits a repro that, with next-solver + -Zthreads, triggered that old assert. +// This test exhibits a repro that, with next-solver + parallel frontend, triggered that old assert. // In the presence of multithreaded solving, it's possible to concurrently evaluate things twice, // which leads to replacing already-solved solutions in the global solution cache! // We assume this is fine if we check to make sure they are solved the same way each time. diff --git a/tests/ui/traits/next-solver/global-cache-and-parallel-frontend.stderr b/tests/ui/parallel-rustc/trait-solver-global-cache.stderr similarity index 88% rename from tests/ui/traits/next-solver/global-cache-and-parallel-frontend.stderr rename to tests/ui/parallel-rustc/trait-solver-global-cache.stderr index 912286a48fafe..6268341029bb2 100644 --- a/tests/ui/traits/next-solver/global-cache-and-parallel-frontend.stderr +++ b/tests/ui/parallel-rustc/trait-solver-global-cache.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `T: Clone` is not satisfied - --> $DIR/global-cache-and-parallel-frontend.rs:16:12 + --> $DIR/trait-solver-global-cache.rs:14:12 | LL | #[derive(Clone, Eq)] | -- in this derive macro expansion @@ -7,7 +7,7 @@ LL | pub struct Struct(T); | ^^^^^^ the trait `Clone` is not implemented for `T` | note: required for `Struct` to implement `PartialEq` - --> $DIR/global-cache-and-parallel-frontend.rs:18:19 + --> $DIR/trait-solver-global-cache.rs:16:19 | LL | impl PartialEq for Struct | ----- ^^^^^^^^^^^^ ^^^^^^^^^ diff --git a/tests/ui/parallel-rustc/ty-variance-issue-124423.rs b/tests/ui/parallel-rustc/ty-variance-issue-124423.rs index 501b0fca7bf5e..5b5f3d4ec641a 100644 --- a/tests/ui/parallel-rustc/ty-variance-issue-124423.rs +++ b/tests/ui/parallel-rustc/ty-variance-issue-124423.rs @@ -1,7 +1,4 @@ // Test for #124423, which causes an ice bug: only `variances_of` returns `&[ty::Variance]` -// -//@ compile-flags: -Z threads=16 -//@ compare-output-by-lines use std::fmt::Debug; diff --git a/tests/ui/parallel-rustc/ty-variance-issue-124423.stderr b/tests/ui/parallel-rustc/ty-variance-issue-124423.stderr index cf0b5f3a248a0..83764f9e22312 100644 --- a/tests/ui/parallel-rustc/ty-variance-issue-124423.stderr +++ b/tests/ui/parallel-rustc/ty-variance-issue-124423.stderr @@ -1,5 +1,5 @@ error: ambiguous `+` in a type - --> $DIR/ty-variance-issue-124423.rs:8:15 + --> $DIR/ty-variance-issue-124423.rs:5:15 | LL | fn elided(_: &impl Copy + 'a) -> _ { x } | ^^^^^^^^^^^^^^ @@ -10,7 +10,7 @@ LL | fn elided(_: &(impl Copy + 'a)) -> _ { x } | + + error: ambiguous `+` in a type - --> $DIR/ty-variance-issue-124423.rs:13:24 + --> $DIR/ty-variance-issue-124423.rs:10:24 | LL | fn explicit<'b>(_: &'a impl Copy + 'a) -> impl 'a { x } | ^^^^^^^^^^^^^^ @@ -21,19 +21,19 @@ LL | fn explicit<'b>(_: &'a (impl Copy + 'a)) -> impl 'a { x } | + + error: expected identifier, found keyword `impl` - --> $DIR/ty-variance-issue-124423.rs:20:13 + --> $DIR/ty-variance-issue-124423.rs:17:13 | LL | fn elided2( impl 'b) -> impl 'a + 'a { x } | ^^^^ expected identifier, found keyword error: expected one of `:` or `|`, found `'b` - --> $DIR/ty-variance-issue-124423.rs:20:18 + --> $DIR/ty-variance-issue-124423.rs:17:18 | LL | fn elided2( impl 'b) -> impl 'a + 'a { x } | ^^ expected one of `:` or `|` error: ambiguous `+` in a type - --> $DIR/ty-variance-issue-124423.rs:27:25 + --> $DIR/ty-variance-issue-124423.rs:24:25 | LL | fn explicit2<'a>(_: &'a impl Copy + 'a) -> impl Copy + 'a { x } | ^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | fn explicit2<'a>(_: &'a (impl Copy + 'a)) -> impl Copy + 'a { x } | + + error: ambiguous `+` in a type - --> $DIR/ty-variance-issue-124423.rs:30:16 + --> $DIR/ty-variance-issue-124423.rs:27:16 | LL | fn foo<'a>(_: &impl Copy + 'a) -> impl 'b + 'a { x } | ^^^^^^^^^^^^^^ @@ -55,7 +55,7 @@ LL | fn foo<'a>(_: &(impl Copy + 'a)) -> impl 'b + 'a { x } | + + error: ambiguous `+` in a type - --> $DIR/ty-variance-issue-124423.rs:35:16 + --> $DIR/ty-variance-issue-124423.rs:32:16 | LL | fn elided3(_: &impl Copy + 'a) -> Box { Box::new(x) } | ^^^^^^^^^^^^^^ @@ -66,7 +66,7 @@ LL | fn elided3(_: &(impl Copy + 'a)) -> Box { Box::new(x) } | + + error: ambiguous `+` in a type - --> $DIR/ty-variance-issue-124423.rs:41:17 + --> $DIR/ty-variance-issue-124423.rs:38:17 | LL | fn x<'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } | ^^^^^^^^^^^^^^ @@ -77,7 +77,7 @@ LL | fn x<'b>(_: &'a (impl Copy + 'a)) -> Box { Box::u32(x) } | + + error: ambiguous `+` in a type - --> $DIR/ty-variance-issue-124423.rs:48:16 + --> $DIR/ty-variance-issue-124423.rs:45:16 | LL | fn elided4(_: &impl Copy + 'a) -> new { x(x) } | ^^^^^^^^^^^^^^ @@ -88,25 +88,25 @@ LL | fn elided4(_: &(impl Copy + 'a)) -> new { x(x) } | + + error: at least one trait must be specified - --> $DIR/ty-variance-issue-124423.rs:13:43 + --> $DIR/ty-variance-issue-124423.rs:10:43 | LL | fn explicit<'b>(_: &'a impl Copy + 'a) -> impl 'a { x } | ^^^^^^^ error: at least one trait must be specified - --> $DIR/ty-variance-issue-124423.rs:20:25 + --> $DIR/ty-variance-issue-124423.rs:17:25 | LL | fn elided2( impl 'b) -> impl 'a + 'a { x } | ^^^^^^^^^^^^ error: at least one trait must be specified - --> $DIR/ty-variance-issue-124423.rs:30:35 + --> $DIR/ty-variance-issue-124423.rs:27:35 | LL | fn foo<'a>(_: &impl Copy + 'a) -> impl 'b + 'a { x } | ^^^^^^^^^^^^ error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/ty-variance-issue-124423.rs:8:27 + --> $DIR/ty-variance-issue-124423.rs:5:27 | LL | fn elided(_: &impl Copy + 'a) -> _ { x } | ^^ undeclared lifetime @@ -117,7 +117,7 @@ LL | fn elided<'a>(_: &impl Copy + 'a) -> _ { x } | ++++ error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/ty-variance-issue-124423.rs:13:21 + --> $DIR/ty-variance-issue-124423.rs:10:21 | LL | fn explicit<'b>(_: &'a impl Copy + 'a) -> impl 'a { x } | ^^ undeclared lifetime @@ -128,7 +128,7 @@ LL | fn explicit<'a, 'b>(_: &'a impl Copy + 'a) -> impl 'a { x } | +++ error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/ty-variance-issue-124423.rs:13:36 + --> $DIR/ty-variance-issue-124423.rs:10:36 | LL | fn explicit<'b>(_: &'a impl Copy + 'a) -> impl 'a { x } | ^^ undeclared lifetime @@ -139,7 +139,7 @@ LL | fn explicit<'a, 'b>(_: &'a impl Copy + 'a) -> impl 'a { x } | +++ error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/ty-variance-issue-124423.rs:13:48 + --> $DIR/ty-variance-issue-124423.rs:10:48 | LL | fn explicit<'b>(_: &'a impl Copy + 'a) -> impl 'a { x } | ^^ undeclared lifetime @@ -150,7 +150,7 @@ LL | fn explicit<'a, 'b>(_: &'a impl Copy + 'a) -> impl 'a { x } | +++ error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/ty-variance-issue-124423.rs:20:30 + --> $DIR/ty-variance-issue-124423.rs:17:30 | LL | fn elided2( impl 'b) -> impl 'a + 'a { x } | ^^ undeclared lifetime @@ -161,7 +161,7 @@ LL | fn elided2<'a>( impl 'b) -> impl 'a + 'a { x } | ++++ error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/ty-variance-issue-124423.rs:20:35 + --> $DIR/ty-variance-issue-124423.rs:17:35 | LL | fn elided2( impl 'b) -> impl 'a + 'a { x } | ^^ undeclared lifetime @@ -172,7 +172,7 @@ LL | fn elided2<'a>( impl 'b) -> impl 'a + 'a { x } | ++++ error[E0261]: use of undeclared lifetime name `'b` - --> $DIR/ty-variance-issue-124423.rs:30:40 + --> $DIR/ty-variance-issue-124423.rs:27:40 | LL | fn foo<'a>(_: &impl Copy + 'a) -> impl 'b + 'a { x } | ^^ undeclared lifetime @@ -183,7 +183,7 @@ LL | fn foo<'b, 'a>(_: &impl Copy + 'a) -> impl 'b + 'a { x } | +++ error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/ty-variance-issue-124423.rs:35:28 + --> $DIR/ty-variance-issue-124423.rs:32:28 | LL | fn elided3(_: &impl Copy + 'a) -> Box { Box::new(x) } | ^^ undeclared lifetime @@ -194,7 +194,7 @@ LL | fn elided3<'a>(_: &impl Copy + 'a) -> Box { Box::new(x) } | ++++ error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/ty-variance-issue-124423.rs:35:43 + --> $DIR/ty-variance-issue-124423.rs:32:43 | LL | fn elided3(_: &impl Copy + 'a) -> Box { Box::new(x) } | ^^ undeclared lifetime @@ -205,7 +205,7 @@ LL | fn elided3<'a>(_: &impl Copy + 'a) -> Box { Box::new(x) } | ++++ error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/ty-variance-issue-124423.rs:41:14 + --> $DIR/ty-variance-issue-124423.rs:38:14 | LL | fn x<'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } | ^^ undeclared lifetime @@ -216,7 +216,7 @@ LL | fn x<'a, 'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } | +++ error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/ty-variance-issue-124423.rs:41:29 + --> $DIR/ty-variance-issue-124423.rs:38:29 | LL | fn x<'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } | ^^ undeclared lifetime @@ -227,7 +227,7 @@ LL | fn x<'a, 'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } | +++ error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/ty-variance-issue-124423.rs:48:28 + --> $DIR/ty-variance-issue-124423.rs:45:28 | LL | fn elided4(_: &impl Copy + 'a) -> new { x(x) } | ^^ undeclared lifetime @@ -238,37 +238,37 @@ LL | fn elided4<'a>(_: &impl Copy + 'a) -> new { x(x) } | ++++ error[E0425]: cannot find type `new` in this scope - --> $DIR/ty-variance-issue-124423.rs:48:36 + --> $DIR/ty-variance-issue-124423.rs:45:36 | LL | fn elided4(_: &impl Copy + 'a) -> new { x(x) } | ^^^ not found in this scope error[E0224]: at least one trait is required for an object type - --> $DIR/ty-variance-issue-124423.rs:55:40 - | -LL | impl<'a> LifetimeTrait<'a> for &'a Box {} - | ^^^^^^ - -error[E0224]: at least one trait is required for an object type - --> $DIR/ty-variance-issue-124423.rs:41:40 + --> $DIR/ty-variance-issue-124423.rs:38:40 | LL | fn x<'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } | ^^^^^^ error[E0224]: at least one trait is required for an object type - --> $DIR/ty-variance-issue-124423.rs:35:39 + --> $DIR/ty-variance-issue-124423.rs:32:39 | LL | fn elided3(_: &impl Copy + 'a) -> Box { Box::new(x) } | ^^^^^^ error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types - --> $DIR/ty-variance-issue-124423.rs:8:34 + --> $DIR/ty-variance-issue-124423.rs:5:34 | LL | fn elided(_: &impl Copy + 'a) -> _ { x } | ^ not allowed in type signatures +error[E0224]: at least one trait is required for an object type + --> $DIR/ty-variance-issue-124423.rs:52:40 + | +LL | impl<'a> LifetimeTrait<'a> for &'a Box {} + | ^^^^^^ + error[E0599]: no associated function or constant named `u32` found for struct `Box<_, _>` in the current scope - --> $DIR/ty-variance-issue-124423.rs:41:55 + --> $DIR/ty-variance-issue-124423.rs:38:55 | LL | fn x<'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } | ^^^ associated function or constant not found in `Box<_, _>` diff --git a/tests/ui/parallel-rustc/ty-variance-issue-127971.rs b/tests/ui/parallel-rustc/ty-variance-issue-127971.rs index db2dc8e4c8567..9e4b32868ca8c 100644 --- a/tests/ui/parallel-rustc/ty-variance-issue-127971.rs +++ b/tests/ui/parallel-rustc/ty-variance-issue-127971.rs @@ -1,7 +1,4 @@ // Test for #127971, which causes an ice bug: only `variances_of` returns `&[ty::Variance]` -// -//@ compile-flags: -Z threads=16 -//@ compare-output-by-lines use std::fmt::Debug; diff --git a/tests/ui/parallel-rustc/ty-variance-issue-127971.stderr b/tests/ui/parallel-rustc/ty-variance-issue-127971.stderr index 47286d90d6816..e1e00243caaa3 100644 --- a/tests/ui/parallel-rustc/ty-variance-issue-127971.stderr +++ b/tests/ui/parallel-rustc/ty-variance-issue-127971.stderr @@ -1,5 +1,5 @@ error: ambiguous `+` in a type - --> $DIR/ty-variance-issue-127971.rs:8:15 + --> $DIR/ty-variance-issue-127971.rs:5:15 | LL | fn elided(_: &impl Copy + 'a) -> _ { x } | ^^^^^^^^^^^^^^ @@ -10,7 +10,7 @@ LL | fn elided(_: &(impl Copy + 'a)) -> _ { x } | + + error: ambiguous `+` in a type - --> $DIR/ty-variance-issue-127971.rs:13:16 + --> $DIR/ty-variance-issue-127971.rs:10:16 | LL | fn foo<'a>(_: &impl Copy + 'a) -> impl 'b + 'a { x } | ^^^^^^^^^^^^^^ @@ -21,7 +21,7 @@ LL | fn foo<'a>(_: &(impl Copy + 'a)) -> impl 'b + 'a { x } | + + error: ambiguous `+` in a type - --> $DIR/ty-variance-issue-127971.rs:18:17 + --> $DIR/ty-variance-issue-127971.rs:15:17 | LL | fn x<'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } | ^^^^^^^^^^^^^^ @@ -32,13 +32,13 @@ LL | fn x<'b>(_: &'a (impl Copy + 'a)) -> Box { Box::u32(x) } | + + error: at least one trait must be specified - --> $DIR/ty-variance-issue-127971.rs:13:35 + --> $DIR/ty-variance-issue-127971.rs:10:35 | LL | fn foo<'a>(_: &impl Copy + 'a) -> impl 'b + 'a { x } | ^^^^^^^^^^^^ error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/ty-variance-issue-127971.rs:8:27 + --> $DIR/ty-variance-issue-127971.rs:5:27 | LL | fn elided(_: &impl Copy + 'a) -> _ { x } | ^^ undeclared lifetime @@ -49,7 +49,7 @@ LL | fn elided<'a>(_: &impl Copy + 'a) -> _ { x } | ++++ error[E0261]: use of undeclared lifetime name `'b` - --> $DIR/ty-variance-issue-127971.rs:13:40 + --> $DIR/ty-variance-issue-127971.rs:10:40 | LL | fn foo<'a>(_: &impl Copy + 'a) -> impl 'b + 'a { x } | ^^ undeclared lifetime @@ -60,7 +60,7 @@ LL | fn foo<'b, 'a>(_: &impl Copy + 'a) -> impl 'b + 'a { x } | +++ error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/ty-variance-issue-127971.rs:18:14 + --> $DIR/ty-variance-issue-127971.rs:15:14 | LL | fn x<'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } | ^^ undeclared lifetime @@ -71,7 +71,7 @@ LL | fn x<'a, 'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } | +++ error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/ty-variance-issue-127971.rs:18:29 + --> $DIR/ty-variance-issue-127971.rs:15:29 | LL | fn x<'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } | ^^ undeclared lifetime @@ -82,19 +82,19 @@ LL | fn x<'a, 'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } | +++ error[E0224]: at least one trait is required for an object type - --> $DIR/ty-variance-issue-127971.rs:18:40 + --> $DIR/ty-variance-issue-127971.rs:15:40 | LL | fn x<'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } | ^^^^^^ error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types - --> $DIR/ty-variance-issue-127971.rs:8:34 + --> $DIR/ty-variance-issue-127971.rs:5:34 | LL | fn elided(_: &impl Copy + 'a) -> _ { x } | ^ not allowed in type signatures error[E0599]: no associated function or constant named `u32` found for struct `Box<_, _>` in the current scope - --> $DIR/ty-variance-issue-127971.rs:18:55 + --> $DIR/ty-variance-issue-127971.rs:15:55 | LL | fn x<'b>(_: &'a impl Copy + 'a) -> Box { Box::u32(x) } | ^^^ associated function or constant not found in `Box<_, _>` diff --git a/tests/ui/parallel-rustc/undefined-function-issue-120760.rs b/tests/ui/parallel-rustc/undefined-function-issue-120760.rs index 2665c30945b96..bee3cafb9ff49 100644 --- a/tests/ui/parallel-rustc/undefined-function-issue-120760.rs +++ b/tests/ui/parallel-rustc/undefined-function-issue-120760.rs @@ -1,8 +1,6 @@ // Test for #120760, which causes an ice bug: no index for a field -// -//@ compile-flags: -Z threads=45 + //@ edition: 2021 -//@ compare-output-by-lines type BoxFuture = std::pin::Pin>>; diff --git a/tests/ui/parallel-rustc/undefined-function-issue-120760.stderr b/tests/ui/parallel-rustc/undefined-function-issue-120760.stderr index 4d2241248fee0..4704df0557d42 100644 --- a/tests/ui/parallel-rustc/undefined-function-issue-120760.stderr +++ b/tests/ui/parallel-rustc/undefined-function-issue-120760.stderr @@ -1,5 +1,5 @@ error[E0261]: use of undeclared lifetime name `'a` - --> $DIR/undefined-function-issue-120760.rs:20:16 + --> $DIR/undefined-function-issue-120760.rs:18:16 | LL | pub name: &'a str, | ^^ undeclared lifetime @@ -9,25 +9,25 @@ help: consider introducing lifetime `'a` here LL | pub struct User<'a, 'dep> { | +++ -error[E0560]: struct `User<'_>` has no field named `dep` - --> $DIR/undefined-function-issue-120760.rs:70:12 - | -LL | User { dep }.save().await; - | ^^^ `User<'_>` does not have this field +error[E0425]: cannot find function `run` in this scope + --> $DIR/undefined-function-issue-120760.rs:12:5 | - = note: available fields are: `name` +LL | run("dependency").await; + | ^^^ not found in this scope error[E0425]: cannot find function `run` in this scope - --> $DIR/undefined-function-issue-120760.rs:61:17 + --> $DIR/undefined-function-issue-120760.rs:59:17 | LL | let _ = run("dependency").await; | ^^^ not found in this scope -error[E0425]: cannot find function `run` in this scope - --> $DIR/undefined-function-issue-120760.rs:14:5 +error[E0560]: struct `User<'_>` has no field named `dep` + --> $DIR/undefined-function-issue-120760.rs:68:12 | -LL | run("dependency").await; - | ^^^ not found in this scope +LL | User { dep }.save().await; + | ^^^ `User<'_>` does not have this field + | + = note: available fields are: `name` error: aborting due to 4 previous errors diff --git a/tests/ui/parallel-rustc/unexpected-type-issue-120601.rs b/tests/ui/parallel-rustc/unexpected-type-issue-120601.rs index c8eaf8284a38e..556c18c8b28e3 100644 --- a/tests/ui/parallel-rustc/unexpected-type-issue-120601.rs +++ b/tests/ui/parallel-rustc/unexpected-type-issue-120601.rs @@ -1,8 +1,6 @@ -//@ edition:2015 // Test for #120601, which causes an ice bug cause of unexpected type -// -//@ compile-flags: -Z threads=40 -//@ compare-output-by-lines + +//@ edition:2015 struct T; struct Tuple(i32); diff --git a/tests/ui/parallel-rustc/unexpected-type-issue-120601.stderr b/tests/ui/parallel-rustc/unexpected-type-issue-120601.stderr index 70471719a70db..622223b9e5968 100644 --- a/tests/ui/parallel-rustc/unexpected-type-issue-120601.stderr +++ b/tests/ui/parallel-rustc/unexpected-type-issue-120601.stderr @@ -1,5 +1,5 @@ error[E0670]: `async fn` is not permitted in Rust 2015 - --> $DIR/unexpected-type-issue-120601.rs:10:1 + --> $DIR/unexpected-type-issue-120601.rs:8:1 | LL | async fn foo() -> Result<(), ()> { | ^^^^^ to use `async fn`, switch to Rust 2018 or later @@ -8,7 +8,7 @@ LL | async fn foo() -> Result<(), ()> { = note: for more on editions, read https://doc.rust-lang.org/edition-guide error[E0670]: `async fn` is not permitted in Rust 2015 - --> $DIR/unexpected-type-issue-120601.rs:16:1 + --> $DIR/unexpected-type-issue-120601.rs:14:1 | LL | async fn tuple() -> Tuple { | ^^^^^ to use `async fn`, switch to Rust 2018 or later @@ -17,7 +17,7 @@ LL | async fn tuple() -> Tuple { = note: for more on editions, read https://doc.rust-lang.org/edition-guide error[E0670]: `async fn` is not permitted in Rust 2015 - --> $DIR/unexpected-type-issue-120601.rs:21:1 + --> $DIR/unexpected-type-issue-120601.rs:19:1 | LL | async fn match_() { | ^^^^^ to use `async fn`, switch to Rust 2018 or later @@ -25,8 +25,14 @@ LL | async fn match_() { = help: pass `--edition 2024` to `rustc` = note: for more on editions, read https://doc.rust-lang.org/edition-guide +error[E0425]: cannot find function, tuple struct or tuple variant `Unstable2` in this scope + --> $DIR/unexpected-type-issue-120601.rs:9:5 + | +LL | Unstable2(()) + | ^^^^^^^^^ not found in this scope + error[E0308]: mismatched types - --> $DIR/unexpected-type-issue-120601.rs:23:9 + --> $DIR/unexpected-type-issue-120601.rs:21:9 | LL | match tuple() { | ------- this expression has type `impl Future` @@ -40,12 +46,6 @@ help: consider `await`ing on the `Future` LL | match tuple().await { | ++++++ -error[E0425]: cannot find function, tuple struct or tuple variant `Unstable2` in this scope - --> $DIR/unexpected-type-issue-120601.rs:11:5 - | -LL | Unstable2(()) - | ^^^^^^^^^ not found in this scope - error: aborting due to 5 previous errors Some errors have detailed explanations: E0308, E0425, E0670. From 5d78eab4dd7c4b079bd9f0eab7cce139c82e2bb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 29 Jul 2026 18:23:20 +0000 Subject: [PATCH 09/10] Try to recover less from incorrectly parsed const arg --- compiler/rustc_parse/src/parser/path.rs | 4 ++-- .../early/closing-args-token.rs | 1 - .../early/closing-args-token.stderr | 19 ++++--------------- .../early/macro_rules-braces.stderr | 4 ++-- .../parser/cast-angle-bracket-precedence.rs | 5 +++++ .../cast-angle-bracket-precedence.stderr | 15 ++++++++++++++- 6 files changed, 27 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 8ed2734b7de9c..066d4402d5fde 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -902,13 +902,13 @@ impl<'a> Parser<'a> { err })?; if !self.expr_is_valid_const_arg(&expr) { - self.dcx().emit_err(ConstGenericWithoutBraces { + return Err(self.dcx().create_err(ConstGenericWithoutBraces { span: expr.span, sugg: ConstGenericWithoutBracesSugg { left: expr.span.shrink_to_lo(), right: expr.span.shrink_to_hi(), }, - }); + })); } Ok(expr) diff --git a/tests/ui/const-generics/early/closing-args-token.rs b/tests/ui/const-generics/early/closing-args-token.rs index cb4d6299ed653..7eb6cb26db021 100644 --- a/tests/ui/const-generics/early/closing-args-token.rs +++ b/tests/ui/const-generics/early/closing-args-token.rs @@ -4,7 +4,6 @@ struct T; fn bad_args_1() { S::<5 + 2 >> 7>; //~^ ERROR expressions must be enclosed in braces to be used as const generic arguments - //~| ERROR comparison operators cannot be chained } fn bad_args_2() { diff --git a/tests/ui/const-generics/early/closing-args-token.stderr b/tests/ui/const-generics/early/closing-args-token.stderr index 58fff3a85afe9..16b69e930998a 100644 --- a/tests/ui/const-generics/early/closing-args-token.stderr +++ b/tests/ui/const-generics/early/closing-args-token.stderr @@ -10,18 +10,7 @@ LL | S::<{ 5 + 2 } >> 7>; | + + error: comparison operators cannot be chained - --> $DIR/closing-args-token.rs:5:16 - | -LL | S::<5 + 2 >> 7>; - | ^ ^ - | -help: split the comparison into two - | -LL | S::<5 + 2 >> 7 && 7>; - | ++++ - -error: comparison operators cannot be chained - --> $DIR/closing-args-token.rs:11:20 + --> $DIR/closing-args-token.rs:10:20 | LL | S::<{ 5 + 2 } >> 7>; | ^ ^ @@ -32,13 +21,13 @@ LL | S::<{ 5 + 2 } >> 7 && 7>; | ++++ error: expected expression, found `;` - --> $DIR/closing-args-token.rs:16:16 + --> $DIR/closing-args-token.rs:15:16 | LL | T::<0 >= 3>; | ^ expected expression error: comparison operators cannot be chained - --> $DIR/closing-args-token.rs:22:12 + --> $DIR/closing-args-token.rs:21:12 | LL | T::>= 2 > 0>; | ^^ ^ @@ -48,5 +37,5 @@ help: split the comparison into two LL | T::>= 2 && 2 > 0>; | ++++ -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/const-generics/early/macro_rules-braces.stderr b/tests/ui/const-generics/early/macro_rules-braces.stderr index 3d2bd39163e62..410b26d823bcc 100644 --- a/tests/ui/const-generics/early/macro_rules-braces.stderr +++ b/tests/ui/const-generics/early/macro_rules-braces.stderr @@ -2,7 +2,7 @@ error: expressions must be enclosed in braces to be used as const generic argume --> $DIR/macro_rules-braces.rs:44:17 | LL | let _: baz!(m::P); - | ^^^^ + | -----^^^^- this macro call doesn't expand to a type | help: enclose the `const` expression in braces | @@ -13,7 +13,7 @@ error: expressions must be enclosed in braces to be used as const generic argume --> $DIR/macro_rules-braces.rs:64:17 | LL | let _: baz!(10 + 7); - | ^^^^^^ + | -----^^^^^^- this macro call doesn't expand to a type | help: enclose the `const` expression in braces | diff --git a/tests/ui/parser/cast-angle-bracket-precedence.rs b/tests/ui/parser/cast-angle-bracket-precedence.rs index 65b598d03bdef..7c33e7114b464 100644 --- a/tests/ui/parser/cast-angle-bracket-precedence.rs +++ b/tests/ui/parser/cast-angle-bracket-precedence.rs @@ -27,3 +27,8 @@ fn main() { println!("{}", a as usize << long_name); //~ ERROR `<<` is interpreted as a start of generic } + +fn foo() { + if 1 as f64 < 0.0 && 1 >= 0 {} + //~^ ERROR `<` is interpreted as a start of generic arguments for `f64`, not a compariso +} diff --git a/tests/ui/parser/cast-angle-bracket-precedence.stderr b/tests/ui/parser/cast-angle-bracket-precedence.stderr index 975bfea9425aa..8f271cb790011 100644 --- a/tests/ui/parser/cast-angle-bracket-precedence.stderr +++ b/tests/ui/parser/cast-angle-bracket-precedence.stderr @@ -1,3 +1,16 @@ +error: `<` is interpreted as a start of generic arguments for `f64`, not a comparison + --> $DIR/cast-angle-bracket-precedence.rs:32:17 + | +LL | if 1 as f64 < 0.0 && 1 >= 0 {} + | ^ ----------- interpreted as generic arguments + | | + | not interpreted as comparison + | +help: try comparing the cast value + | +LL | if (1 as f64) < 0.0 && 1 >= 0 {} + | + + + error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison --> $DIR/cast-angle-bracket-precedence.rs:8:31 | @@ -82,5 +95,5 @@ help: try shifting the cast value LL | println!("{}", (a as usize) << long_name); | + + -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors From 4631affea782c2fe380a6e1dec947d2e227f8aef Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Wed, 29 Jul 2026 16:26:15 -0400 Subject: [PATCH 10/10] Mark `Tuple` and `FnPtr` traits `#[fundamental]` --- library/core/src/marker.rs | 2 ++ tests/ui/fn/fn-ptr-trait.rs | 3 ++- tests/ui/tuple/tuple-trait.rs | 11 +++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 tests/ui/tuple/tuple-trait.rs diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index e3785c92c8d0d..a1a1ec56d14a1 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -1069,6 +1069,7 @@ pub const trait Destruct: PointeeSized {} #[unstable(feature = "tuple_trait", issue = "157987")] #[lang = "tuple_trait"] #[diagnostic::on_unimplemented(message = "`{Self}` is not a tuple")] +#[fundamental] #[rustc_deny_explicit_impl] #[rustc_dyn_incompatible_trait] pub trait Tuple {} @@ -1145,6 +1146,7 @@ marker_impls! { reason = "internal trait for implementing various traits for all function pointers" )] #[lang = "fn_ptr_trait"] +#[fundamental] #[rustc_deny_explicit_impl] #[rustc_dyn_incompatible_trait] pub trait FnPtr: Copy + Clone { diff --git a/tests/ui/fn/fn-ptr-trait.rs b/tests/ui/fn/fn-ptr-trait.rs index 3edde574c2660..b9096d5f303f5 100644 --- a/tests/ui/fn/fn-ptr-trait.rs +++ b/tests/ui/fn/fn-ptr-trait.rs @@ -4,6 +4,7 @@ use std::marker::FnPtr; trait Foo {} -impl Foo for Vec where T: FnPtr {} +impl Foo for T where T: FnPtr {} +impl Foo for i32 {} // works because `FnPtr` is `#[fundamental]` fn main() {} diff --git a/tests/ui/tuple/tuple-trait.rs b/tests/ui/tuple/tuple-trait.rs new file mode 100644 index 0000000000000..1ce504c432ba4 --- /dev/null +++ b/tests/ui/tuple/tuple-trait.rs @@ -0,0 +1,11 @@ +//@ check-pass + +#![feature(tuple_trait)] + +use std::marker::Tuple; + +trait Foo {} +impl Foo for T where T: Tuple {} +impl Foo for i32 {} // works because `Tuple` is `#[fundamental]` + +fn main() {}