Skip to content

Commit 28d5115

Browse files
committed
add new lint that disallow renaming parameters in trait functions
1 parent befb659 commit 28d5115

File tree

6 files changed

+246
-0
lines changed

6 files changed

+246
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5702,6 +5702,7 @@ Released 2018-09-13
57025702
[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
57035703
[`ref_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_patterns
57045704
[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
5705+
[`renamed_function_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#renamed_function_params
57055706
[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once
57065707
[`repeat_vec_with_capacity`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_vec_with_capacity
57075708
[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
205205
crate::functions::MUST_USE_CANDIDATE_INFO,
206206
crate::functions::MUST_USE_UNIT_INFO,
207207
crate::functions::NOT_UNSAFE_PTR_ARG_DEREF_INFO,
208+
crate::functions::RENAMED_FUNCTION_PARAMS_INFO,
208209
crate::functions::RESULT_LARGE_ERR_INFO,
209210
crate::functions::RESULT_UNIT_ERR_INFO,
210211
crate::functions::TOO_MANY_ARGUMENTS_INFO,

clippy_lints/src/functions/mod.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ mod impl_trait_in_params;
22
mod misnamed_getters;
33
mod must_use;
44
mod not_unsafe_ptr_arg_deref;
5+
mod renamed_function_params;
56
mod result;
67
mod too_many_arguments;
78
mod too_many_lines;
@@ -359,6 +360,37 @@ declare_clippy_lint! {
359360
"`impl Trait` is used in the function's parameters"
360361
}
361362

363+
declare_clippy_lint! {
364+
/// ### What it does
365+
/// Lints when the name of function parameters from trait impl is
366+
/// different than its default implementation.
367+
///
368+
/// ### Why is this bad?
369+
/// Using the default name for parameters of a trait method is often
370+
/// more desirable for consistency's sake.
371+
///
372+
/// ### Example
373+
/// ```rust
374+
/// impl From<A> for String {
375+
/// fn from(a: A) -> Self {
376+
/// a.0.to_string()
377+
/// }
378+
/// }
379+
/// ```
380+
/// Use instead:
381+
/// ```rust
382+
/// impl From<A> for String {
383+
/// fn from(value: A) -> Self {
384+
/// value.0.to_string()
385+
/// }
386+
/// }
387+
/// ```
388+
#[clippy::version = "1.74.0"]
389+
pub RENAMED_FUNCTION_PARAMS,
390+
restriction,
391+
"renamed function parameters in trait implementation"
392+
}
393+
362394
#[derive(Copy, Clone)]
363395
#[allow(clippy::struct_field_names)]
364396
pub struct Functions {
@@ -395,6 +427,7 @@ impl_lint_pass!(Functions => [
395427
RESULT_LARGE_ERR,
396428
MISNAMED_GETTERS,
397429
IMPL_TRAIT_IN_PARAMS,
430+
RENAMED_FUNCTION_PARAMS,
398431
]);
399432

400433
impl<'tcx> LateLintPass<'tcx> for Functions {
@@ -424,6 +457,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
424457
must_use::check_impl_item(cx, item);
425458
result::check_impl_item(cx, item, self.large_error_threshold);
426459
impl_trait_in_params::check_impl_item(cx, item);
460+
renamed_function_params::check_impl_item(cx, item);
427461
}
428462

429463
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use clippy_utils::diagnostics::span_lint_and_help;
2+
use rustc_hir::def_id::DefId;
3+
use rustc_hir::hir_id::OwnerId;
4+
use rustc_hir::{ImplItem, ImplItemKind, ItemKind, Node};
5+
use rustc_lint::LateContext;
6+
use rustc_span::symbol::{Ident, Symbol};
7+
use rustc_span::Span;
8+
9+
use super::RENAMED_FUNCTION_PARAMS;
10+
11+
pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &ImplItem<'_>) {
12+
if let ImplItemKind::Fn(_, body_id) = item.kind &&
13+
let Some(did) = impled_item_def_id(cx, item.owner_id)
14+
{
15+
let mut param_idents_iter = cx.tcx.hir().body_param_names(body_id);
16+
let mut default_param_idents_iter = cx.tcx.fn_arg_names(did).iter().copied();
17+
18+
let renames = renamed_params(&mut default_param_idents_iter, &mut param_idents_iter);
19+
// FIXME: Should we use `MultiSpan` to combine output together?
20+
// But how should we display help message if so.
21+
for rename in renames {
22+
span_lint_and_help(
23+
cx,
24+
RENAMED_FUNCTION_PARAMS,
25+
rename.renamed_span,
26+
"function parameter name was renamed from its trait default",
27+
None,
28+
&format!("consider changing the name to: '{}'", rename.default_name.as_str())
29+
);
30+
}
31+
}
32+
}
33+
34+
struct RenamedParam {
35+
renamed_span: Span,
36+
default_name: Symbol,
37+
}
38+
39+
fn renamed_params<I, T>(default_names: &mut I, current_names: &mut T) -> Vec<RenamedParam>
40+
where
41+
I: Iterator<Item = Ident>,
42+
T: Iterator<Item = Ident>,
43+
{
44+
let mut renamed = vec![];
45+
// FIXME: Should we stop if they have different length?
46+
while let (Some(def_name), Some(cur_name)) = (default_names.next(), current_names.next()) {
47+
let current_name = cur_name.name;
48+
let default_name = def_name.name;
49+
if is_ignored_or_empty_symbol(current_name) || is_ignored_or_empty_symbol(default_name) {
50+
continue;
51+
}
52+
if current_name != default_name {
53+
renamed.push(RenamedParam {
54+
renamed_span: cur_name.span,
55+
default_name,
56+
});
57+
}
58+
}
59+
renamed
60+
}
61+
62+
fn is_ignored_or_empty_symbol(symbol: Symbol) -> bool {
63+
let s = symbol.as_str();
64+
s.is_empty() || s.starts_with('_')
65+
}
66+
67+
fn impled_item_def_id(cx: &LateContext<'_>, impl_item_id: OwnerId) -> Option<DefId> {
68+
let trait_node = cx.tcx.hir().find_parent(impl_item_id.into())?;
69+
if let Node::Item(item) = trait_node &&
70+
let ItemKind::Impl(impl_) = &item.kind
71+
{
72+
impl_.items.iter().find_map(|item| {
73+
if item.id.owner_id == impl_item_id {
74+
item.trait_item_def_id
75+
} else {
76+
None
77+
}
78+
})
79+
} else {
80+
None
81+
}
82+
}

tests/ui/renamed_function_params.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#![warn(clippy::renamed_function_params)]
2+
#![allow(clippy::partialeq_ne_impl)]
3+
#![allow(unused)]
4+
5+
use std::hash::{Hash, Hasher};
6+
7+
struct A;
8+
impl From<A> for String {
9+
fn from(_value: A) -> Self {
10+
String::new()
11+
}
12+
}
13+
impl ToString for A {
14+
fn to_string(&self) -> String {
15+
String::new()
16+
}
17+
}
18+
19+
struct B(u32);
20+
impl From<B> for String {
21+
fn from(b: B) -> Self {
22+
//~^ ERROR: function parameter name was renamed from its trait default
23+
b.0.to_string()
24+
}
25+
}
26+
impl PartialEq for B {
27+
fn eq(&self, rhs: &Self) -> bool {
28+
//~^ ERROR: function parameter name was renamed from its trait default
29+
self.0 == rhs.0
30+
}
31+
fn ne(&self, rhs: &Self) -> bool {
32+
//~^ ERROR: function parameter name was renamed from its trait default
33+
self.0 != rhs.0
34+
}
35+
}
36+
37+
trait MyTrait {
38+
fn foo(&self, val: u8);
39+
fn bar(a: u8, b: u8);
40+
fn baz(self, _val: u8);
41+
}
42+
43+
impl MyTrait for B {
44+
fn foo(&self, i_dont_wanna_use_your_name: u8) {}
45+
//~^ ERROR: function parameter name was renamed from its trait default
46+
fn bar(_a: u8, _b: u8) {}
47+
fn baz(self, val: u8) {}
48+
}
49+
50+
impl Hash for B {
51+
fn hash<H: Hasher>(&self, states: &mut H) {
52+
//~^ ERROR: function parameter name was renamed from its trait default
53+
self.0.hash(states);
54+
}
55+
fn hash_slice<H: Hasher>(date: &[Self], states: &mut H) {
56+
//~^ ERROR: function parameter name was renamed from its trait default
57+
for d in date {
58+
d.hash(states);
59+
}
60+
}
61+
}
62+
63+
impl B {
64+
fn totally_irrelevant(&self, right: bool) {}
65+
fn some_fn(&self, other: impl MyTrait) {}
66+
}
67+
68+
fn main() {}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
error: function parameter name was renamed from its trait default
2+
--> $DIR/renamed_function_params.rs:21:13
3+
|
4+
LL | fn from(b: B) -> Self {
5+
| ^
6+
|
7+
= help: consider changing the name to: 'value'
8+
= note: `-D clippy::renamed-function-params` implied by `-D warnings`
9+
= help: to override `-D warnings` add `#[allow(clippy::renamed_function_params)]`
10+
11+
error: function parameter name was renamed from its trait default
12+
--> $DIR/renamed_function_params.rs:27:18
13+
|
14+
LL | fn eq(&self, rhs: &Self) -> bool {
15+
| ^^^
16+
|
17+
= help: consider changing the name to: 'other'
18+
19+
error: function parameter name was renamed from its trait default
20+
--> $DIR/renamed_function_params.rs:31:18
21+
|
22+
LL | fn ne(&self, rhs: &Self) -> bool {
23+
| ^^^
24+
|
25+
= help: consider changing the name to: 'other'
26+
27+
error: function parameter name was renamed from its trait default
28+
--> $DIR/renamed_function_params.rs:44:19
29+
|
30+
LL | fn foo(&self, i_dont_wanna_use_your_name: u8) {}
31+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
32+
|
33+
= help: consider changing the name to: 'val'
34+
35+
error: function parameter name was renamed from its trait default
36+
--> $DIR/renamed_function_params.rs:51:31
37+
|
38+
LL | fn hash<H: Hasher>(&self, states: &mut H) {
39+
| ^^^^^^
40+
|
41+
= help: consider changing the name to: 'state'
42+
43+
error: function parameter name was renamed from its trait default
44+
--> $DIR/renamed_function_params.rs:55:30
45+
|
46+
LL | fn hash_slice<H: Hasher>(date: &[Self], states: &mut H) {
47+
| ^^^^
48+
|
49+
= help: consider changing the name to: 'data'
50+
51+
error: function parameter name was renamed from its trait default
52+
--> $DIR/renamed_function_params.rs:55:45
53+
|
54+
LL | fn hash_slice<H: Hasher>(date: &[Self], states: &mut H) {
55+
| ^^^^^^
56+
|
57+
= help: consider changing the name to: 'state'
58+
59+
error: aborting due to 7 previous errors
60+

0 commit comments

Comments
 (0)