@@ -643,13 +643,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
643
643
}
644
644
}
645
645
646
- fn suggest_traits_to_import < ' b > ( & self ,
647
- err : & mut DiagnosticBuilder < ' _ > ,
648
- span : Span ,
649
- rcvr_ty : Ty < ' tcx > ,
650
- item_name : ast:: Ident ,
651
- source : SelfSource < ' b > ,
652
- valid_out_of_scope_traits : Vec < DefId > ) {
646
+ fn suggest_traits_to_import < ' b > (
647
+ & self ,
648
+ err : & mut DiagnosticBuilder < ' _ > ,
649
+ span : Span ,
650
+ rcvr_ty : Ty < ' tcx > ,
651
+ item_name : ast:: Ident ,
652
+ source : SelfSource < ' b > ,
653
+ valid_out_of_scope_traits : Vec < DefId > ,
654
+ ) {
653
655
if self . suggest_valid_traits ( err, valid_out_of_scope_traits) {
654
656
return ;
655
657
}
@@ -683,30 +685,96 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
683
685
candidates. sort_by ( |a, b| a. cmp ( b) . reverse ( ) ) ;
684
686
candidates. dedup ( ) ;
685
687
686
- // FIXME #21673: this help message could be tuned to the case
687
- // of a type parameter: suggest adding a trait bound rather
688
- // than implementing.
689
- err. help ( "items from traits can only be used if the trait is implemented and in scope" ) ;
690
- let mut msg = format ! ( "the following {traits_define} an item `{name}`, \
691
- perhaps you need to implement {one_of_them}:",
692
- traits_define = if candidates. len( ) == 1 {
693
- "trait defines"
694
- } else {
695
- "traits define"
696
- } ,
697
- one_of_them = if candidates. len( ) == 1 {
698
- "it"
699
- } else {
700
- "one of them"
701
- } ,
702
- name = item_name) ;
703
-
704
- for ( i, trait_info) in candidates. iter ( ) . enumerate ( ) {
705
- msg. push_str ( & format ! ( "\n candidate #{}: `{}`" ,
706
- i + 1 ,
707
- self . tcx. def_path_str( trait_info. def_id) ) ) ;
688
+ let param_type = match rcvr_ty. sty {
689
+ ty:: Param ( param) => Some ( param) ,
690
+ ty:: Ref ( _, ty, _) => match ty. sty {
691
+ ty:: Param ( param) => Some ( param) ,
692
+ _ => None ,
693
+ }
694
+ _ => None ,
695
+ } ;
696
+ err. help ( if param_type. is_some ( ) {
697
+ "items from traits can only be used if the type parameter is bounded by the trait"
698
+ } else {
699
+ "items from traits can only be used if the trait is implemented and in scope"
700
+ } ) ;
701
+ let mut msg = format ! (
702
+ "the following {traits_define} an item `{name}`, perhaps you need to {action} \
703
+ {one_of_them}:",
704
+ traits_define = if candidates. len( ) == 1 {
705
+ "trait defines"
706
+ } else {
707
+ "traits define"
708
+ } ,
709
+ action = if let Some ( param) = param_type {
710
+ format!( "restrict type parameter `{}` with" , param)
711
+ } else {
712
+ "implement" . to_string( )
713
+ } ,
714
+ one_of_them = if candidates. len( ) == 1 {
715
+ "it"
716
+ } else {
717
+ "one of them"
718
+ } ,
719
+ name = item_name,
720
+ ) ;
721
+ // Obtain the span for `param` and use it for a structured suggestion.
722
+ let mut suggested = false ;
723
+ if let ( Some ( ref param) , Some ( ref table) ) = ( param_type, self . in_progress_tables ) {
724
+ let table = table. borrow ( ) ;
725
+ if let Some ( did) = table. local_id_root {
726
+ let generics = self . tcx . generics_of ( did) ;
727
+ let type_param = generics. type_param ( param, self . tcx ) ;
728
+ let hir = & self . tcx . hir ( ) ;
729
+ if let Some ( id) = hir. as_local_hir_id ( type_param. def_id ) {
730
+ // Get the `hir::Param` to verify whether it already has any bounds.
731
+ // We do this to avoid suggesting code that ends up as `T: FooBar`,
732
+ // instead we suggest `T: Foo + Bar` in that case.
733
+ let mut has_bounds = false ;
734
+ if let Node :: GenericParam ( ref param) = hir. get ( id) {
735
+ has_bounds = !param. bounds . is_empty ( ) ;
736
+ }
737
+ let sp = hir. span ( id) ;
738
+ // `sp` only covers `T`, change it so that it covers
739
+ // `T:` when appropriate
740
+ let sp = if has_bounds {
741
+ sp. to ( self . tcx
742
+ . sess
743
+ . source_map ( )
744
+ . next_point ( self . tcx . sess . source_map ( ) . next_point ( sp) ) )
745
+ } else {
746
+ sp
747
+ } ;
748
+
749
+ // FIXME: contrast `t.def_id` against `param.bounds` to not suggest traits
750
+ // already there. That can happen when the cause is that we're in a const
751
+ // scope or associated function used as a method.
752
+ err. span_suggestions (
753
+ sp,
754
+ & msg[ ..] ,
755
+ candidates. iter ( ) . map ( |t| format ! (
756
+ "{}: {}{}" ,
757
+ param,
758
+ self . tcx. def_path_str( t. def_id) ,
759
+ if has_bounds { " +" } else { "" } ,
760
+ ) ) ,
761
+ Applicability :: MaybeIncorrect ,
762
+ ) ;
763
+ suggested = true ;
764
+ }
765
+ } ;
766
+ }
767
+
768
+ if !suggested {
769
+ for ( i, trait_info) in candidates. iter ( ) . enumerate ( ) {
770
+ msg. push_str ( & format ! (
771
+ "\n candidate #{}: `{}`" ,
772
+ i + 1 ,
773
+ self . tcx. def_path_str( trait_info. def_id) ,
774
+ ) ) ;
775
+ }
776
+ err. note ( & msg[ ..] ) ;
708
777
}
709
- err. note ( & msg[ ..] ) ;
710
778
}
711
779
}
712
780
0 commit comments