@@ -16,8 +16,8 @@ use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
16
16
use rustc_hir:: PrimTy ;
17
17
use rustc_session:: config:: nightly_options;
18
18
use rustc_span:: hygiene:: MacroKind ;
19
- use rustc_span:: symbol:: { kw, sym, Ident } ;
20
- use rustc_span:: { BytePos , Span } ;
19
+ use rustc_span:: symbol:: { kw, sym, Ident , Symbol } ;
20
+ use rustc_span:: { BytePos , Span , DUMMY_SP } ;
21
21
22
22
use log:: debug;
23
23
@@ -33,6 +33,7 @@ enum AssocSuggestion {
33
33
crate enum MissingLifetimeSpot < ' tcx > {
34
34
Generics ( & ' tcx hir:: Generics < ' tcx > ) ,
35
35
HigherRanked { span : Span , span_type : ForLifetimeSpanType } ,
36
+ Static ,
36
37
}
37
38
38
39
crate enum ForLifetimeSpanType {
@@ -1195,6 +1196,7 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
1195
1196
https://doc.rust-lang.org/nomicon/hrtb.html",
1196
1197
) ;
1197
1198
}
1199
+ _ => { }
1198
1200
}
1199
1201
}
1200
1202
if nightly_options:: is_nightly_build ( )
@@ -1253,7 +1255,8 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
1253
1255
err : & mut DiagnosticBuilder < ' _ > ,
1254
1256
span : Span ,
1255
1257
count : usize ,
1256
- lifetime_names : & FxHashSet < Ident > ,
1258
+ lifetime_names : & FxHashSet < Symbol > ,
1259
+ lifetime_spans : Vec < Span > ,
1257
1260
params : & [ ElisionFailureInfo ] ,
1258
1261
) {
1259
1262
let snippet = self . tcx . sess . source_map ( ) . span_to_snippet ( span) . ok ( ) ;
@@ -1267,11 +1270,60 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
1267
1270
) ,
1268
1271
) ;
1269
1272
1270
- let suggest_existing = |err : & mut DiagnosticBuilder < ' _ > , sugg| {
1273
+ let suggest_existing = |err : & mut DiagnosticBuilder < ' _ > ,
1274
+ name : & str ,
1275
+ formatter : & dyn Fn ( & str ) -> String | {
1276
+ if let Some ( MissingLifetimeSpot :: HigherRanked { span : for_span, span_type } ) =
1277
+ self . missing_named_lifetime_spots . iter ( ) . rev ( ) . next ( )
1278
+ {
1279
+ // When we have `struct S<'a>(&'a dyn Fn(&X) -> &X);` we want to not only suggest
1280
+ // using `'a`, but also introduce the concept of HRLTs by suggesting
1281
+ // `struct S<'a>(&'a dyn for<'b> Fn(&X) -> &'b X);`. (#72404)
1282
+ let mut introduce_suggestion = vec ! [ ] ;
1283
+
1284
+ let a_to_z_repeat_n = |n| {
1285
+ ( b'a' ..=b'z' ) . map ( move |c| {
1286
+ let mut s = '\'' . to_string ( ) ;
1287
+ s. extend ( std:: iter:: repeat ( char:: from ( c) ) . take ( n) ) ;
1288
+ s
1289
+ } )
1290
+ } ;
1291
+
1292
+ // If all single char lifetime names are present, we wrap around and double the chars.
1293
+ let lt_name = ( 1 ..)
1294
+ . flat_map ( a_to_z_repeat_n)
1295
+ . find ( |lt| !lifetime_names. contains ( & Symbol :: intern ( & lt) ) )
1296
+ . unwrap ( ) ;
1297
+ let msg = format ! (
1298
+ "consider making the {} lifetime-generic with a new `{}` lifetime" ,
1299
+ span_type. descr( ) ,
1300
+ lt_name,
1301
+ ) ;
1302
+ err. note (
1303
+ "for more information on higher-ranked polymorphism, visit \
1304
+ https://doc.rust-lang.org/nomicon/hrtb.html",
1305
+ ) ;
1306
+ let for_sugg = span_type. suggestion ( & lt_name) ;
1307
+ for param in params {
1308
+ if let Ok ( snippet) = self . tcx . sess . source_map ( ) . span_to_snippet ( param. span ) {
1309
+ if snippet. starts_with ( '&' ) && !snippet. starts_with ( "&'" ) {
1310
+ introduce_suggestion
1311
+ . push ( ( param. span , format ! ( "&{} {}" , lt_name, & snippet[ 1 ..] ) ) ) ;
1312
+ } else if snippet. starts_with ( "&'_ " ) {
1313
+ introduce_suggestion
1314
+ . push ( ( param. span , format ! ( "&{} {}" , lt_name, & snippet[ 4 ..] ) ) ) ;
1315
+ }
1316
+ }
1317
+ }
1318
+ introduce_suggestion. push ( ( * for_span, for_sugg. to_string ( ) ) ) ;
1319
+ introduce_suggestion. push ( ( span, formatter ( & lt_name) ) ) ;
1320
+ err. multipart_suggestion ( & msg, introduce_suggestion, Applicability :: MaybeIncorrect ) ;
1321
+ }
1322
+
1271
1323
err. span_suggestion_verbose (
1272
1324
span,
1273
1325
& format ! ( "consider using the `{}` lifetime" , lifetime_names. iter( ) . next( ) . unwrap( ) ) ,
1274
- sugg ,
1326
+ formatter ( name ) ,
1275
1327
Applicability :: MaybeIncorrect ,
1276
1328
) ;
1277
1329
} ;
@@ -1282,6 +1334,15 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
1282
1334
let should_break;
1283
1335
introduce_suggestion. push ( match missing {
1284
1336
MissingLifetimeSpot :: Generics ( generics) => {
1337
+ if generics. span == DUMMY_SP {
1338
+ // Account for malformed generics in the HIR. This shouldn't happen,
1339
+ // but if we make a mistake elsewhere, mainly by keeping something in
1340
+ // `missing_named_lifetime_spots` that we shouldn't, like associated
1341
+ // `const`s or making a mistake in the AST lowering we would provide
1342
+ // non-sensical suggestions. Guard against that by skipping these.
1343
+ // (#74264)
1344
+ continue ;
1345
+ }
1285
1346
msg = "consider introducing a named lifetime parameter" . to_string ( ) ;
1286
1347
should_break = true ;
1287
1348
if let Some ( param) = generics. params . iter ( ) . find ( |p| match p. kind {
@@ -1308,6 +1369,42 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
1308
1369
) ;
1309
1370
( * span, span_type. suggestion ( "'a" ) )
1310
1371
}
1372
+ MissingLifetimeSpot :: Static => {
1373
+ let ( span, sugg) = match snippet. as_deref ( ) {
1374
+ Some ( "&" ) => ( span. shrink_to_hi ( ) , "'static " . to_owned ( ) ) ,
1375
+ Some ( "'_" ) => ( span, "'static" . to_owned ( ) ) ,
1376
+ Some ( snippet) if !snippet. ends_with ( '>' ) => {
1377
+ if snippet == "" {
1378
+ (
1379
+ span,
1380
+ std:: iter:: repeat ( "'static" )
1381
+ . take ( count)
1382
+ . collect :: < Vec < _ > > ( )
1383
+ . join ( ", " ) ,
1384
+ )
1385
+ } else {
1386
+ (
1387
+ span. shrink_to_hi ( ) ,
1388
+ format ! (
1389
+ "<{}>" ,
1390
+ std:: iter:: repeat( "'static" )
1391
+ . take( count)
1392
+ . collect:: <Vec <_>>( )
1393
+ . join( ", " )
1394
+ ) ,
1395
+ )
1396
+ }
1397
+ }
1398
+ _ => continue ,
1399
+ } ;
1400
+ err. span_suggestion_verbose (
1401
+ span,
1402
+ "consider using the `'static` lifetime" ,
1403
+ sugg. to_string ( ) ,
1404
+ Applicability :: MaybeIncorrect ,
1405
+ ) ;
1406
+ continue ;
1407
+ }
1311
1408
} ) ;
1312
1409
for param in params {
1313
1410
if let Ok ( snippet) = self . tcx . sess . source_map ( ) . span_to_snippet ( param. span ) {
@@ -1328,41 +1425,57 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
1328
1425
}
1329
1426
} ;
1330
1427
1331
- match ( lifetime_names. len ( ) , lifetime_names. iter ( ) . next ( ) , snippet. as_deref ( ) ) {
1332
- ( 1 , Some ( name) , Some ( "&" ) ) => {
1333
- suggest_existing ( err, format ! ( "&{} " , name) ) ;
1428
+ let lifetime_names: Vec < _ > = lifetime_names. into_iter ( ) . collect ( ) ;
1429
+ match ( & lifetime_names[ ..] , snippet. as_deref ( ) ) {
1430
+ ( [ name] , Some ( "&" ) ) => {
1431
+ suggest_existing ( err, & name. as_str ( ) [ ..] , & |name| format ! ( "&{} " , name) ) ;
1334
1432
}
1335
- ( 1 , Some ( name) , Some ( "'_" ) ) => {
1336
- suggest_existing ( err, name. to_string ( ) ) ;
1433
+ ( [ name] , Some ( "'_" ) ) => {
1434
+ suggest_existing ( err, & name. as_str ( ) [ .. ] , & |n| n . to_string ( ) ) ;
1337
1435
}
1338
- ( 1 , Some ( name) , Some ( "" ) ) => {
1339
- suggest_existing ( err, format ! ( "{}, " , name ) . repeat ( count) ) ;
1436
+ ( [ name] , Some ( "" ) ) => {
1437
+ suggest_existing ( err, & name . as_str ( ) [ .. ] , & |n| format ! ( "{}, " , n ) . repeat ( count) ) ;
1340
1438
}
1341
- ( 1 , Some ( name) , Some ( snippet) ) if !snippet. ends_with ( '>' ) => {
1342
- suggest_existing (
1343
- err,
1439
+ ( [ name] , Some ( snippet) ) if !snippet. ends_with ( '>' ) => {
1440
+ let f = |name : & str | {
1344
1441
format ! (
1345
1442
"{}<{}>" ,
1346
1443
snippet,
1347
1444
std:: iter:: repeat( name. to_string( ) )
1348
1445
. take( count)
1349
1446
. collect:: <Vec <_>>( )
1350
1447
. join( ", " )
1351
- ) ,
1352
- ) ;
1448
+ )
1449
+ } ;
1450
+ suggest_existing ( err, & name. as_str ( ) [ ..] , & f) ;
1353
1451
}
1354
- ( 0 , _ , Some ( "&" ) ) if count == 1 => {
1452
+ ( [ ] , Some ( "&" ) ) if count == 1 => {
1355
1453
suggest_new ( err, "&'a " ) ;
1356
1454
}
1357
- ( 0 , _ , Some ( "'_" ) ) if count == 1 => {
1455
+ ( [ ] , Some ( "'_" ) ) if count == 1 => {
1358
1456
suggest_new ( err, "'a" ) ;
1359
1457
}
1360
- ( 0 , _, Some ( snippet) ) if !snippet. ends_with ( '>' ) && count == 1 => {
1361
- suggest_new ( err, & format ! ( "{}<'a>" , snippet) ) ;
1458
+ ( [ ] , Some ( snippet) ) if !snippet. ends_with ( '>' ) => {
1459
+ if snippet == "" {
1460
+ // This happens when we have `type Bar<'a> = Foo<T>` where we point at the space
1461
+ // before `T`. We will suggest `type Bar<'a> = Foo<'a, T>`.
1462
+ suggest_new (
1463
+ err,
1464
+ & std:: iter:: repeat ( "'a, " ) . take ( count) . collect :: < Vec < _ > > ( ) . join ( "" ) ,
1465
+ ) ;
1466
+ } else {
1467
+ suggest_new (
1468
+ err,
1469
+ & format ! (
1470
+ "{}<{}>" ,
1471
+ snippet,
1472
+ std:: iter:: repeat( "'a" ) . take( count) . collect:: <Vec <_>>( ) . join( ", " )
1473
+ ) ,
1474
+ ) ;
1475
+ }
1362
1476
}
1363
- ( n, ..) if n > 1 => {
1364
- let spans: Vec < Span > = lifetime_names. iter ( ) . map ( |lt| lt. span ) . collect ( ) ;
1365
- err. span_note ( spans, "these named lifetimes are available to use" ) ;
1477
+ ( lts, ..) if lts. len ( ) > 1 => {
1478
+ err. span_note ( lifetime_spans, "these named lifetimes are available to use" ) ;
1366
1479
if Some ( "" ) == snippet. as_deref ( ) {
1367
1480
// This happens when we have `Foo<T>` where we point at the space before `T`,
1368
1481
// but this can be confusing so we give a suggestion with placeholders.
0 commit comments