@@ -17,6 +17,7 @@ use rustc::mir::{
1717 LocalDecl , LocalKind , Location , Operand , Place , ProjectionElem , Rvalue , Statement ,
1818 StatementKind , VarBindingForm ,
1919} ;
20+ use rustc:: hir;
2021use rustc:: hir:: def_id:: DefId ;
2122use rustc:: ty;
2223use rustc_data_structures:: fx:: FxHashSet ;
@@ -524,10 +525,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
524525 err. span_label (
525526 drop_span,
526527 format ! (
527- "...but `{}` is only valid for the duration of the `{}` function, so it \
528- is dropped here while still borrowed",
529- name,
530- self . infcx. tcx. hir. name( fn_node_id) ,
528+ "but `{}` will be dropped here, when the function `{}` returns" ,
529+ name, self . infcx. tcx. hir. name( fn_node_id) ,
531530 )
532531 ) ;
533532
@@ -1235,67 +1234,137 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
12351234 let fn_node_id = self . infcx . tcx . hir . as_local_node_id ( did) ?;
12361235 let fn_decl = self . infcx . tcx . hir . fn_decl ( fn_node_id) ?;
12371236
1238- // If there is one argument and this error is being reported, that means
1239- // that the lifetime of the borrow could not be made to match the single
1240- // argument's lifetime. We can highlight it.
1237+ // We need to work out which arguments to highlight. We do this by looking
1238+ // at the return type, where there are three cases:
12411239 //
1242- // If there is more than one argument and this error is being reported, that
1243- // means there must be a self parameter - as otherwise there would be an error
1244- // from lifetime elision and not this. So we highlight the self parameter.
1245- let argument_span = fn_decl. inputs . first ( ) ?. span ;
1246- let argument_ty = sig. inputs ( ) . skip_binder ( ) . first ( ) ?;
1247- if is_closure {
1248- // Closure arguments are wrapped in a tuple, so we need to get the first
1249- // from that.
1250- let argument_ty = if let ty:: TyKind :: Tuple ( elems) = argument_ty. sty {
1251- let argument_ty = elems. first ( ) ?;
1252- if let ty:: TyKind :: Ref ( _, _, _) = argument_ty. sty {
1253- argument_ty
1254- } else {
1240+ // 1. If there are named arguments, then we should highlight the return type and
1241+ // highlight any of the arguments that are also references with that lifetime.
1242+ // If there are no arguments that have the same lifetime as the return type,
1243+ // then don't highlight anything.
1244+ // 2. The return type is a reference with an anonymous lifetime. If this is
1245+ // the case, then we can take advantage of (and teach) the lifetime elision
1246+ // rules.
1247+ //
1248+ // We know that an error is being reported. So the arguments and return type
1249+ // must satisfy the elision rules. Therefore, if there is a single argument
1250+ // then that means the return type and first (and only) argument have the same
1251+ // lifetime and the borrow isn't meeting that, we can highlight the argument
1252+ // and return type.
1253+ //
1254+ // If there are multiple arguments then the first argument must be self (else
1255+ // it would not satisfy the elision rules), so we can highlight self and the
1256+ // return type.
1257+ // 3. The return type is not a reference. In this case, we don't highlight
1258+ // anything.
1259+ let return_ty = sig. output ( ) ;
1260+ match return_ty. skip_binder ( ) . sty {
1261+ ty:: TyKind :: Ref ( return_region, _, _) if return_region. has_name ( ) && !is_closure => {
1262+ // This is case 1 from above, return type is a named reference so we need to
1263+ // search for relevant arguments.
1264+ let mut arguments = Vec :: new ( ) ;
1265+ for ( index, argument) in sig. inputs ( ) . skip_binder ( ) . iter ( ) . enumerate ( ) {
1266+ if let ty:: TyKind :: Ref ( argument_region, _, _) = argument. sty {
1267+ if argument_region == return_region {
1268+ // Need to use the `rustc::ty` types to compare against the
1269+ // `return_region`. Then use the `rustc::hir` type to get only
1270+ // the lifetime span.
1271+ match & fn_decl. inputs [ index] . node {
1272+ hir:: TyKind :: Rptr ( lifetime, _) => {
1273+ // With access to the lifetime, we can get
1274+ // the span of it.
1275+ arguments. push ( ( * argument, lifetime. span ) ) ;
1276+ } ,
1277+ _ => bug ! ( "ty type is a ref but hir type is not" ) ,
1278+ }
1279+ }
1280+ }
1281+ }
1282+
1283+ // We need to have arguments. This shouldn't happen, but it's worth checking.
1284+ if arguments. is_empty ( ) {
12551285 return None ;
12561286 }
1257- } else {
1258- return None
1259- } ;
12601287
1261- Some ( AnnotatedBorrowFnSignature :: Closure {
1262- argument_ty,
1263- argument_span,
1264- } )
1265- } else if let ty:: TyKind :: Ref ( argument_region, _, _) = argument_ty. sty {
1266- // We only consider the return type for functions.
1267- let return_span = fn_decl. output . span ( ) ;
1268-
1269- let return_ty = sig. output ( ) ;
1270- let ( return_region, return_ty) = if let ty:: TyKind :: Ref (
1271- return_region, _, _
1272- ) = return_ty. skip_binder ( ) . sty {
1273- ( return_region, * return_ty. skip_binder ( ) )
1274- } else {
1275- return None ;
1276- } ;
1288+ // We use a mix of the HIR and the Ty types to get information
1289+ // as the HIR doesn't have full types for closure arguments.
1290+ let return_ty = * sig. output ( ) . skip_binder ( ) ;
1291+ let mut return_span = fn_decl. output . span ( ) ;
1292+ if let hir:: FunctionRetTy :: Return ( ty) = fn_decl. output {
1293+ if let hir:: TyKind :: Rptr ( lifetime, _) = ty. into_inner ( ) . node {
1294+ return_span = lifetime. span ;
1295+ }
1296+ }
12771297
1278- Some ( AnnotatedBorrowFnSignature :: Function {
1279- argument_ty,
1280- argument_span,
1281- return_ty,
1282- return_span,
1283- regions_equal : return_region == argument_region,
1284- } )
1285- } else {
1286- None
1298+ Some ( AnnotatedBorrowFnSignature :: NamedFunction {
1299+ arguments,
1300+ return_ty,
1301+ return_span,
1302+ } )
1303+ } ,
1304+ ty:: TyKind :: Ref ( _, _, _) if is_closure => {
1305+ // This is case 2 from above but only for closures, return type is anonymous
1306+ // reference so we select
1307+ // the first argument.
1308+ let argument_span = fn_decl. inputs . first ( ) ?. span ;
1309+ let argument_ty = sig. inputs ( ) . skip_binder ( ) . first ( ) ?;
1310+
1311+ // Closure arguments are wrapped in a tuple, so we need to get the first
1312+ // from that.
1313+ if let ty:: TyKind :: Tuple ( elems) = argument_ty. sty {
1314+ let argument_ty = elems. first ( ) ?;
1315+ if let ty:: TyKind :: Ref ( _, _, _) = argument_ty. sty {
1316+ return Some ( AnnotatedBorrowFnSignature :: Closure {
1317+ argument_ty,
1318+ argument_span,
1319+ } ) ;
1320+ }
1321+ }
1322+
1323+ None
1324+ } ,
1325+ ty:: TyKind :: Ref ( _, _, _) => {
1326+ // This is also case 2 from above but for functions, return type is still an
1327+ // anonymous reference so we select the first argument.
1328+ let argument_span = fn_decl. inputs . first ( ) ?. span ;
1329+ let argument_ty = sig. inputs ( ) . skip_binder ( ) . first ( ) ?;
1330+
1331+ let return_span = fn_decl. output . span ( ) ;
1332+ let return_ty = * sig. output ( ) . skip_binder ( ) ;
1333+
1334+ // We expect the first argument to be a reference.
1335+ match argument_ty. sty {
1336+ ty:: TyKind :: Ref ( _, _, _) => { } ,
1337+ _ => return None ,
1338+ }
1339+
1340+ Some ( AnnotatedBorrowFnSignature :: AnonymousFunction {
1341+ argument_ty,
1342+ argument_span,
1343+ return_ty,
1344+ return_span,
1345+ } )
1346+ } ,
1347+ _ => {
1348+ // This is case 3 from above, return type is not a reference so don't highlight
1349+ // anything.
1350+ None
1351+ } ,
12871352 }
12881353 }
12891354}
12901355
12911356#[ derive( Debug ) ]
12921357enum AnnotatedBorrowFnSignature < ' tcx > {
1293- Function {
1358+ NamedFunction {
1359+ arguments : Vec < ( ty:: Ty < ' tcx > , Span ) > ,
1360+ return_ty : ty:: Ty < ' tcx > ,
1361+ return_span : Span ,
1362+ } ,
1363+ AnonymousFunction {
12941364 argument_ty : ty:: Ty < ' tcx > ,
12951365 argument_span : Span ,
12961366 return_ty : ty:: Ty < ' tcx > ,
12971367 return_span : Span ,
1298- regions_equal : bool ,
12991368 } ,
13001369 Closure {
13011370 argument_ty : ty:: Ty < ' tcx > ,
@@ -1304,57 +1373,86 @@ enum AnnotatedBorrowFnSignature<'tcx> {
13041373}
13051374
13061375impl < ' tcx > AnnotatedBorrowFnSignature < ' tcx > {
1376+ /// Annotate the provided diagnostic with information about borrow from the fn signature that
1377+ /// helps explain.
13071378 fn emit (
13081379 & self ,
13091380 diag : & mut DiagnosticBuilder < ' _ >
13101381 ) -> String {
1311- let ( argument_ty, argument_span) = match self {
1312- AnnotatedBorrowFnSignature :: Function {
1313- argument_ty,
1314- argument_span,
1315- ..
1316- } => ( argument_ty, argument_span) ,
1317- AnnotatedBorrowFnSignature :: Closure {
1382+ match self {
1383+ AnnotatedBorrowFnSignature :: Closure { argument_ty, argument_span } => {
1384+ diag. span_label (
1385+ * argument_span,
1386+ format ! ( "has type `{}`" , self . get_name_for_ty( argument_ty, 0 ) ) ,
1387+ ) ;
1388+
1389+ self . get_region_name_for_ty ( argument_ty, 0 )
1390+ } ,
1391+ AnnotatedBorrowFnSignature :: AnonymousFunction {
13181392 argument_ty,
13191393 argument_span,
1320- } => ( argument_ty, argument_span) ,
1321- } ;
1394+ return_ty,
1395+ return_span,
1396+ } => {
1397+ let argument_ty_name = self . get_name_for_ty ( argument_ty, 0 ) ;
1398+ diag. span_label (
1399+ * argument_span,
1400+ format ! ( "has type `{}`" , argument_ty_name)
1401+ ) ;
13221402
1323- let ( argument_region_name, argument_ty_name) = (
1324- self . get_region_name_for_ty ( argument_ty, 0 ) ,
1325- self . get_name_for_ty ( argument_ty, 0 ) ,
1326- ) ;
1327- diag. span_label (
1328- * argument_span,
1329- format ! ( "has type `{}`" , argument_ty_name)
1330- ) ;
1403+ let return_ty_name = self . get_name_for_ty ( return_ty, 0 ) ;
1404+ let types_equal = return_ty_name == argument_ty_name;
1405+ diag. span_label (
1406+ * return_span,
1407+ format ! (
1408+ "{}has type `{}`" ,
1409+ if types_equal { "also " } else { "" } ,
1410+ return_ty_name,
1411+ )
1412+ ) ;
13311413
1332- // Only emit labels for the return value when we're annotating a function.
1333- if let AnnotatedBorrowFnSignature :: Function {
1334- return_ty,
1335- return_span,
1336- regions_equal,
1337- ..
1338- } = self {
1339- let counter = if * regions_equal { 0 } else { 1 } ;
1340- let ( return_region_name, return_ty_name) = (
1341- self . get_region_name_for_ty ( return_ty, counter) ,
1342- self . get_name_for_ty ( return_ty, counter)
1343- ) ;
1414+ diag. note (
1415+ "argument and return type have the same lifetime due to lifetime elision rules" ,
1416+ ) ;
1417+ diag. note (
1418+ "to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch10-03-\
1419+ lifetime-syntax.html#lifetime-elision>",
1420+ ) ;
13441421
1345- let types_equal = return_ty_name == argument_ty_name;
1346- diag. span_label (
1347- * return_span,
1348- format ! (
1349- "{}has type `{}`" ,
1350- if types_equal { "also " } else { "" } ,
1351- return_ty_name,
1352- )
1353- ) ;
1422+ self . get_region_name_for_ty ( return_ty, 0 )
1423+ } ,
1424+ AnnotatedBorrowFnSignature :: NamedFunction {
1425+ arguments,
1426+ return_ty,
1427+ return_span,
1428+ } => {
1429+ // Region of return type and arguments checked to be the same earlier.
1430+ let region_name = self . get_region_name_for_ty ( return_ty, 0 ) ;
1431+ for ( _, argument_span) in arguments {
1432+ diag. span_label (
1433+ * argument_span,
1434+ format ! ( "has lifetime `{}`" , region_name)
1435+ ) ;
1436+ }
13541437
1355- return_region_name
1356- } else {
1357- argument_region_name
1438+ diag. span_label (
1439+ * return_span,
1440+ format ! (
1441+ "also has lifetime `{}`" ,
1442+ region_name,
1443+ )
1444+ ) ;
1445+
1446+ diag. help (
1447+ & format ! (
1448+ "use data from the highlighted arguments which match the `{}` lifetime of \
1449+ the return type",
1450+ region_name,
1451+ ) ,
1452+ ) ;
1453+
1454+ region_name
1455+ } ,
13581456 }
13591457 }
13601458
0 commit comments