Skip to content

Commit 8ea47a6

Browse files
Add clippy::self_only_used_in_recursion lint (#14787)
and use it instead of `clippy::only_used_in_recursion` when the parameter in question is self. Fixes #10370 changelog: [`only_used_in_recursion`]: Don't lint if parameter is `self`; add pedantic `self_only_used_in_recursion` lint.
2 parents fac86e3 + 63a5508 commit 8ea47a6

File tree

5 files changed

+162
-66
lines changed

5 files changed

+162
-66
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6598,6 +6598,7 @@ Released 2018-09-13
65986598
[`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment
65996599
[`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors
66006600
[`self_named_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_module_files
6601+
[`self_only_used_in_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_only_used_in_recursion
66016602
[`semicolon_if_nothing_returned`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned
66026603
[`semicolon_inside_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_inside_block
66036604
[`semicolon_outside_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_outside_block

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
576576
crate::nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES_INFO,
577577
crate::octal_escapes::OCTAL_ESCAPES_INFO,
578578
crate::only_used_in_recursion::ONLY_USED_IN_RECURSION_INFO,
579+
crate::only_used_in_recursion::SELF_ONLY_USED_IN_RECURSION_INFO,
579580
crate::operators::ABSURD_EXTREME_COMPARISONS_INFO,
580581
crate::operators::ARITHMETIC_SIDE_EFFECTS_INFO,
581582
crate::operators::ASSIGN_OP_PATTERN_INFO,

clippy_lints/src/only_used_in_recursion.rs

Lines changed: 122 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -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)]
89167
enum 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();

tests/ui/only_used_in_recursion.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#![warn(clippy::only_used_in_recursion)]
2+
#![warn(clippy::self_only_used_in_recursion)]
23
//@no-rustfix
34
fn _simple(x: u32) -> u32 {
45
x
@@ -74,7 +75,7 @@ impl A {
7475
}
7576

7677
fn _method_self(&self, flag: usize, a: usize) -> usize {
77-
//~^ only_used_in_recursion
78+
//~^ self_only_used_in_recursion
7879
//~| only_used_in_recursion
7980

8081
if flag == 0 { 0 } else { self._method_self(flag - 1, a) }

0 commit comments

Comments
 (0)