]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/trait_bounds.rs
Don't lint for predicates generated in macros
[rust.git] / clippy_lints / src / trait_bounds.rs
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::{GenericBound, Generics, WherePredicate};
6 use rustc_lint::{LateContext, LateLintPass};
7 use rustc_session::{declare_tool_lint, impl_lint_pass};
8
9 declare_clippy_lint! {
10     /// **What it does:** This lint warns about unnecessary type repetitions in trait bounds
11     ///
12     /// **Why is this bad?** Repeating the type for every bound makes the code
13     /// less readable than combining the bounds
14     ///
15     /// **Known problems:** None.
16     ///
17     /// **Example:**
18     /// ```rust
19     /// pub fn foo<T>(t: T) where T: Copy, T: Clone {}
20     /// ```
21     ///
22     /// Could be written as:
23     ///
24     /// ```rust
25     /// pub fn foo<T>(t: T) where T: Copy + Clone {}
26     /// ```
27     pub TYPE_REPETITION_IN_BOUNDS,
28     pedantic,
29     "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
30 }
31
32 #[derive(Copy, Clone)]
33 pub struct TraitBounds {
34     max_trait_bounds: u64,
35 }
36
37 impl TraitBounds {
38     #[must_use]
39     pub fn new(max_trait_bounds: u64) -> Self {
40         Self { max_trait_bounds }
41     }
42 }
43
44 impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS]);
45
46 impl<'tcx> LateLintPass<'tcx> for TraitBounds {
47     fn check_generics(&mut self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
48         if in_macro(gen.span) {
49             return;
50         }
51         let hash = |ty| -> u64 {
52             let mut hasher = SpanlessHash::new(cx);
53             hasher.hash_ty(ty);
54             hasher.finish()
55         };
56         let mut map = FxHashMap::default();
57         let mut applicability = Applicability::MaybeIncorrect;
58         for bound in gen.where_clause.predicates {
59             if_chain! {
60                 if let WherePredicate::BoundPredicate(ref p) = bound;
61                 if p.bounds.len() as u64 <= self.max_trait_bounds;
62                 if !in_macro(p.span);
63                 let h = hash(&p.bounded_ty);
64                 if let Some(ref v) = map.insert(h, p.bounds.iter().collect::<Vec<_>>());
65
66                 then {
67                     let mut hint_string = format!(
68                         "consider combining the bounds: `{}:",
69                         snippet(cx, p.bounded_ty.span, "_")
70                     );
71                     for b in v.iter() {
72                         if let GenericBound::Trait(ref poly_trait_ref, _) = b {
73                             let path = &poly_trait_ref.trait_ref.path;
74                             hint_string.push_str(&format!(
75                                 " {} +",
76                                 snippet_with_applicability(cx, path.span, "..", &mut applicability)
77                             ));
78                         }
79                     }
80                     for b in p.bounds.iter() {
81                         if let GenericBound::Trait(ref poly_trait_ref, _) = b {
82                             let path = &poly_trait_ref.trait_ref.path;
83                             hint_string.push_str(&format!(
84                                 " {} +",
85                                 snippet_with_applicability(cx, path.span, "..", &mut applicability)
86                             ));
87                         }
88                     }
89                     hint_string.truncate(hint_string.len() - 2);
90                     hint_string.push('`');
91                     span_lint_and_help(
92                         cx,
93                         TYPE_REPETITION_IN_BOUNDS,
94                         p.span,
95                         "this type has already been used as a bound predicate",
96                         None,
97                         &hint_string,
98                     );
99                 }
100             }
101         }
102     }
103 }