diff options
Diffstat (limited to 'compiler/rustc_parse')
| -rw-r--r-- | compiler/rustc_parse/messages.ftl | 2 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/errors.rs | 8 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 32 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/item.rs | 81 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/ty.rs | 65 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/validate_attr.rs | 7 |
6 files changed, 116 insertions, 79 deletions
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 1f221b4bf78..f6e0b08b140 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -299,10 +299,12 @@ parse_float_literal_unsupported_base = {$base} float literal is not supported parse_fn_pointer_cannot_be_async = an `fn` pointer type cannot be `async` .label = `async` because of this .suggestion = remove the `async` qualifier + .note = allowed qualifiers are: `unsafe` and `extern` parse_fn_pointer_cannot_be_const = an `fn` pointer type cannot be `const` .label = `const` because of this .suggestion = remove the `const` qualifier + .note = allowed qualifiers are: `unsafe` and `extern` parse_fn_ptr_with_generics = function pointer types may not have generic parameters .suggestion = consider moving the lifetime {$arity -> diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 2dba568a258..0f0c5434800 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2938,22 +2938,22 @@ pub(crate) struct DynAfterMut { #[derive(Diagnostic)] #[diag(parse_fn_pointer_cannot_be_const)] +#[note] pub(crate) struct FnPointerCannotBeConst { #[primary_span] - pub span: Span, #[label] - pub qualifier: Span, + pub span: Span, #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")] pub suggestion: Span, } #[derive(Diagnostic)] #[diag(parse_fn_pointer_cannot_be_async)] +#[note] pub(crate) struct FnPointerCannotBeAsync { #[primary_span] - pub span: Span, #[label] - pub qualifier: Span, + pub span: Span, #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")] pub suggestion: Span, } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index a298c4d4dec..93489aa8ee9 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1520,22 +1520,20 @@ impl<'a> Parser<'a> { Ok(this.mk_expr(this.prev_token.span, ExprKind::Underscore)) } else if this.token_uninterpolated_span().at_least_rust_2018() { // `Span::at_least_rust_2018()` is somewhat expensive; don't get it repeatedly. + let at_async = this.check_keyword(exp!(Async)); + // check for `gen {}` and `gen move {}` + // or `async gen {}` and `async gen move {}` + // FIXME: (async) gen closures aren't yet parsed. + // FIXME(gen_blocks): Parse `gen async` and suggest swap if this.token_uninterpolated_span().at_least_rust_2024() - // check for `gen {}` and `gen move {}` - // or `async gen {}` and `async gen move {}` - && (this.is_gen_block(kw::Gen, 0) - || (this.check_keyword(exp!(Async)) && this.is_gen_block(kw::Gen, 1))) + && this.is_gen_block(kw::Gen, at_async as usize) { - // FIXME: (async) gen closures aren't yet parsed. this.parse_gen_block() - } else if this.check_keyword(exp!(Async)) { - // FIXME(gen_blocks): Parse `gen async` and suggest swap - if this.is_gen_block(kw::Async, 0) { - // Check for `async {` and `async move {`, - this.parse_gen_block() - } else { - this.parse_expr_closure() - } + // Check for `async {` and `async move {`, + } else if this.is_gen_block(kw::Async, 0) { + this.parse_gen_block() + } else if at_async { + this.parse_expr_closure() } else if this.eat_keyword_noexpect(kw::Await) { this.recover_incorrect_await_syntax(lo) } else { @@ -2407,6 +2405,14 @@ impl<'a> Parser<'a> { None }; + if let ClosureBinder::NotPresent = binder + && coroutine_kind.is_some() + { + // coroutine closures and generators can have the same qualifiers, so we might end up + // in here if there is a missing `|` but also no `{`. Adjust the expectations in that case. + self.expected_token_types.insert(TokenType::OpenBrace); + } + let capture_clause = self.parse_capture_clause()?; let (fn_decl, fn_arg_span) = self.parse_fn_block_decl()?; let decl_hi = self.prev_token.span; diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index a325c2a57ab..658ed4bd41c 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -23,7 +23,7 @@ use super::{ AttrWrapper, ExpKeywordPair, ExpTokenPair, FollowedByType, ForceCollect, Parser, PathStyle, Recovered, Trailing, UsePreAttrPos, }; -use crate::errors::{self, MacroExpandsToAdtField}; +use crate::errors::{self, FnPointerCannotBeAsync, FnPointerCannotBeConst, MacroExpandsToAdtField}; use crate::{exp, fluent_generated as fluent}; impl<'a> Parser<'a> { @@ -2402,7 +2402,7 @@ impl<'a> Parser<'a> { case: Case, ) -> PResult<'a, (Ident, FnSig, Generics, Option<P<FnContract>>, Option<P<Block>>)> { let fn_span = self.token.span; - let header = self.parse_fn_front_matter(vis, case)?; // `const ... fn` + let header = self.parse_fn_front_matter(vis, case, 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( @@ -2658,16 +2658,37 @@ impl<'a> Parser<'a> { /// /// `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); @@ -2703,6 +2724,11 @@ impl<'a> Parser<'a> { 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 @@ -2716,7 +2742,21 @@ impl<'a> Parser<'a> { Const::Yes(sp) => Some(WrongKw::Duplicated(sp)), Const::No => { recover_constness = Const::Yes(self.token.span); - Some(WrongKw::Misplaced(async_start_sp)) + 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)) { @@ -2742,7 +2782,21 @@ impl<'a> Parser<'a> { closure_id: DUMMY_NODE_ID, return_impl_trait_id: DUMMY_NODE_ID, }); - Some(WrongKw::Misplaced(unsafe_start_sp)) + 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)) { @@ -2840,14 +2894,20 @@ impl<'a> Parser<'a> { // FIXME(gen_blocks): add keyword recovery logic for genness - if wrong_kw.is_some() + 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(); - err.emit(); + // 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, @@ -3194,3 +3254,12 @@ 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/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 9ddfc179e9b..620a34044d1 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -15,10 +15,11 @@ use thin_vec::{ThinVec, thin_vec}; use super::{Parser, PathStyle, SeqSep, TokenType, Trailing}; use crate::errors::{ self, DynAfterMut, ExpectedFnPathFoundFnKeyword, ExpectedMutOrConstInRawPointerType, - FnPointerCannotBeAsync, FnPointerCannotBeConst, FnPtrWithGenerics, FnPtrWithGenericsSugg, - HelpUseLatestEdition, InvalidDynKeyword, LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime, - NestedCVariadicType, ReturnTypesUseThinArrow, + FnPtrWithGenerics, FnPtrWithGenericsSugg, HelpUseLatestEdition, InvalidDynKeyword, + LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime, NestedCVariadicType, + ReturnTypesUseThinArrow, }; +use crate::parser::item::FrontMatterParsingMode; use crate::{exp, maybe_recover_from_interpolated_ty_qpath}; /// Signals whether parsing a type should allow `+`. @@ -669,62 +670,16 @@ impl<'a> Parser<'a> { tokens: None, }; let span_start = self.token.span; - let ast::FnHeader { ext, safety, constness, coroutine_kind } = - self.parse_fn_front_matter(&inherited_vis, Case::Sensitive)?; - let fn_start_lo = self.prev_token.span.lo(); + let ast::FnHeader { ext, safety, .. } = self.parse_fn_front_matter( + &inherited_vis, + Case::Sensitive, + FrontMatterParsingMode::FunctionPtrType, + )?; if self.may_recover() && self.token == TokenKind::Lt { self.recover_fn_ptr_with_generics(lo, &mut params, param_insertion_point)?; } let decl = self.parse_fn_decl(|_| false, AllowPlus::No, recover_return_sign)?; - let whole_span = lo.to(self.prev_token.span); - - // Order/parsing of "front matter" follows: - // `<constness> <coroutine_kind> <safety> <extern> fn()` - // ^ ^ ^ ^ ^ - // | | | | fn_start_lo - // | | | ext_sp.lo - // | | safety_sp.lo - // | coroutine_sp.lo - // const_sp.lo - if let ast::Const::Yes(const_span) = constness { - let next_token_lo = if let Some( - ast::CoroutineKind::Async { span, .. } - | ast::CoroutineKind::Gen { span, .. } - | ast::CoroutineKind::AsyncGen { span, .. }, - ) = coroutine_kind - { - span.lo() - } else if let ast::Safety::Unsafe(span) | ast::Safety::Safe(span) = safety { - span.lo() - } else if let ast::Extern::Implicit(span) | ast::Extern::Explicit(_, span) = ext { - span.lo() - } else { - fn_start_lo - }; - let sugg_span = const_span.with_hi(next_token_lo); - self.dcx().emit_err(FnPointerCannotBeConst { - span: whole_span, - qualifier: const_span, - suggestion: sugg_span, - }); - } - if let Some(ast::CoroutineKind::Async { span: async_span, .. }) = coroutine_kind { - let next_token_lo = if let ast::Safety::Unsafe(span) | ast::Safety::Safe(span) = safety - { - span.lo() - } else if let ast::Extern::Implicit(span) | ast::Extern::Explicit(_, span) = ext { - span.lo() - } else { - fn_start_lo - }; - let sugg_span = async_span.with_hi(next_token_lo); - self.dcx().emit_err(FnPointerCannotBeAsync { - span: whole_span, - qualifier: async_span, - suggestion: sugg_span, - }); - } - // FIXME(gen_blocks): emit a similar error for `gen fn()` + let decl_span = span_start.to(self.prev_token.span); Ok(TyKind::BareFn(P(BareFnTy { ext, safety, generic_params: params, decl, decl_span }))) } diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 378cbb84637..555ab3cdb2b 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -180,9 +180,14 @@ pub fn check_attribute_safety( let diag_span = attr_item.span(); // Attributes can be safe in earlier editions, and become unsafe in later ones. + // + // Use the span of the attribute's name to determine the edition: the span of the + // attribute as a whole may be inaccurate if it was emitted by a macro. + // + // See https://github.com/rust-lang/rust/issues/142182. let emit_error = match unsafe_since { None => true, - Some(unsafe_since) => attr.span.edition() >= unsafe_since, + Some(unsafe_since) => path_span.edition() >= unsafe_since, }; if emit_error { |
