1 use clippy_utils
::diagnostics
::span_lint_and_note
;
2 use clippy_utils
::{match_def_path, paths}
;
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}
;
10 declare_clippy_lint
! {
12 /// Checks for calls to await while holding a
13 /// non-async-aware MutexGuard.
15 /// ### Why is this bad?
16 /// The Mutex types found in std::sync and parking_lot
17 /// are not designed to operate in an async context across await points.
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.
24 /// ### Known problems
25 /// Will report false positive for explicitly dropped guards ([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)).
29 /// use std::sync::Mutex;
31 /// async fn foo(x: &Mutex<u32>) {
32 /// let guard = x.lock().unwrap();
40 /// use std::sync::Mutex;
42 /// async fn foo(x: &Mutex<u32>) {
44 /// let guard = x.lock().unwrap();
50 pub AWAIT_HOLDING_LOCK
,
52 "Inside an async function, holding a MutexGuard while calling await"
55 declare_clippy_lint
! {
57 /// Checks for calls to await while holding a
58 /// `RefCell` `Ref` or `RefMut`.
60 /// ### Why is this bad?
61 /// `RefCell` refs only check for exclusive mutable access
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.
65 /// ### Known problems
66 /// Will report false positive for explicitly dropped refs ([#6353](https://github.com/rust-lang/rust-clippy/issues/6353)).
70 /// use std::cell::RefCell;
72 /// async fn foo(x: &RefCell<u32>) {
73 /// let mut y = x.borrow_mut();
81 /// use std::cell::RefCell;
83 /// async fn foo(x: &RefCell<u32>) {
85 /// let mut y = x.borrow_mut();
91 pub AWAIT_HOLDING_REFCELL_REF
,
93 "Inside an async function, holding a RefCell ref while calling await"
96 declare_lint_pass
!(AwaitHolding
=> [AWAIT_HOLDING_LOCK
, AWAIT_HOLDING_REFCELL_REF
]);
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
,
105 let typeck_results
= cx
.tcx
.typeck_body(body_id
);
106 check_interior_types(
108 typeck_results
.generator_interior_types
.as_ref().skip_binder(),
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
) {
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",
128 if is_refcell_ref(cx
, adt
.did
) {
131 AWAIT_HOLDING_REFCELL_REF
,
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",
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
)
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
)