Skip to content

Commit 36d4a4e

Browse files
committed
Add unit-hash lint
1 parent ba0cfc7 commit 36d4a4e

File tree

8 files changed

+137
-0
lines changed

8 files changed

+137
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3046,6 +3046,7 @@ Released 2018-09-13
30463046
[`uninit_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_vec
30473047
[`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg
30483048
[`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp
3049+
[`unit_hash`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_hash
30493050
[`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord
30503051
[`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
30513052
[`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map

clippy_lints/src/lib.register_all.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
282282
LintId::of(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
283283
LintId::of(unicode::INVISIBLE_CHARACTERS),
284284
LintId::of(uninit_vec::UNINIT_VEC),
285+
LintId::of(unit_hash::UNIT_HASH),
285286
LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
286287
LintId::of(unit_types::UNIT_ARG),
287288
LintId::of(unit_types::UNIT_CMP),

clippy_lints/src/lib.register_correctness.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve
6464
LintId::of(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
6565
LintId::of(unicode::INVISIBLE_CHARACTERS),
6666
LintId::of(uninit_vec::UNINIT_VEC),
67+
LintId::of(unit_hash::UNIT_HASH),
6768
LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
6869
LintId::of(unit_types::UNIT_CMP),
6970
LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),

clippy_lints/src/lib.register_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,7 @@ store.register_lints(&[
476476
unicode::NON_ASCII_LITERAL,
477477
unicode::UNICODE_NOT_NFC,
478478
uninit_vec::UNINIT_VEC,
479+
unit_hash::UNIT_HASH,
479480
unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD,
480481
unit_types::LET_UNIT_VALUE,
481482
unit_types::UNIT_ARG,

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,7 @@ mod undocumented_unsafe_blocks;
364364
mod undropped_manually_drops;
365365
mod unicode;
366366
mod uninit_vec;
367+
mod unit_hash;
367368
mod unit_return_expecting_ord;
368369
mod unit_types;
369370
mod unnamed_address;
@@ -522,6 +523,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
522523
store.register_late_pass(|| Box::new(collapsible_match::CollapsibleMatch));
523524
store.register_late_pass(|| Box::new(unicode::Unicode));
524525
store.register_late_pass(|| Box::new(uninit_vec::UninitVec));
526+
store.register_late_pass(|| Box::new(unit_hash::UnitHash));
525527
store.register_late_pass(|| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd));
526528
store.register_late_pass(|| Box::new(strings::StringAdd));
527529
store.register_late_pass(|| Box::new(implicit_return::ImplicitReturn));

clippy_lints/src/unit_hash.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
use clippy_utils::diagnostics::span_lint_and_then;
2+
use clippy_utils::source::snippet;
3+
use rustc_errors::Applicability;
4+
use rustc_hir::{Expr, ExprKind};
5+
use rustc_lint::{LateContext, LateLintPass};
6+
use rustc_session::{declare_lint_pass, declare_tool_lint};
7+
use rustc_span::sym;
8+
9+
declare_clippy_lint! {
10+
/// ### What it does
11+
/// Detects `().hash(_)`.
12+
///
13+
/// ### Why is this bad?
14+
/// Hashing a unit value doesn't do anything as the implementation of `Hash` for `()` is a no-op.
15+
///
16+
/// ### Example
17+
/// ```rust
18+
/// # use std::hash::Hash;
19+
/// # use std::collections::hash_map::DefaultHasher;
20+
/// # enum Foo { Empty, WithValue(u8) }
21+
/// # use Foo::*;
22+
/// # let mut state = DefaultHasher::new();
23+
/// # let my_enum = Foo::Empty;
24+
/// match my_enum {
25+
/// Empty => ().hash(&mut state),
26+
/// WithValue(x) => x.hash(&mut state),
27+
/// }
28+
/// ```
29+
/// Use instead:
30+
/// ```rust
31+
/// # use std::hash::Hash;
32+
/// # use std::collections::hash_map::DefaultHasher;
33+
/// # enum Foo { Empty, WithValue(u8) }
34+
/// # use Foo::*;
35+
/// # let mut state = DefaultHasher::new();
36+
/// # let my_enum = Foo::Empty;
37+
/// match my_enum {
38+
/// Empty => 0_u8.hash(&mut state),
39+
/// WithValue(x) => x.hash(&mut state),
40+
/// }
41+
/// ```
42+
pub UNIT_HASH,
43+
correctness,
44+
"hashing a unit value, which does nothing"
45+
}
46+
declare_lint_pass!(UnitHash => [UNIT_HASH]);
47+
48+
impl LateLintPass<'tcx> for UnitHash {
49+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
50+
if_chain! {
51+
if let ExprKind::MethodCall(name_ident, _, args, _) = &expr.kind;
52+
if name_ident.ident.name == sym::hash;
53+
if let [recv, state_param] = args;
54+
if cx.typeck_results().expr_ty(recv).is_unit();
55+
then {
56+
span_lint_and_then(
57+
cx,
58+
UNIT_HASH,
59+
expr.span,
60+
"this call to `hash` on the unit type will do nothing",
61+
|diag| {
62+
diag.span_suggestion(
63+
expr.span,
64+
"remove the call to `hash` or consider using",
65+
format!(
66+
"0_u8.hash({})",
67+
snippet(cx, state_param.span, ".."),
68+
),
69+
Applicability::MaybeIncorrect,
70+
);
71+
diag.note("the implementation of `Hash` for `()` is a no-op");
72+
}
73+
);
74+
}
75+
}
76+
}
77+
}

tests/ui/unit_hash.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#![warn(clippy::unit_hash)]
2+
3+
use std::collections::hash_map::DefaultHasher;
4+
use std::hash::Hash;
5+
6+
enum Foo {
7+
Empty,
8+
WithValue(u8),
9+
}
10+
11+
fn do_nothing() {}
12+
13+
fn main() {
14+
let mut state = DefaultHasher::new();
15+
let my_enum = Foo::Empty;
16+
17+
match my_enum {
18+
Foo::Empty => ().hash(&mut state),
19+
Foo::WithValue(x) => x.hash(&mut state),
20+
}
21+
22+
let res = ();
23+
res.hash(&mut state);
24+
25+
#[allow(clippy::unit_arg)]
26+
do_nothing().hash(&mut state);
27+
}

tests/ui/unit_hash.stderr

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error: this call to `hash` on the unit type will do nothing
2+
--> $DIR/unit_hash.rs:18:23
3+
|
4+
LL | Foo::Empty => ().hash(&mut state),
5+
| ^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)`
6+
|
7+
= note: `-D clippy::unit-hash` implied by `-D warnings`
8+
= note: the implementation of `Hash` for `()` is a no-op
9+
10+
error: this call to `hash` on the unit type will do nothing
11+
--> $DIR/unit_hash.rs:23:5
12+
|
13+
LL | res.hash(&mut state);
14+
| ^^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)`
15+
|
16+
= note: the implementation of `Hash` for `()` is a no-op
17+
18+
error: this call to `hash` on the unit type will do nothing
19+
--> $DIR/unit_hash.rs:26:5
20+
|
21+
LL | do_nothing().hash(&mut state);
22+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)`
23+
|
24+
= note: the implementation of `Hash` for `()` is a no-op
25+
26+
error: aborting due to 3 previous errors
27+

0 commit comments

Comments
 (0)