@@ -303,14 +303,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
303303 }
304304 if !candidates. is_empty ( ) {
305305 let help = format ! (
306- "{an}other candidate{s} {were} found in the following trait{s}, perhaps \
307- add a `use` for {one_of_them}:",
306+ "{an}other candidate{s} {were} found in the following trait{s}" ,
308307 an = if candidates. len( ) == 1 { "an" } else { "" } ,
309308 s = pluralize!( candidates. len( ) ) ,
310309 were = pluralize!( "was" , candidates. len( ) ) ,
311- one_of_them = if candidates. len( ) == 1 { "it" } else { "one_of_them" } ,
312310 ) ;
313- self . suggest_use_candidates ( & mut err, help, candidates) ;
311+ self . suggest_use_candidates (
312+ candidates,
313+ |accessible_sugg, inaccessible_sugg, span| {
314+ let suggest_for_access =
315+ |err : & mut Diag < ' _ > , mut msg : String , sugg : Vec < _ > | {
316+ msg += & format ! (
317+ ", perhaps add a `use` for {one_of_them}:" ,
318+ one_of_them =
319+ if sugg. len( ) == 1 { "it" } else { "one_of_them" } ,
320+ ) ;
321+ err. span_suggestions (
322+ span,
323+ msg,
324+ sugg,
325+ Applicability :: MaybeIncorrect ,
326+ ) ;
327+ } ;
328+ let suggest_for_privacy =
329+ |err : & mut Diag < ' _ > , mut msg : String , sugg : Vec < String > | {
330+ if sugg. len ( ) == 1 {
331+ let msg = format ! ( "\
332+ trait `{}` provides `{item_name}` is implemented but not reachable",
333+ sugg[ 0 ] . trim( )
334+ ) ;
335+ err. help ( msg) ;
336+ } else {
337+ msg += & format ! ( " but {} not reachable" , pluralize!( "is" , sugg. len( ) ) ) ;
338+ err. span_suggestions (
339+ span,
340+ msg,
341+ sugg,
342+ Applicability :: MaybeIncorrect ,
343+ ) ;
344+ }
345+ } ;
346+ if accessible_sugg. is_empty ( ) {
347+ // `inaccessible_sugg` must not be empty
348+ suggest_for_privacy ( & mut err, help, inaccessible_sugg) ;
349+ } else if inaccessible_sugg. is_empty ( ) {
350+ suggest_for_access ( & mut err, help, accessible_sugg) ;
351+ } else {
352+ suggest_for_access ( & mut err, help. clone ( ) , accessible_sugg) ;
353+ suggest_for_privacy ( & mut err, help, inaccessible_sugg) ;
354+ }
355+ } ,
356+ ) ;
314357 }
315358 if let ty:: Ref ( region, t_type, mutability) = rcvr_ty. kind ( ) {
316359 if needs_mut {
@@ -3089,49 +3132,69 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
30893132 }
30903133 }
30913134
3092- fn suggest_use_candidates ( & self , err : & mut Diag < ' _ > , msg : String , candidates : Vec < DefId > ) {
3135+ fn suggest_use_candidates < F > ( & self , candidates : Vec < DefId > , handle_candidates : F )
3136+ where
3137+ F : FnOnce ( Vec < String > , Vec < String > , Span ) ,
3138+ {
30933139 let parent_map = self . tcx . visible_parent_map ( ( ) ) ;
30943140
3095- // Separate out candidates that must be imported with a glob, because they are named `_`
3096- // and cannot be referred with their identifier.
3097- let ( candidates, globs) : ( Vec < _ > , Vec < _ > ) = candidates. into_iter ( ) . partition ( |trait_did| {
3098- if let Some ( parent_did) = parent_map. get ( trait_did) {
3099- // If the item is re-exported as `_`, we should suggest a glob-import instead.
3100- if * parent_did != self . tcx . parent ( * trait_did)
3101- && self
3102- . tcx
3103- . module_children ( * parent_did)
3104- . iter ( )
3105- . filter ( |child| child. res . opt_def_id ( ) == Some ( * trait_did) )
3106- . all ( |child| child. ident . name == kw:: Underscore )
3107- {
3108- return false ;
3109- }
3110- }
3141+ let scope = self . tcx . parent_module_from_def_id ( self . body_id ) ;
3142+ let ( accessible_candidates, inaccessible_candidates) : ( Vec < _ > , Vec < _ > ) =
3143+ candidates. into_iter ( ) . partition ( |id| {
3144+ let vis = self . tcx . visibility ( * id) ;
3145+ vis. is_accessible_from ( scope, self . tcx )
3146+ } ) ;
31113147
3112- true
3113- } ) ;
3148+ let sugg = |candidates : Vec < _ > , visible| {
3149+ // Separate out candidates that must be imported with a glob, because they are named `_`
3150+ // and cannot be referred with their identifier.
3151+ let ( candidates, globs) : ( Vec < _ > , Vec < _ > ) =
3152+ candidates. into_iter ( ) . partition ( |trait_did| {
3153+ if let Some ( parent_did) = parent_map. get ( trait_did) {
3154+ // If the item is re-exported as `_`, we should suggest a glob-import instead.
3155+ if * parent_did != self . tcx . parent ( * trait_did)
3156+ && self
3157+ . tcx
3158+ . module_children ( * parent_did)
3159+ . iter ( )
3160+ . filter ( |child| child. res . opt_def_id ( ) == Some ( * trait_did) )
3161+ . all ( |child| child. ident . name == kw:: Underscore )
3162+ {
3163+ return false ;
3164+ }
3165+ }
31143166
3115- let module_did = self . tcx . parent_module_from_def_id ( self . body_id ) ;
3116- let ( module, _, _) = self . tcx . hir ( ) . get_module ( module_did) ;
3117- let span = module. spans . inject_use_span ;
3167+ true
3168+ } ) ;
31183169
3119- let path_strings = candidates. iter ( ) . map ( |trait_did| {
3120- format ! ( "use {};\n " , with_crate_prefix!( self . tcx. def_path_str( * trait_did) ) , )
3121- } ) ;
3170+ let prefix = if visible { "use " } else { "" } ;
3171+ let postfix = if visible { ";" } else { "" } ;
3172+ let path_strings = candidates. iter ( ) . map ( |trait_did| {
3173+ format ! (
3174+ "{prefix}{}{postfix}\n " ,
3175+ with_crate_prefix!( self . tcx. def_path_str( * trait_did) ) ,
3176+ )
3177+ } ) ;
31223178
3123- let glob_path_strings = globs. iter ( ) . map ( |trait_did| {
3124- let parent_did = parent_map. get ( trait_did) . unwrap ( ) ;
3125- format ! (
3126- "use {}::*; // trait {}\n " ,
3127- with_crate_prefix!( self . tcx. def_path_str( * parent_did) ) ,
3128- self . tcx. item_name( * trait_did) ,
3129- )
3130- } ) ;
3131- let mut sugg: Vec < _ > = path_strings. chain ( glob_path_strings) . collect ( ) ;
3132- sugg. sort ( ) ;
3179+ let glob_path_strings = globs. iter ( ) . map ( |trait_did| {
3180+ let parent_did = parent_map. get ( trait_did) . unwrap ( ) ;
3181+ format ! (
3182+ "{prefix}{}::*{postfix} // trait {}\n " ,
3183+ with_crate_prefix!( self . tcx. def_path_str( * parent_did) ) ,
3184+ self . tcx. item_name( * trait_did) ,
3185+ )
3186+ } ) ;
3187+ let mut sugg: Vec < _ > = path_strings. chain ( glob_path_strings) . collect ( ) ;
3188+ sugg. sort ( ) ;
3189+ sugg
3190+ } ;
31333191
3134- err. span_suggestions ( span, msg, sugg, Applicability :: MaybeIncorrect ) ;
3192+ let accessible_sugg = sugg ( accessible_candidates, true ) ;
3193+ let inaccessible_sugg = sugg ( inaccessible_candidates, false ) ;
3194+
3195+ let ( module, _, _) = self . tcx . hir ( ) . get_module ( scope) ;
3196+ let span = module. spans . inject_use_span ;
3197+ handle_candidates ( accessible_sugg, inaccessible_sugg, span) ;
31353198 }
31363199
31373200 fn suggest_valid_traits (
@@ -3155,21 +3218,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
31553218 if explain {
31563219 err. help ( "items from traits can only be used if the trait is in scope" ) ;
31573220 }
3221+
31583222 let msg = format ! (
3159- "{this_trait_is} implemented but not in scope; perhaps you want to import \
3160- {one_of_them}",
3223+ "{this_trait_is} implemented but not in scope" ,
31613224 this_trait_is = if candidates. len( ) == 1 {
31623225 format!(
31633226 "trait `{}` which provides `{item_name}` is" ,
31643227 self . tcx. item_name( candidates[ 0 ] ) ,
31653228 )
31663229 } else {
31673230 format!( "the following traits which provide `{item_name}` are" )
3168- } ,
3169- one_of_them = if candidates. len( ) == 1 { "it" } else { "one of them" } ,
3231+ }
31703232 ) ;
31713233
3172- self . suggest_use_candidates ( err, msg, candidates) ;
3234+ self . suggest_use_candidates ( candidates, |accessible_sugg, inaccessible_sugg, span| {
3235+ let suggest_for_access = |err : & mut Diag < ' _ > , mut msg : String , sugg : Vec < _ > | {
3236+ msg += & format ! (
3237+ "; perhaps you want to import {one_of}" ,
3238+ one_of = if sugg. len( ) == 1 { "it" } else { "one of them" } ,
3239+ ) ;
3240+ err. span_suggestions ( span, msg, sugg, Applicability :: MaybeIncorrect ) ;
3241+ } ;
3242+ let suggest_for_privacy = |err : & mut Diag < ' _ > , sugg : Vec < String > | {
3243+ let msg = format ! (
3244+ "{this_trait_is} implemented but not reachable" ,
3245+ this_trait_is = if sugg. len( ) == 1 {
3246+ format!( "trait `{}` which provides `{item_name}` is" , sugg[ 0 ] . trim( ) )
3247+ } else {
3248+ format!( "the following traits which provide `{item_name}` are" )
3249+ }
3250+ ) ;
3251+ if sugg. len ( ) == 1 {
3252+ err. help ( msg) ;
3253+ } else {
3254+ err. span_suggestions ( span, msg, sugg, Applicability :: MaybeIncorrect ) ;
3255+ }
3256+ } ;
3257+ if accessible_sugg. is_empty ( ) {
3258+ // `inaccessible_sugg` must not be empty
3259+ suggest_for_privacy ( err, inaccessible_sugg) ;
3260+ } else if inaccessible_sugg. is_empty ( ) {
3261+ suggest_for_access ( err, msg, accessible_sugg) ;
3262+ } else {
3263+ suggest_for_access ( err, msg, accessible_sugg) ;
3264+ suggest_for_privacy ( err, inaccessible_sugg) ;
3265+ }
3266+ } ) ;
3267+
31733268 if let Some ( did) = edition_fix {
31743269 err. note ( format ! (
31753270 "'{}' is included in the prelude starting in Edition 2021" ,
0 commit comments