| use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; | |
| use rustc_data_structures::sorted_map::SortedMap; | |
| use rustc_data_structures::unord::UnordMap; | |
| use rustc_errors::codes::*; | |
| use rustc_errors::{ | |
| Applicability, Diag, ErrorGuaranteed, MultiSpan, SuggestionStyle, listify, msg, pluralize, | |
| struct_span_code_err, | |
| }; | |
| use rustc_hir::def::{CtorOf, DefKind, Res}; | |
| use rustc_hir::def_id::DefId; | |
| use rustc_hir::{self as hir, HirId}; | |
| use rustc_middle::bug; | |
| use rustc_middle::ty::fast_reject::{TreatParams, simplify_type}; | |
| use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _}; | |
| use rustc_middle::ty::{ | |
| self, AdtDef, GenericParamDefKind, Ty, TyCtxt, TypeVisitableExt, | |
| suggest_constraining_type_param, | |
| }; | |
| use rustc_session::errors::feature_err; | |
| use rustc_span::edit_distance::find_best_match_for_name; | |
| use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol, kw, sym}; | |
| use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility; | |
| use rustc_trait_selection::traits::{ | |
| FulfillmentError, dyn_compatibility_violations_for_assoc_item, | |
| }; | |
| use smallvec::SmallVec; | |
| use tracing::debug; | |
| use super::InherentAssocCandidate; | |
| use crate::diagnostics::{ | |
| self, AssocItemConstraintsNotAllowedHere, ManualImplementation, ParenthesizedFnTraitExpansion, | |
| TraitObjectDeclaredWithNoTraits, | |
| }; | |
| use crate::hir_ty_lowering::{AssocItemQSelf, HirTyLowerer}; | |
| impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { | |
| pub(crate) fn report_missing_generic_params( | |
| &self, | |
| missing_generic_params: Vec<(Symbol, ty::GenericParamDefKind)>, | |
| def_id: DefId, | |
| span: Span, | |
| empty_generic_args: bool, | |
| ) { | |
| if missing_generic_params.is_empty() { | |
| return; | |
| } | |
| self.dcx().emit_err(diagnostics::MissingGenericParams { | |
| span, | |
| def_span: self.tcx().def_span(def_id), | |
| span_snippet: self.tcx().sess.source_map().span_to_snippet(span).ok(), | |
| missing_generic_params, | |
| empty_generic_args, | |
| }); | |
| } | |
| /// When the code is using the `Fn` traits directly, instead of the `Fn(A) -> B` syntax, emit | |
| /// an error and attempt to build a reasonable structured suggestion. | |
| pub(crate) fn report_internal_fn_trait( | |
| &self, | |
| span: Span, | |
| trait_def_id: DefId, | |
| trait_segment: &'_ hir::PathSegment<'_>, | |
| is_impl: bool, | |
| ) { | |
| if self.tcx().features().unboxed_closures() { | |
| return; | |
| } | |
| let trait_def = self.tcx().trait_def(trait_def_id); | |
| if !trait_def.paren_sugar { | |
| if trait_segment.args().parenthesized == hir::GenericArgsParentheses::ParenSugar { | |
| // For now, require that parenthetical notation be used only with `Fn()` etc. | |
| feature_err( | |
| &self.tcx().sess, | |
| sym::unboxed_closures, | |
| span, | |
| "parenthetical notation is only stable when used with `Fn`-family traits", | |
| ) | |
| .emit(); | |
| } | |
| return; | |
| } | |
| let sess = self.tcx().sess; | |
| if trait_segment.args().parenthesized != hir::GenericArgsParentheses::ParenSugar { | |
| // For now, require that parenthetical notation be used only with `Fn()` etc. | |
| let mut err = feature_err( | |
| sess, | |
| sym::unboxed_closures, | |
| span, | |
| "the precise format of `Fn`-family traits' type parameters is subject to change", | |
| ); | |
| // Do not suggest the other syntax if we are in trait impl: | |
| // the desugaring would contain an associated type constraint. | |
| if !is_impl { | |
| err.span_suggestion( | |
| span, | |
| "use parenthetical notation instead", | |
| fn_trait_to_string(self.tcx(), trait_segment, true), | |
| Applicability::MaybeIncorrect, | |
| ); | |
| } | |
| err.emit(); | |
| } | |
| if is_impl { | |
| let trait_name = self.tcx().def_path_str(trait_def_id); | |
| self.dcx().emit_err(ManualImplementation { span, trait_name }); | |
| } | |
| } | |
| pub(super) fn report_unresolved_assoc_item<I>( | |
| &self, | |
| all_candidates: impl Fn() -> I, | |
| qself: AssocItemQSelf, | |
| assoc_tag: ty::AssocTag, | |
| assoc_ident: Ident, | |
| span: Span, | |
| constraint: Option<&hir::AssocItemConstraint<'tcx>>, | |
| ) -> ErrorGuaranteed | |
| where | |
| I: Iterator<Item = ty::PolyTraitRef<'tcx>>, | |
| { | |
| let tcx = self.tcx(); | |
| // First and foremost, provide a more user-friendly & “intuitive” error on kind mismatches. | |
| if let Some(assoc_item) = all_candidates().find_map(|r| { | |
| tcx.associated_items(r.def_id()) | |
| .filter_by_name_unhygienic(assoc_ident.name) | |
| .find(|item| tcx.hygienic_eq(assoc_ident, item.ident(tcx), r.def_id())) | |
| }) { | |
| return self.report_assoc_kind_mismatch( | |
| assoc_item, | |
| assoc_tag, | |
| assoc_ident, | |
| span, | |
| constraint, | |
| ); | |
| } | |
| let assoc_kind = assoc_tag_str(assoc_tag); | |
| let qself_str = qself.to_string(tcx); | |
| // The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a | |
| // valid span, so we point at the whole path segment instead. | |
| let is_dummy = assoc_ident.span == DUMMY_SP; | |
| let mut err = diagnostics::AssocItemNotFound { | |
| span: if is_dummy { span } else { assoc_ident.span }, | |
| assoc_ident, | |
| assoc_kind, | |
| qself: &qself_str, | |
| label: None, | |
| sugg: None, | |
| // Try to get the span of the identifier within the path's syntax context | |
| // (if that's different). | |
| within_macro_span: assoc_ident.span.within_macro(span, tcx.sess.source_map()), | |
| }; | |
| if is_dummy { | |
| err.label = Some(diagnostics::AssocItemNotFoundLabel::NotFound { | |
| span, | |
| assoc_ident, | |
| assoc_kind, | |
| }); | |
| return self.dcx().emit_err(err); | |
| } | |
| let all_candidate_names: Vec<_> = all_candidates() | |
| .flat_map(|r| tcx.associated_items(r.def_id()).in_definition_order()) | |
| .filter_map(|item| { | |
| if !item.is_impl_trait_in_trait() && item.tag() == assoc_tag { | |
| item.opt_name() | |
| } else { | |
| None | |
| } | |
| }) | |
| .collect(); | |
| if let Some(suggested_name) = | |
| find_best_match_for_name(&all_candidate_names, assoc_ident.name, None) | |
| { | |
| err.sugg = Some(diagnostics::AssocItemNotFoundSugg::Similar { | |
| span: assoc_ident.span, | |
| assoc_kind, | |
| suggested_name, | |
| }); | |
| return self.dcx().emit_err(err); | |
| } | |
| // If we didn't find a good item in the supertraits (or couldn't get | |
| // the supertraits), like in ItemCtxt, then look more generally from | |
| // all visible traits. If there's one clear winner, just suggest that. | |
| let visible_traits: Vec<_> = tcx | |
| .visible_traits() | |
| .filter(|trait_def_id| { | |
| let viz = tcx.visibility(*trait_def_id); | |
| let def_id = self.item_def_id(); | |
| viz.is_accessible_from(def_id, tcx) | |
| }) | |
| .collect(); | |
| let wider_candidate_names: Vec<_> = visible_traits | |
| .iter() | |
| .flat_map(|trait_def_id| tcx.associated_items(*trait_def_id).in_definition_order()) | |
| .filter_map(|item| { | |
| (!item.is_impl_trait_in_trait() && item.tag() == assoc_tag).then(|| item.name()) | |
| }) | |
| .collect(); | |
| if let Some(suggested_name) = | |
| find_best_match_for_name(&wider_candidate_names, assoc_ident.name, None) | |
| { | |
| if let [best_trait] = visible_traits | |
| .iter() | |
| .copied() | |
| .filter(|&trait_def_id| { | |
| tcx.associated_items(trait_def_id) | |
| .filter_by_name_unhygienic(suggested_name) | |
| .any(|item| item.tag() == assoc_tag) | |
| }) | |
| .collect::<Vec<_>>()[..] | |
| { | |
| let trait_name = tcx.def_path_str(best_trait); | |
| err.label = Some(diagnostics::AssocItemNotFoundLabel::FoundInOtherTrait { | |
| span: assoc_ident.span, | |
| assoc_kind, | |
| trait_name: &trait_name, | |
| suggested_name, | |
| identically_named: suggested_name == assoc_ident.name, | |
| }); | |
| if let AssocItemQSelf::TyParam(ty_param_def_id, ty_param_span) = qself | |
| // Not using `self.item_def_id()` here as that would yield the opaque type itself if we're | |
| // inside an opaque type while we're interested in the overarching type alias (TAIT). | |
| // FIXME: However, for trait aliases, this incorrectly returns the enclosing module... | |
| && let item_def_id = | |
| tcx.hir_get_parent_item(tcx.local_def_id_to_hir_id(ty_param_def_id)) | |
| // FIXME: ...which obviously won't have any generics. | |
| && let Some(generics) = tcx.hir_get_generics(item_def_id.def_id) | |
| { | |
| // FIXME: Suggest adding supertrait bounds if we have a `Self` type param. | |
| // FIXME(trait_alias): Suggest adding `Self: Trait` to | |
| // `trait Alias = where Self::Proj:;` with `trait Trait { type Proj; }`. | |
| if generics | |
| .bounds_for_param(ty_param_def_id) | |
| .flat_map(|pred| pred.bounds.iter()) | |
| .any(|b| match b { | |
| hir::GenericBound::Trait(t, ..) => { | |
| t.trait_ref.trait_def_id() == Some(best_trait) | |
| } | |
| _ => false, | |
| }) | |
| { | |
| // The type param already has a bound for `trait_name`, we just need to | |
| // change the associated item. | |
| err.sugg = Some(diagnostics::AssocItemNotFoundSugg::SimilarInOtherTrait { | |
| span: assoc_ident.span, | |
| trait_name: &trait_name, | |
| assoc_kind, | |
| suggested_name, | |
| }); | |
| return self.dcx().emit_err(err); | |
| } | |
| let trait_args = &ty::GenericArgs::identity_for_item(tcx, best_trait)[1..]; | |
| let mut trait_ref = trait_name.clone(); | |
| let applicability = if let [arg, args @ ..] = trait_args { | |
| use std::fmt::Write; | |
| write!(trait_ref, "</* {arg}").unwrap(); | |
| args.iter().try_for_each(|arg| write!(trait_ref, ", {arg}")).unwrap(); | |
| trait_ref += " */>"; | |
| Applicability::HasPlaceholders | |
| } else { | |
| Applicability::MaybeIncorrect | |
| }; | |
| let identically_named = suggested_name == assoc_ident.name; | |
| if let DefKind::TyAlias = tcx.def_kind(item_def_id) | |
| && !tcx.type_alias_is_lazy(item_def_id) | |
| { | |
| err.sugg = | |
| Some(diagnostics::AssocItemNotFoundSugg::SimilarInOtherTraitQPath { | |
| lo: ty_param_span.shrink_to_lo(), | |
| mi: ty_param_span.shrink_to_hi(), | |
| hi: (!identically_named).then_some(assoc_ident.span), | |
| trait_ref, | |
| identically_named, | |
| suggested_name, | |
| assoc_kind, | |
| applicability, | |
| }); | |
| } else { | |
| let mut err = self.dcx().create_err(err); | |
| if suggest_constraining_type_param( | |
| tcx, | |
| generics, | |
| &mut err, | |
| &qself_str, | |
| &trait_ref, | |
| Some(best_trait), | |
| None, | |
| ) && !identically_named | |
| { | |
| // We suggested constraining a type parameter, but the associated item on it | |
| // was also not an exact match, so we also suggest changing it. | |
| err.span_suggestion_verbose( | |
| assoc_ident.span, | |
| msg!("...and changing the associated {$assoc_kind} name"), | |
| suggested_name, | |
| Applicability::MaybeIncorrect, | |
| ); | |
| } | |
| return err.emit(); | |
| } | |
| } | |
| return self.dcx().emit_err(err); | |
| } | |
| } | |
| // If we still couldn't find any associated item, and only one associated item exists, | |
| // suggest using it. | |
| if let [candidate_name] = all_candidate_names.as_slice() { | |
| err.sugg = Some(diagnostics::AssocItemNotFoundSugg::Other { | |
| span: assoc_ident.span, | |
| qself: &qself_str, | |
| assoc_kind, | |
| suggested_name: *candidate_name, | |
| }); | |
| } else { | |
| err.label = Some(diagnostics::AssocItemNotFoundLabel::NotFound { | |
| span: assoc_ident.span, | |
| assoc_ident, | |
| assoc_kind, | |
| }); | |
| } | |
| self.dcx().emit_err(err) | |
| } | |
| fn report_assoc_kind_mismatch( | |
| &self, | |
| assoc_item: &ty::AssocItem, | |
| assoc_tag: ty::AssocTag, | |
| ident: Ident, | |
| span: Span, | |
| constraint: Option<&hir::AssocItemConstraint<'tcx>>, | |
| ) -> ErrorGuaranteed { | |
| let tcx = self.tcx(); | |
| let bound_on_assoc_const_label = if let ty::AssocKind::Const { .. } = assoc_item.kind | |
| && let Some(constraint) = constraint | |
| && let hir::AssocItemConstraintKind::Bound { .. } = constraint.kind | |
| { | |
| let lo = if constraint.gen_args.span_ext.is_dummy() { | |
| ident.span | |
| } else { | |
| constraint.gen_args.span_ext | |
| }; | |
| Some(lo.between(span.shrink_to_hi())) | |
| } else { | |
| None | |
| }; | |
| // FIXME(mgca): This has quite a few false positives and negatives. | |
| let wrap_in_braces_sugg = if let Some(constraint) = constraint | |
| && let Some(hir_ty) = constraint.ty() | |
| && let ty = self.lower_ty(hir_ty) | |
| && (ty.is_enum() || ty.references_error()) | |
| && tcx.features().min_generic_const_args() | |
| { | |
| Some(diagnostics::AssocKindMismatchWrapInBracesSugg { | |
| lo: hir_ty.span.shrink_to_lo(), | |
| hi: hir_ty.span.shrink_to_hi(), | |
| }) | |
| } else { | |
| None | |
| }; | |
| // For equality constraints, we want to blame the term (RHS) instead of the item (LHS) since | |
| // one can argue that that's more “intuitive” to the user. | |
| let (span, expected_because_label, expected, got) = if let Some(constraint) = constraint | |
| && let hir::AssocItemConstraintKind::Equality { term } = constraint.kind | |
| { | |
| let span = match term { | |
| hir::Term::Ty(ty) => ty.span, | |
| hir::Term::Const(ct) => ct.span, | |
| }; | |
| (span, Some(ident.span), assoc_item.tag(), assoc_tag) | |
| } else { | |
| (ident.span, None, assoc_tag, assoc_item.tag()) | |
| }; | |
| self.dcx().emit_err(diagnostics::AssocKindMismatch { | |
| span, | |
| expected: assoc_tag_str(expected), | |
| got: assoc_tag_str(got), | |
| expected_because_label, | |
| assoc_kind: assoc_tag_str(assoc_item.tag()), | |
| def_span: tcx.def_span(assoc_item.def_id), | |
| bound_on_assoc_const_label, | |
| wrap_in_braces_sugg, | |
| }) | |
| } | |
| pub(super) fn report_ambiguous_assoc_item( | |
| &self, | |
| matching_candidates: &[ty::PolyTraitRef<'tcx>], | |
| qself: AssocItemQSelf, | |
| assoc_tag: ty::AssocTag, | |
| assoc_ident: Ident, | |
| span: Span, | |
| constraint: Option<&hir::AssocItemConstraint<'tcx>>, | |
| ) -> ErrorGuaranteed { | |
| let tcx = self.tcx(); | |
| let assoc_kind_str = assoc_tag_str(assoc_tag); | |
| let qself_str = qself.to_string(tcx); | |
| let mut err = self.dcx().create_err(crate::diagnostics::AmbiguousAssocItem { | |
| span, | |
| assoc_kind: assoc_kind_str, | |
| assoc_ident, | |
| qself: &qself_str, | |
| }); | |
| // Provide a more specific error code index entry for equality bindings. | |
| err.code( | |
| if let Some(constraint) = constraint | |
| && let hir::AssocItemConstraintKind::Equality { .. } = constraint.kind | |
| { | |
| E0222 | |
| } else { | |
| E0221 | |
| }, | |
| ); | |
| // FIXME(#97583): Print associated item bindings properly (i.e., not as equality | |
| // predicates!). | |
| // FIXME: Turn this into a structured, translatable & more actionable suggestion. | |
| let mut where_bounds = vec![]; | |
| for &bound in matching_candidates { | |
| let bound_id = bound.def_id(); | |
| let assoc_item = tcx.associated_items(bound_id).find_by_ident_and_kind( | |
| tcx, | |
| assoc_ident, | |
| assoc_tag, | |
| bound_id, | |
| ); | |
| let bound_span = assoc_item.and_then(|item| tcx.hir_span_if_local(item.def_id)); | |
| if let Some(bound_span) = bound_span { | |
| err.span_label( | |
| bound_span, | |
| format!("ambiguous `{assoc_ident}` from `{}`", bound.print_trait_sugared(),), | |
| ); | |
| if let Some(constraint) = constraint { | |
| match constraint.kind { | |
| hir::AssocItemConstraintKind::Equality { term } => { | |
| let term: ty::Term<'_> = match term { | |
| hir::Term::Ty(ty) => self.lower_ty(ty).into(), | |
| hir::Term::Const(ct) => { | |
| let assoc_item = | |
| assoc_item.expect("assoc_item should be present"); | |
| let projection_term = bound.map_bound(|trait_ref| { | |
| let item_segment = hir::PathSegment { | |
| ident: constraint.ident, | |
| hir_id: constraint.hir_id, | |
| res: Res::Err, | |
| args: Some(constraint.gen_args), | |
| infer_args: false, | |
| }; | |
| let alias_args = self.lower_generic_args_of_assoc_item( | |
| constraint.ident.span, | |
| assoc_item.def_id, | |
| &item_segment, | |
| trait_ref.args, | |
| ); | |
| ty::AliasTerm::new_from_def_id( | |
| tcx, | |
| assoc_item.def_id, | |
| alias_args, | |
| ) | |
| }); | |
| // FIXME(mgca): code duplication with other places we lower | |
| // the rhs' of associated const bindings | |
| let ty = projection_term.map_bound(|alias| { | |
| alias.expect_ct().type_of(tcx).skip_norm_wip() | |
| }); | |
| let ty = super::bounds::check_assoc_const_binding_type( | |
| self, | |
| constraint.ident, | |
| ty, | |
| constraint.hir_id, | |
| ); | |
| self.lower_const_arg(ct, ty).into() | |
| } | |
| }; | |
| if term.references_error() { | |
| continue; | |
| } | |
| // FIXME(#97583): This isn't syntactically well-formed! | |
| where_bounds.push(format!( | |
| " T: {trait}::{assoc_ident} = {term}", | |
| trait = bound.print_only_trait_path(), | |
| )); | |
| } | |
| // FIXME: Provide a suggestion. | |
| hir::AssocItemConstraintKind::Bound { bounds: _ } => {} | |
| } | |
| } else { | |
| err.span_suggestion_verbose( | |
| span.with_hi(assoc_ident.span.lo()), | |
| "use fully-qualified syntax to disambiguate", | |
| format!("<{qself_str} as {}>::", bound.print_only_trait_path()), | |
| Applicability::MaybeIncorrect, | |
| ); | |
| } | |
| } else { | |
| let trait_ = tcx.short_string(bound.print_only_trait_path(), err.long_ty_path()); | |
| err.note(format!( | |
| "associated {assoc_kind_str} `{assoc_ident}` could derive from `{trait_}`", | |
| )); | |
| } | |
| } | |
| if !where_bounds.is_empty() { | |
| err.help(format!( | |
| "consider introducing a new type parameter `T` and adding `where` constraints:\ | |
| \n where\n T: {qself_str},\n{}", | |
| where_bounds.join(",\n"), | |
| )); | |
| } | |
| err.emit() | |
| } | |
| pub(crate) fn report_missing_self_ty_for_resolved_path( | |
| &self, | |
| trait_def_id: DefId, | |
| span: Span, | |
| item_segment: &hir::PathSegment<'tcx>, | |
| assoc_tag: ty::AssocTag, | |
| ) -> ErrorGuaranteed { | |
| let tcx = self.tcx(); | |
| let path_str = tcx.def_path_str(trait_def_id); | |
| let def_id = self.item_def_id(); | |
| debug!(item_def_id = ?def_id); | |
| // FIXME: document why/how this is different from `tcx.local_parent(def_id)` | |
| let parent_def_id = tcx.hir_get_parent_item(tcx.local_def_id_to_hir_id(def_id)).to_def_id(); | |
| debug!(?parent_def_id); | |
| // If the trait in segment is the same as the trait defining the item, | |
| // use the `<Self as ..>` syntax in the error. | |
| let is_part_of_self_trait_constraints = def_id.to_def_id() == trait_def_id; | |
| let is_part_of_fn_in_self_trait = parent_def_id == trait_def_id; | |
| let type_names = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait { | |
| vec!["Self".to_string()] | |
| } else { | |
| // Find all the types that have an `impl` for the trait. | |
| tcx.all_impls(trait_def_id) | |
| .map(|impl_def_id| tcx.impl_trait_header(impl_def_id)) | |
| .filter(|header| { | |
| // Consider only accessible traits | |
| tcx.visibility(trait_def_id).is_accessible_from(self.item_def_id(), tcx) | |
| && header.polarity != ty::ImplPolarity::Negative | |
| }) | |
| .map(|header| header.trait_ref.instantiate_identity().skip_norm_wip().self_ty()) | |
| // We don't care about blanket impls. | |
| .filter(|self_ty| !self_ty.has_non_region_param()) | |
| .map(|self_ty| tcx.erase_and_anonymize_regions(self_ty).to_string()) | |
| .collect() | |
| }; | |
| // FIXME: also look at `tcx.generics_of(self.item_def_id()).params` any that | |
| // references the trait. Relevant for the first case in | |
| // `src/test/ui/associated-types/associated-types-in-ambiguous-context.rs` | |
| self.report_ambiguous_assoc_item_path( | |
| span, | |
| &type_names, | |
| &[path_str], | |
| item_segment.ident, | |
| assoc_tag, | |
| ) | |
| } | |
| pub(super) fn report_unresolved_type_relative_path( | |
| &self, | |
| self_ty: Ty<'tcx>, | |
| hir_self_ty: &hir::Ty<'_>, | |
| assoc_tag: ty::AssocTag, | |
| ident: Ident, | |
| qpath_hir_id: HirId, | |
| span: Span, | |
| variant_def_id: Option<DefId>, | |
| ) -> ErrorGuaranteed { | |
| let tcx = self.tcx(); | |
| let kind_str = assoc_tag_str(assoc_tag); | |
| if variant_def_id.is_some() { | |
| // Variant in type position | |
| let msg = format!("expected {kind_str}, found variant `{ident}`"); | |
| self.dcx().span_err(span, msg) | |
| } else if self_ty.is_enum() { | |
| let mut err = self.dcx().create_err(diagnostics::NoVariantNamed { | |
| span: ident.span, | |
| ident, | |
| ty: self_ty, | |
| }); | |
| let adt_def = self_ty.ty_adt_def().expect("enum is not an ADT"); | |
| if let Some(variant_name) = find_best_match_for_name( | |
| &adt_def.variants().iter().map(|variant| variant.name).collect::<Vec<Symbol>>(), | |
| ident.name, | |
| None, | |
| ) && let Some(variant) = adt_def.variants().iter().find(|s| s.name == variant_name) | |
| { | |
| let mut suggestion = vec![(ident.span, variant_name.to_string())]; | |
| if let hir::Node::Stmt(&hir::Stmt { kind: hir::StmtKind::Semi(expr), .. }) | |
| | hir::Node::Expr(expr) = tcx.parent_hir_node(qpath_hir_id) | |
| && let hir::ExprKind::Struct(..) = expr.kind | |
| { | |
| match variant.ctor { | |
| None => { | |
| // struct | |
| suggestion = vec![( | |
| ident.span.with_hi(expr.span.hi()), | |
| if variant.fields.is_empty() { | |
| format!("{variant_name} {{}}") | |
| } else { | |
| format!( | |
| "{variant_name} {{ {} }}", | |
| variant | |
| .fields | |
| .iter() | |
| .map(|f| format!("{}: /* value */", f.name)) | |
| .collect::<Vec<_>>() | |
| .join(", ") | |
| ) | |
| }, | |
| )]; | |
| } | |
| Some((hir::def::CtorKind::Fn, def_id)) => { | |
| // tuple | |
| let fn_sig = tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip(); | |
| let inputs = fn_sig.inputs().skip_binder(); | |
| suggestion = vec![( | |
| ident.span.with_hi(expr.span.hi()), | |
| format!( | |
| "{variant_name}({})", | |
| inputs | |
| .iter() | |
| .map(|i| format!("/* {i} */")) | |
| .collect::<Vec<_>>() | |
| .join(", ") | |
| ), | |
| )]; | |
| } | |
| Some((hir::def::CtorKind::Const, _)) => { | |
| // unit | |
| suggestion = vec![( | |
| ident.span.with_hi(expr.span.hi()), | |
| variant_name.to_string(), | |
| )]; | |
| } | |
| } | |
| } | |
| err.multipart_suggestion( | |
| "there is a variant with a similar name", | |
| suggestion, | |
| Applicability::HasPlaceholders, | |
| ); | |
| } else { | |
| err.span_label(ident.span, format!("variant not found in `{self_ty}`")); | |
| } | |
| if let Some(sp) = tcx.hir_span_if_local(adt_def.did()) { | |
| err.span_label(sp, format!("variant `{ident}` not found here")); | |
| } | |
| err.emit() | |
| } else if let Err(reported) = self_ty.error_reported() { | |
| reported | |
| } else { | |
| match self.maybe_report_similar_assoc_fn(span, self_ty, hir_self_ty) { | |
| Ok(()) => {} | |
| Err(reported) => return reported, | |
| } | |
| let traits: Vec<_> = self.probe_traits_that_match_assoc_ty(self_ty, ident); | |
| self.report_ambiguous_assoc_item_path( | |
| span, | |
| &[self_ty.to_string()], | |
| &traits, | |
| ident, | |
| assoc_tag, | |
| ) | |
| } | |
| } | |
| fn report_ambiguous_assoc_item_path( | |
| &self, | |
| span: Span, | |
| types: &[String], | |
| traits: &[String], | |
| ident: Ident, | |
| assoc_tag: ty::AssocTag, | |
| ) -> ErrorGuaranteed { | |
| let kind_str = assoc_tag_str(assoc_tag); | |
| let mut err = | |
| struct_span_code_err!(self.dcx(), span, E0223, "ambiguous associated {kind_str}"); | |
| if self | |
| .tcx() | |
| .resolutions(()) | |
| .confused_type_with_std_module | |
| .keys() | |
| .any(|full_span| full_span.contains(span)) | |
| { | |
| err.span_suggestion_verbose( | |
| span.shrink_to_lo(), | |
| "you are looking for the module in `std`, not the primitive type", | |
| "std::", | |
| Applicability::MachineApplicable, | |
| ); | |
| } else { | |
| let sugg_sp = span.until(ident.span); | |
| let mut types = types.to_vec(); | |
| types.sort(); | |
| let mut traits = traits.to_vec(); | |
| traits.sort(); | |
| match (&types[..], &traits[..]) { | |
| ([], []) => { | |
| err.span_suggestion_verbose( | |
| sugg_sp, | |
| format!( | |
| "if there were a type named `Type` that implements a trait named \ | |
| `Trait` with associated {kind_str} `{ident}`, you could use the \ | |
| fully-qualified path", | |
| ), | |
| "<Type as Trait>::", | |
| Applicability::HasPlaceholders, | |
| ); | |
| } | |
| ([], [trait_str]) => { | |
| err.span_suggestion_verbose( | |
| sugg_sp, | |
| format!( | |
| "if there were a type named `Example` that implemented `{trait_str}`, \ | |
| you could use the fully-qualified path", | |
| ), | |
| format!("<Example as {trait_str}>::"), | |
| Applicability::HasPlaceholders, | |
| ); | |
| } | |
| ([], traits) => { | |
| err.span_suggestions_with_style( | |
| sugg_sp, | |
| format!( | |
| "if there were a type named `Example` that implemented one of the \ | |
| traits with associated {kind_str} `{ident}`, you could use the \ | |
| fully-qualified path", | |
| ), | |
| traits.iter().map(|trait_str| format!("<Example as {trait_str}>::")), | |
| Applicability::HasPlaceholders, | |
| SuggestionStyle::ShowAlways, | |
| ); | |
| } | |
| ([type_str], []) => { | |
| err.span_suggestion_verbose( | |
| sugg_sp, | |
| format!( | |
| "if there were a trait named `Example` with associated {kind_str} `{ident}` \ | |
| implemented for `{type_str}`, you could use the fully-qualified path", | |
| ), | |
| format!("<{type_str} as Example>::"), | |
| Applicability::HasPlaceholders, | |
| ); | |
| } | |
| (types, []) => { | |
| err.span_suggestions_with_style( | |
| sugg_sp, | |
| format!( | |
| "if there were a trait named `Example` with associated {kind_str} `{ident}` \ | |
| implemented for one of the types, you could use the fully-qualified \ | |
| path", | |
| ), | |
| types | |
| .into_iter() | |
| .map(|type_str| format!("<{type_str} as Example>::")), | |
| Applicability::HasPlaceholders, | |
| SuggestionStyle::ShowAlways, | |
| ); | |
| } | |
| (types, traits) => { | |
| let mut suggestions = vec![]; | |
| for type_str in types { | |
| for trait_str in traits { | |
| suggestions.push(format!("<{type_str} as {trait_str}>::")); | |
| } | |
| } | |
| err.span_suggestions_with_style( | |
| sugg_sp, | |
| "use fully-qualified syntax", | |
| suggestions, | |
| Applicability::MachineApplicable, | |
| SuggestionStyle::ShowAlways, | |
| ); | |
| } | |
| } | |
| } | |
| err.emit() | |
| } | |
| pub(crate) fn report_ambiguous_inherent_assoc_item( | |
| &self, | |
| name: Ident, | |
| candidates: Vec<DefId>, | |
| span: Span, | |
| ) -> ErrorGuaranteed { | |
| let mut err = struct_span_code_err!( | |
| self.dcx(), | |
| name.span, | |
| E0034, | |
| "multiple applicable items in scope" | |
| ); | |
| err.span_label(name.span, format!("multiple `{name}` found")); | |
| self.note_ambiguous_inherent_assoc_item(&mut err, candidates, span); | |
| err.emit() | |
| } | |
| // FIXME(fmease): Heavily adapted from `rustc_hir_typeck::method::suggest`. Deduplicate. | |
| fn note_ambiguous_inherent_assoc_item( | |
| &self, | |
| err: &mut Diag<'_>, | |
| candidates: Vec<DefId>, | |
| span: Span, | |
| ) { | |
| let tcx = self.tcx(); | |
| // Dynamic limit to avoid hiding just one candidate, which is silly. | |
| let limit = if candidates.len() == 5 { 5 } else { 4 }; | |
| for (index, &item) in candidates.iter().take(limit).enumerate() { | |
| let impl_ = tcx.parent(item); | |
| let note_span = if item.is_local() { | |
| Some(tcx.def_span(item)) | |
| } else if impl_.is_local() { | |
| Some(tcx.def_span(impl_)) | |
| } else { | |
| None | |
| }; | |
| let title = if candidates.len() > 1 { | |
| format!("candidate #{}", index + 1) | |
| } else { | |
| "the candidate".into() | |
| }; | |
| let impl_ty = tcx.at(span).type_of(impl_).instantiate_identity().skip_norm_wip(); | |
| let note = format!("{title} is defined in an impl for the type `{impl_ty}`"); | |
| if let Some(span) = note_span { | |
| err.span_note(span, note); | |
| } else { | |
| err.note(note); | |
| } | |
| } | |
| if candidates.len() > limit { | |
| err.note(format!("and {} others", candidates.len() - limit)); | |
| } | |
| } | |
| // FIXME(inherent_associated_types): Find similarly named associated types and suggest them. | |
| pub(crate) fn report_unresolved_inherent_assoc_item( | |
| &self, | |
| name: Ident, | |
| self_ty: Ty<'tcx>, | |
| candidates: Vec<InherentAssocCandidate>, | |
| fulfillment_errors: Vec<FulfillmentError<'tcx>>, | |
| span: Span, | |
| assoc_tag: ty::AssocTag, | |
| ) -> ErrorGuaranteed { | |
| // FIXME(fmease): This was copied in parts from an old version of `rustc_hir_typeck::method::suggest`. | |
| // Either | |
| // * update this code by applying changes similar to #106702 or by taking a | |
| // Vec<(DefId, (DefId, DefId), Option<Vec<FulfillmentError<'tcx>>>)> or | |
| // * deduplicate this code across the two crates. | |
| let tcx = self.tcx(); | |
| let assoc_tag_str = assoc_tag_str(assoc_tag); | |
| let adt_did = self_ty.ty_adt_def().map(|def| def.did()); | |
| let add_def_label = |err: &mut Diag<'_>| { | |
| if let Some(did) = adt_did { | |
| err.span_label( | |
| tcx.def_span(did), | |
| format!( | |
| "associated {assoc_tag_str} `{name}` not found for this {}", | |
| tcx.def_descr(did) | |
| ), | |
| ); | |
| } | |
| }; | |
| if fulfillment_errors.is_empty() { | |
| // FIXME(fmease): Copied from `rustc_hir_typeck::method::probe`. Deduplicate. | |
| let limit = if candidates.len() == 5 { 5 } else { 4 }; | |
| let type_candidates = candidates | |
| .iter() | |
| .take(limit) | |
| .map(|cand| { | |
| format!( | |
| "- `{}`", | |
| tcx.at(span).type_of(cand.impl_).instantiate_identity().skip_norm_wip() | |
| ) | |
| }) | |
| .collect::<Vec<_>>() | |
| .join("\n"); | |
| let additional_types = if candidates.len() > limit { | |
| format!("\nand {} more types", candidates.len() - limit) | |
| } else { | |
| String::new() | |
| }; | |
| let mut err = struct_span_code_err!( | |
| self.dcx(), | |
| name.span, | |
| E0220, | |
| "associated {assoc_tag_str} `{name}` not found for `{self_ty}` in the current scope" | |
| ); | |
| err.span_label(name.span, format!("associated item not found in `{self_ty}`")); | |
| err.note(format!( | |
| "the associated {assoc_tag_str} was found for\n{type_candidates}{additional_types}", | |
| )); | |
| add_def_label(&mut err); | |
| return err.emit(); | |
| } | |
| let mut bound_spans: SortedMap<Span, Vec<String>> = Default::default(); | |
| let mut bound_span_label = |self_ty: Ty<'_>, obligation: &str, quiet: &str| { | |
| let msg = format!("`{}`", if obligation.len() > 50 { quiet } else { obligation }); | |
| match self_ty.kind() { | |
| // Point at the type that couldn't satisfy the bound. | |
| ty::Adt(def, _) => { | |
| bound_spans.get_mut_or_insert_default(tcx.def_span(def.did())).push(msg) | |
| } | |
| // Point at the trait object that couldn't satisfy the bound. | |
| ty::Dynamic(preds, _) => { | |
| for pred in preds.iter() { | |
| match pred.skip_binder() { | |
| ty::ExistentialPredicate::Trait(tr) => { | |
| bound_spans | |
| .get_mut_or_insert_default(tcx.def_span(tr.def_id)) | |
| .push(msg.clone()); | |
| } | |
| ty::ExistentialPredicate::Projection(_) | |
| | ty::ExistentialPredicate::AutoTrait(_) => {} | |
| } | |
| } | |
| } | |
| // Point at the closure that couldn't satisfy the bound. | |
| ty::Closure(def_id, _) => { | |
| bound_spans | |
| .get_mut_or_insert_default(tcx.def_span(*def_id)) | |
| .push(format!("`{quiet}`")); | |
| } | |
| _ => {} | |
| } | |
| }; | |
| let format_pred = |pred: ty::Predicate<'tcx>| { | |
| let bound_predicate = pred.kind(); | |
| match bound_predicate.skip_binder() { | |
| ty::PredicateKind::Clause(ty::ClauseKind::Projection(pred)) => { | |
| // `<Foo as Iterator>::Item = String`. | |
| let projection_term = pred.projection_term; | |
| let quiet_projection_term = projection_term | |
| .with_replaced_self_ty(tcx, Ty::new_var(tcx, ty::TyVid::ZERO)); | |
| let term = pred.term; | |
| let obligation = format!("{projection_term} = {term}"); | |
| let quiet = format!("{quiet_projection_term} = {term}"); | |
| bound_span_label(projection_term.self_ty(), &obligation, &quiet); | |
| Some((obligation, projection_term.self_ty())) | |
| } | |
| ty::PredicateKind::Clause(ty::ClauseKind::Trait(poly_trait_ref)) => { | |
| let p = poly_trait_ref.trait_ref; | |
| let self_ty = p.self_ty(); | |
| let path = p.print_only_trait_path(); | |
| let obligation = format!("{self_ty}: {path}"); | |
| let quiet = format!("_: {path}"); | |
| bound_span_label(self_ty, &obligation, &quiet); | |
| Some((obligation, self_ty)) | |
| } | |
| _ => None, | |
| } | |
| }; | |
| // FIXME(fmease): `rustc_hir_typeck::method::suggest` uses a `skip_list` to filter out some bounds. | |
| // I would do the same here if it didn't mean more code duplication. | |
| let mut bounds: Vec<_> = fulfillment_errors | |
| .into_iter() | |
| .map(|error| error.root_obligation.predicate) | |
| .filter_map(format_pred) | |
| .map(|(p, _)| format!("`{p}`")) | |
| .collect(); | |
| bounds.sort(); | |
| bounds.dedup(); | |
| let mut err = self.dcx().struct_span_err( | |
| name.span, | |
| format!("the associated {assoc_tag_str} `{name}` exists for `{self_ty}`, but its trait bounds were not satisfied") | |
| ); | |
| if !bounds.is_empty() { | |
| err.note(format!( | |
| "the following trait bounds were not satisfied:\n{}", | |
| bounds.join("\n") | |
| )); | |
| } | |
| err.span_label( | |
| name.span, | |
| format!("associated {assoc_tag_str} cannot be referenced on `{self_ty}` due to unsatisfied trait bounds") | |
| ); | |
| for (span, mut bounds) in bound_spans { | |
| if !tcx.sess.source_map().is_span_accessible(span) { | |
| continue; | |
| } | |
| bounds.sort(); | |
| bounds.dedup(); | |
| let msg = match &bounds[..] { | |
| [bound] => format!("doesn't satisfy {bound}"), | |
| bounds if bounds.len() > 4 => format!("doesn't satisfy {} bounds", bounds.len()), | |
| [bounds @ .., last] => format!("doesn't satisfy {} or {last}", bounds.join(", ")), | |
| [] => unreachable!(), | |
| }; | |
| err.span_label(span, msg); | |
| } | |
| add_def_label(&mut err); | |
| err.emit() | |
| } | |
| /// If there are any missing associated items, emit an error instructing the user to provide | |
| /// them unless that's impossible due to shadowing. Moreover, if any corresponding trait refs | |
| /// are dyn incompatible due to associated items we emit an dyn incompatibility error instead. | |
| pub(crate) fn check_for_required_assoc_items( | |
| &self, | |
| spans: SmallVec<[Span; 1]>, | |
| missing_assoc_items: FxIndexSet<(DefId, ty::PolyTraitRef<'tcx>)>, | |
| potential_assoc_items: Vec<usize>, | |
| trait_bounds: &[hir::PolyTraitRef<'_>], | |
| ) -> Result<(), ErrorGuaranteed> { | |
| if missing_assoc_items.is_empty() { | |
| return Ok(()); | |
| } | |
| let tcx = self.tcx(); | |
| let principal_span = *spans.first().unwrap(); | |
| // FIXME: This logic needs some more care w.r.t handling of conflicts | |
| let missing_assoc_items: Vec<_> = missing_assoc_items | |
| .into_iter() | |
| .map(|(def_id, trait_ref)| (tcx.associated_item(def_id), trait_ref)) | |
| .collect(); | |
| let mut names: FxIndexMap<_, Vec<_>> = Default::default(); | |
| let mut names_len = 0; | |
| let mut descr = None; | |
| enum Descr { | |
| Item, | |
| Tag(ty::AssocTag), | |
| } | |
| for &(assoc_item, trait_ref) in &missing_assoc_items { | |
| // We don't want to suggest specifying associated items if there's something wrong with | |
| // any of them that renders the trait dyn incompatible; providing them certainly won't | |
| // fix the issue and we could also risk suggesting invalid code. | |
| // | |
| // Note that this check is only truly necessary in item ctxts where we merely perform | |
| // *minimal* dyn compatibility checks. In fn ctxts we would've already bailed out with | |
| // an error by this point if the trait was dyn incompatible. | |
| let violations = | |
| dyn_compatibility_violations_for_assoc_item(tcx, trait_ref.def_id(), assoc_item); | |
| if !violations.is_empty() { | |
| return Err(report_dyn_incompatibility( | |
| tcx, | |
| principal_span, | |
| None, | |
| trait_ref.def_id(), | |
| &violations, | |
| ) | |
| .emit()); | |
| } | |
| names.entry(trait_ref).or_default().push(assoc_item.name()); | |
| names_len += 1; | |
| descr = match descr { | |
| None => Some(Descr::Tag(assoc_item.tag())), | |
| Some(Descr::Tag(tag)) if tag != assoc_item.tag() => Some(Descr::Item), | |
| _ => continue, | |
| }; | |
| } | |
| // related to issue #91997, turbofishes added only when in an expr or pat | |
| let mut in_expr_or_pat = false; | |
| if let ([], [bound]) = (&potential_assoc_items[..], &trait_bounds) { | |
| let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.trait_ref.hir_ref_id)); | |
| in_expr_or_pat = match grandparent { | |
| hir::Node::Expr(_) | hir::Node::Pat(_) => true, | |
| _ => false, | |
| }; | |
| } | |
| // We get all the associated items that *are* set, so that we can check if any of | |
| // their names match one of the ones we are missing. | |
| // This would mean that they are shadowing the associated item we are missing, and | |
| // we can then use their span to indicate this to the user. | |
| // | |
| // FIXME: This does not account for trait aliases. I think we should just make | |
| // `lower_trait_object_ty` compute the list of all specified items or give us the | |
| // necessary ingredients if it's too expensive to compute in the happy path. | |
| let bound_names: UnordMap<_, _> = | |
| trait_bounds | |
| .iter() | |
| .filter_map(|poly_trait_ref| { | |
| let path = poly_trait_ref.trait_ref.path.segments.last()?; | |
| let args = path.args?; | |
| let Res::Def(DefKind::Trait, trait_def_id) = path.res else { return None }; | |
| Some(args.constraints.iter().filter_map(move |constraint| { | |
| let hir::AssocItemConstraintKind::Equality { term } = constraint.kind | |
| else { | |
| return None; | |
| }; | |
| let tag = match term { | |
| hir::Term::Ty(_) => ty::AssocTag::Type, | |
| hir::Term::Const(_) => ty::AssocTag::Const, | |
| }; | |
| let assoc_item = tcx | |
| .associated_items(trait_def_id) | |
| .find_by_ident_and_kind(tcx, constraint.ident, tag, trait_def_id)?; | |
| Some(((constraint.ident.name, tag), assoc_item.def_id)) | |
| })) | |
| }) | |
| .flatten() | |
| .collect(); | |
| let mut names: Vec<_> = names | |
| .into_iter() | |
| .map(|(trait_, mut assocs)| { | |
| assocs.sort(); | |
| let trait_ = trait_.print_trait_sugared(); | |
| format!( | |
| "{} in `{trait_}`", | |
| listify(&assocs[..], |a| format!("`{a}`")).unwrap_or_default() | |
| ) | |
| }) | |
| .collect(); | |
| names.sort(); | |
| let names = names.join(", "); | |
| let descr = match descr.unwrap() { | |
| Descr::Item => "associated item", | |
| Descr::Tag(tag) => tag.descr(), | |
| }; | |
| let mut err = struct_span_code_err!( | |
| self.dcx(), | |
| principal_span, | |
| E0191, | |
| "the value of the {descr}{s} {names} must be specified", | |
| s = pluralize!(names_len), | |
| ); | |
| let mut suggestions = vec![]; | |
| let mut items_count = 0; | |
| let mut where_constraints = vec![]; | |
| let mut already_has_generics_args_suggestion = false; | |
| let mut names: UnordMap<_, usize> = Default::default(); | |
| for (item, _) in &missing_assoc_items { | |
| items_count += 1; | |
| *names.entry((item.name(), item.tag())).or_insert(0) += 1; | |
| } | |
| let mut dupes = false; | |
| let mut shadows = false; | |
| for (item, trait_ref) in &missing_assoc_items { | |
| let name = item.name(); | |
| let key = (name, item.tag()); | |
| if names[&key] > 1 { | |
| dupes = true; | |
| } else if bound_names.get(&key).is_some_and(|&def_id| def_id != item.def_id) { | |
| shadows = true; | |
| } | |
| let prefix = if dupes || shadows { | |
| format!("{}::", tcx.def_path_str(trait_ref.def_id())) | |
| } else { | |
| String::new() | |
| }; | |
| let mut is_shadowed = false; | |
| if let Some(&def_id) = bound_names.get(&key) | |
| && def_id != item.def_id | |
| { | |
| is_shadowed = true; | |
| let rename_message = if def_id.is_local() { ", consider renaming it" } else { "" }; | |
| err.span_label( | |
| tcx.def_span(def_id), | |
| format!("`{prefix}{name}` shadowed here{rename_message}"), | |
| ); | |
| } | |
| let rename_message = if is_shadowed { ", consider renaming it" } else { "" }; | |
| if let Some(sp) = tcx.hir_span_if_local(item.def_id) { | |
| err.span_label(sp, format!("`{prefix}{name}` defined here{rename_message}")); | |
| } | |
| } | |
| if potential_assoc_items.len() == missing_assoc_items.len() { | |
| // When the amount of missing associated types equals the number of | |
| // extra type arguments present. A suggesting to replace the generic args with | |
| // associated types is already emitted. | |
| already_has_generics_args_suggestion = true; | |
| } else if let (Ok(snippet), false, false) = | |
| (tcx.sess.source_map().span_to_snippet(principal_span), dupes, shadows) | |
| { | |
| let bindings: Vec<_> = missing_assoc_items | |
| .iter() | |
| .map(|(item, _)| { | |
| format!( | |
| "{} = /* {} */", | |
| item.name(), | |
| match item.kind { | |
| ty::AssocKind::Const { .. } => "CONST", | |
| ty::AssocKind::Type { .. } => "Type", | |
| ty::AssocKind::Fn { .. } => unreachable!(), | |
| } | |
| ) | |
| }) | |
| .collect(); | |
| let code = if let Some(snippet) = snippet.strip_suffix("<>") { | |
| // Empty generics | |
| format!("{snippet}<{}>", bindings.join(", ")) | |
| } else if let Some(snippet) = snippet.strip_suffix('>') { | |
| // Non-empty generics | |
| format!("{snippet}, {}>", bindings.join(", ")) | |
| } else if in_expr_or_pat { | |
| // The user wrote `Trait`, so we don't have a term we can suggest, but at least we | |
| // can clue them to the correct syntax `Trait::<Item = /* ... */>`. | |
| format!("{}::<{}>", snippet, bindings.join(", ")) | |
| } else { | |
| // The user wrote `Trait`, so we don't have a term we can suggest, but at least we | |
| // can clue them to the correct syntax `Trait<Item = /* ... */>`. | |
| format!("{}<{}>", snippet, bindings.join(", ")) | |
| }; | |
| suggestions.push((principal_span, code)); | |
| } else if dupes { | |
| where_constraints.push(principal_span); | |
| } | |
| // FIXME: This note doesn't make sense, get rid of this outright. | |
| // I don't see how adding a type param (to the trait?) would help. | |
| // If the user can modify the trait, they should just rename one of the assoc tys. | |
| // What does it mean with the rest of the message? | |
| // Does it suggest adding equality predicates (unimplemented) to the trait object | |
| // type? (pseudo) "dyn B + <Self as B>::X = T + <Self as A>::X = U"? | |
| // Instead, maybe mention shadowing if applicable (yes, even when no "relevant" | |
| // bindings were provided). | |
| let where_msg = "consider introducing a new type parameter, adding `where` constraints \ | |
| using the fully-qualified path to the associated types"; | |
| if !where_constraints.is_empty() && suggestions.is_empty() { | |
| // If there are duplicates associated type names and a single trait bound do not | |
| // use structured suggestion, it means that there are multiple supertraits with | |
| // the same associated type name. | |
| err.help(where_msg); | |
| } | |
| if suggestions.len() != 1 || already_has_generics_args_suggestion { | |
| // We don't need this label if there's an inline suggestion, show otherwise. | |
| let mut names: FxIndexMap<_, usize> = FxIndexMap::default(); | |
| for (item, _) in &missing_assoc_items { | |
| items_count += 1; | |
| *names.entry(item.name()).or_insert(0) += 1; | |
| } | |
| let mut label = vec![]; | |
| for (item, trait_ref) in &missing_assoc_items { | |
| let name = item.name(); | |
| let postfix = if names[&name] > 1 { | |
| format!(" (from trait `{}`)", trait_ref.print_trait_sugared()) | |
| } else { | |
| String::new() | |
| }; | |
| label.push(format!("`{}`{}", name, postfix)); | |
| } | |
| if !label.is_empty() { | |
| err.span_label( | |
| principal_span, | |
| format!( | |
| "{descr}{s} {names} must be specified", | |
| s = pluralize!(label.len()), | |
| names = label.join(", "), | |
| ), | |
| ); | |
| } | |
| } | |
| suggestions.sort_by_key(|&(span, _)| span); | |
| // There are cases where one bound points to a span within another bound's span, like when | |
| // you have code like the following (#115019), so we skip providing a suggestion in those | |
| // cases to avoid having a malformed suggestion. | |
| // | |
| // pub struct Flatten<I> { | |
| // inner: <IntoIterator<Item: IntoIterator<Item: >>::IntoIterator as Item>::core, | |
| // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| // | ^^^^^^^^^^^^^^^^^^^^^ | |
| // | | | |
| // | associated types `Item`, `IntoIter` must be specified | |
| // associated types `Item`, `IntoIter` must be specified | |
| // } | |
| let overlaps = suggestions.windows(2).any(|pair| pair[0].0.overlaps(pair[1].0)); | |
| if !suggestions.is_empty() && !overlaps { | |
| err.multipart_suggestion( | |
| format!("specify the {descr}{s}", s = pluralize!(items_count)), | |
| suggestions, | |
| Applicability::HasPlaceholders, | |
| ); | |
| if !where_constraints.is_empty() { | |
| err.span_help(where_constraints, where_msg); | |
| } | |
| } | |
| Err(err.emit()) | |
| } | |
| /// On ambiguous associated type, look for an associated function whose name matches the | |
| /// extended path and, if found, emit an E0223 error with a structured suggestion. | |
| /// e.g. for `String::from::utf8`, suggest `String::from_utf8` (#109195) | |
| pub(crate) fn maybe_report_similar_assoc_fn( | |
| &self, | |
| span: Span, | |
| qself_ty: Ty<'tcx>, | |
| qself: &hir::Ty<'_>, | |
| ) -> Result<(), ErrorGuaranteed> { | |
| let tcx = self.tcx(); | |
| if let Some((_, node)) = tcx.hir_parent_iter(qself.hir_id).skip(1).next() | |
| && let hir::Node::Expr(hir::Expr { | |
| kind: | |
| hir::ExprKind::Path(hir::QPath::TypeRelative( | |
| hir::Ty { | |
| kind: | |
| hir::TyKind::Path(hir::QPath::TypeRelative( | |
| _, | |
| hir::PathSegment { ident: ident2, .. }, | |
| )), | |
| .. | |
| }, | |
| hir::PathSegment { ident: ident3, .. }, | |
| )), | |
| .. | |
| }) = node | |
| && let Some(inherent_impls) = qself_ty | |
| .ty_adt_def() | |
| .map(|adt_def| tcx.inherent_impls(adt_def.did())) | |
| .or_else(|| { | |
| simplify_type(tcx, qself_ty, TreatParams::InstantiateWithInfer) | |
| .map(|simple_ty| tcx.incoherent_impls(simple_ty)) | |
| }) | |
| && let name = Symbol::intern(&format!("{ident2}_{ident3}")) | |
| && let Some(item) = inherent_impls | |
| .iter() | |
| .flat_map(|&inherent_impl| { | |
| tcx.associated_items(inherent_impl).filter_by_name_unhygienic(name) | |
| }) | |
| .next() | |
| && item.is_fn() | |
| { | |
| Err(struct_span_code_err!(self.dcx(), span, E0223, "ambiguous associated type") | |
| .with_span_suggestion_verbose( | |
| ident2.span.to(ident3.span), | |
| format!("there is an associated function with a similar name: `{name}`"), | |
| name, | |
| Applicability::MaybeIncorrect, | |
| ) | |
| .emit()) | |
| } else { | |
| Ok(()) | |
| } | |
| } | |
| pub fn report_prohibited_generic_args<'a>( | |
| &self, | |
| segments: impl Iterator<Item = &'a hir::PathSegment<'a>> + Clone, | |
| args_visitors: impl Iterator<Item = &'a hir::GenericArg<'a>> + Clone, | |
| err_extend: GenericsArgsErrExtend<'a>, | |
| ) -> ErrorGuaranteed { | |
| #[derive(PartialEq, Eq, Hash)] | |
| enum ProhibitGenericsArg { | |
| Lifetime, | |
| Type, | |
| Const, | |
| Infer, | |
| } | |
| let mut prohibit_args = FxIndexSet::default(); | |
| args_visitors.for_each(|arg| { | |
| match arg { | |
| hir::GenericArg::Lifetime(_) => prohibit_args.insert(ProhibitGenericsArg::Lifetime), | |
| hir::GenericArg::Type(_) => prohibit_args.insert(ProhibitGenericsArg::Type), | |
| hir::GenericArg::Const(_) => prohibit_args.insert(ProhibitGenericsArg::Const), | |
| hir::GenericArg::Infer(_) => prohibit_args.insert(ProhibitGenericsArg::Infer), | |
| }; | |
| }); | |
| let segments: Vec<_> = segments.collect(); | |
| let types_and_spans: Vec<_> = segments | |
| .iter() | |
| .flat_map(|segment| { | |
| if segment.args().args.is_empty() { | |
| None | |
| } else { | |
| Some(( | |
| match segment.res { | |
| Res::PrimTy(ty) => { | |
| format!("{} `{}`", segment.res.descr(), ty.name()) | |
| } | |
| Res::Def(_, def_id) | |
| if let Some(name) = self.tcx().opt_item_name(def_id) => | |
| { | |
| format!("{} `{name}`", segment.res.descr()) | |
| } | |
| Res::Err => "this type".to_string(), | |
| _ => segment.res.descr().to_string(), | |
| }, | |
| segment.ident.span, | |
| )) | |
| } | |
| }) | |
| .collect(); | |
| let this_type = listify(&types_and_spans, |(t, _)| t.to_string()) | |
| .expect("expected one segment to deny"); | |
| let arg_spans: Vec<Span> = | |
| segments.iter().flat_map(|segment| segment.args().args).map(|arg| arg.span()).collect(); | |
| let mut kinds = Vec::with_capacity(4); | |
| prohibit_args.iter().for_each(|arg| match arg { | |
| ProhibitGenericsArg::Lifetime => kinds.push("lifetime"), | |
| ProhibitGenericsArg::Type => kinds.push("type"), | |
| ProhibitGenericsArg::Const => kinds.push("const"), | |
| ProhibitGenericsArg::Infer => kinds.push("generic"), | |
| }); | |
| let s = pluralize!(kinds.len()); | |
| let kind = | |
| listify(&kinds, |k| k.to_string()).expect("expected at least one generic to prohibit"); | |
| let last_span = *arg_spans.last().unwrap(); | |
| let span: MultiSpan = arg_spans.into(); | |
| let mut err = struct_span_code_err!( | |
| self.dcx(), | |
| span, | |
| E0109, | |
| "{kind} arguments are not allowed on {this_type}", | |
| ); | |
| err.span_label(last_span, format!("{kind} argument{s} not allowed")); | |
| for (what, span) in types_and_spans { | |
| err.span_label(span, format!("not allowed on {what}")); | |
| } | |
| generics_args_err_extend(self.tcx(), segments.into_iter(), &mut err, err_extend); | |
| err.emit() | |
| } | |
| pub fn report_trait_object_addition_traits( | |
| &self, | |
| regular_traits: &Vec<(ty::PolyTraitPredicate<'tcx>, SmallVec<[Span; 1]>)>, | |
| ) -> ErrorGuaranteed { | |
| // we use the last span to point at the traits themselves, | |
| // and all other preceding spans are trait alias expansions. | |
| let (&first_span, first_alias_spans) = regular_traits[0].1.split_last().unwrap(); | |
| let (&second_span, second_alias_spans) = regular_traits[1].1.split_last().unwrap(); | |
| let mut err = struct_span_code_err!( | |
| self.dcx(), | |
| *regular_traits[1].1.first().unwrap(), | |
| E0225, | |
| "only auto traits can be used as additional traits in a trait object" | |
| ); | |
| err.span_label(first_span, "first non-auto trait"); | |
| for &alias_span in first_alias_spans { | |
| err.span_label(alias_span, "first non-auto trait comes from this alias"); | |
| } | |
| err.span_label(second_span, "additional non-auto trait"); | |
| for &alias_span in second_alias_spans { | |
| err.span_label(alias_span, "second non-auto trait comes from this alias"); | |
| } | |
| err.help(format!( | |
| "consider creating a new trait with all of these as supertraits and using that \ | |
| trait here instead: `trait NewTrait: {} {{}}`", | |
| regular_traits | |
| .iter() | |
| // FIXME: This should `print_sugared`, but also needs to integrate projection bounds... | |
| .map(|(pred, _)| pred | |
| .map_bound(|pred| pred.trait_ref) | |
| .print_only_trait_path() | |
| .to_string()) | |
| .collect::<Vec<_>>() | |
| .join(" + "), | |
| )); | |
| err.note( | |
| "auto-traits like `Send` and `Sync` are traits that have special properties; \ | |
| for more information on them, visit \ | |
| <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>", | |
| ); | |
| err.emit() | |
| } | |
| pub fn report_trait_object_with_no_traits( | |
| &self, | |
| span: Span, | |
| user_written_clauses: impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>, | |
| ) -> ErrorGuaranteed { | |
| let tcx = self.tcx(); | |
| let trait_alias_span = user_written_clauses | |
| .into_iter() | |
| .filter_map(|(clause, _)| clause.as_trait_clause()) | |
| .find(|trait_ref| tcx.is_trait_alias(trait_ref.def_id())) | |
| .map(|trait_ref| tcx.def_span(trait_ref.def_id())); | |
| self.dcx().emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span }) | |
| } | |
| } | |
| /// Emit an error for the given associated item constraint. | |
| pub fn prohibit_assoc_item_constraint( | |
| cx: &dyn HirTyLowerer<'_>, | |
| constraint: &hir::AssocItemConstraint<'_>, | |
| segment: Option<(DefId, &hir::PathSegment<'_>, Span)>, | |
| ) -> ErrorGuaranteed { | |
| let tcx = cx.tcx(); | |
| let mut err = cx.dcx().create_err(AssocItemConstraintsNotAllowedHere { | |
| span: constraint.span, | |
| fn_trait_expansion: if let Some((_, segment, span)) = segment | |
| && segment.args().parenthesized == hir::GenericArgsParentheses::ParenSugar | |
| { | |
| Some(ParenthesizedFnTraitExpansion { | |
| span, | |
| expanded_type: fn_trait_to_string(tcx, segment, false), | |
| }) | |
| } else { | |
| None | |
| }, | |
| }); | |
| // Emit a suggestion to turn the assoc item binding into a generic arg | |
| // if the relevant item has a generic param whose name matches the binding name; | |
| // otherwise suggest the removal of the binding. | |
| if let Some((def_id, segment, _)) = segment | |
| && segment.args().parenthesized == hir::GenericArgsParentheses::No | |
| { | |
| // Suggests removal of the offending binding | |
| let suggest_removal = |e: &mut Diag<'_>| { | |
| let constraints = segment.args().constraints; | |
| let args = segment.args().args; | |
| // Compute the span to remove based on the position | |
| // of the binding. We do that as follows: | |
| // 1. Find the index of the binding in the list of bindings | |
| // 2. Locate the spans preceding and following the binding. | |
| // If it's the first binding the preceding span would be | |
| // that of the last arg | |
| // 3. Using this information work out whether the span | |
| // to remove will start from the end of the preceding span, | |
| // the start of the next span or will simply be the | |
| // span encomassing everything within the generics brackets | |
| let Some(index) = constraints.iter().position(|b| b.hir_id == constraint.hir_id) else { | |
| bug!("a type binding exists but its HIR ID not found in generics"); | |
| }; | |
| let preceding_span = if index > 0 { | |
| Some(constraints[index - 1].span) | |
| } else { | |
| args.last().map(|a| a.span()) | |
| }; | |
| let next_span = constraints.get(index + 1).map(|constraint| constraint.span); | |
| let removal_span = match (preceding_span, next_span) { | |
| (Some(prec), _) => constraint.span.with_lo(prec.hi()), | |
| (None, Some(next)) => constraint.span.with_hi(next.lo()), | |
| (None, None) => { | |
| let Some(generics_span) = segment.args().span_ext() else { | |
| bug!("a type binding exists but generic span is empty"); | |
| }; | |
| generics_span | |
| } | |
| }; | |
| // Now emit the suggestion | |
| e.span_suggestion_verbose( | |
| removal_span, | |
| format!("consider removing this associated item {}", constraint.kind.descr()), | |
| "", | |
| Applicability::MaybeIncorrect, | |
| ); | |
| }; | |
| // Suggest replacing the associated item binding with a generic argument. | |
| // i.e., replacing `<..., T = A, ...>` with `<..., A, ...>`. | |
| let suggest_direct_use = |e: &mut Diag<'_>, sp: Span| { | |
| if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(sp) { | |
| e.span_suggestion_verbose( | |
| constraint.span, | |
| format!("to use `{snippet}` as a generic argument specify it directly"), | |
| snippet, | |
| Applicability::MaybeIncorrect, | |
| ); | |
| } | |
| }; | |
| // Check if the type has a generic param with the same name | |
| // as the assoc type name in the associated item binding. | |
| let generics = tcx.generics_of(def_id); | |
| let matching_param = generics.own_params.iter().find(|p| p.name == constraint.ident.name); | |
| // Now emit the appropriate suggestion | |
| if let Some(matching_param) = matching_param { | |
| match (constraint.kind, &matching_param.kind) { | |
| ( | |
| hir::AssocItemConstraintKind::Equality { term: hir::Term::Ty(ty) }, | |
| GenericParamDefKind::Type { .. }, | |
| ) => suggest_direct_use(&mut err, ty.span), | |
| ( | |
| hir::AssocItemConstraintKind::Equality { term: hir::Term::Const(c) }, | |
| GenericParamDefKind::Const { .. }, | |
| ) => { | |
| suggest_direct_use(&mut err, c.span); | |
| } | |
| (hir::AssocItemConstraintKind::Bound { bounds }, _) => { | |
| // Suggest `impl<T: Bound> Trait<T> for Foo` when finding | |
| // `impl Trait<T: Bound> for Foo` | |
| // Get the parent impl block based on the binding we have | |
| // and the trait DefId | |
| let impl_block = tcx | |
| .hir_parent_iter(constraint.hir_id) | |
| .find_map(|(_, node)| node.impl_block_of_trait(def_id)); | |
| let type_with_constraints = | |
| tcx.sess.source_map().span_to_snippet(constraint.span); | |
| if let Some(impl_block) = impl_block | |
| && let Ok(type_with_constraints) = type_with_constraints | |
| { | |
| // Filter out the lifetime parameters because | |
| // they should be declared before the type parameter | |
| let lifetimes: String = bounds | |
| .iter() | |
| .filter_map(|bound| { | |
| if let hir::GenericBound::Outlives(lifetime) = bound { | |
| Some(format!("{lifetime}, ")) | |
| } else { | |
| None | |
| } | |
| }) | |
| .collect(); | |
| // Figure out a span and suggestion string based on | |
| // whether there are any existing parameters | |
| let param_decl = if let Some(param_span) = | |
| impl_block.generics.span_for_param_suggestion() | |
| { | |
| (param_span, format!(", {lifetimes}{type_with_constraints}")) | |
| } else { | |
| ( | |
| impl_block.generics.span.shrink_to_lo(), | |
| format!("<{lifetimes}{type_with_constraints}>"), | |
| ) | |
| }; | |
| let suggestions = vec![ | |
| param_decl, | |
| (constraint.span.with_lo(constraint.ident.span.hi()), String::new()), | |
| ]; | |
| err.multipart_suggestion( | |
| "declare the type parameter right after the `impl` keyword", | |
| suggestions, | |
| Applicability::MaybeIncorrect, | |
| ); | |
| } | |
| } | |
| _ => suggest_removal(&mut err), | |
| } | |
| } else { | |
| suggest_removal(&mut err); | |
| } | |
| } | |
| err.emit() | |
| } | |
| pub(crate) fn fn_trait_to_string( | |
| tcx: TyCtxt<'_>, | |
| trait_segment: &hir::PathSegment<'_>, | |
| parenthesized: bool, | |
| ) -> String { | |
| let args = trait_segment | |
| .args | |
| .and_then(|args| args.args.first()) | |
| .and_then(|arg| match arg { | |
| hir::GenericArg::Type(ty) => match ty.kind { | |
| hir::TyKind::Tup(t) => t | |
| .iter() | |
| .map(|e| tcx.sess.source_map().span_to_snippet(e.span)) | |
| .collect::<Result<Vec<_>, _>>() | |
| .map(|a| a.join(", ")), | |
| _ => tcx.sess.source_map().span_to_snippet(ty.span), | |
| } | |
| .map(|s| { | |
| // `is_empty()` checks to see if the type is the unit tuple, if so we don't want a comma | |
| if parenthesized || s.is_empty() { format!("({s})") } else { format!("({s},)") } | |
| }) | |
| .ok(), | |
| _ => None, | |
| }) | |
| .unwrap_or_else(|| "()".to_string()); | |
| let ret = trait_segment | |
| .args() | |
| .constraints | |
| .iter() | |
| .find_map(|c| { | |
| if c.ident.name == sym::Output | |
| && let Some(ty) = c.ty() | |
| && ty.span != tcx.hir_span(trait_segment.hir_id) | |
| { | |
| tcx.sess.source_map().span_to_snippet(ty.span).ok() | |
| } else { | |
| None | |
| } | |
| }) | |
| .unwrap_or_else(|| "()".to_string()); | |
| if parenthesized { | |
| format!("{}{} -> {}", trait_segment.ident, args, ret) | |
| } else { | |
| format!("{}<{}, Output={}>", trait_segment.ident, args, ret) | |
| } | |
| } | |
| /// Used for generics args error extend. | |
| pub enum GenericsArgsErrExtend<'tcx> { | |
| EnumVariant { | |
| qself: &'tcx hir::Ty<'tcx>, | |
| assoc_segment: &'tcx hir::PathSegment<'tcx>, | |
| adt_def: AdtDef<'tcx>, | |
| }, | |
| OpaqueTy, | |
| PrimTy(hir::PrimTy), | |
| SelfTyAlias { | |
| def_id: DefId, | |
| span: Span, | |
| }, | |
| SelfTyParam(Span), | |
| Param(DefId), | |
| DefVariant(&'tcx [hir::PathSegment<'tcx>]), | |
| None, | |
| } | |
| fn generics_args_err_extend<'a>( | |
| tcx: TyCtxt<'_>, | |
| segments: impl Iterator<Item = &'a hir::PathSegment<'a>> + Clone, | |
| err: &mut Diag<'_>, | |
| err_extend: GenericsArgsErrExtend<'a>, | |
| ) { | |
| match err_extend { | |
| GenericsArgsErrExtend::EnumVariant { qself, assoc_segment, adt_def } => { | |
| err.note("enum variants can't have type parameters"); | |
| let type_name = tcx.item_name(adt_def.did()); | |
| let msg = format!( | |
| "you might have meant to specify type parameters on enum \ | |
| `{type_name}`" | |
| ); | |
| let Some(args) = assoc_segment.args else { | |
| return; | |
| }; | |
| // Get the span of the generics args *including* the leading `::`. | |
| // We do so by stretching args.span_ext to the left by 2. Earlier | |
| // it was done based on the end of assoc segment but that sometimes | |
| // led to impossible spans and caused issues like #116473 | |
| let args_span = args.span_ext.with_lo(args.span_ext.lo() - BytePos(2)); | |
| if tcx.generics_of(adt_def.did()).is_empty() { | |
| // FIXME(estebank): we could also verify that the arguments being | |
| // work for the `enum`, instead of just looking if it takes *any*. | |
| err.span_suggestion_verbose( | |
| args_span, | |
| format!("{type_name} doesn't have generic parameters"), | |
| "", | |
| Applicability::MachineApplicable, | |
| ); | |
| return; | |
| } | |
| let Ok(snippet) = tcx.sess.source_map().span_to_snippet(args_span) else { | |
| err.note(msg); | |
| return; | |
| }; | |
| let (qself_sugg_span, is_self) = | |
| if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind { | |
| // If the path segment already has type params, we want to overwrite | |
| // them. | |
| match &path.segments { | |
| // `segment` is the previous to last element on the path, | |
| // which would normally be the `enum` itself, while the last | |
| // `_` `PathSegment` corresponds to the variant. | |
| [ | |
| .., | |
| hir::PathSegment { | |
| ident, args, res: Res::Def(DefKind::Enum, _), .. | |
| }, | |
| _, | |
| ] => ( | |
| // We need to include the `::` in `Type::Variant::<Args>` | |
| // to point the span to `::<Args>`, not just `<Args>`. | |
| ident | |
| .span | |
| .shrink_to_hi() | |
| .to(args.map_or(ident.span.shrink_to_hi(), |a| a.span_ext)), | |
| false, | |
| ), | |
| [segment] => { | |
| ( | |
| // We need to include the `::` in `Type::Variant::<Args>` | |
| // to point the span to `::<Args>`, not just `<Args>`. | |
| segment.ident.span.shrink_to_hi().to(segment | |
| .args | |
| .map_or(segment.ident.span.shrink_to_hi(), |a| a.span_ext)), | |
| kw::SelfUpper == segment.ident.name, | |
| ) | |
| } | |
| _ => { | |
| err.note(msg); | |
| return; | |
| } | |
| } | |
| } else { | |
| err.note(msg); | |
| return; | |
| }; | |
| let suggestion = vec![ | |
| if is_self { | |
| // Account for people writing `Self::Variant::<Args>`, where | |
| // `Self` is the enum, and suggest replacing `Self` with the | |
| // appropriate type: `Type::<Args>::Variant`. | |
| (qself.span, format!("{type_name}{snippet}")) | |
| } else { | |
| (qself_sugg_span, snippet) | |
| }, | |
| (args_span, String::new()), | |
| ]; | |
| err.multipart_suggestion(msg, suggestion, Applicability::MaybeIncorrect); | |
| } | |
| GenericsArgsErrExtend::DefVariant(segments) => { | |
| let args: Vec<Span> = segments | |
| .iter() | |
| .filter_map(|segment| match segment.res { | |
| Res::Def( | |
| DefKind::Ctor(CtorOf::Variant, _) | DefKind::Variant | DefKind::Enum, | |
| _, | |
| ) => segment.args().span_ext().map(|s| s.with_lo(segment.ident.span.hi())), | |
| _ => None, | |
| }) | |
| .collect(); | |
| if args.len() > 1 | |
| && let Some(span) = args.into_iter().next_back() | |
| { | |
| err.note( | |
| "generic arguments are not allowed on both an enum and its variant's path \ | |
| segments simultaneously; they are only valid in one place or the other", | |
| ); | |
| err.span_suggestion_verbose( | |
| span, | |
| "remove the generics arguments from one of the path segments", | |
| String::new(), | |
| Applicability::MaybeIncorrect, | |
| ); | |
| } | |
| } | |
| GenericsArgsErrExtend::PrimTy(prim_ty) => { | |
| let name = prim_ty.name_str(); | |
| for segment in segments { | |
| if let Some(args) = segment.args { | |
| err.span_suggestion_verbose( | |
| segment.ident.span.shrink_to_hi().to(args.span_ext), | |
| format!("primitive type `{name}` doesn't have generic parameters"), | |
| "", | |
| Applicability::MaybeIncorrect, | |
| ); | |
| } | |
| } | |
| } | |
| GenericsArgsErrExtend::OpaqueTy => { | |
| err.note("`impl Trait` types can't have type parameters"); | |
| } | |
| GenericsArgsErrExtend::Param(def_id) => { | |
| let span = tcx.def_ident_span(def_id).unwrap(); | |
| let kind = tcx.def_descr(def_id); | |
| let name = tcx.item_name(def_id); | |
| err.span_note(span, format!("{kind} `{name}` defined here")); | |
| } | |
| GenericsArgsErrExtend::SelfTyParam(span) => { | |
| err.span_suggestion_verbose( | |
| span, | |
| "the `Self` type doesn't accept type parameters", | |
| "", | |
| Applicability::MaybeIncorrect, | |
| ); | |
| } | |
| GenericsArgsErrExtend::SelfTyAlias { def_id, span } => { | |
| let ty = tcx.at(span).type_of(def_id).instantiate_identity().skip_norm_wip(); | |
| let span_of_impl = tcx.span_of_impl(def_id); | |
| let ty::Adt(self_def, _) = *ty.kind() else { return }; | |
| let def_id = self_def.did(); | |
| let type_name = tcx.item_name(def_id); | |
| let span_of_ty = tcx.def_ident_span(def_id); | |
| let generics = tcx.generics_of(def_id).count(); | |
| let msg = format!("`Self` is of type `{ty}`"); | |
| if let (Ok(i_sp), Some(t_sp)) = (span_of_impl, span_of_ty) { | |
| let mut span: MultiSpan = vec![t_sp].into(); | |
| span.push_span_label( | |
| i_sp, | |
| format!("`Self` is on type `{type_name}` in this `impl`"), | |
| ); | |
| let mut postfix = ""; | |
| if generics == 0 { | |
| postfix = ", which doesn't have generic parameters"; | |
| } | |
| span.push_span_label(t_sp, format!("`Self` corresponds to this type{postfix}")); | |
| err.span_note(span, msg); | |
| } else { | |
| err.note(msg); | |
| } | |
| for segment in segments { | |
| if let Some(args) = segment.args | |
| && segment.ident.name == kw::SelfUpper | |
| { | |
| if generics == 0 { | |
| // FIXME(estebank): we could also verify that the arguments being | |
| // work for the `enum`, instead of just looking if it takes *any*. | |
| err.span_suggestion_verbose( | |
| segment.ident.span.shrink_to_hi().to(args.span_ext), | |
| "the `Self` type doesn't accept type parameters", | |
| "", | |
| Applicability::MachineApplicable, | |
| ); | |
| return; | |
| } else { | |
| err.span_suggestion_verbose( | |
| segment.ident.span, | |
| format!( | |
| "the `Self` type doesn't accept type parameters, use the \ | |
| concrete type's name `{type_name}` instead if you want to \ | |
| specify its type parameters" | |
| ), | |
| type_name, | |
| Applicability::MaybeIncorrect, | |
| ); | |
| } | |
| } | |
| } | |
| } | |
| _ => {} | |
| } | |
| } | |
| pub(super) struct AmbiguityBetweenVariantAndAssocItem<'tcx> { | |
| pub(super) variant_def_id: DefId, | |
| pub(super) item_def_id: DefId, | |
| pub(super) span: Span, | |
| pub(super) segment_ident: Ident, | |
| pub(super) bound_def_id: DefId, | |
| pub(super) self_ty: Ty<'tcx>, | |
| pub(super) tcx: TyCtxt<'tcx>, | |
| pub(super) mode: super::LowerTypeRelativePathMode, | |
| } | |
| impl<'a, 'tcx> rustc_errors::Diagnostic<'a, ()> for AmbiguityBetweenVariantAndAssocItem<'tcx> { | |
| fn into_diag( | |
| self, | |
| dcx: rustc_errors::DiagCtxtHandle<'a>, | |
| level: rustc_errors::Level, | |
| ) -> Diag<'a, ()> { | |
| let Self { | |
| variant_def_id, | |
| item_def_id, | |
| span, | |
| segment_ident, | |
| bound_def_id, | |
| self_ty, | |
| tcx, | |
| mode, | |
| } = self; | |
| let mut lint = Diag::new(dcx, level, "ambiguous associated item"); | |
| let mut could_refer_to = |kind: DefKind, def_id, also| { | |
| let note_msg = format!( | |
| "`{}` could{} refer to the {} defined here", | |
| segment_ident, | |
| also, | |
| tcx.def_kind_descr(kind, def_id) | |
| ); | |
| lint.span_note(tcx.def_span(def_id), note_msg); | |
| }; | |
| could_refer_to(DefKind::Variant, variant_def_id, ""); | |
| could_refer_to(mode.def_kind_for_diagnostics(), item_def_id, " also"); | |
| lint.span_suggestion( | |
| span, | |
| "use fully-qualified syntax", | |
| format!("<{} as {}>::{}", self_ty, tcx.item_name(bound_def_id), segment_ident), | |
| Applicability::MachineApplicable, | |
| ); | |
| lint | |
| } | |
| } | |
| fn assoc_tag_str(assoc_tag: ty::AssocTag) -> &'static str { | |
| match assoc_tag { | |
| ty::AssocTag::Fn => "function", | |
| ty::AssocTag::Const => "constant", | |
| ty::AssocTag::Type => "type", | |
| } | |
| } |