Skip to content

Commit ed7a82e

Browse files
committed
Auto merge of #7653 - lengyijun:same_name_method_crate, r=llogiq
New lint: `same_name_method` changelog: ``[`same_name_method`]`` fix: #7632 It only compares a method in `impl` with another in `impl trait for` It doesn't lint two methods in two traits. I'm not sure my approach is the best way. I meet difficulty in other approaches.
2 parents f99b4ad + e2cdaec commit ed7a82e

File tree

5 files changed

+340
-0
lines changed

5 files changed

+340
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2906,6 +2906,7 @@ Released 2018-09-13
29062906
[`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges
29072907
[`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
29082908
[`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push
2909+
[`same_name_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_name_method
29092910
[`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
29102911
[`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment
29112912
[`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors

clippy_lints/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ mod reference;
331331
mod regex;
332332
mod repeat_once;
333333
mod returns;
334+
mod same_name_method;
334335
mod self_assignment;
335336
mod self_named_constructors;
336337
mod semicolon_if_nothing_returned;
@@ -910,6 +911,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
910911
repeat_once::REPEAT_ONCE,
911912
returns::LET_AND_RETURN,
912913
returns::NEEDLESS_RETURN,
914+
same_name_method::SAME_NAME_METHOD,
913915
self_assignment::SELF_ASSIGNMENT,
914916
self_named_constructors::SELF_NAMED_CONSTRUCTORS,
915917
semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED,
@@ -1053,6 +1055,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10531055
LintId::of(panic_unimplemented::UNIMPLEMENTED),
10541056
LintId::of(panic_unimplemented::UNREACHABLE),
10551057
LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH),
1058+
LintId::of(same_name_method::SAME_NAME_METHOD),
10561059
LintId::of(shadow::SHADOW_REUSE),
10571060
LintId::of(shadow::SHADOW_SAME),
10581061
LintId::of(strings::STRING_ADD),
@@ -1922,6 +1925,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
19221925
store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv)));
19231926

19241927
store.register_late_pass(|| Box::new(size_of_in_element_count::SizeOfInElementCount));
1928+
store.register_late_pass(|| Box::new(same_name_method::SameNameMethod));
19251929
store.register_late_pass(|| Box::new(map_clone::MapClone));
19261930
store.register_late_pass(|| Box::new(map_err_ignore::MapErrIgnore));
19271931
store.register_late_pass(|| Box::new(shadow::Shadow));

clippy_lints/src/same_name_method.rs

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
use clippy_utils::diagnostics::span_lint_and_then;
2+
use rustc_data_structures::fx::FxHashMap;
3+
use rustc_hir::def::{DefKind, Res};
4+
use rustc_hir::{Crate, Impl, ItemKind, Node, Path, QPath, TraitRef, TyKind};
5+
use rustc_lint::{LateContext, LateLintPass};
6+
use rustc_middle::ty::AssocKind;
7+
use rustc_session::{declare_lint_pass, declare_tool_lint};
8+
use rustc_span::symbol::Symbol;
9+
use rustc_span::Span;
10+
use std::collections::{BTreeMap, BTreeSet};
11+
12+
declare_clippy_lint! {
13+
/// ### What it does
14+
/// It lints if a struct has two method with same time:
15+
/// one from a trait, another not from trait.
16+
///
17+
/// ### Why is this bad?
18+
/// Confusing.
19+
///
20+
/// ### Example
21+
/// ```rust
22+
/// trait T {
23+
/// fn foo(&self) {}
24+
/// }
25+
///
26+
/// struct S;
27+
///
28+
/// impl T for S {
29+
/// fn foo(&self) {}
30+
/// }
31+
///
32+
/// impl S {
33+
/// fn foo(&self) {}
34+
/// }
35+
/// ```
36+
pub SAME_NAME_METHOD,
37+
restriction,
38+
"two method with same name"
39+
}
40+
41+
declare_lint_pass!(SameNameMethod => [SAME_NAME_METHOD]);
42+
43+
struct ExistingName {
44+
impl_methods: BTreeMap<Symbol, Span>,
45+
trait_methods: BTreeMap<Symbol, Vec<Span>>,
46+
}
47+
48+
impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
49+
fn check_crate_post(&mut self, cx: &LateContext<'tcx>, krate: &'tcx Crate<'tcx>) {
50+
let mut map = FxHashMap::<Res, ExistingName>::default();
51+
52+
for item in krate.items() {
53+
if let ItemKind::Impl(Impl {
54+
items,
55+
of_trait,
56+
self_ty,
57+
..
58+
}) = &item.kind
59+
{
60+
if let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind {
61+
if !map.contains_key(res) {
62+
map.insert(
63+
*res,
64+
ExistingName {
65+
impl_methods: BTreeMap::new(),
66+
trait_methods: BTreeMap::new(),
67+
},
68+
);
69+
}
70+
let existing_name = map.get_mut(res).unwrap();
71+
72+
match of_trait {
73+
Some(trait_ref) => {
74+
let mut methods_in_trait: BTreeSet<Symbol> = if_chain! {
75+
if let Some(Node::TraitRef(TraitRef { path, .. })) =
76+
cx.tcx.hir().find(trait_ref.hir_ref_id);
77+
if let Res::Def(DefKind::Trait, did) = path.res;
78+
then{
79+
// FIXME: if
80+
// `rustc_middle::ty::assoc::AssocItems::items` is public,
81+
// we can iterate its keys instead of `in_definition_order`,
82+
// which's more efficient
83+
cx.tcx
84+
.associated_items(did)
85+
.in_definition_order()
86+
.filter(|assoc_item| {
87+
matches!(assoc_item.kind, AssocKind::Fn)
88+
})
89+
.map(|assoc_item| assoc_item.ident.name)
90+
.collect()
91+
}else{
92+
BTreeSet::new()
93+
}
94+
};
95+
96+
let mut check_trait_method = |method_name: Symbol, trait_method_span: Span| {
97+
if let Some(impl_span) = existing_name.impl_methods.get(&method_name) {
98+
span_lint_and_then(
99+
cx,
100+
SAME_NAME_METHOD,
101+
*impl_span,
102+
"method's name is same to an existing method in a trait",
103+
|diag| {
104+
diag.span_note(
105+
trait_method_span,
106+
&format!("existing `{}` defined here", method_name),
107+
);
108+
},
109+
);
110+
}
111+
if let Some(v) = existing_name.trait_methods.get_mut(&method_name) {
112+
v.push(trait_method_span);
113+
} else {
114+
existing_name.trait_methods.insert(method_name, vec![trait_method_span]);
115+
}
116+
};
117+
118+
for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
119+
matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
120+
}) {
121+
let method_name = impl_item_ref.ident.name;
122+
methods_in_trait.remove(&method_name);
123+
check_trait_method(method_name, impl_item_ref.span);
124+
}
125+
126+
for method_name in methods_in_trait {
127+
check_trait_method(method_name, item.span);
128+
}
129+
},
130+
None => {
131+
for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
132+
matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
133+
}) {
134+
let method_name = impl_item_ref.ident.name;
135+
let impl_span = impl_item_ref.span;
136+
if let Some(trait_spans) = existing_name.trait_methods.get(&method_name) {
137+
span_lint_and_then(
138+
cx,
139+
SAME_NAME_METHOD,
140+
impl_span,
141+
"method's name is same to an existing method in a trait",
142+
|diag| {
143+
// TODO should we `span_note` on every trait?
144+
// iterate on trait_spans?
145+
diag.span_note(
146+
trait_spans[0],
147+
&format!("existing `{}` defined here", method_name),
148+
);
149+
},
150+
);
151+
}
152+
existing_name.impl_methods.insert(method_name, impl_span);
153+
}
154+
},
155+
}
156+
}
157+
}
158+
}
159+
}
160+
}

