Skip to content

Commit 9bc2526

Browse files
committed
trait_duplication_in_bounds: warn on duplicate trait bounds of the same syntax
1 parent c90f42a commit 9bc2526

File tree

5 files changed

+168
-64
lines changed

5 files changed

+168
-64
lines changed

compiler/rustc_lint/src/builtin.rs

Lines changed: 65 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ use rustc_trait_selection::traits::misc::can_type_implement_copy;
5757
use crate::nonstandard_style::{method_context, MethodLateContext};
5858

5959
use std::fmt::Write;
60+
use std::hash::{Hash, Hasher};
6061
use tracing::{debug, trace};
6162

6263
// hardwired lints from librustc_middle
@@ -3174,61 +3175,82 @@ declare_lint_pass!(TraitDuplicationInBounds => [TRAIT_DUPLICATION_IN_BOUNDS]);
31743175

31753176
impl<'tcx> LateLintPass<'tcx> for TraitDuplicationInBounds {
31763177
fn check_generics(&mut self, cx: &LateContext<'tcx>, gen: &'tcx hir::Generics<'_>) {
3177-
fn get_trait_res_span_from_bound(bound: &hir::GenericBound<'_>) -> Option<(Res, Span)> {
3178-
if let hir::GenericBound::Trait(t, _) = bound {
3179-
Some((t.trait_ref.path.res, t.span))
3180-
} else {
3181-
None
3178+
struct TraitRes {
3179+
res: Res,
3180+
span: Span,
3181+
}
3182+
3183+
impl TraitRes {
3184+
fn from_bound(bound: &hir::GenericBound<'_>) -> Option<Self> {
3185+
if let hir::GenericBound::Trait(t, _) = bound {
3186+
Some(Self { res: t.trait_ref.path.res, span: t.span })
3187+
} else {
3188+
None
3189+
}
31823190
}
31833191
}
3184-
if gen.span.from_expansion()
3185-
|| gen.params.is_empty()
3186-
|| gen.where_clause.predicates.is_empty()
3187-
{
3192+
3193+
impl PartialEq for TraitRes {
3194+
fn eq(&self, other: &Self) -> bool {
3195+
self.res == other.res
3196+
}
3197+
}
3198+
3199+
impl Hash for TraitRes {
3200+
fn hash<H: Hasher>(&self, state: &mut H) {
3201+
self.res.hash(state)
3202+
}
3203+
}
3204+
3205+
impl Eq for TraitRes {}
3206+
3207+
if gen.span.from_expansion() {
31883208
return;
31893209
}
31903210

3191-
let mut map = FxHashMap::default();
3211+
let mut trait_resolutions = FxHashMap::default();
31923212
for param in gen.params {
31933213
if let hir::ParamName::Plain(ref ident) = param.name {
3194-
let res = param
3195-
.bounds
3196-
.iter()
3197-
.filter_map(get_trait_res_span_from_bound)
3198-
.collect::<Vec<_>>();
3199-
map.insert(*ident, res);
3214+
let mut uniq = FxHashSet::default();
3215+
3216+
for res in param.bounds.iter().filter_map(TraitRes::from_bound) {
3217+
let span = res.span.clone();
3218+
if !uniq.insert(res) {
3219+
cx.struct_span_lint(TRAIT_DUPLICATION_IN_BOUNDS, span, |lint| {
3220+
lint.build("this trait bound has already been specified")
3221+
.help("consider removing this trait bound")
3222+
.emit()
3223+
});
3224+
}
3225+
}
3226+
3227+
trait_resolutions.insert(*ident, uniq);
32003228
}
32013229
}
32023230

32033231
for predicate in gen.where_clause.predicates {
32043232
if let hir::WherePredicate::BoundPredicate(ref bound_predicate) = predicate {
3205-
if !bound_predicate.span.from_expansion() {
3206-
if let hir::TyKind::Path(hir::QPath::Resolved(_, hir::Path { segments, .. })) =
3207-
bound_predicate.bounded_ty.kind
3208-
{
3209-
if let Some(segment) = segments.first() {
3210-
if let Some(trait_resolutions_direct) = map.get(&segment.ident) {
3211-
for (res_where, _) in bound_predicate
3212-
.bounds
3213-
.iter()
3214-
.filter_map(get_trait_res_span_from_bound)
3215-
{
3216-
if let Some((_, span_direct)) = trait_resolutions_direct
3217-
.iter()
3218-
.find(|(res_direct, _)| *res_direct == res_where)
3219-
{
3220-
cx.struct_span_lint(
3221-
TRAIT_DUPLICATION_IN_BOUNDS,
3222-
*span_direct,
3223-
|lint| {
3224-
lint.build(
3225-
"this trait bound is already specified in the where clause"
3226-
)
3227-
.help("consider removing this trait bound")
3228-
.emit()
3229-
},
3230-
);
3231-
}
3233+
if let hir::TyKind::Path(hir::QPath::Resolved(_, hir::Path { segments, .. })) =
3234+
bound_predicate.bounded_ty.kind
3235+
{
3236+
if let Some(segment) = segments.first() {
3237+
if let Some(trait_resolutions) = trait_resolutions.get_mut(&segment.ident) {
3238+
for res in
3239+
bound_predicate.bounds.iter().filter_map(TraitRes::from_bound)
3240+
{
3241+
let span = res.span.clone();
3242+
if !trait_resolutions.insert(res) {
3243+
cx.struct_span_lint(
3244+
TRAIT_DUPLICATION_IN_BOUNDS,
3245+
span,
3246+
|lint| {
3247+
lint.build(
3248+
"this trait bound has already been specified",
3249+
)
3250+
.help("consider removing this trait bound")
3251+
.emit()
3252+
},
3253+
);
32323254
}
32333255
}
32343256
}

library/core/src/iter/traits/iterator.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2434,6 +2434,7 @@ pub trait Iterator {
24342434
/// assert!(result.is_err());
24352435
/// ```
24362436
#[inline]
2437+
#[cfg_attr(not(bootstrap), allow(trait_duplication_in_bounds))]
24372438
#[unstable(feature = "try_find", reason = "new API", issue = "63178")]
24382439
fn try_find<F, R, E>(&mut self, f: F) -> Result<Option<Self::Item>, E>
24392440
where

library/proc_macro/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1233,6 +1233,7 @@ pub mod tracked_env {
12331233
/// Besides the dependency tracking this function should be equivalent to `env::var` from the
12341234
/// standard library, except that the argument must be UTF-8.
12351235
#[unstable(feature = "proc_macro_tracked_env", issue = "74690")]
1236+
#[cfg_attr(not(bootstrap), allow(trait_duplication_in_bounds))]
12361237
pub fn var<K: AsRef<OsStr> + AsRef<str>>(key: K) -> Result<String, VarError> {
12371238
let key: &str = key.as_ref();
12381239
let value = env::var(key);

src/test/ui/lint/trait_duplication_in_bounds.rs

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,47 @@
11
#![deny(trait_duplication_in_bounds)]
22

3-
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
4-
5-
fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
6-
//~^ ERROR this trait bound is already specified in the where clause
7-
//~| ERROR this trait bound is already specified in the where clause
3+
trait DupDirectAndWhere {}
4+
fn dup_direct_and_where<T: DupDirectAndWhere>(t: T)
85
where
9-
T: Clone,
10-
T: Default,
6+
T: DupDirectAndWhere,
7+
//~^ ERROR this trait bound has already been specified
8+
T: DupDirectAndWhere,
9+
//~^ ERROR this trait bound has already been specified
1110
{
1211
unimplemented!();
1312
}
1413

15-
fn good_bar<T: Clone + Default>(arg: T) {
14+
trait DupDirect {}
15+
fn dup_direct<T: DupDirect + DupDirect>(t: T) {
16+
//~^ ERROR this trait bound has already been specified
1617
unimplemented!();
1718
}
1819

19-
fn good_foo<T>(arg: T)
20+
trait DupWhere {}
21+
fn dup_where<T>(t: T)
2022
where
21-
T: Clone + Default,
23+
T: DupWhere + DupWhere,
24+
//~^ ERROR this trait bound has already been specified
2225
{
2326
unimplemented!();
2427
}
2528

26-
fn good_foobar<T: Default>(arg: T)
29+
trait NotDup {}
30+
fn not_dup<T: NotDup, U: NotDup>((t, u): (T, U)) {
31+
unimplemented!();
32+
}
33+
34+
trait Everything {}
35+
fn everything<T: Everything + Everything, U: Everything + Everything>((t, u): (T, U))
36+
//~^ ERROR this trait bound has already been specified
37+
//~| ERROR this trait bound has already been specified
2738
where
28-
T: Clone,
39+
T: Everything + Everything + Everything,
40+
//~^ ERROR this trait bound has already been specified
41+
//~| ERROR this trait bound has already been specified
42+
//~| ERROR this trait bound has already been specified
43+
U: Everything,
44+
//~^ ERROR this trait bound has already been specified
2945
{
3046
unimplemented!();
3147
}
Lines changed: 73 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: this trait bound is already specified in the where clause
2-
--> $DIR/trait_duplication_in_bounds.rs:5:15
1+
error: this trait bound has already been specified
2+
--> $DIR/trait_duplication_in_bounds.rs:6:8
33
|
4-
LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
5-
| ^^^^^
4+
LL | T: DupDirectAndWhere,
5+
| ^^^^^^^^^^^^^^^^^
66
|
77
note: the lint level is defined here
88
--> $DIR/trait_duplication_in_bounds.rs:1:9
@@ -11,13 +11,77 @@ LL | #![deny(trait_duplication_in_bounds)]
1111
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
1212
= help: consider removing this trait bound
1313

14-
error: this trait bound is already specified in the where clause
15-
--> $DIR/trait_duplication_in_bounds.rs:5:23
14+
error: this trait bound has already been specified
15+
--> $DIR/trait_duplication_in_bounds.rs:8:8
1616
|
17-
LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
18-
| ^^^^^^^
17+
LL | T: DupDirectAndWhere,
18+
| ^^^^^^^^^^^^^^^^^
1919
|
2020
= help: consider removing this trait bound
2121

22-
error: aborting due to 2 previous errors
22+
error: this trait bound has already been specified
23+
--> $DIR/trait_duplication_in_bounds.rs:15:30
24+
|
25+
LL | fn dup_direct<T: DupDirect + DupDirect>(t: T) {
26+
| ^^^^^^^^^
27+
|
28+
= help: consider removing this trait bound
29+
30+
error: this trait bound has already been specified
31+
--> $DIR/trait_duplication_in_bounds.rs:23:19
32+
|
33+
LL | T: DupWhere + DupWhere,
34+
| ^^^^^^^^
35+
|
36+
= help: consider removing this trait bound
37+
38+
error: this trait bound has already been specified
39+
--> $DIR/trait_duplication_in_bounds.rs:35:31
40+
|
41+
LL | fn everything<T: Everything + Everything, U: Everything + Everything>((t, u): (T, U))
42+
| ^^^^^^^^^^
43+
|
44+
= help: consider removing this trait bound
45+
46+
error: this trait bound has already been specified
47+
--> $DIR/trait_duplication_in_bounds.rs:35:59
48+
|
49+
LL | fn everything<T: Everything + Everything, U: Everything + Everything>((t, u): (T, U))
50+
| ^^^^^^^^^^
51+
|
52+
= help: consider removing this trait bound
53+
54+
error: this trait bound has already been specified
55+
--> $DIR/trait_duplication_in_bounds.rs:39:8
56+
|
57+
LL | T: Everything + Everything + Everything,
58+
| ^^^^^^^^^^
59+
|
60+
= help: consider removing this trait bound
61+
62+
error: this trait bound has already been specified
63+
--> $DIR/trait_duplication_in_bounds.rs:39:21
64+
|
65+
LL | T: Everything + Everything + Everything,
66+
| ^^^^^^^^^^
67+
|
68+
= help: consider removing this trait bound
69+
70+
error: this trait bound has already been specified
71+
--> $DIR/trait_duplication_in_bounds.rs:39:34
72+
|
73+
LL | T: Everything + Everything + Everything,
74+
| ^^^^^^^^^^
75+
|
76+
= help: consider removing this trait bound
77+
78+
error: this trait bound has already been specified
79+
--> $DIR/trait_duplication_in_bounds.rs:43:8
80+
|
81+
LL | U: Everything,
82+
| ^^^^^^^^^^
83+
|
84+
= help: consider removing this trait bound
85+
86+
error: aborting due to 10 previous errors
2387

0 commit comments

Comments
 (0)