2 use rustc
::ty
::{self, Ty}
;
4 use syntax
::codemap
::Span
;
6 use utils
::{is_automatically_derived, is_copy, match_path, span_lint_and_then}
;
8 /// **What it does:** Checks for deriving `Hash` but implementing `PartialEq`
11 /// **Why is this bad?** The implementation of these traits must agree (for
12 /// example for use with `HashMap`) so it’s probably a bad idea to use a
13 /// default-generated `Hash` implementation with an explicitly defined
14 /// `PartialEq`. In particular, the following must hold for any type:
17 /// k1 == k2 ⇒ hash(k1) == hash(k2)
20 /// **Known problems:** None.
27 /// impl PartialEq for Foo {
32 pub DERIVE_HASH_XOR_EQ
,
34 "deriving `Hash` but implementing `PartialEq` explicitly"
37 /// **What it does:** Checks for explicit `Clone` implementations for `Copy`
40 /// **Why is this bad?** To avoid surprising behaviour, these traits should
41 /// agree and the behaviour of `Copy` cannot be overridden. In almost all
42 /// situations a `Copy` type should have a `Clone` implementation that does
43 /// nothing more than copy the object, which is what `#[derive(Copy, Clone)]`
46 /// **Known problems:** None.
53 /// impl Clone for Foo {
58 pub EXPL_IMPL_CLONE_ON_COPY
,
60 "implementing `Clone` explicitly on `Copy` types"
65 impl LintPass
for Derive
{
66 fn get_lints(&self) -> LintArray
{
67 lint_array
!(EXPL_IMPL_CLONE_ON_COPY
, DERIVE_HASH_XOR_EQ
)
71 impl<'a
, 'tcx
> LateLintPass
<'a
, 'tcx
> for Derive
{
72 fn check_item(&mut self, cx
: &LateContext
<'a
, 'tcx
>, item
: &'tcx Item
) {
73 if let ItemImpl(_
, _
, _
, _
, Some(ref trait_ref
), _
, _
) = item
.node
{
74 let ty
= cx
.tcx
.type_of(cx
.tcx
.hir
.local_def_id(item
.id
));
75 let is_automatically_derived
= is_automatically_derived(&*item
.attrs
);
77 check_hash_peq(cx
, item
.span
, trait_ref
, ty
, is_automatically_derived
);
79 if !is_automatically_derived
{
80 check_copy_clone(cx
, item
, trait_ref
, ty
);
86 /// Implementation of the `DERIVE_HASH_XOR_EQ` lint.
87 fn check_hash_peq
<'a
, 'tcx
>(
88 cx
: &LateContext
<'a
, 'tcx
>,
92 hash_is_automatically_derived
: bool
,
95 if match_path(&trait_ref
.path
, &paths
::HASH
);
96 if let Some(peq_trait_def_id
) = cx
.tcx
.lang_items().eq_trait();
98 // Look for the PartialEq implementations for `ty`
99 cx
.tcx
.for_each_relevant_impl(peq_trait_def_id
, ty
, |impl_id
| {
100 let peq_is_automatically_derived
= is_automatically_derived(&cx
.tcx
.get_attrs(impl_id
));
102 if peq_is_automatically_derived
== hash_is_automatically_derived
{
106 let trait_ref
= cx
.tcx
.impl_trait_ref(impl_id
).expect("must be a trait implementation");
108 // Only care about `impl PartialEq<Foo> for Foo`
109 // For `impl PartialEq<B> for A, input_types is [A, B]
110 if trait_ref
.substs
.type_at(1) == ty
{
111 let mess
= if peq_is_automatically_derived
{
112 "you are implementing `Hash` explicitly but have derived `PartialEq`"
114 "you are deriving `Hash` but have implemented `PartialEq` explicitly"
118 cx
, DERIVE_HASH_XOR_EQ
, span
,
121 if let Some(node_id
) = cx
.tcx
.hir
.as_local_node_id(impl_id
) {
123 cx
.tcx
.hir
.span(node_id
),
124 "`PartialEq` implemented here"
134 /// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
135 fn check_copy_clone
<'a
, 'tcx
>(cx
: &LateContext
<'a
, 'tcx
>, item
: &Item
, trait_ref
: &TraitRef
, ty
: Ty
<'tcx
>) {
136 if match_path(&trait_ref
.path
, &paths
::CLONE_TRAIT
) {
137 if !is_copy(cx
, ty
) {
142 ty
::TyAdt(def
, _
) if def
.is_union() => return,
144 // Some types are not Clone by default but could be cloned “by hand” if necessary
145 ty
::TyAdt(def
, substs
) => for variant
in &def
.variants
{
146 for field
in &variant
.fields
{
147 if let ty
::TyFnDef(..) = field
.ty(cx
.tcx
, substs
).sty
{
157 EXPL_IMPL_CLONE_ON_COPY
,
159 "you are implementing `Clone` explicitly on a `Copy` type",
161 db
.span_note(item
.span
, "consider deriving `Clone` or removing `Copy`");