Skip to content

Commit 84df61c

Browse files
committed
Auto merge of rust-lang#9167 - aldhsu:fix-trait-duplication-false-pos, r=flip1995
Fixes [`trait_duplication_in_bounds`] false positives Fixes rust-lang#9076 rust-lang#9151 rust-lang#8757. Partially fixes rust-lang#8771. changelog: [`trait_duplication_in_bounds`]: Reduce number of false positives.
2 parents 4d5d191 + 8bae517 commit 84df61c

7 files changed

+481
-334
lines changed

clippy_lints/src/trait_bounds.rs

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use clippy_utils::{SpanlessEq, SpanlessHash};
44
use core::hash::{Hash, Hasher};
55
use if_chain::if_chain;
66
use itertools::Itertools;
7-
use rustc_data_structures::fx::FxHashMap;
7+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
88
use rustc_data_structures::unhash::UnhashMap;
99
use rustc_errors::Applicability;
1010
use rustc_hir::def::Res;
@@ -103,7 +103,6 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
103103
fn check_generics(&mut self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
104104
self.check_type_repetition(cx, gen);
105105
check_trait_bound_duplication(cx, gen);
106-
check_bounds_or_where_duplication(cx, gen);
107106
}
108107

109108
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
@@ -234,35 +233,61 @@ impl TraitBounds {
234233
}
235234

236235
fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
237-
if gen.span.from_expansion() || gen.params.is_empty() || gen.predicates.is_empty() {
236+
if gen.span.from_expansion() {
238237
return;
239238
}
240239

241-
let mut map = FxHashMap::<_, Vec<_>>::default();
242-
for predicate in gen.predicates {
240+
// Explanation:
241+
// fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
242+
// where T: Clone + Default, { unimplemented!(); }
243+
// ^^^^^^^^^^^^^^^^^^
244+
// |
245+
// collects each of these where clauses into a set keyed by generic name and comparable trait
246+
// eg. (T, Clone)
247+
let where_predicates = gen
248+
.predicates
249+
.iter()
250+
.filter_map(|pred| {
251+
if_chain! {
252+
if pred.in_where_clause();
253+
if let WherePredicate::BoundPredicate(bound_predicate) = pred;
254+
if let TyKind::Path(QPath::Resolved(_, path)) = bound_predicate.bounded_ty.kind;
255+
then {
256+
return Some(
257+
rollup_traits(cx, bound_predicate.bounds, "these where clauses contain repeated elements")
258+
.into_keys().map(|trait_ref| (path.res, trait_ref)))
259+
}
260+
}
261+
None
262+
})
263+
.flatten()
264+
.collect::<FxHashSet<_>>();
265+
266+
// Explanation:
267+
// fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) ...
268+
// ^^^^^^^^^^^^^^^^^^ ^^^^^^^
269+
// |
270+
// compare trait bounds keyed by generic name and comparable trait to collected where
271+
// predicates eg. (T, Clone)
272+
for predicate in gen.predicates.iter().filter(|pred| !pred.in_where_clause()) {
243273
if_chain! {
244-
if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
274+
if let WherePredicate::BoundPredicate(bound_predicate) = predicate;
245275
if bound_predicate.origin != PredicateOrigin::ImplTrait;
246276
if !bound_predicate.span.from_expansion();
247-
if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind;
248-
if let Some(segment) = segments.first();
277+
if let TyKind::Path(QPath::Resolved(_, path)) = bound_predicate.bounded_ty.kind;
249278
then {
250-
for (res_where, _, span_where) in bound_predicate.bounds.iter().filter_map(get_trait_info_from_bound) {
251-
let trait_resolutions_direct = map.entry(segment.ident).or_default();
252-
if let Some((_, span_direct)) = trait_resolutions_direct
253-
.iter()
254-
.find(|(res_direct, _)| *res_direct == res_where) {
279+
let traits = rollup_traits(cx, bound_predicate.bounds, "these bounds contain repeated elements");
280+
for (trait_ref, span) in traits {
281+
let key = (path.res, trait_ref);
282+
if where_predicates.contains(&key) {
255283
span_lint_and_help(
256284
cx,
257285
TRAIT_DUPLICATION_IN_BOUNDS,
258-
*span_direct,
286+
span,
259287
"this trait bound is already specified in the where clause",
260288
None,
261289
"consider removing this trait bound",
262-
);
263-
}
264-
else {
265-
trait_resolutions_direct.push((res_where, span_where));
290+
);
266291
}
267292
}
268293
}
@@ -273,23 +298,6 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
273298
#[derive(PartialEq, Eq, Hash, Debug)]
274299
struct ComparableTraitRef(Res, Vec<Res>);
275300

276-
fn check_bounds_or_where_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
277-
if gen.span.from_expansion() {
278-
return;
279-
}
280-
281-
for predicate in gen.predicates {
282-
if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate {
283-
let msg = if predicate.in_where_clause() {
284-
"these where clauses contain repeated elements"
285-
} else {
286-
"these bounds contain repeated elements"
287-
};
288-
rollup_traits(cx, bound_predicate.bounds, msg);
289-
}
290-
}
291-
}
292-
293301
fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'a [PathSegment<'a>], Span)> {
294302
if let GenericBound::Trait(t, tbm) = bound {
295303
let trait_path = t.trait_ref.path;
@@ -331,7 +339,7 @@ fn into_comparable_trait_ref(trait_ref: &TraitRef<'_>) -> ComparableTraitRef {
331339
)
332340
}
333341

334-
fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) {
342+
fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) -> FxHashMap<ComparableTraitRef, Span> {
335343
let mut map = FxHashMap::default();
336344
let mut repeated_res = false;
337345

@@ -373,4 +381,6 @@ fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) {
373381
);
374382
}
375383
}
384+
385+
map
376386
}

