@@ -24,6 +24,33 @@ declare_clippy_lint! {
24
24
/// the calculations have no side-effects (function calls or mutating dereference)
25
25
/// and the assigned variables are also only in recursion, it is useless.
26
26
///
27
+ /// ### Example
28
+ /// ```no_run
29
+ /// fn f(a: usize, b: usize) -> usize {
30
+ /// if a == 0 {
31
+ /// 1
32
+ /// } else {
33
+ /// f(a - 1, b + 1)
34
+ /// }
35
+ /// }
36
+ /// # fn main() {
37
+ /// # print!("{}", f(1, 1));
38
+ /// # }
39
+ /// ```
40
+ /// Use instead:
41
+ /// ```no_run
42
+ /// fn f(a: usize) -> usize {
43
+ /// if a == 0 {
44
+ /// 1
45
+ /// } else {
46
+ /// f(a - 1)
47
+ /// }
48
+ /// }
49
+ /// # fn main() {
50
+ /// # print!("{}", f(1));
51
+ /// # }
52
+ /// ```
53
+ ///
27
54
/// ### Known problems
28
55
/// Too many code paths in the linting code are currently untested and prone to produce false
29
56
/// positives or are prone to have performance implications.
@@ -51,39 +78,90 @@ declare_clippy_lint! {
51
78
/// - struct pattern binding
52
79
///
53
80
/// Also, when you recurse the function name with path segments, it is not possible to detect.
81
+ #[ clippy:: version = "1.61.0" ]
82
+ pub ONLY_USED_IN_RECURSION ,
83
+ complexity,
84
+ "arguments that is only used in recursion can be removed"
85
+ }
86
+
87
+ declare_clippy_lint ! {
88
+ /// ### What it does
89
+ /// Checks for `self` receiver that is only used in recursion with no side-effects.
90
+ ///
91
+ /// ### Why is this bad?
92
+ ///
93
+ /// It may be possible to remove the `self` argument, allowing the function to be
94
+ /// used without an object of type `Self`.
54
95
///
55
96
/// ### Example
56
97
/// ```no_run
57
- /// fn f(a: usize, b: usize) -> usize {
58
- /// if a == 0 {
59
- /// 1
60
- /// } else {
61
- /// f(a - 1, b + 1)
98
+ /// struct Foo;
99
+ /// impl Foo {
100
+ /// fn f(&self, n: u32) -> u32 {
101
+ /// if n == 0 {
102
+ /// 1
103
+ /// } else {
104
+ /// n * self.f(n - 1)
105
+ /// }
62
106
/// }
63
107
/// }
64
108
/// # fn main() {
65
- /// # print!("{}", f(1, 1 ));
109
+ /// # print!("{}", Foo.f(10 ));
66
110
/// # }
67
111
/// ```
68
112
/// Use instead:
69
113
/// ```no_run
70
- /// fn f(a: usize) -> usize {
71
- /// if a == 0 {
72
- /// 1
73
- /// } else {
74
- /// f(a - 1)
114
+ /// struct Foo;
115
+ /// impl Foo {
116
+ /// fn f(n: u32) -> u32 {
117
+ /// if n == 0 {
118
+ /// 1
119
+ /// } else {
120
+ /// n * Self::f(n - 1)
121
+ /// }
75
122
/// }
76
123
/// }
77
124
/// # fn main() {
78
- /// # print!("{}", f(1 ));
125
+ /// # print!("{}", Foo::f(10 ));
79
126
/// # }
80
127
/// ```
81
- #[ clippy:: version = "1.61.0" ]
82
- pub ONLY_USED_IN_RECURSION ,
83
- complexity,
84
- "arguments that is only used in recursion can be removed"
128
+ ///
129
+ /// ### Known problems
130
+ /// Too many code paths in the linting code are currently untested and prone to produce false
131
+ /// positives or are prone to have performance implications.
132
+ ///
133
+ /// In some cases, this would not catch all useless arguments.
134
+ ///
135
+ /// ```no_run
136
+ /// struct Foo;
137
+ /// impl Foo {
138
+ /// fn foo(&self, a: usize) -> usize {
139
+ /// let f = |x| x;
140
+ ///
141
+ /// if a == 0 {
142
+ /// 1
143
+ /// } else {
144
+ /// f(self).foo(a)
145
+ /// }
146
+ /// }
147
+ /// }
148
+ /// ```
149
+ ///
150
+ /// For example, here `self` is only used in recursion, but the lint would not catch it.
151
+ ///
152
+ /// List of some examples that can not be caught:
153
+ /// - binary operation of non-primitive types
154
+ /// - closure usage
155
+ /// - some `break` relative operations
156
+ /// - struct pattern binding
157
+ ///
158
+ /// Also, when you recurse the function name with path segments, it is not possible to detect.
159
+ #[ clippy:: version = "1.92.0" ]
160
+ pub SELF_ONLY_USED_IN_RECURSION ,
161
+ pedantic,
162
+ "self receiver only used to recursively call method can be removed"
85
163
}
86
- impl_lint_pass ! ( OnlyUsedInRecursion => [ ONLY_USED_IN_RECURSION ] ) ;
164
+ impl_lint_pass ! ( OnlyUsedInRecursion => [ ONLY_USED_IN_RECURSION , SELF_ONLY_USED_IN_RECURSION ] ) ;
87
165
88
166
#[ derive( Clone , Copy ) ]
89
167
enum FnKind {
@@ -357,26 +435,39 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
357
435
self . params . flag_for_linting ( ) ;
358
436
for param in & self . params . params {
359
437
if param. apply_lint . get ( ) {
360
- span_lint_and_then (
361
- cx,
362
- ONLY_USED_IN_RECURSION ,
363
- param. ident . span ,
364
- "parameter is only used in recursion" ,
365
- |diag| {
366
- if param. ident . name != kw:: SelfLower {
438
+ if param. ident . name == kw:: SelfLower {
439
+ span_lint_and_then (
440
+ cx,
441
+ SELF_ONLY_USED_IN_RECURSION ,
442
+ param. ident . span ,
443
+ "`self` is only used in recursion" ,
444
+ |diag| {
445
+ diag. span_note (
446
+ param. uses . iter ( ) . map ( |x| x. span ) . collect :: < Vec < _ > > ( ) ,
447
+ "`self` used here" ,
448
+ ) ;
449
+ } ,
450
+ ) ;
451
+ } else {
452
+ span_lint_and_then (
453
+ cx,
454
+ ONLY_USED_IN_RECURSION ,
455
+ param. ident . span ,
456
+ "parameter is only used in recursion" ,
457
+ |diag| {
367
458
diag. span_suggestion (
368
459
param. ident . span ,
369
460
"if this is intentional, prefix it with an underscore" ,
370
461
format ! ( "_{}" , param. ident. name) ,
371
462
Applicability :: MaybeIncorrect ,
372
463
) ;
373
- }
374
- diag . span_note (
375
- param . uses . iter ( ) . map ( |x| x . span ) . collect :: < Vec < _ > > ( ) ,
376
- "parameter used here" ,
377
- ) ;
378
- } ,
379
- ) ;
464
+ diag . span_note (
465
+ param . uses . iter ( ) . map ( |x| x . span ) . collect :: < Vec < _ > > ( ) ,
466
+ "parameter used here" ,
467
+ ) ;
468
+ } ,
469
+ ) ;
470
+ }
380
471
}
381
472
}
382
473
self . params . clear ( ) ;
0 commit comments