|  | use clippy_config::Conf; | 
|  | use clippy_config::types::{DisallowedPathWithoutReplacement, create_disallowed_map}; | 
|  | use clippy_utils::diagnostics::span_lint_and_then; | 
|  | use clippy_utils::paths::{self, PathNS}; | 
|  | use rustc_hir as hir; | 
|  | use rustc_hir::def_id::{DefId, DefIdMap}; | 
|  | use rustc_lint::{LateContext, LateLintPass}; | 
|  | use rustc_middle::mir::CoroutineLayout; | 
|  | use rustc_middle::ty::TyCtxt; | 
|  | use rustc_session::impl_lint_pass; | 
|  | use rustc_span::{Span, sym}; | 
|  |  | 
|  | declare_clippy_lint! { | 
|  | /// ### What it does | 
|  | /// Checks for calls to `await` while holding a non-async-aware | 
|  | /// `MutexGuard`. | 
|  | /// | 
|  | /// ### Why is this bad? | 
|  | /// The Mutex types found in [`std::sync`](https://doc.rust-lang.org/stable/std/sync/) and | 
|  | /// [`parking_lot`](https://docs.rs/parking_lot/latest/parking_lot/) are | 
|  | /// not designed to operate in an async context across await points. | 
|  | /// | 
|  | /// There are two potential solutions. One is to use an async-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`](https://doc.rust-lang.org/std/ops/trait.Drop.html). | 
|  | /// | 
|  | /// ### Known problems | 
|  | /// Will report false positive for explicitly dropped guards | 
|  | /// ([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)). A | 
|  | /// workaround for this is to wrap the `.lock()` call in a block instead of | 
|  | /// explicitly dropping the guard. | 
|  | /// | 
|  | /// ### Example | 
|  | /// ```no_run | 
|  | /// # use std::sync::Mutex; | 
|  | /// # async fn baz() {} | 
|  | /// async fn foo(x: &Mutex<u32>) { | 
|  | ///   let mut guard = x.lock().unwrap(); | 
|  | ///   *guard += 1; | 
|  | ///   baz().await; | 
|  | /// } | 
|  | /// | 
|  | /// async fn bar(x: &Mutex<u32>) { | 
|  | ///   let mut guard = x.lock().unwrap(); | 
|  | ///   *guard += 1; | 
|  | ///   drop(guard); // explicit drop | 
|  | ///   baz().await; | 
|  | /// } | 
|  | /// ``` | 
|  | /// | 
|  | /// Use instead: | 
|  | /// ```no_run | 
|  | /// # use std::sync::Mutex; | 
|  | /// # async fn baz() {} | 
|  | /// async fn foo(x: &Mutex<u32>) { | 
|  | ///   { | 
|  | ///     let mut guard = x.lock().unwrap(); | 
|  | ///     *guard += 1; | 
|  | ///   } | 
|  | ///   baz().await; | 
|  | /// } | 
|  | /// | 
|  | /// async fn bar(x: &Mutex<u32>) { | 
|  | ///   { | 
|  | ///     let mut guard = x.lock().unwrap(); | 
|  | ///     *guard += 1; | 
|  | ///   } // guard dropped here at end of scope | 
|  | ///   baz().await; | 
|  | /// } | 
|  | /// ``` | 
|  | #[clippy::version = "1.45.0"] | 
|  | pub AWAIT_HOLDING_LOCK, | 
|  | suspicious, | 
|  | "inside an async function, holding a `MutexGuard` while calling `await`" | 
|  | } | 
|  |  | 
|  | 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 a `RefCell` ref across an await suspension point | 
|  | /// risks panics from a mutable ref shared while other refs are outstanding. | 
|  | /// | 
|  | /// ### Known problems | 
|  | /// Will report false positive for explicitly dropped refs | 
|  | /// ([#6353](https://github.com/rust-lang/rust-clippy/issues/6353)). A workaround for this is | 
|  | /// to wrap the `.borrow[_mut]()` call in a block instead of explicitly dropping the ref. | 
|  | /// | 
|  | /// ### Example | 
|  | /// ```no_run | 
|  | /// # use std::cell::RefCell; | 
|  | /// # async fn baz() {} | 
|  | /// async fn foo(x: &RefCell<u32>) { | 
|  | ///   let mut y = x.borrow_mut(); | 
|  | ///   *y += 1; | 
|  | ///   baz().await; | 
|  | /// } | 
|  | /// | 
|  | /// async fn bar(x: &RefCell<u32>) { | 
|  | ///   let mut y = x.borrow_mut(); | 
|  | ///   *y += 1; | 
|  | ///   drop(y); // explicit drop | 
|  | ///   baz().await; | 
|  | /// } | 
|  | /// ``` | 
|  | /// | 
|  | /// Use instead: | 
|  | /// ```no_run | 
|  | /// # use std::cell::RefCell; | 
|  | /// # async fn baz() {} | 
|  | /// async fn foo(x: &RefCell<u32>) { | 
|  | ///   { | 
|  | ///      let mut y = x.borrow_mut(); | 
|  | ///      *y += 1; | 
|  | ///   } | 
|  | ///   baz().await; | 
|  | /// } | 
|  | /// | 
|  | /// async fn bar(x: &RefCell<u32>) { | 
|  | ///   { | 
|  | ///     let mut y = x.borrow_mut(); | 
|  | ///     *y += 1; | 
|  | ///   } // y dropped here at end of scope | 
|  | ///   baz().await; | 
|  | /// } | 
|  | /// ``` | 
|  | #[clippy::version = "1.49.0"] | 
|  | pub AWAIT_HOLDING_REFCELL_REF, | 
|  | suspicious, | 
|  | "inside an async function, holding a `RefCell` ref while calling `await`" | 
|  | } | 
|  |  | 
|  | declare_clippy_lint! { | 
|  | /// ### What it does | 
|  | /// Allows users to configure types which should not be held across await | 
|  | /// suspension points. | 
|  | /// | 
|  | /// ### Why is this bad? | 
|  | /// There are some types which are perfectly safe to use concurrently from | 
|  | /// a memory access perspective, but that will cause bugs at runtime if | 
|  | /// they are held in such a way. | 
|  | /// | 
|  | /// ### Example | 
|  | /// | 
|  | /// ```toml | 
|  | /// await-holding-invalid-types = [ | 
|  | ///   # You can specify a type name | 
|  | ///   "CustomLockType", | 
|  | ///   # You can (optionally) specify a reason | 
|  | ///   { path = "OtherCustomLockType", reason = "Relies on a thread local" } | 
|  | /// ] | 
|  | /// ``` | 
|  | /// | 
|  | /// ```no_run | 
|  | /// # async fn baz() {} | 
|  | /// struct CustomLockType; | 
|  | /// struct OtherCustomLockType; | 
|  | /// async fn foo() { | 
|  | ///   let _x = CustomLockType; | 
|  | ///   let _y = OtherCustomLockType; | 
|  | ///   baz().await; // Lint violation | 
|  | /// } | 
|  | /// ``` | 
|  | #[clippy::version = "1.62.0"] | 
|  | pub AWAIT_HOLDING_INVALID_TYPE, | 
|  | suspicious, | 
|  | "holding a type across an await point which is not allowed to be held as per the configuration" | 
|  | } | 
|  |  | 
|  | impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF, AWAIT_HOLDING_INVALID_TYPE]); | 
|  |  | 
|  | pub struct AwaitHolding { | 
|  | def_ids: DefIdMap<(&'static str, &'static DisallowedPathWithoutReplacement)>, | 
|  | } | 
|  |  | 
|  | impl AwaitHolding { | 
|  | pub(crate) fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { | 
|  | let (def_ids, _) = create_disallowed_map( | 
|  | tcx, | 
|  | &conf.await_holding_invalid_types, | 
|  | PathNS::Type, | 
|  | crate::disallowed_types::def_kind_predicate, | 
|  | "type", | 
|  | false, | 
|  | ); | 
|  | Self { def_ids } | 
|  | } | 
|  | } | 
|  |  | 
|  | impl<'tcx> LateLintPass<'tcx> for AwaitHolding { | 
|  | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { | 
|  | if let hir::ExprKind::Closure(hir::Closure { | 
|  | kind: hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)), | 
|  | def_id, | 
|  | .. | 
|  | }) = expr.kind | 
|  | && let Some(coroutine_layout) = cx.tcx.mir_coroutine_witnesses(*def_id) | 
|  | { | 
|  | self.check_interior_types(cx, coroutine_layout); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | impl AwaitHolding { | 
|  | fn check_interior_types(&self, cx: &LateContext<'_>, coroutine: &CoroutineLayout<'_>) { | 
|  | for (ty_index, ty_cause) in coroutine.field_tys.iter_enumerated() { | 
|  | if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { | 
|  | let await_points = || { | 
|  | coroutine | 
|  | .variant_source_info | 
|  | .iter_enumerated() | 
|  | .filter_map(|(variant, source_info)| { | 
|  | coroutine.variant_fields[variant] | 
|  | .raw | 
|  | .contains(&ty_index) | 
|  | .then_some(source_info.span) | 
|  | }) | 
|  | .collect::<Vec<_>>() | 
|  | }; | 
|  | if is_mutex_guard(cx, adt.did()) { | 
|  | span_lint_and_then( | 
|  | cx, | 
|  | AWAIT_HOLDING_LOCK, | 
|  | ty_cause.source_info.span, | 
|  | "this `MutexGuard` is held across an await point", | 
|  | |diag| { | 
|  | diag.help( | 
|  | "consider using an async-aware `Mutex` type or ensuring the \ | 
|  | `MutexGuard` is dropped before calling `await`", | 
|  | ); | 
|  | diag.span_note( | 
|  | await_points(), | 
|  | "these are all the await points this lock is held through", | 
|  | ); | 
|  | }, | 
|  | ); | 
|  | } else if is_refcell_ref(cx, adt.did()) { | 
|  | span_lint_and_then( | 
|  | cx, | 
|  | AWAIT_HOLDING_REFCELL_REF, | 
|  | ty_cause.source_info.span, | 
|  | "this `RefCell` reference is held across an await point", | 
|  | |diag| { | 
|  | diag.help("ensure the reference is dropped before calling `await`"); | 
|  | diag.span_note( | 
|  | await_points(), | 
|  | "these are all the await points this reference is held through", | 
|  | ); | 
|  | }, | 
|  | ); | 
|  | } else if let Some(&(path, disallowed_path)) = self.def_ids.get(&adt.did()) { | 
|  | emit_invalid_type(cx, ty_cause.source_info.span, path, disallowed_path); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | fn emit_invalid_type( | 
|  | cx: &LateContext<'_>, | 
|  | span: Span, | 
|  | path: &'static str, | 
|  | disallowed_path: &'static DisallowedPathWithoutReplacement, | 
|  | ) { | 
|  | span_lint_and_then( | 
|  | cx, | 
|  | AWAIT_HOLDING_INVALID_TYPE, | 
|  | span, | 
|  | format!("holding a disallowed type across an await point `{path}`"), | 
|  | disallowed_path.diag_amendment(span), | 
|  | ); | 
|  | } | 
|  |  | 
|  | fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool { | 
|  | match cx.tcx.get_diagnostic_name(def_id) { | 
|  | Some(name) => matches!(name, sym::MutexGuard | sym::RwLockReadGuard | sym::RwLockWriteGuard), | 
|  | None => paths::PARKING_LOT_GUARDS.iter().any(|guard| guard.matches(cx, def_id)), | 
|  | } | 
|  | } | 
|  |  | 
|  | fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool { | 
|  | matches!( | 
|  | cx.tcx.get_diagnostic_name(def_id), | 
|  | Some(sym::RefCellRef | sym::RefCellRefMut) | 
|  | ) | 
|  | } |