Skip to content

Commit b5ba621

Browse files
committed
Make DERIVE_HASH_NOT_EQ symmetric
1 parent 2641c4e commit b5ba621

File tree

4 files changed

+42
-15
lines changed

4 files changed

+42
-15
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ name
3333
[collapsible_if](https://github.com/Manishearth/rust-clippy/wiki#collapsible_if) | warn | two nested `if`-expressions can be collapsed into one, e.g. `if x { if y { foo() } }` can be written as `if x && y { foo() }` and an `else { if .. } expression can be collapsed to `else if`
3434
[cyclomatic_complexity](https://github.com/Manishearth/rust-clippy/wiki#cyclomatic_complexity) | warn | finds functions that should be split up into multiple functions
3535
[deprecated_semver](https://github.com/Manishearth/rust-clippy/wiki#deprecated_semver) | warn | `Warn` on `#[deprecated(since = "x")]` where x is not semver
36-
[derive_hash_not_eq](https://github.com/Manishearth/rust-clippy/wiki#derive_hash_not_eq) | warn | deriving `Hash` but implementing `PartialEq` explicitly
36+
[derive_hash_xor_eq](https://github.com/Manishearth/rust-clippy/wiki#derive_hash_xor_eq) | warn | deriving `Hash` but implementing `PartialEq` explicitly
3737
[drop_ref](https://github.com/Manishearth/rust-clippy/wiki#drop_ref) | warn | call to `std::mem::drop` with a reference instead of an owned value, which will not call the `Drop::drop` method on the underlying value
3838
[duplicate_underscore_argument](https://github.com/Manishearth/rust-clippy/wiki#duplicate_underscore_argument) | warn | Function arguments having names which only differ by an underscore
3939
[empty_loop](https://github.com/Manishearth/rust-clippy/wiki#empty_loop) | warn | empty `loop {}` detected

src/derive.rs

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use utils::{match_path, span_lint_and_then};
3232
/// }
3333
/// ```
3434
declare_lint! {
35-
pub DERIVE_HASH_NOT_EQ,
35+
pub DERIVE_HASH_XOR_EQ,
3636
Warn,
3737
"deriving `Hash` but implementing `PartialEq` explicitly"
3838
}
@@ -65,7 +65,7 @@ pub struct Derive;
6565

6666
impl LintPass for Derive {
6767
fn get_lints(&self) -> LintArray {
68-
lint_array!(EXPL_IMPL_CLONE_ON_COPY, DERIVE_HASH_NOT_EQ)
68+
lint_array!(EXPL_IMPL_CLONE_ON_COPY, DERIVE_HASH_XOR_EQ)
6969
}
7070
}
7171

@@ -75,19 +75,25 @@ impl LateLintPass for Derive {
7575
let ItemImpl(_, _, _, Some(ref trait_ref), _, _) = item.node
7676
], {
7777
let ty = cx.tcx.lookup_item_type(cx.tcx.map.local_def_id(item.id)).ty;
78-
if item.attrs.iter().any(is_automatically_derived) {
79-
check_hash_peq(cx, item.span, trait_ref, ty);
80-
}
81-
else {
78+
let is_automatically_derived = item.attrs.iter().any(is_automatically_derived);
79+
80+
check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);
81+
82+
if !is_automatically_derived {
8283
check_copy_clone(cx, item, trait_ref, ty);
8384
}
8485
}}
8586
}
8687
}
8788

88-
/// Implementation of the `DERIVE_HASH_NOT_EQ` lint.
89-
fn check_hash_peq(cx: &LateContext, span: Span, trait_ref: &TraitRef, ty: ty::Ty) {
90-
// If `item` is an automatically derived `Hash` implementation
89+
/// Implementation of the `DERIVE_HASH_XOR_EQ` lint.
90+
fn check_hash_peq(
91+
cx: &LateContext,
92+
span: Span,
93+
trait_ref: &TraitRef,
94+
ty: ty::Ty,
95+
hash_is_automatically_derived: bool
96+
) {
9197
if_let_chain! {[
9298
match_path(&trait_ref.path, &HASH_PATH),
9399
let Some(peq_trait_def_id) = cx.tcx.lang_items.eq_trait()
@@ -103,14 +109,25 @@ fn check_hash_peq(cx: &LateContext, span: Span, trait_ref: &TraitRef, ty: ty::Ty
103109
let Some(impl_ids) = peq_impls.get(&simpl_ty)
104110
], {
105111
for &impl_id in impl_ids {
112+
let peq_is_automatically_derived = cx.tcx.get_attrs(impl_id).iter().any(is_automatically_derived);
113+
114+
if peq_is_automatically_derived == hash_is_automatically_derived {
115+
return;
116+
}
117+
106118
let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
107119

108120
// Only care about `impl PartialEq<Foo> for Foo`
109-
if trait_ref.input_types()[0] == ty &&
110-
!cx.tcx.get_attrs(impl_id).iter().any(is_automatically_derived) {
121+
if trait_ref.input_types()[0] == ty {
122+
let mess = if peq_is_automatically_derived {
123+
"you are implementing `Hash` explicitly but have derived `PartialEq`"
124+
} else {
125+
"you are deriving `Hash` but have implemented `PartialEq` explicitly"
126+
};
127+
111128
span_lint_and_then(
112-
cx, DERIVE_HASH_NOT_EQ, span,
113-
"you are deriving `Hash` but have implemented `PartialEq` explicitly",
129+
cx, DERIVE_HASH_XOR_EQ, span,
130+
mess,
114131
|db| {
115132
if let Some(node_id) = cx.tcx.map.as_local_node_id(impl_id) {
116133
db.span_note(

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ pub fn plugin_registrar(reg: &mut Registry) {
198198
copies::IFS_SAME_COND,
199199
copies::MATCH_SAME_ARMS,
200200
cyclomatic_complexity::CYCLOMATIC_COMPLEXITY,
201-
derive::DERIVE_HASH_NOT_EQ,
201+
derive::DERIVE_HASH_XOR_EQ,
202202
derive::EXPL_IMPL_CLONE_ON_COPY,
203203
drop_ref::DROP_REF,
204204
entry::MAP_ENTRY,

tests/compile-fail/derive.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#![deny(warnings)]
55
#![allow(dead_code)]
66

7+
use std::hash::{Hash, Hasher};
8+
79
#[derive(PartialEq, Hash)]
810
struct Foo;
911

@@ -27,6 +29,14 @@ impl PartialEq<Baz> for Baz {
2729
fn eq(&self, _: &Baz) -> bool { true }
2830
}
2931

32+
#[derive(PartialEq)]
33+
struct Bah;
34+
35+
impl Hash for Bah {
36+
//~^ ERROR you are implementing `Hash` explicitly but have derived `PartialEq`
37+
fn hash<H: Hasher>(&self, _: &mut H) {}
38+
}
39+
3040
#[derive(Copy)]
3141
struct Qux;
3242

0 commit comments

Comments
 (0)