@@ -284,14 +284,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
284284 }
285285 if !candidates. is_empty ( ) {
286286 let help = format ! (
287- "{an}other candidate{s} {were} found in the following trait{s}, perhaps \
288- add a `use` for {one_of_them}:",
287+ "{an}other candidate{s} {were} found in the following trait{s}" ,
289288 an = if candidates. len( ) == 1 { "an" } else { "" } ,
290289 s = pluralize!( candidates. len( ) ) ,
291290 were = pluralize!( "was" , candidates. len( ) ) ,
292- one_of_them = if candidates. len( ) == 1 { "it" } else { "one_of_them" } ,
293291 ) ;
294- self . suggest_use_candidates ( & mut err, help, candidates) ;
292+ self . suggest_use_candidates (
293+ candidates,
294+ |accessible_sugg, inaccessible_sugg, span| {
295+ let suggest_for_access =
296+ |err : & mut Diag < ' _ > , mut msg : String , sugg : Vec < _ > | {
297+ msg += & format ! (
298+ ", perhaps add a `use` for {one_of_them}:" ,
299+ one_of_them =
300+ if sugg. len( ) == 1 { "it" } else { "one_of_them" } ,
301+ ) ;
302+ err. span_suggestions (
303+ span,
304+ msg,
305+ sugg,
306+ Applicability :: MaybeIncorrect ,
307+ ) ;
308+ } ;
309+ let suggest_for_privacy =
310+ |err : & mut Diag < ' _ > , mut msg : String , sugg : Vec < String > | {
311+ if sugg. len ( ) == 1 {
312+ let msg = format ! ( "\
313+ trait `{}` provides `{item_name}` is implemented but not reachable",
314+ sugg[ 0 ] . trim( )
315+ ) ;
316+ err. help ( msg) ;
317+ } else {
318+ msg += & format ! ( " but {} not reachable" , pluralize!( "is" , sugg. len( ) ) ) ;
319+ err. span_suggestions (
320+ span,
321+ msg,
322+ sugg,
323+ Applicability :: MaybeIncorrect ,
324+ ) ;
325+ }
326+ } ;
327+ if accessible_sugg. is_empty ( ) {
328+ // `inaccessible_sugg` must not be empty
329+ suggest_for_privacy ( & mut err, help, inaccessible_sugg) ;
330+ } else if inaccessible_sugg. is_empty ( ) {
331+ suggest_for_access ( & mut err, help, accessible_sugg) ;
332+ } else {
333+ suggest_for_access ( & mut err, help. clone ( ) , accessible_sugg) ;
334+ suggest_for_privacy ( & mut err, help, inaccessible_sugg) ;
335+ }
336+ } ,
337+ ) ;
295338 }
296339 if let ty:: Ref ( region, t_type, mutability) = rcvr_ty. kind ( ) {
297340 if needs_mut {
@@ -3069,49 +3112,69 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
30693112 }
30703113 }
30713114
3072- fn suggest_use_candidates ( & self , err : & mut Diag < ' _ > , msg : String , candidates : Vec < DefId > ) {
3115+ fn suggest_use_candidates < F > ( & self , candidates : Vec < DefId > , handle_candidates : F )
3116+ where
3117+ F : FnOnce ( Vec < String > , Vec < String > , Span ) ,
3118+ {
30733119 let parent_map = self . tcx . visible_parent_map ( ( ) ) ;
30743120
3075- // Separate out candidates that must be imported with a glob, because they are named `_`
3076- // and cannot be referred with their identifier.
3077- let ( candidates, globs) : ( Vec < _ > , Vec < _ > ) = candidates. into_iter ( ) . partition ( |trait_did| {
3078- if let Some ( parent_did) = parent_map. get ( trait_did) {
3079- // If the item is re-exported as `_`, we should suggest a glob-import instead.
3080- if * parent_did != self . tcx . parent ( * trait_did)
3081- && self
3082- . tcx
3083- . module_children ( * parent_did)
3084- . iter ( )
3085- . filter ( |child| child. res . opt_def_id ( ) == Some ( * trait_did) )
3086- . all ( |child| child. ident . name == kw:: Underscore )
3087- {
3088- return false ;
3089- }
3090- }
3121+ let scope = self . tcx . parent_module_from_def_id ( self . body_id ) ;
3122+ let ( accessible_candidates, inaccessible_candidates) : ( Vec < _ > , Vec < _ > ) =
3123+ candidates. into_iter ( ) . partition ( |id| {
3124+ let vis = self . tcx . visibility ( * id) ;
3125+ vis. is_accessible_from ( scope, self . tcx )
3126+ } ) ;
30913127
3092- true
3093- } ) ;
3128+ let sugg = |candidates : Vec < _ > , visible| {
3129+ // Separate out candidates that must be imported with a glob, because they are named `_`
3130+ // and cannot be referred with their identifier.
3131+ let ( candidates, globs) : ( Vec < _ > , Vec < _ > ) =
3132+ candidates. into_iter ( ) . partition ( |trait_did| {
3133+ if let Some ( parent_did) = parent_map. get ( trait_did) {
3134+ // If the item is re-exported as `_`, we should suggest a glob-import instead.
3135+ if * parent_did != self . tcx . parent ( * trait_did)
3136+ && self
3137+ . tcx
3138+ . module_children ( * parent_did)
3139+ . iter ( )
3140+ . filter ( |child| child. res . opt_def_id ( ) == Some ( * trait_did) )
3141+ . all ( |child| child. ident . name == kw:: Underscore )
3142+ {
3143+ return false ;
3144+ }
3145+ }
30943146
3095- let module_did = self . tcx . parent_module_from_def_id ( self . body_id ) ;
3096- let ( module, _, _) = self . tcx . hir ( ) . get_module ( module_did) ;
3097- let span = module. spans . inject_use_span ;
3147+ true
3148+ } ) ;
30983149
3099- let path_strings = candidates. iter ( ) . map ( |trait_did| {
3100- format ! ( "use {};\n " , with_crate_prefix!( self . tcx. def_path_str( * trait_did) ) , )
3101- } ) ;
3150+ let prefix = if visible { "use " } else { "" } ;
3151+ let postfix = if visible { ";" } else { "" } ;
3152+ let path_strings = candidates. iter ( ) . map ( |trait_did| {
3153+ format ! (
3154+ "{prefix}{}{postfix}\n " ,
3155+ with_crate_prefix!( self . tcx. def_path_str( * trait_did) ) ,
3156+ )
3157+ } ) ;
31023158
3103- let glob_path_strings = globs. iter ( ) . map ( |trait_did| {
3104- let parent_did = parent_map. get ( trait_did) . unwrap ( ) ;
3105- format ! (
3106- "use {}::*; // trait {}\n " ,
3107- with_crate_prefix!( self . tcx. def_path_str( * parent_did) ) ,
3108- self . tcx. item_name( * trait_did) ,
3109- )
3110- } ) ;
3111- let mut sugg: Vec < _ > = path_strings. chain ( glob_path_strings) . collect ( ) ;
3112- sugg. sort ( ) ;
3159+ let glob_path_strings = globs. iter ( ) . map ( |trait_did| {
3160+ let parent_did = parent_map. get ( trait_did) . unwrap ( ) ;
3161+ format ! (
3162+ "{prefix}{}::*{postfix} // trait {}\n " ,
3163+ with_crate_prefix!( self . tcx. def_path_str( * parent_did) ) ,
3164+ self . tcx. item_name( * trait_did) ,
3165+ )
3166+ } ) ;
3167+ let mut sugg: Vec < _ > = path_strings. chain ( glob_path_strings) . collect ( ) ;
3168+ sugg. sort ( ) ;
3169+ sugg
3170+ } ;
31133171
3114- err. span_suggestions ( span, msg, sugg, Applicability :: MaybeIncorrect ) ;
3172+ let accessible_sugg = sugg ( accessible_candidates, true ) ;
3173+ let inaccessible_sugg = sugg ( inaccessible_candidates, false ) ;
3174+
3175+ let ( module, _, _) = self . tcx . hir ( ) . get_module ( scope) ;
3176+ let span = module. spans . inject_use_span ;
3177+ handle_candidates ( accessible_sugg, inaccessible_sugg, span) ;
31153178 }
31163179
31173180 fn suggest_valid_traits (
@@ -3135,21 +3198,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
31353198 if explain {
31363199 err. help ( "items from traits can only be used if the trait is in scope" ) ;
31373200 }
3201+
31383202 let msg = format ! (
3139- "{this_trait_is} implemented but not in scope; perhaps you want to import \
3140- {one_of_them}",
3203+ "{this_trait_is} implemented but not in scope" ,
31413204 this_trait_is = if candidates. len( ) == 1 {
31423205 format!(
31433206 "trait `{}` which provides `{item_name}` is" ,
31443207 self . tcx. item_name( candidates[ 0 ] ) ,
31453208 )
31463209 } else {
31473210 format!( "the following traits which provide `{item_name}` are" )
3148- } ,
3149- one_of_them = if candidates. len( ) == 1 { "it" } else { "one of them" } ,
3211+ }
31503212 ) ;
31513213
3152- self . suggest_use_candidates ( err, msg, candidates) ;
3214+ self . suggest_use_candidates ( candidates, |accessible_sugg, inaccessible_sugg, span| {
3215+ let suggest_for_access = |err : & mut Diag < ' _ > , mut msg : String , sugg : Vec < _ > | {
3216+ msg += & format ! (
3217+ "; perhaps you want to import {one_of}" ,
3218+ one_of = if sugg. len( ) == 1 { "it" } else { "one of them" } ,
3219+ ) ;
3220+ err. span_suggestions ( span, msg, sugg, Applicability :: MaybeIncorrect ) ;
3221+ } ;
3222+ let suggest_for_privacy = |err : & mut Diag < ' _ > , sugg : Vec < String > | {
3223+ let msg = format ! (
3224+ "{this_trait_is} implemented but not reachable" ,
3225+ this_trait_is = if sugg. len( ) == 1 {
3226+ format!( "trait `{}` which provides `{item_name}` is" , sugg[ 0 ] . trim( ) )
3227+ } else {
3228+ format!( "the following traits which provide `{item_name}` are" )
3229+ }
3230+ ) ;
3231+ if sugg. len ( ) == 1 {
3232+ err. help ( msg) ;
3233+ } else {
3234+ err. span_suggestions ( span, msg, sugg, Applicability :: MaybeIncorrect ) ;
3235+ }
3236+ } ;
3237+ if accessible_sugg. is_empty ( ) {
3238+ // `inaccessible_sugg` must not be empty
3239+ suggest_for_privacy ( err, inaccessible_sugg) ;
3240+ } else if inaccessible_sugg. is_empty ( ) {
3241+ suggest_for_access ( err, msg, accessible_sugg) ;
3242+ } else {
3243+ suggest_for_access ( err, msg, accessible_sugg) ;
3244+ suggest_for_privacy ( err, inaccessible_sugg) ;
3245+ }
3246+ } ) ;
3247+
31533248 if let Some ( did) = edition_fix {
31543249 err. note ( format ! (
31553250 "'{}' is included in the prelude starting in Edition 2021" ,
0 commit comments