Skip to content

Commit 6c83a47

Browse files
authored
Fix multiple_inherent_impl false negatives for generic impl blocks (#16284)
lint now correctly detects multiple impl blocks for generic types with identical bounds. Fixes: #16283 changelog: [`multiple_inherent_impl`]: fix false negatives for generic impl blocks
2 parents 4882141 + ec91742 commit 6c83a47

File tree

3 files changed

+138
-17
lines changed

3 files changed

+138
-17
lines changed

clippy_lints/src/inherent_impl.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,21 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl {
101101
InherentImplLintScope::Crate => Criterion::Crate,
102102
};
103103
let is_test = is_cfg_test(cx.tcx, hir_id) || is_in_cfg_test(cx.tcx, hir_id);
104-
match type_map.entry((impl_ty, criterion, is_test)) {
104+
let predicates = {
105+
// Gets the predicates (bounds) for the given impl block,
106+
// sorted for consistent comparison to allow distinguishing between impl blocks
107+
// with different generic bounds.
108+
let mut predicates = cx
109+
.tcx
110+
.predicates_of(impl_id)
111+
.predicates
112+
.iter()
113+
.map(|(clause, _)| *clause)
114+
.collect::<Vec<_>>();
115+
predicates.sort_by_key(|c| format!("{c:?}"));
116+
predicates
117+
};
118+
match type_map.entry((impl_ty, predicates, criterion, is_test)) {
105119
Entry::Vacant(e) => {
106120
// Store the id for the first impl block of this type. The span is retrieved lazily.
107121
e.insert(IdOrSpan::Id(impl_id));
@@ -152,15 +166,12 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl {
152166
fn get_impl_span(cx: &LateContext<'_>, id: LocalDefId) -> Option<Span> {
153167
let id = cx.tcx.local_def_id_to_hir_id(id);
154168
if let Node::Item(&Item {
155-
kind: ItemKind::Impl(impl_item),
169+
kind: ItemKind::Impl(_),
156170
span,
157171
..
158172
}) = cx.tcx.hir_node(id)
159173
{
160-
(!span.from_expansion()
161-
&& impl_item.generics.params.is_empty()
162-
&& !fulfill_or_allowed(cx, MULTIPLE_INHERENT_IMPL, [id]))
163-
.then_some(span)
174+
(!span.from_expansion() && !fulfill_or_allowed(cx, MULTIPLE_INHERENT_IMPL, [id])).then_some(span)
164175
} else {
165176
None
166177
}

tests/ui/impl.rs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ impl MyStruct {
1414
}
1515

1616
impl<'a> MyStruct {
17+
//~^ multiple_inherent_impl
1718
fn lifetimed() {}
1819
}
1920

@@ -90,10 +91,12 @@ struct Lifetime<'s> {
9091
}
9192

9293
impl Lifetime<'_> {}
93-
impl Lifetime<'_> {} // false negative
94+
impl Lifetime<'_> {}
95+
//~^ multiple_inherent_impl
9496

9597
impl<'a> Lifetime<'a> {}
96-
impl<'a> Lifetime<'a> {} // false negative
98+
impl<'a> Lifetime<'a> {}
99+
//~^ multiple_inherent_impl
97100

98101
impl<'b> Lifetime<'b> {} // false negative?
99102

@@ -104,6 +107,39 @@ struct Generic<G> {
104107
}
105108

106109
impl<G> Generic<G> {}
107-
impl<G> Generic<G> {} // false negative
110+
impl<G> Generic<G> {}
111+
//~^ multiple_inherent_impl
112+
113+
use std::fmt::Debug;
114+
115+
#[derive(Debug)]
116+
struct GenericWithBounds<T: Debug>(T);
117+
118+
impl<T: Debug> GenericWithBounds<T> {
119+
fn make_one(_one: T) -> Self {
120+
todo!()
121+
}
122+
}
123+
124+
impl<T: Debug> GenericWithBounds<T> {
125+
//~^ multiple_inherent_impl
126+
fn make_two(_two: T) -> Self {
127+
todo!()
128+
}
129+
}
130+
131+
struct MultipleTraitBounds<T>(T);
132+
133+
impl<T: Debug> MultipleTraitBounds<T> {
134+
fn debug_fn() {}
135+
}
136+
137+
impl<T: Clone> MultipleTraitBounds<T> {
138+
fn clone_fn() {}
139+
}
140+
141+
impl<T: Debug + Clone> MultipleTraitBounds<T> {
142+
fn debug_clone_fn() {}
143+
}
108144

109145
fn main() {}

tests/ui/impl.stderr

Lines changed: 82 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,24 @@ LL | | }
1919
= help: to override `-D warnings` add `#[allow(clippy::multiple_inherent_impl)]`
2020

2121
error: multiple implementations of this structure
22-
--> tests/ui/impl.rs:26:5
22+
--> tests/ui/impl.rs:16:1
23+
|
24+
LL | / impl<'a> MyStruct {
25+
LL | |
26+
LL | | fn lifetimed() {}
27+
LL | | }
28+
| |_^
29+
|
30+
note: first implementation here
31+
--> tests/ui/impl.rs:6:1
32+
|
33+
LL | / impl MyStruct {
34+
LL | | fn first() {}
35+
LL | | }
36+
| |_^
37+
38+
error: multiple implementations of this structure
39+
--> tests/ui/impl.rs:27:5
2340
|
2441
LL | / impl super::MyStruct {
2542
LL | |
@@ -37,7 +54,7 @@ LL | | }
3754
| |_^
3855

3956
error: multiple implementations of this structure
40-
--> tests/ui/impl.rs:48:1
57+
--> tests/ui/impl.rs:49:1
4158
|
4259
LL | / impl WithArgs<u64> {
4360
LL | |
@@ -47,36 +64,93 @@ LL | | }
4764
| |_^
4865
|
4966
note: first implementation here
50-
--> tests/ui/impl.rs:45:1
67+
--> tests/ui/impl.rs:46:1
5168
|
5269
LL | / impl WithArgs<u64> {
5370
LL | | fn f2() {}
5471
LL | | }
5572
| |_^
5673

5774
error: multiple implementations of this structure
58-
--> tests/ui/impl.rs:71:1
75+
--> tests/ui/impl.rs:72:1
5976
|
6077
LL | impl OneAllowedImpl {}
6178
| ^^^^^^^^^^^^^^^^^^^^^^
6279
|
6380
note: first implementation here
64-
--> tests/ui/impl.rs:68:1
81+
--> tests/ui/impl.rs:69:1
6582
|
6683
LL | impl OneAllowedImpl {}
6784
| ^^^^^^^^^^^^^^^^^^^^^^
6885

6986
error: multiple implementations of this structure
70-
--> tests/ui/impl.rs:84:1
87+
--> tests/ui/impl.rs:85:1
7188
|
7289
LL | impl OneExpected {}
7390
| ^^^^^^^^^^^^^^^^^^^
7491
|
7592
note: first implementation here
76-
--> tests/ui/impl.rs:81:1
93+
--> tests/ui/impl.rs:82:1
7794
|
7895
LL | impl OneExpected {}
7996
| ^^^^^^^^^^^^^^^^^^^
8097

81-
error: aborting due to 5 previous errors
98+
error: multiple implementations of this structure
99+
--> tests/ui/impl.rs:94:1
100+
|
101+
LL | impl Lifetime<'_> {}
102+
| ^^^^^^^^^^^^^^^^^^^^
103+
|
104+
note: first implementation here
105+
--> tests/ui/impl.rs:93:1
106+
|
107+
LL | impl Lifetime<'_> {}
108+
| ^^^^^^^^^^^^^^^^^^^^
109+
110+
error: multiple implementations of this structure
111+
--> tests/ui/impl.rs:98:1
112+
|
113+
LL | impl<'a> Lifetime<'a> {}
114+
| ^^^^^^^^^^^^^^^^^^^^^^^^
115+
|
116+
note: first implementation here
117+
--> tests/ui/impl.rs:97:1
118+
|
119+
LL | impl<'a> Lifetime<'a> {}
120+
| ^^^^^^^^^^^^^^^^^^^^^^^^
121+
122+
error: multiple implementations of this structure
123+
--> tests/ui/impl.rs:110:1
124+
|
125+
LL | impl<G> Generic<G> {}
126+
| ^^^^^^^^^^^^^^^^^^^^^
127+
|
128+
note: first implementation here
129+
--> tests/ui/impl.rs:109:1
130+
|
131+
LL | impl<G> Generic<G> {}
132+
| ^^^^^^^^^^^^^^^^^^^^^
133+
134+
error: multiple implementations of this structure
135+
--> tests/ui/impl.rs:124:1
136+
|
137+
LL | / impl<T: Debug> GenericWithBounds<T> {
138+
LL | |
139+
LL | | fn make_two(_two: T) -> Self {
140+
LL | | todo!()
141+
LL | | }
142+
LL | | }
143+
| |_^
144+
|
145+
note: first implementation here
146+
--> tests/ui/impl.rs:118:1
147+
|
148+
LL | / impl<T: Debug> GenericWithBounds<T> {
149+
LL | | fn make_one(_one: T) -> Self {
150+
LL | | todo!()
151+
LL | | }
152+
LL | | }
153+
| |_^
154+
155+
error: aborting due to 10 previous errors
82156

0 commit comments

Comments
 (0)