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 ;
68use rustc_span:: symbol:: sym;
7- use rustc_span:: { BytePos , Span } ;
9+ use rustc_span:: { BytePos , MultiSpan , Span } ;
810use rustc_target:: spec:: abi;
911
1012use std:: borrow:: Cow ;
@@ -332,6 +334,7 @@ impl<'tcx> TyCtxt<'tcx> {
332334 self ,
333335 db : & mut DiagnosticBuilder < ' _ > ,
334336 err : & TypeError < ' tcx > ,
337+ cause : & ObligationCause < ' tcx > ,
335338 sp : Span ,
336339 body_owner_def_id : DefId ,
337340 ) {
@@ -370,7 +373,7 @@ impl<'tcx> TyCtxt<'tcx> {
370373 sp,
371374 "use a float literal" ,
372375 format ! ( "{}.0" , snippet) ,
373- Applicability :: MachineApplicable ,
376+ MachineApplicable ,
374377 ) ;
375378 }
376379 }
@@ -451,41 +454,27 @@ impl<T> Trait<T> for X {
451454 db. span_label ( p_span, "this type parameter" ) ;
452455 }
453456 }
454- ( ty:: Projection ( _) , _) => {
455- db. note ( & format ! (
456- "consider constraining the associated type `{}` to `{}` or calling a \
457- method that returns `{0}`",
458- values. expected, values. found,
459- ) ) ;
460- if self . sess . teach ( & db. get_code ( ) . unwrap ( ) ) {
461- db. help (
462- "given an associated type `T` and a method `foo`:
463- ```
464- trait Trait {
465- type T;
466- fn foo(&self) -> Self::T;
467- }
468- ```
469- the only way of implementing method `foo` is to constrain `T` with an explicit associated type:
470- ```
471- impl Trait for X {
472- type T = String;
473- fn foo(&self) -> Self::T { String::new() }
474- }
475- ```" ,
476- ) ;
477- }
478- db. note (
479- "for more information, visit \
480- https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
457+ ( ty:: Projection ( proj_ty) , _) => {
458+ self . expected_projection (
459+ db,
460+ proj_ty,
461+ values,
462+ body_owner_def_id,
463+ & cause. code ,
481464 ) ;
482465 }
483466 ( _, ty:: Projection ( proj_ty) ) => {
484467 let msg = format ! (
485468 "consider constraining the associated type `{}` to `{}`" ,
486469 values. found, values. expected,
487470 ) ;
488- if !self . suggest_constraint ( db, & msg, body_owner_def_id, proj_ty, values) {
471+ if !self . suggest_constraint (
472+ db,
473+ & msg,
474+ body_owner_def_id,
475+ proj_ty,
476+ values. expected ,
477+ ) {
489478 db. help ( & msg) ;
490479 db. note (
491480 "for more information, visit \
@@ -533,7 +522,7 @@ impl Trait for X {
533522 msg : & str ,
534523 body_owner_def_id : DefId ,
535524 proj_ty : & ty:: ProjectionTy < ' tcx > ,
536- values : & ExpectedFound < Ty < ' tcx > > ,
525+ ty : Ty < ' tcx > ,
537526 ) -> bool {
538527 let assoc = self . associated_item ( proj_ty. item_def_id ) ;
539528 let trait_ref = proj_ty. trait_ref ( * self ) ;
@@ -570,7 +559,7 @@ impl Trait for X {
570559 & trait_ref,
571560 pred. bounds ,
572561 & assoc,
573- values ,
562+ ty ,
574563 msg,
575564 ) {
576565 return true ;
@@ -587,7 +576,7 @@ impl Trait for X {
587576 & trait_ref,
588577 param. bounds ,
589578 & assoc,
590- values ,
579+ ty ,
591580 msg,
592581 ) ;
593582 }
@@ -597,15 +586,145 @@ impl Trait for X {
597586 false
598587 }
599588
589+ fn expected_projection (
590+ & self ,
591+ db : & mut DiagnosticBuilder < ' _ > ,
592+ proj_ty : & ty:: ProjectionTy < ' tcx > ,
593+ values : & ExpectedFound < Ty < ' tcx > > ,
594+ body_owner_def_id : DefId ,
595+ cause_code : & ObligationCauseCode < ' _ > ,
596+ ) {
597+ let msg = format ! (
598+ "consider constraining the associated type `{}` to `{}`" ,
599+ values. expected, values. found
600+ ) ;
601+ let mut suggested = false ;
602+ let body_owner = self . hir ( ) . get_if_local ( body_owner_def_id) ;
603+ let current_method_ident = body_owner. and_then ( |n| n. ident ( ) ) . map ( |i| i. name ) ;
604+
605+ let callable_scope = match body_owner {
606+ Some (
607+ hir:: Node :: Item ( hir:: Item {
608+ kind :
609+ hir:: ItemKind :: Trait ( ..)
610+ | hir:: ItemKind :: Impl { .. }
611+ | hir:: ItemKind :: Const ( ..)
612+ | hir:: ItemKind :: Enum ( ..)
613+ | hir:: ItemKind :: Struct ( ..)
614+ | hir:: ItemKind :: Union ( ..) ,
615+ ..
616+ } )
617+ | hir:: Node :: TraitItem ( hir:: TraitItem {
618+ kind : hir:: TraitItemKind :: Const ( ..) | hir:: TraitItemKind :: Type ( ..) ,
619+ ..
620+ } )
621+ | hir:: Node :: ImplItem ( hir:: ImplItem {
622+ kind : hir:: ImplItemKind :: Const ( ..) | hir:: ImplItemKind :: TyAlias ( ..) ,
623+ ..
624+ } ) ,
625+ ) => false ,
626+ _ => true ,
627+ } ;
628+ let impl_comparison =
629+ matches ! ( cause_code, ObligationCauseCode :: CompareImplMethodObligation { .. } ) ;
630+ if !callable_scope || impl_comparison {
631+ // We do not want to suggest calling functions when the reason of the
632+ // type error is a comparison of an `impl` with its `trait` or when the
633+ // scope is outside of a `Body`.
634+ } else {
635+ let assoc = self . associated_item ( proj_ty. item_def_id ) ;
636+ let items = self . associated_items ( assoc. container . id ( ) ) ;
637+ // Find all the methods in the trait that could be called to construct the
638+ // expected associated type.
639+ let methods: Vec < ( Span , String ) > = items
640+ . items
641+ . iter ( )
642+ . filter ( |( name, item) | {
643+ ty:: AssocKind :: Method == item. kind && Some ( * * name) != current_method_ident
644+ } )
645+ . filter_map ( |( _, item) | {
646+ let method = self . fn_sig ( item. def_id ) ;
647+ match method. output ( ) . skip_binder ( ) . kind {
648+ ty:: Projection ( ty:: ProjectionTy { item_def_id, .. } )
649+ if item_def_id == proj_ty. item_def_id =>
650+ {
651+ Some ( (
652+ self . sess . source_map ( ) . guess_head_span ( self . def_span ( item. def_id ) ) ,
653+ format ! ( "consider calling `{}`" , self . def_path_str( item. def_id) ) ,
654+ ) )
655+ }
656+ _ => None ,
657+ }
658+ } )
659+ . collect ( ) ;
660+ if !methods. is_empty ( ) {
661+ // Use a single `help:` to show all the methods in the trait that can
662+ // be used to construct the expected associated type.
663+ let mut span: MultiSpan =
664+ methods. iter ( ) . map ( |( sp, _) | * sp) . collect :: < Vec < Span > > ( ) . into ( ) ;
665+ let msg = format ! (
666+ "{some} method{s} {are} available that return{r} `{ty}`" ,
667+ some = if methods. len( ) == 1 { "a" } else { "some" } ,
668+ s = pluralize!( methods. len( ) ) ,
669+ are = if methods. len( ) == 1 { "is" } else { "are" } ,
670+ r = if methods. len( ) == 1 { "s" } else { "" } ,
671+ ty = values. expected
672+ ) ;
673+ for ( sp, label) in methods. into_iter ( ) {
674+ span. push_span_label ( sp, label) ;
675+ }
676+ db. span_help ( span, & msg) ;
677+ suggested = true ;
678+ }
679+ // Possibly suggest constraining the associated type to conform to the
680+ // found type.
681+ suggested |=
682+ self . suggest_constraint ( db, & msg, body_owner_def_id, proj_ty, values. found ) ;
683+ }
684+ if !suggested && !impl_comparison {
685+ // Generic suggestion when we can't be more specific.
686+ if callable_scope {
687+ db. help (
688+ & format ! ( "{} or calling a method that returns `{}`" , msg, values. expected, ) ,
689+ ) ;
690+ } else {
691+ db. help ( & msg) ;
692+ }
693+ db. note (
694+ "for more information, visit \
695+ https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
696+ ) ;
697+ }
698+ if self . sess . teach ( & db. get_code ( ) . unwrap ( ) ) {
699+ db. help (
700+ "given an associated type `T` and a method `foo`:
701+ ```
702+ trait Trait {
703+ type T;
704+ fn foo(&self) -> Self::T;
705+ }
706+ ```
707+ the only way of implementing method `foo` is to constrain `T` with an explicit associated type:
708+ ```
709+ impl Trait for X {
710+ type T = String;
711+ fn foo(&self) -> Self::T { String::new() }
712+ }
713+ ```" ,
714+ ) ;
715+ }
716+ }
717+
600718 fn constrain_associated_type_structured_suggestion (
601719 & self ,
602720 db : & mut DiagnosticBuilder < ' _ > ,
603721 trait_ref : & ty:: TraitRef < ' tcx > ,
604722 bounds : hir:: GenericBounds < ' _ > ,
605723 assoc : & ty:: AssocItem ,
606- values : & ExpectedFound < Ty < ' tcx > > ,
724+ ty : Ty < ' tcx > ,
607725 msg : & str ,
608726 ) -> bool {
727+ // FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting.
609728 for bound in bounds {
610729 match bound {
611730 hir:: GenericBound :: Trait ( ptr, hir:: TraitBoundModifier :: None ) => {
@@ -620,14 +739,11 @@ impl Trait for X {
620739 let ( span, sugg) = if has_params {
621740 let pos = ptr. span . hi ( ) - BytePos ( 1 ) ;
622741 let span = Span :: new ( pos, pos, ptr. span . ctxt ( ) ) ;
623- ( span, format ! ( ", {} = {}" , assoc. ident, values . expected ) )
742+ ( span, format ! ( ", {} = {}" , assoc. ident, ty ) )
624743 } else {
625- (
626- ptr. span . shrink_to_hi ( ) ,
627- format ! ( "<{} = {}>" , assoc. ident, values. expected) ,
628- )
744+ ( ptr. span . shrink_to_hi ( ) , format ! ( "<{} = {}>" , assoc. ident, ty) )
629745 } ;
630- db. span_suggestion ( span, msg, sugg, Applicability :: MaybeIncorrect ) ;
746+ db. span_suggestion_verbose ( span, msg, sugg, MaybeIncorrect ) ;
631747 return true ;
632748 }
633749 }
0 commit comments