| use rustc_ast::{ | |
| self as ast, AttrVec, DUMMY_NODE_ID, GenericBounds, GenericParam, GenericParamKind, TyKind, | |
| WhereClause, token, | |
| }; | |
| use rustc_errors::{Applicability, Diag, PResult}; | |
| use rustc_span::{Ident, Span, kw, sym}; | |
| use thin_vec::ThinVec; | |
| use super::{ForceCollect, Parser, Trailing, UsePreAttrPos}; | |
| use crate::diagnostics::{ | |
| self, MultipleWhereClauses, UnexpectedDefaultValueForLifetimeInGenericParameters, | |
| UnexpectedSelfInGenericParameters, WhereClauseBeforeTupleStructBody, | |
| WhereClauseBeforeTupleStructBodySugg, | |
| }; | |
| use crate::exp; | |
| enum PredicateKindOrStructBody { | |
| PredicateKind(ast::WherePredicateKind), | |
| StructBody(ThinVec<ast::FieldDef>), | |
| } | |
| impl<'a> Parser<'a> { | |
| /// Parses bounds of a lifetime parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`. | |
| /// | |
| /// ```text | |
| /// BOUND = LT_BOUND (e.g., `'a`) | |
| /// ``` | |
| pub(crate) fn parse_lt_param_bounds(&mut self) -> GenericBounds { | |
| let mut lifetimes = ThinVec::new(); | |
| while self.check_lifetime() { | |
| lifetimes.push(ast::GenericBound::Outlives(self.expect_lifetime())); | |
| if !self.eat_plus() { | |
| break; | |
| } | |
| } | |
| lifetimes | |
| } | |
| /// Matches `typaram = IDENT (`?` unbound)? optbounds ( EQ ty )?`. | |
| fn parse_ty_param(&mut self, preceding_attrs: AttrVec) -> PResult<'a, GenericParam> { | |
| let ident = self.parse_ident()?; | |
| // We might have a typo'd `Const` that was parsed as a type parameter. | |
| if self.may_recover() | |
| && ident.name.as_str().to_ascii_lowercase() == kw::Const.as_str() | |
| && self.check_ident() | |
| // `Const` followed by IDENT | |
| { | |
| return self.recover_const_param_with_mistyped_const(preceding_attrs, ident); | |
| } | |
| // Parse optional colon and param bounds. | |
| let mut colon_span = None; | |
| let bounds = if self.eat(exp!(Colon)) { | |
| colon_span = Some(self.prev_token.span); | |
| // recover from `impl Trait` in type param bound | |
| if self.token.is_keyword(kw::Impl) { | |
| let impl_span = self.token.span; | |
| let snapshot = self.create_snapshot_for_diagnostic(); | |
| match self.parse_ty() { | |
| Ok(p) => { | |
| if let TyKind::ImplTrait(_, bounds) = &p.kind { | |
| let span = impl_span.to(self.token.span.shrink_to_lo()); | |
| let mut err = self.dcx().struct_span_err( | |
| span, | |
| "expected trait bound, found `impl Trait` type", | |
| ); | |
| err.span_label(span, "not a trait"); | |
| if let [bound, ..] = &bounds[..] { | |
| err.span_suggestion_verbose( | |
| impl_span.until(bound.span()), | |
| "use the trait bounds directly", | |
| String::new(), | |
| Applicability::MachineApplicable, | |
| ); | |
| } | |
| return Err(err); | |
| } | |
| } | |
| Err(err) => { | |
| err.cancel(); | |
| } | |
| } | |
| self.restore_snapshot(snapshot); | |
| } | |
| self.parse_generic_bounds()? | |
| } else { | |
| ThinVec::new() | |
| }; | |
| let default = if self.eat(exp!(Eq)) { Some(self.parse_ty()?) } else { None }; | |
| Ok(GenericParam { | |
| ident, | |
| id: ast::DUMMY_NODE_ID, | |
| attrs: preceding_attrs, | |
| bounds, | |
| kind: GenericParamKind::Type { default }, | |
| is_placeholder: false, | |
| colon_span, | |
| }) | |
| } | |
| pub(crate) fn parse_const_param( | |
| &mut self, | |
| preceding_attrs: AttrVec, | |
| ) -> PResult<'a, GenericParam> { | |
| let const_span = self.token.span; | |
| self.expect_keyword(exp!(Const))?; | |
| let ident = self.parse_ident()?; | |
| if let Err(mut err) = self.expect(exp!(Colon)) { | |
| return if self.token.kind == token::Comma || self.token.kind == token::Gt { | |
| // Recover parse from `<const N>` where the type is missing. | |
| let span = const_span.to(ident.span); | |
| err.span_suggestion_verbose( | |
| ident.span.shrink_to_hi(), | |
| "you likely meant to write the type of the const parameter here", | |
| ": /* Type */".to_string(), | |
| Applicability::HasPlaceholders, | |
| ); | |
| let kind = TyKind::Err(err.emit()); | |
| let ty = self.mk_ty(span, kind); | |
| Ok(GenericParam { | |
| ident, | |
| id: ast::DUMMY_NODE_ID, | |
| attrs: preceding_attrs, | |
| bounds: ThinVec::new(), | |
| kind: GenericParamKind::Const { ty, span, default: None }, | |
| is_placeholder: false, | |
| colon_span: None, | |
| }) | |
| } else { | |
| Err(err) | |
| }; | |
| } | |
| let ty = self.parse_ty()?; | |
| // Parse optional const generics default value. | |
| let default = if self.eat(exp!(Eq)) { Some(self.parse_const_arg()?) } else { None }; | |
| let span = if let Some(ref default) = default { | |
| const_span.to(default.value.span) | |
| } else { | |
| const_span.to(ty.span) | |
| }; | |
| Ok(GenericParam { | |
| ident, | |
| id: ast::DUMMY_NODE_ID, | |
| attrs: preceding_attrs, | |
| bounds: ThinVec::new(), | |
| kind: GenericParamKind::Const { ty, span, default }, | |
| is_placeholder: false, | |
| colon_span: None, | |
| }) | |
| } | |
| pub(crate) fn recover_const_param_with_mistyped_const( | |
| &mut self, | |
| preceding_attrs: AttrVec, | |
| mistyped_const_ident: Ident, | |
| ) -> PResult<'a, GenericParam> { | |
| let ident = self.parse_ident()?; | |
| self.expect(exp!(Colon))?; | |
| let ty = self.parse_ty()?; | |
| // Parse optional const generics default value. | |
| let default = if self.eat(exp!(Eq)) { Some(self.parse_const_arg()?) } else { None }; | |
| let span = if let Some(ref default) = default { | |
| mistyped_const_ident.span.to(default.value.span) | |
| } else { | |
| mistyped_const_ident.span.to(ty.span) | |
| }; | |
| self.dcx() | |
| .struct_span_err( | |
| mistyped_const_ident.span, | |
| format!("`const` keyword was mistyped as `{}`", mistyped_const_ident.as_str()), | |
| ) | |
| .with_span_suggestion_verbose( | |
| mistyped_const_ident.span, | |
| "use the `const` keyword", | |
| kw::Const, | |
| Applicability::MachineApplicable, | |
| ) | |
| .emit(); | |
| Ok(GenericParam { | |
| ident, | |
| id: ast::DUMMY_NODE_ID, | |
| attrs: preceding_attrs, | |
| bounds: ThinVec::new(), | |
| kind: GenericParamKind::Const { ty, span, default }, | |
| is_placeholder: false, | |
| colon_span: None, | |
| }) | |
| } | |
| /// Parse a (possibly empty) list of generic (lifetime, type, const) parameters. | |
| /// | |
| /// ```ebnf | |
| /// GenericParams = (GenericParam ("," GenericParam)* ","?)? | |
| /// ``` | |
| pub(super) fn parse_generic_params(&mut self) -> PResult<'a, ThinVec<ast::GenericParam>> { | |
| let mut params = ThinVec::new(); | |
| let mut done = false; | |
| let prev = self.parsing_generics; | |
| self.parsing_generics = true; | |
| while !done { | |
| let attrs = self.parse_outer_attributes()?; | |
| let param = match self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| { | |
| if this.eat_keyword_noexpect(kw::SelfUpper) { | |
| // `Self` as a generic param is invalid. Here we emit the diagnostic and continue parsing | |
| // as if `Self` never existed. | |
| this.dcx() | |
| .emit_err(UnexpectedSelfInGenericParameters { span: this.prev_token.span }); | |
| // Eat a trailing comma, if it exists. | |
| let _ = this.eat(exp!(Comma)); | |
| } | |
| let param = if this.check_lifetime() { | |
| let lifetime = this.expect_lifetime(); | |
| // Parse lifetime parameter. | |
| let (colon_span, bounds) = if this.eat(exp!(Colon)) { | |
| (Some(this.prev_token.span), this.parse_lt_param_bounds()) | |
| } else { | |
| (None, ThinVec::new()) | |
| }; | |
| if this.check_noexpect(&token::Eq) && this.look_ahead(1, |t| t.is_lifetime()) { | |
| let lo = this.token.span; | |
| // Parse `= 'lifetime`. | |
| this.bump(); // `=` | |
| this.bump(); // `'lifetime` | |
| let span = lo.to(this.prev_token.span); | |
| this.dcx().emit_err(UnexpectedDefaultValueForLifetimeInGenericParameters { | |
| span, | |
| }); | |
| } | |
| Some(ast::GenericParam { | |
| ident: lifetime.ident, | |
| id: lifetime.id, | |
| attrs, | |
| bounds, | |
| kind: ast::GenericParamKind::Lifetime, | |
| is_placeholder: false, | |
| colon_span, | |
| }) | |
| } else if this.check_keyword(exp!(Const)) { | |
| // Parse const parameter. | |
| Some(this.parse_const_param(attrs)?) | |
| } else if this.check_ident() { | |
| // Parse type parameter. | |
| Some(this.parse_ty_param(attrs)?) | |
| } else if this.token.can_begin_type() { | |
| // Trying to write an associated type bound? (#26271) | |
| let snapshot = this.create_snapshot_for_diagnostic(); | |
| let lo = this.token.span; | |
| match this.parse_ty_where_predicate_kind() { | |
| Ok(_) => { | |
| this.dcx().emit_err(diagnostics::BadAssocTypeBounds { | |
| span: lo.to(this.prev_token.span), | |
| }); | |
| // FIXME - try to continue parsing other generics? | |
| } | |
| Err(err) => { | |
| err.cancel(); | |
| // FIXME - maybe we should overwrite 'self' outside of `collect_tokens`? | |
| this.restore_snapshot(snapshot); | |
| } | |
| } | |
| return Ok((None, Trailing::No, UsePreAttrPos::No)); | |
| } else { | |
| // Check for trailing attributes and stop parsing. | |
| if !attrs.is_empty() { | |
| if !params.is_empty() { | |
| this.dcx() | |
| .emit_err(diagnostics::AttrAfterGeneric { span: attrs[0].span }); | |
| } else { | |
| this.dcx() | |
| .emit_err(diagnostics::AttrWithoutGenerics { span: attrs[0].span }); | |
| } | |
| } | |
| return Ok((None, Trailing::No, UsePreAttrPos::No)); | |
| }; | |
| if !this.eat(exp!(Comma)) { | |
| done = true; | |
| } | |
| // We just ate the comma, so no need to capture the trailing token. | |
| Ok((param, Trailing::No, UsePreAttrPos::No)) | |
| }) { | |
| Ok(param) => param, | |
| Err(err) => { | |
| self.parsing_generics = prev; | |
| return Err(err); | |
| } | |
| }; | |
| if let Some(param) = param { | |
| params.push(param); | |
| } else { | |
| break; | |
| } | |
| } | |
| self.parsing_generics = prev; | |
| Ok(params) | |
| } | |
| /// Parses a set of optional generic type parameter declarations. Where | |
| /// clauses are not parsed here, and must be added later via | |
| /// `parse_where_clause()`. | |
| /// | |
| /// matches generics = ( ) | ( < > ) | ( < typaramseq ( , )? > ) | ( < lifetimes ( , )? > ) | |
| /// | ( < lifetimes , typaramseq ( , )? > ) | |
| /// where typaramseq = ( typaram ) | ( typaram , typaramseq ) | |
| pub(super) fn parse_generics(&mut self) -> PResult<'a, ast::Generics> { | |
| // invalid path separator `::` in function definition | |
| // for example `fn invalid_path_separator::<T>() {}` | |
| if self.eat_noexpect(&token::PathSep) { | |
| self.dcx() | |
| .emit_err(diagnostics::InvalidPathSepInFnDefinition { span: self.prev_token.span }); | |
| } | |
| let span_lo = self.token.span; | |
| let (params, span) = if self.eat_lt() { | |
| let params = self.parse_generic_params()?; | |
| self.expect_gt_or_maybe_suggest_closing_generics(¶ms)?; | |
| (params, span_lo.to(self.prev_token.span)) | |
| } else { | |
| (ThinVec::new(), self.prev_token.span.shrink_to_hi()) | |
| }; | |
| Ok(ast::Generics { | |
| params, | |
| where_clause: WhereClause { | |
| has_where_token: false, | |
| predicates: ThinVec::new(), | |
| span: self.prev_token.span.shrink_to_hi(), | |
| }, | |
| span, | |
| }) | |
| } | |
| /// Parses an experimental fn contract | |
| /// (`contract_requires(WWW) contract_ensures(ZZZ)`) | |
| pub(super) fn parse_contract(&mut self) -> PResult<'a, Option<Box<ast::FnContract>>> { | |
| let (declarations, requires) = self.parse_contract_requires()?; | |
| let ensures = self.parse_contract_ensures()?; | |
| if requires.is_none() && ensures.is_none() { | |
| Ok(None) | |
| } else { | |
| Ok(Some(Box::new(ast::FnContract { declarations, requires, ensures }))) | |
| } | |
| } | |
| fn parse_contract_requires( | |
| &mut self, | |
| ) -> PResult<'a, (ThinVec<rustc_ast::Stmt>, Option<Box<rustc_ast::Expr>>)> { | |
| Ok(if self.eat_keyword_noexpect(exp!(ContractRequires).kw) { | |
| self.psess.gated_spans.gate(sym::contracts_internals, self.prev_token.span); | |
| let mut decls_and_precond = self.parse_block()?; | |
| let precond = match decls_and_precond.stmts.pop() { | |
| Some(precond) => match precond.kind { | |
| rustc_ast::StmtKind::Expr(expr) => expr, | |
| // Insert dummy node that will be rejected by typechecker to | |
| // avoid reinventing an error | |
| _ => self.mk_unit_expr(decls_and_precond.span), | |
| }, | |
| None => self.mk_unit_expr(decls_and_precond.span), | |
| }; | |
| let precond = self.mk_closure_expr(precond.span, precond); | |
| let decls = decls_and_precond.stmts; | |
| (decls, Some(precond)) | |
| } else { | |
| (Default::default(), None) | |
| }) | |
| } | |
| fn parse_contract_ensures(&mut self) -> PResult<'a, Option<Box<rustc_ast::Expr>>> { | |
| Ok(if self.eat_keyword_noexpect(exp!(ContractEnsures).kw) { | |
| self.psess.gated_spans.gate(sym::contracts_internals, self.prev_token.span); | |
| let postcond = self.parse_expr()?; | |
| Some(postcond) | |
| } else { | |
| None | |
| }) | |
| } | |
| /// Parses an optional where-clause. | |
| /// | |
| /// ```ignore (only-for-syntax-highlight) | |
| /// where T : Trait<U, V> + 'b, 'a : 'b | |
| /// ``` | |
| pub(super) fn parse_where_clause(&mut self) -> PResult<'a, WhereClause> { | |
| self.parse_where_clause_common(None).map(|(clause, _)| clause) | |
| } | |
| pub(super) fn parse_struct_where_clause( | |
| &mut self, | |
| struct_name: Ident, | |
| body_insertion_point: Span, | |
| ) -> PResult<'a, (WhereClause, Option<ThinVec<ast::FieldDef>>)> { | |
| self.parse_where_clause_common(Some((struct_name, body_insertion_point))) | |
| } | |
| fn parse_where_clause_common( | |
| &mut self, | |
| struct_: Option<(Ident, Span)>, | |
| ) -> PResult<'a, (WhereClause, Option<ThinVec<ast::FieldDef>>)> { | |
| let mut where_clause = WhereClause { | |
| has_where_token: false, | |
| predicates: ThinVec::new(), | |
| span: self.prev_token.span.shrink_to_hi(), | |
| }; | |
| let mut tuple_struct_body = None; | |
| if !self.eat_keyword(exp!(Where)) { | |
| return Ok((where_clause, None)); | |
| } | |
| if self.eat_noexpect(&token::Colon) { | |
| let colon_span = self.prev_token.span; | |
| self.dcx() | |
| .struct_span_err(colon_span, "unexpected colon after `where`") | |
| .with_span_suggestion_short( | |
| colon_span, | |
| "remove the colon", | |
| "", | |
| Applicability::MachineApplicable, | |
| ) | |
| .emit(); | |
| } | |
| where_clause.has_where_token = true; | |
| let where_lo = self.prev_token.span; | |
| // We are considering adding generics to the `where` keyword as an alternative higher-rank | |
| // parameter syntax (as in `where<'a>` or `where<T>`. To avoid that being a breaking | |
| // change we parse those generics now, but report an error. | |
| if self.choose_generics_over_qpath(0) { | |
| let generics = self.parse_generics()?; | |
| self.dcx().emit_err(diagnostics::WhereOnGenerics { span: generics.span }); | |
| } | |
| loop { | |
| let where_sp = where_lo.to(self.prev_token.span); | |
| let attrs = self.parse_outer_attributes()?; | |
| let pred_lo = self.token.span; | |
| let predicate = self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| { | |
| for attr in &attrs { | |
| self.psess.gated_spans.gate(sym::where_clause_attrs, attr.span); | |
| } | |
| let kind = if this.check_lifetime() && this.look_ahead(1, |t| !t.is_like_plus()) { | |
| let lifetime = this.expect_lifetime(); | |
| // Bounds starting with a colon are mandatory, but possibly empty. | |
| this.expect(exp!(Colon))?; | |
| let bounds = this.parse_lt_param_bounds(); | |
| Some(ast::WherePredicateKind::RegionPredicate(ast::WhereRegionPredicate { | |
| lifetime, | |
| bounds, | |
| })) | |
| } else if this.check_type() { | |
| match this.parse_ty_where_predicate_kind_or_recover_tuple_struct_body( | |
| struct_, pred_lo, where_sp, | |
| )? { | |
| PredicateKindOrStructBody::PredicateKind(kind) => Some(kind), | |
| PredicateKindOrStructBody::StructBody(body) => { | |
| tuple_struct_body = Some(body); | |
| None | |
| } | |
| } | |
| } else { | |
| if let [.., last] = &attrs[..] { | |
| if last.is_doc_comment() { | |
| this.dcx().emit_err(diagnostics::DocCommentDoesNotDocumentAnything { | |
| span: last.span, | |
| missing_comma: None, | |
| }); | |
| } else { | |
| this.dcx().emit_err(diagnostics::AttrWithoutWherePredicates { | |
| span: last.span, | |
| }); | |
| } | |
| } | |
| None | |
| }; | |
| let predicate = kind.map(|kind| ast::WherePredicate { | |
| attrs, | |
| kind, | |
| id: DUMMY_NODE_ID, | |
| span: pred_lo.to(this.prev_token.span), | |
| is_placeholder: false, | |
| }); | |
| Ok((predicate, Trailing::No, UsePreAttrPos::No)) | |
| })?; | |
| match predicate { | |
| Some(predicate) => where_clause.predicates.push(predicate), | |
| None => break, | |
| } | |
| let prev_token = self.prev_token.span; | |
| let ate_comma = self.eat(exp!(Comma)); | |
| if self.eat_keyword_noexpect(kw::Where) { | |
| self.dcx().emit_err(MultipleWhereClauses { | |
| span: self.token.span, | |
| previous: pred_lo, | |
| between: prev_token.shrink_to_hi().to(self.prev_token.span), | |
| }); | |
| } else if !ate_comma { | |
| break; | |
| } | |
| } | |
| where_clause.span = where_lo.to(self.prev_token.span); | |
| Ok((where_clause, tuple_struct_body)) | |
| } | |
| fn parse_ty_where_predicate_kind_or_recover_tuple_struct_body( | |
| &mut self, | |
| struct_: Option<(Ident, Span)>, | |
| pred_lo: Span, | |
| where_sp: Span, | |
| ) -> PResult<'a, PredicateKindOrStructBody> { | |
| let mut snapshot = None; | |
| if let Some(struct_) = struct_ | |
| && self.may_recover() | |
| && self.token == token::OpenParen | |
| { | |
| snapshot = Some((struct_, self.create_snapshot_for_diagnostic())); | |
| }; | |
| match self.parse_ty_where_predicate_kind() { | |
| Ok(pred) => Ok(PredicateKindOrStructBody::PredicateKind( | |
| ast::WherePredicateKind::BoundPredicate(pred), | |
| )), | |
| Err(type_err) => { | |
| let Some(((struct_name, body_insertion_point), mut snapshot)) = snapshot else { | |
| return Err(type_err); | |
| }; | |
| // Check if we might have encountered an out of place tuple struct body. | |
| match snapshot.parse_tuple_struct_body() { | |
| // Since we don't know the exact reason why we failed to parse the | |
| // predicate (we might have stumbled upon something bogus like `(T): ?`), | |
| // employ a simple heuristic to weed out some pathological cases: | |
| // Look for a semicolon (strong indicator) or anything that might mark | |
| // the end of the item (weak indicator) following the body. | |
| Ok(body) | |
| if matches!(snapshot.token.kind, token::Semi | token::Eof) | |
| || snapshot.token.can_begin_item() => | |
| { | |
| type_err.cancel(); | |
| let body_sp = pred_lo.to(snapshot.prev_token.span); | |
| let map = self.psess.source_map(); | |
| self.dcx().emit_err(WhereClauseBeforeTupleStructBody { | |
| span: where_sp, | |
| name: struct_name.span, | |
| body: body_sp, | |
| sugg: map.span_to_snippet(body_sp).ok().map(|body| { | |
| WhereClauseBeforeTupleStructBodySugg { | |
| left: body_insertion_point.shrink_to_hi(), | |
| snippet: body, | |
| right: map.end_point(where_sp).to(body_sp), | |
| } | |
| }), | |
| }); | |
| self.restore_snapshot(snapshot); | |
| Ok(PredicateKindOrStructBody::StructBody(body)) | |
| } | |
| Ok(_) => Err(type_err), | |
| Err(body_err) => { | |
| body_err.cancel(); | |
| Err(type_err) | |
| } | |
| } | |
| } | |
| } | |
| } | |
| pub(crate) fn parse_ty_where_predicate_kind( | |
| &mut self, | |
| ) -> PResult<'a, ast::WhereBoundPredicate> { | |
| // Parse optional `for<'a, 'b>`. | |
| // This `for` is parsed greedily and applies to the whole predicate, | |
| // the bounded type can have its own `for` applying only to it. | |
| // Examples: | |
| // * `for<'a> Trait1<'a>: Trait2<'a /* ok */>` | |
| // * `(for<'a> Trait1<'a>): Trait2<'a /* not ok */>` | |
| // * `for<'a> for<'b> Trait1<'a, 'b>: Trait2<'a /* ok */, 'b /* not ok */>` | |
| let (bound_vars, _) = self.parse_higher_ranked_binder()?; | |
| let ty = self.parse_ty_for_where_clause()?; | |
| if self.eat(exp!(Colon)) { | |
| // The bounds may be empty; we intentionally accept predicates like `Ty:`. | |
| let bounds = self.parse_generic_bounds()?; | |
| return Ok(ast::WhereBoundPredicate { | |
| bound_generic_params: bound_vars, | |
| bounded_ty: ty, | |
| bounds, | |
| }); | |
| } | |
| // NOTE: If we ever end up impl'ing and stabilizing equality predicates (#20041), | |
| // we need to pick between `=` and `==`, both is not an option! | |
| if self.eat(exp!(Eq)) || self.eat(exp!(EqEq)) { | |
| let lhs_ty = ty; | |
| let rhs_ty = self.parse_ty()?; | |
| // NOTE: If we ever end up impl'ing equality predicates, | |
| // we ought to track the binder in the AST node! | |
| let _ = bound_vars; | |
| let mut diag = self.dcx().struct_span_err( | |
| lhs_ty.span.to(rhs_ty.span), | |
| "general type equality constraints are not supported", | |
| ); | |
| diag.note( | |
| "see issue #20041 <https://github.com/rust-lang/rust/issues/20041> \ | |
| for more information", | |
| ); | |
| diag.span(lhs_ty.span.to(rhs_ty.span)); | |
| diag.span_label(lhs_ty.span.to(rhs_ty.span), "not supported"); | |
| suggest_replacing_equality_pred_with_assoc_item_constraint(&mut diag, *lhs_ty, *rhs_ty); | |
| return Err(diag); | |
| } | |
| self.maybe_recover_bounds_doubled_colon(&ty)?; | |
| self.unexpected_any() | |
| } | |
| pub(super) fn choose_generics_over_qpath(&self, start: usize) -> bool { | |
| // There's an ambiguity between generic parameters and qualified paths in impls. | |
| // If we see `<` it may start both, so we have to inspect some following tokens. | |
| // The following combinations can only start generics, | |
| // but not qualified paths (with one exception): | |
| // `<` `>` - empty generic parameters | |
| // `<` `#` - generic parameters with attributes | |
| // `<` (LIFETIME|IDENT) `>` - single generic parameter | |
| // `<` (LIFETIME|IDENT) `,` - first generic parameter in a list | |
| // `<` (LIFETIME|IDENT) `:` - generic parameter with bounds | |
| // `<` (LIFETIME|IDENT) `=` - generic parameter with a default | |
| // `<` const - generic const parameter | |
| // `<` IDENT `?` - RECOVERY for `impl<T ?Bound` missing a `:`, meant to | |
| // avoid the `T?` to `Option<T>` recovery for types. | |
| // The only truly ambiguous case is | |
| // `<` IDENT `>` `::` IDENT ... | |
| // we disambiguate it in favor of generics (`impl<T> ::absolute::Path<T> { ... }`) | |
| // because this is what almost always expected in practice, qualified paths in impls | |
| // (`impl <Type>::AssocTy { ... }`) aren't even allowed by type checker at the moment. | |
| self.look_ahead(start, |t| t == &token::Lt) | |
| && (self.look_ahead(start + 1, |t| t == &token::Pound || t == &token::Gt) | |
| || self.look_ahead(start + 1, |t| t.is_lifetime() || t.is_ident()) | |
| && self.look_ahead(start + 2, |t| { | |
| matches!(t.kind, token::Gt | token::Comma | token::Colon | token::Eq) | |
| // Recovery-only branch -- this could be removed, | |
| // since it only affects diagnostics currently. | |
| || t.kind == token::Question | |
| }) | |
| || self.is_keyword_ahead(start + 1, &[kw::Const])) | |
| } | |
| } | |
| fn suggest_replacing_equality_pred_with_assoc_item_constraint( | |
| diag: &mut Diag<'_>, | |
| lhs_ty: ast::Ty, | |
| rhs_ty: ast::Ty, | |
| ) { | |
| let TyKind::Path(qself, ast::Path { segments, .. }) = lhs_ty.kind else { return }; | |
| let mut parts = Vec::new(); | |
| let applicability = match qself { | |
| // We have something like `Ty::Item<i32> = Rhs`. | |
| None if let [self_ty_seg, assoc_item_seg] = &segments[..] | |
| && self_ty_seg.ident.name != kw::PathRoot => | |
| { | |
| parts.push(( | |
| self_ty_seg.span().between(assoc_item_seg.span()), | |
| ": /* Trait */</* ... */".into(), | |
| )); | |
| Applicability::HasPlaceholders | |
| } | |
| Some(qself) if let [assoc_item_seg] = &segments[qself.position..] => { | |
| parts.push((lhs_ty.span.until(qself.ty.span), String::new())); | |
| // We have something like `<Option<usize> as self::Trait<i32>>::Item = Rhs`. | |
| if let trait_segs @ [.., final_trait_seg] = &segments[..qself.position] { | |
| parts.push((qself.ty.span.between(trait_segs[0].span()), ": ".into())); | |
| let (span, snippet) = match &final_trait_seg.args { | |
| Some(args) => { | |
| let ast::GenericArgs::AngleBracketed(args) = args else { return }; | |
| let Some(args) = args.args.last() else { return }; | |
| (args.span(), ", ") | |
| } | |
| None => (final_trait_seg.span(), "<"), | |
| }; | |
| parts.push((span.between(assoc_item_seg.span()), snippet.into())); | |
| Applicability::MaybeIncorrect | |
| } | |
| // We have something like `<[u8]>::Item == Rhs`. | |
| else { | |
| parts.push(( | |
| qself.ty.span.between(assoc_item_seg.span()), | |
| ": /* Trait */</* ... */".into(), | |
| )); | |
| Applicability::HasPlaceholders | |
| } | |
| } | |
| _ => return, | |
| }; | |
| parts.push((lhs_ty.span.between(rhs_ty.span), " = ".into())); | |
| parts.push((rhs_ty.span.shrink_to_hi(), ">".into())); | |
| diag.multipart_suggestion( | |
| "replace it with an associated item constraint if possible", | |
| parts, | |
| applicability, | |
| ); | |
| } |