@@ -5,12 +5,13 @@ use itertools::{izip, Itertools};
5
5
use rustc_ast:: { walk_list, Label , Mutability } ;
6
6
use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
7
7
use rustc_errors:: Applicability ;
8
- use rustc_hir:: def:: Res ;
8
+ use rustc_hir:: def:: { DefKind , Res } ;
9
+ use rustc_hir:: def_id:: DefId ;
9
10
use rustc_hir:: definitions:: { DefPathData , DisambiguatedDefPathData } ;
10
11
use rustc_hir:: intravisit:: { walk_expr, FnKind , Visitor } ;
11
12
use 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 ,
14
15
} ;
15
16
use rustc_lint:: { LateContext , LateLintPass } ;
16
17
use rustc_middle:: ty;
@@ -94,13 +95,15 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
94
95
& mut self ,
95
96
cx : & LateContext < ' tcx > ,
96
97
kind : FnKind < ' tcx > ,
97
- _ : & ' tcx rustc_hir:: FnDecl < ' tcx > ,
98
+ decl : & ' tcx rustc_hir:: FnDecl < ' tcx > ,
98
99
body : & ' tcx Body < ' tcx > ,
99
100
_: Span ,
100
101
id : HirId ,
101
102
) {
102
103
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
+
104
107
if data. len ( ) > 1 {
105
108
match data. get ( data. len ( ) - 2 ) {
106
109
Some ( DisambiguatedDefPathData {
@@ -111,6 +114,8 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
111
114
}
112
115
}
113
116
117
+ let has_self = !matches ! ( decl. implicit_self, ImplicitSelfKind :: None ) ;
118
+
114
119
let ty_res = cx. typeck_results ( ) ;
115
120
let param_span = body
116
121
. params
@@ -122,10 +127,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
122
127
} ) ;
123
128
v
124
129
} )
125
- . skip ( match kind {
126
- FnKind :: Method ( ..) => 1 ,
127
- _ => 0 ,
128
- } )
130
+ . skip ( if has_self { 1 } else { 0 } )
129
131
. filter ( |( _, _, ident) | !ident. name . as_str ( ) . starts_with ( '_' ) )
130
132
. collect_vec ( ) ;
131
133
@@ -139,7 +141,9 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
139
141
break_vars : FxHashMap :: default ( ) ,
140
142
params,
141
143
fn_ident : ident,
144
+ fn_def_id : def_id,
142
145
is_method : matches ! ( kind, FnKind :: Method ( ..) ) ,
146
+ has_self,
143
147
ty_res,
144
148
ty_ctx : cx. tcx ,
145
149
} ;
@@ -242,7 +246,9 @@ pub struct SideEffectVisit<'tcx> {
242
246
break_vars : FxHashMap < Ident , Vec < ( HirId , bool ) > > ,
243
247
params : Vec < & ' tcx Pat < ' tcx > > ,
244
248
fn_ident : Ident ,
249
+ fn_def_id : DefId ,
245
250
is_method : bool ,
251
+ has_self : bool ,
246
252
ty_res : & ' tcx TypeckResults < ' tcx > ,
247
253
ty_ctx : TyCtxt < ' tcx > ,
248
254
}
@@ -479,41 +485,55 @@ impl<'tcx> SideEffectVisit<'tcx> {
479
485
let mut ret_vars = std:: mem:: take ( & mut self . ret_vars ) ;
480
486
self . add_side_effect ( ret_vars. clone ( ) ) ;
481
487
488
+ let mut is_recursive = false ;
489
+
482
490
if_chain ! {
483
- if !self . is_method ;
491
+ if !self . has_self ;
484
492
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;
489
495
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 ;
514
508
}
515
509
}
516
510
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
+
517
537
self . ret_vars . append ( & mut ret_vars) ;
518
538
}
519
539
0 commit comments