Skip to content

Commit 86bd596

Browse files
committed
Make DERIVE_HASH_NOT_EQ symmetric
1 parent cd35b9e commit 86bd596

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
@@ -31,7 +31,7 @@ use utils::{match_path, span_lint_and_then};
3131
/// }
3232
/// ```
3333
declare_lint! {
34-
pub DERIVE_HASH_NOT_EQ,
34+
pub DERIVE_HASH_XOR_EQ,
3535
Warn,
3636
"deriving `Hash` but implementing `PartialEq` explicitly"
3737
}
@@ -64,7 +64,7 @@ pub struct Derive;
6464

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

@@ -76,19 +76,25 @@ impl LateLintPass for Derive {
7676
let ItemImpl(_, _, ref ast_generics, Some(ref trait_ref), ref ast_ty, _) = item.node,
7777
let Some(&ty) = ast_ty_to_ty_cache.get(&ast_ty.id)
7878
], {
79-
if item.attrs.iter().any(is_automatically_derived) {
80-
check_hash_peq(cx, item.span, trait_ref, ty);
81-
}
82-
else if !ast_generics.is_lt_parameterized() {
79+
let is_automatically_derived = item.attrs.iter().any(is_automatically_derived);
80+
81+
check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);
82+
83+
if !is_automatically_derived && !ast_generics.is_lt_parameterized() {
8384
check_copy_clone(cx, item, trait_ref, ty);
8485
}
8586
}}
8687
}
8788
}
8889

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

109121
// Only care about `impl PartialEq<Foo> for Foo`
110-
if trait_ref.input_types()[0] == ty &&
111-
!cx.tcx.get_attrs(impl_id).iter().any(is_automatically_derived) {
122+
if trait_ref.input_types()[0] == ty {
123+
let mess = if peq_is_automatically_derived {
124+
"you are implementing `Hash` explicitly but have derived `PartialEq`"
125+
} else {
126+
"you are deriving `Hash` but have implemented `PartialEq` explicitly"
127+
};
128+
112129
span_lint_and_then(
113-
cx, DERIVE_HASH_NOT_EQ, span,
114-
"you are deriving `Hash` but have implemented `PartialEq` explicitly",
130+
cx, DERIVE_HASH_XOR_EQ, span,
131+
mess,
115132
|db| {
116133
if let Some(node_id) = cx.tcx.map.as_local_node_id(impl_id) {
117134
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)