Skip to content

Commit 7cfb9a0

Browse files
committed
Auto merge of #11540 - J-ZhengLi:issue11443, r=xFrednet
add new lint that disallow renaming parameters in trait functions fixes: #11443 fixes: #486 changelog: add new lint [`renamed_function_params`] Note that the lint name is not final, because I have a bad reputation in naming things, and I don't trust myself.
2 parents 9b446c7 + 46659ac commit 7cfb9a0

File tree

13 files changed

+413
-3
lines changed

13 files changed

+413
-3
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -5703,6 +5703,7 @@ Released 2018-09-13
57035703
[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
57045704
[`ref_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_patterns
57055705
[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
5706+
[`renamed_function_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#renamed_function_params
57065707
[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once
57075708
[`repeat_vec_with_capacity`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_vec_with_capacity
57085709
[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
@@ -5942,6 +5943,7 @@ Released 2018-09-13
59425943
[`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings
59435944
[`allow-print-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-print-in-tests
59445945
[`allow-private-module-inception`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-private-module-inception
5946+
[`allow-renamed-params-for`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-renamed-params-for
59455947
[`allow-unwrap-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-in-tests
59465948
[`allow-useless-vec-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-useless-vec-in-tests
59475949
[`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles

book/src/lint_configuration.md

+22
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,28 @@ Whether to allow module inception if it's not public.
122122
* [`module_inception`](https://rust-lang.github.io/rust-clippy/master/index.html#module_inception)
123123

124124

125+
## `allow-renamed-params-for`
126+
List of trait paths to ignore when checking renamed function parameters.
127+
128+
#### Example
129+
130+
```toml
131+
allow-renamed-params-for = [ "std::convert::From" ]
132+
```
133+
134+
#### Noteworthy
135+
136+
- By default, the following traits are ignored: `From`, `TryFrom`, `FromStr`
137+
- `".."` can be used as part of the list to indicate that the configured values should be appended to the
138+
default configuration of Clippy. By default, any configuration will replace the default value.
139+
140+
**Default Value:** `["core::convert::From", "core::convert::TryFrom", "core::str::FromStr"]`
141+
142+
---
143+
**Affected lints:**
144+
* [`renamed_function_params`](https://rust-lang.github.io/rust-clippy/master/index.html#renamed_function_params)
145+
146+
125147
## `allow-unwrap-in-tests`
126148
Whether `unwrap` should be allowed in test functions or `#[cfg(test)]`
127149

clippy_config/src/conf.rs

+23
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[
4040
const DEFAULT_DISALLOWED_NAMES: &[&str] = &["foo", "baz", "quux"];
4141
const DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS: &[&str] = &["i", "j", "x", "y", "z", "w", "n"];
4242
const DEFAULT_ALLOWED_PREFIXES: &[&str] = &["to", "as", "into", "from", "try_into", "try_from"];
43+
const DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS: &[&str] =
44+
&["core::convert::From", "core::convert::TryFrom", "core::str::FromStr"];
4345

4446
/// Conf with parse errors
4547
#[derive(Default)]
@@ -613,6 +615,23 @@ define_Conf! {
613615
/// - Use `".."` as part of the list to indicate that the configured values should be appended to the
614616
/// default configuration of Clippy. By default, any configuration will replace the default value
615617
(allowed_prefixes: Vec<String> = DEFAULT_ALLOWED_PREFIXES.iter().map(ToString::to_string).collect()),
618+
/// Lint: RENAMED_FUNCTION_PARAMS.
619+
///
620+
/// List of trait paths to ignore when checking renamed function parameters.
621+
///
622+
/// #### Example
623+
///
624+
/// ```toml
625+
/// allow-renamed-params-for = [ "std::convert::From" ]
626+
/// ```
627+
///
628+
/// #### Noteworthy
629+
///
630+
/// - By default, the following traits are ignored: `From`, `TryFrom`, `FromStr`
631+
/// - `".."` can be used as part of the list to indicate that the configured values should be appended to the
632+
/// default configuration of Clippy. By default, any configuration will replace the default value.
633+
(allow_renamed_params_for: Vec<String> =
634+
DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS.iter().map(ToString::to_string).collect()),
616635
}
617636

618637
/// Search for the configuration file.
@@ -674,6 +693,10 @@ fn deserialize(file: &SourceFile) -> TryConf {
674693
extend_vec_if_indicator_present(&mut conf.conf.doc_valid_idents, DEFAULT_DOC_VALID_IDENTS);
675694
extend_vec_if_indicator_present(&mut conf.conf.disallowed_names, DEFAULT_DISALLOWED_NAMES);
676695
extend_vec_if_indicator_present(&mut conf.conf.allowed_prefixes, DEFAULT_ALLOWED_PREFIXES);
696+
extend_vec_if_indicator_present(
697+
&mut conf.conf.allow_renamed_params_for,
698+
DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS,
699+
);
677700
// TODO: THIS SHOULD BE TESTED, this comment will be gone soon
678701
if conf.conf.allowed_idents_below_min_chars.contains("..") {
679702
conf.conf

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
206206
crate::functions::MUST_USE_CANDIDATE_INFO,
207207
crate::functions::MUST_USE_UNIT_INFO,
208208
crate::functions::NOT_UNSAFE_PTR_ARG_DEREF_INFO,
209+
crate::functions::RENAMED_FUNCTION_PARAMS_INFO,
209210
crate::functions::RESULT_LARGE_ERR_INFO,
210211
crate::functions::RESULT_UNIT_ERR_INFO,
211212
crate::functions::TOO_MANY_ARGUMENTS_INFO,

clippy_lints/src/functions/mod.rs

+56-3
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@ 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;
89

10+
use clippy_utils::def_path_def_ids;
911
use rustc_hir as hir;
1012
use rustc_hir::intravisit;
1113
use rustc_lint::{LateContext, LateLintPass};
1214
use rustc_session::impl_lint_pass;
13-
use rustc_span::def_id::LocalDefId;
15+
use rustc_span::def_id::{DefIdSet, LocalDefId};
1416
use rustc_span::Span;
1517

1618
declare_clippy_lint! {
@@ -359,13 +361,51 @@ declare_clippy_lint! {
359361
"`impl Trait` is used in the function's parameters"
360362
}
361363

362-
#[derive(Copy, Clone)]
363-
#[allow(clippy::struct_field_names)]
364+
declare_clippy_lint! {
365+
/// ### What it does
366+
/// Lints when the name of function parameters from trait impl is
367+
/// different than its default implementation.
368+
///
369+
/// ### Why is this bad?
370+
/// Using the default name for parameters of a trait method is often
371+
/// more desirable for consistency's sake.
372+
///
373+
/// ### Example
374+
/// ```rust
375+
/// struct A(u32);
376+
///
377+
/// impl PartialEq for A {
378+
/// fn eq(&self, b: &Self) -> bool {
379+
/// self.0 == b.0
380+
/// }
381+
/// }
382+
/// ```
383+
/// Use instead:
384+
/// ```rust
385+
/// struct A(u32);
386+
///
387+
/// impl PartialEq for A {
388+
/// fn eq(&self, other: &Self) -> bool {
389+
/// self.0 == other.0
390+
/// }
391+
/// }
392+
/// ```
393+
#[clippy::version = "1.74.0"]
394+
pub RENAMED_FUNCTION_PARAMS,
395+
restriction,
396+
"renamed function parameters in trait implementation"
397+
}
398+
399+
#[derive(Clone)]
364400
pub struct Functions {
365401
too_many_arguments_threshold: u64,
366402
too_many_lines_threshold: u64,
367403
large_error_threshold: u64,
368404
avoid_breaking_exported_api: bool,
405+
allow_renamed_params_for: Vec<String>,
406+
/// A set of resolved `def_id` of traits that are configured to allow
407+
/// function params renaming.
408+
trait_ids: DefIdSet,
369409
}
370410

371411
impl Functions {
@@ -374,12 +414,15 @@ impl Functions {
374414
too_many_lines_threshold: u64,
375415
large_error_threshold: u64,
376416
avoid_breaking_exported_api: bool,
417+
allow_renamed_params_for: Vec<String>,
377418
) -> Self {
378419
Self {
379420
too_many_arguments_threshold,
380421
too_many_lines_threshold,
381422
large_error_threshold,
382423
avoid_breaking_exported_api,
424+
allow_renamed_params_for,
425+
trait_ids: DefIdSet::default(),
383426
}
384427
}
385428
}
@@ -395,6 +438,7 @@ impl_lint_pass!(Functions => [
395438
RESULT_LARGE_ERR,
396439
MISNAMED_GETTERS,
397440
IMPL_TRAIT_IN_PARAMS,
441+
RENAMED_FUNCTION_PARAMS,
398442
]);
399443

400444
impl<'tcx> LateLintPass<'tcx> for Functions {
@@ -424,6 +468,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
424468
must_use::check_impl_item(cx, item);
425469
result::check_impl_item(cx, item, self.large_error_threshold);
426470
impl_trait_in_params::check_impl_item(cx, item);
471+
renamed_function_params::check_impl_item(cx, item, &self.trait_ids);
427472
}
428473

429474
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
@@ -433,4 +478,12 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
433478
result::check_trait_item(cx, item, self.large_error_threshold);
434479
impl_trait_in_params::check_trait_item(cx, item, self.avoid_breaking_exported_api);
435480
}
481+
482+
fn check_crate(&mut self, cx: &LateContext<'tcx>) {
483+
for path in &self.allow_renamed_params_for {
484+
let path_segments: Vec<&str> = path.split("::").collect();
485+
let ids = def_path_def_ids(cx, &path_segments);
486+
self.trait_ids.extend(ids);
487+
}
488+
}
436489
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
use clippy_utils::diagnostics::span_lint_and_then;
2+
use rustc_errors::{Applicability, MultiSpan};
3+
use rustc_hir::def_id::{DefId, DefIdSet};
4+
use rustc_hir::hir_id::OwnerId;
5+
use rustc_hir::{Impl, ImplItem, ImplItemKind, ImplItemRef, ItemKind, Node, TraitRef};
6+
use rustc_lint::LateContext;
7+
use rustc_span::symbol::{kw, Ident, Symbol};
8+
use rustc_span::Span;
9+
10+
use super::RENAMED_FUNCTION_PARAMS;
11+
12+
pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &ImplItem<'_>, ignored_traits: &DefIdSet) {
13+
if !item.span.from_expansion()
14+
&& let ImplItemKind::Fn(_, body_id) = item.kind
15+
&& let parent_node = cx.tcx.parent_hir_node(item.hir_id())
16+
&& let Node::Item(parent_item) = parent_node
17+
&& let ItemKind::Impl(Impl {
18+
items,
19+
of_trait: Some(trait_ref),
20+
..
21+
}) = &parent_item.kind
22+
&& let Some(did) = trait_item_def_id_of_impl(items, item.owner_id)
23+
&& !is_from_ignored_trait(trait_ref, ignored_traits)
24+
{
25+
let mut param_idents_iter = cx.tcx.hir().body_param_names(body_id);
26+
let mut default_param_idents_iter = cx.tcx.fn_arg_names(did).iter().copied();
27+
28+
let renames = RenamedFnArgs::new(&mut default_param_idents_iter, &mut param_idents_iter);
29+
if !renames.0.is_empty() {
30+
let multi_span = renames.multi_span();
31+
let plural = if renames.0.len() == 1 { "" } else { "s" };
32+
span_lint_and_then(
33+
cx,
34+
RENAMED_FUNCTION_PARAMS,
35+
multi_span,
36+
format!("renamed function parameter{plural} of trait impl"),
37+
|diag| {
38+
diag.multipart_suggestion(
39+
format!("consider using the default name{plural}"),
40+
renames.0,
41+
Applicability::Unspecified,
42+
);
43+
},
44+
);
45+
}
46+
}
47+
}
48+
49+
struct RenamedFnArgs(Vec<(Span, String)>);
50+
51+
impl RenamedFnArgs {
52+
/// Comparing between an iterator of default names and one with current names,
53+
/// then collect the ones that got renamed.
54+
fn new<I, T>(default_names: &mut I, current_names: &mut T) -> Self
55+
where
56+
I: Iterator<Item = Ident>,
57+
T: Iterator<Item = Ident>,
58+
{
59+
let mut renamed: Vec<(Span, String)> = vec![];
60+
61+
debug_assert!(default_names.size_hint() == current_names.size_hint());
62+
while let (Some(def_name), Some(cur_name)) = (default_names.next(), current_names.next()) {
63+
let current_name = cur_name.name;
64+
let default_name = def_name.name;
65+
if is_unused_or_empty_symbol(current_name) || is_unused_or_empty_symbol(default_name) {
66+
continue;
67+
}
68+
if current_name != default_name {
69+
renamed.push((cur_name.span, default_name.to_string()));
70+
}
71+
}
72+
73+
Self(renamed)
74+
}
75+
76+
fn multi_span(&self) -> MultiSpan {
77+
self.0
78+
.iter()
79+
.map(|(span, _)| span)
80+
.copied()
81+
.collect::<Vec<Span>>()
82+
.into()
83+
}
84+
}
85+
86+
fn is_unused_or_empty_symbol(symbol: Symbol) -> bool {
87+
// FIXME: `body_param_names` currently returning empty symbols for `wild` as well,
88+
// so we need to check if the symbol is empty first.
89+
// Therefore the check of whether it's equal to [`kw::Underscore`] has no use for now,
90+
// but it would be nice to keep it here just to be future-proof.
91+
symbol.is_empty() || symbol == kw::Underscore || symbol.as_str().starts_with('_')
92+
}
93+
94+
/// Get the [`trait_item_def_id`](ImplItemRef::trait_item_def_id) of a relevant impl item.
95+
fn trait_item_def_id_of_impl(items: &[ImplItemRef], target: OwnerId) -> Option<DefId> {
96+
items.iter().find_map(|item| {
97+
if item.id.owner_id == target {
98+
item.trait_item_def_id
99+
} else {
100+
None
101+
}
102+
})
103+
}
104+
105+
fn is_from_ignored_trait(of_trait: &TraitRef<'_>, ignored_traits: &DefIdSet) -> bool {
106+
let Some(trait_did) = of_trait.trait_def_id() else {
107+
return false;
108+
};
109+
ignored_traits.contains(&trait_did)
110+
}

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
595595
ref allowed_duplicate_crates,
596596
allow_comparison_to_zero,
597597
ref allowed_prefixes,
598+
ref allow_renamed_params_for,
598599

599600
blacklisted_names: _,
600601
cyclomatic_complexity_threshold: _,
@@ -788,6 +789,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
788789
too_many_lines_threshold,
789790
large_error_threshold,
790791
avoid_breaking_exported_api,
792+
allow_renamed_params_for.clone(),
791793
))
792794
});
793795
store.register_late_pass(move |_| Box::new(doc::Documentation::new(doc_valid_idents, check_private_items)));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Ignore `From`, `TryFrom`, `FromStr` by default
2+
# allow-renamed-params-for = []
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Ignore `From`, `TryFrom`, `FromStr` by default
2+
allow-renamed-params-for = [ "..", "std::ops::Add", "renamed_function_params::MyTrait" ]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
error: renamed function parameter of trait impl
2+
--> tests/ui-toml/renamed_function_params/renamed_function_params.rs:30:18
3+
|
4+
LL | fn eq(&self, rhs: &Self) -> bool {
5+
| ^^^ help: consider using the default name: `other`
6+
|
7+
= note: `-D clippy::renamed-function-params` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::renamed_function_params)]`
9+
10+
error: renamed function parameter of trait impl
11+
--> tests/ui-toml/renamed_function_params/renamed_function_params.rs:34:18
12+
|
13+
LL | fn ne(&self, rhs: &Self) -> bool {
14+
| ^^^ help: consider using the default name: `other`
15+
16+
error: renamed function parameter of trait impl
17+
--> tests/ui-toml/renamed_function_params/renamed_function_params.rs:48:19
18+
|
19+
LL | fn foo(&self, i_dont_wanna_use_your_name: u8) {} // only lint in `extend`
20+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the default name: `val`
21+
22+
error: renamed function parameter of trait impl
23+
--> tests/ui-toml/renamed_function_params/renamed_function_params.rs:55:31
24+
|
25+
LL | fn hash<H: Hasher>(&self, states: &mut H) {
26+
| ^^^^^^ help: consider using the default name: `state`
27+
28+
error: renamed function parameters of trait impl
29+
--> tests/ui-toml/renamed_function_params/renamed_function_params.rs:59:30
30+
|
31+
LL | fn hash_slice<H: Hasher>(date: &[Self], states: &mut H) {
32+
| ^^^^ ^^^^^^
33+
|
34+
help: consider using the default names
35+
|
36+
LL | fn hash_slice<H: Hasher>(data: &[Self], state: &mut H) {
37+
| ~~~~ ~~~~~
38+
39+
error: renamed function parameter of trait impl
40+
--> tests/ui-toml/renamed_function_params/renamed_function_params.rs:80:18
41+
|
42+
LL | fn add(self, b: B) -> C {
43+
| ^ help: consider using the default name: `rhs`
44+
45+
error: aborting due to 6 previous errors
46+

0 commit comments

Comments
 (0)