@@ -16,8 +16,8 @@ use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
1616use rustc_hir:: PrimTy ;
1717use rustc_session:: config:: nightly_options;
1818use 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 } ;
2121
2222use log:: debug;
2323
@@ -33,6 +33,7 @@ enum AssocSuggestion {
3333crate enum MissingLifetimeSpot < ' tcx > {
3434 Generics ( & ' tcx hir:: Generics < ' tcx > ) ,
3535 HigherRanked { span : Span , span_type : ForLifetimeSpanType } ,
36+ Static ,
3637}
3738
3839crate enum ForLifetimeSpanType {
@@ -1195,6 +1196,7 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
11951196 https://doc.rust-lang.org/nomicon/hrtb.html",
11961197 ) ;
11971198 }
1199+ _ => { }
11981200 }
11991201 }
12001202 if nightly_options:: is_nightly_build ( )
@@ -1253,7 +1255,8 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
12531255 err : & mut DiagnosticBuilder < ' _ > ,
12541256 span : Span ,
12551257 count : usize ,
1256- lifetime_names : & FxHashSet < Ident > ,
1258+ lifetime_names : & FxHashSet < Symbol > ,
1259+ lifetime_spans : Vec < Span > ,
12571260 params : & [ ElisionFailureInfo ] ,
12581261 ) {
12591262 let snippet = self . tcx . sess . source_map ( ) . span_to_snippet ( span) . ok ( ) ;
@@ -1267,11 +1270,60 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
12671270 ) ,
12681271 ) ;
12691272
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+
12711323 err. span_suggestion_verbose (
12721324 span,
12731325 & format ! ( "consider using the `{}` lifetime" , lifetime_names. iter( ) . next( ) . unwrap( ) ) ,
1274- sugg ,
1326+ formatter ( name ) ,
12751327 Applicability :: MaybeIncorrect ,
12761328 ) ;
12771329 } ;
@@ -1282,6 +1334,15 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
12821334 let should_break;
12831335 introduce_suggestion. push ( match missing {
12841336 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+ }
12851346 msg = "consider introducing a named lifetime parameter" . to_string ( ) ;
12861347 should_break = true ;
12871348 if let Some ( param) = generics. params . iter ( ) . find ( |p| match p. kind {
@@ -1308,6 +1369,42 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
13081369 ) ;
13091370 ( * span, span_type. suggestion ( "'a" ) )
13101371 }
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+ }
13111408 } ) ;
13121409 for param in params {
13131410 if let Ok ( snippet) = self . tcx . sess . source_map ( ) . span_to_snippet ( param. span ) {
@@ -1328,41 +1425,57 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
13281425 }
13291426 } ;
13301427
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) ) ;
13341432 }
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 ( ) ) ;
13371435 }
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) ) ;
13401438 }
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 | {
13441441 format ! (
13451442 "{}<{}>" ,
13461443 snippet,
13471444 std:: iter:: repeat( name. to_string( ) )
13481445 . take( count)
13491446 . collect:: <Vec <_>>( )
13501447 . join( ", " )
1351- ) ,
1352- ) ;
1448+ )
1449+ } ;
1450+ suggest_existing ( err, & name. as_str ( ) [ ..] , & f) ;
13531451 }
1354- ( 0 , _ , Some ( "&" ) ) if count == 1 => {
1452+ ( [ ] , Some ( "&" ) ) if count == 1 => {
13551453 suggest_new ( err, "&'a " ) ;
13561454 }
1357- ( 0 , _ , Some ( "'_" ) ) if count == 1 => {
1455+ ( [ ] , Some ( "'_" ) ) if count == 1 => {
13581456 suggest_new ( err, "'a" ) ;
13591457 }
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+ }
13621476 }
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" ) ;
13661479 if Some ( "" ) == snippet. as_deref ( ) {
13671480 // This happens when we have `Foo<T>` where we point at the space before `T`,
13681481 // but this can be confusing so we give a suggestion with placeholders.
0 commit comments