1 use crate::utils::{match_def_path, paths, span_lint_and_note};
2 use rustc_hir::def_id::DefId;
3 use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind};
4 use rustc_lint::{LateContext, LateLintPass};
5 use rustc_middle::ty::GeneratorInteriorTypeCause;
6 use rustc_session::{declare_lint_pass, declare_tool_lint};
10 /// **What it does:** Checks for calls to await while holding a
11 /// non-async-aware MutexGuard.
13 /// **Why is this bad?** The Mutex types found in std::sync and parking_lot
14 /// are not designed to operate in an async context across await points.
16 /// There are two potential solutions. One is to use an asynx-aware Mutex
17 /// type. Many asynchronous foundation crates provide such a Mutex type. The
18 /// other solution is to ensure the mutex is unlocked before calling await,
19 /// either by introducing a scope or an explicit call to Drop::drop.
21 /// **Known problems:** None.
26 /// use std::sync::Mutex;
28 /// async fn foo(x: &Mutex<u32>) {
29 /// let guard = x.lock().unwrap();
37 /// use std::sync::Mutex;
39 /// async fn foo(x: &Mutex<u32>) {
41 /// let guard = x.lock().unwrap();
47 pub AWAIT_HOLDING_LOCK,
49 "Inside an async function, holding a MutexGuard while calling await"
52 declare_lint_pass!(AwaitHoldingLock => [AWAIT_HOLDING_LOCK]);
54 impl LateLintPass<'_> for AwaitHoldingLock {
55 fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
56 use AsyncGeneratorKind::{Block, Closure, Fn};
57 if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind {
58 let body_id = BodyId {
59 hir_id: body.value.hir_id,
61 let def_id = cx.tcx.hir().body_owner_def_id(body_id);
62 let typeck_results = cx.tcx.typeck(def_id);
63 check_interior_types_lock(cx, &typeck_results.generator_interior_types, body.value.span);
68 fn check_interior_types_lock(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) {
69 for ty_cause in ty_causes {
70 if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() {
71 if is_mutex_guard(cx, adt.did) {
76 "this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await.",
77 ty_cause.scope_span.or(Some(span)),
78 "these are all the await points this lock is held through",
85 fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool {
86 match_def_path(cx, def_id, &paths::MUTEX_GUARD)
87 || match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD)
88 || match_def_path(cx, def_id, &paths::RWLOCK_WRITE_GUARD)
89 || match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD)
90 || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD)
91 || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD)
94 declare_clippy_lint! {
95 /// **What it does:** Checks for calls to await while holding a
96 /// `RefCell` `Ref` or `RefMut`.
98 /// **Why is this bad?** `RefCell` refs only check for exclusive mutable access
99 /// at runtime. Holding onto a `RefCell` ref across an `await` suspension point
100 /// risks panics from a mutable ref shared while other refs are outstanding.
102 /// **Known problems:** None.
107 /// use std::cell::RefCell;
109 /// async fn foo(x: &RefCell<u32>) {
110 /// let b = x.borrow_mut()();
118 /// use std::cell::RefCell;
120 /// async fn foo(x: &RefCell<u32>) {
122 /// let b = x.borrow_mut();
128 pub AWAIT_HOLDING_REFCELL_REF,
130 "Inside an async function, holding a RefCell ref while calling await"
133 declare_lint_pass!(AwaitHoldingRefCellRef => [AWAIT_HOLDING_REFCELL_REF]);
135 impl LateLintPass<'_> for AwaitHoldingRefCellRef {
136 fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
137 use AsyncGeneratorKind::{Block, Closure, Fn};
138 if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind {
139 let body_id = BodyId {
140 hir_id: body.value.hir_id,
142 let def_id = cx.tcx.hir().body_owner_def_id(body_id);
143 let typeck_results = cx.tcx.typeck(def_id);
144 check_interior_types_refcell(cx, &typeck_results.generator_interior_types, body.value.span);
149 fn check_interior_types_refcell(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) {
150 for ty_cause in ty_causes {
151 if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() {
152 if is_refcell_ref(cx, adt.did) {
155 AWAIT_HOLDING_REFCELL_REF,
157 "this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.",
158 ty_cause.scope_span.or(Some(span)),
159 "these are all the await points this ref is held through",
166 fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool {
167 match_def_path(cx, def_id, &paths::REFCELL_REF) || match_def_path(cx, def_id, &paths::REFCELL_REFMUT)