| use rustc_ast::token::{self, IdentKind, MetaVarKind, Token, TokenKind}; | |
| use rustc_ast::util::case::Case; | |
| use rustc_ast::{ | |
| self as ast, BoundAsyncness, BoundConstness, BoundPolarity, DUMMY_NODE_ID, FnPtrTy, FnRetTy, | |
| GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability, | |
| Path, Pinnedness, PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers, TraitObjectSyntax, | |
| Ty, TyKind, UnsafeBinderTy, | |
| }; | |
| use rustc_errors::{Applicability, Diag, E0516, PResult}; | |
| use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym}; | |
| use thin_vec::{ThinVec, thin_vec}; | |
| use super::{Parser, PathStyle, SeqSep, TokenType, Trailing}; | |
| use crate::diagnostics::{ | |
| self, DynAfterMut, ExpectedFnPathFoundFnKeyword, ExpectedMutOrConstInRawPointerType, | |
| FnPtrWithGenerics, FnPtrWithGenericsSugg, HelpUseLatestEdition, InvalidCVariadicType, | |
| InvalidDynKeyword, LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime, NestedCVariadicType, | |
| ReturnTypesUseThinArrow, | |
| }; | |
| use crate::parser::{FnContext, FnParseMode, FrontMatterParsingMode}; | |
| use crate::{exp, maybe_recover_from_interpolated_ty_qpath}; | |
| /// Signals whether parsing a type should allow `+`. | |
| /// | |
| /// For example, let T be the type `impl Default + 'static` | |
| /// With `AllowPlus::Yes`, T will be parsed successfully | |
| /// With `AllowPlus::No`, parsing T will return a parse error | |
| #[derive(Copy, Clone, PartialEq)] | |
| pub(super) enum AllowPlus { | |
| Yes, | |
| No, | |
| } | |
| #[derive(PartialEq)] | |
| pub(super) enum RecoverQPath { | |
| Yes, | |
| No, | |
| } | |
| pub(super) enum RecoverQuestionMark { | |
| Yes, | |
| No, | |
| } | |
| /// Signals whether parsing a type should recover `->`. | |
| /// | |
| /// More specifically, when parsing a function like: | |
| /// ```compile_fail | |
| /// fn foo() => u8 { 0 } | |
| /// fn bar(): u8 { 0 } | |
| /// ``` | |
| /// The compiler will try to recover interpreting `foo() => u8` as `foo() -> u8` when calling | |
| /// `parse_ty` with anything except `RecoverReturnSign::No`, and it will try to recover `bar(): u8` | |
| /// as `bar() -> u8` when passing `RecoverReturnSign::Yes` to `parse_ty` | |
| #[derive(Copy, Clone, PartialEq)] | |
| pub(super) enum RecoverReturnSign { | |
| Yes, | |
| OnlyFatArrow, | |
| No, | |
| } | |
| impl RecoverReturnSign { | |
| /// [RecoverReturnSign::Yes] allows for recovering `fn foo() => u8` and `fn foo(): u8`, | |
| /// [RecoverReturnSign::OnlyFatArrow] allows for recovering only `fn foo() => u8` (recovering | |
| /// colons can cause problems when parsing where clauses), and | |
| /// [RecoverReturnSign::No] doesn't allow for any recovery of the return type arrow | |
| fn can_recover(self, token: &TokenKind) -> bool { | |
| match self { | |
| Self::Yes => matches!(token, token::FatArrow | token::Colon), | |
| Self::OnlyFatArrow => matches!(token, token::FatArrow), | |
| Self::No => false, | |
| } | |
| } | |
| } | |
| // Is `...` (`CVarArgs`) legal at this level of type parsing? | |
| #[derive(PartialEq)] | |
| enum AllowCVariadic { | |
| Yes, | |
| No, | |
| } | |
| /// Determine if the given token can begin a bound assuming it follows Rust 2015 identifier `dyn`. | |
| /// | |
| /// In Rust 2015, `dyn` is a contextual keyword, not a full one. | |
| fn can_begin_dyn_bound_in_edition_2015(t: Token) -> bool { | |
| if t.is_path_start() { | |
| // In `dyn::x`, `dyn<X>` and `dyn<<X>::Y>`, `dyn` should (continue to) denote a regular path | |
| // segment for backward compatibility. We make an exception for `dyn(X)` which used to be | |
| // interpreted as a path with parenthesized generic arguments which can be semantically | |
| // well-formed (consider: `use std::ops::Fn as dyn;`). Instead, we treat it as a trait | |
| // object type whose first bound is parenthesized. | |
| return t != token::PathSep && t != token::Lt && t != token::Shl; | |
| } | |
| // Contrary to `Parser::can_begin_bound`, `!`, `const`, `[` and `async` are deliberately not | |
| // part of this list to contain the number of potential regressions esp. in MBE code. | |
| // `const` and `[` would regress UI test `macro-dyn-const-2015.rs` and | |
| // `!` would regress `dyn!(...)` macro calls in Rust 2015 for example. | |
| t == token::OpenParen || t == token::Question || t.is_lifetime() || t.is_keyword(kw::For) | |
| } | |
| impl<'a> Parser<'a> { | |
| /// Parses a type. | |
| pub fn parse_ty(&mut self) -> PResult<'a, Box<Ty>> { | |
| if self.token == token::DotDotDot { | |
| // We special case this so that we don't talk about "nested C-variadics" in types. | |
| // We still pass in `AllowCVariadic::No` so that `parse_ty_common` can complain about | |
| // things like `Vec<...>`. | |
| let span = self.token.span; | |
| self.bump(); | |
| let kind = TyKind::Err(self.dcx().emit_err(InvalidCVariadicType { span })); | |
| return Ok(self.mk_ty(span, kind)); | |
| } | |
| self.parse_ty_common( | |
| AllowPlus::Yes, | |
| AllowCVariadic::No, | |
| RecoverQPath::Yes, | |
| RecoverReturnSign::Yes, | |
| None, | |
| RecoverQuestionMark::Yes, | |
| ) | |
| } | |
| pub(super) fn parse_ty_with_generics_recovery( | |
| &mut self, | |
| ty_params: &Generics, | |
| ) -> PResult<'a, Box<Ty>> { | |
| self.parse_ty_common( | |
| AllowPlus::Yes, | |
| AllowCVariadic::No, | |
| RecoverQPath::Yes, | |
| RecoverReturnSign::Yes, | |
| Some(ty_params), | |
| RecoverQuestionMark::Yes, | |
| ) | |
| } | |
| /// Parse a type suitable for a function or function pointer parameter. | |
| /// The difference from `parse_ty` is that this version allows `...` | |
| /// (`CVarArgs`) at the top level of the type. | |
| pub(super) fn parse_ty_for_param(&mut self) -> PResult<'a, Box<Ty>> { | |
| let ty = self.parse_ty_common( | |
| AllowPlus::Yes, | |
| AllowCVariadic::Yes, | |
| RecoverQPath::Yes, | |
| RecoverReturnSign::Yes, | |
| None, | |
| RecoverQuestionMark::Yes, | |
| )?; | |
| // Recover a trailing `= EXPR` if present. | |
| if self.may_recover() | |
| && self.check_noexpect(&token::Eq) | |
| && self.look_ahead(1, |tok| tok.can_begin_expr()) | |
| { | |
| let snapshot = self.create_snapshot_for_diagnostic(); | |
| self.bump(); | |
| let eq_span = self.prev_token.span; | |
| match self.parse_expr() { | |
| Ok(e) => { | |
| self.dcx() | |
| .struct_span_err(eq_span.to(e.span), "parameter defaults are not supported") | |
| .emit(); | |
| } | |
| Err(diag) => { | |
| diag.cancel(); | |
| self.restore_snapshot(snapshot); | |
| } | |
| } | |
| } | |
| Ok(ty) | |
| } | |
| /// Parses a type in restricted contexts where `+` is not permitted. | |
| /// | |
| /// Example 1: `&'a TYPE` | |
| /// `+` is prohibited to maintain operator priority (P(+) < P(&)). | |
| /// Example 2: `value1 as TYPE + value2` | |
| /// `+` is prohibited to avoid interactions with expression grammar. | |
| pub(super) fn parse_ty_no_plus(&mut self) -> PResult<'a, Box<Ty>> { | |
| self.parse_ty_common( | |
| AllowPlus::No, | |
| AllowCVariadic::No, | |
| RecoverQPath::Yes, | |
| RecoverReturnSign::Yes, | |
| None, | |
| RecoverQuestionMark::Yes, | |
| ) | |
| } | |
| /// Parses a type following an `as` cast. Similar to `parse_ty_no_plus`, but signaling origin | |
| /// for better diagnostics involving `?`. | |
| pub(super) fn parse_as_cast_ty(&mut self) -> PResult<'a, Box<Ty>> { | |
| self.parse_ty_common( | |
| AllowPlus::No, | |
| AllowCVariadic::No, | |
| RecoverQPath::Yes, | |
| RecoverReturnSign::Yes, | |
| None, | |
| RecoverQuestionMark::No, | |
| ) | |
| } | |
| pub(super) fn parse_ty_no_question_mark_recover(&mut self) -> PResult<'a, Box<Ty>> { | |
| self.parse_ty_common( | |
| AllowPlus::Yes, | |
| AllowCVariadic::No, | |
| RecoverQPath::Yes, | |
| RecoverReturnSign::Yes, | |
| None, | |
| RecoverQuestionMark::No, | |
| ) | |
| } | |
| /// Parse a type without recovering `:` as `->` to avoid breaking code such | |
| /// as `where fn() : for<'a>`. | |
| pub(super) fn parse_ty_for_where_clause(&mut self) -> PResult<'a, Box<Ty>> { | |
| self.parse_ty_common( | |
| AllowPlus::Yes, | |
| AllowCVariadic::No, | |
| RecoverQPath::Yes, | |
| RecoverReturnSign::OnlyFatArrow, | |
| None, | |
| RecoverQuestionMark::Yes, | |
| ) | |
| } | |
| /// Parses an optional return type `[ -> TY ]` in a function declaration. | |
| pub(super) fn parse_ret_ty( | |
| &mut self, | |
| allow_plus: AllowPlus, | |
| recover_qpath: RecoverQPath, | |
| recover_return_sign: RecoverReturnSign, | |
| ) -> PResult<'a, FnRetTy> { | |
| let lo = self.prev_token.span; | |
| Ok(if self.eat(exp!(RArrow)) { | |
| // FIXME(Centril): Can we unconditionally `allow_plus`? | |
| let ty = self.parse_ty_common( | |
| allow_plus, | |
| AllowCVariadic::No, | |
| recover_qpath, | |
| recover_return_sign, | |
| None, | |
| RecoverQuestionMark::Yes, | |
| )?; | |
| FnRetTy::Ty(ty) | |
| } else if recover_return_sign.can_recover(&self.token.kind) { | |
| // Don't `eat` to prevent `=>` from being added as an expected token which isn't | |
| // actually expected and could only confuse users | |
| self.bump(); | |
| self.dcx().emit_err(ReturnTypesUseThinArrow { | |
| span: self.prev_token.span, | |
| suggestion: lo.between(self.token.span), | |
| }); | |
| let ty = self.parse_ty_common( | |
| allow_plus, | |
| AllowCVariadic::No, | |
| recover_qpath, | |
| recover_return_sign, | |
| None, | |
| RecoverQuestionMark::Yes, | |
| )?; | |
| FnRetTy::Ty(ty) | |
| } else { | |
| FnRetTy::Default(self.prev_token.span.shrink_to_hi()) | |
| }) | |
| } | |
| fn parse_ty_common( | |
| &mut self, | |
| allow_plus: AllowPlus, | |
| allow_c_variadic: AllowCVariadic, | |
| recover_qpath: RecoverQPath, | |
| recover_return_sign: RecoverReturnSign, | |
| ty_generics: Option<&Generics>, | |
| recover_question_mark: RecoverQuestionMark, | |
| ) -> PResult<'a, Box<Ty>> { | |
| let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes; | |
| maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery); | |
| if let Some(ty) = self.eat_metavar_seq_with_matcher( | |
| |mv_kind| matches!(mv_kind, MetaVarKind::Ty { .. }), | |
| |this| this.parse_ty_no_question_mark_recover(), | |
| ) { | |
| return Ok(ty); | |
| } | |
| self.recover_from_outer_attributes("types")?; | |
| let lo = self.token.span; | |
| let mut impl_dyn_multi = false; | |
| let kind = if self.check(exp!(OpenParen)) { | |
| self.parse_paren_start_ty(lo, allow_plus)? | |
| } else if self.eat(exp!(Bang)) { | |
| // Never type `!` | |
| TyKind::Never | |
| } else if self.eat(exp!(Star)) { | |
| self.parse_ty_ptr()? | |
| } else if self.eat(exp!(OpenBracket)) { | |
| self.parse_array_or_slice_ty()? | |
| } else if self.check(exp!(And)) || self.check(exp!(AndAnd)) { | |
| // Reference | |
| self.expect_and()?; | |
| self.parse_borrowed_pointee()? | |
| } else if self.eat_keyword_noexpect(kw::Typeof) { | |
| self.parse_typeof_ty(lo)? | |
| } else if self.is_builtin() { | |
| self.parse_builtin_ty()? | |
| } else if self.eat_keyword(exp!(Underscore)) { | |
| // A type to be inferred `_` | |
| TyKind::Infer | |
| } else if self.check_fn_front_matter(false, Case::Sensitive) { | |
| // Function pointer type | |
| self.parse_ty_fn_ptr(lo, ThinVec::new(), None, recover_return_sign)? | |
| } else if self.check_keyword(exp!(For)) { | |
| // Function pointer type or bound list (trait object type) starting with a poly-trait. | |
| // `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T` | |
| // `for<'lt> Trait1<'lt> + Trait2 + 'a` | |
| let (bound_vars, _) = self.parse_higher_ranked_binder()?; | |
| if self.check_fn_front_matter(false, Case::Sensitive) { | |
| self.parse_ty_fn_ptr( | |
| lo, | |
| bound_vars, | |
| Some(self.prev_token.span.shrink_to_lo()), | |
| recover_return_sign, | |
| )? | |
| } else { | |
| // Try to recover `for<'a> dyn Trait` or `for<'a> impl Trait`. | |
| if self.may_recover() | |
| && (self.eat_keyword_noexpect(kw::Impl) || self.eat_keyword_noexpect(kw::Dyn)) | |
| { | |
| let kw = self.prev_token.ident().unwrap().0; | |
| let removal_span = kw.span.with_hi(self.token.span.lo()); | |
| let path = self.parse_path(PathStyle::Type)?; | |
| let mut bounds = thin_vec![GenericBound::Trait(PolyTraitRef::new( | |
| bound_vars, | |
| path, | |
| TraitBoundModifiers::NONE, | |
| lo.to(self.prev_token.span), | |
| ast::Parens::No, | |
| ))]; | |
| if allow_plus == AllowPlus::Yes && self.check_plus() { | |
| self.eat_plus(); | |
| bounds.append(&mut self.parse_generic_bounds()?); | |
| } | |
| self.dcx().emit_err(diagnostics::TransposeDynOrImpl { | |
| span: kw.span, | |
| kw: kw.name.as_str(), | |
| sugg: diagnostics::TransposeDynOrImplSugg { | |
| removal_span, | |
| insertion_span: lo.shrink_to_lo(), | |
| kw: kw.name.as_str(), | |
| }, | |
| }); | |
| match kw.name { | |
| kw::Dyn => TyKind::TraitObject(bounds, TraitObjectSyntax::Dyn), | |
| kw::Impl => TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds), | |
| _ => unreachable!(), | |
| } | |
| } else { | |
| let path = self.parse_path(PathStyle::Type)?; | |
| let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus(); | |
| self.finish_parsing_bare_trait_object_ty( | |
| bound_vars, | |
| path, | |
| lo, | |
| parse_plus, | |
| ast::Parens::No, | |
| )? | |
| } | |
| } | |
| } else if self.eat_keyword(exp!(Impl)) { | |
| self.parse_impl_ty(&mut impl_dyn_multi)? | |
| } else if self.is_explicit_dyn_type() { | |
| self.parse_dyn_ty(&mut impl_dyn_multi)? | |
| } else if self.eat_lt() { | |
| // Qualified path | |
| let (qself, path) = self.parse_qpath(PathStyle::Type)?; | |
| TyKind::Path(Some(qself), path) | |
| } else if (self.token.is_keyword(kw::Const) || self.token.is_keyword(kw::Mut)) | |
| && self.look_ahead(1, |t| *t == token::Star) | |
| { | |
| self.parse_ty_c_style_pointer()? | |
| } else if self.check_path() { | |
| self.parse_path_start_ty(lo, allow_plus, ty_generics)? | |
| } else if self.can_begin_bound() { | |
| self.parse_bare_trait_object_ty(lo, allow_plus)? | |
| } else if self.eat(exp!(DotDotDot)) { | |
| match allow_c_variadic { | |
| AllowCVariadic::Yes => TyKind::CVarArgs, | |
| AllowCVariadic::No => { | |
| // FIXME(c_variadic): Should we just allow `...` syntactically | |
| // anywhere in a type and use semantic restrictions instead? | |
| // NOTE: This may regress certain MBE calls if done incorrectly. | |
| let guar = self.dcx().emit_err(NestedCVariadicType { span: lo }); | |
| TyKind::Err(guar) | |
| } | |
| } | |
| } else if self.check_keyword(exp!(Unsafe)) | |
| && self.look_ahead(1, |tok| tok.kind == token::Lt) | |
| { | |
| self.parse_unsafe_binder_ty()? | |
| } else { | |
| let msg = format!("expected type, found {}", super::token_descr(&self.token)); | |
| let mut err = self.dcx().struct_span_err(lo, msg); | |
| err.span_label(lo, "expected type"); | |
| if self.may_recover() | |
| && (self.eat_keyword_noexpect(kw::True) || self.eat_keyword_noexpect(kw::False)) | |
| { | |
| err.span_suggestion( | |
| self.prev_token.span, | |
| "the type is called", | |
| "bool", | |
| Applicability::MachineApplicable, | |
| ); | |
| err.emit(); | |
| TyKind::Path( | |
| None, | |
| Path::from_ident(Ident { span: self.prev_token.span, name: sym::bool }), | |
| ) | |
| } else { | |
| return Err(err); | |
| } | |
| }; | |
| let span = lo.to(self.prev_token.span); | |
| let mut ty = self.mk_ty(span, kind); | |
| // Try to recover from use of `+` with incorrect priority. | |
| match allow_plus { | |
| AllowPlus::Yes => self.maybe_recover_from_bad_type_plus(&ty)?, | |
| AllowPlus::No => self.maybe_report_ambiguous_plus(impl_dyn_multi, &ty), | |
| } | |
| if let RecoverQuestionMark::Yes = recover_question_mark { | |
| ty = self.maybe_recover_from_question_mark(ty); | |
| } | |
| if allow_qpath_recovery { self.maybe_recover_from_bad_qpath(ty) } else { Ok(ty) } | |
| } | |
| fn parse_unsafe_binder_ty(&mut self) -> PResult<'a, TyKind> { | |
| let lo = self.token.span; | |
| assert!(self.eat_keyword(exp!(Unsafe))); | |
| self.expect_lt()?; | |
| let generic_params = self.parse_generic_params()?; | |
| self.expect_gt()?; | |
| let inner_ty = self.parse_ty()?; | |
| let span = lo.to(self.prev_token.span); | |
| self.psess.gated_spans.gate(sym::unsafe_binders, span); | |
| Ok(TyKind::UnsafeBinder(Box::new(UnsafeBinderTy { generic_params, inner_ty }))) | |
| } | |
| /// Parse a type that begins with an opening parenthesis `(`. | |
| /// | |
| /// More specifically, it parses one of the following: | |
| /// | |
| /// 1. parenthesized type | |
| /// 2. tuple type | |
| /// 3. bare trait object type where the first trait bound is parenthesized | |
| fn parse_paren_start_ty(&mut self, lo: Span, allow_plus: AllowPlus) -> PResult<'a, TyKind> { | |
| let mut trailing_plus = false; | |
| let (ts, trailing) = self.parse_paren_comma_seq(|p| { | |
| let ty = p.parse_ty()?; | |
| trailing_plus = p.prev_token == TokenKind::Plus; | |
| Ok(ty) | |
| })?; | |
| if ts.len() == 1 && matches!(trailing, Trailing::No) { | |
| let ty = ts.into_iter().next().unwrap(); | |
| // Let's check if we actually have a bare trait object type where the first trait bound | |
| // is parenthesized. That's the case if the parentheses are followed by a `+` and if | |
| // what's contained between the parentheses resembles a *BareTraitBound*. | |
| // | |
| // For context, looking at bounds in general (see *Bound*), only trait bounds are | |
| // allowed to be wrapped in parentheses, not however lifetime and use bounds. | |
| let maybe_bounds = allow_plus == AllowPlus::Yes && self.token.is_like_plus(); | |
| match ty.kind { | |
| // `"(" TypePath ")" "+"` | |
| TyKind::Path(None, path) if maybe_bounds => self | |
| .finish_parsing_bare_trait_object_ty( | |
| ThinVec::new(), | |
| path, | |
| lo, | |
| true, | |
| ast::Parens::Yes, | |
| ), | |
| // `"(" BareTraitBound\TypePath | UseBound ")" "+"` | |
| // | |
| // * FIXME: As alluded to above, only trait bounds are meant to allow parens. | |
| // Arguably, it's an accident that we're permitting *UseBound*s and thus types | |
| // like `(use<>)+`. Might need a T-lang FCP to change this. | |
| // * We're checking `!trailing_plus` to prevent us from accepting code like | |
| // `(T+)+` or `('a+)+`. | |
| // * While we could be looking at `('a)+` which we don't want to accept, we | |
| // know that the `parse_ty` above has already emitted an error since the | |
| // lifetime isn't immediately followed by a `+`. | |
| TyKind::TraitObject(mut bounds, TraitObjectSyntax::None) | |
| if maybe_bounds && bounds.len() == 1 && !trailing_plus => | |
| { | |
| self.eat_plus(); | |
| bounds.append(&mut self.parse_generic_bounds()?); | |
| Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::None)) | |
| } | |
| _ => Ok(TyKind::Paren(ty)), | |
| } | |
| } else { | |
| Ok(TyKind::Tup(ts)) | |
| } | |
| } | |
| fn parse_bare_trait_object_ty( | |
| &mut self, | |
| lo: Span, | |
| allow_plus: AllowPlus, | |
| ) -> PResult<'a, TyKind> { | |
| // A lifetime only begins a bare trait object type if it is followed by `+`! | |
| if self.token.is_lifetime() && !self.look_ahead(1, |t| t.is_like_plus()) { | |
| // In Rust 2021 and beyond, we assume that the user didn't intend to write a bare trait | |
| // object type with a leading lifetime bound since that seems very unlikely given the | |
| // fact that `dyn`-less trait objects are *semantically* invalid. | |
| if self.psess.edition.at_least_rust_2021() { | |
| let lt = self.expect_lifetime(); | |
| let mut err = self.dcx().struct_span_err(lo, "expected type, found lifetime"); | |
| err.span_label(lo, "expected type"); | |
| return Ok(match self.maybe_recover_ref_ty_no_leading_ampersand(lt, lo, err) { | |
| Ok(ref_ty) => ref_ty, | |
| Err(err) => TyKind::Err(err.emit()), | |
| }); | |
| } | |
| self.dcx().emit_err(NeedPlusAfterTraitObjectLifetime { | |
| span: lo, | |
| suggestion: lo.shrink_to_hi(), | |
| }); | |
| } | |
| Ok(TyKind::TraitObject( | |
| self.parse_generic_bounds_common(allow_plus)?, | |
| TraitObjectSyntax::None, | |
| )) | |
| } | |
| fn maybe_recover_ref_ty_no_leading_ampersand<'cx>( | |
| &mut self, | |
| lt: Lifetime, | |
| lo: Span, | |
| mut err: Diag<'cx>, | |
| ) -> Result<TyKind, Diag<'cx>> { | |
| if !self.may_recover() { | |
| return Err(err); | |
| } | |
| let snapshot = self.create_snapshot_for_diagnostic(); | |
| let mutbl = self.parse_mutability(); | |
| match self.parse_ty_no_plus() { | |
| Ok(ty) => { | |
| err.span_suggestion_verbose( | |
| lo.shrink_to_lo(), | |
| "you might have meant to write a reference type here", | |
| "&", | |
| Applicability::MaybeIncorrect, | |
| ); | |
| err.emit(); | |
| Ok(TyKind::Ref(Some(lt), MutTy { ty, mutbl })) | |
| } | |
| Err(diag) => { | |
| diag.cancel(); | |
| self.restore_snapshot(snapshot); | |
| Err(err) | |
| } | |
| } | |
| } | |
| fn finish_parsing_bare_trait_object_ty( | |
| &mut self, | |
| generic_params: ThinVec<GenericParam>, | |
| path: ast::Path, | |
| lo: Span, | |
| parse_plus: bool, | |
| parens: ast::Parens, | |
| ) -> PResult<'a, TyKind> { | |
| let mut bounds = thin_vec![GenericBound::Trait(PolyTraitRef::new( | |
| generic_params, | |
| path, | |
| TraitBoundModifiers::NONE, | |
| lo.to(self.prev_token.span), | |
| parens, | |
| ))]; | |
| if parse_plus { | |
| self.eat_plus(); | |
| bounds.append(&mut self.parse_generic_bounds()?); | |
| } | |
| Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::None)) | |
| } | |
| /// Parses a raw pointer with a C-style typo | |
| fn parse_ty_c_style_pointer(&mut self) -> PResult<'a, TyKind> { | |
| let kw_span = self.token.span; | |
| let mutbl = self.parse_mut_or_const(); | |
| if let Some(mutbl) = mutbl | |
| && self.eat(exp!(Star)) | |
| { | |
| let star_span = self.prev_token.span; | |
| let mutability = match mutbl { | |
| Mutability::Not => "const", | |
| Mutability::Mut => "mut", | |
| }; | |
| let ty = self.parse_ty_no_question_mark_recover()?; | |
| self.dcx() | |
| .struct_span_err( | |
| kw_span, | |
| format!("raw pointer types must be written as `*{mutability} T`"), | |
| ) | |
| .with_multipart_suggestion( | |
| format!("put the `*` before `{mutability}`"), | |
| vec![(star_span, String::new()), (kw_span.shrink_to_lo(), "*".to_string())], | |
| Applicability::MachineApplicable, | |
| ) | |
| .emit(); | |
| return Ok(TyKind::Ptr(MutTy { ty, mutbl })); | |
| } | |
| // This is unreachable because we always get into if above and return from it | |
| unreachable!("this could never happen") | |
| } | |
| /// Parses a raw pointer type: `*[const | mut] $type`. | |
| fn parse_ty_ptr(&mut self) -> PResult<'a, TyKind> { | |
| let mutbl = self.parse_mut_or_const().unwrap_or_else(|| { | |
| let span = self.prev_token.span; | |
| self.dcx().emit_err(ExpectedMutOrConstInRawPointerType { | |
| span, | |
| after_asterisk: span.shrink_to_hi(), | |
| }); | |
| Mutability::Not | |
| }); | |
| let ty = self.parse_ty_no_plus()?; | |
| Ok(TyKind::Ptr(MutTy { ty, mutbl })) | |
| } | |
| /// Parses an array (`[TYPE; EXPR]`) or slice (`[TYPE]`) type. | |
| /// The opening `[` bracket is already eaten. | |
| fn parse_array_or_slice_ty(&mut self) -> PResult<'a, TyKind> { | |
| let elt_ty = self.parse_ty()?; | |
| let ty = if self.eat(exp!(Semi)) { | |
| let mut length = self.parse_expr_anon_const()?; | |
| if let Err(e) = self.expect(exp!(CloseBracket)) { | |
| // Try to recover from `X<Y, ...>` when `X::<Y, ...>` works | |
| self.check_mistyped_turbofish_with_multiple_type_params(e, &mut length.value)?; | |
| self.expect(exp!(CloseBracket))?; | |
| } | |
| TyKind::Array(elt_ty, length) | |
| } else if self.eat(exp!(CloseBracket)) { | |
| TyKind::Slice(elt_ty) | |
| } else { | |
| self.maybe_recover_array_ty_without_semi(elt_ty)? | |
| }; | |
| Ok(ty) | |
| } | |
| /// Recover from malformed array type syntax. | |
| /// | |
| /// This method attempts to recover from cases like: | |
| /// - `[u8, 5]` → suggests using `;`, return a Array type | |
| /// - `[u8 5]` → suggests using `;`, return a Array type | |
| /// Consider to add more cases in the future. | |
| fn maybe_recover_array_ty_without_semi(&mut self, elt_ty: Box<Ty>) -> PResult<'a, TyKind> { | |
| let span = self.token.span; | |
| let token_descr = super::token_descr(&self.token); | |
| let mut err = | |
| self.dcx().struct_span_err(span, format!("expected `;` or `]`, found {}", token_descr)); | |
| err.span_label(span, "expected `;` or `]`"); | |
| // If we cannot recover, return the error immediately. | |
| if !self.may_recover() { | |
| return Err(err); | |
| } | |
| let snapshot = self.create_snapshot_for_diagnostic(); | |
| // Consume common erroneous separators. | |
| let hi = self.prev_token.span.hi(); | |
| _ = self.eat(exp!(Comma)) || self.eat(exp!(Colon)) || self.eat(exp!(Star)); | |
| let suggestion_span = self.prev_token.span.with_lo(hi); | |
| // FIXME(mgca): recovery is broken for `const {` args | |
| // we first try to parse pattern like `[u8 5]` | |
| let length = match self.parse_expr_anon_const() { | |
| Ok(length) => length, | |
| Err(e) => { | |
| e.cancel(); | |
| self.restore_snapshot(snapshot); | |
| return Err(err); | |
| } | |
| }; | |
| if let Err(e) = self.expect(exp!(CloseBracket)) { | |
| e.cancel(); | |
| self.restore_snapshot(snapshot); | |
| return Err(err); | |
| } | |
| err.span_suggestion_verbose( | |
| suggestion_span, | |
| "you might have meant to use `;` as the separator", | |
| ";", | |
| Applicability::MaybeIncorrect, | |
| ); | |
| err.emit(); | |
| Ok(TyKind::Array(elt_ty, length)) | |
| } | |
| fn parse_borrowed_pointee(&mut self) -> PResult<'a, TyKind> { | |
| let and_span = self.prev_token.span; | |
| let mut opt_lifetime = self.check_lifetime().then(|| self.expect_lifetime()); | |
| let (pinned, mut mutbl) = self.parse_pin_and_mut(); | |
| if self.token.is_lifetime() && mutbl == Mutability::Mut && opt_lifetime.is_none() { | |
| // A lifetime is invalid here: it would be part of a bare trait bound, which requires | |
| // it to be followed by a plus, but we disallow plus in the pointee type. | |
| // So we can handle this case as an error here, and suggest `'a mut`. | |
| // If there *is* a plus next though, handling the error later provides better suggestions | |
| // (like adding parentheses) | |
| if !self.look_ahead(1, |t| t.is_like_plus()) { | |
| let lifetime_span = self.token.span; | |
| let span = and_span.to(lifetime_span); | |
| let (suggest_lifetime, snippet) = | |
| if let Ok(lifetime_src) = self.span_to_snippet(lifetime_span) { | |
| (Some(span), lifetime_src) | |
| } else { | |
| (None, String::new()) | |
| }; | |
| self.dcx().emit_err(LifetimeAfterMut { span, suggest_lifetime, snippet }); | |
| opt_lifetime = Some(self.expect_lifetime()); | |
| } | |
| } else if self.token.is_keyword(kw::Dyn) | |
| && mutbl == Mutability::Not | |
| && self.look_ahead(1, |t| t.is_keyword(kw::Mut)) | |
| { | |
| // We have `&dyn mut ...`, which is invalid and should be `&mut dyn ...`. | |
| let span = and_span.to(self.look_ahead(1, |t| t.span)); | |
| self.dcx().emit_err(DynAfterMut { span }); | |
| // Recovery | |
| mutbl = Mutability::Mut; | |
| let (dyn_tok, dyn_tok_sp) = (self.token, self.token_spacing); | |
| self.bump(); | |
| self.bump_with((dyn_tok, dyn_tok_sp)); | |
| } | |
| let ty = self.parse_ty_no_plus()?; | |
| Ok(match pinned { | |
| Pinnedness::Not => TyKind::Ref(opt_lifetime, MutTy { ty, mutbl }), | |
| Pinnedness::Pinned => TyKind::PinnedRef(opt_lifetime, MutTy { ty, mutbl }), | |
| }) | |
| } | |
| /// Parse nothing, mutability or `pin` followed by "explicit" mutability. | |
| /// | |
| /// ```ebnf | |
| /// PinAndMut = "pin" MutOrConst | "mut" | |
| /// ``` | |
| pub(crate) fn parse_pin_and_mut(&mut self) -> (Pinnedness, Mutability) { | |
| if self.token.is_keyword(kw::Pin) && self.look_ahead(1, Token::is_mutability) { | |
| self.psess.gated_spans.gate(sym::pin_ergonomics, self.token.span); | |
| assert!(self.eat_keyword(exp!(Pin))); | |
| let mutbl = self.parse_mut_or_const().unwrap(); | |
| (Pinnedness::Pinned, mutbl) | |
| } else { | |
| (Pinnedness::Not, self.parse_mutability()) | |
| } | |
| } | |
| /// Parses the `typeof(EXPR)` for better diagnostics before returning | |
| /// an error type. | |
| fn parse_typeof_ty(&mut self, lo: Span) -> PResult<'a, TyKind> { | |
| self.expect(exp!(OpenParen))?; | |
| let _expr = self.parse_expr_anon_const()?; | |
| self.expect(exp!(CloseParen))?; | |
| let span = lo.to(self.prev_token.span); | |
| let guar = self | |
| .dcx() | |
| .struct_span_err(span, "`typeof` is a reserved keyword but unimplemented") | |
| .with_note("consider replacing `typeof(...)` with an actual type") | |
| .with_code(E0516) | |
| .emit(); | |
| Ok(TyKind::Err(guar)) | |
| } | |
| fn parse_builtin_ty(&mut self) -> PResult<'a, TyKind> { | |
| self.parse_builtin(|this, lo, ident| { | |
| Ok(match ident.name { | |
| sym::field_of => Some(this.parse_ty_field_of(lo)?), | |
| _ => None, | |
| }) | |
| }) | |
| } | |
| pub(crate) fn parse_ty_field_of(&mut self, _lo: Span) -> PResult<'a, TyKind> { | |
| let container = self.parse_ty()?; | |
| self.expect(exp!(Comma))?; | |
| let fields = self.parse_floating_field_access()?; | |
| let trailing_comma = self.eat_noexpect(&TokenKind::Comma); | |
| if let Err(mut e) = self.expect_one_of(&[], &[exp!(CloseParen)]) { | |
| if trailing_comma { | |
| e.note("unexpected third argument to field_of"); | |
| } else { | |
| e.note("field_of expects dot-separated field and variant names"); | |
| } | |
| e.emit(); | |
| } | |
| // Eat tokens until the macro call ends. | |
| if self.may_recover() { | |
| while !self.token.kind.is_close_delim_or_eof() { | |
| self.bump(); | |
| } | |
| } | |
| match *fields { | |
| [] => Err(self.dcx().struct_span_err( | |
| self.token.span, | |
| "`field_of!` expects dot-separated field and variant names", | |
| )), | |
| [field] => Ok(TyKind::FieldOf(container, None, field)), | |
| [variant, field] => Ok(TyKind::FieldOf(container, Some(variant), field)), | |
| _ => Err(self.dcx().struct_span_err( | |
| fields.iter().map(|f| f.span).collect::<Vec<_>>(), | |
| "`field_of!` only supports a single field or a variant with a field", | |
| )), | |
| } | |
| } | |
| /// Parses a function pointer type (`TyKind::FnPtr`). | |
| /// ```ignore (illustrative) | |
| /// [unsafe] [extern "ABI"] fn (S) -> T | |
| /// // ^~~~~^ ^~~~^ ^~^ ^ | |
| /// // | | | | | |
| /// // | | | Return type | |
| /// // Function Style ABI Parameter types | |
| /// ``` | |
| /// We actually parse `FnHeader FnDecl`, but we error on `const` and `async` qualifiers. | |
| fn parse_ty_fn_ptr( | |
| &mut self, | |
| lo: Span, | |
| mut params: ThinVec<GenericParam>, | |
| param_insertion_point: Option<Span>, | |
| recover_return_sign: RecoverReturnSign, | |
| ) -> PResult<'a, TyKind> { | |
| let inherited_vis = rustc_ast::Visibility { | |
| span: rustc_span::DUMMY_SP, | |
| kind: rustc_ast::VisibilityKind::Inherited, | |
| }; | |
| let span_start = self.token.span; | |
| 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 mode = crate::parser::FnParseMode { | |
| req_name: |_, _| false, | |
| context: FnContext::FunctionPtrType, | |
| req_body: false, | |
| }; | |
| let decl = self.parse_fn_decl(&mode, AllowPlus::No, recover_return_sign)?; | |
| let decl_span = span_start.to(self.prev_token.span); | |
| Ok(TyKind::FnPtr(Box::new(FnPtrTy { | |
| ext, | |
| safety, | |
| generic_params: params, | |
| decl, | |
| decl_span, | |
| }))) | |
| } | |
| /// Recover from function pointer types with a generic parameter list (e.g. `fn<'a>(&'a str)`). | |
| fn recover_fn_ptr_with_generics( | |
| &mut self, | |
| lo: Span, | |
| params: &mut ThinVec<GenericParam>, | |
| param_insertion_point: Option<Span>, | |
| ) -> PResult<'a, ()> { | |
| let generics = self.parse_generics()?; | |
| let arity = generics.params.len(); | |
| let mut lifetimes: ThinVec<_> = generics | |
| .params | |
| .into_iter() | |
| .filter(|param| matches!(param.kind, ast::GenericParamKind::Lifetime)) | |
| .collect(); | |
| let sugg = if !lifetimes.is_empty() { | |
| let snippet = | |
| lifetimes.iter().map(|param| param.ident.as_str()).intersperse(", ").collect(); | |
| let (left, snippet) = if let Some(span) = param_insertion_point { | |
| (span, if params.is_empty() { snippet } else { format!(", {snippet}") }) | |
| } else { | |
| (lo.shrink_to_lo(), format!("for<{snippet}> ")) | |
| }; | |
| Some(FnPtrWithGenericsSugg { | |
| left, | |
| snippet, | |
| right: generics.span, | |
| arity, | |
| for_param_list_exists: param_insertion_point.is_some(), | |
| }) | |
| } else { | |
| None | |
| }; | |
| self.dcx().emit_err(FnPtrWithGenerics { span: generics.span, sugg }); | |
| params.append(&mut lifetimes); | |
| Ok(()) | |
| } | |
| /// Parses an `impl B0 + ... + Bn` type. | |
| fn parse_impl_ty(&mut self, impl_dyn_multi: &mut bool) -> PResult<'a, TyKind> { | |
| // If we encounter a type like `impl 'a Sized`, suggest `impl 'a + Sized`. | |
| if self.token.is_lifetime() | |
| && let Some(ident) = self.look_ahead(1, |t| t.non_reserved_ident()) | |
| { | |
| self.dcx().emit_err(diagnostics::MissingPlusBounds { | |
| span: self.token.span, | |
| hi: self.token.span.shrink_to_hi(), | |
| sym: ident.name, | |
| }); | |
| } | |
| // Always parse bounds greedily for better error recovery. | |
| let bounds = self.parse_generic_bounds()?; | |
| *impl_dyn_multi = bounds.len() > 1 || self.prev_token == TokenKind::Plus; | |
| Ok(TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds)) | |
| } | |
| /// Parse a use-bound aka precise capturing list. | |
| /// | |
| /// ```ebnf | |
| /// UseBound = "use" "<" (PreciseCapture ("," PreciseCapture)* ","?)? ">" | |
| /// PreciseCapture = "Self" | Ident | Lifetime | |
| /// ``` | |
| fn parse_use_bound(&mut self, lo: Span, parens: ast::Parens) -> PResult<'a, GenericBound> { | |
| self.expect_lt()?; | |
| let (args, _, _) = self.parse_seq_to_before_tokens( | |
| &[exp!(Gt)], | |
| &[&TokenKind::Ge, &TokenKind::Shr, &TokenKind::Shr], | |
| SeqSep::trailing_allowed(exp!(Comma)), | |
| |self_| { | |
| if self_.check_keyword(exp!(SelfUpper)) { | |
| self_.bump(); | |
| Ok(PreciseCapturingArg::Arg( | |
| ast::Path::from_ident(self_.prev_token.ident().unwrap().0), | |
| DUMMY_NODE_ID, | |
| )) | |
| } else if self_.check_ident() { | |
| Ok(PreciseCapturingArg::Arg( | |
| ast::Path::from_ident(self_.parse_ident()?), | |
| DUMMY_NODE_ID, | |
| )) | |
| } else if self_.check_lifetime() { | |
| Ok(PreciseCapturingArg::Lifetime(self_.expect_lifetime())) | |
| } else { | |
| self_.unexpected_any() | |
| } | |
| }, | |
| )?; | |
| self.expect_gt()?; | |
| if let ast::Parens::Yes = parens { | |
| self.expect(exp!(CloseParen))?; | |
| self.report_parenthesized_bound(lo, self.prev_token.span, "precise capturing lists"); | |
| } | |
| Ok(GenericBound::Use(args, lo.to(self.prev_token.span))) | |
| } | |
| /// Is a `dyn B0 + ... + Bn` type allowed here? | |
| fn is_explicit_dyn_type(&mut self) -> bool { | |
| self.check_keyword(exp!(Dyn)) | |
| && (self.token_uninterpolated_span().at_least_rust_2018() | |
| || self.look_ahead(1, |&t| can_begin_dyn_bound_in_edition_2015(t))) | |
| } | |
| /// Parses a `dyn B0 + ... + Bn` type. | |
| /// | |
| /// Note that this does *not* parse bare trait objects. | |
| fn parse_dyn_ty(&mut self, impl_dyn_multi: &mut bool) -> PResult<'a, TyKind> { | |
| self.bump(); // `dyn` | |
| // Always parse bounds greedily for better error recovery. | |
| let bounds = self.parse_generic_bounds()?; | |
| *impl_dyn_multi = bounds.len() > 1 || self.prev_token == TokenKind::Plus; | |
| Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::Dyn)) | |
| } | |
| /// Parse a type that begins with a path. | |
| /// | |
| /// This can be: | |
| /// 1. a type macro, `mac!(...)`, | |
| /// 2. a bare trait object type, `B0 + ... + Bn`, | |
| /// 3. or a path, `path::to::MyType`. | |
| fn parse_path_start_ty( | |
| &mut self, | |
| lo: Span, | |
| allow_plus: AllowPlus, | |
| ty_generics: Option<&Generics>, | |
| ) -> PResult<'a, TyKind> { | |
| // Simple path | |
| let path = self.parse_path_inner(PathStyle::Type, ty_generics)?; | |
| if self.eat(exp!(Bang)) { | |
| // Macro invocation in type position | |
| Ok(TyKind::MacCall(Box::new(MacCall { path, args: self.parse_delim_args()? }))) | |
| } else if allow_plus == AllowPlus::Yes && self.check_plus() { | |
| // `Trait1 + Trait2 + 'a` | |
| self.finish_parsing_bare_trait_object_ty( | |
| ThinVec::new(), | |
| path, | |
| lo, | |
| true, | |
| ast::Parens::No, | |
| ) | |
| } else { | |
| // Just a type path. | |
| Ok(TyKind::Path(None, path)) | |
| } | |
| } | |
| pub(super) fn parse_generic_bounds(&mut self) -> PResult<'a, GenericBounds> { | |
| self.parse_generic_bounds_common(AllowPlus::Yes) | |
| } | |
| /// Parse generic bounds. | |
| /// | |
| /// Only if `allow_plus` this parses a `+`-separated list of bounds (trailing `+` is admitted). | |
| /// Otherwise, this only parses a single bound or none. | |
| fn parse_generic_bounds_common(&mut self, allow_plus: AllowPlus) -> PResult<'a, GenericBounds> { | |
| let mut bounds = ThinVec::new(); | |
| // In addition to looping while we find generic bounds: | |
| // We continue even if we find a keyword. This is necessary for error recovery on, | |
| // for example, `impl fn()`. The only keyword that can go after generic bounds is | |
| // `where`, so stop if it's it. | |
| // We also continue if we find types (not traits), again for error recovery. | |
| while self.can_begin_bound() | |
| || (self.may_recover() | |
| && (self.token.can_begin_type() | |
| || (self.token.is_reserved_ident() && !self.token.is_keyword(kw::Where)))) | |
| { | |
| if self.token.is_keyword(kw::Dyn) && self.token.span.edition().at_least_rust_2018() { | |
| // Account for `&dyn Trait + dyn Other`. | |
| self.bump(); | |
| self.dcx().emit_err(InvalidDynKeyword { | |
| span: self.prev_token.span, | |
| suggestion: self.prev_token.span.until(self.token.span), | |
| }); | |
| } | |
| bounds.push(self.parse_generic_bound()?); | |
| if allow_plus == AllowPlus::No || !self.eat_plus() { | |
| break; | |
| } | |
| } | |
| Ok(bounds) | |
| } | |
| /// Can the current token begin a bound? | |
| fn can_begin_bound(&mut self) -> bool { | |
| // NOTE: Tokens `!`, `~`, `const` & `async` which represent the start of currently unstable | |
| // trait bound modifiers are intentionally not included in `Token::can_begin_type` to | |
| // avoid affecting stable macro matching behavior. | |
| self.check_path() | |
| || self.check_lifetime() | |
| || self.check(exp!(Bang)) | |
| || self.check(exp!(Question)) | |
| || self.check(exp!(Tilde)) | |
| || self.check_keyword(exp!(For)) | |
| || self.check(exp!(OpenParen)) | |
| || self.can_begin_maybe_const_bound() | |
| || self.check_keyword(exp!(Const)) | |
| || self.check_keyword(exp!(Async)) | |
| || self.check_keyword(exp!(Use)) | |
| } | |
| fn can_begin_maybe_const_bound(&mut self) -> bool { | |
| self.check(exp!(OpenBracket)) | |
| && self.look_ahead(1, |t| t.is_keyword(kw::Const)) | |
| && self.look_ahead(2, |t| *t == token::CloseBracket) | |
| } | |
| /// Parse a bound. | |
| /// | |
| /// ```ebnf | |
| /// Bound = LifetimeBound | UseBound | TraitBound | |
| /// ``` | |
| fn parse_generic_bound(&mut self) -> PResult<'a, GenericBound> { | |
| let leading_token = self.prev_token; | |
| let lo = self.token.span; | |
| // We only admit parenthesized *trait* bounds. However, we want to gracefully recover from | |
| // other kinds of parenthesized bounds, so parse the opening parenthesis *here*. | |
| // | |
| // In the future we might want to lift this syntactic restriction and | |
| // introduce "`GenericBound::Paren(Box<GenericBound>)`". | |
| let parens = if self.eat(exp!(OpenParen)) { ast::Parens::Yes } else { ast::Parens::No }; | |
| if self.token.is_lifetime() { | |
| self.parse_lifetime_bound(lo, parens) | |
| } else if self.eat_keyword(exp!(Use)) { | |
| self.parse_use_bound(lo, parens) | |
| } else { | |
| self.parse_trait_bound(lo, parens, &leading_token) | |
| } | |
| } | |
| /// Parse a lifetime-bound aka outlives-bound. | |
| /// | |
| /// ```ebnf | |
| /// LifetimeBound = Lifetime | |
| /// ``` | |
| fn parse_lifetime_bound(&mut self, lo: Span, parens: ast::Parens) -> PResult<'a, GenericBound> { | |
| let lt = self.expect_lifetime(); | |
| if let ast::Parens::Yes = parens { | |
| self.expect(exp!(CloseParen))?; | |
| self.report_parenthesized_bound(lo, self.prev_token.span, "lifetime bounds"); | |
| } | |
| Ok(GenericBound::Outlives(lt)) | |
| } | |
| fn report_parenthesized_bound(&self, lo: Span, hi: Span, kind: &str) -> ErrorGuaranteed { | |
| let mut diag = | |
| self.dcx().struct_span_err(lo.to(hi), format!("{kind} may not be parenthesized")); | |
| diag.multipart_suggestion( | |
| "remove the parentheses", | |
| vec![(lo, String::new()), (hi, String::new())], | |
| Applicability::MachineApplicable, | |
| ); | |
| diag.emit() | |
| } | |
| /// Emits an error if any trait bound modifiers were present. | |
| fn error_lt_bound_with_modifiers( | |
| &self, | |
| modifiers: TraitBoundModifiers, | |
| binder_span: Option<Span>, | |
| ) -> ErrorGuaranteed { | |
| let TraitBoundModifiers { constness, asyncness, polarity } = modifiers; | |
| match constness { | |
| BoundConstness::Never => {} | |
| BoundConstness::Always(span) | BoundConstness::Maybe(span) => { | |
| return self.dcx().emit_err(diagnostics::ModifierLifetime { | |
| span, | |
| modifier: constness.as_str(), | |
| }); | |
| } | |
| } | |
| match polarity { | |
| BoundPolarity::Positive => {} | |
| BoundPolarity::Negative(span) | BoundPolarity::Maybe(span) => { | |
| return self | |
| .dcx() | |
| .emit_err(diagnostics::ModifierLifetime { span, modifier: polarity.as_str() }); | |
| } | |
| } | |
| match asyncness { | |
| BoundAsyncness::Normal => {} | |
| BoundAsyncness::Async(span) => { | |
| return self.dcx().emit_err(diagnostics::ModifierLifetime { | |
| span, | |
| modifier: asyncness.as_str(), | |
| }); | |
| } | |
| } | |
| if let Some(span) = binder_span { | |
| return self | |
| .dcx() | |
| .emit_err(diagnostics::ModifierLifetime { span, modifier: "for<...>" }); | |
| } | |
| unreachable!("lifetime bound intercepted in `parse_generic_ty_bound` but no modifiers?") | |
| } | |
| /// Parses the modifiers that may precede a trait in a bound, e.g. `?Trait` or `[const] Trait`. | |
| /// | |
| /// If no modifiers are present, this does not consume any tokens. | |
| /// | |
| /// ```ebnf | |
| /// Constness = ("const" | "[" "const" "]")? | |
| /// Asyncness = "async"? | |
| /// Polarity = ("?" | "!")? | |
| /// ``` | |
| /// | |
| /// See `parse_trait_bound` for more context. | |
| fn parse_trait_bound_modifiers(&mut self) -> PResult<'a, TraitBoundModifiers> { | |
| let modifier_lo = self.token.span; | |
| let constness = self.parse_bound_constness()?; | |
| let asyncness = if self.token_uninterpolated_span().at_least_rust_2018() | |
| && self.eat_keyword(exp!(Async)) | |
| { | |
| self.psess.gated_spans.gate(sym::async_trait_bounds, self.prev_token.span); | |
| BoundAsyncness::Async(self.prev_token.span) | |
| } else if self.may_recover() | |
| && self.token_uninterpolated_span().is_rust_2015() | |
| && self.is_kw_followed_by_ident(kw::Async) | |
| { | |
| self.bump(); // eat `async` | |
| self.dcx().emit_err(diagnostics::AsyncBoundModifierIn2015 { | |
| span: self.prev_token.span, | |
| help: HelpUseLatestEdition::new(), | |
| }); | |
| self.psess.gated_spans.gate(sym::async_trait_bounds, self.prev_token.span); | |
| BoundAsyncness::Async(self.prev_token.span) | |
| } else { | |
| BoundAsyncness::Normal | |
| }; | |
| let modifier_hi = self.prev_token.span; | |
| let polarity = if self.eat(exp!(Question)) { | |
| BoundPolarity::Maybe(self.prev_token.span) | |
| } else if self.eat(exp!(Bang)) { | |
| self.psess.gated_spans.gate(sym::negative_bounds, self.prev_token.span); | |
| BoundPolarity::Negative(self.prev_token.span) | |
| } else { | |
| BoundPolarity::Positive | |
| }; | |
| // Enforce the mutual-exclusivity of `const`/`async` and `?`/`!`. | |
| match polarity { | |
| BoundPolarity::Positive => { | |
| // All trait bound modifiers allowed to combine with positive polarity | |
| } | |
| BoundPolarity::Maybe(polarity_span) | BoundPolarity::Negative(polarity_span) => { | |
| match (asyncness, constness) { | |
| (BoundAsyncness::Normal, BoundConstness::Never) => { | |
| // Ok, no modifiers. | |
| } | |
| (_, _) => { | |
| let constness = constness.as_str(); | |
| let asyncness = asyncness.as_str(); | |
| let glue = | |
| if !constness.is_empty() && !asyncness.is_empty() { " " } else { "" }; | |
| let modifiers_concatenated = format!("{constness}{glue}{asyncness}"); | |
| self.dcx().emit_err(diagnostics::PolarityAndModifiers { | |
| polarity_span, | |
| polarity: polarity.as_str(), | |
| modifiers_span: modifier_lo.to(modifier_hi), | |
| modifiers_concatenated, | |
| }); | |
| } | |
| } | |
| } | |
| } | |
| Ok(TraitBoundModifiers { constness, asyncness, polarity }) | |
| } | |
| pub fn parse_bound_constness(&mut self) -> PResult<'a, BoundConstness> { | |
| // FIXME(const_trait_impl): remove `~const` parser support once bootstrap has the new syntax | |
| // in rustfmt | |
| Ok(if self.eat(exp!(Tilde)) { | |
| let tilde = self.prev_token.span; | |
| self.expect_keyword(exp!(Const))?; | |
| let span = tilde.to(self.prev_token.span); | |
| self.psess.gated_spans.gate(sym::const_trait_impl, span); | |
| BoundConstness::Maybe(span) | |
| } else if self.can_begin_maybe_const_bound() { | |
| let start = self.token.span; | |
| self.bump(); | |
| self.expect_keyword(exp!(Const)).unwrap(); | |
| self.bump(); | |
| let span = start.to(self.prev_token.span); | |
| self.psess.gated_spans.gate(sym::const_trait_impl, span); | |
| BoundConstness::Maybe(span) | |
| } else if self.eat_keyword(exp!(Const)) { | |
| self.psess.gated_spans.gate(sym::const_trait_impl, self.prev_token.span); | |
| BoundConstness::Always(self.prev_token.span) | |
| } else { | |
| BoundConstness::Never | |
| }) | |
| } | |
| /// Parse a trait bound. | |
| /// | |
| /// ```ebnf | |
| /// TraitBound = BareTraitBound | "(" BareTraitBound ")" | |
| /// BareTraitBound = | |
| /// (HigherRankedBinder Constness Asyncness | Polarity) | |
| /// TypePath | |
| /// ``` | |
| fn parse_trait_bound( | |
| &mut self, | |
| lo: Span, | |
| parens: ast::Parens, | |
| leading_token: &Token, | |
| ) -> PResult<'a, GenericBound> { | |
| let (mut bound_vars, binder_span) = self.parse_higher_ranked_binder()?; | |
| let modifiers_lo = self.token.span; | |
| let modifiers = self.parse_trait_bound_modifiers()?; | |
| let modifiers_span = modifiers_lo.to(self.prev_token.span); | |
| if let Some(binder_span) = binder_span { | |
| match modifiers.polarity { | |
| BoundPolarity::Negative(polarity_span) | BoundPolarity::Maybe(polarity_span) => { | |
| self.dcx().emit_err(diagnostics::BinderAndPolarity { | |
| binder_span, | |
| polarity_span, | |
| polarity: modifiers.polarity.as_str(), | |
| }); | |
| } | |
| BoundPolarity::Positive => {} | |
| } | |
| } | |
| // Recover erroneous lifetime bound with modifiers or binder. | |
| // e.g. `T: for<'a> 'a` or `T: [const] 'a`. | |
| if self.token.is_lifetime() { | |
| let _: ErrorGuaranteed = self.error_lt_bound_with_modifiers(modifiers, binder_span); | |
| return self.parse_lifetime_bound(lo, parens); | |
| } | |
| if let (more_bound_vars, Some(binder_span)) = self.parse_higher_ranked_binder()? { | |
| bound_vars.extend(more_bound_vars); | |
| self.dcx().emit_err(diagnostics::BinderBeforeModifiers { binder_span, modifiers_span }); | |
| } | |
| let mut path = if self.token.is_keyword(kw::Fn) | |
| && self.look_ahead(1, |t| *t == TokenKind::OpenParen) | |
| && let Some(path) = self.recover_path_from_fn() | |
| { | |
| path | |
| } else if !self.token.is_path_start() && self.token.can_begin_type() { | |
| let ty = self.parse_ty_no_plus()?; | |
| // Instead of finding a path (a trait), we found a type. | |
| let mut err = self.dcx().struct_span_err(ty.span, "expected a trait, found type"); | |
| // If we can recover, try to extract a path from the type. Note | |
| // that we do not use the try operator when parsing the type because | |
| // if it fails then we get a parser error which we don't want (we're trying | |
| // to recover from errors, not make more). | |
| let path = if self.may_recover() { | |
| let (span, message, sugg, path, applicability) = match &ty.kind { | |
| TyKind::Ptr(..) | TyKind::Ref(..) | |
| if let TyKind::Path(_, path) = &ty.peel_refs().kind => | |
| { | |
| ( | |
| ty.span.until(path.span), | |
| "consider removing the indirection", | |
| "", | |
| path, | |
| Applicability::MaybeIncorrect, | |
| ) | |
| } | |
| TyKind::ImplTrait(_, bounds) | |
| if let [GenericBound::Trait(tr, ..), ..] = bounds.as_slice() => | |
| { | |
| ( | |
| ty.span.until(tr.span), | |
| "use the trait bounds directly", | |
| "", | |
| &tr.trait_ref.path, | |
| Applicability::MachineApplicable, | |
| ) | |
| } | |
| _ => return Err(err), | |
| }; | |
| err.span_suggestion_verbose(span, message, sugg, applicability); | |
| path.clone() | |
| } else { | |
| return Err(err); | |
| }; | |
| err.emit(); | |
| path | |
| } else { | |
| self.parse_path(PathStyle::Type)? | |
| }; | |
| if self.may_recover() && self.token == TokenKind::OpenParen { | |
| self.recover_fn_trait_with_lifetime_params(&mut path, &mut bound_vars)?; | |
| } | |
| if let ast::Parens::Yes = parens { | |
| // Someone has written something like `&dyn (Trait + Other)`. The correct code | |
| // would be `&(dyn Trait + Other)` | |
| if self.token.is_like_plus() && leading_token.is_keyword(kw::Dyn) { | |
| self.eat_plus(); | |
| self.parse_generic_bounds()?; | |
| self.expect(exp!(CloseParen))?; | |
| self.dcx().emit_err(diagnostics::IncorrectParensTraitBounds { | |
| span: vec![lo, self.prev_token.span], | |
| sugg: diagnostics::IncorrectParensTraitBoundsSugg { | |
| wrong_span: leading_token.span.shrink_to_hi().to(lo), | |
| new_span: leading_token.span.shrink_to_lo(), | |
| }, | |
| }); | |
| } else { | |
| self.expect(exp!(CloseParen))?; | |
| } | |
| } | |
| let poly_trait = | |
| PolyTraitRef::new(bound_vars, path, modifiers, lo.to(self.prev_token.span), parens); | |
| Ok(GenericBound::Trait(poly_trait)) | |
| } | |
| // recovers a `Fn(..)` parenthesized-style path from `fn(..)` | |
| fn recover_path_from_fn(&mut self) -> Option<ast::Path> { | |
| let fn_token_span = self.token.span; | |
| self.bump(); | |
| let args_lo = self.token.span; | |
| let snapshot = self.create_snapshot_for_diagnostic(); | |
| let mode = | |
| FnParseMode { req_name: |_, _| false, context: FnContext::Free, req_body: false }; | |
| match self.parse_fn_decl(&mode, AllowPlus::No, RecoverReturnSign::OnlyFatArrow) { | |
| Ok(decl) => { | |
| self.dcx().emit_err(ExpectedFnPathFoundFnKeyword { fn_token_span }); | |
| Some(ast::Path { | |
| span: fn_token_span.to(self.prev_token.span), | |
| segments: thin_vec![ast::PathSegment { | |
| ident: Ident::new(sym::Fn, fn_token_span), | |
| id: DUMMY_NODE_ID, | |
| args: Some(Box::new(ast::GenericArgs::Parenthesized( | |
| ast::ParenthesizedArgs { | |
| span: args_lo.to(self.prev_token.span), | |
| inputs: decl.inputs.iter().map(|a| a.clone()).collect(), | |
| inputs_span: args_lo.until(decl.output.span()), | |
| output: decl.output.clone(), | |
| } | |
| ))), | |
| }], | |
| }) | |
| } | |
| Err(diag) => { | |
| diag.cancel(); | |
| self.restore_snapshot(snapshot); | |
| None | |
| } | |
| } | |
| } | |
| /// Parse an optional higher-ranked binder. | |
| /// | |
| /// ```ebnf | |
| /// HigherRankedBinder = ("for" "<" GenericParams ">")? | |
| /// ``` | |
| pub(super) fn parse_higher_ranked_binder( | |
| &mut self, | |
| ) -> PResult<'a, (ThinVec<GenericParam>, Option<Span>)> { | |
| if self.eat_keyword(exp!(For)) { | |
| let lo = self.token.span; | |
| self.expect_lt()?; | |
| let params = self.parse_generic_params()?; | |
| self.expect_gt()?; | |
| // We rely on AST validation to rule out invalid cases: There must not be | |
| // type or const parameters, and parameters must not have bounds. | |
| Ok((params, Some(lo.to(self.prev_token.span)))) | |
| } else { | |
| Ok((ThinVec::new(), None)) | |
| } | |
| } | |
| /// Recover from `Fn`-family traits (Fn, FnMut, FnOnce) with lifetime arguments | |
| /// (e.g. `FnOnce<'a>(&'a str) -> bool`). Up to generic arguments have already | |
| /// been eaten. | |
| fn recover_fn_trait_with_lifetime_params( | |
| &mut self, | |
| fn_path: &mut ast::Path, | |
| lifetime_defs: &mut ThinVec<GenericParam>, | |
| ) -> PResult<'a, ()> { | |
| let fn_path_segment = fn_path.segments.last_mut().unwrap(); | |
| let generic_args = if let Some(p_args) = &fn_path_segment.args { | |
| *p_args.clone() | |
| } else { | |
| // Normally it wouldn't come here because the upstream should have parsed | |
| // generic parameters (otherwise it's impossible to call this function). | |
| return Ok(()); | |
| }; | |
| let lifetimes = | |
| if let ast::GenericArgs::AngleBracketed(ast::AngleBracketedArgs { span: _, args }) = | |
| &generic_args | |
| { | |
| args.into_iter() | |
| .filter_map(|arg| { | |
| if let ast::AngleBracketedArg::Arg(generic_arg) = arg | |
| && let ast::GenericArg::Lifetime(lifetime) = generic_arg | |
| { | |
| Some(lifetime) | |
| } else { | |
| None | |
| } | |
| }) | |
| .collect() | |
| } else { | |
| Vec::new() | |
| }; | |
| // Only try to recover if the trait has lifetime params. | |
| if lifetimes.is_empty() { | |
| return Ok(()); | |
| } | |
| let snapshot = if self.parsing_generics { | |
| // The snapshot is only relevant if we're parsing the generics of an `fn` to avoid | |
| // incorrect recovery. | |
| Some(self.create_snapshot_for_diagnostic()) | |
| } else { | |
| None | |
| }; | |
| // Parse `(T, U) -> R`. | |
| let inputs_lo = self.token.span; | |
| let mode = | |
| FnParseMode { req_name: |_, _| false, context: FnContext::Free, req_body: false }; | |
| let inputs = match self.parse_fn_params(&mode) { | |
| Ok(params) => params, | |
| Err(err) => { | |
| if let Some(snapshot) = snapshot { | |
| self.restore_snapshot(snapshot); | |
| err.cancel(); | |
| return Ok(()); | |
| } else { | |
| return Err(err); | |
| } | |
| } | |
| }; | |
| let inputs_span = inputs_lo.to(self.prev_token.span); | |
| let output = match self.parse_ret_ty(AllowPlus::No, RecoverQPath::No, RecoverReturnSign::No) | |
| { | |
| Ok(output) => output, | |
| Err(err) => { | |
| if let Some(snapshot) = snapshot { | |
| self.restore_snapshot(snapshot); | |
| err.cancel(); | |
| return Ok(()); | |
| } else { | |
| return Err(err); | |
| } | |
| } | |
| }; | |
| let args = ast::ParenthesizedArgs { | |
| span: fn_path_segment.span().to(self.prev_token.span), | |
| inputs, | |
| inputs_span, | |
| output, | |
| } | |
| .into(); | |
| if let Some(snapshot) = snapshot | |
| && ![token::Comma, token::Gt, token::Plus].contains(&self.token.kind) | |
| { | |
| // We would expect another bound or the end of type params by now. Most likely we've | |
| // encountered a `(` *not* representing `Trait()`, but rather the start of the `fn`'s | |
| // argument list where the generic param list wasn't properly closed. | |
| self.restore_snapshot(snapshot); | |
| return Ok(()); | |
| } | |
| *fn_path_segment = ast::PathSegment { | |
| ident: fn_path_segment.ident, | |
| args: Some(args), | |
| id: ast::DUMMY_NODE_ID, | |
| }; | |
| // Convert parsed `<'a>` in `Fn<'a>` into `for<'a>`. | |
| let mut generic_params = lifetimes | |
| .iter() | |
| .map(|lt| GenericParam { | |
| id: lt.id, | |
| ident: lt.ident, | |
| attrs: ast::AttrVec::new(), | |
| bounds: ThinVec::new(), | |
| is_placeholder: false, | |
| kind: ast::GenericParamKind::Lifetime, | |
| colon_span: None, | |
| }) | |
| .collect::<ThinVec<GenericParam>>(); | |
| lifetime_defs.append(&mut generic_params); | |
| let generic_args_span = generic_args.span(); | |
| let snippet = format!( | |
| "for<{}> ", | |
| lifetimes.iter().map(|lt| lt.ident.as_str()).intersperse(", ").collect::<String>(), | |
| ); | |
| let before_fn_path = fn_path.span.shrink_to_lo(); | |
| self.dcx() | |
| .struct_span_err(generic_args_span, "`Fn` traits cannot take lifetime parameters") | |
| .with_multipart_suggestion( | |
| "consider using a higher-ranked trait bound instead", | |
| vec![(generic_args_span, "".to_owned()), (before_fn_path, snippet)], | |
| Applicability::MaybeIncorrect, | |
| ) | |
| .emit(); | |
| Ok(()) | |
| } | |
| pub(super) fn check_lifetime(&mut self) -> bool { | |
| self.expected_token_types.insert(TokenType::Lifetime); | |
| self.token.is_lifetime() | |
| } | |
| /// Parses a single lifetime `'a` or panics. | |
| pub(super) fn expect_lifetime(&mut self) -> Lifetime { | |
| if let Some((ident, kind)) = self.token.lifetime() { | |
| if kind == IdentKind::Normal && ident.without_first_quote().is_reserved_lifetime() { | |
| self.dcx().emit_err(diagnostics::KeywordLifetime { span: ident.span }); | |
| } | |
| self.bump(); | |
| Lifetime { ident, id: ast::DUMMY_NODE_ID } | |
| } else { | |
| self.dcx().span_bug(self.token.span, "not a lifetime") | |
| } | |
| } | |
| pub(super) fn mk_ty(&self, span: Span, kind: TyKind) -> Box<Ty> { | |
| Box::new(Ty { kind, span, id: ast::DUMMY_NODE_ID }) | |
| } | |
| } |