1+ use crate :: traits:: { ObligationCause , ObligationCauseCode } ;
12use crate :: ty:: { self , BoundRegion , Region , Ty , TyCtxt } ;
23use rustc_ast:: ast;
3- use rustc_errors:: { pluralize, Applicability , DiagnosticBuilder } ;
4+ use rustc_errors:: Applicability :: { MachineApplicable , MaybeIncorrect } ;
5+ use rustc_errors:: { pluralize, DiagnosticBuilder } ;
46use rustc_hir as hir;
57use rustc_hir:: def_id:: DefId ;
6- use rustc_span:: { BytePos , Span } ;
8+ use rustc_span:: { BytePos , MultiSpan , Span } ;
79use rustc_target:: spec:: abi;
810
911use std:: borrow:: Cow ;
@@ -323,6 +325,7 @@ impl<'tcx> TyCtxt<'tcx> {
323325 self ,
324326 db : & mut DiagnosticBuilder < ' _ > ,
325327 err : & TypeError < ' tcx > ,
328+ cause : & ObligationCause < ' tcx > ,
326329 sp : Span ,
327330 body_owner_def_id : DefId ,
328331 ) {
@@ -361,7 +364,7 @@ impl<'tcx> TyCtxt<'tcx> {
361364 sp,
362365 "use a float literal" ,
363366 format ! ( "{}.0" , snippet) ,
364- Applicability :: MachineApplicable ,
367+ MachineApplicable ,
365368 ) ;
366369 }
367370 }
@@ -442,41 +445,27 @@ impl<T> Trait<T> for X {
442445 db. span_label ( p_span, "this type parameter" ) ;
443446 }
444447 }
445- ( ty:: Projection ( _) , _) => {
446- db. note ( & format ! (
447- "consider constraining the associated type `{}` to `{}` or calling a \
448- method that returns `{0}`",
449- values. expected, values. found,
450- ) ) ;
451- if self . sess . teach ( & db. get_code ( ) . unwrap ( ) ) {
452- db. help (
453- "given an associated type `T` and a method `foo`:
454- ```
455- trait Trait {
456- type T;
457- fn foo(&self) -> Self::T;
458- }
459- ```
460- the only way of implementing method `foo` is to constrain `T` with an explicit associated type:
461- ```
462- impl Trait for X {
463- type T = String;
464- fn foo(&self) -> Self::T { String::new() }
465- }
466- ```" ,
467- ) ;
468- }
469- db. note (
470- "for more information, visit \
471- https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
448+ ( ty:: Projection ( proj_ty) , _) => {
449+ self . expected_projection (
450+ db,
451+ proj_ty,
452+ values,
453+ body_owner_def_id,
454+ & cause. code ,
472455 ) ;
473456 }
474457 ( _, ty:: Projection ( proj_ty) ) => {
475458 let msg = format ! (
476459 "consider constraining the associated type `{}` to `{}`" ,
477460 values. found, values. expected,
478461 ) ;
479- if !self . suggest_constraint ( db, & msg, body_owner_def_id, proj_ty, values) {
462+ if !self . suggest_constraint (
463+ db,
464+ & msg,
465+ body_owner_def_id,
466+ proj_ty,
467+ values. expected ,
468+ ) {
480469 db. help ( & msg) ;
481470 db. note (
482471 "for more information, visit \
@@ -512,7 +501,7 @@ impl Trait for X {
512501 msg : & str ,
513502 body_owner_def_id : DefId ,
514503 proj_ty : & ty:: ProjectionTy < ' tcx > ,
515- values : & ExpectedFound < Ty < ' tcx > > ,
504+ ty : Ty < ' tcx > ,
516505 ) -> bool {
517506 let assoc = self . associated_item ( proj_ty. item_def_id ) ;
518507 let trait_ref = proj_ty. trait_ref ( * self ) ;
@@ -549,7 +538,7 @@ impl Trait for X {
549538 & trait_ref,
550539 pred. bounds ,
551540 & assoc,
552- values ,
541+ ty ,
553542 msg,
554543 ) {
555544 return true ;
@@ -566,7 +555,7 @@ impl Trait for X {
566555 & trait_ref,
567556 param. bounds ,
568557 & assoc,
569- values ,
558+ ty ,
570559 msg,
571560 ) ;
572561 }
@@ -576,15 +565,145 @@ impl Trait for X {
576565 false
577566 }
578567
568+ fn expected_projection (
569+ & self ,
570+ db : & mut DiagnosticBuilder < ' _ > ,
571+ proj_ty : & ty:: ProjectionTy < ' tcx > ,
572+ values : & ExpectedFound < Ty < ' tcx > > ,
573+ body_owner_def_id : DefId ,
574+ cause_code : & ObligationCauseCode < ' _ > ,
575+ ) {
576+ let msg = format ! (
577+ "consider constraining the associated type `{}` to `{}`" ,
578+ values. expected, values. found
579+ ) ;
580+ let mut suggested = false ;
581+ let body_owner = self . hir ( ) . get_if_local ( body_owner_def_id) ;
582+ let current_method_ident = body_owner. and_then ( |n| n. ident ( ) ) . map ( |i| i. name ) ;
583+
584+ let callable_scope = match body_owner {
585+ Some (
586+ hir:: Node :: Item ( hir:: Item {
587+ kind :
588+ hir:: ItemKind :: Trait ( ..)
589+ | hir:: ItemKind :: Impl { .. }
590+ | hir:: ItemKind :: Const ( ..)
591+ | hir:: ItemKind :: Enum ( ..)
592+ | hir:: ItemKind :: Struct ( ..)
593+ | hir:: ItemKind :: Union ( ..) ,
594+ ..
595+ } )
596+ | hir:: Node :: TraitItem ( hir:: TraitItem {
597+ kind : hir:: TraitItemKind :: Const ( ..) | hir:: TraitItemKind :: Type ( ..) ,
598+ ..
599+ } )
600+ | hir:: Node :: ImplItem ( hir:: ImplItem {
601+ kind : hir:: ImplItemKind :: Const ( ..) | hir:: ImplItemKind :: TyAlias ( ..) ,
602+ ..
603+ } ) ,
604+ ) => false ,
605+ _ => true ,
606+ } ;
607+ let impl_comparison =
608+ matches ! ( cause_code, ObligationCauseCode :: CompareImplMethodObligation { .. } ) ;
609+ if !callable_scope || impl_comparison {
610+ // We do not want to suggest calling functions when the reason of the
611+ // type error is a comparison of an `impl` with its `trait` or when the
612+ // scope is outside of a `Body`.
613+ } else {
614+ let assoc = self . associated_item ( proj_ty. item_def_id ) ;
615+ let items = self . associated_items ( assoc. container . id ( ) ) ;
616+ // Find all the methods in the trait that could be called to construct the
617+ // expected associated type.
618+ let methods: Vec < ( Span , String ) > = items
619+ . items
620+ . iter ( )
621+ . filter ( |( name, item) | {
622+ ty:: AssocKind :: Method == item. kind && Some ( * * name) != current_method_ident
623+ } )
624+ . filter_map ( |( _, item) | {
625+ let method = self . fn_sig ( item. def_id ) ;
626+ match method. output ( ) . skip_binder ( ) . kind {
627+ ty:: Projection ( ty:: ProjectionTy { item_def_id, .. } )
628+ if item_def_id == proj_ty. item_def_id =>
629+ {
630+ Some ( (
631+ self . sess . source_map ( ) . guess_head_span ( self . def_span ( item. def_id ) ) ,
632+ format ! ( "consider calling `{}`" , self . def_path_str( item. def_id) ) ,
633+ ) )
634+ }
635+ _ => None ,
636+ }
637+ } )
638+ . collect ( ) ;
639+ if !methods. is_empty ( ) {
640+ // Use a single `help:` to show all the methods in the trait that can
641+ // be used to construct the expected associated type.
642+ let mut span: MultiSpan =
643+ methods. iter ( ) . map ( |( sp, _) | * sp) . collect :: < Vec < Span > > ( ) . into ( ) ;
644+ let msg = format ! (
645+ "{some} method{s} {are} available that return{r} `{ty}`" ,
646+ some = if methods. len( ) == 1 { "a" } else { "some" } ,
647+ s = pluralize!( methods. len( ) ) ,
648+ are = if methods. len( ) == 1 { "is" } else { "are" } ,
649+ r = if methods. len( ) == 1 { "s" } else { "" } ,
650+ ty = values. expected
651+ ) ;
652+ for ( sp, label) in methods. into_iter ( ) {
653+ span. push_span_label ( sp, label) ;
654+ }
655+ db. span_help ( span, & msg) ;
656+ suggested = true ;
657+ }
658+ // Possibly suggest constraining the associated type to conform to the
659+ // found type.
660+ suggested |=
661+ self . suggest_constraint ( db, & msg, body_owner_def_id, proj_ty, values. found ) ;
662+ }
663+ if !suggested && !impl_comparison {
664+ // Generic suggestion when we can't be more specific.
665+ if callable_scope {
666+ db. help (
667+ & format ! ( "{} or calling a method that returns `{}`" , msg, values. expected, ) ,
668+ ) ;
669+ } else {
670+ db. help ( & msg) ;
671+ }
672+ db. note (
673+ "for more information, visit \
674+ https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
675+ ) ;
676+ }
677+ if self . sess . teach ( & db. get_code ( ) . unwrap ( ) ) {
678+ db. help (
679+ "given an associated type `T` and a method `foo`:
680+ ```
681+ trait Trait {
682+ type T;
683+ fn foo(&self) -> Self::T;
684+ }
685+ ```
686+ the only way of implementing method `foo` is to constrain `T` with an explicit associated type:
687+ ```
688+ impl Trait for X {
689+ type T = String;
690+ fn foo(&self) -> Self::T { String::new() }
691+ }
692+ ```" ,
693+ ) ;
694+ }
695+ }
696+
579697 fn constrain_associated_type_structured_suggestion (
580698 & self ,
581699 db : & mut DiagnosticBuilder < ' _ > ,
582700 trait_ref : & ty:: TraitRef < ' tcx > ,
583701 bounds : hir:: GenericBounds < ' _ > ,
584702 assoc : & ty:: AssocItem ,
585- values : & ExpectedFound < Ty < ' tcx > > ,
703+ ty : Ty < ' tcx > ,
586704 msg : & str ,
587705 ) -> bool {
706+ // FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting.
588707 for bound in bounds {
589708 match bound {
590709 hir:: GenericBound :: Trait ( ptr, hir:: TraitBoundModifier :: None ) => {
@@ -599,14 +718,11 @@ impl Trait for X {
599718 let ( span, sugg) = if has_params {
600719 let pos = ptr. span . hi ( ) - BytePos ( 1 ) ;
601720 let span = Span :: new ( pos, pos, ptr. span . ctxt ( ) ) ;
602- ( span, format ! ( ", {} = {}" , assoc. ident, values . expected ) )
721+ ( span, format ! ( ", {} = {}" , assoc. ident, ty ) )
603722 } else {
604- (
605- ptr. span . shrink_to_hi ( ) ,
606- format ! ( "<{} = {}>" , assoc. ident, values. expected) ,
607- )
723+ ( ptr. span . shrink_to_hi ( ) , format ! ( "<{} = {}>" , assoc. ident, ty) )
608724 } ;
609- db. span_suggestion ( span, msg, sugg, Applicability :: MaybeIncorrect ) ;
725+ db. span_suggestion_verbose ( span, msg, sugg, MaybeIncorrect ) ;
610726 return true ;
611727 }
612728 }
0 commit comments