@@ -60,12 +60,12 @@ use rustc_data_structures::fx::FxHashMap;
60
60
use rustc_hir as hir;
61
61
use rustc_hir:: def:: { DefKind , Res } ;
62
62
use rustc_hir:: def_id:: { DefId , LOCAL_CRATE } ;
63
- use rustc_hir:: intravisit:: { self , NestedVisitorMap , Visitor } ;
63
+ use rustc_hir:: intravisit:: { self , walk_expr , ErasedMap , NestedVisitorMap , Visitor } ;
64
64
use rustc_hir:: LangItem :: { ResultErr , ResultOk } ;
65
65
use rustc_hir:: {
66
- def, Arm , BindingAnnotation , Block , Body , Constness , Expr , ExprKind , FnDecl , GenericArgs , HirId , Impl , ImplItem ,
67
- ImplItemKind , Item , ItemKind , LangItem , MatchSource , Node , Param , Pat , PatKind , Path , PathSegment , QPath ,
68
- TraitItem , TraitItemKind , TraitRef , TyKind ,
66
+ def, Arm , BindingAnnotation , Block , Body , Constness , Destination , Expr , ExprKind , FnDecl , GenericArgs , HirId , Impl ,
67
+ ImplItem , ImplItemKind , Item , ItemKind , LangItem , Local , MatchSource , Node , Param , Pat , PatKind , Path , PathSegment ,
68
+ QPath , Stmt , StmtKind , TraitItem , TraitItemKind , TraitRef , TyKind ,
69
69
} ;
70
70
use rustc_lint:: { LateContext , Level , Lint , LintContext } ;
71
71
use rustc_middle:: hir:: exports:: Export ;
@@ -82,7 +82,7 @@ use rustc_span::{Span, DUMMY_SP};
82
82
use rustc_target:: abi:: Integer ;
83
83
84
84
use crate :: consts:: { constant, Constant } ;
85
- use crate :: ty:: is_recursively_primitive_type;
85
+ use crate :: ty:: { can_partially_move_ty , is_recursively_primitive_type} ;
86
86
87
87
pub fn parse_msrv ( msrv : & str , sess : Option < & Session > , span : Option < Span > ) -> Option < RustcVersion > {
88
88
if let Ok ( version) = RustcVersion :: parse ( msrv) {
@@ -548,6 +548,73 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio
548
548
None
549
549
}
550
550
551
+ /// Checks if the top level expression can be moved into a closure as is.
552
+ pub fn can_move_expr_to_closure_no_visit ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > , jump_targets : & [ HirId ] ) -> bool {
553
+ match expr. kind {
554
+ ExprKind :: Break ( Destination { target_id : Ok ( id) , .. } , _)
555
+ | ExprKind :: Continue ( Destination { target_id : Ok ( id) , .. } )
556
+ if jump_targets. contains ( & id) =>
557
+ {
558
+ true
559
+ } ,
560
+ ExprKind :: Break ( ..)
561
+ | ExprKind :: Continue ( _)
562
+ | ExprKind :: Ret ( _)
563
+ | ExprKind :: Yield ( ..)
564
+ | ExprKind :: InlineAsm ( _)
565
+ | ExprKind :: LlvmInlineAsm ( _) => false ,
566
+ // Accessing a field of a local value can only be done if the type isn't
567
+ // partially moved.
568
+ ExprKind :: Field ( base_expr, _)
569
+ if matches ! (
570
+ base_expr. kind,
571
+ ExprKind :: Path ( QPath :: Resolved ( _, Path { res: Res :: Local ( _) , .. } ) )
572
+ ) && can_partially_move_ty ( cx, cx. typeck_results ( ) . expr_ty ( base_expr) ) =>
573
+ {
574
+ // TODO: check if the local has been partially moved. Assume it has for now.
575
+ false
576
+ }
577
+ _ => true ,
578
+ }
579
+ }
580
+
581
+ /// Checks if the expression can be moved into a closure as is.
582
+ pub fn can_move_expr_to_closure ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) -> bool {
583
+ struct V < ' cx , ' tcx > {
584
+ cx : & ' cx LateContext < ' tcx > ,
585
+ loops : Vec < HirId > ,
586
+ allow_closure : bool ,
587
+ }
588
+ impl Visitor < ' tcx > for V < ' _ , ' tcx > {
589
+ type Map = ErasedMap < ' tcx > ;
590
+ fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
591
+ NestedVisitorMap :: None
592
+ }
593
+
594
+ fn visit_expr ( & mut self , e : & ' tcx Expr < ' _ > ) {
595
+ if !self . allow_closure {
596
+ return ;
597
+ }
598
+ if let ExprKind :: Loop ( b, ..) = e. kind {
599
+ self . loops . push ( e. hir_id ) ;
600
+ self . visit_block ( b) ;
601
+ self . loops . pop ( ) ;
602
+ } else {
603
+ self . allow_closure &= can_move_expr_to_closure_no_visit ( self . cx , e, & self . loops ) ;
604
+ walk_expr ( self , e) ;
605
+ }
606
+ }
607
+ }
608
+
609
+ let mut v = V {
610
+ cx,
611
+ allow_closure : true ,
612
+ loops : Vec :: new ( ) ,
613
+ } ;
614
+ v. visit_expr ( expr) ;
615
+ v. allow_closure
616
+ }
617
+
551
618
/// Returns the method names and argument list of nested method call expressions that make up
552
619
/// `expr`. method/span lists are sorted with the most recent call first.
553
620
pub fn method_calls < ' tcx > (
@@ -1272,6 +1339,51 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
1272
1339
did. map_or ( false , |did| must_use_attr ( & cx. tcx . get_attrs ( did) ) . is_some ( ) )
1273
1340
}
1274
1341
1342
+ /// Gets the node where an expression is either used, or it's type is unified with another branch.
1343
+ pub fn get_expr_use_or_unification_node ( tcx : TyCtxt < ' tcx > , expr : & Expr < ' _ > ) -> Option < Node < ' tcx > > {
1344
+ let map = tcx. hir ( ) ;
1345
+ let mut child_id = expr. hir_id ;
1346
+ let mut iter = map. parent_iter ( child_id) ;
1347
+ loop {
1348
+ match iter. next ( ) {
1349
+ None => break None ,
1350
+ Some ( ( id, Node :: Block ( _) ) ) => child_id = id,
1351
+ Some ( ( id, Node :: Arm ( arm) ) ) if arm. body . hir_id == child_id => child_id = id,
1352
+ Some ( ( _, Node :: Expr ( expr) ) ) => match expr. kind {
1353
+ ExprKind :: Match ( _, [ arm] , _) if arm. hir_id == child_id => child_id = expr. hir_id ,
1354
+ ExprKind :: Block ( ..) | ExprKind :: DropTemps ( _) => child_id = expr. hir_id ,
1355
+ ExprKind :: If ( _, then_expr, None ) if then_expr. hir_id == child_id => break None ,
1356
+ _ => break Some ( Node :: Expr ( expr) ) ,
1357
+ } ,
1358
+ Some ( ( _, node) ) => break Some ( node) ,
1359
+ }
1360
+ }
1361
+ }
1362
+
1363
+ /// Checks if the result of an expression is used, or it's type is unified with another branch.
1364
+ pub fn is_expr_used_or_unified ( tcx : TyCtxt < ' _ > , expr : & Expr < ' _ > ) -> bool {
1365
+ !matches ! (
1366
+ get_expr_use_or_unification_node( tcx, expr) ,
1367
+ None | Some ( Node :: Stmt ( Stmt {
1368
+ kind: StmtKind :: Expr ( _)
1369
+ | StmtKind :: Semi ( _)
1370
+ | StmtKind :: Local ( Local {
1371
+ pat: Pat {
1372
+ kind: PatKind :: Wild ,
1373
+ ..
1374
+ } ,
1375
+ ..
1376
+ } ) ,
1377
+ ..
1378
+ } ) )
1379
+ )
1380
+ }
1381
+
1382
+ /// Checks if the expression is the final expression returned from a block.
1383
+ pub fn is_expr_final_block_expr ( tcx : TyCtxt < ' _ > , expr : & Expr < ' _ > ) -> bool {
1384
+ matches ! ( get_parent_node( tcx, expr. hir_id) , Some ( Node :: Block ( ..) ) )
1385
+ }
1386
+
1275
1387
pub fn is_no_std_crate ( cx : & LateContext < ' _ > ) -> bool {
1276
1388
cx. tcx . hir ( ) . attrs ( hir:: CRATE_HIR_ID ) . iter ( ) . any ( |attr| {
1277
1389
if let ast:: AttrKind :: Normal ( ref attr, _) = attr. kind {
@@ -1441,28 +1553,43 @@ pub fn peel_hir_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
1441
1553
peel ( pat, 0 )
1442
1554
}
1443
1555
1556
+ /// Peels of expressions while the given closure returns `Some`.
1557
+ pub fn peel_hir_expr_while < ' tcx > (
1558
+ mut expr : & ' tcx Expr < ' tcx > ,
1559
+ mut f : impl FnMut ( & ' tcx Expr < ' tcx > ) -> Option < & ' tcx Expr < ' tcx > > ,
1560
+ ) -> & ' tcx Expr < ' tcx > {
1561
+ while let Some ( e) = f ( expr) {
1562
+ expr = e;
1563
+ }
1564
+ expr
1565
+ }
1566
+
1444
1567
/// Peels off up to the given number of references on the expression. Returns the underlying
1445
1568
/// expression and the number of references removed.
1446
1569
pub fn peel_n_hir_expr_refs ( expr : & ' a Expr < ' a > , count : usize ) -> ( & ' a Expr < ' a > , usize ) {
1447
- fn f ( expr : & ' a Expr < ' a > , count : usize , target : usize ) -> ( & ' a Expr < ' a > , usize ) {
1448
- match expr. kind {
1449
- ExprKind :: AddrOf ( _, _, expr) if count != target => f ( expr, count + 1 , target) ,
1450
- _ => ( expr, count) ,
1451
- }
1452
- }
1453
- f ( expr, 0 , count)
1570
+ let mut remaining = count;
1571
+ let e = peel_hir_expr_while ( expr, |e| match e. kind {
1572
+ ExprKind :: AddrOf ( BorrowKind :: Ref , _, e) if remaining != 0 => {
1573
+ remaining -= 1 ;
1574
+ Some ( e)
1575
+ } ,
1576
+ _ => None ,
1577
+ } ) ;
1578
+ ( e, count - remaining)
1454
1579
}
1455
1580
1456
1581
/// Peels off all references on the expression. Returns the underlying expression and the number of
1457
1582
/// references removed.
1458
1583
pub fn peel_hir_expr_refs ( expr : & ' a Expr < ' a > ) -> ( & ' a Expr < ' a > , usize ) {
1459
- fn f ( expr : & ' a Expr < ' a > , count : usize ) -> ( & ' a Expr < ' a > , usize ) {
1460
- match expr. kind {
1461
- ExprKind :: AddrOf ( BorrowKind :: Ref , _, expr) => f ( expr, count + 1 ) ,
1462
- _ => ( expr, count) ,
1463
- }
1464
- }
1465
- f ( expr, 0 )
1584
+ let mut count = 0 ;
1585
+ let e = peel_hir_expr_while ( expr, |e| match e. kind {
1586
+ ExprKind :: AddrOf ( BorrowKind :: Ref , _, e) => {
1587
+ count += 1 ;
1588
+ Some ( e)
1589
+ } ,
1590
+ _ => None ,
1591
+ } ) ;
1592
+ ( e, count)
1466
1593
}
1467
1594
1468
1595
#[ macro_export]
0 commit comments