From cd68a28daa460e04875a16b9677117af93046979 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 8 Jul 2024 16:44:00 -0400 Subject: [PATCH] Move some stuff into the ambiguity and suggestion modules --- .../src/error_reporting/traits/ambiguity.rs | 568 ++++++++++++++- .../src/error_reporting/traits/mod.rs | 58 -- .../src/error_reporting/traits/suggestions.rs | 169 +++++ .../traits/type_err_ctxt_ext.rs | 681 +----------------- 4 files changed, 742 insertions(+), 734 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs index e6cb28df593af..c301deac6164d 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -1,10 +1,27 @@ +use std::ops::ControlFlow; + +use rustc_errors::{ + struct_span_code_err, Applicability, Diag, MultiSpan, StashKey, E0283, E0284, E0790, +}; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::Visitor as _; +use rustc_hir::LangItem; +use rustc_infer::infer::error_reporting::{TypeAnnotationNeeded, TypeErrCtxt}; use rustc_infer::infer::{BoundRegionConversionTime, InferCtxt}; use rustc_infer::traits::util::elaborate; -use rustc_infer::traits::{Obligation, ObligationCause, PolyTraitObligation}; -use rustc_middle::ty; -use rustc_span::{Span, DUMMY_SP}; +use rustc_infer::traits::{ + Obligation, ObligationCause, ObligationCauseCode, PolyTraitObligation, PredicateObligation, +}; +use rustc_macros::extension; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable as _, TypeVisitableExt as _}; +use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP}; +use crate::error_reporting::traits::suggestions::TypeErrCtxtExt as _; +use crate::error_reporting::traits::{ + to_pretty_impl_header, FindExprBySpan, InferCtxtPrivExt as _, +}; use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::ObligationCtxt; @@ -134,3 +151,548 @@ pub fn compute_applicable_impls_for_diagnostics<'tcx>( ambiguities } + +#[extension(pub trait TypeErrCtxtAmbiguityExt<'a, 'tcx>)] +impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { + #[instrument(skip(self), level = "debug")] + fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>) -> ErrorGuaranteed { + // Unable to successfully determine, probably means + // insufficient type information, but could mean + // ambiguous impls. The latter *ought* to be a + // coherence violation, so we don't report it here. + + let predicate = self.resolve_vars_if_possible(obligation.predicate); + let span = obligation.cause.span; + + debug!(?predicate, obligation.cause.code = ?obligation.cause.code()); + + // Ambiguity errors are often caused as fallout from earlier errors. + // We ignore them if this `infcx` is tainted in some cases below. + + let bound_predicate = predicate.kind(); + let mut err = match bound_predicate.skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { + let trait_ref = bound_predicate.rebind(data.trait_ref); + debug!(?trait_ref); + + if let Err(e) = predicate.error_reported() { + return e; + } + + if let Err(guar) = self.tcx.ensure().coherent_trait(trait_ref.def_id()) { + // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case + // other `Foo` impls are incoherent. + return guar; + } + + // This is kind of a hack: it frequently happens that some earlier + // error prevents types from being fully inferred, and then we get + // a bunch of uninteresting errors saying something like " doesn't implement Sized". It may even be true that we + // could just skip over all checks where the self-ty is an + // inference variable, but I was afraid that there might be an + // inference variable created, registered as an obligation, and + // then never forced by writeback, and hence by skipping here we'd + // be ignoring the fact that we don't KNOW the type works + // out. Though even that would probably be harmless, given that + // we're only talking about builtin traits, which are known to be + // inhabited. We used to check for `self.tcx.sess.has_errors()` to + // avoid inundating the user with unnecessary errors, but we now + // check upstream for type errors and don't add the obligations to + // begin with in those cases. + if self.tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) { + match self.tainted_by_errors() { + None => { + let err = self.emit_inference_failure_err( + obligation.cause.body_id, + span, + trait_ref.self_ty().skip_binder().into(), + TypeAnnotationNeeded::E0282, + false, + ); + return err.stash(span, StashKey::MaybeForgetReturn).unwrap(); + } + Some(e) => return e, + } + } + + // Typically, this ambiguity should only happen if + // there are unresolved type inference variables + // (otherwise it would suggest a coherence + // failure). But given #21974 that is not necessarily + // the case -- we can have multiple where clauses that + // are only distinguished by a region, which results + // in an ambiguity even when all types are fully + // known, since we don't dispatch based on region + // relationships. + + // Pick the first generic parameter that still contains inference variables as the one + // we're going to emit an error for. If there are none (see above), fall back to + // a more general error. + let arg = data.trait_ref.args.iter().find(|s| s.has_non_region_infer()); + + let mut err = if let Some(arg) = arg { + self.emit_inference_failure_err( + obligation.cause.body_id, + span, + arg, + TypeAnnotationNeeded::E0283, + true, + ) + } else { + struct_span_code_err!( + self.dcx(), + span, + E0283, + "type annotations needed: cannot satisfy `{}`", + predicate, + ) + }; + + let mut ambiguities = compute_applicable_impls_for_diagnostics( + self.infcx, + &obligation.with(self.tcx, trait_ref), + ); + let has_non_region_infer = + trait_ref.skip_binder().args.types().any(|t| !t.is_ty_or_numeric_infer()); + // It doesn't make sense to talk about applicable impls if there are more than a + // handful of them. If there are a lot of them, but only a few of them have no type + // params, we only show those, as they are more likely to be useful/intended. + if ambiguities.len() > 5 { + let infcx = self.infcx; + if !ambiguities.iter().all(|option| match option { + CandidateSource::DefId(did) => infcx.tcx.generics_of(*did).count() == 0, + CandidateSource::ParamEnv(_) => true, + }) { + // If not all are blanket impls, we filter blanked impls out. + ambiguities.retain(|option| match option { + CandidateSource::DefId(did) => infcx.tcx.generics_of(*did).count() == 0, + CandidateSource::ParamEnv(_) => true, + }); + } + } + if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer { + if let Some(e) = self.tainted_by_errors() + && arg.is_none() + { + // If `arg.is_none()`, then this is probably two param-env + // candidates or impl candidates that are equal modulo lifetimes. + // Therefore, if we've already emitted an error, just skip this + // one, since it's not particularly actionable. + err.cancel(); + return e; + } + self.annotate_source_of_ambiguity(&mut err, &ambiguities, predicate); + } else { + if let Some(e) = self.tainted_by_errors() { + err.cancel(); + return e; + } + err.note(format!("cannot satisfy `{predicate}`")); + let impl_candidates = + self.find_similar_impl_candidates(predicate.as_trait_clause().unwrap()); + if impl_candidates.len() < 40 { + self.report_similar_impl_candidates( + impl_candidates.as_slice(), + trait_ref, + obligation.cause.body_id, + &mut err, + false, + obligation.param_env, + ); + } + } + + if let ObligationCauseCode::WhereClause(def_id, _) + | ObligationCauseCode::WhereClauseInExpr(def_id, ..) = *obligation.cause.code() + { + self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id()); + } + + if let Some(ty::GenericArgKind::Type(_)) = arg.map(|arg| arg.unpack()) + && let Some(body) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) + { + let mut expr_finder = FindExprBySpan::new(span, self.tcx); + expr_finder.visit_expr(&body.value); + + if let Some(hir::Expr { + kind: + hir::ExprKind::Call( + hir::Expr { + kind: hir::ExprKind::Path(hir::QPath::Resolved(None, path)), + .. + }, + _, + ) + | hir::ExprKind::Path(hir::QPath::Resolved(None, path)), + .. + }) = expr_finder.result + && let [ + .., + trait_path_segment @ hir::PathSegment { + res: Res::Def(DefKind::Trait, trait_id), + .. + }, + hir::PathSegment { + ident: assoc_item_name, + res: Res::Def(_, item_id), + .. + }, + ] = path.segments + && data.trait_ref.def_id == *trait_id + && self.tcx.trait_of_item(*item_id) == Some(*trait_id) + && let None = self.tainted_by_errors() + { + let (verb, noun) = match self.tcx.associated_item(item_id).kind { + ty::AssocKind::Const => ("refer to the", "constant"), + ty::AssocKind::Fn => ("call", "function"), + // This is already covered by E0223, but this following single match + // arm doesn't hurt here. + ty::AssocKind::Type => ("refer to the", "type"), + }; + + // Replace the more general E0283 with a more specific error + err.cancel(); + err = self.dcx().struct_span_err( + span, + format!( + "cannot {verb} associated {noun} on trait without specifying the \ + corresponding `impl` type", + ), + ); + err.code(E0790); + + if let Some(local_def_id) = data.trait_ref.def_id.as_local() + && let hir::Node::Item(hir::Item { + ident: trait_name, + kind: hir::ItemKind::Trait(_, _, _, _, trait_item_refs), + .. + }) = self.tcx.hir_node_by_def_id(local_def_id) + && let Some(method_ref) = trait_item_refs + .iter() + .find(|item_ref| item_ref.ident == *assoc_item_name) + { + err.span_label( + method_ref.span, + format!("`{trait_name}::{assoc_item_name}` defined here"), + ); + } + + err.span_label(span, format!("cannot {verb} associated {noun} of trait")); + + let trait_impls = self.tcx.trait_impls_of(data.trait_ref.def_id); + + if let Some(impl_def_id) = + trait_impls.non_blanket_impls().values().flatten().next() + { + let non_blanket_impl_count = + trait_impls.non_blanket_impls().values().flatten().count(); + // If there is only one implementation of the trait, suggest using it. + // Otherwise, use a placeholder comment for the implementation. + let (message, self_type) = if non_blanket_impl_count == 1 { + ( + "use the fully-qualified path to the only available \ + implementation", + format!( + "{}", + self.tcx.type_of(impl_def_id).instantiate_identity() + ), + ) + } else { + ( + "use a fully-qualified path to a specific available \ + implementation", + "/* self type */".to_string(), + ) + }; + let mut suggestions = + vec![(path.span.shrink_to_lo(), format!("<{self_type} as "))]; + if let Some(generic_arg) = trait_path_segment.args { + let between_span = + trait_path_segment.ident.span.between(generic_arg.span_ext); + // get rid of :: between Trait and + // must be '::' between them, otherwise the parser won't accept the code + suggestions.push((between_span, "".to_string())); + suggestions + .push((generic_arg.span_ext.shrink_to_hi(), ">".to_string())); + } else { + suggestions.push(( + trait_path_segment.ident.span.shrink_to_hi(), + ">".to_string(), + )); + } + err.multipart_suggestion( + message, + suggestions, + Applicability::MaybeIncorrect, + ); + } + } + }; + + err + } + + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { + // Same hacky approach as above to avoid deluging user + // with error messages. + + if let Err(e) = arg.error_reported() { + return e; + } + if let Some(e) = self.tainted_by_errors() { + return e; + } + + self.emit_inference_failure_err( + obligation.cause.body_id, + span, + arg, + TypeAnnotationNeeded::E0282, + false, + ) + } + + ty::PredicateKind::Subtype(data) => { + if let Err(e) = data.error_reported() { + return e; + } + if let Some(e) = self.tainted_by_errors() { + return e; + } + let ty::SubtypePredicate { a_is_expected: _, a, b } = data; + // both must be type variables, or the other would've been instantiated + assert!(a.is_ty_var() && b.is_ty_var()); + self.emit_inference_failure_err( + obligation.cause.body_id, + span, + a.into(), + TypeAnnotationNeeded::E0282, + true, + ) + } + ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => { + if let Err(e) = predicate.error_reported() { + return e; + } + if let Some(e) = self.tainted_by_errors() { + return e; + } + + if let Err(guar) = + self.tcx.ensure().coherent_trait(self.tcx.parent(data.projection_term.def_id)) + { + // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case + // other `Foo` impls are incoherent. + return guar; + } + let arg = data + .projection_term + .args + .iter() + .chain(Some(data.term.into_arg())) + .find(|g| g.has_non_region_infer()); + if let Some(arg) = arg { + self.emit_inference_failure_err( + obligation.cause.body_id, + span, + arg, + TypeAnnotationNeeded::E0284, + true, + ) + .with_note(format!("cannot satisfy `{predicate}`")) + } else { + // If we can't find a generic parameter, just print a generic error + struct_span_code_err!( + self.dcx(), + span, + E0284, + "type annotations needed: cannot satisfy `{}`", + predicate, + ) + .with_span_label(span, format!("cannot satisfy `{predicate}`")) + } + } + + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(data)) => { + if let Err(e) = predicate.error_reported() { + return e; + } + if let Some(e) = self.tainted_by_errors() { + return e; + } + let arg = data.walk().find(|g| g.is_non_region_infer()); + if let Some(arg) = arg { + let err = self.emit_inference_failure_err( + obligation.cause.body_id, + span, + arg, + TypeAnnotationNeeded::E0284, + true, + ); + err + } else { + // If we can't find a generic parameter, just print a generic error + struct_span_code_err!( + self.dcx(), + span, + E0284, + "type annotations needed: cannot satisfy `{}`", + predicate, + ) + .with_span_label(span, format!("cannot satisfy `{predicate}`")) + } + } + + ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ..)) => self + .emit_inference_failure_err( + obligation.cause.body_id, + span, + ct.into(), + TypeAnnotationNeeded::E0284, + true, + ), + ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term }) + if term.is_infer() => + { + if let Some(e) = self.tainted_by_errors() { + return e; + } + struct_span_code_err!( + self.dcx(), + span, + E0284, + "type annotations needed: cannot normalize `{alias}`", + ) + .with_span_label(span, format!("cannot normalize `{alias}`")) + } + + _ => { + if let Some(e) = self.tainted_by_errors() { + return e; + } + struct_span_code_err!( + self.dcx(), + span, + E0284, + "type annotations needed: cannot satisfy `{}`", + predicate, + ) + .with_span_label(span, format!("cannot satisfy `{predicate}`")) + } + }; + self.note_obligation_cause(&mut err, obligation); + err.emit() + } + + fn annotate_source_of_ambiguity( + &self, + err: &mut Diag<'_>, + ambiguities: &[CandidateSource], + predicate: ty::Predicate<'tcx>, + ) { + let mut spans = vec![]; + let mut crates = vec![]; + let mut post = vec![]; + let mut has_param_env = false; + for ambiguity in ambiguities { + match ambiguity { + CandidateSource::DefId(impl_def_id) => match self.tcx.span_of_impl(*impl_def_id) { + Ok(span) => spans.push(span), + Err(name) => { + crates.push(name); + if let Some(header) = to_pretty_impl_header(self.tcx, *impl_def_id) { + post.push(header); + } + } + }, + CandidateSource::ParamEnv(span) => { + has_param_env = true; + spans.push(*span); + } + } + } + let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{n}`")).collect(); + crate_names.sort(); + crate_names.dedup(); + post.sort(); + post.dedup(); + + if self.tainted_by_errors().is_some() + && (crate_names.len() == 1 + && spans.len() == 0 + && ["`core`", "`alloc`", "`std`"].contains(&crate_names[0].as_str()) + || predicate.visit_with(&mut HasNumericInferVisitor).is_break()) + { + // Avoid complaining about other inference issues for expressions like + // `42 >> 1`, where the types are still `{integer}`, but we want to + // Do we need `trait_ref.skip_binder().self_ty().is_numeric() &&` too? + // NOTE(eddyb) this was `.cancel()`, but `err` + // is borrowed, so we can't fully defuse it. + err.downgrade_to_delayed_bug(); + return; + } + + let msg = format!( + "multiple `impl`s{} satisfying `{}` found", + if has_param_env { " or `where` clauses" } else { "" }, + predicate + ); + let post = if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) { + format!(":\n{}", post.iter().map(|p| format!("- {p}")).collect::>().join("\n"),) + } else if post.len() == 1 { + format!(": `{}`", post[0]) + } else { + String::new() + }; + + match (spans.len(), crates.len(), crate_names.len()) { + (0, 0, 0) => { + err.note(format!("cannot satisfy `{predicate}`")); + } + (0, _, 1) => { + err.note(format!("{} in the `{}` crate{}", msg, crates[0], post,)); + } + (0, _, _) => { + err.note(format!( + "{} in the following crates: {}{}", + msg, + crate_names.join(", "), + post, + )); + } + (_, 0, 0) => { + let span: MultiSpan = spans.into(); + err.span_note(span, msg); + } + (_, 1, 1) => { + let span: MultiSpan = spans.into(); + err.span_note(span, msg); + err.note(format!("and another `impl` found in the `{}` crate{}", crates[0], post,)); + } + _ => { + let span: MultiSpan = spans.into(); + err.span_note(span, msg); + err.note(format!( + "and more `impl`s found in the following crates: {}{}", + crate_names.join(", "), + post, + )); + } + } + } +} + +struct HasNumericInferVisitor; + +impl<'tcx> ty::TypeVisitor> for HasNumericInferVisitor { + type Result = ControlFlow<()>; + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { + if matches!(ty.kind(), ty::Infer(ty::FloatVar(_) | ty::IntVar(_))) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + } +} diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index 7d334d623cc24..e776248e68476 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -15,7 +15,6 @@ use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode, Pred use rustc_middle::ty::print::PrintTraitRefExt as _; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; -use std::ops::ControlFlow; pub use self::infer_ctxt_ext::*; pub use self::overflow::*; @@ -91,49 +90,6 @@ impl<'v> Visitor<'v> for FindExprBySpan<'v> { } } -/// Look for type `param` in an ADT being used only through a reference to confirm that suggesting -/// `param: ?Sized` would be a valid constraint. -struct FindTypeParam { - param: rustc_span::Symbol, - invalid_spans: Vec, - nested: bool, -} - -impl<'v> Visitor<'v> for FindTypeParam { - fn visit_where_predicate(&mut self, _: &'v hir::WherePredicate<'v>) { - // Skip where-clauses, to avoid suggesting indirection for type parameters found there. - } - - fn visit_ty(&mut self, ty: &hir::Ty<'_>) { - // We collect the spans of all uses of the "bare" type param, like in `field: T` or - // `field: (T, T)` where we could make `T: ?Sized` while skipping cases that are known to be - // valid like `field: &'a T` or `field: *mut T` and cases that *might* have further `Sized` - // obligations like `Box` and `Vec`, but we perform no extra analysis for those cases - // and suggest `T: ?Sized` regardless of their obligations. This is fine because the errors - // in that case should make what happened clear enough. - match ty.kind { - hir::TyKind::Ptr(_) | hir::TyKind::Ref(..) | hir::TyKind::TraitObject(..) => {} - hir::TyKind::Path(hir::QPath::Resolved(None, path)) - if path.segments.len() == 1 && path.segments[0].ident.name == self.param => - { - if !self.nested { - debug!(?ty, "FindTypeParam::visit_ty"); - self.invalid_spans.push(ty.span); - } - } - hir::TyKind::Path(_) => { - let prev = self.nested; - self.nested = true; - hir::intravisit::walk_ty(self, ty); - self.nested = prev; - } - _ => { - hir::intravisit::walk_ty(self, ty); - } - } - } -} - /// Summarizes information #[derive(Clone)] pub enum ArgKind { @@ -165,20 +121,6 @@ impl ArgKind { } } -struct HasNumericInferVisitor; - -impl<'tcx> ty::TypeVisitor> for HasNumericInferVisitor { - type Result = ControlFlow<()>; - - fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { - if matches!(ty.kind(), ty::Infer(ty::FloatVar(_) | ty::IntVar(_))) { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } - } -} - #[derive(Copy, Clone)] pub enum DefIdOrName { DefId(DefId), diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index fdc6db4abec0b..11a2b1d323379 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -4623,6 +4623,132 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); } } + + #[instrument(level = "debug", skip_all)] + fn suggest_unsized_bound_if_applicable( + &self, + err: &mut Diag<'_>, + obligation: &PredicateObligation<'tcx>, + ) { + let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) = + obligation.predicate.kind().skip_binder() + else { + return; + }; + let (ObligationCauseCode::WhereClause(item_def_id, span) + | ObligationCauseCode::WhereClauseInExpr(item_def_id, span, ..)) = + *obligation.cause.code().peel_derives() + else { + return; + }; + if span.is_dummy() { + return; + } + debug!(?pred, ?item_def_id, ?span); + + let (Some(node), true) = ( + self.tcx.hir().get_if_local(item_def_id), + self.tcx.is_lang_item(pred.def_id(), LangItem::Sized), + ) else { + return; + }; + + let Some(generics) = node.generics() else { + return; + }; + let sized_trait = self.tcx.lang_items().sized_trait(); + debug!(?generics.params); + debug!(?generics.predicates); + let Some(param) = generics.params.iter().find(|param| param.span == span) else { + return; + }; + // Check that none of the explicit trait bounds is `Sized`. Assume that an explicit + // `Sized` bound is there intentionally and we don't need to suggest relaxing it. + let explicitly_sized = generics + .bounds_for_param(param.def_id) + .flat_map(|bp| bp.bounds) + .any(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) == sized_trait); + if explicitly_sized { + return; + } + debug!(?param); + match node { + hir::Node::Item( + item @ hir::Item { + // Only suggest indirection for uses of type parameters in ADTs. + kind: + hir::ItemKind::Enum(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Union(..), + .. + }, + ) => { + if self.suggest_indirection_for_unsized(err, item, param) { + return; + } + } + _ => {} + }; + + // Didn't add an indirection suggestion, so add a general suggestion to relax `Sized`. + let (span, separator, open_paren_sp) = + if let Some((s, open_paren_sp)) = generics.bounds_span_for_suggestions(param.def_id) { + (s, " +", open_paren_sp) + } else { + (param.name.ident().span.shrink_to_hi(), ":", None) + }; + + let mut suggs = vec![]; + let suggestion = format!("{separator} ?Sized"); + + if let Some(open_paren_sp) = open_paren_sp { + suggs.push((open_paren_sp, "(".to_string())); + suggs.push((span, format!("){suggestion}"))); + } else { + suggs.push((span, suggestion)); + } + + err.multipart_suggestion_verbose( + "consider relaxing the implicit `Sized` restriction", + suggs, + Applicability::MachineApplicable, + ); + } + + fn suggest_indirection_for_unsized( + &self, + err: &mut Diag<'_>, + item: &hir::Item<'tcx>, + param: &hir::GenericParam<'tcx>, + ) -> bool { + // Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a + // borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S(T);` + // is not. Look for invalid "bare" parameter uses, and suggest using indirection. + let mut visitor = + FindTypeParam { param: param.name.ident().name, invalid_spans: vec![], nested: false }; + visitor.visit_item(item); + if visitor.invalid_spans.is_empty() { + return false; + } + let mut multispan: MultiSpan = param.span.into(); + multispan.push_span_label( + param.span, + format!("this could be changed to `{}: ?Sized`...", param.name.ident()), + ); + for sp in visitor.invalid_spans { + multispan.push_span_label( + sp, + format!("...if indirection were used here: `Box<{}>`", param.name.ident()), + ); + } + err.span_help( + multispan, + format!( + "you could relax the implicit `Sized` bound on `{T}` if it were \ + used through indirection like `&{T}` or `Box<{T}>`", + T = param.name.ident(), + ), + ); + true + } } /// Add a hint to add a missing borrow or remove an unnecessary one. @@ -5126,3 +5252,46 @@ fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec) { (ty, refs) } + +/// Look for type `param` in an ADT being used only through a reference to confirm that suggesting +/// `param: ?Sized` would be a valid constraint. +struct FindTypeParam { + param: rustc_span::Symbol, + invalid_spans: Vec, + nested: bool, +} + +impl<'v> Visitor<'v> for FindTypeParam { + fn visit_where_predicate(&mut self, _: &'v hir::WherePredicate<'v>) { + // Skip where-clauses, to avoid suggesting indirection for type parameters found there. + } + + fn visit_ty(&mut self, ty: &hir::Ty<'_>) { + // We collect the spans of all uses of the "bare" type param, like in `field: T` or + // `field: (T, T)` where we could make `T: ?Sized` while skipping cases that are known to be + // valid like `field: &'a T` or `field: *mut T` and cases that *might* have further `Sized` + // obligations like `Box` and `Vec`, but we perform no extra analysis for those cases + // and suggest `T: ?Sized` regardless of their obligations. This is fine because the errors + // in that case should make what happened clear enough. + match ty.kind { + hir::TyKind::Ptr(_) | hir::TyKind::Ref(..) | hir::TyKind::TraitObject(..) => {} + hir::TyKind::Path(hir::QPath::Resolved(None, path)) + if path.segments.len() == 1 && path.segments[0].ident.name == self.param => + { + if !self.nested { + debug!(?ty, "FindTypeParam::visit_ty"); + self.invalid_spans.push(ty.span); + } + } + hir::TyKind::Path(_) => { + let prev = self.nested; + self.nested = true; + hir::intravisit::walk_ty(self, ty); + self.nested = prev; + } + _ => { + hir::intravisit::walk_ty(self, ty); + } + } + } +} diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/type_err_ctxt_ext.rs index ee5f4f44251c0..dec3b26420c24 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/type_err_ctxt_ext.rs @@ -1,15 +1,12 @@ -// ignore-tidy-filelength :( - +use super::ambiguity::TypeErrCtxtAmbiguityExt as _; use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote, TypeErrCtxtExt as _}; use super::suggestions::{get_explanation_based_on_obligation, TypeErrCtxtExt as _}; use crate::error_reporting::traits::infer_ctxt_ext::InferCtxtExt; use crate::error_reporting::traits::overflow::TypeErrCtxtOverflowExt; -use crate::error_reporting::traits::to_pretty_impl_header; -use crate::error_reporting::traits::{ambiguity, ambiguity::CandidateSource::*}; use crate::errors::{ AsyncClosureNotFn, ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch, }; -use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; +use crate::infer::error_reporting::TyCategory; use crate::infer::InferCtxtExt as _; use crate::infer::{self, InferCtxt}; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; @@ -23,13 +20,13 @@ use core::ops::ControlFlow; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; -use rustc_errors::{pluralize, struct_span_code_err, Applicability, MultiSpan, StringPart}; +use rustc_errors::{pluralize, struct_span_code_err, Applicability, StringPart}; use rustc_errors::{Diag, ErrorGuaranteed, StashKey}; -use rustc_hir::def::{DefKind, Namespace, Res}; +use rustc_hir::def::Namespace; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; +use rustc_hir::Node; use rustc_hir::{self as hir, LangItem}; -use rustc_hir::{GenericParam, Item, Node}; use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::{InferOk, TypeTrace}; use rustc_macros::extension; @@ -43,8 +40,7 @@ use rustc_middle::ty::print::{ PrintTraitRefExt as _, }; use rustc_middle::ty::{ - self, SubtypePredicate, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitable, - TypeVisitableExt, Upcast, + self, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast, }; use rustc_middle::{bug, span_bug}; use rustc_span::symbol::sym; @@ -53,8 +49,7 @@ use std::borrow::Cow; use std::iter; use super::{ - ArgKind, CandidateSimilarity, FindExprBySpan, FindTypeParam, GetSafeTransmuteErrorAndReason, - HasNumericInferVisitor, ImplCandidate, UnsatisfiedConst, + ArgKind, CandidateSimilarity, GetSafeTransmuteErrorAndReason, ImplCandidate, UnsatisfiedConst, }; pub use rustc_infer::traits::error_reporting::*; @@ -956,7 +951,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } /// When the `E` of the resulting `Result` in an expression `foo().bar().baz()?`, - /// identify thoe method chain sub-expressions that could or could not have been annotated + /// identify those method chain sub-expressions that could or could not have been annotated /// with `?`. fn try_conversion_context( &self, @@ -2172,536 +2167,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { Obligation::new(self.tcx, ObligationCause::dummy(), param_env, trait_pred) } - #[instrument(skip(self), level = "debug")] - fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>) -> ErrorGuaranteed { - // Unable to successfully determine, probably means - // insufficient type information, but could mean - // ambiguous impls. The latter *ought* to be a - // coherence violation, so we don't report it here. - - let predicate = self.resolve_vars_if_possible(obligation.predicate); - let span = obligation.cause.span; - - debug!(?predicate, obligation.cause.code = ?obligation.cause.code()); - - // Ambiguity errors are often caused as fallout from earlier errors. - // We ignore them if this `infcx` is tainted in some cases below. - - let bound_predicate = predicate.kind(); - let mut err = match bound_predicate.skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { - let trait_ref = bound_predicate.rebind(data.trait_ref); - debug!(?trait_ref); - - if let Err(e) = predicate.error_reported() { - return e; - } - - if let Err(guar) = self.tcx.ensure().coherent_trait(trait_ref.def_id()) { - // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case - // other `Foo` impls are incoherent. - return guar; - } - - // This is kind of a hack: it frequently happens that some earlier - // error prevents types from being fully inferred, and then we get - // a bunch of uninteresting errors saying something like " doesn't implement Sized". It may even be true that we - // could just skip over all checks where the self-ty is an - // inference variable, but I was afraid that there might be an - // inference variable created, registered as an obligation, and - // then never forced by writeback, and hence by skipping here we'd - // be ignoring the fact that we don't KNOW the type works - // out. Though even that would probably be harmless, given that - // we're only talking about builtin traits, which are known to be - // inhabited. We used to check for `self.tcx.sess.has_errors()` to - // avoid inundating the user with unnecessary errors, but we now - // check upstream for type errors and don't add the obligations to - // begin with in those cases. - if self.tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) { - match self.tainted_by_errors() { - None => { - let err = self.emit_inference_failure_err( - obligation.cause.body_id, - span, - trait_ref.self_ty().skip_binder().into(), - ErrorCode::E0282, - false, - ); - return err.stash(span, StashKey::MaybeForgetReturn).unwrap(); - } - Some(e) => return e, - } - } - - // Typically, this ambiguity should only happen if - // there are unresolved type inference variables - // (otherwise it would suggest a coherence - // failure). But given #21974 that is not necessarily - // the case -- we can have multiple where clauses that - // are only distinguished by a region, which results - // in an ambiguity even when all types are fully - // known, since we don't dispatch based on region - // relationships. - - // Pick the first generic parameter that still contains inference variables as the one - // we're going to emit an error for. If there are none (see above), fall back to - // a more general error. - let arg = data.trait_ref.args.iter().find(|s| s.has_non_region_infer()); - - let mut err = if let Some(arg) = arg { - self.emit_inference_failure_err( - obligation.cause.body_id, - span, - arg, - ErrorCode::E0283, - true, - ) - } else { - struct_span_code_err!( - self.dcx(), - span, - E0283, - "type annotations needed: cannot satisfy `{}`", - predicate, - ) - }; - - let mut ambiguities = ambiguity::compute_applicable_impls_for_diagnostics( - self.infcx, - &obligation.with(self.tcx, trait_ref), - ); - let has_non_region_infer = - trait_ref.skip_binder().args.types().any(|t| !t.is_ty_or_numeric_infer()); - // It doesn't make sense to talk about applicable impls if there are more than a - // handful of them. If there are a lot of them, but only a few of them have no type - // params, we only show those, as they are more likely to be useful/intended. - if ambiguities.len() > 5 { - let infcx = self.infcx; - if !ambiguities.iter().all(|option| match option { - DefId(did) => infcx.tcx.generics_of(*did).count() == 0, - ParamEnv(_) => true, - }) { - // If not all are blanket impls, we filter blanked impls out. - ambiguities.retain(|option| match option { - DefId(did) => infcx.tcx.generics_of(*did).count() == 0, - ParamEnv(_) => true, - }); - } - } - if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer { - if let Some(e) = self.tainted_by_errors() - && arg.is_none() - { - // If `arg.is_none()`, then this is probably two param-env - // candidates or impl candidates that are equal modulo lifetimes. - // Therefore, if we've already emitted an error, just skip this - // one, since it's not particularly actionable. - err.cancel(); - return e; - } - self.annotate_source_of_ambiguity(&mut err, &ambiguities, predicate); - } else { - if let Some(e) = self.tainted_by_errors() { - err.cancel(); - return e; - } - err.note(format!("cannot satisfy `{predicate}`")); - let impl_candidates = - self.find_similar_impl_candidates(predicate.as_trait_clause().unwrap()); - if impl_candidates.len() < 40 { - self.report_similar_impl_candidates( - impl_candidates.as_slice(), - trait_ref, - obligation.cause.body_id, - &mut err, - false, - obligation.param_env, - ); - } - } - - if let ObligationCauseCode::WhereClause(def_id, _) - | ObligationCauseCode::WhereClauseInExpr(def_id, ..) = *obligation.cause.code() - { - self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id()); - } - - if let Some(ty::GenericArgKind::Type(_)) = arg.map(|arg| arg.unpack()) - && let Some(body) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) - { - let mut expr_finder = FindExprBySpan::new(span, self.tcx); - expr_finder.visit_expr(&body.value); - - if let Some(hir::Expr { - kind: - hir::ExprKind::Call( - hir::Expr { - kind: hir::ExprKind::Path(hir::QPath::Resolved(None, path)), - .. - }, - _, - ) - | hir::ExprKind::Path(hir::QPath::Resolved(None, path)), - .. - }) = expr_finder.result - && let [ - .., - trait_path_segment @ hir::PathSegment { - res: Res::Def(DefKind::Trait, trait_id), - .. - }, - hir::PathSegment { - ident: assoc_item_name, - res: Res::Def(_, item_id), - .. - }, - ] = path.segments - && data.trait_ref.def_id == *trait_id - && self.tcx.trait_of_item(*item_id) == Some(*trait_id) - && let None = self.tainted_by_errors() - { - let (verb, noun) = match self.tcx.associated_item(item_id).kind { - ty::AssocKind::Const => ("refer to the", "constant"), - ty::AssocKind::Fn => ("call", "function"), - // This is already covered by E0223, but this following single match - // arm doesn't hurt here. - ty::AssocKind::Type => ("refer to the", "type"), - }; - - // Replace the more general E0283 with a more specific error - err.cancel(); - err = self.dcx().struct_span_err( - span, - format!( - "cannot {verb} associated {noun} on trait without specifying the \ - corresponding `impl` type", - ), - ); - err.code(E0790); - - if let Some(local_def_id) = data.trait_ref.def_id.as_local() - && let hir::Node::Item(hir::Item { - ident: trait_name, - kind: hir::ItemKind::Trait(_, _, _, _, trait_item_refs), - .. - }) = self.tcx.hir_node_by_def_id(local_def_id) - && let Some(method_ref) = trait_item_refs - .iter() - .find(|item_ref| item_ref.ident == *assoc_item_name) - { - err.span_label( - method_ref.span, - format!("`{trait_name}::{assoc_item_name}` defined here"), - ); - } - - err.span_label(span, format!("cannot {verb} associated {noun} of trait")); - - let trait_impls = self.tcx.trait_impls_of(data.trait_ref.def_id); - - if let Some(impl_def_id) = - trait_impls.non_blanket_impls().values().flatten().next() - { - let non_blanket_impl_count = - trait_impls.non_blanket_impls().values().flatten().count(); - // If there is only one implementation of the trait, suggest using it. - // Otherwise, use a placeholder comment for the implementation. - let (message, self_type) = if non_blanket_impl_count == 1 { - ( - "use the fully-qualified path to the only available \ - implementation", - format!( - "{}", - self.tcx.type_of(impl_def_id).instantiate_identity() - ), - ) - } else { - ( - "use a fully-qualified path to a specific available \ - implementation", - "/* self type */".to_string(), - ) - }; - let mut suggestions = - vec![(path.span.shrink_to_lo(), format!("<{self_type} as "))]; - if let Some(generic_arg) = trait_path_segment.args { - let between_span = - trait_path_segment.ident.span.between(generic_arg.span_ext); - // get rid of :: between Trait and - // must be '::' between them, otherwise the parser won't accept the code - suggestions.push((between_span, "".to_string())); - suggestions - .push((generic_arg.span_ext.shrink_to_hi(), ">".to_string())); - } else { - suggestions.push(( - trait_path_segment.ident.span.shrink_to_hi(), - ">".to_string(), - )); - } - err.multipart_suggestion( - message, - suggestions, - Applicability::MaybeIncorrect, - ); - } - } - }; - - err - } - - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { - // Same hacky approach as above to avoid deluging user - // with error messages. - - if let Err(e) = arg.error_reported() { - return e; - } - if let Some(e) = self.tainted_by_errors() { - return e; - } - - self.emit_inference_failure_err( - obligation.cause.body_id, - span, - arg, - ErrorCode::E0282, - false, - ) - } - - ty::PredicateKind::Subtype(data) => { - if let Err(e) = data.error_reported() { - return e; - } - if let Some(e) = self.tainted_by_errors() { - return e; - } - let SubtypePredicate { a_is_expected: _, a, b } = data; - // both must be type variables, or the other would've been instantiated - assert!(a.is_ty_var() && b.is_ty_var()); - self.emit_inference_failure_err( - obligation.cause.body_id, - span, - a.into(), - ErrorCode::E0282, - true, - ) - } - ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => { - if let Err(e) = predicate.error_reported() { - return e; - } - if let Some(e) = self.tainted_by_errors() { - return e; - } - - if let Err(guar) = - self.tcx.ensure().coherent_trait(self.tcx.parent(data.projection_term.def_id)) - { - // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case - // other `Foo` impls are incoherent. - return guar; - } - let arg = data - .projection_term - .args - .iter() - .chain(Some(data.term.into_arg())) - .find(|g| g.has_non_region_infer()); - if let Some(arg) = arg { - self.emit_inference_failure_err( - obligation.cause.body_id, - span, - arg, - ErrorCode::E0284, - true, - ) - .with_note(format!("cannot satisfy `{predicate}`")) - } else { - // If we can't find a generic parameter, just print a generic error - struct_span_code_err!( - self.dcx(), - span, - E0284, - "type annotations needed: cannot satisfy `{}`", - predicate, - ) - .with_span_label(span, format!("cannot satisfy `{predicate}`")) - } - } - - ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(data)) => { - if let Err(e) = predicate.error_reported() { - return e; - } - if let Some(e) = self.tainted_by_errors() { - return e; - } - let arg = data.walk().find(|g| g.is_non_region_infer()); - if let Some(arg) = arg { - let err = self.emit_inference_failure_err( - obligation.cause.body_id, - span, - arg, - ErrorCode::E0284, - true, - ); - err - } else { - // If we can't find a generic parameter, just print a generic error - struct_span_code_err!( - self.dcx(), - span, - E0284, - "type annotations needed: cannot satisfy `{}`", - predicate, - ) - .with_span_label(span, format!("cannot satisfy `{predicate}`")) - } - } - - ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ..)) => self - .emit_inference_failure_err( - obligation.cause.body_id, - span, - ct.into(), - ErrorCode::E0284, - true, - ), - ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term }) - if term.is_infer() => - { - if let Some(e) = self.tainted_by_errors() { - return e; - } - struct_span_code_err!( - self.dcx(), - span, - E0284, - "type annotations needed: cannot normalize `{alias}`", - ) - .with_span_label(span, format!("cannot normalize `{alias}`")) - } - - _ => { - if let Some(e) = self.tainted_by_errors() { - return e; - } - struct_span_code_err!( - self.dcx(), - span, - E0284, - "type annotations needed: cannot satisfy `{}`", - predicate, - ) - .with_span_label(span, format!("cannot satisfy `{predicate}`")) - } - }; - self.note_obligation_cause(&mut err, obligation); - err.emit() - } - - fn annotate_source_of_ambiguity( - &self, - err: &mut Diag<'_>, - ambiguities: &[ambiguity::CandidateSource], - predicate: ty::Predicate<'tcx>, - ) { - let mut spans = vec![]; - let mut crates = vec![]; - let mut post = vec![]; - let mut has_param_env = false; - for ambiguity in ambiguities { - match ambiguity { - ambiguity::CandidateSource::DefId(impl_def_id) => { - match self.tcx.span_of_impl(*impl_def_id) { - Ok(span) => spans.push(span), - Err(name) => { - crates.push(name); - if let Some(header) = to_pretty_impl_header(self.tcx, *impl_def_id) { - post.push(header); - } - } - } - } - ambiguity::CandidateSource::ParamEnv(span) => { - has_param_env = true; - spans.push(*span); - } - } - } - let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{n}`")).collect(); - crate_names.sort(); - crate_names.dedup(); - post.sort(); - post.dedup(); - - if self.tainted_by_errors().is_some() - && (crate_names.len() == 1 - && spans.len() == 0 - && ["`core`", "`alloc`", "`std`"].contains(&crate_names[0].as_str()) - || predicate.visit_with(&mut HasNumericInferVisitor).is_break()) - { - // Avoid complaining about other inference issues for expressions like - // `42 >> 1`, where the types are still `{integer}`, but we want to - // Do we need `trait_ref.skip_binder().self_ty().is_numeric() &&` too? - // NOTE(eddyb) this was `.cancel()`, but `err` - // is borrowed, so we can't fully defuse it. - err.downgrade_to_delayed_bug(); - return; - } - - let msg = format!( - "multiple `impl`s{} satisfying `{}` found", - if has_param_env { " or `where` clauses" } else { "" }, - predicate - ); - let post = if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) { - format!(":\n{}", post.iter().map(|p| format!("- {p}")).collect::>().join("\n"),) - } else if post.len() == 1 { - format!(": `{}`", post[0]) - } else { - String::new() - }; - - match (spans.len(), crates.len(), crate_names.len()) { - (0, 0, 0) => { - err.note(format!("cannot satisfy `{predicate}`")); - } - (0, _, 1) => { - err.note(format!("{} in the `{}` crate{}", msg, crates[0], post,)); - } - (0, _, _) => { - err.note(format!( - "{} in the following crates: {}{}", - msg, - crate_names.join(", "), - post, - )); - } - (_, 0, 0) => { - let span: MultiSpan = spans.into(); - err.span_note(span, msg); - } - (_, 1, 1) => { - let span: MultiSpan = spans.into(); - err.span_note(span, msg); - err.note(format!("and another `impl` found in the `{}` crate{}", crates[0], post,)); - } - _ => { - let span: MultiSpan = spans.into(); - err.span_note(span, msg); - err.note(format!( - "and more `impl`s found in the following crates: {}{}", - crate_names.join(", "), - post, - )); - } - } - } - /// Returns `true` if the trait predicate may apply for *some* assignment /// to the type parameters. fn predicate_can_apply( @@ -2769,136 +2234,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - #[instrument(level = "debug", skip_all)] - fn suggest_unsized_bound_if_applicable( - &self, - err: &mut Diag<'_>, - obligation: &PredicateObligation<'tcx>, - ) { - let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) = - obligation.predicate.kind().skip_binder() - else { - return; - }; - let (ObligationCauseCode::WhereClause(item_def_id, span) - | ObligationCauseCode::WhereClauseInExpr(item_def_id, span, ..)) = - *obligation.cause.code().peel_derives() - else { - return; - }; - if span.is_dummy() { - return; - } - debug!(?pred, ?item_def_id, ?span); - - let (Some(node), true) = ( - self.tcx.hir().get_if_local(item_def_id), - self.tcx.is_lang_item(pred.def_id(), LangItem::Sized), - ) else { - return; - }; - self.maybe_suggest_unsized_generics(err, span, node); - } - - #[instrument(level = "debug", skip_all)] - fn maybe_suggest_unsized_generics(&self, err: &mut Diag<'_>, span: Span, node: Node<'tcx>) { - let Some(generics) = node.generics() else { - return; - }; - let sized_trait = self.tcx.lang_items().sized_trait(); - debug!(?generics.params); - debug!(?generics.predicates); - let Some(param) = generics.params.iter().find(|param| param.span == span) else { - return; - }; - // Check that none of the explicit trait bounds is `Sized`. Assume that an explicit - // `Sized` bound is there intentionally and we don't need to suggest relaxing it. - let explicitly_sized = generics - .bounds_for_param(param.def_id) - .flat_map(|bp| bp.bounds) - .any(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) == sized_trait); - if explicitly_sized { - return; - } - debug!(?param); - match node { - hir::Node::Item( - item @ hir::Item { - // Only suggest indirection for uses of type parameters in ADTs. - kind: - hir::ItemKind::Enum(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Union(..), - .. - }, - ) => { - if self.maybe_indirection_for_unsized(err, item, param) { - return; - } - } - _ => {} - }; - - // Didn't add an indirection suggestion, so add a general suggestion to relax `Sized`. - let (span, separator, open_paren_sp) = - if let Some((s, open_paren_sp)) = generics.bounds_span_for_suggestions(param.def_id) { - (s, " +", open_paren_sp) - } else { - (param.name.ident().span.shrink_to_hi(), ":", None) - }; - - let mut suggs = vec![]; - let suggestion = format!("{separator} ?Sized"); - - if let Some(open_paren_sp) = open_paren_sp { - suggs.push((open_paren_sp, "(".to_string())); - suggs.push((span, format!("){suggestion}"))); - } else { - suggs.push((span, suggestion)); - } - - err.multipart_suggestion_verbose( - "consider relaxing the implicit `Sized` restriction", - suggs, - Applicability::MachineApplicable, - ); - } - - fn maybe_indirection_for_unsized( - &self, - err: &mut Diag<'_>, - item: &Item<'tcx>, - param: &GenericParam<'tcx>, - ) -> bool { - // Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a - // borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S(T);` - // is not. Look for invalid "bare" parameter uses, and suggest using indirection. - let mut visitor = - FindTypeParam { param: param.name.ident().name, invalid_spans: vec![], nested: false }; - visitor.visit_item(item); - if visitor.invalid_spans.is_empty() { - return false; - } - let mut multispan: MultiSpan = param.span.into(); - multispan.push_span_label( - param.span, - format!("this could be changed to `{}: ?Sized`...", param.name.ident()), - ); - for sp in visitor.invalid_spans { - multispan.push_span_label( - sp, - format!("...if indirection were used here: `Box<{}>`", param.name.ident()), - ); - } - err.span_help( - multispan, - format!( - "you could relax the implicit `Sized` bound on `{T}` if it were \ - used through indirection like `&{T}` or `Box<{T}>`", - T = param.name.ident(), - ), - ); - true - } - fn is_recursive_obligation( &self, obligated_types: &mut Vec>,