Skip to content

Commit 77e4d7a

Browse files
committed
Auto merge of #10727 - john-h-k:lint/dup-auto-traits, r=Manishearth
Extend `trait_duplication_in_bounds` to cover trait objects This PR extends `trait_duplication_in_bounds` to cover trait objects. Currently, ```rs fn foo(_a: &(dyn Any + Send + Send)) {} ``` generates no warnings. With this PR, it will complain about a duplicate trait and can remove it Moved from rust-lang/rust#110991 changelog: [`trait_duplication_in_bounds`]: warn on duplicate trait object constraints
2 parents b4075e8 + b169bdb commit 77e4d7a

File tree

4 files changed

+92
-11
lines changed

4 files changed

+92
-11
lines changed

Diff for: clippy_lints/src/trait_bounds.rs

+57-2
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@ declare_clippy_lint! {
3737
#[clippy::version = "1.38.0"]
3838
pub TYPE_REPETITION_IN_BOUNDS,
3939
nursery,
40-
"types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
40+
"types are repeated unnecessarily in trait bounds, use `+` instead of using `T: _, T: _`"
4141
}
4242

4343
declare_clippy_lint! {
4444
/// ### What it does
45-
/// Checks for cases where generics are being used and multiple
45+
/// Checks for cases where generics or trait objects are being used and multiple
4646
/// syntax specifications for trait bounds are used simultaneously.
4747
///
4848
/// ### Why is this bad?
@@ -167,6 +167,61 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
167167
}
168168
}
169169
}
170+
171+
fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
172+
if_chain! {
173+
if let TyKind::Ref(.., mut_ty) = &ty.kind;
174+
if let TyKind::TraitObject(bounds, ..) = mut_ty.ty.kind;
175+
if bounds.len() > 2;
176+
then {
177+
178+
// Build up a hash of every trait we've seen
179+
// When we see a trait for the first time, add it to unique_traits
180+
// so we can later use it to build a string of all traits exactly once, without duplicates
181+
182+
let mut seen_def_ids = FxHashSet::default();
183+
let mut unique_traits = Vec::new();
184+
185+
// Iterate the bounds and add them to our seen hash
186+
// If we haven't yet seen it, add it to the fixed traits
187+
for bound in bounds.iter() {
188+
let Some(def_id) = bound.trait_ref.trait_def_id() else { continue; };
189+
190+
let new_trait = seen_def_ids.insert(def_id);
191+
192+
if new_trait {
193+
unique_traits.push(bound);
194+
}
195+
}
196+
197+
// If the number of unique traits isn't the same as the number of traits in the bounds,
198+
// there must be 1 or more duplicates
199+
if bounds.len() != unique_traits.len() {
200+
let mut bounds_span = bounds[0].span;
201+
202+
for bound in bounds.iter().skip(1) {
203+
bounds_span = bounds_span.to(bound.span);
204+
}
205+
206+
let fixed_trait_snippet = unique_traits
207+
.iter()
208+
.filter_map(|b| snippet_opt(cx, b.span))
209+
.collect::<Vec<_>>()
210+
.join(" + ");
211+
212+
span_lint_and_sugg(
213+
cx,
214+
TRAIT_DUPLICATION_IN_BOUNDS,
215+
bounds_span,
216+
"this trait bound is already specified in trait declaration",
217+
"try",
218+
fixed_trait_snippet,
219+
Applicability::MaybeIncorrect,
220+
);
221+
}
222+
}
223+
}
224+
}
170225
}
171226

172227
impl TraitBounds {

Diff for: tests/ui/trait_duplication_in_bounds.fixed

+10
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
#![deny(clippy::trait_duplication_in_bounds)]
33
#![allow(unused)]
44

5+
use std::any::Any;
6+
57
fn bad_foo<T: Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
68
unimplemented!();
79
}
@@ -109,4 +111,12 @@ fn qualified_path<T: std::clone::Clone + foo::Clone>(arg0: T) {
109111
unimplemented!();
110112
}
111113

