From 57bf80f77626b134faaf2cd95664403627fba0da Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Thu, 10 Sep 2020 15:53:36 -0400 Subject: [PATCH 01/10] Add lint for holding RefCell Ref across an await --- clippy_lints/src/await_holding_refcell_ref.rs | 83 +++++++++++++++++++ clippy_lints/src/lib.rs | 4 + clippy_lints/src/utils/paths.rs | 2 + tests/ui/await_holding_refcell_ref.rs | 71 ++++++++++++++++ tests/ui/await_holding_refcell_ref.stderr | 77 +++++++++++++++++ 5 files changed, 237 insertions(+) create mode 100644 clippy_lints/src/await_holding_refcell_ref.rs create mode 100644 tests/ui/await_holding_refcell_ref.rs create mode 100644 tests/ui/await_holding_refcell_ref.stderr diff --git a/clippy_lints/src/await_holding_refcell_ref.rs b/clippy_lints/src/await_holding_refcell_ref.rs new file mode 100644 index 000000000000..9a75911acbea --- /dev/null +++ b/clippy_lints/src/await_holding_refcell_ref.rs @@ -0,0 +1,83 @@ +use crate::utils::{match_def_path, paths, span_lint_and_note}; +use rustc_hir::def_id::DefId; +use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::GeneratorInteriorTypeCause; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for calls to await while holding a + /// `RefCell` `Ref` or `RefMut`. + /// + /// **Why is this bad?** `RefCell` refs only check for exclusive mutable access + /// at runtime. Holding onto a `RefCell` ref across an `await` suspension point + /// risks panics from a mutable ref shared while other refs are outstanding. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,ignore + /// use std::cell::RefCell; + /// + /// async fn foo(x: &RefCell) { + /// let b = x.borrow_mut()(); + /// *ref += 1; + /// bar.await; + /// } + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// use std::cell::RefCell; + /// + /// async fn foo(x: &RefCell) { + /// { + /// let b = x.borrow_mut(); + /// *ref += 1; + /// } + /// bar.await; + /// } + /// ``` + pub AWAIT_HOLDING_REFCELL_REF, + pedantic, + "Inside an async function, holding a RefCell ref while calling await" +} + +declare_lint_pass!(AwaitHoldingRefCellRef => [AWAIT_HOLDING_REFCELL_REF]); + +impl LateLintPass<'_> for AwaitHoldingRefCellRef { + fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { + use AsyncGeneratorKind::{Block, Closure, Fn}; + if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { + let body_id = BodyId { + hir_id: body.value.hir_id, + }; + let def_id = cx.tcx.hir().body_owner_def_id(body_id); + let typeck_results = cx.tcx.typeck(def_id); + check_interior_types(cx, &typeck_results.generator_interior_types, body.value.span); + } + } +} + +fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { + for ty_cause in ty_causes { + if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { + if is_refcell_ref(cx, adt.did) { + span_lint_and_note( + cx, + AWAIT_HOLDING_REFCELL_REF, + ty_cause.span, + "this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.", + ty_cause.scope_span.or(Some(span)), + "these are all the await points this ref is held through", + ); + } + } + } +} + +fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool { + match_def_path(cx, def_id, &paths::REFCELL_REF) || match_def_path(cx, def_id, &paths::REFCELL_REFMUT) +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d4d2f92a6a69..697199861047 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -161,6 +161,7 @@ mod async_yields_async; mod atomic_ordering; mod attrs; mod await_holding_lock; +mod await_holding_refcell_ref; mod bit_mask; mod blacklisted_name; mod blocks_in_if_conditions; @@ -510,6 +511,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &attrs::UNKNOWN_CLIPPY_LINTS, &attrs::USELESS_ATTRIBUTE, &await_holding_lock::AWAIT_HOLDING_LOCK, + &await_holding_refcell_ref::AWAIT_HOLDING_REFCELL_REF, &bit_mask::BAD_BIT_MASK, &bit_mask::INEFFECTIVE_BIT_MASK, &bit_mask::VERBOSE_BIT_MASK, @@ -906,6 +908,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: // end register lints, do not remove this comment, it’s used in `update_lints` store.register_late_pass(|| box await_holding_lock::AwaitHoldingLock); + store.register_late_pass(|| box await_holding_refcell_ref::AwaitHoldingRefCellRef); store.register_late_pass(|| box serde_api::SerdeAPI); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); @@ -1189,6 +1192,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(&attrs::INLINE_ALWAYS), LintId::of(&await_holding_lock::AWAIT_HOLDING_LOCK), + LintId::of(&await_holding_refcell_ref::AWAIT_HOLDING_REFCELL_REF), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&copies::MATCH_SAME_ARMS), diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 5e769c690a69..cd9b92efe583 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -93,6 +93,8 @@ pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const RC: [&str; 3] = ["alloc", "rc", "Rc"]; pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"]; pub const RECEIVER: [&str; 4] = ["std", "sync", "mpsc", "Receiver"]; +pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"]; +pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"]; pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"]; pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"]; pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"]; diff --git a/tests/ui/await_holding_refcell_ref.rs b/tests/ui/await_holding_refcell_ref.rs new file mode 100644 index 000000000000..6e330f47dfc2 --- /dev/null +++ b/tests/ui/await_holding_refcell_ref.rs @@ -0,0 +1,71 @@ +// edition:2018 +#![warn(clippy::await_holding_refcell_ref)] + +use std::cell::RefCell; + +async fn bad(x: &RefCell) -> u32 { + let b = x.borrow(); + baz().await +} + +async fn bad_mut(x: &RefCell) -> u32 { + let b = x.borrow_mut(); + baz().await +} + +async fn good(x: &RefCell) -> u32 { + { + let b = x.borrow_mut(); + let y = *b + 1; + } + baz().await; + let b = x.borrow_mut(); + 47 +} + +async fn baz() -> u32 { + 42 +} + +async fn also_bad(x: &RefCell) -> u32 { + let first = baz().await; + + let b = x.borrow_mut(); + + let second = baz().await; + + let third = baz().await; + + first + second + third +} + +async fn not_good(x: &RefCell) -> u32 { + let first = baz().await; + + let second = { + let b = x.borrow_mut(); + baz().await + }; + + let third = baz().await; + + first + second + third +} + +#[allow(clippy::manual_async_fn)] +fn block_bad(x: &RefCell) -> impl std::future::Future + '_ { + async move { + let b = x.borrow_mut(); + baz().await + } +} + +fn main() { + let m = RefCell::new(100); + good(&m); + bad(&m); + bad_mut(&m); + also_bad(&m); + not_good(&m); + block_bad(&m); +} diff --git a/tests/ui/await_holding_refcell_ref.stderr b/tests/ui/await_holding_refcell_ref.stderr new file mode 100644 index 000000000000..b114945504a2 --- /dev/null +++ b/tests/ui/await_holding_refcell_ref.stderr @@ -0,0 +1,77 @@ +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:7:9 + | +LL | let b = x.borrow(); + | ^ + | + = note: `-D clippy::await-holding-refcell-ref` implied by `-D warnings` +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:7:5 + | +LL | / let b = x.borrow(); +LL | | baz().await +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:12:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:12:5 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:33:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:33:5 + | +LL | / let b = x.borrow_mut(); +LL | | +LL | | let second = baz().await; +LL | | +... | +LL | | first + second + third +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:46:13 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:46:9 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | }; + | |_____^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:58:13 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:58:9 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | } + | |_____^ + +error: aborting due to 5 previous errors + From 8727169f7243c87e3708d99e9602562370f01a1a Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Thu, 10 Sep 2020 16:01:27 -0400 Subject: [PATCH 02/10] fmt --- tests/ui/await_holding_refcell_ref.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/await_holding_refcell_ref.rs b/tests/ui/await_holding_refcell_ref.rs index 6e330f47dfc2..8e30da85d141 100644 --- a/tests/ui/await_holding_refcell_ref.rs +++ b/tests/ui/await_holding_refcell_ref.rs @@ -64,7 +64,7 @@ fn main() { let m = RefCell::new(100); good(&m); bad(&m); - bad_mut(&m); + bad_mut(&m); also_bad(&m); not_good(&m); block_bad(&m); From 070a751d4cf350a71901f75bc99ca0e0922a3133 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Thu, 10 Sep 2020 16:02:00 -0400 Subject: [PATCH 03/10] update_lints --- CHANGELOG.md | 1 + src/lintlist/mod.rs | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d82f970b8bf2..287301772e28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1632,6 +1632,7 @@ Released 2018-09-13 [`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops [`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async [`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock +[`await_holding_refcell_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_refcell_ref [`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask [`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map [`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6301d623a2b1..21185a08d5c3 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -67,6 +67,13 @@ vec![ deprecation: None, module: "await_holding_lock", }, + Lint { + name: "await_holding_refcell_ref", + group: "pedantic", + desc: "Inside an async function, holding a RefCell ref while calling await", + deprecation: None, + module: "await_holding_refcell_ref", + }, Lint { name: "bad_bit_mask", group: "correctness", From 0f4abbf99a6f1ed783ea6935c83427c2aef95144 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Mon, 28 Sep 2020 12:57:18 -0400 Subject: [PATCH 04/10] Better naming post copy/paste --- tests/ui/await_holding_refcell_ref.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/ui/await_holding_refcell_ref.rs b/tests/ui/await_holding_refcell_ref.rs index 8e30da85d141..dd3adc494f1b 100644 --- a/tests/ui/await_holding_refcell_ref.rs +++ b/tests/ui/await_holding_refcell_ref.rs @@ -61,11 +61,11 @@ fn block_bad(x: &RefCell) -> impl std::future::Future + '_ { } fn main() { - let m = RefCell::new(100); - good(&m); - bad(&m); - bad_mut(&m); - also_bad(&m); - not_good(&m); - block_bad(&m); + let rc = RefCell::new(100); + good(&rc); + bad(&rc); + bad_mut(&rc); + also_bad(&rc); + not_good(&rc); + block_bad(&rc); } From b3a427d8733a549b11f9bc88eceb31c857851411 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Mon, 28 Sep 2020 13:29:37 -0400 Subject: [PATCH 05/10] Add another test case --- tests/ui/await_holding_refcell_ref.rs | 15 ++++++++++++ tests/ui/await_holding_refcell_ref.stderr | 28 +++++++++++++++++++---- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/tests/ui/await_holding_refcell_ref.rs b/tests/ui/await_holding_refcell_ref.rs index dd3adc494f1b..88841597bb60 100644 --- a/tests/ui/await_holding_refcell_ref.rs +++ b/tests/ui/await_holding_refcell_ref.rs @@ -39,6 +39,20 @@ async fn also_bad(x: &RefCell) -> u32 { first + second + third } +async fn less_bad(x: &RefCell) -> u32 { + let first = baz().await; + + let b = x.borrow_mut(); + + let second = baz().await; + + drop(b); + + let third = baz().await; + + first + second + third +} + async fn not_good(x: &RefCell) -> u32 { let first = baz().await; @@ -66,6 +80,7 @@ fn main() { bad(&rc); bad_mut(&rc); also_bad(&rc); + less_bad(&rc); not_good(&rc); block_bad(&rc); } diff --git a/tests/ui/await_holding_refcell_ref.stderr b/tests/ui/await_holding_refcell_ref.stderr index b114945504a2..b504f0454913 100644 --- a/tests/ui/await_holding_refcell_ref.stderr +++ b/tests/ui/await_holding_refcell_ref.stderr @@ -46,13 +46,31 @@ LL | | } | |_^ error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:46:13 + --> $DIR/await_holding_refcell_ref.rs:45:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:45:5 + | +LL | / let b = x.borrow_mut(); +LL | | +LL | | let second = baz().await; +LL | | +... | +LL | | first + second + third +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:60:13 | LL | let b = x.borrow_mut(); | ^ | note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:46:9 + --> $DIR/await_holding_refcell_ref.rs:60:9 | LL | / let b = x.borrow_mut(); LL | | baz().await @@ -60,18 +78,18 @@ LL | | }; | |_____^ error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:58:13 + --> $DIR/await_holding_refcell_ref.rs:72:13 | LL | let b = x.borrow_mut(); | ^ | note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:58:9 + --> $DIR/await_holding_refcell_ref.rs:72:9 | LL | / let b = x.borrow_mut(); LL | | baz().await LL | | } | |_____^ -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors From 3ed69cdb13e5953467f9d849d7ad480479ca01d6 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Tue, 13 Oct 2020 12:39:20 -0400 Subject: [PATCH 06/10] Move existing lint into shared file --- ..._holding_lock.rs => await_holding_invalid.rs} | 0 clippy_lints/src/lib.rs | 8 ++++---- src/lintlist/mod.rs | 2 +- ..._holding_lock.rs => await_holding_invalid.rs} | 0 ..._lock.stderr => await_holding_invalid.stderr} | 16 ++++++++-------- 5 files changed, 13 insertions(+), 13 deletions(-) rename clippy_lints/src/{await_holding_lock.rs => await_holding_invalid.rs} (100%) rename tests/ui/{await_holding_lock.rs => await_holding_invalid.rs} (100%) rename tests/ui/{await_holding_lock.stderr => await_holding_invalid.stderr} (84%) diff --git a/clippy_lints/src/await_holding_lock.rs b/clippy_lints/src/await_holding_invalid.rs similarity index 100% rename from clippy_lints/src/await_holding_lock.rs rename to clippy_lints/src/await_holding_invalid.rs diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 697199861047..47cc0c903229 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -160,7 +160,7 @@ mod assign_ops; mod async_yields_async; mod atomic_ordering; mod attrs; -mod await_holding_lock; +mod await_holding_invalid; mod await_holding_refcell_ref; mod bit_mask; mod blacklisted_name; @@ -510,7 +510,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &attrs::MISMATCHED_TARGET_OS, &attrs::UNKNOWN_CLIPPY_LINTS, &attrs::USELESS_ATTRIBUTE, - &await_holding_lock::AWAIT_HOLDING_LOCK, + &await_holding_invalid::AWAIT_HOLDING_LOCK, &await_holding_refcell_ref::AWAIT_HOLDING_REFCELL_REF, &bit_mask::BAD_BIT_MASK, &bit_mask::INEFFECTIVE_BIT_MASK, @@ -907,7 +907,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ]); // end register lints, do not remove this comment, it’s used in `update_lints` - store.register_late_pass(|| box await_holding_lock::AwaitHoldingLock); + store.register_late_pass(|| box await_holding_invalid::AwaitHoldingLock); store.register_late_pass(|| box await_holding_refcell_ref::AwaitHoldingRefCellRef); store.register_late_pass(|| box serde_api::SerdeAPI); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); @@ -1191,7 +1191,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(&attrs::INLINE_ALWAYS), - LintId::of(&await_holding_lock::AWAIT_HOLDING_LOCK), + LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK), LintId::of(&await_holding_refcell_ref::AWAIT_HOLDING_REFCELL_REF), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 21185a08d5c3..63e9220ccd50 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -65,7 +65,7 @@ vec![ group: "pedantic", desc: "Inside an async function, holding a MutexGuard while calling await", deprecation: None, - module: "await_holding_lock", + module: "await_holding_invalid", }, Lint { name: "await_holding_refcell_ref", diff --git a/tests/ui/await_holding_lock.rs b/tests/ui/await_holding_invalid.rs similarity index 100% rename from tests/ui/await_holding_lock.rs rename to tests/ui/await_holding_invalid.rs diff --git a/tests/ui/await_holding_lock.stderr b/tests/ui/await_holding_invalid.stderr similarity index 84% rename from tests/ui/await_holding_lock.stderr rename to tests/ui/await_holding_invalid.stderr index 21bf49d16f04..315d5731b962 100644 --- a/tests/ui/await_holding_lock.stderr +++ b/tests/ui/await_holding_invalid.stderr @@ -1,12 +1,12 @@ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_lock.rs:7:9 + --> $DIR/await_holding_invalid.rs:7:9 | LL | let guard = x.lock().unwrap(); | ^^^^^ | = note: `-D clippy::await-holding-lock` implied by `-D warnings` note: these are all the await points this lock is held through - --> $DIR/await_holding_lock.rs:7:5 + --> $DIR/await_holding_invalid.rs:7:5 | LL | / let guard = x.lock().unwrap(); LL | | baz().await @@ -14,13 +14,13 @@ LL | | } | |_^ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_lock.rs:28:9 + --> $DIR/await_holding_invalid.rs:28:9 | LL | let guard = x.lock().unwrap(); | ^^^^^ | note: these are all the await points this lock is held through - --> $DIR/await_holding_lock.rs:28:5 + --> $DIR/await_holding_invalid.rs:28:5 | LL | / let guard = x.lock().unwrap(); LL | | @@ -32,13 +32,13 @@ LL | | } | |_^ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_lock.rs:41:13 + --> $DIR/await_holding_invalid.rs:41:13 | LL | let guard = x.lock().unwrap(); | ^^^^^ | note: these are all the await points this lock is held through - --> $DIR/await_holding_lock.rs:41:9 + --> $DIR/await_holding_invalid.rs:41:9 | LL | / let guard = x.lock().unwrap(); LL | | baz().await @@ -46,13 +46,13 @@ LL | | }; | |_____^ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_lock.rs:53:13 + --> $DIR/await_holding_invalid.rs:53:13 | LL | let guard = x.lock().unwrap(); | ^^^^^ | note: these are all the await points this lock is held through - --> $DIR/await_holding_lock.rs:53:9 + --> $DIR/await_holding_invalid.rs:53:9 | LL | / let guard = x.lock().unwrap(); LL | | baz().await From ee20ebadafc9b2e4995015097e376c0a866d84af Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Tue, 13 Oct 2020 13:15:45 -0400 Subject: [PATCH 07/10] Move refcell lint into shared module --- clippy_lints/src/await_holding_invalid.rs | 80 ++++++++++++- clippy_lints/src/await_holding_refcell_ref.rs | 83 ------------- clippy_lints/src/lib.rs | 7 +- src/lintlist/mod.rs | 2 +- tests/ui/await_holding_invalid.rs | 104 ++++++++++++++-- tests/ui/await_holding_invalid.stderr | 111 ++++++++++++++++-- tests/ui/await_holding_refcell_ref.rs | 86 -------------- tests/ui/await_holding_refcell_ref.stderr | 95 --------------- 8 files changed, 276 insertions(+), 292 deletions(-) delete mode 100644 clippy_lints/src/await_holding_refcell_ref.rs delete mode 100644 tests/ui/await_holding_refcell_ref.rs delete mode 100644 tests/ui/await_holding_refcell_ref.stderr diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 367534499fd0..2000bdae363f 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -60,12 +60,12 @@ impl LateLintPass<'_> for AwaitHoldingLock { }; let def_id = cx.tcx.hir().body_owner_def_id(body_id); let typeck_results = cx.tcx.typeck(def_id); - check_interior_types(cx, &typeck_results.generator_interior_types, body.value.span); + check_interior_types_lock(cx, &typeck_results.generator_interior_types, body.value.span); } } } -fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { +fn check_interior_types_lock(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { for ty_cause in ty_causes { if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { if is_mutex_guard(cx, adt.did) { @@ -90,3 +90,79 @@ fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool { || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD) || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD) } + +declare_clippy_lint! { + /// **What it does:** Checks for calls to await while holding a + /// `RefCell` `Ref` or `RefMut`. + /// + /// **Why is this bad?** `RefCell` refs only check for exclusive mutable access + /// at runtime. Holding onto a `RefCell` ref across an `await` suspension point + /// risks panics from a mutable ref shared while other refs are outstanding. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,ignore + /// use std::cell::RefCell; + /// + /// async fn foo(x: &RefCell) { + /// let b = x.borrow_mut()(); + /// *ref += 1; + /// bar.await; + /// } + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// use std::cell::RefCell; + /// + /// async fn foo(x: &RefCell) { + /// { + /// let b = x.borrow_mut(); + /// *ref += 1; + /// } + /// bar.await; + /// } + /// ``` + pub AWAIT_HOLDING_REFCELL_REF, + pedantic, + "Inside an async function, holding a RefCell ref while calling await" +} + +declare_lint_pass!(AwaitHoldingRefCellRef => [AWAIT_HOLDING_REFCELL_REF]); + +impl LateLintPass<'_> for AwaitHoldingRefCellRef { + fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { + use AsyncGeneratorKind::{Block, Closure, Fn}; + if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { + let body_id = BodyId { + hir_id: body.value.hir_id, + }; + let def_id = cx.tcx.hir().body_owner_def_id(body_id); + let typeck_results = cx.tcx.typeck(def_id); + check_interior_types_refcell(cx, &typeck_results.generator_interior_types, body.value.span); + } + } +} + +fn check_interior_types_refcell(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { + for ty_cause in ty_causes { + if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { + if is_refcell_ref(cx, adt.did) { + span_lint_and_note( + cx, + AWAIT_HOLDING_REFCELL_REF, + ty_cause.span, + "this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.", + ty_cause.scope_span.or(Some(span)), + "these are all the await points this ref is held through", + ); + } + } + } +} + +fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool { + match_def_path(cx, def_id, &paths::REFCELL_REF) || match_def_path(cx, def_id, &paths::REFCELL_REFMUT) +} diff --git a/clippy_lints/src/await_holding_refcell_ref.rs b/clippy_lints/src/await_holding_refcell_ref.rs deleted file mode 100644 index 9a75911acbea..000000000000 --- a/clippy_lints/src/await_holding_refcell_ref.rs +++ /dev/null @@ -1,83 +0,0 @@ -use crate::utils::{match_def_path, paths, span_lint_and_note}; -use rustc_hir::def_id::DefId; -use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::GeneratorInteriorTypeCause; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; - -declare_clippy_lint! { - /// **What it does:** Checks for calls to await while holding a - /// `RefCell` `Ref` or `RefMut`. - /// - /// **Why is this bad?** `RefCell` refs only check for exclusive mutable access - /// at runtime. Holding onto a `RefCell` ref across an `await` suspension point - /// risks panics from a mutable ref shared while other refs are outstanding. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// ```rust,ignore - /// use std::cell::RefCell; - /// - /// async fn foo(x: &RefCell) { - /// let b = x.borrow_mut()(); - /// *ref += 1; - /// bar.await; - /// } - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// use std::cell::RefCell; - /// - /// async fn foo(x: &RefCell) { - /// { - /// let b = x.borrow_mut(); - /// *ref += 1; - /// } - /// bar.await; - /// } - /// ``` - pub AWAIT_HOLDING_REFCELL_REF, - pedantic, - "Inside an async function, holding a RefCell ref while calling await" -} - -declare_lint_pass!(AwaitHoldingRefCellRef => [AWAIT_HOLDING_REFCELL_REF]); - -impl LateLintPass<'_> for AwaitHoldingRefCellRef { - fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { - use AsyncGeneratorKind::{Block, Closure, Fn}; - if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { - let body_id = BodyId { - hir_id: body.value.hir_id, - }; - let def_id = cx.tcx.hir().body_owner_def_id(body_id); - let typeck_results = cx.tcx.typeck(def_id); - check_interior_types(cx, &typeck_results.generator_interior_types, body.value.span); - } - } -} - -fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { - for ty_cause in ty_causes { - if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { - if is_refcell_ref(cx, adt.did) { - span_lint_and_note( - cx, - AWAIT_HOLDING_REFCELL_REF, - ty_cause.span, - "this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.", - ty_cause.scope_span.or(Some(span)), - "these are all the await points this ref is held through", - ); - } - } - } -} - -fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool { - match_def_path(cx, def_id, &paths::REFCELL_REF) || match_def_path(cx, def_id, &paths::REFCELL_REFMUT) -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 47cc0c903229..0c5baf96970c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -161,7 +161,6 @@ mod async_yields_async; mod atomic_ordering; mod attrs; mod await_holding_invalid; -mod await_holding_refcell_ref; mod bit_mask; mod blacklisted_name; mod blocks_in_if_conditions; @@ -511,7 +510,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &attrs::UNKNOWN_CLIPPY_LINTS, &attrs::USELESS_ATTRIBUTE, &await_holding_invalid::AWAIT_HOLDING_LOCK, - &await_holding_refcell_ref::AWAIT_HOLDING_REFCELL_REF, + &await_holding_invalid::AWAIT_HOLDING_REFCELL_REF, &bit_mask::BAD_BIT_MASK, &bit_mask::INEFFECTIVE_BIT_MASK, &bit_mask::VERBOSE_BIT_MASK, @@ -908,7 +907,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: // end register lints, do not remove this comment, it’s used in `update_lints` store.register_late_pass(|| box await_holding_invalid::AwaitHoldingLock); - store.register_late_pass(|| box await_holding_refcell_ref::AwaitHoldingRefCellRef); + store.register_late_pass(|| box await_holding_invalid::AwaitHoldingRefCellRef); store.register_late_pass(|| box serde_api::SerdeAPI); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); @@ -1192,7 +1191,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(&attrs::INLINE_ALWAYS), LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK), - LintId::of(&await_holding_refcell_ref::AWAIT_HOLDING_REFCELL_REF), + LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&copies::MATCH_SAME_ARMS), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 63e9220ccd50..c955f37b83a3 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -72,7 +72,7 @@ vec![ group: "pedantic", desc: "Inside an async function, holding a RefCell ref while calling await", deprecation: None, - module: "await_holding_refcell_ref", + module: "await_holding_invalid", }, Lint { name: "bad_bit_mask", diff --git a/tests/ui/await_holding_invalid.rs b/tests/ui/await_holding_invalid.rs index 0458950edee1..45aa3e64292e 100644 --- a/tests/ui/await_holding_invalid.rs +++ b/tests/ui/await_holding_invalid.rs @@ -1,14 +1,15 @@ // edition:2018 -#![warn(clippy::await_holding_lock)] +#![warn(clippy::await_holding_lock, clippy::await_holding_refcell_ref)] +use std::cell::RefCell; use std::sync::Mutex; -async fn bad(x: &Mutex) -> u32 { +async fn bad_lock(x: &Mutex) -> u32 { let guard = x.lock().unwrap(); baz().await } -async fn good(x: &Mutex) -> u32 { +async fn good_lock(x: &Mutex) -> u32 { { let guard = x.lock().unwrap(); let y = *guard + 1; @@ -22,7 +23,7 @@ async fn baz() -> u32 { 42 } -async fn also_bad(x: &Mutex) -> u32 { +async fn also_bad_lock(x: &Mutex) -> u32 { let first = baz().await; let guard = x.lock().unwrap(); @@ -34,7 +35,7 @@ async fn also_bad(x: &Mutex) -> u32 { first + second + third } -async fn not_good(x: &Mutex) -> u32 { +async fn not_good_lock(x: &Mutex) -> u32 { let first = baz().await; let second = { @@ -48,18 +49,97 @@ async fn not_good(x: &Mutex) -> u32 { } #[allow(clippy::manual_async_fn)] -fn block_bad(x: &Mutex) -> impl std::future::Future + '_ { +fn block_bad_lock(x: &Mutex) -> impl std::future::Future + '_ { async move { let guard = x.lock().unwrap(); baz().await } } +async fn bad_refcell(x: &RefCell) -> u32 { + let b = x.borrow(); + baz().await +} + +async fn bad_mut_refcell(x: &RefCell) -> u32 { + let b = x.borrow_mut(); + baz().await +} + +async fn good_refcell(x: &RefCell) -> u32 { + { + let b = x.borrow_mut(); + let y = *b + 1; + } + baz().await; + let b = x.borrow_mut(); + 47 +} + +async fn also_bad_refcell(x: &RefCell) -> u32 { + let first = baz().await; + + let b = x.borrow_mut(); + + let second = baz().await; + + let third = baz().await; + + first + second + third +} + +async fn less_bad_refcell(x: &RefCell) -> u32 { + let first = baz().await; + + let b = x.borrow_mut(); + + let second = baz().await; + + drop(b); + + let third = baz().await; + + first + second + third +} + +async fn not_good_refcell(x: &RefCell) -> u32 { + let first = baz().await; + + let second = { + let b = x.borrow_mut(); + baz().await + }; + + let third = baz().await; + + first + second + third +} + +#[allow(clippy::manual_async_fn)] +fn block_bad_refcell(x: &RefCell) -> impl std::future::Future + '_ { + async move { + let b = x.borrow_mut(); + baz().await + } +} + fn main() { - let m = Mutex::new(100); - good(&m); - bad(&m); - also_bad(&m); - not_good(&m); - block_bad(&m); + { + let m = Mutex::new(100); + good_lock(&m); + bad_lock(&m); + also_bad_lock(&m); + not_good_lock(&m); + block_bad_lock(&m); + } + { + let rc = RefCell::new(100); + good_refcell(&rc); + bad_refcell(&rc); + bad_mut_refcell(&rc); + also_bad_refcell(&rc); + less_bad_refcell(&rc); + not_good_refcell(&rc); + block_bad_refcell(&rc); + } } diff --git a/tests/ui/await_holding_invalid.stderr b/tests/ui/await_holding_invalid.stderr index 315d5731b962..c8d49820c020 100644 --- a/tests/ui/await_holding_invalid.stderr +++ b/tests/ui/await_holding_invalid.stderr @@ -1,12 +1,12 @@ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:7:9 + --> $DIR/await_holding_invalid.rs:8:9 | LL | let guard = x.lock().unwrap(); | ^^^^^ | = note: `-D clippy::await-holding-lock` implied by `-D warnings` note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:7:5 + --> $DIR/await_holding_invalid.rs:8:5 | LL | / let guard = x.lock().unwrap(); LL | | baz().await @@ -14,13 +14,13 @@ LL | | } | |_^ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:28:9 + --> $DIR/await_holding_invalid.rs:29:9 | LL | let guard = x.lock().unwrap(); | ^^^^^ | note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:28:5 + --> $DIR/await_holding_invalid.rs:29:5 | LL | / let guard = x.lock().unwrap(); LL | | @@ -32,13 +32,13 @@ LL | | } | |_^ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:41:13 + --> $DIR/await_holding_invalid.rs:42:13 | LL | let guard = x.lock().unwrap(); | ^^^^^ | note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:41:9 + --> $DIR/await_holding_invalid.rs:42:9 | LL | / let guard = x.lock().unwrap(); LL | | baz().await @@ -46,18 +46,111 @@ LL | | }; | |_____^ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:53:13 + --> $DIR/await_holding_invalid.rs:54:13 | LL | let guard = x.lock().unwrap(); | ^^^^^ | note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:53:9 + --> $DIR/await_holding_invalid.rs:54:9 | LL | / let guard = x.lock().unwrap(); LL | | baz().await LL | | } | |_____^ -error: aborting due to 4 previous errors +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_invalid.rs:60:9 + | +LL | let b = x.borrow(); + | ^ + | + = note: `-D clippy::await-holding-refcell-ref` implied by `-D warnings` +note: these are all the await points this ref is held through + --> $DIR/await_holding_invalid.rs:60:5 + | +LL | / let b = x.borrow(); +LL | | baz().await +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_invalid.rs:65:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_invalid.rs:65:5 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_invalid.rs:82:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_invalid.rs:82:5 + | +LL | / let b = x.borrow_mut(); +LL | | +LL | | let second = baz().await; +LL | | +... | +LL | | first + second + third +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_invalid.rs:94:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_invalid.rs:94:5 + | +LL | / let b = x.borrow_mut(); +LL | | +LL | | let second = baz().await; +LL | | +... | +LL | | first + second + third +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_invalid.rs:109:13 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_invalid.rs:109:9 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | }; + | |_____^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_invalid.rs:121:13 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_invalid.rs:121:9 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | } + | |_____^ + +error: aborting due to 10 previous errors diff --git a/tests/ui/await_holding_refcell_ref.rs b/tests/ui/await_holding_refcell_ref.rs deleted file mode 100644 index 88841597bb60..000000000000 --- a/tests/ui/await_holding_refcell_ref.rs +++ /dev/null @@ -1,86 +0,0 @@ -// edition:2018 -#![warn(clippy::await_holding_refcell_ref)] - -use std::cell::RefCell; - -async fn bad(x: &RefCell) -> u32 { - let b = x.borrow(); - baz().await -} - -async fn bad_mut(x: &RefCell) -> u32 { - let b = x.borrow_mut(); - baz().await -} - -async fn good(x: &RefCell) -> u32 { - { - let b = x.borrow_mut(); - let y = *b + 1; - } - baz().await; - let b = x.borrow_mut(); - 47 -} - -async fn baz() -> u32 { - 42 -} - -async fn also_bad(x: &RefCell) -> u32 { - let first = baz().await; - - let b = x.borrow_mut(); - - let second = baz().await; - - let third = baz().await; - - first + second + third -} - -async fn less_bad(x: &RefCell) -> u32 { - let first = baz().await; - - let b = x.borrow_mut(); - - let second = baz().await; - - drop(b); - - let third = baz().await; - - first + second + third -} - -async fn not_good(x: &RefCell) -> u32 { - let first = baz().await; - - let second = { - let b = x.borrow_mut(); - baz().await - }; - - let third = baz().await; - - first + second + third -} - -#[allow(clippy::manual_async_fn)] -fn block_bad(x: &RefCell) -> impl std::future::Future + '_ { - async move { - let b = x.borrow_mut(); - baz().await - } -} - -fn main() { - let rc = RefCell::new(100); - good(&rc); - bad(&rc); - bad_mut(&rc); - also_bad(&rc); - less_bad(&rc); - not_good(&rc); - block_bad(&rc); -} diff --git a/tests/ui/await_holding_refcell_ref.stderr b/tests/ui/await_holding_refcell_ref.stderr deleted file mode 100644 index b504f0454913..000000000000 --- a/tests/ui/await_holding_refcell_ref.stderr +++ /dev/null @@ -1,95 +0,0 @@ -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:7:9 - | -LL | let b = x.borrow(); - | ^ - | - = note: `-D clippy::await-holding-refcell-ref` implied by `-D warnings` -note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:7:5 - | -LL | / let b = x.borrow(); -LL | | baz().await -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:12:9 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:12:5 - | -LL | / let b = x.borrow_mut(); -LL | | baz().await -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:33:9 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:33:5 - | -LL | / let b = x.borrow_mut(); -LL | | -LL | | let second = baz().await; -LL | | -... | -LL | | first + second + third -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:45:9 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:45:5 - | -LL | / let b = x.borrow_mut(); -LL | | -LL | | let second = baz().await; -LL | | -... | -LL | | first + second + third -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:60:13 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:60:9 - | -LL | / let b = x.borrow_mut(); -LL | | baz().await -LL | | }; - | |_____^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:72:13 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:72:9 - | -LL | / let b = x.borrow_mut(); -LL | | baz().await -LL | | } - | |_____^ - -error: aborting due to 6 previous errors - From d8c6bce4407b1c99ed961f75a093ffe767818069 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Tue, 13 Oct 2020 13:20:01 -0400 Subject: [PATCH 08/10] Convert the await holding lints to correctness --- clippy_lints/src/await_holding_invalid.rs | 4 ++-- clippy_lints/src/lib.rs | 6 ++++-- src/lintlist/mod.rs | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 2000bdae363f..1d201ff095b9 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -45,7 +45,7 @@ declare_clippy_lint! { /// } /// ``` pub AWAIT_HOLDING_LOCK, - pedantic, + correctness, "Inside an async function, holding a MutexGuard while calling await" } @@ -126,7 +126,7 @@ declare_clippy_lint! { /// } /// ``` pub AWAIT_HOLDING_REFCELL_REF, - pedantic, + correctness, "Inside an async function, holding a RefCell ref while calling await" } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0c5baf96970c..c627df209763 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1190,8 +1190,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(&attrs::INLINE_ALWAYS), - LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK), - LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&copies::MATCH_SAME_ARMS), @@ -1290,6 +1288,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&attrs::MISMATCHED_TARGET_OS), LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS), LintId::of(&attrs::USELESS_ATTRIBUTE), + LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK), + LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(&bit_mask::BAD_BIT_MASK), LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK), LintId::of(&blacklisted_name::BLACKLISTED_NAME), @@ -1736,6 +1736,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&attrs::DEPRECATED_SEMVER), LintId::of(&attrs::MISMATCHED_TARGET_OS), LintId::of(&attrs::USELESS_ATTRIBUTE), + LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK), + LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(&bit_mask::BAD_BIT_MASK), LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK), LintId::of(&booleans::LOGIC_BUG), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index c955f37b83a3..d9e019e2b611 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -62,14 +62,14 @@ vec![ }, Lint { name: "await_holding_lock", - group: "pedantic", + group: "correctness", desc: "Inside an async function, holding a MutexGuard while calling await", deprecation: None, module: "await_holding_invalid", }, Lint { name: "await_holding_refcell_ref", - group: "pedantic", + group: "correctness", desc: "Inside an async function, holding a RefCell ref while calling await", deprecation: None, module: "await_holding_invalid", From 86f2b29d2ff33862264e2e6dfdc7cc20ad054ad1 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Mon, 19 Oct 2020 12:06:43 -0400 Subject: [PATCH 09/10] Merge lints into one pass --- clippy_lints/src/await_holding_invalid.rs | 69 ++++++++--------------- clippy_lints/src/lib.rs | 3 +- 2 files changed, 24 insertions(+), 48 deletions(-) diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 1d201ff095b9..fcebb54c6c21 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -49,48 +49,6 @@ declare_clippy_lint! { "Inside an async function, holding a MutexGuard while calling await" } -declare_lint_pass!(AwaitHoldingLock => [AWAIT_HOLDING_LOCK]); - -impl LateLintPass<'_> for AwaitHoldingLock { - fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { - use AsyncGeneratorKind::{Block, Closure, Fn}; - if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { - let body_id = BodyId { - hir_id: body.value.hir_id, - }; - let def_id = cx.tcx.hir().body_owner_def_id(body_id); - let typeck_results = cx.tcx.typeck(def_id); - check_interior_types_lock(cx, &typeck_results.generator_interior_types, body.value.span); - } - } -} - -fn check_interior_types_lock(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { - for ty_cause in ty_causes { - if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { - if is_mutex_guard(cx, adt.did) { - span_lint_and_note( - cx, - AWAIT_HOLDING_LOCK, - ty_cause.span, - "this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await.", - ty_cause.scope_span.or(Some(span)), - "these are all the await points this lock is held through", - ); - } - } - } -} - -fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool { - match_def_path(cx, def_id, &paths::MUTEX_GUARD) - || match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD) - || match_def_path(cx, def_id, &paths::RWLOCK_WRITE_GUARD) - || match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD) - || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD) - || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD) -} - declare_clippy_lint! { /// **What it does:** Checks for calls to await while holding a /// `RefCell` `Ref` or `RefMut`. @@ -130,9 +88,9 @@ declare_clippy_lint! { "Inside an async function, holding a RefCell ref while calling await" } -declare_lint_pass!(AwaitHoldingRefCellRef => [AWAIT_HOLDING_REFCELL_REF]); +declare_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF]); -impl LateLintPass<'_> for AwaitHoldingRefCellRef { +impl LateLintPass<'_> for AwaitHolding { fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { use AsyncGeneratorKind::{Block, Closure, Fn}; if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { @@ -141,14 +99,24 @@ impl LateLintPass<'_> for AwaitHoldingRefCellRef { }; let def_id = cx.tcx.hir().body_owner_def_id(body_id); let typeck_results = cx.tcx.typeck(def_id); - check_interior_types_refcell(cx, &typeck_results.generator_interior_types, body.value.span); + check_interior_types(cx, &typeck_results.generator_interior_types, body.value.span); } } } -fn check_interior_types_refcell(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { +fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { for ty_cause in ty_causes { if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { + if is_mutex_guard(cx, adt.did) { + span_lint_and_note( + cx, + AWAIT_HOLDING_LOCK, + ty_cause.span, + "this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await.", + ty_cause.scope_span.or(Some(span)), + "these are all the await points this lock is held through", + ); + } if is_refcell_ref(cx, adt.did) { span_lint_and_note( cx, @@ -163,6 +131,15 @@ fn check_interior_types_refcell(cx: &LateContext<'_>, ty_causes: &[GeneratorInte } } +fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool { + match_def_path(cx, def_id, &paths::MUTEX_GUARD) + || match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD) + || match_def_path(cx, def_id, &paths::RWLOCK_WRITE_GUARD) + || match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD) + || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD) + || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD) +} + fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool { match_def_path(cx, def_id, &paths::REFCELL_REF) || match_def_path(cx, def_id, &paths::REFCELL_REFMUT) } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c627df209763..6d00e313ce1b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -906,8 +906,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ]); // end register lints, do not remove this comment, it’s used in `update_lints` - store.register_late_pass(|| box await_holding_invalid::AwaitHoldingLock); - store.register_late_pass(|| box await_holding_invalid::AwaitHoldingRefCellRef); + store.register_late_pass(|| box await_holding_invalid::AwaitHolding); store.register_late_pass(|| box serde_api::SerdeAPI); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); From 4d3322525d9b65ce4c6fc183bc1cd3cfc9477300 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Mon, 19 Oct 2020 12:07:04 -0400 Subject: [PATCH 10/10] Separate tests for each lint --- tests/ui/await_holding_invalid.rs | 145 -------------------- tests/ui/await_holding_invalid.stderr | 156 ---------------------- tests/ui/await_holding_lock.rs | 65 +++++++++ tests/ui/await_holding_lock.stderr | 63 +++++++++ tests/ui/await_holding_refcell_ref.rs | 86 ++++++++++++ tests/ui/await_holding_refcell_ref.stderr | 95 +++++++++++++ 6 files changed, 309 insertions(+), 301 deletions(-) delete mode 100644 tests/ui/await_holding_invalid.rs delete mode 100644 tests/ui/await_holding_invalid.stderr create mode 100644 tests/ui/await_holding_lock.rs create mode 100644 tests/ui/await_holding_lock.stderr create mode 100644 tests/ui/await_holding_refcell_ref.rs create mode 100644 tests/ui/await_holding_refcell_ref.stderr diff --git a/tests/ui/await_holding_invalid.rs b/tests/ui/await_holding_invalid.rs deleted file mode 100644 index 45aa3e64292e..000000000000 --- a/tests/ui/await_holding_invalid.rs +++ /dev/null @@ -1,145 +0,0 @@ -// edition:2018 -#![warn(clippy::await_holding_lock, clippy::await_holding_refcell_ref)] - -use std::cell::RefCell; -use std::sync::Mutex; - -async fn bad_lock(x: &Mutex) -> u32 { - let guard = x.lock().unwrap(); - baz().await -} - -async fn good_lock(x: &Mutex) -> u32 { - { - let guard = x.lock().unwrap(); - let y = *guard + 1; - } - baz().await; - let guard = x.lock().unwrap(); - 47 -} - -async fn baz() -> u32 { - 42 -} - -async fn also_bad_lock(x: &Mutex) -> u32 { - let first = baz().await; - - let guard = x.lock().unwrap(); - - let second = baz().await; - - let third = baz().await; - - first + second + third -} - -async fn not_good_lock(x: &Mutex) -> u32 { - let first = baz().await; - - let second = { - let guard = x.lock().unwrap(); - baz().await - }; - - let third = baz().await; - - first + second + third -} - -#[allow(clippy::manual_async_fn)] -fn block_bad_lock(x: &Mutex) -> impl std::future::Future + '_ { - async move { - let guard = x.lock().unwrap(); - baz().await - } -} - -async fn bad_refcell(x: &RefCell) -> u32 { - let b = x.borrow(); - baz().await -} - -async fn bad_mut_refcell(x: &RefCell) -> u32 { - let b = x.borrow_mut(); - baz().await -} - -async fn good_refcell(x: &RefCell) -> u32 { - { - let b = x.borrow_mut(); - let y = *b + 1; - } - baz().await; - let b = x.borrow_mut(); - 47 -} - -async fn also_bad_refcell(x: &RefCell) -> u32 { - let first = baz().await; - - let b = x.borrow_mut(); - - let second = baz().await; - - let third = baz().await; - - first + second + third -} - -async fn less_bad_refcell(x: &RefCell) -> u32 { - let first = baz().await; - - let b = x.borrow_mut(); - - let second = baz().await; - - drop(b); - - let third = baz().await; - - first + second + third -} - -async fn not_good_refcell(x: &RefCell) -> u32 { - let first = baz().await; - - let second = { - let b = x.borrow_mut(); - baz().await - }; - - let third = baz().await; - - first + second + third -} - -#[allow(clippy::manual_async_fn)] -fn block_bad_refcell(x: &RefCell) -> impl std::future::Future + '_ { - async move { - let b = x.borrow_mut(); - baz().await - } -} - -fn main() { - { - let m = Mutex::new(100); - good_lock(&m); - bad_lock(&m); - also_bad_lock(&m); - not_good_lock(&m); - block_bad_lock(&m); - } - { - let rc = RefCell::new(100); - good_refcell(&rc); - bad_refcell(&rc); - bad_mut_refcell(&rc); - also_bad_refcell(&rc); - less_bad_refcell(&rc); - not_good_refcell(&rc); - block_bad_refcell(&rc); - } -} diff --git a/tests/ui/await_holding_invalid.stderr b/tests/ui/await_holding_invalid.stderr deleted file mode 100644 index c8d49820c020..000000000000 --- a/tests/ui/await_holding_invalid.stderr +++ /dev/null @@ -1,156 +0,0 @@ -error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:8:9 - | -LL | let guard = x.lock().unwrap(); - | ^^^^^ - | - = note: `-D clippy::await-holding-lock` implied by `-D warnings` -note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:8:5 - | -LL | / let guard = x.lock().unwrap(); -LL | | baz().await -LL | | } - | |_^ - -error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:29:9 - | -LL | let guard = x.lock().unwrap(); - | ^^^^^ - | -note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:29:5 - | -LL | / let guard = x.lock().unwrap(); -LL | | -LL | | let second = baz().await; -LL | | -... | -LL | | first + second + third -LL | | } - | |_^ - -error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:42:13 - | -LL | let guard = x.lock().unwrap(); - | ^^^^^ - | -note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:42:9 - | -LL | / let guard = x.lock().unwrap(); -LL | | baz().await -LL | | }; - | |_____^ - -error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:54:13 - | -LL | let guard = x.lock().unwrap(); - | ^^^^^ - | -note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:54:9 - | -LL | / let guard = x.lock().unwrap(); -LL | | baz().await -LL | | } - | |_____^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_invalid.rs:60:9 - | -LL | let b = x.borrow(); - | ^ - | - = note: `-D clippy::await-holding-refcell-ref` implied by `-D warnings` -note: these are all the await points this ref is held through - --> $DIR/await_holding_invalid.rs:60:5 - | -LL | / let b = x.borrow(); -LL | | baz().await -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_invalid.rs:65:9 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_invalid.rs:65:5 - | -LL | / let b = x.borrow_mut(); -LL | | baz().await -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_invalid.rs:82:9 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_invalid.rs:82:5 - | -LL | / let b = x.borrow_mut(); -LL | | -LL | | let second = baz().await; -LL | | -... | -LL | | first + second + third -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_invalid.rs:94:9 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_invalid.rs:94:5 - | -LL | / let b = x.borrow_mut(); -LL | | -LL | | let second = baz().await; -LL | | -... | -LL | | first + second + third -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_invalid.rs:109:13 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_invalid.rs:109:9 - | -LL | / let b = x.borrow_mut(); -LL | | baz().await -LL | | }; - | |_____^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_invalid.rs:121:13 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_invalid.rs:121:9 - | -LL | / let b = x.borrow_mut(); -LL | | baz().await -LL | | } - | |_____^ - -error: aborting due to 10 previous errors - diff --git a/tests/ui/await_holding_lock.rs b/tests/ui/await_holding_lock.rs new file mode 100644 index 000000000000..0458950edee1 --- /dev/null +++ b/tests/ui/await_holding_lock.rs @@ -0,0 +1,65 @@ +// edition:2018 +#![warn(clippy::await_holding_lock)] + +use std::sync::Mutex; + +async fn bad(x: &Mutex) -> u32 { + let guard = x.lock().unwrap(); + baz().await +} + +async fn good(x: &Mutex) -> u32 { + { + let guard = x.lock().unwrap(); + let y = *guard + 1; + } + baz().await; + let guard = x.lock().unwrap(); + 47 +} + +async fn baz() -> u32 { + 42 +} + +async fn also_bad(x: &Mutex) -> u32 { + let first = baz().await; + + let guard = x.lock().unwrap(); + + let second = baz().await; + + let third = baz().await; + + first + second + third +} + +async fn not_good(x: &Mutex) -> u32 { + let first = baz().await; + + let second = { + let guard = x.lock().unwrap(); + baz().await + }; + + let third = baz().await; + + first + second + third +} + +#[allow(clippy::manual_async_fn)] +fn block_bad(x: &Mutex) -> impl std::future::Future + '_ { + async move { + let guard = x.lock().unwrap(); + baz().await + } +} + +fn main() { + let m = Mutex::new(100); + good(&m); + bad(&m); + also_bad(&m); + not_good(&m); + block_bad(&m); +} diff --git a/tests/ui/await_holding_lock.stderr b/tests/ui/await_holding_lock.stderr new file mode 100644 index 000000000000..21bf49d16f04 --- /dev/null +++ b/tests/ui/await_holding_lock.stderr @@ -0,0 +1,63 @@ +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. + --> $DIR/await_holding_lock.rs:7:9 + | +LL | let guard = x.lock().unwrap(); + | ^^^^^ + | + = note: `-D clippy::await-holding-lock` implied by `-D warnings` +note: these are all the await points this lock is held through + --> $DIR/await_holding_lock.rs:7:5 + | +LL | / let guard = x.lock().unwrap(); +LL | | baz().await +LL | | } + | |_^ + +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. + --> $DIR/await_holding_lock.rs:28:9 + | +LL | let guard = x.lock().unwrap(); + | ^^^^^ + | +note: these are all the await points this lock is held through + --> $DIR/await_holding_lock.rs:28:5 + | +LL | / let guard = x.lock().unwrap(); +LL | | +LL | | let second = baz().await; +LL | | +... | +LL | | first + second + third +LL | | } + | |_^ + +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. + --> $DIR/await_holding_lock.rs:41:13 + | +LL | let guard = x.lock().unwrap(); + | ^^^^^ + | +note: these are all the await points this lock is held through + --> $DIR/await_holding_lock.rs:41:9 + | +LL | / let guard = x.lock().unwrap(); +LL | | baz().await +LL | | }; + | |_____^ + +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. + --> $DIR/await_holding_lock.rs:53:13 + | +LL | let guard = x.lock().unwrap(); + | ^^^^^ + | +note: these are all the await points this lock is held through + --> $DIR/await_holding_lock.rs:53:9 + | +LL | / let guard = x.lock().unwrap(); +LL | | baz().await +LL | | } + | |_____^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/await_holding_refcell_ref.rs b/tests/ui/await_holding_refcell_ref.rs new file mode 100644 index 000000000000..88841597bb60 --- /dev/null +++ b/tests/ui/await_holding_refcell_ref.rs @@ -0,0 +1,86 @@ +// edition:2018 +#![warn(clippy::await_holding_refcell_ref)] + +use std::cell::RefCell; + +async fn bad(x: &RefCell) -> u32 { + let b = x.borrow(); + baz().await +} + +async fn bad_mut(x: &RefCell) -> u32 { + let b = x.borrow_mut(); + baz().await +} + +async fn good(x: &RefCell) -> u32 { + { + let b = x.borrow_mut(); + let y = *b + 1; + } + baz().await; + let b = x.borrow_mut(); + 47 +} + +async fn baz() -> u32 { + 42 +} + +async fn also_bad(x: &RefCell) -> u32 { + let first = baz().await; + + let b = x.borrow_mut(); + + let second = baz().await; + + let third = baz().await; + + first + second + third +} + +async fn less_bad(x: &RefCell) -> u32 { + let first = baz().await; + + let b = x.borrow_mut(); + + let second = baz().await; + + drop(b); + + let third = baz().await; + + first + second + third +} + +async fn not_good(x: &RefCell) -> u32 { + let first = baz().await; + + let second = { + let b = x.borrow_mut(); + baz().await + }; + + let third = baz().await; + + first + second + third +} + +#[allow(clippy::manual_async_fn)] +fn block_bad(x: &RefCell) -> impl std::future::Future + '_ { + async move { + let b = x.borrow_mut(); + baz().await + } +} + +fn main() { + let rc = RefCell::new(100); + good(&rc); + bad(&rc); + bad_mut(&rc); + also_bad(&rc); + less_bad(&rc); + not_good(&rc); + block_bad(&rc); +} diff --git a/tests/ui/await_holding_refcell_ref.stderr b/tests/ui/await_holding_refcell_ref.stderr new file mode 100644 index 000000000000..b504f0454913 --- /dev/null +++ b/tests/ui/await_holding_refcell_ref.stderr @@ -0,0 +1,95 @@ +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:7:9 + | +LL | let b = x.borrow(); + | ^ + | + = note: `-D clippy::await-holding-refcell-ref` implied by `-D warnings` +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:7:5 + | +LL | / let b = x.borrow(); +LL | | baz().await +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:12:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:12:5 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:33:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:33:5 + | +LL | / let b = x.borrow_mut(); +LL | | +LL | | let second = baz().await; +LL | | +... | +LL | | first + second + third +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:45:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:45:5 + | +LL | / let b = x.borrow_mut(); +LL | | +LL | | let second = baz().await; +LL | | +... | +LL | | first + second + third +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:60:13 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:60:9 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | }; + | |_____^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:72:13 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:72:9 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | } + | |_____^ + +error: aborting due to 6 previous errors +