Skip to content

Commit 63a5508

Browse files
committed
Add clippy::self_only_used_in_recursion lint
and use it instead of clippy::only_used_in_recursion when the parameter in question is self.
1 parent 4a8b7ea commit 63a5508

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
@@ -6597,6 +6597,7 @@ Released 2018-09-13
65976597
[`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment
65986598
[`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors
65996599
[`self_named_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_module_files
6600+
[`self_only_used_in_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_only_used_in_recursion
66006601
[`semicolon_if_nothing_returned`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned
66016602
[`semicolon_inside_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_inside_block
66026603
[`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
@@ -575,6 +575,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
575575
crate::nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES_INFO,
576576
crate::octal_escapes::OCTAL_ESCAPES_INFO,
577577
crate::only_used_in_recursion::ONLY_USED_IN_RECURSION_INFO,
578+
crate::only_used_in_recursion::SELF_ONLY_USED_IN_RECURSION_INFO,
578579
crate::operators::ABSURD_EXTREME_COMPARISONS_INFO,
579580
crate::operators::ARITHMETIC_SIDE_EFFECTS_INFO,
580581
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 {
@@ -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();

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)