11#![ feature( array_chunks) ]
22#![ feature( box_patterns) ]
3+ #![ feature( control_flow_enum) ]
34#![ feature( if_let_guard) ]
45#![ feature( let_chains) ]
56#![ feature( lint_reasons) ]
@@ -2508,26 +2509,30 @@ pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool {
25082509 && item. ident . name . as_str ( ) . split ( '_' ) . any ( |a| a == "test" || a == "tests" )
25092510}
25102511
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.
25142514///
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.
25172522pub fn walk_to_expr_usage < ' tcx , T > (
25182523 cx : & LateContext < ' tcx > ,
25192524 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 ) > > {
25222527 let map = cx. tcx . hir ( ) ;
25232528 let mut iter = map. parent_iter ( e. hir_id ) ;
25242529 let mut child_id = e. hir_id ;
25252530
25262531 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 ) ) ;
25292534 }
2530- let parent = match parent {
2535+ let parent_expr = match parent {
25312536 Node :: Expr ( e) => e,
25322537 Node :: Block ( Block { expr : Some ( body) , .. } ) | Node :: Arm ( Arm { body, .. } ) if body. hir_id == child_id => {
25332538 child_id = parent_id;
@@ -2537,18 +2542,19 @@ pub fn walk_to_expr_usage<'tcx, T>(
25372542 child_id = parent_id;
25382543 continue ;
25392544 } ,
2540- _ => return None ,
2545+ _ => return Some ( ControlFlow :: Continue ( ( parent , child_id ) ) ) ,
25412546 } ;
2542- match parent . kind {
2547+ match parent_expr . kind {
25432548 ExprKind :: If ( child, ..) | ExprKind :: Match ( child, ..) if child. hir_id != child_id => child_id = parent_id,
25442549 ExprKind :: Break ( Destination { target_id : Ok ( id) , .. } , _) => {
25452550 child_id = id;
25462551 iter = map. parent_iter ( id) ;
25472552 } ,
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 ) ) ) ,
25502555 }
25512556 }
2557+ debug_assert ! ( false , "no parent node found for `{child_id:?}`" ) ;
25522558 None
25532559}
25542560
@@ -2592,6 +2598,8 @@ pub enum ExprUseNode<'tcx> {
25922598 Callee ,
25932599 /// Access of a field.
25942600 FieldAccess ( Ident ) ,
2601+ Expr ,
2602+ Other ,
25952603}
25962604impl < ' tcx > ExprUseNode < ' tcx > {
25972605 /// Checks if the value is returned from the function.
@@ -2668,144 +2676,104 @@ impl<'tcx> ExprUseNode<'tcx> {
26682676 let sig = cx. tcx . fn_sig ( id) . skip_binder ( ) ;
26692677 Some ( DefinedTy :: Mir ( cx. tcx . param_env ( id) . and ( sig. input ( i) ) ) )
26702678 } ,
2671- Self :: Local ( _) | Self :: FieldAccess ( ..) | Self :: Callee => None ,
2679+ Self :: Local ( _) | Self :: FieldAccess ( ..) | Self :: Callee | Self :: Expr | Self :: Other => None ,
26722680 }
26732681 }
26742682}
26752683
26762684/// Gets the context an expression's value is used in.
2677- #[ expect( clippy:: too_many_lines) ]
26782685pub fn expr_use_ctxt < ' tcx > ( cx : & LateContext < ' tcx > , e : & ' tcx Expr < ' tcx > ) -> Option < ExprUseCtxt < ' tcx > > {
26792686 let mut adjustments = [ ] . as_slice ( ) ;
26802687 let mut is_ty_unified = false ;
26812688 let mut moved_before_use = false ;
26822689 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| {
26852691 if adjustments. is_empty ( )
26862692 && let Node :: Expr ( e) = cx. tcx . hir ( ) . get ( child_id)
26872693 {
26882694 adjustments = cx. typeck_results ( ) . expr_adjustments ( e) ;
26892695 }
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+
26972721 Node :: Item ( & Item {
26982722 kind : ItemKind :: Static ( ..) | ItemKind :: Const ( ..) ,
26992723 owner_id,
2700- span,
27012724 ..
27022725 } )
27032726 | Node :: TraitItem ( & TraitItem {
27042727 kind : TraitItemKind :: Const ( ..) ,
27052728 owner_id,
2706- span,
27072729 ..
27082730 } )
27092731 | Node :: ImplItem ( & ImplItem {
27102732 kind : ImplItemKind :: Const ( ..) ,
27112733 owner_id,
2712- span,
27132734 ..
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) ,
27202736
27212737 Node :: Item ( & Item {
27222738 kind : ItemKind :: Fn ( ..) ,
27232739 owner_id,
2724- span,
27252740 ..
27262741 } )
27272742 | Node :: TraitItem ( & TraitItem {
27282743 kind : TraitItemKind :: Fn ( ..) ,
27292744 owner_id,
2730- span,
27312745 ..
27322746 } )
27332747 | Node :: ImplItem ( & ImplItem {
27342748 kind : ImplItemKind :: Fn ( ..) ,
27352749 owner_id,
2736- span,
27372750 ..
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) ,
27512752
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 ( ) ) ,
27752756 } ) ,
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 ,
28012761 } ,
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 ,
28072769 } ,
2808- _ => None ,
2770+ _ => ExprUseNode :: Other ,
2771+ } ;
2772+ ExprUseCtxt {
2773+ node,
2774+ adjustments,
2775+ is_ty_unified,
2776+ moved_before_use,
28092777 }
28102778 } )
28112779}
0 commit comments