tests/compile-test.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,6 @@ const RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS: &[&str] = &[
394394
"single_component_path_imports_nested_first.rs",
395395
"string_add.rs",
396396
"toplevel_ref_arg_non_rustfix.rs",
397-
"trait_duplication_in_bounds.rs",
398397
"unit_arg.rs",
399398
"unnecessary_clone.rs",
400399
"unnecessary_lazy_eval_unfixable.rs",
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// run-rustfix
2+
#![deny(clippy::trait_duplication_in_bounds)]
3+
#![allow(unused)]
4+
5+
fn bad_foo<T: Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
6+
unimplemented!();
7+
}
8+
9+
fn bad_bar<T, U>(arg0: T, arg1: U)
10+
where
11+
T: Clone + Copy,
12+
U: Clone + Copy,
13+
{
14+
unimplemented!();
15+
}
16+
17+
fn good_bar<T: Clone + Copy, U: Clone + Copy>(arg0: T, arg1: U) {
18+
unimplemented!();
19+
}
20+
21+
fn good_foo<T, U>(arg0: T, arg1: U)
22+
where
23+
T: Clone + Copy,
24+
U: Clone + Copy,
25+
{
26+
unimplemented!();
27+
}
28+
29+
trait GoodSelfTraitBound: Clone + Copy {
30+
fn f();
31+
}
32+
33+
trait GoodSelfWhereClause {
34+
fn f()
35+
where
36+
Self: Clone + Copy;
37+
}
38+
39+
trait BadSelfTraitBound: Clone {
40+
fn f();
41+
}
42+
43+
trait BadSelfWhereClause {
44+
fn f()
45+
where
46+
Self: Clone;
47+
}
48+
49+
trait GoodTraitBound<T: Clone + Copy, U: Clone + Copy> {
50+
fn f();
51+
}
52+
53+
trait GoodWhereClause<T, U> {
54+
fn f()
55+
where
56+
T: Clone + Copy,
57+
U: Clone + Copy;
58+
}
59+
60+
trait BadTraitBound<T: Clone + Copy, U: Clone + Copy> {
61+
fn f();
62+
}
63+
64+
trait BadWhereClause<T, U> {
65+
fn f()
66+
where
67+
T: Clone + Copy,
68+
U: Clone + Copy;
69+
}
70+
71+
struct GoodStructBound<T: Clone + Copy, U: Clone + Copy> {
72+
t: T,
73+
u: U,
74+
}
75+
76+
impl<T: Clone + Copy, U: Clone + Copy> GoodTraitBound<T, U> for GoodStructBound<T, U> {
77+
// this should not warn
78+
fn f() {}
79+
}
80+
81+
struct GoodStructWhereClause;
82+
83+
impl<T, U> GoodTraitBound<T, U> for GoodStructWhereClause
84+
where
85+
T: Clone + Copy,
86+
U: Clone + Copy,
87+
{
88+
// this should not warn
89+
fn f() {}
90+
}
91+
92+
fn no_error_separate_arg_bounds(program: impl AsRef<()>, dir: impl AsRef<()>, args: &[impl AsRef<()>]) {}
93+
94+
trait GenericTrait<T> {}
95+
96+
fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
97+
unimplemented!();
98+
}
99+
100+
fn bad_generic<T: GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
101+
unimplemented!();
102+
}
103+
104+
mod foo {
105+
pub trait Clone {}
106+
}
107+
108+
fn qualified_path<T: Clone + foo::Clone>(arg0: T) {
109+
unimplemented!();
110+
}
111+
112+
fn main() {}

0 commit comments

Comments
 (0)