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) ]
@@ -2508,26 +2509,30 @@ pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool {
2508
2509
&& item. ident . name . as_str ( ) . split ( '_' ) . any ( |a| a == "test" || a == "tests" )
2509
2510
}
2510
2511
2511
- /// Walks the HIR tree from the given expression, up to the node where the value produced by the
2512
- /// expression is consumed. Calls the function for every node encountered this way until it returns
2513
- /// `Some`.
2512
+ /// Walks up the HIR tree from the given expression in an attempt to find where the value is
2513
+ /// consumed.
2514
2514
///
2515
- /// This allows walking through `if`, `match`, `break`, block expressions to find where the value
2516
- /// produced by the expression is consumed.
2515
+ /// Termination has three conditions:
2516
+ /// - The given function returns `Break`. This function will return the value.
2517
+ /// - The consuming node is found. This function will return `Continue(use_node, child_id)`.
2518
+ /// - No further parent nodes are found. This will trigger a debug assert or return `None`.
2519
+ ///
2520
+ /// This allows walking through `if`, `match`, `break`, and block expressions to find where the
2521
+ /// value produced by the expression is consumed.
2517
2522
pub fn walk_to_expr_usage < ' tcx , T > (
2518
2523
cx : & LateContext < ' tcx > ,
2519
2524
e : & Expr < ' tcx > ,
2520
- mut f : impl FnMut ( Node < ' tcx > , HirId ) -> Option < T > ,
2521
- ) -> Option < T > {
2525
+ mut f : impl FnMut ( HirId , Node < ' tcx > , HirId ) -> ControlFlow < T > ,
2526
+ ) -> Option < ControlFlow < T , ( Node < ' tcx > , HirId ) > > {
2522
2527
let map = cx. tcx . hir ( ) ;
2523
2528
let mut iter = map. parent_iter ( e. hir_id ) ;
2524
2529
let mut child_id = e. hir_id ;
2525
2530
2526
2531
while let Some ( ( parent_id, parent) ) = iter. next ( ) {
2527
- if let Some ( x) = f ( parent, child_id) {
2528
- return Some ( x ) ;
2532
+ if let ControlFlow :: Break ( x) = f ( parent_id , parent, child_id) {
2533
+ return Some ( ControlFlow :: Break ( x ) ) ;
2529
2534
}
2530
- let parent = match parent {
2535
+ let parent_expr = match parent {
2531
2536
Node :: Expr ( e) => e,
2532
2537
Node :: Block ( Block { expr : Some ( body) , .. } ) | Node :: Arm ( Arm { body, .. } ) if body. hir_id == child_id => {
2533
2538
child_id = parent_id;
@@ -2537,18 +2542,19 @@ pub fn walk_to_expr_usage<'tcx, T>(
2537
2542
child_id = parent_id;
2538
2543
continue ;
2539
2544
} ,
2540
- _ => return None ,
2545
+ _ => return Some ( ControlFlow :: Continue ( ( parent , child_id ) ) ) ,
2541
2546
} ;
2542
- match parent . kind {
2547
+ match parent_expr . kind {
2543
2548
ExprKind :: If ( child, ..) | ExprKind :: Match ( child, ..) if child. hir_id != child_id => child_id = parent_id,
2544
2549
ExprKind :: Break ( Destination { target_id : Ok ( id) , .. } , _) => {
2545
2550
child_id = id;
2546
2551
iter = map. parent_iter ( id) ;
2547
2552
} ,
2548
- ExprKind :: Block ( ..) => child_id = parent_id,
2549
- _ => return None ,
2553
+ ExprKind :: Block ( ..) | ExprKind :: DropTemps ( _ ) => child_id = parent_id,
2554
+ _ => return Some ( ControlFlow :: Continue ( ( parent , child_id ) ) ) ,
2550
2555
}
2551
2556
}
2557
+ debug_assert ! ( false , "no parent node found for `{child_id:?}`" ) ;
2552
2558
None
2553
2559
}
2554
2560
@@ -2592,6 +2598,8 @@ pub enum ExprUseNode<'tcx> {
2592
2598
Callee ,
2593
2599
/// Access of a field.
2594
2600
FieldAccess ( Ident ) ,
2601
+ Expr ,
2602
+ Other ,
2595
2603
}
2596
2604
impl < ' tcx > ExprUseNode < ' tcx > {
2597
2605
/// Checks if the value is returned from the function.
@@ -2668,144 +2676,104 @@ impl<'tcx> ExprUseNode<'tcx> {
2668
2676
let sig = cx. tcx . fn_sig ( id) . skip_binder ( ) ;
2669
2677
Some ( DefinedTy :: Mir ( cx. tcx . param_env ( id) . and ( sig. input ( i) ) ) )
2670
2678
} ,
2671
- Self :: Local ( _) | Self :: FieldAccess ( ..) | Self :: Callee => None ,
2679
+ Self :: Local ( _) | Self :: FieldAccess ( ..) | Self :: Callee | Self :: Expr | Self :: Other => None ,
2672
2680
}
2673
2681
}
2674
2682
}
2675
2683
2676
2684
/// Gets the context an expression's value is used in.
2677
- #[ expect( clippy:: too_many_lines) ]
2678
2685
pub fn expr_use_ctxt < ' tcx > ( cx : & LateContext < ' tcx > , e : & ' tcx Expr < ' tcx > ) -> Option < ExprUseCtxt < ' tcx > > {
2679
2686
let mut adjustments = [ ] . as_slice ( ) ;
2680
2687
let mut is_ty_unified = false ;
2681
2688
let mut moved_before_use = false ;
2682
2689
let ctxt = e. span . ctxt ( ) ;
2683
- walk_to_expr_usage ( cx, e, & mut |parent, child_id| {
2684
- // LocalTableInContext returns the wrong lifetime, so go use `expr_adjustments` instead.
2690
+ walk_to_expr_usage ( cx, e, & mut |parent_id, parent, child_id| {
2685
2691
if adjustments. is_empty ( )
2686
2692
&& let Node :: Expr ( e) = cx. tcx . hir ( ) . get ( child_id)
2687
2693
{
2688
2694
adjustments = cx. typeck_results ( ) . expr_adjustments ( e) ;
2689
2695
}
2690
- match parent {
2691
- Node :: Local ( l) if l. span . ctxt ( ) == ctxt => Some ( ExprUseCtxt {
2692
- node : ExprUseNode :: Local ( l) ,
2693
- adjustments,
2694
- is_ty_unified,
2695
- moved_before_use,
2696
- } ) ,
2696
+ if !cx. tcx . hir ( ) . opt_span ( parent_id) . is_some_and ( |x| x. ctxt ( ) == ctxt) {
2697
+ return ControlFlow :: Break ( ( ) ) ;
2698
+ }
2699
+ if let Node :: Expr ( e) = parent {
2700
+ match e. kind {
2701
+ ExprKind :: If ( e, _, _) | ExprKind :: Match ( e, _, _) if e. hir_id != child_id => {
2702
+ is_ty_unified = true ;
2703
+ moved_before_use = true ;
2704
+ } ,
2705
+ ExprKind :: Block ( _, Some ( _) ) | ExprKind :: Break ( ..) => {
2706
+ is_ty_unified = true ;
2707
+ moved_before_use = true ;
2708
+ } ,
2709
+ ExprKind :: Block ( ..) => moved_before_use = true ,
2710
+ _ => { } ,
2711
+ }
2712
+ }
2713
+ ControlFlow :: Continue ( ( ) )
2714
+ } ) ?
2715
+ . continue_value ( )
2716
+ . map ( |( use_node, child_id) | {
2717
+ let node = match use_node {
2718
+ Node :: Local ( l) => ExprUseNode :: Local ( l) ,
2719
+ Node :: ExprField ( field) => ExprUseNode :: Field ( field) ,
2720
+
2697
2721
Node :: Item ( & Item {
2698
2722
kind : ItemKind :: Static ( ..) | ItemKind :: Const ( ..) ,
2699
2723
owner_id,
2700
- span,
2701
2724
..
2702
2725
} )
2703
2726
| Node :: TraitItem ( & TraitItem {
2704
2727
kind : TraitItemKind :: Const ( ..) ,
2705
2728
owner_id,
2706
- span,
2707
2729
..
2708
2730
} )
2709
2731
| Node :: ImplItem ( & ImplItem {
2710
2732
kind : ImplItemKind :: Const ( ..) ,
2711
2733
owner_id,
2712
- span,
2713
2734
..
2714
- } ) if span. ctxt ( ) == ctxt => Some ( ExprUseCtxt {
2715
- node : ExprUseNode :: ConstStatic ( owner_id) ,
2716
- adjustments,
2717
- is_ty_unified,
2718
- moved_before_use,
2719
- } ) ,
2735
+ } ) => ExprUseNode :: ConstStatic ( owner_id) ,
2720
2736
2721
2737
Node :: Item ( & Item {
2722
2738
kind : ItemKind :: Fn ( ..) ,
2723
2739
owner_id,
2724
- span,
2725
2740
..
2726
2741
} )
2727
2742
| Node :: TraitItem ( & TraitItem {
2728
2743
kind : TraitItemKind :: Fn ( ..) ,
2729
2744
owner_id,
2730
- span,
2731
2745
..
2732
2746
} )
2733
2747
| Node :: ImplItem ( & ImplItem {
2734
2748
kind : ImplItemKind :: Fn ( ..) ,
2735
2749
owner_id,
2736
- span,
2737
2750
..
2738
- } ) if span. ctxt ( ) == ctxt => Some ( ExprUseCtxt {
2739
- node : ExprUseNode :: Return ( owner_id) ,
2740
- adjustments,
2741
- is_ty_unified,
2742
- moved_before_use,
2743
- } ) ,
2744
-
2745
- Node :: ExprField ( field) if field. span . ctxt ( ) == ctxt => Some ( ExprUseCtxt {
2746
- node : ExprUseNode :: Field ( field) ,
2747
- adjustments,
2748
- is_ty_unified,
2749
- moved_before_use,
2750
- } ) ,
2751
+ } ) => ExprUseNode :: Return ( owner_id) ,
2751
2752
2752
- Node :: Expr ( parent) if parent. span . ctxt ( ) == ctxt => match parent. kind {
2753
- ExprKind :: Ret ( _) => Some ( ExprUseCtxt {
2754
- node : ExprUseNode :: Return ( OwnerId {
2755
- def_id : cx. tcx . hir ( ) . body_owner_def_id ( cx. enclosing_body . unwrap ( ) ) ,
2756
- } ) ,
2757
- adjustments,
2758
- is_ty_unified,
2759
- moved_before_use,
2760
- } ) ,
2761
- ExprKind :: Closure ( closure) => Some ( ExprUseCtxt {
2762
- node : ExprUseNode :: Return ( OwnerId { def_id : closure. def_id } ) ,
2763
- adjustments,
2764
- is_ty_unified,
2765
- moved_before_use,
2766
- } ) ,
2767
- ExprKind :: Call ( func, args) => Some ( ExprUseCtxt {
2768
- node : match args. iter ( ) . position ( |arg| arg. hir_id == child_id) {
2769
- Some ( i) => ExprUseNode :: FnArg ( func, i) ,
2770
- None => ExprUseNode :: Callee ,
2771
- } ,
2772
- adjustments,
2773
- is_ty_unified,
2774
- moved_before_use,
2753
+ Node :: Expr ( use_expr) => match use_expr. kind {
2754
+ ExprKind :: Ret ( _) => ExprUseNode :: Return ( OwnerId {
2755
+ def_id : cx. tcx . hir ( ) . body_owner_def_id ( cx. enclosing_body . unwrap ( ) ) ,
2775
2756
} ) ,
2776
- ExprKind :: MethodCall ( name, _, args, _) => Some ( ExprUseCtxt {
2777
- node : ExprUseNode :: MethodArg (
2778
- parent. hir_id ,
2779
- name. args ,
2780
- args. iter ( ) . position ( |arg| arg. hir_id == child_id) . map_or ( 0 , |i| i + 1 ) ,
2781
- ) ,
2782
- adjustments,
2783
- is_ty_unified,
2784
- moved_before_use,
2785
- } ) ,
2786
- ExprKind :: Field ( child, name) if child. hir_id == e. hir_id => Some ( ExprUseCtxt {
2787
- node : ExprUseNode :: FieldAccess ( name) ,
2788
- adjustments,
2789
- is_ty_unified,
2790
- moved_before_use,
2791
- } ) ,
2792
- ExprKind :: If ( e, _, _) | ExprKind :: Match ( e, _, _) if e. hir_id != child_id => {
2793
- is_ty_unified = true ;
2794
- moved_before_use = true ;
2795
- None
2796
- } ,
2797
- ExprKind :: Block ( _, Some ( _) ) | ExprKind :: Break ( ..) => {
2798
- is_ty_unified = true ;
2799
- moved_before_use = true ;
2800
- None
2757
+ ExprKind :: Closure ( closure) => ExprUseNode :: Return ( OwnerId { def_id : closure. def_id } ) ,
2758
+ ExprKind :: Call ( func, args) => match args. iter ( ) . position ( |arg| arg. hir_id == child_id) {
2759
+ Some ( i) => ExprUseNode :: FnArg ( func, i) ,
2760
+ None => ExprUseNode :: Callee ,
2801
2761
} ,
2802
- ExprKind :: Block ( ..) => {
2803
- moved_before_use = true ;
2804
- None
2805
- } ,
2806
- _ => None ,
2762
+ ExprKind :: MethodCall ( name, _, args, _) => ExprUseNode :: MethodArg (
2763
+ use_expr. hir_id ,
2764
+ name. args ,
2765
+ args. iter ( ) . position ( |arg| arg. hir_id == child_id) . map_or ( 0 , |i| i + 1 ) ,
2766
+ ) ,
2767
+ ExprKind :: Field ( child, name) if child. hir_id == e. hir_id => ExprUseNode :: FieldAccess ( name) ,
2768
+ _ => ExprUseNode :: Expr ,
2807
2769
} ,
2808
- _ => None ,
2770
+ _ => ExprUseNode :: Other ,
2771
+ } ;
2772
+ ExprUseCtxt {
2773
+ node,
2774
+ adjustments,
2775
+ is_ty_unified,
2776
+ moved_before_use,
2809
2777
}
2810
2778
} )
2811
2779
}
0 commit comments