1
1
#![ feature( array_chunks) ]
2
2
#![ feature( box_patterns) ]
3
+ #![ feature( control_flow_enum) ]
3
4
#![ feature( if_let_guard) ]
4
5
#![ feature( let_chains) ]
5
6
#![ feature( lint_reasons) ]
@@ -116,10 +117,7 @@ use visitors::Visitable;
116
117
117
118
use crate :: consts:: { constant, mir_to_const, Constant } ;
118
119
use crate :: higher:: Range ;
119
- use crate :: ty:: {
120
- adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type,
121
- ty_is_fn_once_param,
122
- } ;
120
+ use crate :: ty:: { adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type} ;
123
121
use crate :: visitors:: for_each_expr;
124
122
125
123
use rustc_middle:: hir:: nested_filter;
@@ -1355,46 +1353,12 @@ pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
1355
1353
for ( _, node) in cx. tcx . hir ( ) . parent_iter ( expr. hir_id ) {
1356
1354
match node {
1357
1355
Node :: Expr ( e) => match e. kind {
1358
- ExprKind :: Closure { .. } => {
1356
+ ExprKind :: Closure { .. }
1359
1357
if let rustc_ty:: Closure ( _, subs) = cx. typeck_results ( ) . expr_ty ( e) . kind ( )
1360
- && subs. as_closure ( ) . kind ( ) == ClosureKind :: FnOnce
1361
- {
1362
- continue ;
1363
- }
1364
- let is_once = walk_to_expr_usage ( cx, e, |node, id| {
1365
- let Node :: Expr ( e) = node else {
1366
- return None ;
1367
- } ;
1368
- match e. kind {
1369
- ExprKind :: Call ( f, _) if f. hir_id == id => Some ( ( ) ) ,
1370
- ExprKind :: Call ( f, args) => {
1371
- let i = args. iter ( ) . position ( |arg| arg. hir_id == id) ?;
1372
- let sig = expr_sig ( cx, f) ?;
1373
- let predicates = sig
1374
- . predicates_id ( )
1375
- . map_or ( cx. param_env , |id| cx. tcx . param_env ( id) )
1376
- . caller_bounds ( ) ;
1377
- sig. input ( i) . and_then ( |ty| {
1378
- ty_is_fn_once_param ( cx. tcx , ty. skip_binder ( ) , predicates) . then_some ( ( ) )
1379
- } )
1380
- } ,
1381
- ExprKind :: MethodCall ( _, receiver, args, _) => {
1382
- let i = std:: iter:: once ( receiver)
1383
- . chain ( args. iter ( ) )
1384
- . position ( |arg| arg. hir_id == id) ?;
1385
- let id = cx. typeck_results ( ) . type_dependent_def_id ( e. hir_id ) ?;
1386
- let ty = cx. tcx . fn_sig ( id) . instantiate_identity ( ) . skip_binder ( ) . inputs ( ) [ i] ;
1387
- ty_is_fn_once_param ( cx. tcx , ty, cx. tcx . param_env ( id) . caller_bounds ( ) ) . then_some ( ( ) )
1388
- } ,
1389
- _ => None ,
1390
- }
1391
- } )
1392
- . is_some ( ) ;
1393
- if !is_once {
1394
- return Some ( e) ;
1395
- }
1396
- } ,
1397
- ExprKind :: Loop ( ..) => return Some ( e) ,
1358
+ && subs. as_closure ( ) . kind ( ) == ClosureKind :: FnOnce => { } ,
1359
+
1360
+ // Note: A closure's kind is determined by how it's used, not it's captures.
1361
+ ExprKind :: Closure { .. } | ExprKind :: Loop ( ..) => return Some ( e) ,
1398
1362
_ => ( ) ,
1399
1363
} ,
1400
1364
Node :: Stmt ( _) | Node :: Block ( _) | Node :: Local ( _) | Node :: Arm ( _) => ( ) ,
@@ -2592,26 +2556,30 @@ pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool {
2592
2556
&& item. ident . name . as_str ( ) . split ( '_' ) . any ( |a| a == "test" || a == "tests" )
2593
2557
}
2594
2558
2595
- /// Walks the HIR tree from the given expression, up to the node where the value produced by the
2596
- /// expression is consumed. Calls the function for every node encountered this way until it returns
2597
- /// `Some`.
2559
+ /// Walks up the HIR tree from the given expression in an attempt to find where the value is
2560
+ /// consumed.
2561
+ ///
2562
+ /// Termination has three conditions:
2563
+ /// - The given function returns `Break`. This function will return the value.
2564
+ /// - The consuming node is found. This function will return `Continue(use_node, child_id)`.
2565
+ /// - No further parent nodes are found. This will trigger a debug assert or return `None`.
2598
2566
///
2599
- /// This allows walking through `if`, `match`, `break`, block expressions to find where the value
2600
- /// produced by the expression is consumed.
2567
+ /// This allows walking through `if`, `match`, `break`, and block expressions to find where the
2568
+ /// value produced by the expression is consumed.
2601
2569
pub fn walk_to_expr_usage < ' tcx , T > (
2602
2570
cx : & LateContext < ' tcx > ,
2603
2571
e : & Expr < ' tcx > ,
2604
- mut f : impl FnMut ( Node < ' tcx > , HirId ) -> Option < T > ,
2605
- ) -> Option < T > {
2572
+ mut f : impl FnMut ( HirId , Node < ' tcx > , HirId ) -> ControlFlow < T > ,
2573
+ ) -> Option < ControlFlow < T , ( Node < ' tcx > , HirId ) > > {
2606
2574
let map = cx. tcx . hir ( ) ;
2607
2575
let mut iter = map. parent_iter ( e. hir_id ) ;
2608
2576
let mut child_id = e. hir_id ;
2609
2577
2610
2578
while let Some ( ( parent_id, parent) ) = iter. next ( ) {
2611
- if let Some ( x) = f ( parent, child_id) {
2612
- return Some ( x ) ;
2579
+ if let ControlFlow :: Break ( x) = f ( parent_id , parent, child_id) {
2580
+ return Some ( ControlFlow :: Break ( x ) ) ;
2613
2581
}
2614
- let parent = match parent {
2582
+ let parent_expr = match parent {
2615
2583
Node :: Expr ( e) => e,
2616
2584
Node :: Block ( Block { expr : Some ( body) , .. } ) | Node :: Arm ( Arm { body, .. } ) if body. hir_id == child_id => {
2617
2585
child_id = parent_id;
@@ -2621,18 +2589,19 @@ pub fn walk_to_expr_usage<'tcx, T>(
2621
2589
child_id = parent_id;
2622
2590
continue ;
2623
2591
} ,
2624
- _ => return None ,
2592
+ _ => return Some ( ControlFlow :: Continue ( ( parent , child_id ) ) ) ,
2625
2593
} ;
2626
- match parent . kind {
2594
+ match parent_expr . kind {
2627
2595
ExprKind :: If ( child, ..) | ExprKind :: Match ( child, ..) if child. hir_id != child_id => child_id = parent_id,
2628
2596
ExprKind :: Break ( Destination { target_id : Ok ( id) , .. } , _) => {
2629
2597
child_id = id;
2630
2598
iter = map. parent_iter ( id) ;
2631
2599
} ,
2632
- ExprKind :: Block ( ..) => child_id = parent_id,
2633
- _ => return None ,
2600
+ ExprKind :: Block ( ..) | ExprKind :: DropTemps ( _ ) => child_id = parent_id,
2601
+ _ => return Some ( ControlFlow :: Continue ( ( parent , child_id ) ) ) ,
2634
2602
}
2635
2603
}
2604
+ debug_assert ! ( false , "no parent node found for `{child_id:?}`" ) ;
2636
2605
None
2637
2606
}
2638
2607
@@ -2676,6 +2645,8 @@ pub enum ExprUseNode<'tcx> {
2676
2645
Callee ,
2677
2646
/// Access of a field.
2678
2647
FieldAccess ( Ident ) ,
2648
+ Expr ,
2649
+ Other ,
2679
2650
}
2680
2651
impl < ' tcx > ExprUseNode < ' tcx > {
2681
2652
/// Checks if the value is returned from the function.
@@ -2752,144 +2723,104 @@ impl<'tcx> ExprUseNode<'tcx> {
2752
2723
let sig = cx. tcx . fn_sig ( id) . skip_binder ( ) ;
2753
2724
Some ( DefinedTy :: Mir ( cx. tcx . param_env ( id) . and ( sig. input ( i) ) ) )
2754
2725
} ,
2755
- Self :: Local ( _) | Self :: FieldAccess ( ..) | Self :: Callee => None ,
2726
+ Self :: Local ( _) | Self :: FieldAccess ( ..) | Self :: Callee | Self :: Expr | Self :: Other => None ,
2756
2727
}
2757
2728
}
2758
2729
}
2759
2730
2760
2731
/// Gets the context an expression's value is used in.
2761
- #[ expect( clippy:: too_many_lines) ]
2762
2732
pub fn expr_use_ctxt < ' tcx > ( cx : & LateContext < ' tcx > , e : & ' tcx Expr < ' tcx > ) -> Option < ExprUseCtxt < ' tcx > > {
2763
2733
let mut adjustments = [ ] . as_slice ( ) ;
2764
2734
let mut is_ty_unified = false ;
2765
2735
let mut moved_before_use = false ;
2766
2736
let ctxt = e. span . ctxt ( ) ;
2767
- walk_to_expr_usage ( cx, e, & mut |parent, child_id| {
2768
- // LocalTableInContext returns the wrong lifetime, so go use `expr_adjustments` instead.
2737
+ walk_to_expr_usage ( cx, e, & mut |parent_id, parent, child_id| {
2769
2738
if adjustments. is_empty ( )
2770
2739
&& let Node :: Expr ( e) = cx. tcx . hir_node ( child_id)
2771
2740
{
2772
2741
adjustments = cx. typeck_results ( ) . expr_adjustments ( e) ;
2773
2742
}
2774
- match parent {
2775
- Node :: Local ( l) if l. span . ctxt ( ) == ctxt => Some ( ExprUseCtxt {
2776
- node : ExprUseNode :: Local ( l) ,
2777
- adjustments,
2778
- is_ty_unified,
2779
- moved_before_use,
2780
- } ) ,
2743
+ if !cx. tcx . hir ( ) . opt_span ( parent_id) . is_some_and ( |x| x. ctxt ( ) == ctxt) {
2744
+ return ControlFlow :: Break ( ( ) ) ;
2745
+ }
2746
+ if let Node :: Expr ( e) = parent {
2747
+ match e. kind {
2748
+ ExprKind :: If ( e, _, _) | ExprKind :: Match ( e, _, _) if e. hir_id != child_id => {
2749
+ is_ty_unified = true ;
2750
+ moved_before_use = true ;
2751
+ } ,
2752
+ ExprKind :: Block ( _, Some ( _) ) | ExprKind :: Break ( ..) => {
2753
+ is_ty_unified = true ;
2754
+ moved_before_use = true ;
2755
+ } ,
2756
+ ExprKind :: Block ( ..) => moved_before_use = true ,
2757
+ _ => { } ,
2758
+ }
2759
+ }
2760
+ ControlFlow :: Continue ( ( ) )
2761
+ } ) ?
2762
+ . continue_value ( )
2763
+ . map ( |( use_node, child_id) | {
2764
+ let node = match use_node {
2765
+ Node :: Local ( l) => ExprUseNode :: Local ( l) ,
2766
+ Node :: ExprField ( field) => ExprUseNode :: Field ( field) ,
2767
+
2781
2768
Node :: Item ( & Item {
2782
2769
kind : ItemKind :: Static ( ..) | ItemKind :: Const ( ..) ,
2783
2770
owner_id,
2784
- span,
2785
2771
..
2786
2772
} )
2787
2773
| Node :: TraitItem ( & TraitItem {
2788
2774
kind : TraitItemKind :: Const ( ..) ,
2789
2775
owner_id,
2790
- span,
2791
2776
..
2792
2777
} )
2793
2778
| Node :: ImplItem ( & ImplItem {
2794
2779
kind : ImplItemKind :: Const ( ..) ,
2795
2780
owner_id,
2796
- span,
2797
2781
..
2798
- } ) if span. ctxt ( ) == ctxt => Some ( ExprUseCtxt {
2799
- node : ExprUseNode :: ConstStatic ( owner_id) ,
2800
- adjustments,
2801
- is_ty_unified,
2802
- moved_before_use,
2803
- } ) ,
2782
+ } ) => ExprUseNode :: ConstStatic ( owner_id) ,
2804
2783
2805
2784
Node :: Item ( & Item {
2806
2785
kind : ItemKind :: Fn ( ..) ,
2807
2786
owner_id,
2808
- span,
2809
2787
..
2810
2788
} )
2811
2789
| Node :: TraitItem ( & TraitItem {
2812
2790
kind : TraitItemKind :: Fn ( ..) ,
2813
2791
owner_id,
2814
- span,
2815
2792
..
2816
2793
} )
2817
2794
| Node :: ImplItem ( & ImplItem {
2818
2795
kind : ImplItemKind :: Fn ( ..) ,
2819
2796
owner_id,
2820
- span,
2821
2797
..
2822
- } ) if span. ctxt ( ) == ctxt => Some ( ExprUseCtxt {
2823
- node : ExprUseNode :: Return ( owner_id) ,
2824
- adjustments,
2825
- is_ty_unified,
2826
- moved_before_use,
2827
- } ) ,
2828
-
2829
- Node :: ExprField ( field) if field. span . ctxt ( ) == ctxt => Some ( ExprUseCtxt {
2830
- node : ExprUseNode :: Field ( field) ,
2831
- adjustments,
2832
- is_ty_unified,
2833
- moved_before_use,
2834
- } ) ,
2798
+ } ) => ExprUseNode :: Return ( owner_id) ,
2835
2799
2836
- Node :: Expr ( parent) if parent. span . ctxt ( ) == ctxt => match parent. kind {
2837
- ExprKind :: Ret ( _) => Some ( ExprUseCtxt {
2838
- node : ExprUseNode :: Return ( OwnerId {
2839
- def_id : cx. tcx . hir ( ) . body_owner_def_id ( cx. enclosing_body . unwrap ( ) ) ,
2840
- } ) ,
2841
- adjustments,
2842
- is_ty_unified,
2843
- moved_before_use,
2844
- } ) ,
2845
- ExprKind :: Closure ( closure) => Some ( ExprUseCtxt {
2846
- node : ExprUseNode :: Return ( OwnerId { def_id : closure. def_id } ) ,
2847
- adjustments,
2848
- is_ty_unified,
2849
- moved_before_use,
2800
+ Node :: Expr ( use_expr) => match use_expr. kind {
2801
+ ExprKind :: Ret ( _) => ExprUseNode :: Return ( OwnerId {
2802
+ def_id : cx. tcx . hir ( ) . body_owner_def_id ( cx. enclosing_body . unwrap ( ) ) ,
2850
2803
} ) ,
2851
- ExprKind :: Call ( func, args) => Some ( ExprUseCtxt {
2852
- node : match args. iter ( ) . position ( |arg| arg. hir_id == child_id) {
2853
- Some ( i) => ExprUseNode :: FnArg ( func, i) ,
2854
- None => ExprUseNode :: Callee ,
2855
- } ,
2856
- adjustments,
2857
- is_ty_unified,
2858
- moved_before_use,
2859
- } ) ,
2860
- ExprKind :: MethodCall ( name, _, args, _) => Some ( ExprUseCtxt {
2861
- node : ExprUseNode :: MethodArg (
2862
- parent. hir_id ,
2863
- name. args ,
2864
- args. iter ( ) . position ( |arg| arg. hir_id == child_id) . map_or ( 0 , |i| i + 1 ) ,
2865
- ) ,
2866
- adjustments,
2867
- is_ty_unified,
2868
- moved_before_use,
2869
- } ) ,
2870
- ExprKind :: Field ( child, name) if child. hir_id == e. hir_id => Some ( ExprUseCtxt {
2871
- node : ExprUseNode :: FieldAccess ( name) ,
2872
- adjustments,
2873
- is_ty_unified,
2874
- moved_before_use,
2875
- } ) ,
2876
- ExprKind :: If ( e, _, _) | ExprKind :: Match ( e, _, _) if e. hir_id != child_id => {
2877
- is_ty_unified = true ;
2878
- moved_before_use = true ;
2879
- None
2880
- } ,
2881
- ExprKind :: Block ( _, Some ( _) ) | ExprKind :: Break ( ..) => {
2882
- is_ty_unified = true ;
2883
- moved_before_use = true ;
2884
- None
2804
+ ExprKind :: Closure ( closure) => ExprUseNode :: Return ( OwnerId { def_id : closure. def_id } ) ,
2805
+ ExprKind :: Call ( func, args) => match args. iter ( ) . position ( |arg| arg. hir_id == child_id) {
2806
+ Some ( i) => ExprUseNode :: FnArg ( func, i) ,
2807
+ None => ExprUseNode :: Callee ,
2885
2808
} ,
2886
- ExprKind :: Block ( ..) => {
2887
- moved_before_use = true ;
2888
- None
2889
- } ,
2890
- _ => None ,
2809
+ ExprKind :: MethodCall ( name, _, args, _) => ExprUseNode :: MethodArg (
2810
+ use_expr. hir_id ,
2811
+ name. args ,
2812
+ args. iter ( ) . position ( |arg| arg. hir_id == child_id) . map_or ( 0 , |i| i + 1 ) ,
2813
+ ) ,
2814
+ ExprKind :: Field ( child, name) if child. hir_id == e. hir_id => ExprUseNode :: FieldAccess ( name) ,
2815
+ _ => ExprUseNode :: Expr ,
2891
2816
} ,
2892
- _ => None ,
2817
+ _ => ExprUseNode :: Other ,
2818
+ } ;
2819
+ ExprUseCtxt {
2820
+ node,
2821
+ adjustments,
2822
+ is_ty_unified,
2823
+ moved_before_use,
2893
2824
}
2894
2825
} )
2895
2826
}
0 commit comments