Skip to content

Commit e96177a

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

4 files changed

+123
-58
lines changed

clippy_lints/src/trait_bounds.rs

+91-52
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ use rustc_data_structures::unhash::UnhashMap;
88
use rustc_errors::Applicability;
99
use rustc_hir::def::Res;
1010
use rustc_hir::{
11-
GenericBound, Generics, Item, ItemKind, Node, Path, PathSegment, QPath, TraitItem, Ty, TyKind, WherePredicate,
11+
GenericArg, GenericBound, Generics, Item, ItemKind, Node, Path, PathSegment, QPath, TraitItem, Ty, TyKind,
12+
WherePredicate,
1213
};
1314
use rustc_lint::{LateContext, LateLintPass};
1415
use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -94,7 +95,7 @@ declare_clippy_lint! {
9495
/// ```
9596
#[clippy::version = "1.62.0"]
9697
pub REPEATED_WHERE_CLAUSE_OR_TRAIT_BOUND,
97-
pedantic,
98+
nursery,
9899
"Traits are repeated within trait bounds or where clause"
99100
}
100101

@@ -280,61 +281,26 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
280281
}
281282
}
282283

283-
fn check_bounds_or_where_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
284-
fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) {
285-
let mut map = FxHashMap::default();
286-
let mut repeated_spans = false;
287-
for bound in bounds.iter().filter_map(get_trait_info_from_bound) {
288-
let (definition, _, span_direct) = bound;
289-
if map.insert(definition, span_direct).is_some() {
290-
repeated_spans = true;
291-
}
292-
}
293-
294-
if_chain! {
295-
if repeated_spans;
296-
if let Some(first_trait) = bounds.get(0);
297-
if let Some(last_trait) = bounds.iter().last();
298-
then {
299-
let all_trait_span = first_trait.span().to(last_trait.span());
300-
301-
let mut traits = map.values()
302-
.filter_map(|span| snippet_opt(cx, *span))
303-
.collect::<Vec<_>>();
304-
traits.sort_unstable();
305-
let traits = traits.join(" + ");
284+
#[derive(PartialEq, Eq, Hash, Debug)]
285+
struct ComparableBound(
286+
Res,
287+
Vec<Res>,
288+
// Vec<ComparableBound>
289+
);
306290

307-
span_lint_and_sugg(
308-
cx,
309-
REPEATED_WHERE_CLAUSE_OR_TRAIT_BOUND,
310-
all_trait_span,
311-
msg,
312-
"try",
313-
traits,
314-
Applicability::MachineApplicable
315-
);
316-
}
317-
}
318-
}
319-
320-
if gen.span.from_expansion() || (gen.params.is_empty() && gen.where_clause.predicates.is_empty()) {
291+
fn check_bounds_or_where_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
292+
if gen.span.from_expansion() {
321293
return;
322294
}
323295

324-
for param in gen.params {
325-
if let ParamName::Plain(_) = param.name {
326-
// other alternatives are errors and elided which won't have duplicates
327-
rollup_traits(cx, param.bounds, "this trait bound contains repeated elements");
328-
}
329-
}
330-
331-
for predicate in gen.where_clause.predicates {
296+
for predicate in gen.predicates {
332297
if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate {
333-
rollup_traits(
334-
cx,
335-
bound_predicate.bounds,
336-
"this where clause contains repeated elements",
337-
);
298+
let msg = if predicate.in_where_clause() {
299+
"these where clauses contain repeated elements"
300+
} else {
301+
"these bounds contain repeated elements"
302+
};
303+
rollup_traits(cx, bound_predicate.bounds, msg);
338304
}
339305
}
340306
}
@@ -346,3 +312,76 @@ fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'
346312
None
347313
}
348314
}
315+
316+
fn try_into_comparable_bound(bound: &GenericBound<'_>) -> Option<ComparableBound> {
317+
if let GenericBound::Trait(t, _) = bound {
318+
Some(ComparableBound(
319+
t.trait_ref.path.res,
320+
t.trait_ref
321+
.path
322+
.segments
323+
.iter()
324+
.filter_map(|segment| {
325+
// get trait bound type arguments
326+
Some(segment.args?.args.iter().filter_map(|arg| {
327+
if_chain! {
328+
if let GenericArg::Type(ty) = arg;
329+
if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
330+
then { return Some(path.res) }
331+
}
332+
None
333+
}))
334+
})
335+
.flatten()
336+
.collect(),
337+
// t.bound_generic_params
338+
// .iter()
339+
// .flat_map(|param| param.bounds.iter().filter_map(try_into_comparable_bound))
340+
// .collect(),
341+
))
342+
} else {
343+
None
344+
}
345+
}
346+
347+
fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) {
348+
let mut map = FxHashMap::default();
349+
let mut repeated_spans = false;
350+
for bound in bounds.iter().filter_map(|bound| {
351+
if let GenericBound::Trait(t, _) = bound {
352+
Some((try_into_comparable_bound(bound)?, t.span))
353+
} else {
354+
None
355+
}
356+
}) {
357+
let (comparable_bound, span_direct) = bound;
358+
if map.insert(comparable_bound, span_direct).is_some() {
359+
repeated_spans = true;
360+
}
361+
}
362+
363+
if_chain! {
364+
if repeated_spans;
365+
if let Some(first_trait) = bounds.get(0);
366+
if let Some(last_trait) = bounds.iter().last();
367+
then {
368+
let all_trait_span = first_trait.span().to(last_trait.span());
369+
370+
let mut traits = map.values()
371+
.filter_map(|span| snippet_opt(cx, *span))
372+
.collect::<Vec<_>>();
373+
traits.sort_unstable();
374+
let traits = traits.join(" + ");
375+
376+
span_lint_and_sugg(
377+
cx,
378+
REPEATED_WHERE_CLAUSE_OR_TRAIT_BOUND,
379+
all_trait_span,
380+
msg,
381+
"try",
382+
traits,
383+
Applicability::MachineApplicable
384+
);
385+
}
386+
}
387+
}

tests/ui/repeated_where_clause_or_trait_bound.fixed

+10
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

+10
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() {}
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)