From 6c25c3c381ccfa0bf9b7bc7386a09cc4421fd790 Mon Sep 17 00:00:00 2001 From: Andy Weiss Date: Tue, 7 Apr 2020 21:20:37 -0700 Subject: [PATCH 1/5] Lint for holding locks across await points Fixes #4226 This introduces the lint await_holding_lock. For async functions, we iterate over all types in generator_interior_types and look for types named MutexGuard, RwLockReadGuard, or RwLockWriteGuard. If we find one then we emit a lint. --- CHANGELOG.md | 1 + clippy_lints/src/await_holding_lock.rs | 100 +++++++++++++++++++++++++ clippy_lints/src/lib.rs | 4 + src/lintlist/mod.rs | 7 ++ tests/ui/await_holding_lock.rs | 42 +++++++++++ tests/ui/await_holding_lock.stderr | 35 +++++++++ 6 files changed, 189 insertions(+) create mode 100644 clippy_lints/src/await_holding_lock.rs create mode 100644 tests/ui/await_holding_lock.rs create mode 100644 tests/ui/await_holding_lock.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index d244f8aa167b..abd7167502b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1188,6 +1188,7 @@ Released 2018-09-13 [`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants [`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern [`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops +[`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock [`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask [`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name [`block_in_if_condition_expr`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_expr diff --git a/clippy_lints/src/await_holding_lock.rs b/clippy_lints/src/await_holding_lock.rs new file mode 100644 index 000000000000..ae4c5bfc9f7c --- /dev/null +++ b/clippy_lints/src/await_holding_lock.rs @@ -0,0 +1,100 @@ +use crate::utils::span_lint_and_note; +use if_chain::if_chain; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, FnDecl, HirId, IsAsync}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{Span, Symbol}; + +declare_clippy_lint! { + /// **What it does:** Checks for calls to await while holding a MutexGuard. + /// + /// **Why is this bad?** This is almost certainly an error which can result + /// in a deadlock because the reactor will invoke code not visible to the + /// currently visible scope. + /// + /// **Known problems:** Detects only specifically named guard types: + /// MutexGuard, RwLockReadGuard, and RwLockWriteGuard. + /// + /// **Example:** + /// + /// ```rust + /// use std::sync::Mutex; + /// + /// async fn foo(x: &Mutex) { + /// let guard = x.lock().unwrap(); + /// *guard += 1; + /// bar.await; + /// } + /// ``` + /// Use instead: + /// ```rust + /// use std::sync::Mutex; + /// + /// async fn foo(x: &Mutex) { + /// { + /// let guard = x.lock().unwrap(); + /// *guard += 1; + /// } + /// bar.await; + /// } + /// ``` + pub AWAIT_HOLDING_LOCK, + pedantic, + "Inside an async function, holding a MutexGuard while calling await" +} + +const MUTEX_GUARD_TYPES: [&str; 3] = ["MutexGuard", "RwLockReadGuard", "RwLockWriteGuard"]; + +declare_lint_pass!(AwaitHoldingLock => [AWAIT_HOLDING_LOCK]); + +impl LateLintPass<'_, '_> for AwaitHoldingLock { + fn check_fn( + &mut self, + cx: &LateContext<'_, '_>, + fn_kind: FnKind<'_>, + _: &FnDecl<'_>, + _: &Body<'_>, + span: Span, + _: HirId, + ) { + if !is_async_fn(fn_kind) { + return; + } + + for ty_clause in &cx.tables.generator_interior_types { + if_chain! { + if let rustc_middle::ty::Adt(adt, _) = ty_clause.ty.kind; + if let Some(&sym) = cx.get_def_path(adt.did).iter().last(); + if is_symbol_mutex_guard(sym); + then { + span_lint_and_note( + cx, + AWAIT_HOLDING_LOCK, + ty_clause.span, + "this MutexGuard is held across an 'await' point", + ty_clause.scope_span.unwrap_or(span), + "these are all the await points this lock is held through" + ); + } + } + } + } +} + +fn is_async_fn(fn_kind: FnKind<'_>) -> bool { + fn_kind.header().map_or(false, |h| match h.asyncness { + IsAsync::Async => true, + IsAsync::NotAsync => false, + }) +} + +fn is_symbol_mutex_guard(sym: Symbol) -> bool { + let sym_str = sym.as_str(); + for ty in &MUTEX_GUARD_TYPES { + if sym_str == *ty { + return true; + } + } + false +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f3f73835e848..dee4188b75f3 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -177,6 +177,7 @@ mod assertions_on_constants; mod assign_ops; mod atomic_ordering; mod attrs; +mod await_holding_lock; mod bit_mask; mod blacklisted_name; mod block_in_if_condition; @@ -497,6 +498,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &attrs::INLINE_ALWAYS, &attrs::UNKNOWN_CLIPPY_LINTS, &attrs::USELESS_ATTRIBUTE, + &await_holding_lock::AWAIT_HOLDING_LOCK, &bit_mask::BAD_BIT_MASK, &bit_mask::INEFFECTIVE_BIT_MASK, &bit_mask::VERBOSE_BIT_MASK, @@ -864,6 +866,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 serde_api::SerdeAPI); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); @@ -1102,6 +1105,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(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&copies::MATCH_SAME_ARMS), LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 95931be73416..2c466aa20c67 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -52,6 +52,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "assign_ops", }, + Lint { + name: "await_holding_lock", + group: "pedantic", + desc: "Inside an async function, holding a MutexGuard while calling await", + deprecation: None, + module: "await_holding_lock", + }, Lint { name: "bad_bit_mask", group: "correctness", diff --git a/tests/ui/await_holding_lock.rs b/tests/ui/await_holding_lock.rs new file mode 100644 index 000000000000..fab31f37ffcc --- /dev/null +++ b/tests/ui/await_holding_lock.rs @@ -0,0 +1,42 @@ +// 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 +} + +fn main() { + let m = Mutex::new(100); + good(&m); + bad(&m); + also_bad(&m); +} diff --git a/tests/ui/await_holding_lock.stderr b/tests/ui/await_holding_lock.stderr new file mode 100644 index 000000000000..8d4fd0c20a9c --- /dev/null +++ b/tests/ui/await_holding_lock.stderr @@ -0,0 +1,35 @@ +error: this MutexGuard is held across an 'await' point + --> $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 + --> $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: aborting due to 2 previous errors + From 2dc8c083f54454ca87bb09d691577eada2d23539 Mon Sep 17 00:00:00 2001 From: Andy Weiss Date: Thu, 9 Apr 2020 21:50:23 -0700 Subject: [PATCH 2/5] Switch to matching against full paths instead of just the last element of the path --- clippy_lints/src/await_holding_lock.rs | 48 +++++++++++--------------- clippy_lints/src/utils/paths.rs | 3 ++ 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/clippy_lints/src/await_holding_lock.rs b/clippy_lints/src/await_holding_lock.rs index ae4c5bfc9f7c..8e3e0ed0430b 100644 --- a/clippy_lints/src/await_holding_lock.rs +++ b/clippy_lints/src/await_holding_lock.rs @@ -1,10 +1,10 @@ -use crate::utils::span_lint_and_note; -use if_chain::if_chain; +use crate::utils::{match_def_path, paths, span_lint_and_note}; +use rustc_hir::def_id::DefId; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, HirId, IsAsync}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{Span, Symbol}; +use rustc_span::Span; declare_clippy_lint! { /// **What it does:** Checks for calls to await while holding a MutexGuard. @@ -44,8 +44,6 @@ declare_clippy_lint! { "Inside an async function, holding a MutexGuard while calling await" } -const MUTEX_GUARD_TYPES: [&str; 3] = ["MutexGuard", "RwLockReadGuard", "RwLockWriteGuard"]; - declare_lint_pass!(AwaitHoldingLock => [AWAIT_HOLDING_LOCK]); impl LateLintPass<'_, '_> for AwaitHoldingLock { @@ -62,21 +60,18 @@ impl LateLintPass<'_, '_> for AwaitHoldingLock { return; } - for ty_clause in &cx.tables.generator_interior_types { - if_chain! { - if let rustc_middle::ty::Adt(adt, _) = ty_clause.ty.kind; - if let Some(&sym) = cx.get_def_path(adt.did).iter().last(); - if is_symbol_mutex_guard(sym); - then { - span_lint_and_note( - cx, - AWAIT_HOLDING_LOCK, - ty_clause.span, - "this MutexGuard is held across an 'await' point", - ty_clause.scope_span.unwrap_or(span), - "these are all the await points this lock is held through" + for ty_cause in &cx.tables.generator_interior_types { + 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", + ty_cause.scope_span.unwrap_or(span), + "these are all the await points this lock is held through", ); - } + } } } } @@ -89,12 +84,11 @@ fn is_async_fn(fn_kind: FnKind<'_>) -> bool { }) } -fn is_symbol_mutex_guard(sym: Symbol) -> bool { - let sym_str = sym.as_str(); - for ty in &MUTEX_GUARD_TYPES { - if sym_str == *ty { - return true; - } - } - false +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) } diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index f85845be56dc..7ad09eabec15 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -72,6 +72,9 @@ pub const ORD: [&str; 3] = ["core", "cmp", "Ord"]; pub const OS_STRING: [&str; 4] = ["std", "ffi", "os_str", "OsString"]; pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"]; pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"]; +pub const PARKING_LOT_MUTEX_GUARD: [&str; 2] = ["parking_lot", "MutexGuard"]; +pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 2] = ["parking_lot", "RwLockReadGuard"]; +pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 2] = ["parking_lot", "RwLockWriteGuard"]; pub const PATH: [&str; 3] = ["std", "path", "Path"]; pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"]; pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"]; From 54e7f7e5f2d38ef74e5e3e8de44beccc251cf5d7 Mon Sep 17 00:00:00 2001 From: Andy Weiss Date: Thu, 9 Apr 2020 22:12:34 -0700 Subject: [PATCH 3/5] don't test the code in the lint docs --- clippy_lints/src/await_holding_lock.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/await_holding_lock.rs b/clippy_lints/src/await_holding_lock.rs index 8e3e0ed0430b..1b2b020bd555 100644 --- a/clippy_lints/src/await_holding_lock.rs +++ b/clippy_lints/src/await_holding_lock.rs @@ -18,7 +18,7 @@ declare_clippy_lint! { /// /// **Example:** /// - /// ```rust + /// ```rust,ignore /// use std::sync::Mutex; /// /// async fn foo(x: &Mutex) { @@ -28,7 +28,7 @@ declare_clippy_lint! { /// } /// ``` /// Use instead: - /// ```rust + /// ```rust,ignore /// use std::sync::Mutex; /// /// async fn foo(x: &Mutex) { From d6e55e97ff7060a2d752ff2413211adc7f8f4469 Mon Sep 17 00:00:00 2001 From: Andy Weiss Date: Thu, 16 Apr 2020 23:21:49 -0700 Subject: [PATCH 4/5] Make lint also capture blocks and closures, adjust language to mention other mutex types --- clippy_lints/src/await_holding_lock.rs | 79 +++++++++++++------------- tests/ui/await_holding_lock.rs | 22 +++++++ tests/ui/await_holding_lock.stderr | 34 ++++++++++- 3 files changed, 94 insertions(+), 41 deletions(-) diff --git a/clippy_lints/src/await_holding_lock.rs b/clippy_lints/src/await_holding_lock.rs index 1b2b020bd555..b2aa3437923d 100644 --- a/clippy_lints/src/await_holding_lock.rs +++ b/clippy_lints/src/await_holding_lock.rs @@ -1,20 +1,24 @@ use crate::utils::{match_def_path, paths, span_lint_and_note}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, FnDecl, HirId, IsAsync}; +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 MutexGuard. + /// **What it does:** Checks for calls to await while holding a + /// non-async-aware MutexGuard. /// - /// **Why is this bad?** This is almost certainly an error which can result - /// in a deadlock because the reactor will invoke code not visible to the - /// currently visible scope. + /// **Why is this bad?** The Mutex types found in syd::sync and parking_lot + /// are not designed to operator in an async context across await points. /// - /// **Known problems:** Detects only specifically named guard types: - /// MutexGuard, RwLockReadGuard, and RwLockWriteGuard. + /// There are two potential solutions. One is to use an asynx-aware Mutex + /// type. Many asynchronous foundation crates provide such a Mutex type. The + /// other solution is to ensure the mutex is unlocked before calling await, + /// either by introducing a scope or an explicit call to Drop::drop. + /// + /// **Known problems:** None. /// /// **Example:** /// @@ -27,6 +31,7 @@ declare_clippy_lint! { /// bar.await; /// } /// ``` + /// /// Use instead: /// ```rust,ignore /// use std::sync::Mutex; @@ -47,43 +52,41 @@ declare_clippy_lint! { declare_lint_pass!(AwaitHoldingLock => [AWAIT_HOLDING_LOCK]); impl LateLintPass<'_, '_> for AwaitHoldingLock { - fn check_fn( - &mut self, - cx: &LateContext<'_, '_>, - fn_kind: FnKind<'_>, - _: &FnDecl<'_>, - _: &Body<'_>, - span: Span, - _: HirId, - ) { - if !is_async_fn(fn_kind) { - return; + fn check_body(&mut self, cx: &LateContext<'_, '_>, body: &'_ Body<'_>) { + use AsyncGeneratorKind::{Block, Closure, Fn}; + match body.generator_kind { + Some(GeneratorKind::Async(Block)) + | Some(GeneratorKind::Async(Closure)) + | Some(GeneratorKind::Async(Fn)) => { + let body_id = BodyId { + hir_id: body.value.hir_id, + }; + let def_id = cx.tcx.hir().body_owner_def_id(body_id); + let tables = cx.tcx.typeck_tables_of(def_id); + check_interior_types(cx, &tables.generator_interior_types, body.value.span); + }, + _ => {}, } + } +} - for ty_cause in &cx.tables.generator_interior_types { - 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", - ty_cause.scope_span.unwrap_or(span), - "these are all the await points this lock is held through", - ); - } +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.unwrap_or(span), + "these are all the await points this lock is held through", + ); } } } } -fn is_async_fn(fn_kind: FnKind<'_>) -> bool { - fn_kind.header().map_or(false, |h| match h.asyncness { - IsAsync::Async => true, - IsAsync::NotAsync => false, - }) -} - 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) diff --git a/tests/ui/await_holding_lock.rs b/tests/ui/await_holding_lock.rs index fab31f37ffcc..5c1fdd83efb0 100644 --- a/tests/ui/await_holding_lock.rs +++ b/tests/ui/await_holding_lock.rs @@ -34,9 +34,31 @@ async fn also_bad(x: &Mutex) -> u32 { 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 +} + +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 index 8d4fd0c20a9c..8c47cb37d8c9 100644 --- a/tests/ui/await_holding_lock.stderr +++ b/tests/ui/await_holding_lock.stderr @@ -1,4 +1,4 @@ -error: this MutexGuard is held across an 'await' point +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(); @@ -13,7 +13,7 @@ LL | | baz().await LL | | } | |_^ -error: this MutexGuard is held across an 'await' point +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(); @@ -31,5 +31,33 @@ LL | | first + second + third LL | | } | |_^ -error: aborting due to 2 previous errors +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:52:13 + | +LL | let guard = x.lock().unwrap(); + | ^^^^^ + | +note: these are all the await points this lock is held through + --> $DIR/await_holding_lock.rs:52:9 + | +LL | / let guard = x.lock().unwrap(); +LL | | baz().await +LL | | } + | |_____^ + +error: aborting due to 4 previous errors From 8b052d3142fe3e335c676981c58235328268805e Mon Sep 17 00:00:00 2001 From: Andy Weiss Date: Tue, 21 Apr 2020 21:28:23 -0700 Subject: [PATCH 5/5] span_lint_and_note now takes an Option for the note_span instead of just a span --- clippy_lints/src/await_holding_lock.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/await_holding_lock.rs b/clippy_lints/src/await_holding_lock.rs index b2aa3437923d..832910763e60 100644 --- a/clippy_lints/src/await_holding_lock.rs +++ b/clippy_lints/src/await_holding_lock.rs @@ -79,7 +79,7 @@ fn check_interior_types(cx: &LateContext<'_, '_>, ty_causes: &[GeneratorInterior 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.unwrap_or(span), + ty_cause.scope_span.or(Some(span)), "these are all the await points this lock is held through", ); }