@@ -60,12 +60,12 @@ use rustc_data_structures::fx::FxHashMap;
6060use rustc_hir as hir;
6161use rustc_hir:: def:: { DefKind , Res } ;
6262use 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 } ;
6464use rustc_hir:: LangItem :: { ResultErr , ResultOk } ;
6565use 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 ,
6969} ;
7070use rustc_lint:: { LateContext , Level , Lint , LintContext } ;
7171use rustc_middle:: hir:: exports:: Export ;
@@ -82,7 +82,7 @@ use rustc_span::{Span, DUMMY_SP};
8282use rustc_target:: abi:: Integer ;
8383
8484use crate :: consts:: { constant, Constant } ;
85- use crate :: ty:: is_recursively_primitive_type;
85+ use crate :: ty:: { can_partially_move_ty , is_recursively_primitive_type} ;
8686
8787pub fn parse_msrv ( msrv : & str , sess : Option < & Session > , span : Option < Span > ) -> Option < RustcVersion > {
8888 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
548548 None
549549}
550550
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+
551618/// Returns the method names and argument list of nested method call expressions that make up
552619/// `expr`. method/span lists are sorted with the most recent call first.
553620pub fn method_calls < ' tcx > (
@@ -1272,6 +1339,51 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
12721339 did. map_or ( false , |did| must_use_attr ( & cx. tcx . get_attrs ( did) ) . is_some ( ) )
12731340}
12741341
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+
12751387pub fn is_no_std_crate ( cx : & LateContext < ' _ > ) -> bool {
12761388 cx. tcx . hir ( ) . attrs ( hir:: CRATE_HIR_ID ) . iter ( ) . any ( |attr| {
12771389 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) {
14411553 peel ( pat, 0 )
14421554}
14431555
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+
14441567/// Peels off up to the given number of references on the expression. Returns the underlying
14451568/// expression and the number of references removed.
14461569pub 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)
14541579}
14551580
14561581/// Peels off all references on the expression. Returns the underlying expression and the number of
14571582/// references removed.
14581583pub 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)
14661593}
14671594
14681595#[ macro_export]
0 commit comments