]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
New upstream version 1.53.0+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / await_holding_invalid.rs
CommitLineData
cdc7bbd5
XL
1use clippy_utils::diagnostics::span_lint_and_note;
2use clippy_utils::{match_def_path, paths};
f20569fa
XL
3use rustc_hir::def_id::DefId;
4use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind};
5use rustc_lint::{LateContext, LateLintPass};
6use rustc_middle::ty::GeneratorInteriorTypeCause;
7use rustc_session::{declare_lint_pass, declare_tool_lint};
8use rustc_span::Span;
9
10declare_clippy_lint! {
11 /// **What it does:** Checks for calls to await while holding a
12 /// non-async-aware MutexGuard.
13 ///
14 /// **Why is this bad?** The Mutex types found in std::sync and parking_lot
15 /// are not designed to operate in an async context across await points.
16 ///
17 /// There are two potential solutions. One is to use an asynx-aware Mutex
18 /// type. Many asynchronous foundation crates provide such a Mutex type. The
19 /// other solution is to ensure the mutex is unlocked before calling await,
20 /// either by introducing a scope or an explicit call to Drop::drop.
21 ///
22 /// **Known problems:** Will report false positive for explicitly dropped guards ([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)).
23 ///
24 /// **Example:**
25 ///
26 /// ```rust,ignore
27 /// use std::sync::Mutex;
28 ///
29 /// async fn foo(x: &Mutex<u32>) {
30 /// let guard = x.lock().unwrap();
31 /// *guard += 1;
32 /// bar.await;
33 /// }
34 /// ```
35 ///
36 /// Use instead:
37 /// ```rust,ignore
38 /// use std::sync::Mutex;
39 ///
40 /// async fn foo(x: &Mutex<u32>) {
41 /// {
42 /// let guard = x.lock().unwrap();
43 /// *guard += 1;
44 /// }
45 /// bar.await;
46 /// }
47 /// ```
48 pub AWAIT_HOLDING_LOCK,
49 pedantic,
50 "Inside an async function, holding a MutexGuard while calling await"
51}
52
53declare_clippy_lint! {
54 /// **What it does:** Checks for calls to await while holding a
55 /// `RefCell` `Ref` or `RefMut`.
56 ///
57 /// **Why is this bad?** `RefCell` refs only check for exclusive mutable access
58 /// at runtime. Holding onto a `RefCell` ref across an `await` suspension point
59 /// risks panics from a mutable ref shared while other refs are outstanding.
60 ///
61 /// **Known problems:** Will report false positive for explicitly dropped refs ([#6353](https://github.com/rust-lang/rust-clippy/issues/6353)).
62 ///
63 /// **Example:**
64 ///
65 /// ```rust,ignore
66 /// use std::cell::RefCell;
67 ///
68 /// async fn foo(x: &RefCell<u32>) {
69 /// let mut y = x.borrow_mut();
70 /// *y += 1;
71 /// bar.await;
72 /// }
73 /// ```
74 ///
75 /// Use instead:
76 /// ```rust,ignore
77 /// use std::cell::RefCell;
78 ///
79 /// async fn foo(x: &RefCell<u32>) {
80 /// {
81 /// let mut y = x.borrow_mut();
82 /// *y += 1;
83 /// }
84 /// bar.await;
85 /// }
86 /// ```
87 pub AWAIT_HOLDING_REFCELL_REF,
88 pedantic,
89 "Inside an async function, holding a RefCell ref while calling await"
90}
91
92declare_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF]);
93
94impl LateLintPass<'_> for AwaitHolding {
95 fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
96 use AsyncGeneratorKind::{Block, Closure, Fn};
97 if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind {
98 let body_id = BodyId {
99 hir_id: body.value.hir_id,
100 };
101 let typeck_results = cx.tcx.typeck_body(body_id);
102 check_interior_types(
103 cx,
cdc7bbd5 104 typeck_results.generator_interior_types.as_ref().skip_binder(),
f20569fa
XL
105 body.value.span,
106 );
107 }
108 }
109}
110
111fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) {
112 for ty_cause in ty_causes {
113 if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() {
114 if is_mutex_guard(cx, adt.did) {
115 span_lint_and_note(
116 cx,
117 AWAIT_HOLDING_LOCK,
118 ty_cause.span,
119 "this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await",
120 ty_cause.scope_span.or(Some(span)),
121 "these are all the await points this lock is held through",
122 );
123 }
124 if is_refcell_ref(cx, adt.did) {
125 span_lint_and_note(
126 cx,
127 AWAIT_HOLDING_REFCELL_REF,
128 ty_cause.span,
129 "this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await",
130 ty_cause.scope_span.or(Some(span)),
131 "these are all the await points this ref is held through",
132 );
133 }
134 }
135 }
136}
137
138fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool {
139 match_def_path(cx, def_id, &paths::MUTEX_GUARD)
140 || match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD)
141 || match_def_path(cx, def_id, &paths::RWLOCK_WRITE_GUARD)
142 || match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD)
143 || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD)
144 || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD)
145}
146
147fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool {
148 match_def_path(cx, def_id, &paths::REFCELL_REF) || match_def_path(cx, def_id, &paths::REFCELL_REFMUT)
149}