@@ -5,12 +5,13 @@ use itertools::{izip, Itertools};
55use rustc_ast:: { walk_list, Label , Mutability } ;
66use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
77use rustc_errors:: Applicability ;
8- use rustc_hir:: def:: Res ;
8+ use rustc_hir:: def:: { DefKind , Res } ;
9+ use rustc_hir:: def_id:: DefId ;
910use rustc_hir:: definitions:: { DefPathData , DisambiguatedDefPathData } ;
1011use rustc_hir:: intravisit:: { walk_expr, FnKind , Visitor } ;
1112use rustc_hir:: {
12- Arm , Block , Body , Expr , ExprKind , Guard , HirId , Let , Local , Pat , PatKind , Path , PathSegment , QPath , Stmt , StmtKind ,
13- UnOp ,
13+ Arm , Block , Body , Expr , ExprKind , Guard , HirId , ImplicitSelfKind , Let , Local , Pat , PatKind , Path , PathSegment ,
14+ QPath , Stmt , StmtKind , TyKind , UnOp ,
1415} ;
1516use rustc_lint:: { LateContext , LateLintPass } ;
1617use rustc_middle:: ty;
@@ -94,13 +95,15 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
9495 & mut self ,
9596 cx : & LateContext < ' tcx > ,
9697 kind : FnKind < ' tcx > ,
97- _ : & ' tcx rustc_hir:: FnDecl < ' tcx > ,
98+ decl : & ' tcx rustc_hir:: FnDecl < ' tcx > ,
9899 body : & ' tcx Body < ' tcx > ,
99100 _: Span ,
100101 id : HirId ,
101102 ) {
102103 if let FnKind :: ItemFn ( ident, ..) | FnKind :: Method ( ident, ..) = kind {
103- let data = cx. tcx . def_path ( cx. tcx . hir ( ) . local_def_id ( id) . to_def_id ( ) ) . data ;
104+ let def_id = id. owner . to_def_id ( ) ;
105+ let data = cx. tcx . def_path ( def_id) . data ;
106+
104107 if data. len ( ) > 1 {
105108 match data. get ( data. len ( ) - 2 ) {
106109 Some ( DisambiguatedDefPathData {
@@ -111,6 +114,8 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
111114 }
112115 }
113116
117+ let has_self = !matches ! ( decl. implicit_self, ImplicitSelfKind :: None ) ;
118+
114119 let ty_res = cx. typeck_results ( ) ;
115120 let param_span = body
116121 . params
@@ -122,10 +127,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
122127 } ) ;
123128 v
124129 } )
125- . skip ( match kind {
126- FnKind :: Method ( ..) => 1 ,
127- _ => 0 ,
128- } )
130+ . skip ( if has_self { 1 } else { 0 } )
129131 . filter ( |( _, _, ident) | !ident. name . as_str ( ) . starts_with ( '_' ) )
130132 . collect_vec ( ) ;
131133
@@ -139,7 +141,9 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
139141 break_vars : FxHashMap :: default ( ) ,
140142 params,
141143 fn_ident : ident,
144+ fn_def_id : def_id,
142145 is_method : matches ! ( kind, FnKind :: Method ( ..) ) ,
146+ has_self,
143147 ty_res,
144148 ty_ctx : cx. tcx ,
145149 } ;
@@ -242,7 +246,9 @@ pub struct SideEffectVisit<'tcx> {
242246 break_vars : FxHashMap < Ident , Vec < ( HirId , bool ) > > ,
243247 params : Vec < & ' tcx Pat < ' tcx > > ,
244248 fn_ident : Ident ,
249+ fn_def_id : DefId ,
245250 is_method : bool ,
251+ has_self : bool ,
246252 ty_res : & ' tcx TypeckResults < ' tcx > ,
247253 ty_ctx : TyCtxt < ' tcx > ,
248254}
@@ -479,41 +485,55 @@ impl<'tcx> SideEffectVisit<'tcx> {
479485 let mut ret_vars = std:: mem:: take ( & mut self . ret_vars ) ;
480486 self . add_side_effect ( ret_vars. clone ( ) ) ;
481487
488+ let mut is_recursive = false ;
489+
482490 if_chain ! {
483- if !self . is_method ;
491+ if !self . has_self ;
484492 if let ExprKind :: Path ( QPath :: Resolved ( _, path) ) = callee. kind;
485- if let Res :: Def ( ..) = path. res;
486- if path. segments. len( ) == 1 ;
487- let ident = path. segments. last( ) . unwrap( ) . ident;
488- if ident == self . fn_ident;
493+ if let Res :: Def ( DefKind :: Fn , def_id) = path. res;
494+ if self . fn_def_id == def_id;
489495 then {
490- izip!( self . params. clone( ) , args)
491- . for_each( |( pat, expr) | {
492- self . visit_pat_expr( pat, expr, true ) ;
493- self . ret_vars. clear( ) ;
494- } ) ;
495- } else {
496- // This would set arguments used in closure that does not have side-effect.
497- // Closure itself can be detected whether there is a side-effect, but the
498- // value of variable that is holding closure can change.
499- // So, we just check the variables.
500- self . ret_vars = args
501- . iter( )
502- . flat_map( |expr| {
503- self . visit_expr( expr) ;
504- std:: mem:: take( & mut self . ret_vars)
505- } )
506- . collect_vec( )
507- . into_iter( )
508- . map( |id| {
509- self . has_side_effect. insert( id. 0 ) ;
510- id
511- } )
512- . collect( ) ;
513- self . contains_side_effect = true ;
496+ is_recursive = true ;
497+ }
498+ }
499+
500+ if_chain ! {
501+ if !self . has_self && self . is_method;
502+ if let ExprKind :: Path ( QPath :: TypeRelative ( ty, segment) ) = callee. kind;
503+ if segment. ident == self . fn_ident;
504+ if let TyKind :: Path ( QPath :: Resolved ( _, path) ) = ty. kind;
505+ if let Res :: SelfTy ( ..) = path. res;
506+ then {
507+ is_recursive = true ;
514508 }
515509 }
516510
511+ if is_recursive {
512+ izip ! ( self . params. clone( ) , args) . for_each ( |( pat, expr) | {
513+ self . visit_pat_expr ( pat, expr, true ) ;
514+ self . ret_vars . clear ( ) ;
515+ } ) ;
516+ } else {
517+ // This would set arguments used in closure that does not have side-effect.
518+ // Closure itself can be detected whether there is a side-effect, but the
519+ // value of variable that is holding closure can change.
520+ // So, we just check the variables.
521+ self . ret_vars = args
522+ . iter ( )
523+ . flat_map ( |expr| {
524+ self . visit_expr ( expr) ;
525+ std:: mem:: take ( & mut self . ret_vars )
526+ } )
527+ . collect_vec ( )
528+ . into_iter ( )
529+ . map ( |id| {
530+ self . has_side_effect . insert ( id. 0 ) ;
531+ id
532+ } )
533+ . collect ( ) ;
534+ self . contains_side_effect = true ;
535+ }
536+
517537 self . ret_vars . append ( & mut ret_vars) ;
518538 }
519539
0 commit comments