@@ -59,12 +59,12 @@ use rustc_data_structures::fx::FxHashMap;
59
59
use rustc_hir as hir;
60
60
use rustc_hir:: def:: { CtorKind , CtorOf , DefKind , Res } ;
61
61
use rustc_hir:: def_id:: { DefId , LOCAL_CRATE } ;
62
- use rustc_hir:: intravisit:: { self , NestedVisitorMap , Visitor } ;
62
+ use rustc_hir:: intravisit:: { self , walk_expr , ErasedMap , NestedVisitorMap , Visitor } ;
63
63
use rustc_hir:: {
64
- def, Arm , BindingAnnotation , Block , Body , Constness , CrateItem , Expr , ExprKind , FnDecl , ForeignItem , GenericArgs ,
65
- GenericParam , HirId , Impl , ImplItem , ImplItemKind , Item , ItemKind , LangItem , Lifetime , Local , MacroDef ,
66
- MatchSource , Node , Param , Pat , PatKind , Path , PathSegment , QPath , Stmt , StructField , TraitItem , TraitItemKind ,
67
- TraitRef , TyKind , Variant , Visibility ,
64
+ def, Arm , BindingAnnotation , Block , Body , Constness , CrateItem , Destination , Expr , ExprKind , FnDecl , ForeignItem ,
65
+ GenericArgs , GenericParam , HirId , Impl , ImplItem , ImplItemKind , Item , ItemKind , LangItem , Lifetime , Local ,
66
+ MacroDef , MatchSource , Node , Param , Pat , PatKind , Path , PathSegment , QPath , Stmt , StmtKind , StructField , TraitItem ,
67
+ TraitItemKind , TraitRef , TyKind , Variant , Visibility ,
68
68
} ;
69
69
use rustc_lint:: { LateContext , Level , Lint , LintContext } ;
70
70
use rustc_middle:: hir:: exports:: Export ;
@@ -81,7 +81,7 @@ use rustc_span::{Span, DUMMY_SP};
81
81
use rustc_target:: abi:: Integer ;
82
82
83
83
use crate :: consts:: { constant, Constant } ;
84
- use crate :: ty:: is_recursively_primitive_type;
84
+ use crate :: ty:: { can_partially_move_ty , is_recursively_primitive_type} ;
85
85
86
86
pub fn parse_msrv ( msrv : & str , sess : Option < & Session > , span : Option < Span > ) -> Option < RustcVersion > {
87
87
if let Ok ( version) = RustcVersion :: parse ( msrv) {
@@ -515,6 +515,73 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio
515
515
None
516
516
}
517
517
518
+ /// Checks if the top level expression can be moved into a closure as is.
519
+ pub fn can_move_expr_to_closure_no_visit ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > , jump_targets : & [ HirId ] ) -> bool {
520
+ match expr. kind {
521
+ ExprKind :: Break ( Destination { target_id : Ok ( id) , .. } , _)
522
+ | ExprKind :: Continue ( Destination { target_id : Ok ( id) , .. } )
523
+ if jump_targets. contains ( & id) =>
524
+ {
525
+ true
526
+ } ,
527
+ ExprKind :: Break ( ..)
528
+ | ExprKind :: Continue ( _)
529
+ | ExprKind :: Ret ( _)
530
+ | ExprKind :: Yield ( ..)
531
+ | ExprKind :: InlineAsm ( _)
532
+ | ExprKind :: LlvmInlineAsm ( _) => false ,
533
+ // Accessing a field of a local value can only be done if the type isn't
534
+ // partially moved.
535
+ ExprKind :: Field ( base_expr, _)
536
+ if matches ! (
537
+ base_expr. kind,
538
+ ExprKind :: Path ( QPath :: Resolved ( _, Path { res: Res :: Local ( _) , .. } ) )
539
+ ) && can_partially_move_ty ( cx, cx. typeck_results ( ) . expr_ty ( base_expr) ) =>
540
+ {
541
+ // TODO: check if the local has been partially moved. Assume it has for now.
542
+ false
543
+ }
544
+ _ => true ,
545
+ }
546
+ }
547
+
548
+ /// Checks if the expression can be moved into a closure as is.
549
+ pub fn can_move_expr_to_closure ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) -> bool {
550
+ struct V < ' cx , ' tcx > {
551
+ cx : & ' cx LateContext < ' tcx > ,
552
+ loops : Vec < HirId > ,
553
+ allow_closure : bool ,
554
+ }
555
+ impl Visitor < ' tcx > for V < ' _ , ' tcx > {
556
+ type Map = ErasedMap < ' tcx > ;
557
+ fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
558
+ NestedVisitorMap :: None
559
+ }
560
+
561
+ fn visit_expr ( & mut self , e : & ' tcx Expr < ' _ > ) {
562
+ if !self . allow_closure {
563
+ return ;
564
+ }
565
+ if let ExprKind :: Loop ( b, ..) = e. kind {
566
+ self . loops . push ( e. hir_id ) ;
567
+ self . visit_block ( b) ;
568
+ self . loops . pop ( ) ;
569
+ } else {
570
+ self . allow_closure &= can_move_expr_to_closure_no_visit ( self . cx , e, & self . loops ) ;
571
+ walk_expr ( self , e) ;
572
+ }
573
+ }
574
+ }
575
+
576
+ let mut v = V {
577
+ cx,
578
+ allow_closure : true ,
579
+ loops : Vec :: new ( ) ,
580
+ } ;
581
+ v. visit_expr ( expr) ;
582
+ v. allow_closure
583
+ }
584
+
518
585
/// Returns the method names and argument list of nested method call expressions that make up
519
586
/// `expr`. method/span lists are sorted with the most recent call first.
520
587
pub fn method_calls < ' tcx > (
@@ -1254,6 +1321,48 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1254
1321
did. map_or ( false , |did| must_use_attr ( & cx. tcx . get_attrs ( did) ) . is_some ( ) )
1255
1322
}
1256
1323
1324
+ pub fn get_expr_use_node ( tcx : TyCtxt < ' tcx > , expr : & Expr < ' _ > ) -> Option < Node < ' tcx > > {
1325
+ let map = tcx. hir ( ) ;
1326
+ let mut child_id = expr. hir_id ;
1327
+ let mut iter = map. parent_iter ( child_id) ;
1328
+ loop {
1329
+ match iter. next ( ) {
1330
+ None => break None ,
1331
+ Some ( ( id, Node :: Block ( _) ) ) => child_id = id,
1332
+ Some ( ( id, Node :: Arm ( arm) ) ) if arm. body . hir_id == child_id => child_id = id,
1333
+ Some ( ( _, Node :: Expr ( expr) ) ) => match expr. kind {
1334
+ ExprKind :: Break (
1335
+ Destination {
1336
+ target_id : Ok ( dest) , ..
1337
+ } ,
1338
+ _,
1339
+ ) => {
1340
+ iter = map. parent_iter ( dest) ;
1341
+ child_id = dest;
1342
+ } ,
1343
+ ExprKind :: DropTemps ( _) | ExprKind :: Block ( ..) => child_id = expr. hir_id ,
1344
+ ExprKind :: If ( control_expr, ..) | ExprKind :: Match ( control_expr, ..)
1345
+ if control_expr. hir_id != child_id =>
1346
+ {
1347
+ child_id = expr. hir_id
1348
+ } ,
1349
+ _ => break Some ( Node :: Expr ( expr) ) ,
1350
+ } ,
1351
+ Some ( ( _, node) ) => break Some ( node) ,
1352
+ }
1353
+ }
1354
+ }
1355
+
1356
+ pub fn is_expr_used ( tcx : TyCtxt < ' _ > , expr : & Expr < ' _ > ) -> bool {
1357
+ !matches ! (
1358
+ get_expr_use_node( tcx, expr) ,
1359
+ Some ( Node :: Stmt ( Stmt {
1360
+ kind: StmtKind :: Expr ( _) | StmtKind :: Semi ( _) ,
1361
+ ..
1362
+ } ) )
1363
+ )
1364
+ }
1365
+
1257
1366
pub fn is_no_std_crate ( cx : & LateContext < ' _ > ) -> bool {
1258
1367
cx. tcx . hir ( ) . attrs ( hir:: CRATE_HIR_ID ) . iter ( ) . any ( |attr| {
1259
1368
if let ast:: AttrKind :: Normal ( ref attr, _) = attr. kind {
@@ -1413,28 +1522,43 @@ pub fn peel_hir_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
1413
1522
peel ( pat, 0 )
1414
1523
}
1415
1524
1525
+ /// Peels of expressions while the given closure returns `Some`.
1526
+ pub fn peel_hir_expr_while < ' tcx > (
1527
+ mut expr : & ' tcx Expr < ' tcx > ,
1528
+ mut f : impl FnMut ( & ' tcx Expr < ' tcx > ) -> Option < & ' tcx Expr < ' tcx > > ,
1529
+ ) -> & ' tcx Expr < ' tcx > {
1530
+ while let Some ( e) = f ( expr) {
1531
+ expr = e;
1532
+ }
1533
+ expr
1534
+ }
1535
+
1416
1536
/// Peels off up to the given number of references on the expression. Returns the underlying
1417
1537
/// expression and the number of references removed.
1418
1538
pub fn peel_n_hir_expr_refs ( expr : & ' a Expr < ' a > , count : usize ) -> ( & ' a Expr < ' a > , usize ) {
1419
- fn f ( expr : & ' a Expr < ' a > , count : usize , target : usize ) -> ( & ' a Expr < ' a > , usize ) {
1420
- match expr. kind {
1421
- ExprKind :: AddrOf ( _, _, expr) if count != target => f ( expr, count + 1 , target) ,
1422
- _ => ( expr, count) ,
1423
- }
1424
- }
1425
- f ( expr, 0 , count)
1539
+ let mut remaining = count;
1540
+ let e = peel_hir_expr_while ( expr, |e| match e. kind {
1541
+ ExprKind :: AddrOf ( BorrowKind :: Ref , _, e) if remaining != 0 => {
1542
+ remaining -= 1 ;
1543
+ Some ( e)
1544
+ } ,
1545
+ _ => None ,
1546
+ } ) ;
1547
+ ( e, count - remaining)
1426
1548
}
1427
1549
1428
1550
/// Peels off all references on the expression. Returns the underlying expression and the number of
1429
1551
/// references removed.
1430
1552
pub fn peel_hir_expr_refs ( expr : & ' a Expr < ' a > ) -> ( & ' a Expr < ' a > , usize ) {
1431
- fn f ( expr : & ' a Expr < ' a > , count : usize ) -> ( & ' a Expr < ' a > , usize ) {
1432
- match expr. kind {
1433
- ExprKind :: AddrOf ( BorrowKind :: Ref , _, expr) => f ( expr, count + 1 ) ,
1434
- _ => ( expr, count) ,
1435
- }
1436
- }
1437
- f ( expr, 0 )
1553
+ let mut count = 0 ;
1554
+ let e = peel_hir_expr_while ( expr, |e| match e. kind {
1555
+ ExprKind :: AddrOf ( BorrowKind :: Ref , _, e) => {
1556
+ count += 1 ;
1557
+ Some ( e)
1558
+ } ,
1559
+ _ => None ,
1560
+ } ) ;
1561
+ ( e, count)
1438
1562
}
1439
1563
1440
1564
#[ macro_export]
0 commit comments