Skip to content

Commit 1da193a

Browse files
author
Allen Hsu
committed
Support trait bounds with type parameters.
1 parent c18c0ed commit 1da193a

File tree

4 files changed

+110
-58
lines changed

4 files changed

+110
-58
lines changed

clippy_lints/src/trait_bounds.rs

Lines changed: 78 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use rustc_data_structures::unhash::UnhashMap;
99
use rustc_errors::Applicability;
1010
use rustc_hir::def::Res;
1111
use rustc_hir::{
12-
GenericBound, Generics, Item, ItemKind, Node, Path, PathSegment, PredicateOrigin, QPath, TraitBoundModifier,
13-
TraitItem, Ty, TyKind, WherePredicate,
12+
GenericArg, GenericBound, Generics, Item, ItemKind, Node, Path, PathSegment, PredicateOrigin, QPath,
13+
TraitBoundModifier, TraitItem, TraitRef, Ty, TyKind, WherePredicate,
1414
};
1515
use rustc_lint::{LateContext, LateLintPass};
1616
use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -271,61 +271,22 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
271271
}
272272
}
273273

274-
fn check_bounds_or_where_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
275-
fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) {
276-
let mut map = FxHashMap::default();
277-
let mut repeated_spans = false;
278-
for bound in bounds.iter().filter_map(get_trait_info_from_bound) {
279-
let (definition, _, span_direct) = bound;
280-
if map.insert(definition, span_direct).is_some() {
281-
repeated_spans = true;
282-
}
283-
}
274+
#[derive(PartialEq, Eq, Hash, Debug)]
275+
struct ComparableTraitRef(Res, Vec<Res>);
284276

285-
if_chain! {
286-
if repeated_spans;
287-
if let Some(first_trait) = bounds.get(0);
288-
if let Some(last_trait) = bounds.iter().last();
289-
then {
290-
let all_trait_span = first_trait.span().to(last_trait.span());
291-
292-
let mut traits = map.values()
293-
.filter_map(|span| snippet_opt(cx, *span))
294-
.collect::<Vec<_>>();
295-
traits.sort_unstable();
296-
let traits = traits.join(" + ");
297-
298-
span_lint_and_sugg(
299-
cx,
300-
REPEATED_WHERE_CLAUSE_OR_TRAIT_BOUND,
301-
all_trait_span,
302-
msg,
303-
"try",
304-
traits,
305-
Applicability::MachineApplicable
306-
);
307-
}
308-
}
309-
}
310-
311-
if gen.span.from_expansion() || (gen.params.is_empty() && gen.where_clause.predicates.is_empty()) {
277+
fn check_bounds_or_where_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
278+
if gen.span.from_expansion() {
312279
return;
313280
}
314281

315-
for param in gen.params {
316-
if let ParamName::Plain(_) = param.name {
317-
// other alternatives are errors and elided which won't have duplicates
318-
rollup_traits(cx, param.bounds, "this trait bound contains repeated elements");
319-
}
320-
}
321-
322-
for predicate in gen.where_clause.predicates {
282+
for predicate in gen.predicates {
323283
if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate {
324-
rollup_traits(
325-
cx,
326-
bound_predicate.bounds,
327-
"this where clause contains repeated elements",
328-
);
284+
let msg = if predicate.in_where_clause() {
285+
"these where clauses contain repeated elements"
286+
} else {
287+
"these bounds contain repeated elements"
288+
};
289+
rollup_traits(cx, bound_predicate.bounds, msg);
329290
}
330291
}
331292
}
@@ -346,3 +307,68 @@ fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'
346307
None
347308
}
348309
}
310+
311+
// FIXME: ComparableTraitRef does not support nested bounds needed for associated_type_bounds
312+
fn into_comparable_trait_ref(trait_ref: &TraitRef<'_>) -> ComparableTraitRef {
313+
ComparableTraitRef(
314+
trait_ref.path.res,
315+
trait_ref
316+
.path
317+
.segments
318+
.iter()
319+
.filter_map(|segment| {
320+
// get trait bound type arguments
321+
Some(segment.args?.args.iter().filter_map(|arg| {
322+
if_chain! {
323+
if let GenericArg::Type(ty) = arg;
324+
if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
325+
then { return Some(path.res) }
326+
}
327+
None
328+
}))
329+
})
330+
.flatten()
331+
.collect(),
332+
)
333+
}
334+
335+
fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) {
336+
let mut map = FxHashMap::default();
337+
let mut repeated_res = false;
338+
for bound in bounds.iter().filter_map(|bound| {
339+
if let GenericBound::Trait(t, _) = bound {
340+
Some((into_comparable_trait_ref(&t.trait_ref), t.span))
341+
} else {
342+
None
343+
}
344+
}) {
345+
let (comparable_bound, span_direct) = bound;
346+
if map.insert(comparable_bound, span_direct).is_some() {
347+
repeated_res = true;
348+
}
349+
}
350+
351+
if_chain! {
352+
if repeated_res;
353+
if let [first_trait, .., last_trait] = bounds;
354+
then {
355+
let all_trait_span = first_trait.span().to(last_trait.span());
356+
357+
let mut traits = map.values()
358+
.filter_map(|span| snippet_opt(cx, *span))
359+
.collect::<Vec<_>>();
360+
traits.sort_unstable();
361+
let traits = traits.join(" + ");
362+
363+
span_lint_and_sugg(
364+
cx,
365+
REPEATED_WHERE_CLAUSES_OR_TRAIT_BOUNDS,
366+
all_trait_span,
367+
msg,
368+
"try",
369+
traits,
370+
Applicability::MachineApplicable
371+
);
372+
}
373+
}
374+
}

