1 use clippy_utils
::diagnostics
::{span_lint_and_help, span_lint_and_sugg}
;
2 use clippy_utils
::source
::{snippet, snippet_opt, snippet_with_applicability}
;
3 use clippy_utils
::{SpanlessEq, SpanlessHash}
;
4 use core
::hash
::{Hash, Hasher}
;
5 use if_chain
::if_chain
;
6 use itertools
::Itertools
;
7 use rustc_data_structures
::fx
::{FxHashMap, FxHashSet}
;
8 use rustc_data_structures
::unhash
::UnhashMap
;
9 use rustc_errors
::Applicability
;
10 use rustc_hir
::def
::Res
;
12 GenericArg
, GenericBound
, Generics
, Item
, ItemKind
, Node
, Path
, PathSegment
, PredicateOrigin
, QPath
,
13 TraitBoundModifier
, TraitItem
, TraitRef
, Ty
, TyKind
, WherePredicate
,
15 use rustc_lint
::{LateContext, LateLintPass}
;
16 use rustc_session
::{declare_tool_lint, impl_lint_pass}
;
17 use rustc_span
::{BytePos, Span}
;
18 use std
::collections
::hash_map
::Entry
;
20 declare_clippy_lint
! {
22 /// This lint warns about unnecessary type repetitions in trait bounds
24 /// ### Why is this bad?
25 /// Repeating the type for every bound makes the code
26 /// less readable than combining the bounds
30 /// pub fn foo<T>(t: T) where T: Copy, T: Clone {}
35 /// pub fn foo<T>(t: T) where T: Copy + Clone {}
37 #[clippy::version = "1.38.0"]
38 pub TYPE_REPETITION_IN_BOUNDS
,
40 "types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
43 declare_clippy_lint
! {
45 /// Checks for cases where generics are being used and multiple
46 /// syntax specifications for trait bounds are used simultaneously.
48 /// ### Why is this bad?
49 /// Duplicate bounds makes the code
50 /// less readable than specifying them only once.
54 /// fn func<T: Clone + Default>(arg: T) where T: Clone + Default {}
60 /// fn func<T: Clone + Default>(arg: T) {}
65 /// fn func<T>(arg: T) where T: Clone + Default {}
69 /// fn foo<T: Default + Default>(bar: T) {}
73 /// fn foo<T: Default>(bar: T) {}
77 /// fn foo<T>(bar: T) where T: Default + Default {}
81 /// fn foo<T>(bar: T) where T: Default {}
83 #[clippy::version = "1.47.0"]
84 pub TRAIT_DUPLICATION_IN_BOUNDS
,
86 "check if the same trait bounds are specified more than once during a generic declaration"
89 #[derive(Copy, Clone)]
90 pub struct TraitBounds
{
91 max_trait_bounds
: u64,
96 pub fn new(max_trait_bounds
: u64) -> Self {
97 Self { max_trait_bounds }
101 impl_lint_pass
!(TraitBounds
=> [TYPE_REPETITION_IN_BOUNDS
, TRAIT_DUPLICATION_IN_BOUNDS
]);
103 impl<'tcx
> LateLintPass
<'tcx
> for TraitBounds
{
104 fn check_generics(&mut self, cx
: &LateContext
<'tcx
>, gen
: &'tcx Generics
<'_
>) {
105 self.check_type_repetition(cx
, gen
);
106 check_trait_bound_duplication(cx
, gen
);
109 fn check_item(&mut self, cx
: &LateContext
<'tcx
>, item
: &'tcx Item
<'tcx
>) {
110 // special handling for self trait bounds as these are not considered generics
111 // ie. trait Foo: Display {}
113 kind
: ItemKind
::Trait(_
, _
, _
, bounds
, ..),
117 rollup_traits(cx
, bounds
, "these bounds contain repeated elements");
121 fn check_trait_item(&mut self, cx
: &LateContext
<'tcx
>, item
: &'tcx TraitItem
<'tcx
>) {
122 let mut self_bounds_map
= FxHashMap
::default();
124 for predicate
in item
.generics
.predicates
{
126 if let WherePredicate
::BoundPredicate(ref bound_predicate
) = predicate
;
127 if bound_predicate
.origin
!= PredicateOrigin
::ImplTrait
;
128 if !bound_predicate
.span
.from_expansion();
129 if let TyKind
::Path(QPath
::Resolved(_
, Path { segments, .. }
)) = bound_predicate
.bounded_ty
.kind
;
130 if let Some(PathSegment
{
131 res
: Res
::SelfTyParam { trait_: def_id }
, ..
132 }) = segments
.first();
136 kind
: ItemKind
::Trait(_
, _
, _
, self_bounds
, _
),
139 ) = cx
.tcx
.hir().get_if_local(*def_id
);
141 if self_bounds_map
.is_empty() {
142 for bound
in self_bounds
.iter() {
143 let Some((self_res
, self_segments
, _
)) = get_trait_info_from_bound(bound
) else { continue }
;
144 self_bounds_map
.insert(self_res
, self_segments
);
151 .filter_map(get_trait_info_from_bound
)
152 .for_each(|(trait_item_res
, trait_item_segments
, span
)| {
153 if let Some(self_segments
) = self_bounds_map
.get(&trait_item_res
) {
154 if SpanlessEq
::new(cx
).eq_path_segments(self_segments
, trait_item_segments
) {
157 TRAIT_DUPLICATION_IN_BOUNDS
,
159 "this trait bound is already specified in trait declaration",
161 "consider removing this trait bound",
173 fn check_type_repetition
<'tcx
>(self, cx
: &LateContext
<'tcx
>, gen
: &'tcx Generics
<'_
>) {
174 struct SpanlessTy
<'cx
, 'tcx
> {
176 cx
: &'cx LateContext
<'tcx
>,
178 impl PartialEq
for SpanlessTy
<'_
, '_
> {
179 fn eq(&self, other
: &Self) -> bool
{
180 let mut eq
= SpanlessEq
::new(self.cx
);
181 eq
.inter_expr().eq_ty(self.ty
, other
.ty
)
184 impl Hash
for SpanlessTy
<'_
, '_
> {
185 fn hash
<H
: Hasher
>(&self, h
: &mut H
) {
186 let mut t
= SpanlessHash
::new(self.cx
);
188 h
.write_u64(t
.finish());
191 impl Eq
for SpanlessTy
<'_
, '_
> {}
193 if gen
.span
.from_expansion() {
196 let mut map
: UnhashMap
<SpanlessTy
<'_
, '_
>, Vec
<&GenericBound
<'_
>>> = UnhashMap
::default();
197 let mut applicability
= Applicability
::MaybeIncorrect
;
198 for bound
in gen
.predicates
{
200 if let WherePredicate
::BoundPredicate(ref p
) = bound
;
201 if p
.origin
!= PredicateOrigin
::ImplTrait
;
202 if p
.bounds
.len() as u64 <= self.max_trait_bounds
;
203 if !p
.span
.from_expansion();
204 if let Some(ref v
) = map
.insert(
205 SpanlessTy { ty: p.bounded_ty, cx }
,
206 p
.bounds
.iter().collect
::<Vec
<_
>>()
213 .chain(p
.bounds
.iter())
214 .filter_map(get_trait_info_from_bound
)
215 .map(|(_
, _
, span
)| snippet_with_applicability(cx
, span
, "..", &mut applicability
))
217 let hint_string
= format
!(
218 "consider combining the bounds: `{}: {trait_bounds}`",
219 snippet(cx
, p
.bounded_ty
.span
, "_"),
223 TYPE_REPETITION_IN_BOUNDS
,
225 "this type has already been used as a bound predicate",
235 fn check_trait_bound_duplication(cx
: &LateContext
<'_
>, gen
: &'_ Generics
<'_
>) {
236 if gen
.span
.from_expansion() {
241 // fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
242 // where T: Clone + Default, { unimplemented!(); }
243 // ^^^^^^^^^^^^^^^^^^
245 // collects each of these where clauses into a set keyed by generic name and comparable trait
247 let where_predicates
= gen
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
;
257 rollup_traits(cx
, bound_predicate
.bounds
, "these where clauses contain repeated elements")
258 .into_iter().map(|(trait_ref
, _
)| (path
.res
, trait_ref
)))
264 .collect
::<FxHashSet
<_
>>();
267 // fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) ...
268 // ^^^^^^^^^^^^^^^^^^ ^^^^^^^
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()) {
274 if let WherePredicate
::BoundPredicate(bound_predicate
) = predicate
;
275 if bound_predicate
.origin
!= PredicateOrigin
::ImplTrait
;
276 if !bound_predicate
.span
.from_expansion();
277 if let TyKind
::Path(QPath
::Resolved(_
, path
)) = bound_predicate
.bounded_ty
.kind
;
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
) {
285 TRAIT_DUPLICATION_IN_BOUNDS
,
287 "this trait bound is already specified in the where clause",
289 "consider removing this trait bound",
298 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
299 struct ComparableTraitRef(Res
, Vec
<Res
>);
300 impl Default
for ComparableTraitRef
{
301 fn default() -> Self {
302 Self(Res
::Err
, Vec
::new())
306 fn get_trait_info_from_bound
<'a
>(bound
: &'a GenericBound
<'_
>) -> Option
<(Res
, &'a
[PathSegment
<'a
>], Span
)> {
307 if let GenericBound
::Trait(t
, tbm
) = bound
{
308 let trait_path
= t
.trait_ref
.path
;
310 let path_span
= trait_path
.span
;
311 if let TraitBoundModifier
::Maybe
= tbm
{
312 path_span
.with_lo(path_span
.lo() - BytePos(1)) // include the `?`
317 Some((trait_path
.res
, trait_path
.segments
, trait_span
))
323 // FIXME: ComparableTraitRef does not support nested bounds needed for associated_type_bounds
324 fn into_comparable_trait_ref(trait_ref
: &TraitRef
<'_
>) -> ComparableTraitRef
{
331 .filter_map(|segment
| {
332 // get trait bound type arguments
333 Some(segment
.args?
.args
.iter().filter_map(|arg
| {
335 if let GenericArg
::Type(ty
) = arg
;
336 if let TyKind
::Path(QPath
::Resolved(_
, path
)) = ty
.kind
;
337 then { return Some(path.res) }
347 fn rollup_traits(cx
: &LateContext
<'_
>, bounds
: &[GenericBound
<'_
>], msg
: &str) -> Vec
<(ComparableTraitRef
, Span
)> {
348 let mut map
= FxHashMap
::default();
349 let mut repeated_res
= false;
351 let only_comparable_trait_refs
= |bound
: &GenericBound
<'_
>| {
352 if let GenericBound
::Trait(t
, _
) = bound
{
353 Some((into_comparable_trait_ref(&t
.trait_ref
), t
.span
))
360 for bound
in bounds
.iter().filter_map(only_comparable_trait_refs
) {
361 let (comparable_bound
, span_direct
) = bound
;
362 match map
.entry(comparable_bound
) {
363 Entry
::Occupied(_
) => repeated_res
= true,
364 Entry
::Vacant(e
) => {
365 e
.insert((span_direct
, i
));
371 // Put bounds in source order
372 let mut comparable_bounds
= vec
![Default
::default(); map
.len()];
373 for (k
, (v
, i
)) in map
{
374 comparable_bounds
[i
] = (k
, v
);
379 if let [first_trait
, .., last_trait
] = bounds
;
381 let all_trait_span
= first_trait
.span().to(last_trait
.span());
383 let traits
= comparable_bounds
.iter()
384 .filter_map(|&(_
, span
)| snippet_opt(cx
, span
))
385 .collect
::<Vec
<_
>>();
386 let traits
= traits
.join(" + ");
390 TRAIT_DUPLICATION_IN_BOUNDS
,
395 Applicability
::MachineApplicable