| use clippy_utils::diagnostics::span_lint_and_then; | |
| use clippy_utils::res::MaybeDef; | |
| use clippy_utils::source::{IntoSpan, SpanRangeExt}; | |
| use clippy_utils::sugg::Sugg; | |
| use clippy_utils::ty::ty_from_hir_ty; | |
| use rustc_errors::{Applicability, Diag}; | |
| use rustc_hir::{self as hir, Expr, ExprKind, Item, ItemKind, LetStmt, QPath}; | |
| use rustc_lint::{LateContext, LateLintPass, LintContext}; | |
| use rustc_middle::mir::Mutability; | |
| use rustc_middle::ty::{self, IntTy, Ty, UintTy}; | |
| use rustc_session::declare_lint_pass; | |
| use rustc_span::sym; | |
| declare_clippy_lint! { | |
| /// ### What it does | |
| /// Checks for usage of `Mutex<X>` where an atomic will do. | |
| /// | |
| /// ### Why restrict this? | |
| /// Using a mutex just to make access to a plain bool or | |
| /// reference sequential is shooting flies with cannons. | |
| /// `std::sync::atomic::AtomicBool` and `std::sync::atomic::AtomicPtr` are leaner and | |
| /// faster. | |
| /// | |
| /// On the other hand, `Mutex`es are, in general, easier to | |
| /// verify correctness. An atomic does not behave the same as | |
| /// an equivalent mutex. See [this issue](https://github.com/rust-lang/rust-clippy/issues/4295)'s | |
| /// commentary for more details. | |
| /// | |
| /// ### Known problems | |
| /// * This lint cannot detect if the mutex is actually used | |
| /// for waiting before a critical section. | |
| /// * This lint has a false positive that warns without considering the case | |
| /// where `Mutex` is used together with `Condvar`. | |
| /// | |
| /// ### Example | |
| /// ```no_run | |
| /// # let y = true; | |
| /// # use std::sync::Mutex; | |
| /// let x = Mutex::new(&y); | |
| /// ``` | |
| /// | |
| /// Use instead: | |
| /// ```no_run | |
| /// # let y = true; | |
| /// # use std::sync::atomic::AtomicBool; | |
| /// let x = AtomicBool::new(y); | |
| /// ``` | |
| #[clippy::version = "pre 1.29.0"] | |
| pub MUTEX_ATOMIC, | |
| restriction, | |
| "using a mutex where an atomic value could be used instead." | |
| } | |
| declare_clippy_lint! { | |
| /// ### What it does | |
| /// Checks for usage of `Mutex<X>` where `X` is an integral | |
| /// type. | |
| /// | |
| /// ### Why restrict this? | |
| /// Using a mutex just to make access to a plain integer | |
| /// sequential is | |
| /// shooting flies with cannons. `std::sync::atomic::AtomicUsize` is leaner and faster. | |
| /// | |
| /// On the other hand, `Mutex`es are, in general, easier to | |
| /// verify correctness. An atomic does not behave the same as | |
| /// an equivalent mutex. See [this issue](https://github.com/rust-lang/rust-clippy/issues/4295)'s | |
| /// commentary for more details. | |
| /// | |
| /// ### Known problems | |
| /// * This lint cannot detect if the mutex is actually used | |
| /// for waiting before a critical section. | |
| /// * This lint has a false positive that warns without considering the case | |
| /// where `Mutex` is used together with `Condvar`. | |
| /// * This lint suggest using `AtomicU64` instead of `Mutex<u64>`, but | |
| /// `AtomicU64` is not available on some 32-bit platforms. | |
| /// | |
| /// ### Example | |
| /// ```no_run | |
| /// # use std::sync::Mutex; | |
| /// let x = Mutex::new(0usize); | |
| /// ``` | |
| /// | |
| /// Use instead: | |
| /// ```no_run | |
| /// # use std::sync::atomic::AtomicUsize; | |
| /// let x = AtomicUsize::new(0usize); | |
| /// ``` | |
| #[clippy::version = "pre 1.29.0"] | |
| pub MUTEX_INTEGER, | |
| restriction, | |
| "using a mutex for an integer type" | |
| } | |
| declare_lint_pass!(Mutex => [MUTEX_ATOMIC, MUTEX_INTEGER]); | |
| // NOTE: we don't use `check_expr` because that would make us lint every _use_ of such mutexes, not | |
| // just their definitions | |
| impl<'tcx> LateLintPass<'tcx> for Mutex { | |
| fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { | |
| if !item.span.from_expansion() | |
| && let ItemKind::Static(_, _, ty, body_id) = item.kind | |
| { | |
| let body = cx.tcx.hir_body(body_id); | |
| let mid_ty = ty_from_hir_ty(cx, ty); | |
| check_expr(cx, body.value.peel_blocks(), &TypeAscriptionKind::Required(ty), mid_ty); | |
| } | |
| } | |
| fn check_local(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx LetStmt<'_>) { | |
| if !stmt.span.from_expansion() | |
| && let Some(init) = stmt.init | |
| { | |
| let mid_ty = cx.typeck_results().expr_ty(init); | |
| check_expr(cx, init.peel_blocks(), &TypeAscriptionKind::Optional(stmt.ty), mid_ty); | |
| } | |
| } | |
| } | |
| /// Whether the type ascription `: Mutex<X>` (which we'll suggest replacing with `AtomicX`) is | |
| /// required | |
| enum TypeAscriptionKind<'tcx> { | |
| /// Yes; for us, this is the case for statics | |
| Required(&'tcx hir::Ty<'tcx>), | |
| /// No; the ascription might've been necessary in an expression like: | |
| /// ```ignore | |
| /// let mutex: Mutex<u64> = Mutex::new(0); | |
| /// ``` | |
| /// to specify the type of `0`, but since `AtomicX` already refers to a concrete type, we won't | |
| /// need this ascription anymore. | |
| Optional(Option<&'tcx hir::Ty<'tcx>>), | |
| } | |
| fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, ty_ascription: &TypeAscriptionKind<'tcx>, ty: Ty<'tcx>) { | |
| if let ty::Adt(_, subst) = ty.kind() | |
| && ty.is_diag_item(cx, sym::Mutex) | |
| && let mutex_param = subst.type_at(0) | |
| && let Some(atomic_name) = get_atomic_name(mutex_param) | |
| { | |
| let msg = "using a `Mutex` where an atomic would do"; | |
| let diag = |diag: &mut Diag<'_, _>| { | |
| // if `expr = Mutex::new(arg)`, we can try emitting a suggestion | |
| if let ExprKind::Call(qpath, [arg]) = expr.kind | |
| && let ExprKind::Path(QPath::TypeRelative(_mutex, new)) = qpath.kind | |
| && new.ident.name == sym::new | |
| { | |
| let mut applicability = Applicability::MaybeIncorrect; | |
| let arg = Sugg::hir_with_context(cx, arg, expr.span.ctxt(), "_", &mut applicability); | |
| let mut suggs = vec![(expr.span, format!("std::sync::atomic::{atomic_name}::new({arg})"))]; | |
| match ty_ascription { | |
| TypeAscriptionKind::Required(ty_ascription) => { | |
| suggs.push((ty_ascription.span, format!("std::sync::atomic::{atomic_name}"))); | |
| }, | |
| TypeAscriptionKind::Optional(Some(ty_ascription)) => { | |
| // See https://github.com/rust-lang/rust-clippy/pull/15386 for why this is | |
| // required | |
| let colon_ascription = (cx.sess().source_map()) | |
| .span_extend_to_prev_char_before(ty_ascription.span, ':', true) | |
| .with_leading_whitespace(cx) | |
| .into_span(); | |
| suggs.push((colon_ascription, String::new())); | |
| }, | |
| TypeAscriptionKind::Optional(None) => {}, // nothing to remove/replace | |
| } | |
| diag.multipart_suggestion("try", suggs, applicability); | |
| } else { | |
| diag.help(format!("consider using an `{atomic_name}` instead")); | |
| } | |
| diag.help("if you just want the locking behavior and not the internal type, consider using `Mutex<()>`"); | |
| }; | |
| match *mutex_param.kind() { | |
| ty::Uint(t) if t != UintTy::Usize => span_lint_and_then(cx, MUTEX_INTEGER, expr.span, msg, diag), | |
| ty::Int(t) if t != IntTy::Isize => span_lint_and_then(cx, MUTEX_INTEGER, expr.span, msg, diag), | |
| _ => span_lint_and_then(cx, MUTEX_ATOMIC, expr.span, msg, diag), | |
| } | |
| } | |
| } | |
| fn get_atomic_name(ty: Ty<'_>) -> Option<&'static str> { | |
| match ty.kind() { | |
| ty::Bool => Some("AtomicBool"), | |
| ty::Uint(uint_ty) => { | |
| match uint_ty { | |
| UintTy::U8 => Some("AtomicU8"), | |
| UintTy::U16 => Some("AtomicU16"), | |
| UintTy::U32 => Some("AtomicU32"), | |
| UintTy::U64 => Some("AtomicU64"), | |
| UintTy::Usize => Some("AtomicUsize"), | |
| // `AtomicU128` is unstable and only available on a few platforms: https://github.com/rust-lang/rust/issues/99069 | |
| UintTy::U128 => None, | |
| } | |
| }, | |
| ty::Int(int_ty) => { | |
| match int_ty { | |
| IntTy::I8 => Some("AtomicI8"), | |
| IntTy::I16 => Some("AtomicI16"), | |
| IntTy::I32 => Some("AtomicI32"), | |
| IntTy::I64 => Some("AtomicI64"), | |
| IntTy::Isize => Some("AtomicIsize"), | |
| // `AtomicU128` is unstable and only available on a few platforms: https://github.com/rust-lang/rust/issues/99069 | |
| IntTy::I128 => None, | |
| } | |
| }, | |
| // `AtomicPtr` only accepts `*mut T` | |
| ty::RawPtr(_, Mutability::Mut) => Some("AtomicPtr"), | |
| _ => None, | |
| } | |
| } |