tests/ui/repeated_where_clause_or_trait_bound.fixed

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,14 @@ where
9292

9393
fn no_error_separate_arg_bounds(program: impl AsRef<()>, dir: impl AsRef<()>, args: &[impl AsRef<()>]) {}
9494

95+
trait GenericTrait<T> {}
96+
97+
fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
98+
unimplemented!();
99+
}
100+
101+
fn bad_generic<T: GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
102+
unimplemented!();
103+
}
104+
95105
fn main() {}

tests/ui/repeated_where_clause_or_trait_bound.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,14 @@ where
9292

9393
fn no_error_separate_arg_bounds(program: impl AsRef<()>, dir: impl AsRef<()>, args: &[impl AsRef<()>]) {}
9494

95+
trait GenericTrait<T> {}
96+
97+
fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
98+
unimplemented!();
99+
}
100+
101+
fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
102+
unimplemented!();
103+
}
104+
95105
fn main() {}
Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error: this trait bound contains repeated elements
1+
error: these bounds contain repeated elements
22
--> $DIR/repeated_where_clause_or_trait_bound.rs:6:15
33
|
44
LL | fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
@@ -10,29 +10,35 @@ note: the lint level is defined here
1010
LL | #![deny(clippy::repeated_where_clause_or_trait_bound)]
1111
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1212

13-
error: this where clause contains repeated elements
13+
error: these where clauses contain repeated elements
1414
--> $DIR/repeated_where_clause_or_trait_bound.rs:12:8
1515
|
1616
LL | T: Clone + Clone + Clone + Copy,
1717
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
1818

19-
error: this where clause contains repeated elements
19+
error: these where clauses contain repeated elements
2020
--> $DIR/repeated_where_clause_or_trait_bound.rs:47:15
2121
|
2222
LL | Self: Clone + Clone + Clone;
2323
| ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
2424

25-
error: this trait bound contains repeated elements
25+
error: these bounds contain repeated elements
2626
--> $DIR/repeated_where_clause_or_trait_bound.rs:61:24
2727
|
2828
LL | trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
2929
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
3030

31-
error: this where clause contains repeated elements
31+
error: these where clauses contain repeated elements
3232
--> $DIR/repeated_where_clause_or_trait_bound.rs:68:12
3333
|
3434
LL | T: Clone + Clone + Clone + Copy,
3535
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
3636

37-
error: aborting due to 5 previous errors
37+
error: these bounds contain repeated elements
38+
--> $DIR/repeated_where_clause_or_trait_bound.rs:101:19
39+
|
40+
LL | fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
41+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `GenericTrait<u32> + GenericTrait<u64>`
42+
43+
error: aborting due to 6 previous errors
3844

0 commit comments

Comments
 (0)