114+
fn good_trait_object(arg0: &(dyn Any + Send)) {
115+
unimplemented!();
116+
}
117+
118+
fn bad_trait_object(arg0: &(dyn Any + Send)) {
119+
unimplemented!();
120+
}
121+
112122
fn main() {}

Diff for: tests/ui/trait_duplication_in_bounds.rs

+10
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
#![deny(clippy::trait_duplication_in_bounds)]
33
#![allow(unused)]
44

5+
use std::any::Any;
6+
57
fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
68
unimplemented!();
79
}
@@ -109,4 +111,12 @@ fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
109111
unimplemented!();
110112
}
111113

114+
fn good_trait_object(arg0: &(dyn Any + Send)) {
115+
unimplemented!();
116+
}
117+
118+
fn bad_trait_object(arg0: &(dyn Any + Send + Send)) {
119+
unimplemented!();
120+
}
121+
112122
fn main() {}

Diff for: tests/ui/trait_duplication_in_bounds.stderr

+15-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: these bounds contain repeated elements
2-
--> $DIR/trait_duplication_in_bounds.rs:5:15
2+
--> $DIR/trait_duplication_in_bounds.rs:7:15
33
|
44
LL | fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
@@ -11,46 +11,52 @@ LL | #![deny(clippy::trait_duplication_in_bounds)]
1111
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1212

1313
error: these where clauses contain repeated elements
14-
--> $DIR/trait_duplication_in_bounds.rs:11:8
14+
--> $DIR/trait_duplication_in_bounds.rs:13:8
1515
|
1616
LL | T: Clone + Clone + Clone + Copy,
1717
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
1818

1919
error: these bounds contain repeated elements
20-
--> $DIR/trait_duplication_in_bounds.rs:39:26
20+
--> $DIR/trait_duplication_in_bounds.rs:41:26
2121
|
2222
LL | trait BadSelfTraitBound: Clone + Clone + Clone {
2323
| ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
2424

2525
error: these where clauses contain repeated elements
26-
--> $DIR/trait_duplication_in_bounds.rs:46:15
26+
--> $DIR/trait_duplication_in_bounds.rs:48:15
2727
|
2828
LL | Self: Clone + Clone + Clone;
2929
| ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
3030

3131
error: these bounds contain repeated elements
32-
--> $DIR/trait_duplication_in_bounds.rs:60:24
32+
--> $DIR/trait_duplication_in_bounds.rs:62:24
3333
|
3434
LL | trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
3535
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
3636

3737
error: these where clauses contain repeated elements
38-
--> $DIR/trait_duplication_in_bounds.rs:67:12
38+
--> $DIR/trait_duplication_in_bounds.rs:69:12
3939
|
4040
LL | T: Clone + Clone + Clone + Copy,
4141
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
4242

4343
error: these bounds contain repeated elements
44-
--> $DIR/trait_duplication_in_bounds.rs:100:19
44+
--> $DIR/trait_duplication_in_bounds.rs:102:19
4545
|
4646
LL | fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
4747
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `GenericTrait<u64> + GenericTrait<u32>`
4848

4949
error: these bounds contain repeated elements
50-
--> $DIR/trait_duplication_in_bounds.rs:108:22
50+
--> $DIR/trait_duplication_in_bounds.rs:110:22
5151
|
5252
LL | fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
5353
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::clone::Clone + foo::Clone`
5454

55-
error: aborting due to 8 previous errors
55+
error: this trait bound is already specified in trait declaration
56+
--> $DIR/trait_duplication_in_bounds.rs:118:33
57+
|
58+
LL | fn bad_trait_object(arg0: &(dyn Any + Send + Send)) {
59+
| ^^^^^^^^^^^^^^^^^ help: try: `Any + Send`
60+
61+
error: aborting due to 9 previous errors
5662

0 commit comments

Comments
 (0)