@@ -284,14 +284,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
284
284
}
285
285
if !candidates. is_empty ( ) {
286
286
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}" ,
289
288
an = if candidates. len( ) == 1 { "an" } else { "" } ,
290
289
s = pluralize!( candidates. len( ) ) ,
291
290
were = pluralize!( "was" , candidates. len( ) ) ,
292
- one_of_them = if candidates. len( ) == 1 { "it" } else { "one_of_them" } ,
293
291
) ;
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
+ ) ;
295
338
}
296
339
if let ty:: Ref ( region, t_type, mutability) = rcvr_ty. kind ( ) {
297
340
if needs_mut {
@@ -3069,49 +3112,69 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
3069
3112
}
3070
3113
}
3071
3114
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
+ {
3073
3119
let parent_map = self . tcx . visible_parent_map ( ( ) ) ;
3074
3120
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
+ } ) ;
3091
3127
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
+ }
3094
3146
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
+ } ) ;
3098
3149
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
+ } ) ;
3102
3158
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
+ } ;
3113
3171
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) ;
3115
3178
}
3116
3179
3117
3180
fn suggest_valid_traits (
@@ -3135,21 +3198,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
3135
3198
if explain {
3136
3199
err. help ( "items from traits can only be used if the trait is in scope" ) ;
3137
3200
}
3201
+
3138
3202
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" ,
3141
3204
this_trait_is = if candidates. len( ) == 1 {
3142
3205
format!(
3143
3206
"trait `{}` which provides `{item_name}` is" ,
3144
3207
self . tcx. item_name( candidates[ 0 ] ) ,
3145
3208
)
3146
3209
} else {
3147
3210
format!( "the following traits which provide `{item_name}` are" )
3148
- } ,
3149
- one_of_them = if candidates. len( ) == 1 { "it" } else { "one of them" } ,
3211
+ }
3150
3212
) ;
3151
3213
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
+
3153
3248
if let Some ( did) = edition_fix {
3154
3249
err. note ( format ! (
3155
3250
"'{}' is included in the prelude starting in Edition 2021" ,
0 commit comments