tests/ui/same_name_method.rs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#![warn(clippy::same_name_method)]
2+
#![allow(dead_code, non_camel_case_types)]
3+
4+
trait T1 {
5+
fn foo() {}
6+
}
7+
8+
trait T2 {
9+
fn foo() {}
10+
}
11+
12+
mod should_lint {
13+
14+
mod test_basic_case {
15+
use crate::T1;
16+
17+
struct S;
18+
19+
impl S {
20+
fn foo() {}
21+
}
22+
23+
impl T1 for S {
24+
fn foo() {}
25+
}
26+
}
27+
28+
mod test_derive {
29+
30+
#[derive(Clone)]
31+
struct S;
32+
33+
impl S {
34+
fn clone() {}
35+
}
36+
}
37+
38+
mod with_generic {
39+
use crate::T1;
40+
41+
struct S<U>(U);
42+
43+
impl<U> S<U> {
44+
fn foo() {}
45+
}
46+
47+
impl<U: Copy> T1 for S<U> {
48+
fn foo() {}
49+
}
50+
}
51+
52+
mod default_method {
53+
use crate::T1;
54+
55+
struct S;
56+
57+
impl S {
58+
fn foo() {}
59+
}
60+
61+
impl T1 for S {}
62+
}
63+
64+
mod mulitply_conflicit_trait {
65+
use crate::{T1, T2};
66+
67+
struct S;
68+
69+
impl S {
70+
fn foo() {}
71+
}
72+
73+
impl T1 for S {}
74+
75+
impl T2 for S {}
76+
}
77+
}
78+
79+
mod should_not_lint {
80+
81+
mod not_lint_two_trait_method {
82+
use crate::{T1, T2};
83+
84+
struct S;
85+
86+
impl T1 for S {
87+
fn foo() {}
88+
}
89+
90+
impl T2 for S {
91+
fn foo() {}
92+
}
93+
}
94+
95+
mod only_lint_on_method {
96+
trait T3 {
97+
type foo;
98+
}
99+
100+
struct S;
101+
102+
impl S {
103+
fn foo() {}
104+
}
105+
impl T3 for S {
106+
type foo = usize;
107+
}
108+
}
109+
}
110+
111+
fn main() {}

