about summary refs log tree commit diff
path: root/clippy_lints/src/unit_hash.rs
blob: dcf8a9d7c84d39416926d2e4f7f30dd4aa1bb3b3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;

declare_clippy_lint! {
    /// ### What it does
    /// Detects `().hash(_)`.
    ///
    /// ### Why is this bad?
    /// Hashing a unit value doesn't do anything as the implementation of `Hash` for `()` is a no-op.
    ///
    /// ### Example
    /// ```rust
    /// # use std::hash::Hash;
    /// # use std::collections::hash_map::DefaultHasher;
    /// # enum Foo { Empty, WithValue(u8) }
    /// # use Foo::*;
    /// # let mut state = DefaultHasher::new();
    /// # let my_enum = Foo::Empty;
    /// match my_enum {
    /// 	Empty => ().hash(&mut state),
    /// 	WithValue(x) => x.hash(&mut state),
    /// }
    /// ```
    /// Use instead:
    /// ```rust
    /// # use std::hash::Hash;
    /// # use std::collections::hash_map::DefaultHasher;
    /// # enum Foo { Empty, WithValue(u8) }
    /// # use Foo::*;
    /// # let mut state = DefaultHasher::new();
    /// # let my_enum = Foo::Empty;
    /// match my_enum {
    /// 	Empty => 0_u8.hash(&mut state),
    /// 	WithValue(x) => x.hash(&mut state),
    /// }
    /// ```
    #[clippy::version = "1.58.0"]
    pub UNIT_HASH,
    correctness,
    "hashing a unit value, which does nothing"
}
declare_lint_pass!(UnitHash => [UNIT_HASH]);

impl<'tcx> LateLintPass<'tcx> for UnitHash {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
        if_chain! {
            if let ExprKind::MethodCall(name_ident, _, args, _) = &expr.kind;
            if name_ident.ident.name == sym::hash;
            if let [recv, state_param] = args;
            if cx.typeck_results().expr_ty(recv).is_unit();
            then {
                span_lint_and_then(
                    cx,
                    UNIT_HASH,
                    expr.span,
                    "this call to `hash` on the unit type will do nothing",
                    |diag| {
                        diag.span_suggestion(
                            expr.span,
                            "remove the call to `hash` or consider using",
                            format!(
                                "0_u8.hash({})",
                                snippet(cx, state_param.span, ".."),
                            ),
                            Applicability::MaybeIncorrect,
                        );
                        diag.note("the implementation of `Hash` for `()` is a no-op");
                    }
                );
            }
        }
    }
}