summaryrefslogtreecommitdiffstats
path: root/src/tools/clippy/clippy_lints/src/let_underscore.rs
blob: 61f87b91400d76405f8fcb914066040f49a9b4ff (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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type};
use clippy_utils::{is_must_use_func_call, paths};
use rustc_hir::{Local, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_session::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
    /// ### What it does
    /// Checks for `let _ = <expr>` where expr is `#[must_use]`
    ///
    /// ### Why is this bad?
    /// It's better to explicitly handle the value of a `#[must_use]`
    /// expr
    ///
    /// ### Example
    /// ```rust
    /// fn f() -> Result<u32, u32> {
    ///     Ok(0)
    /// }
    ///
    /// let _ = f();
    /// // is_ok() is marked #[must_use]
    /// let _ = f().is_ok();
    /// ```
    #[clippy::version = "1.42.0"]
    pub LET_UNDERSCORE_MUST_USE,
    restriction,
    "non-binding `let` on a `#[must_use]` expression"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for `let _ = sync_lock`. This supports `mutex` and `rwlock` in
    /// `parking_lot`. For `std` locks see the `rustc` lint
    /// [`let_underscore_lock`](https://doc.rust-lang.org/nightly/rustc/lints/listing/deny-by-default.html#let-underscore-lock)
    ///
    /// ### Why is this bad?
    /// This statement immediately drops the lock instead of
    /// extending its lifetime to the end of the scope, which is often not intended.
    /// To extend lock lifetime to the end of the scope, use an underscore-prefixed
    /// name instead (i.e. _lock). If you want to explicitly drop the lock,
    /// `std::mem::drop` conveys your intention better and is less error-prone.
    ///
    /// ### Example
    /// ```rust,ignore
    /// let _ = mutex.lock();
    /// ```
    ///
    /// Use instead:
    /// ```rust,ignore
    /// let _lock = mutex.lock();
    /// ```
    #[clippy::version = "1.43.0"]
    pub LET_UNDERSCORE_LOCK,
    correctness,
    "non-binding `let` on a synchronization lock"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for `let _ = <expr>` where the resulting type of expr implements `Future`
    ///
    /// ### Why is this bad?
    /// Futures must be polled for work to be done. The original intention was most likely to await the future
    /// and ignore the resulting value.
    ///
    /// ### Example
    /// ```rust
    /// async fn foo() -> Result<(), ()> {
    ///     Ok(())
    /// }
    /// let _ = foo();
    /// ```
    ///
    /// Use instead:
    /// ```rust
    /// # async fn context() {
    /// async fn foo() -> Result<(), ()> {
    ///     Ok(())
    /// }
    /// let _ = foo().await;
    /// # }
    /// ```
    #[clippy::version = "1.66"]
    pub LET_UNDERSCORE_FUTURE,
    suspicious,
    "non-binding `let` on a future"
}

declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_FUTURE]);

const SYNC_GUARD_PATHS: [&[&str]; 3] = [
    &paths::PARKING_LOT_MUTEX_GUARD,
    &paths::PARKING_LOT_RWLOCK_READ_GUARD,
    &paths::PARKING_LOT_RWLOCK_WRITE_GUARD,
];

impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
    fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) {
        if !in_external_macro(cx.tcx.sess, local.span)
            && let PatKind::Wild = local.pat.kind
            && let Some(init) = local.init
        {
            let init_ty = cx.typeck_results().expr_ty(init);
            let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() {
                GenericArgKind::Type(inner_ty) => SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)),
                GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
            });
            if contains_sync_guard {
                span_lint_and_help(
                    cx,
                    LET_UNDERSCORE_LOCK,
                    local.span,
                    "non-binding `let` on a synchronization lock",
                    None,
                    "consider using an underscore-prefixed named \
                            binding or dropping explicitly with `std::mem::drop`",
                );
            } else if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait()
                && implements_trait(cx, cx.typeck_results().expr_ty(init), future_trait_def_id, &[]) {
                span_lint_and_help(
                    cx,
                    LET_UNDERSCORE_FUTURE,
                    local.span,
                    "non-binding `let` on a future",
                    None,
                    "consider awaiting the future or dropping explicitly with `std::mem::drop`"
                );
            } else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) {
                span_lint_and_help(
                    cx,
                    LET_UNDERSCORE_MUST_USE,
                    local.span,
                    "non-binding `let` on an expression with `#[must_use]` type",
                    None,
                    "consider explicitly using expression value",
                );
            } else if is_must_use_func_call(cx, init) {
                span_lint_and_help(
                    cx,
                    LET_UNDERSCORE_MUST_USE,
                    local.span,
                    "non-binding `let` on a result of a `#[must_use]` function",
                    None,
                    "consider explicitly using function result",
                );
            }
        }
    }
}