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