@@ -303,14 +303,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
303
303
}
304
304
if !candidates. is_empty ( ) {
305
305
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}" ,
308
307
an = if candidates. len( ) == 1 { "an" } else { "" } ,
309
308
s = pluralize!( candidates. len( ) ) ,
310
309
were = pluralize!( "was" , candidates. len( ) ) ,
311
- one_of_them = if candidates. len( ) == 1 { "it" } else { "one_of_them" } ,
312
310
) ;
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
+ ) ;
314
357
}
315
358
if let ty:: Ref ( region, t_type, mutability) = rcvr_ty. kind ( ) {
316
359
if needs_mut {
@@ -3089,49 +3132,69 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
3089
3132
}
3090
3133
}
3091
3134
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
+ {
3093
3139
let parent_map = self . tcx . visible_parent_map ( ( ) ) ;
3094
3140
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
+ } ) ;
3111
3147
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
+ }
3114
3166
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
+ } ) ;
3118
3169
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
+ } ) ;
3122
3178
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
+ } ;
3133
3191
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) ;
3135
3198
}
3136
3199
3137
3200
fn suggest_valid_traits (
@@ -3155,21 +3218,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
3155
3218
if explain {
3156
3219
err. help ( "items from traits can only be used if the trait is in scope" ) ;
3157
3220
}
3221
+
3158
3222
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" ,
3161
3224
this_trait_is = if candidates. len( ) == 1 {
3162
3225
format!(
3163
3226
"trait `{}` which provides `{item_name}` is" ,
3164
3227
self . tcx. item_name( candidates[ 0 ] ) ,
3165
3228
)
3166
3229
} else {
3167
3230
format!( "the following traits which provide `{item_name}` are" )
3168
- } ,
3169
- one_of_them = if candidates. len( ) == 1 { "it" } else { "one of them" } ,
3231
+ }
3170
3232
) ;
3171
3233
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
+
3173
3268
if let Some ( did) = edition_fix {
3174
3269
err. note ( format ! (
3175
3270
"'{}' is included in the prelude starting in Edition 2021" ,
0 commit comments