@@ -24,6 +24,33 @@ declare_clippy_lint! {
2424 /// the calculations have no side-effects (function calls or mutating dereference)
2525 /// and the assigned variables are also only in recursion, it is useless.
2626 ///
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+ ///
2754 /// ### Known problems
2855 /// Too many code paths in the linting code are currently untested and prone to produce false
2956 /// positives or are prone to have performance implications.
@@ -51,39 +78,90 @@ declare_clippy_lint! {
5178 /// - struct pattern binding
5279 ///
5380 /// 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`.
5495 ///
5596 /// ### Example
5697 /// ```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+ /// }
62106 /// }
63107 /// }
64108 /// # fn main() {
65- /// # print!("{}", f(1, 1 ));
109+ /// # print!("{}", Foo.f(10 ));
66110 /// # }
67111 /// ```
68112 /// Use instead:
69113 /// ```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+ /// }
75122 /// }
76123 /// }
77124 /// # fn main() {
78- /// # print!("{}", f(1 ));
125+ /// # print!("{}", Foo::f(10 ));
79126 /// # }
80127 /// ```
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"
85163}
86- impl_lint_pass ! ( OnlyUsedInRecursion => [ ONLY_USED_IN_RECURSION ] ) ;
164+ impl_lint_pass ! ( OnlyUsedInRecursion => [ ONLY_USED_IN_RECURSION , SELF_ONLY_USED_IN_RECURSION ] ) ;
87165
88166#[ derive( Clone , Copy ) ]
89167enum FnKind {
@@ -355,26 +433,39 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
355433 self . params . flag_for_linting ( ) ;
356434 for param in & self . params . params {
357435 if param. apply_lint . get ( ) {
358- span_lint_and_then (
359- cx,
360- ONLY_USED_IN_RECURSION ,
361- param. ident . span ,
362- "parameter is only used in recursion" ,
363- |diag| {
364- if param. ident . name != kw:: SelfLower {
436+ if param. ident . name == kw:: SelfLower {
437+ span_lint_and_then (
438+ cx,
439+ SELF_ONLY_USED_IN_RECURSION ,
440+ param. ident . span ,
441+ "`self` is only used in recursion" ,
442+ |diag| {
443+ diag. span_note (
444+ param. uses . iter ( ) . map ( |x| x. span ) . collect :: < Vec < _ > > ( ) ,
445+ "`self` used here" ,
446+ ) ;
447+ } ,
448+ ) ;
449+ } else {
450+ span_lint_and_then (
451+ cx,
452+ ONLY_USED_IN_RECURSION ,
453+ param. ident . span ,
454+ "parameter is only used in recursion" ,
455+ |diag| {
365456 diag. span_suggestion (
366457 param. ident . span ,
367458 "if this is intentional, prefix it with an underscore" ,
368459 format ! ( "_{}" , param. ident. name) ,
369460 Applicability :: MaybeIncorrect ,
370461 ) ;
371- }
372- diag . span_note (
373- param . uses . iter ( ) . map ( |x| x . span ) . collect :: < Vec < _ > > ( ) ,
374- "parameter used here" ,
375- ) ;
376- } ,
377- ) ;
462+ diag . span_note (
463+ param . uses . iter ( ) . map ( |x| x . span ) . collect :: < Vec < _ > > ( ) ,
464+ "parameter used here" ,
465+ ) ;
466+ } ,
467+ ) ;
468+ }
378469 }
379470 }
380471 self . params . clear ( ) ;
0 commit comments