1 use crate::utils
::{in_macro, snippet, snippet_with_applicability, span_lint_and_help, SpanlessHash}
;
2 use if_chain
::if_chain
;
3 use rustc_data_structures
::fx
::FxHashMap
;
4 use rustc_errors
::Applicability
;
5 use rustc_hir
::{def::Res, GenericBound, Generics, ParamName, Path, QPath, TyKind, WherePredicate}
;
6 use rustc_lint
::{LateContext, LateLintPass}
;
7 use rustc_session
::{declare_tool_lint, impl_lint_pass}
;
10 declare_clippy_lint
! {
11 /// **What it does:** This lint warns about unnecessary type repetitions in trait bounds
13 /// **Why is this bad?** Repeating the type for every bound makes the code
14 /// less readable than combining the bounds
16 /// **Known problems:** None.
20 /// pub fn foo<T>(t: T) where T: Copy, T: Clone {}
23 /// Could be written as:
26 /// pub fn foo<T>(t: T) where T: Copy + Clone {}
28 pub TYPE_REPETITION_IN_BOUNDS
,
30 "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
33 declare_clippy_lint
! {
34 /// **What it does:** Checks for cases where generics are being used and multiple
35 /// syntax specifications for trait bounds are used simultaneously.
37 /// **Why is this bad?** Duplicate bounds makes the code
38 /// less readable than specifing them only once.
40 /// **Known problems:** None.
44 /// fn func<T: Clone + Default>(arg: T) where T: Clone + Default {}
47 /// Could be written as:
50 /// fn func<T: Clone + Default>(arg: T) {}
55 /// fn func<T>(arg: T) where T: Clone + Default {}
57 pub TRAIT_DUPLICATION_IN_BOUNDS
,
59 "Check if the same trait bounds are specified twice during a function declaration"
62 #[derive(Copy, Clone)]
63 pub struct TraitBounds
{
64 max_trait_bounds
: u64,
69 pub fn new(max_trait_bounds
: u64) -> Self {
70 Self { max_trait_bounds }
74 impl_lint_pass
!(TraitBounds
=> [TYPE_REPETITION_IN_BOUNDS
, TRAIT_DUPLICATION_IN_BOUNDS
]);
76 impl<'tcx
> LateLintPass
<'tcx
> for TraitBounds
{
77 fn check_generics(&mut self, cx
: &LateContext
<'tcx
>, gen
: &'tcx Generics
<'_
>) {
78 self.check_type_repetition(cx
, gen
);
79 check_trait_bound_duplication(cx
, gen
);
83 fn get_trait_res_span_from_bound(bound
: &GenericBound
<'_
>) -> Option
<(Res
, Span
)> {
84 if let GenericBound
::Trait(t
, _
) = bound
{
85 Some((t
.trait_ref
.path
.res
, t
.span
))
92 fn check_type_repetition(self, cx
: &LateContext
<'_
>, gen
: &'_ Generics
<'_
>) {
93 if in_macro(gen
.span
) {
96 let hash
= |ty
| -> u64 {
97 let mut hasher
= SpanlessHash
::new(cx
);
101 let mut map
= FxHashMap
::default();
102 let mut applicability
= Applicability
::MaybeIncorrect
;
103 for bound
in gen
.where_clause
.predicates
{
105 if let WherePredicate
::BoundPredicate(ref p
) = bound
;
106 if p
.bounds
.len() as u64 <= self.max_trait_bounds
;
107 if !in_macro(p
.span
);
108 let h
= hash(&p
.bounded_ty
);
109 if let Some(ref v
) = map
.insert(h
, p
.bounds
.iter().collect
::<Vec
<_
>>());
112 let mut hint_string
= format
!(
113 "consider combining the bounds: `{}:",
114 snippet(cx
, p
.bounded_ty
.span
, "_")
117 if let GenericBound
::Trait(ref poly_trait_ref
, _
) = b
{
118 let path
= &poly_trait_ref
.trait_ref
.path
;
119 hint_string
.push_str(&format
!(
121 snippet_with_applicability(cx
, path
.span
, "..", &mut applicability
)
125 for b
in p
.bounds
.iter() {
126 if let GenericBound
::Trait(ref poly_trait_ref
, _
) = b
{
127 let path
= &poly_trait_ref
.trait_ref
.path
;
128 hint_string
.push_str(&format
!(
130 snippet_with_applicability(cx
, path
.span
, "..", &mut applicability
)
134 hint_string
.truncate(hint_string
.len() - 2);
135 hint_string
.push('`'
);
138 TYPE_REPETITION_IN_BOUNDS
,
140 "this type has already been used as a bound predicate",
150 fn check_trait_bound_duplication(cx
: &LateContext
<'_
>, gen
: &'_ Generics
<'_
>) {
151 if in_macro(gen
.span
) || gen
.params
.is_empty() || gen
.where_clause
.predicates
.is_empty() {
155 let mut map
= FxHashMap
::default();
156 for param
in gen
.params
{
157 if let ParamName
::Plain(ref ident
) = param
.name
{
161 .filter_map(get_trait_res_span_from_bound
)
162 .collect
::<Vec
<_
>>();
163 map
.insert(*ident
, res
);
167 for predicate
in gen
.where_clause
.predicates
{
169 if let WherePredicate
::BoundPredicate(ref bound_predicate
) = predicate
;
170 if !in_macro(bound_predicate
.span
);
171 if let TyKind
::Path(QPath
::Resolved(_
, Path { ref segments, .. }
)) = bound_predicate
.bounded_ty
.kind
;
172 if let Some(segment
) = segments
.first();
173 if let Some(trait_resolutions_direct
) = map
.get(&segment
.ident
);
175 for (res_where
, _
) in bound_predicate
.bounds
.iter().filter_map(get_trait_res_span_from_bound
) {
176 if let Some((_
, span_direct
)) = trait_resolutions_direct
178 .find(|(res_direct
, _
)| *res_direct
== res_where
) {
181 TRAIT_DUPLICATION_IN_BOUNDS
,
183 "this trait bound is already specified in the where clause",
185 "consider removing this trait bound",