@@ -83,7 +83,85 @@ declare_clippy_lint! {
8383 complexity,
8484 "arguments that is only used in recursion can be removed"
8585}
86- impl_lint_pass ! ( OnlyUsedInRecursion => [ ONLY_USED_IN_RECURSION ] ) ;
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`.
95+ ///
96+ /// ### Known problems
97+ /// Too many code paths in the linting code are currently untested and prone to produce false
98+ /// positives or are prone to have performance implications.
99+ ///
100+ /// In some cases, this would not catch all useless arguments.
101+ ///
102+ /// ```no_run
103+ /// struct Foo;
104+ /// impl Foo {
105+ /// fn foo(&self, a: usize) -> usize {
106+ /// let f = |x| x;
107+ ///
108+ /// if a == 0 {
109+ /// 1
110+ /// } else {
111+ /// f(self).foo(a)
112+ /// }
113+ /// }
114+ /// }
115+ /// ```
116+ ///
117+ /// For example, here `self` is only used in recursion, but the lint would not catch it.
118+ ///
119+ /// List of some examples that can not be caught:
120+ /// - binary operation of non-primitive types
121+ /// - closure usage
122+ /// - some `break` relative operations
123+ /// - struct pattern binding
124+ ///
125+ /// Also, when you recurse the function name with path segments, it is not possible to detect.
126+ ///
127+ /// ### Example
128+ /// ```no_run
129+ /// struct Foo;
130+ /// impl Foo {
131+ /// fn f(&self, n: u32) -> u32 {
132+ /// if n == 0 {
133+ /// 1
134+ /// } else {
135+ /// n * self.f(n - 1)
136+ /// }
137+ /// }
138+ /// }
139+ /// # fn main() {
140+ /// # print!("{}", Foo.f(10));
141+ /// # }
142+ /// ```
143+ /// Use instead:
144+ /// ```no_run
145+ /// struct Foo;
146+ /// impl Foo {
147+ /// fn f(n: u32) -> u32 {
148+ /// if n == 0 {
149+ /// 1
150+ /// } else {
151+ /// n * Self::f(n - 1)
152+ /// }
153+ /// }
154+ /// }
155+ /// # fn main() {
156+ /// # print!("{}", Foo::f(10));
157+ /// # }
158+ /// ```
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"
163+ }
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