@@ -643,13 +643,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
643643 }
644644 }
645645
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+ ) {
653655 if self . suggest_valid_traits ( err, valid_out_of_scope_traits) {
654656 return ;
655657 }
@@ -683,30 +685,96 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
683685 candidates. sort_by ( |a, b| a. cmp ( b) . reverse ( ) ) ;
684686 candidates. dedup ( ) ;
685687
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[ ..] ) ;
708777 }
709- err. note ( & msg[ ..] ) ;
710778 }
711779 }
712780
0 commit comments