@@ -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 {
@@ -357,26 +435,39 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
357435 self . params . flag_for_linting ( ) ;
358436 for param in & self . params . params {
359437 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| {
367458 diag. span_suggestion (
368459 param. ident . span ,
369460 "if this is intentional, prefix it with an underscore" ,
370461 format ! ( "_{}" , param. ident. name) ,
371462 Applicability :: MaybeIncorrect ,
372463 ) ;
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+ }
380471 }
381472 }
382473 self . params . clear ( ) ;
0 commit comments