]>
Commit | Line | Data |
---|---|---|
cdc7bbd5 XL |
1 | use clippy_utils::diagnostics::span_lint_and_note; |
2 | use clippy_utils::{match_def_path, paths}; | |
f20569fa XL |
3 | use rustc_hir::def_id::DefId; |
4 | use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; | |
5 | use rustc_lint::{LateContext, LateLintPass}; | |
6 | use rustc_middle::ty::GeneratorInteriorTypeCause; | |
7 | use rustc_session::{declare_lint_pass, declare_tool_lint}; | |
8 | use rustc_span::Span; | |
9 | ||
10 | declare_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 | ||
55 | declare_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 | ||
96 | declare_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF]); | |
97 | ||
98 | impl 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 | ||
115 | fn 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 | ||
142 | fn 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 | ||
151 | fn 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 | } |