@@ -126,29 +126,36 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
126126 }
127127 }
128128
129- let mut pre_order = FxHashMap :: default ( ) ;
130-
131- visitor. graph . iter ( ) . for_each ( |( _, next) | {
132- next. iter ( ) . for_each ( |i| {
133- * pre_order. entry ( * i) . or_insert ( 0 ) += 1 ;
134- } ) ;
135- } ) ;
136-
137129 for ( id, span, ident) in param_span {
138130 // if the variable is not used in recursion, it would be marked as unused
139- if !visitor. has_side_effect . contains ( & id)
140- && * pre_order. get ( & id) . unwrap_or ( & 0 ) > 0
141- && visitor. graph . contains_key ( & id)
142- {
143- span_lint_and_sugg (
144- cx,
145- ONLY_USED_IN_RECURSION ,
146- span,
147- "parameter is only used in recursion with no side-effects" ,
148- "if this is intentional, prefix with an underscore" ,
149- format ! ( "_{}" , ident. name. as_str( ) ) ,
150- Applicability :: MaybeIncorrect ,
151- ) ;
131+ if !visitor. has_side_effect . contains ( & id) {
132+ let mut queue = VecDeque :: new ( ) ;
133+ let mut visited = FxHashSet :: default ( ) ;
134+
135+ queue. push_back ( id) ;
136+
137+ while let Some ( id) = queue. pop_front ( ) {
138+ if let Some ( next) = visitor. graph . get ( & id) {
139+ for i in next {
140+ if !visited. contains ( i) {
141+ visited. insert ( id) ;
142+ queue. push_back ( * i) ;
143+ }
144+ }
145+ }
146+ }
147+
148+ if visited. contains ( & id) {
149+ span_lint_and_sugg (
150+ cx,
151+ ONLY_USED_IN_RECURSION ,
152+ span,
153+ "parameter is only used in recursion with no side-effects" ,
154+ "if this is intentional, prefix with an underscore" ,
155+ format ! ( "_{}" , ident. name. as_str( ) ) ,
156+ Applicability :: MaybeIncorrect ,
157+ ) ;
158+ }
152159 }
153160 }
154161 }
@@ -200,7 +207,7 @@ impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> {
200207 StmtKind :: Local ( Local {
201208 pat, init : Some ( init) , ..
202209 } ) => {
203- self . visit_pat_expr ( pat, init) ;
210+ self . visit_pat_expr ( pat, init, false ) ;
204211 } ,
205212 StmtKind :: Item ( i) => {
206213 let item = self . ty_ctx . hir ( ) . item ( i) ;
@@ -229,7 +236,7 @@ impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> {
229236 self . visit_bin_op ( lhs, rhs) ;
230237 } ,
231238 ExprKind :: Unary ( op, expr) => self . visit_un_op ( op, expr) ,
232- ExprKind :: Let ( Let { pat, init, .. } ) => self . visit_pat_expr ( pat, init) ,
239+ ExprKind :: Let ( Let { pat, init, .. } ) => self . visit_pat_expr ( pat, init, false ) ,
233240 ExprKind :: If ( bind, then_expr, else_expr) => {
234241 self . visit_if ( bind, then_expr, else_expr) ;
235242 } ,
@@ -333,7 +340,7 @@ impl<'tcx> SideEffectVisit<'tcx> {
333340 let lhs_vars = std:: mem:: take ( & mut self . ret_vars ) ;
334341 self . visit_expr ( rhs) ;
335342 let rhs_vars = std:: mem:: take ( & mut self . ret_vars ) ;
336- self . connect_assign ( & lhs_vars, & rhs_vars) ;
343+ self . connect_assign ( & lhs_vars, & rhs_vars, false ) ;
337344 } ,
338345 }
339346 }
@@ -373,26 +380,26 @@ impl<'tcx> SideEffectVisit<'tcx> {
373380 }
374381 }
375382
376- fn visit_pat_expr ( & mut self , pat : & ' tcx Pat < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
383+ fn visit_pat_expr ( & mut self , pat : & ' tcx Pat < ' tcx > , expr : & ' tcx Expr < ' tcx > , connect_self : bool ) {
377384 match ( & pat. kind , & expr. kind ) {
378385 ( PatKind :: Tuple ( pats, _) , ExprKind :: Tup ( exprs) ) => {
379386 self . ret_vars = izip ! ( * pats, * exprs)
380387 . flat_map ( |( pat, expr) | {
381- self . visit_pat_expr ( pat, expr) ;
388+ self . visit_pat_expr ( pat, expr, connect_self ) ;
382389 std:: mem:: take ( & mut self . ret_vars )
383390 } )
384391 . collect ( ) ;
385392 } ,
386393 ( PatKind :: Slice ( front_exprs, _, back_exprs) , ExprKind :: Array ( exprs) ) => {
387394 let mut vars = izip ! ( * front_exprs, * exprs)
388395 . flat_map ( |( pat, expr) | {
389- self . visit_pat_expr ( pat, expr) ;
396+ self . visit_pat_expr ( pat, expr, connect_self ) ;
390397 std:: mem:: take ( & mut self . ret_vars )
391398 } )
392399 . collect ( ) ;
393400 self . ret_vars = izip ! ( back_exprs. iter( ) . rev( ) , exprs. iter( ) . rev( ) )
394401 . flat_map ( |( pat, expr) | {
395- self . visit_pat_expr ( pat, expr) ;
402+ self . visit_pat_expr ( pat, expr, connect_self ) ;
396403 std:: mem:: take ( & mut self . ret_vars )
397404 } )
398405 . collect ( ) ;
@@ -403,7 +410,7 @@ impl<'tcx> SideEffectVisit<'tcx> {
403410 pat. each_binding ( |_, id, _, _| lhs_vars. push ( ( id, false ) ) ) ;
404411 self . visit_expr ( expr) ;
405412 let rhs_vars = std:: mem:: take ( & mut self . ret_vars ) ;
406- self . connect_assign ( & lhs_vars, & rhs_vars) ;
413+ self . connect_assign ( & lhs_vars, & rhs_vars, connect_self ) ;
407414 self . ret_vars = rhs_vars;
408415 } ,
409416 }
@@ -424,7 +431,7 @@ impl<'tcx> SideEffectVisit<'tcx> {
424431 then {
425432 izip!( self . params. clone( ) , args)
426433 . for_each( |( pat, expr) | {
427- self . visit_pat_expr( pat, expr) ;
434+ self . visit_pat_expr( pat, expr, true ) ;
428435 self . ret_vars. clear( ) ;
429436 } ) ;
430437 } else {
@@ -463,7 +470,7 @@ impl<'tcx> SideEffectVisit<'tcx> {
463470 then {
464471 izip!( self . params. clone( ) , args. iter( ) )
465472 . for_each( |( pat, expr) | {
466- self . visit_pat_expr( pat, expr) ;
473+ self . visit_pat_expr( pat, expr, true ) ;
467474 self . ret_vars. clear( ) ;
468475 } ) ;
469476 } else {
@@ -511,7 +518,7 @@ impl<'tcx> SideEffectVisit<'tcx> {
511518 self . contains_side_effect = false ;
512519 // this would visit `expr` multiple times
513520 // but couldn't think of a better way
514- self . visit_pat_expr ( arm. pat , expr) ;
521+ self . visit_pat_expr ( arm. pat , expr, false ) ;
515522 let mut vars = std:: mem:: take ( & mut self . ret_vars ) ;
516523 let _ = arm. guard . as_ref ( ) . map ( |guard| {
517524 self . visit_expr ( match guard {
@@ -532,7 +539,7 @@ impl<'tcx> SideEffectVisit<'tcx> {
532539 self . ret_vars . append ( & mut expr_vars) ;
533540 }
534541
535- fn connect_assign ( & mut self , lhs : & [ ( HirId , bool ) ] , rhs : & [ ( HirId , bool ) ] ) {
542+ fn connect_assign ( & mut self , lhs : & [ ( HirId , bool ) ] , rhs : & [ ( HirId , bool ) ] , connect_self : bool ) {
536543 // if mutable dereference is on assignment it can have side-effect
537544 // (this can lead to parameter mutable dereference and change the original value)
538545 // too hard to detect whether this value is from parameter, so this would all
@@ -554,15 +561,19 @@ impl<'tcx> SideEffectVisit<'tcx> {
554561 // unwrap is possible since rhs is not empty
555562 let rhs_first = rhs. first ( ) . unwrap ( ) ;
556563 for ( id, _) in lhs. iter ( ) {
557- self . graph
558- . entry ( * id)
559- . or_insert_with ( FxHashSet :: default)
560- . insert ( rhs_first. 0 ) ;
564+ if connect_self || * id != rhs_first. 0 {
565+ self . graph
566+ . entry ( * id)
567+ . or_insert_with ( FxHashSet :: default)
568+ . insert ( rhs_first. 0 ) ;
569+ }
561570 }
562571
563572 let rhs = rhs. iter ( ) ;
564573 izip ! ( rhs. clone( ) . cycle( ) . skip( 1 ) , rhs) . for_each ( |( from, to) | {
565- self . graph . entry ( from. 0 ) . or_insert_with ( FxHashSet :: default) . insert ( to. 0 ) ;
574+ if connect_self || from. 0 != to. 0 {
575+ self . graph . entry ( from. 0 ) . or_insert_with ( FxHashSet :: default) . insert ( to. 0 ) ;
576+ }
566577 } ) ;
567578 }
568579
0 commit comments