diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index 89a6b98cd8f5..b2026266b511 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -10,7 +10,7 @@ use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, }; use rustc_middle::ty::TyKind::{Adt, Array, Char, FnDef, Never, Ref, Str, Tuple, Uint}; -use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TypeFoldable}; +use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt, TypeFoldable}; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; @@ -254,49 +254,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !lhs_ty.references_error() { let source_map = self.tcx.sess.source_map(); - let suggest_constraining_param = - |mut err: &mut DiagnosticBuilder<'_>, - missing_trait: &str, - p: ty::ParamTy, - set_output: bool| { - let hir = self.tcx.hir(); - let msg = - &format!("`{}` might need a bound for `{}`", lhs_ty, missing_trait); - if let Some(def_id) = hir - .find(hir.get_parent_item(expr.hir_id)) - .and_then(|node| node.hir_id()) - .and_then(|hir_id| hir.opt_local_def_id(hir_id)) - { - let generics = self.tcx.generics_of(def_id); - let param_def_id = generics.type_param(&p, self.tcx).def_id; - if let Some(generics) = hir - .as_local_hir_id(param_def_id) - .and_then(|id| hir.find(hir.get_parent_item(id))) - .as_ref() - .and_then(|node| node.generics()) - { - let output = if set_output { - format!("", rhs_ty) - } else { - String::new() - }; - suggest_constraining_type_param( - self.tcx, - generics, - &mut err, - &format!("{}", lhs_ty), - &format!("{}{}", missing_trait, output), - None, - ); - } else { - let span = self.tcx.def_span(param_def_id); - err.span_label(span, msg); - } - } else { - err.note(&msg); - } - }; - match is_assign { IsAssign::Yes => { let mut err = struct_span_err!( @@ -362,7 +319,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // concatenation (e.g., "Hello " += "World!"). This means // we don't want the note in the else clause to be emitted } else if let ty::Param(p) = lhs_ty.kind { - suggest_constraining_param(&mut err, missing_trait, p, false); + suggest_constraining_param( + self.tcx, + &mut err, + lhs_ty, + rhs_ty, + &expr, + missing_trait, + p, + false, + ); } else if !suggested_deref { suggest_impl_missing(&mut err, lhs_ty, &missing_trait); } @@ -514,7 +480,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // we don't want the note in the else clause to be emitted } else if let ty::Param(p) = lhs_ty.kind { suggest_constraining_param( + self.tcx, &mut err, + lhs_ty, + rhs_ty, + &expr, missing_trait, p, use_output, @@ -965,3 +935,49 @@ fn suggest_impl_missing(err: &mut DiagnosticBuilder<'_>, ty: Ty<'_>, missing_tra } } } + +fn suggest_constraining_param( + tcx: TyCtxt<'_>, + mut err: &mut DiagnosticBuilder<'_>, + lhs_ty: Ty<'_>, + rhs_ty: Ty<'_>, + expr: &hir::Expr<'_>, + missing_trait: &str, + p: ty::ParamTy, + set_output: bool, +) { + let hir = tcx.hir(); + let msg = &format!("`{}` might need a bound for `{}`", lhs_ty, missing_trait); + // Try to find the def-id and details for the parameter p. We have only the index, + // so we have to find the enclosing function's def-id, then look through its declared + // generic parameters to get the declaration. + if let Some(def_id) = hir + .find(hir.get_parent_item(expr.hir_id)) + .and_then(|node| node.hir_id()) + .and_then(|hir_id| hir.opt_local_def_id(hir_id)) + { + let generics = tcx.generics_of(def_id); + let param_def_id = generics.type_param(&p, tcx).def_id; + if let Some(generics) = hir + .as_local_hir_id(param_def_id) + .and_then(|id| hir.find(hir.get_parent_item(id))) + .as_ref() + .and_then(|node| node.generics()) + { + let output = if set_output { format!("", rhs_ty) } else { String::new() }; + suggest_constraining_type_param( + tcx, + generics, + &mut err, + &format!("{}", lhs_ty), + &format!("{}{}", missing_trait, output), + None, + ); + } else { + let span = tcx.def_span(param_def_id); + err.span_label(span, msg); + } + } else { + err.note(&msg); + } +} diff --git a/src/test/ui/issues/issue-20005.stderr b/src/test/ui/issues/issue-20005.stderr index 19ccf7076199..f53489a99f3b 100644 --- a/src/test/ui/issues/issue-20005.stderr +++ b/src/test/ui/issues/issue-20005.stderr @@ -5,12 +5,18 @@ LL | trait From { | --- required by this bound in `From` ... LL | ) -> >::Result where Dst: From { - | ^^^^^^^^^^- help: consider further restricting `Self`: `, Self: std::marker::Sized` - | | - | doesn't have a size known at compile-time + | ^^^^^^^^^^ doesn't have a size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `Self` = note: to learn more, visit +help: consider further restricting `Self` + | +LL | ) -> >::Result where Dst: From, Self: std::marker::Sized { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider relaxing the implicit `Sized` restriction + | +LL | trait From { + | ^^^^^^^^ error: aborting due to previous error