mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-23 02:27:39 +03:00
1acf1c5367
add field representing types *[View all comments](https://triagebot.infra.rust-lang.org/gh-comments/rust-lang/rust/pull/152730)* > [!NOTE] > This is a rewrite of #146307 by using a lang item instead of a custom `TyKind`. We still need a `hir::TyKind::FieldOf` variant, because resolving the field name cannot be done before HIR construction. The advantage of doing it this way is that we don't need to make any changes to types after HIR (including symbol mangling). At the very beginning of this feature implementation, I tried to do it using a lang item, but then quickly abandoned the approach, because at that time I was still intending to support nested fields. Here is a [range-diff](https://triagebot.infra.rust-lang.org/gh-range-diff/rust-lang/rust/605f49b27444a738ea4032cb77e3bdc4eb811bab..d15f5052095b3549111854a2555dd7026b0a729e/605f49b27444a738ea4032cb77e3bdc4eb811bab..f5f42d1e03495dbaa23671c46b15fccddeb3492f) between the two PRs --- # Add Field Representing Types (FRTs) This PR implements the first step of the field projection lang experiment (Tracking Issue: rust-lang/rust#145383). Field representing types (FRTs) are a new kind of type. They can be named through the use of the `field_of!` macro with the first argument being the type and the second the name of the field (or variant and field in the case of an enum). No nested fields are supported. FRTs natively implement the `Field` trait that's also added in this PR. It exposes information about the field such as the type of the field, the type of the base (i.e. the type that contains the field) and the offset within that base type. Only fields of non-packed structs are supported, fields of enums an unions have unique types for each field, but those do not implement the `Field` trait. This PR was created in collaboration with @dingxiangfei2009, it wouldn't have been possible without him, so huge thanks for mentoring me! I updated my library solution for field projections to use the FRTs from `core` instead of creating my own using the hash of the name of the field. See the [Rust-for-Linux/field-projection `lang-experiment` branch](https://github.com/Rust-for-Linux/field-projection/tree/lang-experiment). ## API added to `core::field` ```rust pub unsafe trait Field { type Base; type Type; const OFFSET: usize; } pub macro field_of($Container:ty, $($fields:expr)+ $(,)?); ``` Along with a perma-unstable type that the compiler uses in the expansion of the macro: ```rust #[unstable(feature = "field_representing_type_raw", issue = "none")] pub struct FieldRepresentingType<T: ?Sized, const VARIANT: u32, const FIELD: u32> { _phantom: PhantomData<T>, } ``` ## Explanation of Field Representing Types (FRTs) FRTs are used for compile-time & trait-level reflection for fields of structs & tuples. Each struct & tuple has a unique compiler-generated type nameable through the `field_of!` macro. This type natively contains information about the field such as the outermost container, type of the field and its offset. Users may implement additional traits on these types in order to record custom information (for example a crate may define a [`PinnableField` trait](https://github.com/Rust-for-Linux/field-projection/blob/lang-experiment/src/marker.rs#L9-L23) that records whether the field is structurally pinned). They are the foundation of field projections, a general operation that's generic over the fields of a struct. This genericism needs to be expressible in the trait system. FRTs make this possible, since an operation generic over fields can just be a function with a generic parameter `F: Field`. > [!NOTE] > The approach of field projections has changed considerably since this PR was opened. In the end we might not need FRTs, so this API is highly experimental. FRTs should act as though they were defined as `struct MyStruct_my_field<StructGenerics>;` next to the struct. So it should be local to the crate defining the struct so that one can implement any trait for the FRT from that crate. The `Field` traits should be implemented by the compiler & populated with correct information (`unsafe` code needs to be able to rely on them being correct). ## TODOs There are some `FIXME(FRTs)` scattered around the code: - Diagnostics for `field_of!` can be improved - `tests/ui/field_representing_types/nonexistent.rs` - `tests/ui/field_representing_types/non-struct.rs` - `tests/ui/field_representing_types/offset.rs` - `tests/ui/field_representing_types/not-field-if-packed.rs` - `tests/ui/field_representing_types/invalid.rs` - Simple type alias already seem to work, but might need some extra work in `compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs` r? @oli-obk
1647 lines
64 KiB
Rust
1647 lines
64 KiB
Rust
use rustc_ast::token::{self, IdentIsRaw, 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, MgcaDisambiguation,
|
|
MutTy, Mutability, Pinnedness, PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers,
|
|
TraitObjectSyntax, Ty, TyKind, UnsafeBinderTy,
|
|
};
|
|
use rustc_data_structures::stack::ensure_sufficient_stack;
|
|
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::errors::{
|
|
self, AttributeOnEmptyType, AttributeOnType, DynAfterMut, ExpectedFnPathFoundFnKeyword,
|
|
ExpectedMutOrConstInRawPointerType, FnPtrWithGenerics, FnPtrWithGenericsSugg,
|
|
HelpUseLatestEdition, InvalidCVariadicType, InvalidDynKeyword, LifetimeAfterMut,
|
|
NeedPlusAfterTraitObjectLifetime, NestedCVariadicType, ReturnTypesUseThinArrow,
|
|
};
|
|
use crate::parser::item::FrontMatterParsingMode;
|
|
use crate::parser::{FnContext, FnParseMode};
|
|
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));
|
|
}
|
|
// Make sure deeply nested types don't overflow the stack.
|
|
ensure_sufficient_stack(|| {
|
|
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 self.token == token::Pound && self.look_ahead(1, |t| *t == token::OpenBracket) {
|
|
let attrs_wrapper = self.parse_outer_attributes()?;
|
|
let raw_attrs = attrs_wrapper.take_for_recovery(self.psess);
|
|
let attr_span = raw_attrs[0].span.to(raw_attrs.last().unwrap().span);
|
|
let (full_span, guar) = match self.parse_ty() {
|
|
Ok(ty) => {
|
|
let full_span = attr_span.until(ty.span);
|
|
let guar = self
|
|
.dcx()
|
|
.emit_err(AttributeOnType { span: attr_span, fix_span: full_span });
|
|
(attr_span, guar)
|
|
}
|
|
Err(err) => {
|
|
err.cancel();
|
|
let guar = self.dcx().emit_err(AttributeOnEmptyType { span: attr_span });
|
|
(attr_span, guar)
|
|
}
|
|
};
|
|
|
|
return Ok(self.mk_ty(full_span, TyKind::Err(guar)));
|
|
}
|
|
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);
|
|
}
|
|
|
|
let lo = self.token.span;
|
|
let mut impl_dyn_multi = false;
|
|
let kind = if self.check(exp!(OpenParen)) {
|
|
self.parse_ty_tuple_or_parens(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 parse_plus = allow_plus == AllowPlus::Yes && self.check_plus();
|
|
let kind = self.parse_remaining_bounds_path(
|
|
bound_vars,
|
|
path,
|
|
lo,
|
|
parse_plus,
|
|
ast::Parens::No,
|
|
)?;
|
|
let err = self.dcx().create_err(errors::TransposeDynOrImpl {
|
|
span: kw.span,
|
|
kw: kw.name.as_str(),
|
|
sugg: errors::TransposeDynOrImplSugg {
|
|
removal_span,
|
|
insertion_span: lo.shrink_to_lo(),
|
|
kw: kw.name.as_str(),
|
|
},
|
|
});
|
|
|
|
// Take the parsed bare trait object and turn it either
|
|
// into a `dyn` object or an `impl Trait`.
|
|
let kind = match (kind, kw.name) {
|
|
(TyKind::TraitObject(bounds, _), kw::Dyn) => {
|
|
TyKind::TraitObject(bounds, TraitObjectSyntax::Dyn)
|
|
}
|
|
(TyKind::TraitObject(bounds, _), kw::Impl) => {
|
|
TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds)
|
|
}
|
|
_ => return Err(err),
|
|
};
|
|
err.emit();
|
|
kind
|
|
} else {
|
|
let path = self.parse_path(PathStyle::Type)?;
|
|
let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus();
|
|
self.parse_remaining_bounds_path(
|
|
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(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");
|
|
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 })))
|
|
}
|
|
|
|
/// Parses either:
|
|
/// - `(TYPE)`, a parenthesized type.
|
|
/// - `(TYPE,)`, a tuple with a single field of type TYPE.
|
|
fn parse_ty_tuple_or_parens(&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 maybe_bounds = allow_plus == AllowPlus::Yes && self.token.is_like_plus();
|
|
match ty.kind {
|
|
// `"(" BareTraitBound ")" "+" Bound "+" ...`.
|
|
TyKind::Path(None, path) if maybe_bounds => self.parse_remaining_bounds_path(
|
|
ThinVec::new(),
|
|
path,
|
|
lo,
|
|
true,
|
|
ast::Parens::Yes,
|
|
),
|
|
// For `('a) + …`, we know that `'a` in type position already lead to an error being
|
|
// emitted. To reduce output, let's indirectly suppress E0178 (bad `+` in type) and
|
|
// other irrelevant consequential errors.
|
|
TyKind::TraitObject(bounds, TraitObjectSyntax::None)
|
|
if maybe_bounds && bounds.len() == 1 && !trailing_plus =>
|
|
{
|
|
self.parse_remaining_bounds(bounds, true)
|
|
}
|
|
// `(TYPE)`
|
|
_ => Ok(TyKind::Paren(ty)),
|
|
}
|
|
} else {
|
|
Ok(TyKind::Tup(ts))
|
|
}
|
|
}
|
|
|
|
fn parse_bare_trait_object(&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 parse_remaining_bounds_path(
|
|
&mut self,
|
|
generic_params: ThinVec<GenericParam>,
|
|
path: ast::Path,
|
|
lo: Span,
|
|
parse_plus: bool,
|
|
parens: ast::Parens,
|
|
) -> PResult<'a, TyKind> {
|
|
let poly_trait_ref = PolyTraitRef::new(
|
|
generic_params,
|
|
path,
|
|
TraitBoundModifiers::NONE,
|
|
lo.to(self.prev_token.span),
|
|
parens,
|
|
);
|
|
let bounds = vec![GenericBound::Trait(poly_trait_ref)];
|
|
self.parse_remaining_bounds(bounds, parse_plus)
|
|
}
|
|
|
|
/// Parse the remainder of a bare trait object type given an already parsed list.
|
|
fn parse_remaining_bounds(
|
|
&mut self,
|
|
mut bounds: GenericBounds,
|
|
plus: bool,
|
|
) -> PResult<'a, TyKind> {
|
|
if plus {
|
|
self.eat_plus(); // `+`, or `+=` gets split and `+` is discarded
|
|
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_const_or_mut();
|
|
|
|
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_const_or_mut().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 = match self.parse_ty() {
|
|
Ok(ty) => ty,
|
|
Err(err)
|
|
if self.look_ahead(1, |t| *t == token::CloseBracket)
|
|
| self.look_ahead(1, |t| *t == token::Semi) =>
|
|
{
|
|
// Recover from `[LIT; EXPR]` and `[LIT]`
|
|
self.bump();
|
|
let guar = err.emit();
|
|
self.mk_ty(self.prev_token.span, TyKind::Err(guar))
|
|
}
|
|
Err(err) => return Err(err),
|
|
};
|
|
|
|
let ty = if self.eat(exp!(Semi)) {
|
|
let mut length = self.parse_expr_anon_const(|_, _| MgcaDisambiguation::Direct)?;
|
|
|
|
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(|_, _| MgcaDisambiguation::Direct) {
|
|
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 }),
|
|
})
|
|
}
|
|
|
|
/// Parses `pin` and `mut` annotations on references, patterns, or borrow modifiers.
|
|
///
|
|
/// It must be either `pin const`, `pin mut`, `mut`, or nothing (immutable).
|
|
pub(crate) fn parse_pin_and_mut(&mut self) -> (Pinnedness, Mutability) {
|
|
if self.token.is_ident_named(sym::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_const_or_mut().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(|_, _| MgcaDisambiguation::AnonConst)?;
|
|
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,
|
|
tokens: None,
|
|
};
|
|
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::item::FnParseMode {
|
|
req_name: |_, _| false,
|
|
context: FnContext::Free,
|
|
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 self.token.is_lifetime() {
|
|
self.look_ahead(1, |t| {
|
|
if let token::Ident(sym, _) = t.kind {
|
|
// parse pattern with "'a Sized" we're supposed to give suggestion like
|
|
// "'a + Sized"
|
|
self.dcx().emit_err(errors::MissingPlusBounds {
|
|
span: self.token.span,
|
|
hi: self.token.span.shrink_to_hi(),
|
|
sym,
|
|
});
|
|
}
|
|
})
|
|
}
|
|
|
|
// 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))
|
|
}
|
|
|
|
/// Parses a type starting with a path.
|
|
///
|
|
/// This can be:
|
|
/// 1. a type macro, `mac!(...)`,
|
|
/// 2. a bare trait object, `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.parse_remaining_bounds_path(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 = Vec::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) {
|
|
// 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 {
|
|
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(errors::ModifierLifetime { span, modifier: constness.as_str() });
|
|
}
|
|
}
|
|
|
|
match polarity {
|
|
BoundPolarity::Positive => {}
|
|
BoundPolarity::Negative(span) | BoundPolarity::Maybe(span) => {
|
|
return self
|
|
.dcx()
|
|
.emit_err(errors::ModifierLifetime { span, modifier: polarity.as_str() });
|
|
}
|
|
}
|
|
|
|
match asyncness {
|
|
BoundAsyncness::Normal => {}
|
|
BoundAsyncness::Async(span) => {
|
|
return self
|
|
.dcx()
|
|
.emit_err(errors::ModifierLifetime { span, modifier: asyncness.as_str() });
|
|
}
|
|
}
|
|
|
|
if let Some(span) = binder_span {
|
|
return self.dcx().emit_err(errors::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(errors::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(errors::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(errors::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(errors::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) {
|
|
let bounds = vec![];
|
|
self.parse_remaining_bounds(bounds, true)?;
|
|
self.expect(exp!(CloseParen))?;
|
|
self.dcx().emit_err(errors::IncorrectParensTraitBounds {
|
|
span: vec![lo, self.prev_token.span],
|
|
sugg: errors::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.ty.clone()).collect(),
|
|
inputs_span: args_lo.until(decl.output.span()),
|
|
output: decl.output.clone(),
|
|
}
|
|
))),
|
|
}],
|
|
tokens: None,
|
|
})
|
|
}
|
|
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 params = 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: ThinVec<_> = params.into_iter().map(|input| input.ty).collect();
|
|
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: Vec::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, is_raw)) = self.token.lifetime() {
|
|
if is_raw == IdentIsRaw::No && ident.without_first_quote().is_reserved_lifetime() {
|
|
self.dcx().emit_err(errors::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, tokens: None })
|
|
}
|
|
}
|