tests/ui/same_name_method.stderr

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
error: method's name is same to an existing method in a trait
2+
--> $DIR/same_name_method.rs:20:13
3+
|
4+
LL | fn foo() {}
5+
| ^^^^^^^^^^^
6+
|
7+
= note: `-D clippy::same-name-method` implied by `-D warnings`
8+
note: existing `foo` defined here
9+
--> $DIR/same_name_method.rs:24:13
10+
|
11+
LL | fn foo() {}
12+
| ^^^^^^^^^^^
13+
14+
error: method's name is same to an existing method in a trait
15+
--> $DIR/same_name_method.rs:44:13
16+
|
17+
LL | fn foo() {}
18+
| ^^^^^^^^^^^
19+
|
20+
note: existing `foo` defined here
21+
--> $DIR/same_name_method.rs:48:13
22+
|
23+
LL | fn foo() {}
24+
| ^^^^^^^^^^^
25+
26+
error: method's name is same to an existing method in a trait
27+
--> $DIR/same_name_method.rs:58:13
28+
|
29+
LL | fn foo() {}
30+
| ^^^^^^^^^^^
31+
|
32+
note: existing `foo` defined here
33+
--> $DIR/same_name_method.rs:61:9
34+
|
35+
LL | impl T1 for S {}
36+
| ^^^^^^^^^^^^^^^^
37+
38+
error: method's name is same to an existing method in a trait
39+
--> $DIR/same_name_method.rs:70:13
40+
|
41+
LL | fn foo() {}
42+
| ^^^^^^^^^^^
43+
|
44+
note: existing `foo` defined here
45+
--> $DIR/same_name_method.rs:73:9
46+
|
47+
LL | impl T1 for S {}
48+
| ^^^^^^^^^^^^^^^^
49+
50+
error: method's name is same to an existing method in a trait
51+
--> $DIR/same_name_method.rs:34:13
52+
|
53+
LL | fn clone() {}
54+
| ^^^^^^^^^^^^^
55+
|
56+
note: existing `clone` defined here
57+
--> $DIR/same_name_method.rs:30:18
58+
|
59+
LL | #[derive(Clone)]
60+
| ^^^^^
61+
= note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
62+
63+
error: aborting due to 5 previous errors
64+

0 commit comments

Comments
 (0)