@@ -12,7 +12,7 @@ use rustc_hir::{ExprKind, Node, QPath};
12
12
use rustc_infer:: infer:: type_variable:: { TypeVariableOrigin , TypeVariableOriginKind } ;
13
13
use rustc_middle:: ty:: fast_reject:: { simplify_type, SimplifyParams , StripReferences } ;
14
14
use rustc_middle:: ty:: print:: with_crate_prefix;
15
- use rustc_middle:: ty:: { self , ToPredicate , Ty , TyCtxt , TypeFoldable } ;
15
+ use rustc_middle:: ty:: { self , DefIdTree , ToPredicate , Ty , TyCtxt , TypeFoldable } ;
16
16
use rustc_span:: lev_distance;
17
17
use rustc_span:: symbol:: { kw, sym, Ident } ;
18
18
use rustc_span:: { source_map, FileName , MultiSpan , Span , Symbol } ;
@@ -1310,25 +1310,66 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1310
1310
mut msg : String ,
1311
1311
candidates : Vec < DefId > ,
1312
1312
) {
1313
+ let parent_map = self . tcx . visible_parent_map ( ( ) ) ;
1314
+
1315
+ // Separate out candidates that must be imported with a glob, because they are named `_`
1316
+ // and cannot be referred with their identifier.
1317
+ let ( candidates, globs) : ( Vec < _ > , Vec < _ > ) = candidates. into_iter ( ) . partition ( |trait_did| {
1318
+ if let Some ( parent_did) = parent_map. get ( trait_did) {
1319
+ // If the item is re-exported as `_`, we should suggest a glob-import instead.
1320
+ if Some ( * parent_did) != self . tcx . parent ( * trait_did)
1321
+ && self
1322
+ . tcx
1323
+ . item_children ( * parent_did)
1324
+ . iter ( )
1325
+ . filter ( |child| child. res . opt_def_id ( ) == Some ( * trait_did) )
1326
+ . all ( |child| child. ident . name == kw:: Underscore )
1327
+ {
1328
+ return false ;
1329
+ }
1330
+ }
1331
+
1332
+ true
1333
+ } ) ;
1334
+
1313
1335
let module_did = self . tcx . parent_module ( self . body_id ) ;
1314
1336
let ( span, found_use) = find_use_placement ( self . tcx , module_did) ;
1315
1337
if let Some ( span) = span {
1316
- let path_strings = candidates. iter ( ) . map ( |did | {
1338
+ let path_strings = candidates. iter ( ) . map ( |trait_did | {
1317
1339
// Produce an additional newline to separate the new use statement
1318
1340
// from the directly following item.
1319
1341
let additional_newline = if found_use { "" } else { "\n " } ;
1320
1342
format ! (
1321
1343
"use {};\n {}" ,
1322
- with_crate_prefix( || self . tcx. def_path_str( * did ) ) ,
1344
+ with_crate_prefix( || self . tcx. def_path_str( * trait_did ) ) ,
1323
1345
additional_newline
1324
1346
)
1325
1347
} ) ;
1326
1348
1327
- err. span_suggestions ( span, & msg, path_strings, Applicability :: MaybeIncorrect ) ;
1349
+ let glob_path_strings = globs. iter ( ) . map ( |trait_did| {
1350
+ let parent_did = parent_map. get ( trait_did) . unwrap ( ) ;
1351
+
1352
+ // Produce an additional newline to separate the new use statement
1353
+ // from the directly following item.
1354
+ let additional_newline = if found_use { "" } else { "\n " } ;
1355
+ format ! (
1356
+ "use {}::*; // trait {}\n {}" ,
1357
+ with_crate_prefix( || self . tcx. def_path_str( * parent_did) ) ,
1358
+ self . tcx. item_name( * trait_did) ,
1359
+ additional_newline
1360
+ )
1361
+ } ) ;
1362
+
1363
+ err. span_suggestions (
1364
+ span,
1365
+ & msg,
1366
+ path_strings. chain ( glob_path_strings) ,
1367
+ Applicability :: MaybeIncorrect ,
1368
+ ) ;
1328
1369
} else {
1329
- let limit = if candidates. len ( ) == 5 { 5 } else { 4 } ;
1370
+ let limit = if candidates. len ( ) + globs . len ( ) == 5 { 5 } else { 4 } ;
1330
1371
for ( i, trait_did) in candidates. iter ( ) . take ( limit) . enumerate ( ) {
1331
- if candidates. len ( ) > 1 {
1372
+ if candidates. len ( ) + globs . len ( ) > 1 {
1332
1373
msg. push_str ( & format ! (
1333
1374
"\n candidate #{}: `use {};`" ,
1334
1375
i + 1 ,
@@ -1341,8 +1382,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1341
1382
) ) ;
1342
1383
}
1343
1384
}
1385
+ for ( i, trait_did) in
1386
+ globs. iter ( ) . take ( limit. saturating_sub ( candidates. len ( ) ) ) . enumerate ( )
1387
+ {
1388
+ let parent_did = parent_map. get ( trait_did) . unwrap ( ) ;
1389
+
1390
+ if candidates. len ( ) + globs. len ( ) > 1 {
1391
+ msg. push_str ( & format ! (
1392
+ "\n candidate #{}: `use {}::*; // trait {}`" ,
1393
+ candidates. len( ) + i + 1 ,
1394
+ with_crate_prefix( || self . tcx. def_path_str( * parent_did) ) ,
1395
+ self . tcx. item_name( * trait_did) ,
1396
+ ) ) ;
1397
+ } else {
1398
+ msg. push_str ( & format ! (
1399
+ "\n `use {}::*; // trait {}`" ,
1400
+ with_crate_prefix( || self . tcx. def_path_str( * parent_did) ) ,
1401
+ self . tcx. item_name( * trait_did) ,
1402
+ ) ) ;
1403
+ }
1404
+ }
1344
1405
if candidates. len ( ) > limit {
1345
- msg. push_str ( & format ! ( "\n and {} others" , candidates. len( ) - limit) ) ;
1406
+ msg. push_str ( & format ! ( "\n and {} others" , candidates. len( ) + globs . len ( ) - limit) ) ;
1346
1407
}
1347
1408
err. note ( & msg) ;
1348
1409
}
0 commit comments