@@ -1311,43 +1311,167 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
1311
1311
issue_span : Span ,
1312
1312
expr_span : Span ,
1313
1313
body_expr : Option < & ' hir hir:: Expr < ' hir > > ,
1314
- loop_bind : Option < Symbol > ,
1314
+ loop_bind : Option < & ' hir Ident > ,
1315
+ loop_span : Option < Span > ,
1316
+ head_span : Option < Span > ,
1317
+ pat_span : Option < Span > ,
1318
+ head : Option < & ' hir hir:: Expr < ' hir > > ,
1315
1319
}
1316
1320
impl < ' hir > Visitor < ' hir > for ExprFinder < ' hir > {
1317
1321
fn visit_expr ( & mut self , ex : & ' hir hir:: Expr < ' hir > ) {
1318
- if let hir:: ExprKind :: Loop ( hir:: Block { stmts : [ stmt, ..] , ..} , _, hir:: LoopSource :: ForLoop , _) = ex. kind &&
1319
- let hir:: StmtKind :: Expr ( hir:: Expr { kind : hir:: ExprKind :: Match ( call, [ _, bind, ..] , _) , ..} ) = stmt. kind &&
1320
- let hir:: ExprKind :: Call ( path, _args) = call. kind &&
1321
- let hir:: ExprKind :: Path ( hir:: QPath :: LangItem ( LangItem :: IteratorNext , _, _, ) ) = path. kind &&
1322
- let hir:: PatKind :: Struct ( path, [ field, ..] , _) = bind. pat . kind &&
1323
- let hir:: QPath :: LangItem ( LangItem :: OptionSome , _, _) = path &&
1324
- let PatField { pat : hir:: Pat { kind : hir:: PatKind :: Binding ( _, _, ident, ..) , .. } , ..} = field &&
1325
- self . issue_span . source_equal ( call. span ) {
1326
- self . loop_bind = Some ( ident. name ) ;
1322
+ // Try to find
1323
+ // let result = match IntoIterator::into_iter(<head>) {
1324
+ // mut iter => {
1325
+ // [opt_ident]: loop {
1326
+ // match Iterator::next(&mut iter) {
1327
+ // None => break,
1328
+ // Some(<pat>) => <body>,
1329
+ // };
1330
+ // }
1331
+ // }
1332
+ // };
1333
+ // corresponding to the desugaring of a for loop `for <pat> in <head> { <body> }`.
1334
+ if let hir:: ExprKind :: Call ( path, [ arg] ) = ex. kind
1335
+ && let hir:: ExprKind :: Path (
1336
+ hir:: QPath :: LangItem ( LangItem :: IntoIterIntoIter , _, _) ,
1337
+ ) = path. kind
1338
+ && arg. span == self . issue_span
1339
+ {
1340
+ // Find `IntoIterator::into_iter(<head>)`
1341
+ self . head = Some ( arg) ;
1342
+ }
1343
+ if let hir:: ExprKind :: Loop (
1344
+ hir:: Block { stmts : [ stmt, ..] , .. } ,
1345
+ _,
1346
+ hir:: LoopSource :: ForLoop ,
1347
+ _,
1348
+ ) = ex. kind
1349
+ && let hir:: StmtKind :: Expr ( hir:: Expr {
1350
+ kind : hir:: ExprKind :: Match ( call, [ _, bind, ..] , _) ,
1351
+ span : head_span,
1352
+ ..
1353
+ } ) = stmt. kind
1354
+ && let hir:: ExprKind :: Call ( path, _args) = call. kind
1355
+ && let hir:: ExprKind :: Path (
1356
+ hir:: QPath :: LangItem ( LangItem :: IteratorNext , _, _) ,
1357
+ ) = path. kind
1358
+ && let hir:: PatKind :: Struct ( path, [ field, ..] , _) = bind. pat . kind
1359
+ && let hir:: QPath :: LangItem ( LangItem :: OptionSome , pat_span, _) = path
1360
+ && self . issue_span . source_equal ( call. span )
1361
+ {
1362
+ // Find `<pat>` and the span for the whole `for` loop.
1363
+ if let PatField { pat : hir:: Pat {
1364
+ kind : hir:: PatKind :: Binding ( _, _, ident, ..) ,
1365
+ ..
1366
+ } , ..} = field {
1367
+ self . loop_bind = Some ( ident) ;
1327
1368
}
1369
+ self . head_span = Some ( * head_span) ;
1370
+ self . pat_span = Some ( pat_span) ;
1371
+ self . loop_span = Some ( stmt. span ) ;
1372
+ }
1328
1373
1329
- if let hir:: ExprKind :: MethodCall ( body_call, _recv, ..) = ex. kind &&
1330
- body_call. ident . name == sym:: next && ex. span . source_equal ( self . expr_span ) {
1331
- self . body_expr = Some ( ex) ;
1374
+ if let hir:: ExprKind :: MethodCall ( body_call, _recv, ..) = ex. kind
1375
+ && body_call. ident . name == sym:: next && ex. span . source_equal ( self . expr_span )
1376
+ {
1377
+ self . body_expr = Some ( ex) ;
1332
1378
}
1333
1379
1334
1380
hir:: intravisit:: walk_expr ( self , ex) ;
1335
1381
}
1336
1382
}
1337
- let mut finder =
1338
- ExprFinder { expr_span : span, issue_span, loop_bind : None , body_expr : None } ;
1383
+ let mut finder = ExprFinder {
1384
+ expr_span : span,
1385
+ issue_span,
1386
+ loop_bind : None ,
1387
+ body_expr : None ,
1388
+ head_span : None ,
1389
+ loop_span : None ,
1390
+ pat_span : None ,
1391
+ head : None ,
1392
+ } ;
1339
1393
finder. visit_expr ( hir. body ( body_id) . value ) ;
1340
1394
1341
- if let Some ( loop_bind) = finder. loop_bind &&
1342
- let Some ( body_expr) = finder. body_expr &&
1343
- let Some ( def_id) = typeck_results. type_dependent_def_id ( body_expr. hir_id ) &&
1344
- let Some ( trait_did) = tcx. trait_of_item ( def_id) &&
1345
- tcx. is_diagnostic_item ( sym:: Iterator , trait_did) {
1346
- err. note ( format ! (
1347
- "a for loop advances the iterator for you, the result is stored in `{}`." ,
1348
- loop_bind
1395
+ if let Some ( body_expr) = finder. body_expr
1396
+ && let Some ( loop_span) = finder. loop_span
1397
+ && let Some ( def_id) = typeck_results. type_dependent_def_id ( body_expr. hir_id )
1398
+ && let Some ( trait_did) = tcx. trait_of_item ( def_id)
1399
+ && tcx. is_diagnostic_item ( sym:: Iterator , trait_did)
1400
+ {
1401
+ if let Some ( loop_bind) = finder. loop_bind {
1402
+ err. note ( format ! (
1403
+ "a for loop advances the iterator for you, the result is stored in `{}`" ,
1404
+ loop_bind. name,
1405
+ ) ) ;
1406
+ } else {
1407
+ err. note (
1408
+ "a for loop advances the iterator for you, the result is stored in its pattern" ,
1409
+ ) ;
1410
+ }
1411
+ let msg = "if you want to call `next` on a iterator within the loop, consider using \
1412
+ `while let`";
1413
+ if let Some ( head) = finder. head
1414
+ && let Some ( pat_span) = finder. pat_span
1415
+ && loop_span. contains ( body_expr. span )
1416
+ && loop_span. contains ( head. span )
1417
+ {
1418
+ let sm = self . infcx . tcx . sess . source_map ( ) ;
1419
+
1420
+ let mut sugg = vec ! [ ] ;
1421
+ if let hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( None , _) ) = head. kind {
1422
+ // A bare path doesn't need a `let` assignment, it's already a simple
1423
+ // binding access.
1424
+ // As a new binding wasn't added, we don't need to modify the advancing call.
1425
+ sugg. push ( (
1426
+ loop_span. with_hi ( pat_span. lo ( ) ) ,
1427
+ format ! ( "while let Some(" ) ,
1428
+ ) ) ;
1429
+ sugg. push ( (
1430
+ pat_span. shrink_to_hi ( ) . with_hi ( head. span . lo ( ) ) ,
1431
+ ") = " . to_string ( ) ,
1432
+ ) ) ;
1433
+ sugg. push ( (
1434
+ head. span . shrink_to_hi ( ) ,
1435
+ ".next()" . to_string ( ) ,
1436
+ ) ) ;
1437
+ } else {
1438
+ // Needs a new a `let` binding.
1439
+ let indent = if let Some ( indent) = sm. indentation_before ( loop_span) {
1440
+ format ! ( "\n {indent}" )
1441
+ } else {
1442
+ " " . to_string ( )
1443
+ } ;
1444
+ let Ok ( head_str) = sm. span_to_snippet ( head. span ) else {
1445
+ err. help ( msg) ;
1446
+ return ;
1447
+ } ;
1448
+ sugg. push ( (
1449
+ loop_span. with_hi ( pat_span. lo ( ) ) ,
1450
+ format ! ( "let iter = {head_str};{indent}while let Some(" ) ,
1349
1451
) ) ;
1350
- err. help ( "if you want to call `next` on a iterator within the loop, consider using `while let`." ) ;
1452
+ sugg. push ( (
1453
+ pat_span. shrink_to_hi ( ) . with_hi ( head. span . hi ( ) ) ,
1454
+ ") = iter.next()" . to_string ( ) ,
1455
+ ) ) ;
1456
+ // As a new binding was added, we should change how the iterator is advanced to
1457
+ // use the newly introduced binding.
1458
+ if let hir:: ExprKind :: MethodCall ( _, recv, ..) = body_expr. kind
1459
+ && let hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( None , ..) ) = recv. kind
1460
+ {
1461
+ // As we introduced a `let iter = <head>;`, we need to change where the
1462
+ // already borrowed value was accessed from `<recv>.next()` to
1463
+ // `iter.next()`.
1464
+ sugg. push ( ( recv. span , "iter" . to_string ( ) ) ) ;
1465
+ }
1466
+ }
1467
+ err. multipart_suggestion (
1468
+ msg,
1469
+ sugg,
1470
+ Applicability :: MaybeIncorrect ,
1471
+ ) ;
1472
+ } else {
1473
+ err. help ( msg) ;
1474
+ }
1351
1475
}
1352
1476
}
1353
1477
0 commit comments