@@ -21,7 +21,7 @@ use rustc_span::Span;
21
21
22
22
declare_clippy_lint ! {
23
23
/// ### What it does
24
- /// Checks for arguments that is only used in recursion with no side-effects.
24
+ /// Checks for arguments that are only used in recursion with no side-effects.
25
25
/// The arguments can be involved in calculations and assignments but as long as
26
26
/// the calculations have no side-effects (function calls or mutating dereference)
27
27
/// and the assigned variables are also only in recursion, it is useless.
@@ -30,7 +30,21 @@ declare_clippy_lint! {
30
30
/// The could contain a useless calculation and can make function simpler.
31
31
///
32
32
/// ### Known problems
33
- /// It could not catch the variable that has no side effects but only used in recursion.
33
+ /// In some cases, this would not catch all useless arguments.
34
+ ///
35
+ /// ```rust
36
+ /// fn foo(a: usize, b: usize) -> usize {
37
+ /// let f = |x| x + 1;
38
+ ///
39
+ /// if a == 0 {
40
+ /// 1
41
+ /// } else {
42
+ /// foo(a - 1, f(b))
43
+ /// }
44
+ /// }
45
+ /// ```
46
+ ///
47
+ /// For example, the argument `b` is only used in recursion, but the lint would not catch it.
34
48
///
35
49
/// ### Example
36
50
/// ```rust
@@ -111,10 +125,12 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
111
125
112
126
visitor. visit_expr ( & body. value ) ;
113
127
let vars = std:: mem:: take ( & mut visitor. ret_vars ) ;
128
+ // this would set the return variables to side effect
114
129
visitor. add_side_effect ( vars) ;
115
130
116
131
let mut queue = visitor. has_side_effect . iter ( ) . copied ( ) . collect :: < VecDeque < _ > > ( ) ;
117
132
133
+ // a simple BFS to check all the variables that have side effect
118
134
while let Some ( id) = queue. pop_front ( ) {
119
135
if let Some ( next) = visitor. graph . get ( & id) {
120
136
for i in next {
@@ -134,6 +150,8 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
134
150
135
151
queue. push_back ( id) ;
136
152
153
+ // a simple BFS to check the graph can reach to itself
154
+ // if it can't, it means the variable is never used in recursion
137
155
while let Some ( id) = queue. pop_front ( ) {
138
156
if let Some ( next) = visitor. graph . get ( & id) {
139
157
for i in next {
@@ -150,7 +168,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
150
168
cx,
151
169
ONLY_USED_IN_RECURSION ,
152
170
span,
153
- "parameter is only used in recursion with no side-effects " ,
171
+ "parameter is only used in recursion" ,
154
172
"if this is intentional, prefix with an underscore" ,
155
173
format ! ( "_{}" , ident. name. as_str( ) ) ,
156
174
Applicability :: MaybeIncorrect ,
@@ -178,6 +196,21 @@ pub fn is_array(ty: Ty<'_>) -> bool {
178
196
}
179
197
}
180
198
199
+ /// This builds the graph of side effect.
200
+ /// The edge `a -> b` means if `a` has side effect, `b` will have side effect.
201
+ ///
202
+ /// There are some exmaple in following code:
203
+ /// ```rust, ignore
204
+ /// let b = 1;
205
+ /// let a = b; // a -> b
206
+ /// let (c, d) = (a, b); // c -> b, d -> b
207
+ ///
208
+ /// let e = if a == 0 { // e -> a
209
+ /// c // e -> c
210
+ /// } else {
211
+ /// d // e -> d
212
+ /// };
213
+ /// ```
181
214
pub struct SideEffectVisit < ' tcx > {
182
215
graph : FxHashMap < HirId , FxHashSet < HirId > > ,
183
216
has_side_effect : FxHashSet < HirId > ,
@@ -241,6 +274,7 @@ impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> {
241
274
self . visit_if ( bind, then_expr, else_expr) ;
242
275
} ,
243
276
ExprKind :: Match ( expr, arms, _) => self . visit_match ( expr, arms) ,
277
+ // since analysing the closure is not easy, just set all variables in it to side-effect
244
278
ExprKind :: Closure ( _, _, body_id, _, _) => {
245
279
let body = self . ty_ctx . hir ( ) . body ( body_id) ;
246
280
self . visit_body ( body) ;
@@ -359,6 +393,9 @@ impl<'tcx> SideEffectVisit<'tcx> {
359
393
let mut ret_vars = std:: mem:: take ( & mut self . ret_vars ) ;
360
394
self . visit_expr ( rhs) ;
361
395
self . ret_vars . append ( & mut ret_vars) ;
396
+
397
+ // the binary operation between non primitive values are overloaded operators
398
+ // so they can have side-effects
362
399
if !is_primitive ( self . ty_res . expr_ty ( lhs) ) || !is_primitive ( self . ty_res . expr_ty ( rhs) ) {
363
400
self . ret_vars . iter ( ) . for_each ( |id| {
364
401
self . has_side_effect . insert ( id. 0 ) ;
0 commit comments