Skip to content

Commit 85d7655

Browse files
author
Daniel Smith
committed
Move refcell lint into shared module
1 parent db647ba commit 85d7655

8 files changed

+276
-292
lines changed

clippy_lints/src/await_holding_invalid.rs

+78-2
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,12 @@ impl LateLintPass<'_> for AwaitHoldingLock {
6060
};
6161
let def_id = cx.tcx.hir().body_owner_def_id(body_id);
6262
let typeck_results = cx.tcx.typeck(def_id);
63-
check_interior_types(cx, &typeck_results.generator_interior_types, body.value.span);
63+
check_interior_types_lock(cx, &typeck_results.generator_interior_types, body.value.span);
6464
}
6565
}
6666
}
6767

68-
fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) {
68+
fn check_interior_types_lock(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) {
6969
for ty_cause in ty_causes {
7070
if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() {
7171
if is_mutex_guard(cx, adt.did) {
@@ -90,3 +90,79 @@ fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool {
9090
|| match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD)
9191
|| match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD)
9292
}
93+
94+
declare_clippy_lint! {
95+
/// **What it does:** Checks for calls to await while holding a
96+
/// `RefCell` `Ref` or `RefMut`.
97+
///
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.
101+
///
102+
/// **Known problems:** None.
103+
///
104+
/// **Example:**
105+
///
106+
/// ```rust,ignore
107+
/// use std::cell::RefCell;
108+
///
109+
/// async fn foo(x: &RefCell<u32>) {
110+
/// let b = x.borrow_mut()();
111+
/// *ref += 1;
112+
/// bar.await;
113+
/// }
114+
/// ```
115+
///
116+
/// Use instead:
117+
/// ```rust,ignore
118+
/// use std::cell::RefCell;
119+
///
120+
/// async fn foo(x: &RefCell<u32>) {
121+
/// {
122+
/// let b = x.borrow_mut();
123+
/// *ref += 1;
124+
/// }
125+
/// bar.await;
126+
/// }
127+
/// ```
128+
pub AWAIT_HOLDING_REFCELL_REF,
129+
pedantic,
130+
"Inside an async function, holding a RefCell ref while calling await"
131+
}
132+
133+
declare_lint_pass!(AwaitHoldingRefCellRef => [AWAIT_HOLDING_REFCELL_REF]);
134+
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,
141+
};
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);
145+
}
146+
}
147+
}
148+
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) {
153+
span_lint_and_note(
154+
cx,
155+
AWAIT_HOLDING_REFCELL_REF,
156+
ty_cause.span,
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",
160+
);
161+
}
162+
}
163+
}
164+
}
165+
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)
168+
}

clippy_lints/src/await_holding_refcell_ref.rs

-83
This file was deleted.

clippy_lints/src/lib.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,6 @@ mod async_yields_async;
159159
mod atomic_ordering;
160160
mod attrs;
161161
mod await_holding_invalid;
162-
mod await_holding_refcell_ref;
163162
mod bit_mask;
164163
mod blacklisted_name;
165164
mod blocks_in_if_conditions;
@@ -502,7 +501,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
502501
&attrs::UNKNOWN_CLIPPY_LINTS,
503502
&attrs::USELESS_ATTRIBUTE,
504503
&await_holding_invalid::AWAIT_HOLDING_LOCK,
505-
&await_holding_refcell_ref::AWAIT_HOLDING_REFCELL_REF,
504+
&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF,
506505
&bit_mask::BAD_BIT_MASK,
507506
&bit_mask::INEFFECTIVE_BIT_MASK,
508507
&bit_mask::VERBOSE_BIT_MASK,
@@ -897,7 +896,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
897896
// end register lints, do not remove this comment, it’s used in `update_lints`
898897

899898
store.register_late_pass(|| box await_holding_invalid::AwaitHoldingLock);
900-
store.register_late_pass(|| box await_holding_refcell_ref::AwaitHoldingRefCellRef);
899+
store.register_late_pass(|| box await_holding_invalid::AwaitHoldingRefCellRef);
901900
store.register_late_pass(|| box serde_api::SerdeAPI);
902901
store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new());
903902
store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default());
@@ -1175,7 +1174,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
11751174
store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
11761175
LintId::of(&attrs::INLINE_ALWAYS),
11771176
LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK),
1178-
LintId::of(&await_holding_refcell_ref::AWAIT_HOLDING_REFCELL_REF),
1177+
LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
11791178
LintId::of(&bit_mask::VERBOSE_BIT_MASK),
11801179
LintId::of(&checked_conversions::CHECKED_CONVERSIONS),
11811180
LintId::of(&copies::MATCH_SAME_ARMS),

