@@ -13,6 +13,7 @@ use rustc_middle::ty::TyKind::{Adt, Array, Char, FnDef, Never, Ref, Str, Tuple,
1313use rustc_middle:: ty:: { self , Ty , TypeFoldable } ;
1414use rustc_span:: Span ;
1515use rustc_trait_selection:: infer:: InferCtxtExt ;
16+ use rustc_trait_selection:: traits:: error_reporting:: suggest_constraining_type_param;
1617
1718impl < ' a , ' tcx > FnCtxt < ' a , ' tcx > {
1819 /// Checks a `a <op>= b`
@@ -253,6 +254,50 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
253254 // error types are considered "builtin"
254255 if !lhs_ty. references_error ( ) {
255256 let source_map = self . tcx . sess . source_map ( ) ;
257+
258+ let suggest_constraining_param =
259+ |mut err : & mut DiagnosticBuilder < ' _ > ,
260+ missing_trait : & str ,
261+ p : ty:: ParamTy ,
262+ set_output : bool | {
263+ let hir = self . tcx . hir ( ) ;
264+ let msg =
265+ & format ! ( "`{}` might need a bound for `{}`" , lhs_ty, missing_trait) ;
266+ if let Some ( def_id) = hir
267+ . find ( hir. get_parent_item ( expr. hir_id ) )
268+ . and_then ( |node| node. hir_id ( ) )
269+ . and_then ( |hir_id| hir. opt_local_def_id ( hir_id) )
270+ {
271+ let generics = self . tcx . generics_of ( def_id) ;
272+ let param_def_id = generics. type_param ( & p, self . tcx ) . def_id ;
273+ if let Some ( generics) = hir
274+ . as_local_hir_id ( param_def_id)
275+ . and_then ( |id| hir. find ( hir. get_parent_item ( id) ) )
276+ . as_ref ( )
277+ . and_then ( |node| node. generics ( ) )
278+ {
279+ let output = if set_output {
280+ format ! ( "<Output = {}>" , rhs_ty)
281+ } else {
282+ String :: new ( )
283+ } ;
284+ suggest_constraining_type_param (
285+ self . tcx ,
286+ generics,
287+ & mut err,
288+ & format ! ( "{}" , lhs_ty) ,
289+ & format ! ( "{}{}" , missing_trait, output) ,
290+ None ,
291+ ) ;
292+ } else {
293+ let span = self . tcx . def_span ( param_def_id) ;
294+ err. span_label ( span, msg) ;
295+ }
296+ } else {
297+ err. note ( & msg) ;
298+ }
299+ } ;
300+
256301 match is_assign {
257302 IsAssign :: Yes => {
258303 let mut err = struct_span_err ! (
@@ -317,59 +362,65 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
317362 // This has nothing here because it means we did string
318363 // concatenation (e.g., "Hello " += "World!"). This means
319364 // we don't want the note in the else clause to be emitted
320- } else if let ty:: Param ( _) = lhs_ty. kind {
321- // FIXME: point to span of param
322- err. note ( & format ! (
323- "`{}` might need a bound for `{}`" ,
324- lhs_ty, missing_trait
325- ) ) ;
365+ } else if let ty:: Param ( p) = lhs_ty. kind {
366+ suggest_constraining_param ( & mut err, missing_trait, p, false ) ;
326367 } else if !suggested_deref {
327368 suggest_impl_missing ( & mut err, lhs_ty, & missing_trait) ;
328369 }
329370 }
330371 err. emit ( ) ;
331372 }
332373 IsAssign :: No => {
333- let ( message, missing_trait) = match op. node {
374+ let ( message, missing_trait, use_output ) = match op. node {
334375 hir:: BinOpKind :: Add => (
335376 format ! ( "cannot add `{}` to `{}`" , rhs_ty, lhs_ty) ,
336377 Some ( "std::ops::Add" ) ,
378+ true ,
337379 ) ,
338380 hir:: BinOpKind :: Sub => (
339381 format ! ( "cannot subtract `{}` from `{}`" , rhs_ty, lhs_ty) ,
340382 Some ( "std::ops::Sub" ) ,
383+ true ,
341384 ) ,
342385 hir:: BinOpKind :: Mul => (
343386 format ! ( "cannot multiply `{}` to `{}`" , rhs_ty, lhs_ty) ,
344387 Some ( "std::ops::Mul" ) ,
388+ true ,
345389 ) ,
346390 hir:: BinOpKind :: Div => (
347391 format ! ( "cannot divide `{}` by `{}`" , lhs_ty, rhs_ty) ,
348392 Some ( "std::ops::Div" ) ,
393+ true ,
349394 ) ,
350395 hir:: BinOpKind :: Rem => (
351396 format ! ( "cannot mod `{}` by `{}`" , lhs_ty, rhs_ty) ,
352397 Some ( "std::ops::Rem" ) ,
398+ true ,
353399 ) ,
354400 hir:: BinOpKind :: BitAnd => (
355401 format ! ( "no implementation for `{} & {}`" , lhs_ty, rhs_ty) ,
356402 Some ( "std::ops::BitAnd" ) ,
403+ true ,
357404 ) ,
358405 hir:: BinOpKind :: BitXor => (
359406 format ! ( "no implementation for `{} ^ {}`" , lhs_ty, rhs_ty) ,
360407 Some ( "std::ops::BitXor" ) ,
408+ true ,
361409 ) ,
362410 hir:: BinOpKind :: BitOr => (
363411 format ! ( "no implementation for `{} | {}`" , lhs_ty, rhs_ty) ,
364412 Some ( "std::ops::BitOr" ) ,
413+ true ,
365414 ) ,
366415 hir:: BinOpKind :: Shl => (
367416 format ! ( "no implementation for `{} << {}`" , lhs_ty, rhs_ty) ,
368417 Some ( "std::ops::Shl" ) ,
418+ true ,
369419 ) ,
370420 hir:: BinOpKind :: Shr => (
371421 format ! ( "no implementation for `{} >> {}`" , lhs_ty, rhs_ty) ,
372422 Some ( "std::ops::Shr" ) ,
423+ true ,
373424 ) ,
374425 hir:: BinOpKind :: Eq | hir:: BinOpKind :: Ne => (
375426 format ! (
@@ -378,6 +429,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
378429 lhs_ty
379430 ) ,
380431 Some ( "std::cmp::PartialEq" ) ,
432+ false ,
381433 ) ,
382434 hir:: BinOpKind :: Lt
383435 | hir:: BinOpKind :: Le
@@ -389,6 +441,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
389441 lhs_ty
390442 ) ,
391443 Some ( "std::cmp::PartialOrd" ) ,
444+ false ,
392445 ) ,
393446 _ => (
394447 format ! (
@@ -397,6 +450,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
397450 lhs_ty
398451 ) ,
399452 None ,
453+ false ,
400454 ) ,
401455 } ;
402456 let mut err = struct_span_err ! (
@@ -459,12 +513,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
459513 // This has nothing here because it means we did string
460514 // concatenation (e.g., "Hello " + "World!"). This means
461515 // we don't want the note in the else clause to be emitted
462- } else if let ty:: Param ( _) = lhs_ty. kind {
463- // FIXME: point to span of param
464- err. note ( & format ! (
465- "`{}` might need a bound for `{}`" ,
466- lhs_ty, missing_trait
467- ) ) ;
516+ } else if let ty:: Param ( p) = lhs_ty. kind {
517+ suggest_constraining_param (
518+ & mut err,
519+ missing_trait,
520+ p,
521+ use_output,
522+ ) ;
468523 } else if !suggested_deref && !involves_fn {
469524 suggest_impl_missing ( & mut err, lhs_ty, & missing_trait) ;
470525 }
0 commit comments