Skip to content

Commit cb2e6f5

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 cb2e6f5

File tree

5 files changed

+146
-50
lines changed

5 files changed

+146
-50
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: 106 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -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)]
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)