]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/derive.rs
Fix false positive with cast_sign_loss lint
[rust.git] / clippy_lints / src / derive.rs
1 use crate::utils::paths;
2 use crate::utils::{is_automatically_derived, is_copy, match_path, span_lint_and_then};
3 use if_chain::if_chain;
4 use rustc::declare_lint_pass;
5 use rustc::hir::*;
6 use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
7 use rustc::ty::{self, Ty};
8 use rustc_session::declare_tool_lint;
9 use syntax::source_map::Span;
10
11 declare_clippy_lint! {
12     /// **What it does:** Checks for deriving `Hash` but implementing `PartialEq`
13     /// explicitly or vice versa.
14     ///
15     /// **Why is this bad?** The implementation of these traits must agree (for
16     /// example for use with `HashMap`) so it’s probably a bad idea to use a
17     /// default-generated `Hash` implementation with an explicitly defined
18     /// `PartialEq`. In particular, the following must hold for any type:
19     ///
20     /// ```text
21     /// k1 == k2 ⇒ hash(k1) == hash(k2)
22     /// ```
23     ///
24     /// **Known problems:** None.
25     ///
26     /// **Example:**
27     /// ```ignore
28     /// #[derive(Hash)]
29     /// struct Foo;
30     ///
31     /// impl PartialEq for Foo {
32     ///     ...
33     /// }
34     /// ```
35     pub DERIVE_HASH_XOR_EQ,
36     correctness,
37     "deriving `Hash` but implementing `PartialEq` explicitly"
38 }
39
40 declare_clippy_lint! {
41     /// **What it does:** Checks for explicit `Clone` implementations for `Copy`
42     /// types.
43     ///
44     /// **Why is this bad?** To avoid surprising behaviour, these traits should
45     /// agree and the behaviour of `Copy` cannot be overridden. In almost all
46     /// situations a `Copy` type should have a `Clone` implementation that does
47     /// nothing more than copy the object, which is what `#[derive(Copy, Clone)]`
48     /// gets you.
49     ///
50     /// **Known problems:** Bounds of generic types are sometimes wrong: https://github.com/rust-lang/rust/issues/26925
51     ///
52     /// **Example:**
53     /// ```rust,ignore
54     /// #[derive(Copy)]
55     /// struct Foo;
56     ///
57     /// impl Clone for Foo {
58     ///     // ..
59     /// }
60     /// ```
61     pub EXPL_IMPL_CLONE_ON_COPY,
62     pedantic,
63     "implementing `Clone` explicitly on `Copy` types"
64 }
65
66 declare_lint_pass!(Derive => [EXPL_IMPL_CLONE_ON_COPY, DERIVE_HASH_XOR_EQ]);
67
68 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Derive {
69     fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
70         if let ItemKind::Impl(_, _, _, _, Some(ref trait_ref), _, _) = item.kind {
71             let ty = cx.tcx.type_of(cx.tcx.hir().local_def_id(item.hir_id));
72             let is_automatically_derived = is_automatically_derived(&*item.attrs);
73
74             check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);
75
76             if !is_automatically_derived {
77                 check_copy_clone(cx, item, trait_ref, ty);
78             }
79         }
80     }
81 }
82
83 /// Implementation of the `DERIVE_HASH_XOR_EQ` lint.
84 fn check_hash_peq<'a, 'tcx>(
85     cx: &LateContext<'a, 'tcx>,
86     span: Span,
87     trait_ref: &TraitRef,
88     ty: Ty<'tcx>,
89     hash_is_automatically_derived: bool,
90 ) {
91     if_chain! {
92         if match_path(&trait_ref.path, &paths::HASH);
93         if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait();
94         if !&trait_ref.trait_def_id().is_local();
95         then {
96             // Look for the PartialEq implementations for `ty`
97             cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| {
98                 let peq_is_automatically_derived = is_automatically_derived(&cx.tcx.get_attrs(impl_id));
99
100                 if peq_is_automatically_derived == hash_is_automatically_derived {
101                     return;
102                 }
103
104                 let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
105
106                 // Only care about `impl PartialEq<Foo> for Foo`
107                 // For `impl PartialEq<B> for A, input_types is [A, B]
108                 if trait_ref.substs.type_at(1) == ty {
109                     let mess = if peq_is_automatically_derived {
110                         "you are implementing `Hash` explicitly but have derived `PartialEq`"
111                     } else {
112                         "you are deriving `Hash` but have implemented `PartialEq` explicitly"
113                     };
114
115                     span_lint_and_then(
116                         cx, DERIVE_HASH_XOR_EQ, span,
117                         mess,
118                         |db| {
119                         if let Some(node_id) = cx.tcx.hir().as_local_hir_id(impl_id) {
120                             db.span_note(
121                                 cx.tcx.hir().span(node_id),
122                                 "`PartialEq` implemented here"
123                             );
124                         }
125                     });
126                 }
127             });
128         }
129     }
130 }
131
132 /// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
133 fn check_copy_clone<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, item: &Item, trait_ref: &TraitRef, ty: Ty<'tcx>) {
134     if match_path(&trait_ref.path, &paths::CLONE_TRAIT) {
135         if !is_copy(cx, ty) {
136             return;
137         }
138
139         match ty.kind {
140             ty::Adt(def, _) if def.is_union() => return,
141
142             // Some types are not Clone by default but could be cloned “by hand” if necessary
143             ty::Adt(def, substs) => {
144                 for variant in &def.variants {
145                     for field in &variant.fields {
146                         if let ty::FnDef(..) = field.ty(cx.tcx, substs).kind {
147                             return;
148                         }
149                     }
150                     for subst in substs {
151                         if let ty::subst::GenericArgKind::Type(subst) = subst.unpack() {
152                             if let ty::Param(_) = subst.kind {
153                                 return;
154                             }
155                         }
156                     }
157                 }
158             },
159             _ => (),
160         }
161
162         span_lint_and_then(
163             cx,
164             EXPL_IMPL_CLONE_ON_COPY,
165             item.span,
166             "you are implementing `Clone` explicitly on a `Copy` type",
167             |db| {
168                 db.span_note(item.span, "consider deriving `Clone` or removing `Copy`");
169             },
170         );
171     }
172 }