src/lintlist/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
7171
group: "pedantic",
7272
desc: "Inside an async function, holding a RefCell ref while calling await",
7373
deprecation: None,
74-
module: "await_holding_refcell_ref",
74+
module: "await_holding_invalid",
7575
},
7676
Lint {
7777
name: "bad_bit_mask",

tests/ui/await_holding_invalid.rs

+92-12
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
// edition:2018
2-
#![warn(clippy::await_holding_lock)]
2+
#![warn(clippy::await_holding_lock, clippy::await_holding_refcell_ref)]
33

4+
use std::cell::RefCell;
45
use std::sync::Mutex;
56

6-
async fn bad(x: &Mutex<u32>) -> u32 {
7+
async fn bad_lock(x: &Mutex<u32>) -> u32 {
78
let guard = x.lock().unwrap();
89
baz().await
910
}
1011

11-
async fn good(x: &Mutex<u32>) -> u32 {
12+
async fn good_lock(x: &Mutex<u32>) -> u32 {
1213
{
1314
let guard = x.lock().unwrap();
1415
let y = *guard + 1;
@@ -22,7 +23,7 @@ async fn baz() -> u32 {
2223
42
2324
}
2425

25-
async fn also_bad(x: &Mutex<u32>) -> u32 {
26+
async fn also_bad_lock(x: &Mutex<u32>) -> u32 {
2627
let first = baz().await;
2728

2829
let guard = x.lock().unwrap();
@@ -34,7 +35,7 @@ async fn also_bad(x: &Mutex<u32>) -> u32 {
3435
first + second + third
3536
}
3637

37-
async fn not_good(x: &Mutex<u32>) -> u32 {
38+
async fn not_good_lock(x: &Mutex<u32>) -> u32 {
3839
let first = baz().await;
3940

4041
let second = {
@@ -48,18 +49,97 @@ async fn not_good(x: &Mutex<u32>) -> u32 {
4849
}
4950

5051
#[allow(clippy::manual_async_fn)]
51-
fn block_bad(x: &Mutex<u32>) -> impl std::future::Future<Output = u32> + '_ {
52+
fn block_bad_lock(x: &Mutex<u32>) -> impl std::future::Future<Output = u32> + '_ {
5253
async move {
5354
let guard = x.lock().unwrap();
5455
baz().await
5556
}
5657
}
5758

59+
async fn bad_refcell(x: &RefCell<u32>) -> u32 {
60+
let b = x.borrow();
61+
baz().await
62+
}
63+
64+
async fn bad_mut_refcell(x: &RefCell<u32>) -> u32 {
65+
let b = x.borrow_mut();
66+
baz().await
67+
}
68+
69+
async fn good_refcell(x: &RefCell<u32>) -> u32 {
70+
{
71+
let b = x.borrow_mut();
72+
let y = *b + 1;
73+
}
74+
baz().await;
75+
let b = x.borrow_mut();
76+
47
77+
}
78+
79+
async fn also_bad_refcell(x: &RefCell<u32>) -> u32 {
80+
let first = baz().await;
81+
82+
let b = x.borrow_mut();
83+
84+
let second = baz().await;
85+
86+
let third = baz().await;
87+
88+
first + second + third
89+
}
90+
91+
async fn less_bad_refcell(x: &RefCell<u32>) -> u32 {
92+
let first = baz().await;
93+
94+
let b = x.borrow_mut();
95+
96+
let second = baz().await;
97+
98+
drop(b);
99+
100+
let third = baz().await;
101+
102+
first + second + third
103+
}
104+
105+
async fn not_good_refcell(x: &RefCell<u32>) -> u32 {
106+
let first = baz().await;
107+
108+
let second = {
109+
let b = x.borrow_mut();
110+
baz().await
111+
};
112+
113+
let third = baz().await;
114+
115+
first + second + third
116+
}
117+
118+
#[allow(clippy::manual_async_fn)]
119+
fn block_bad_refcell(x: &RefCell<u32>) -> impl std::future::Future<Output = u32> + '_ {
120+
async move {
121+
let b = x.borrow_mut();
122+
baz().await
123+
}
124+
}
125+
58126
fn main() {
59-
let m = Mutex::new(100);
60-
good(&m);
61-
bad(&m);
62-
also_bad(&m);
63-
not_good(&m);
64-
block_bad(&m);
127+
{
128+
let m = Mutex::new(100);
129+
good_lock(&m);
130+
bad_lock(&m);
131+
also_bad_lock(&m);
132+
not_good_lock(&m);
133+
block_bad_lock(&m);
134+
}
135+
{
136+
let rc = RefCell::new(100);
137+
good_refcell(&rc);
138+
bad_refcell(&rc);
139+
bad_mut_refcell(&rc);
140+
also_bad_refcell(&rc);
141+
less_bad_refcell(&rc);
142+
not_good_refcell(&rc);
143+
block_bad_refcell(&rc);
144+
}
65145
}

0 commit comments

Comments
 (0)