Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7239,6 +7239,7 @@ Released 2018-09-13
[`sliced_string_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#sliced_string_as_bytes
[`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization
[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive
[`static_mut_vars`]: https://rust-lang.github.io/rust-clippy/master/index.html#static_mut_vars
[`std_instead_of_alloc`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_alloc
[`std_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_core
[`str_split_at_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_split_at_newline
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
crate::size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT_INFO,
crate::size_of_ref::SIZE_OF_REF_INFO,
crate::slow_vector_initialization::SLOW_VECTOR_INITIALIZATION_INFO,
crate::static_mut_vars::STATIC_MUT_VARS_INFO,
crate::std_instead_of_core::ALLOC_INSTEAD_OF_CORE_INFO,
crate::std_instead_of_core::STD_INSTEAD_OF_ALLOC_INFO,
crate::std_instead_of_core::STD_INSTEAD_OF_CORE_INFO,
Expand Down
2 changes: 2 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ mod single_range_in_vec_init;
mod size_of_in_element_count;
mod size_of_ref;
mod slow_vector_initialization;
mod static_mut_vars;
mod std_instead_of_core;
mod string_patterns;
mod strings;
Expand Down Expand Up @@ -869,6 +870,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
Box::new(move |_| Box::new(manual_noop_waker::ManualNoopWaker::new(conf))),
Box::new(|_| Box::new(byte_char_slices::ByteCharSlice)),
Box::new(|_| Box::new(manual_assert_eq::ManualAssertEq)),
Box::new(|_| Box::new(static_mut_vars::StaticMutVars)),
// add late passes here, used by `cargo dev new_lint`
];
store.late_passes.extend(late_lints);
Expand Down
146 changes: 146 additions & 0 deletions clippy_lints/src/static_mut_vars.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::{IntoSpan, SpanRangeExt, snippet};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::ty_from_hir_ty;
use rustc_ast::{IntTy, UintTy};
use rustc_errors::Applicability;
use rustc_hir::{Item, ItemKind, MutTy, Mutability, Ty, TyKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_middle::ty::Ty as MidTy;
use rustc_session::declare_lint_pass;

declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `static mut` variables.
///
/// ### Why is this bad?
/// `static mut` allows mutable global state without synchronization.
/// Accessing it is `unsafe` because it can cause:
///
/// * Data races in multithreaded contexts
/// * Undefined behavior
/// * Violations of Rust’s aliasing guarantees
/// * Hard-to-debug global state bugs
///
/// Also, since Rust 2024, the built-in lint `static_mut_refs` denies by default `&` or `&mut`
/// references to a `static mut`.
///
/// ### Alternatives
/// Prefer one of:
/// * `Atomic*` for integer types, booleans and pointers
/// * `Mutex<T>` or `RwLock<T>` synchronization primitives for interior mutability
/// * `OnceLock<T>` or `LazyLock<T>` for one-time initialization
/// * `UnsafeCell<T>` with wrapper implementing `Sync` (or nightly `SyncUnsafeCell<T>`)
///
/// ### Example
/// ```no_run
/// static mut A: i32 = 0i32;
/// static mut B: (u64, u64) = (0u64, 0u64);
/// ```
/// Use instead:
/// ```no_run
/// use std::sync::atomic::AtomicI32;
/// use std::sync::RwLock;
///
/// static A: AtomicI32 = AtomicI32::new(0i32);
/// static B: RwLock<(u64, u64)> = RwLock::new((0u64, 0u64));
/// ```
#[clippy::version = "1.96.0"]
pub STATIC_MUT_VARS,
correctness,
"using a static mutable variable"
}

declare_lint_pass!(StaticMutVars => [STATIC_MUT_VARS]);

enum Replacement<'tcx> {
AtomicPrimTy(&'static str),
AtomicPtr(&'tcx Ty<'tcx>),
}

impl Replacement<'_> {
fn new<'tcx>(hir_ty: &Ty<'tcx>, mid_ty: MidTy<'tcx>) -> Option<Replacement<'tcx>> {
match mid_ty.kind() {
ty::Int(int_ty) => match int_ty {
IntTy::Isize => Some(Replacement::AtomicPrimTy("AtomicIsize")),
IntTy::I8 => Some(Replacement::AtomicPrimTy("AtomicI8")),
IntTy::I16 => Some(Replacement::AtomicPrimTy("AtomicI16")),
IntTy::I32 => Some(Replacement::AtomicPrimTy("AtomicI32")),
IntTy::I64 => Some(Replacement::AtomicPrimTy("AtomicI64")),
IntTy::I128 => None,
},

ty::Uint(uint_ty) => match uint_ty {
UintTy::Usize => Some(Replacement::AtomicPrimTy("AtomicUsize")),
UintTy::U8 => Some(Replacement::AtomicPrimTy("AtomicU8")),
UintTy::U16 => Some(Replacement::AtomicPrimTy("AtomicU16")),
UintTy::U32 => Some(Replacement::AtomicPrimTy("AtomicU32")),
UintTy::U64 => Some(Replacement::AtomicPrimTy("AtomicU64")),
UintTy::U128 => None,
},

ty::Bool => Some(Replacement::AtomicPrimTy("AtomicBool")),

ty::RawPtr(_, Mutability::Mut) => match hir_ty.kind {
TyKind::Ptr(MutTy { ty: ptr_ty, .. }) => Some(Replacement::AtomicPtr(ptr_ty)),
_ => None,
},

_ => None,
}
}
}

impl<'tcx> LateLintPass<'tcx> for StaticMutVars {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
if let ItemKind::Static(Mutability::Mut, ref ident, hir_ty, body_id) = item.kind {
span_lint_and_then(
cx,
STATIC_MUT_VARS,
item.span,
format!("static `{}` is declared as mutable", ident.name),
|diag| match Replacement::new(hir_ty, ty_from_hir_ty(cx, hir_ty)) {
Some(repl) => {
let mut applicability = Applicability::MaybeIncorrect;

let body = cx.tcx.hir_body(body_id).value.peel_blocks();
let arg = Sugg::hir_with_context(cx, body, item.span.ctxt(), "_", &mut applicability);

let (sugg_ty, sugg_body) = match repl {
Replacement::AtomicPrimTy(ty) => (
format!("std::sync::atomic::{ty}"),
format!("std::sync::atomic::{ty}::new({arg})"),
),

Replacement::AtomicPtr(ty) => {
let ty_snippet = snippet(cx, ty.span, "..");
(
format!("std::sync::atomic::AtomicPtr<{ty_snippet}>"),
format!("std::sync::atomic::AtomicPtr::<{ty_snippet}>::new({arg})"),
)
},
};

diag.multipart_suggestion(
"try",
vec![
(
item.span.until(ident.span.with_leading_whitespace(cx).into_span()),
"static".to_string(),
),
(hir_ty.span, sugg_ty),
(body.span, sugg_body),
],
applicability,
);
},

None => {
diag.help("consider using a safer alternative");
},
},
);
}
}
}
3 changes: 2 additions & 1 deletion tests/ui-toml/excessive_precision/excessive_precision.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
overflowing_literals,
unused_variables,
clippy::print_literal,
clippy::useless_vec
clippy::useless_vec,
clippy::static_mut_vars
)]

fn main() {
Expand Down
3 changes: 2 additions & 1 deletion tests/ui-toml/excessive_precision/excessive_precision.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
overflowing_literals,
unused_variables,
clippy::print_literal,
clippy::useless_vec
clippy::useless_vec,
clippy::static_mut_vars
)]

fn main() {
Expand Down
8 changes: 4 additions & 4 deletions tests/ui-toml/excessive_precision/excessive_precision.stderr
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
error: float has excessive precision
--> tests/ui-toml/excessive_precision/excessive_precision.rs:12:18
--> tests/ui-toml/excessive_precision/excessive_precision.rs:13:18
|
LL | let _: f32 = 1.012345678901234567890;
| ^^^^^^^^^^^^^^^^^^^^^^^
|
note: consider making it a `const` item
--> tests/ui-toml/excessive_precision/excessive_precision.rs:12:5
--> tests/ui-toml/excessive_precision/excessive_precision.rs:13:5
|
LL | let _: f32 = 1.012345678901234567890;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -18,13 +18,13 @@ LL + let _: f32 = 1.012_345_7;
|

error: float has excessive precision
--> tests/ui-toml/excessive_precision/excessive_precision.rs:14:18
--> tests/ui-toml/excessive_precision/excessive_precision.rs:15:18
|
LL | let _: f64 = 1.012345678901234567890;
| ^^^^^^^^^^^^^^^^^^^^^^^
|
note: consider making it a `const` item
--> tests/ui-toml/excessive_precision/excessive_precision.rs:14:5
--> tests/ui-toml/excessive_precision/excessive_precision.rs:15:5
|
LL | let _: f64 = 1.012345678901234567890;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
1 change: 1 addition & 0 deletions tests/ui/checked_unwrap/simple_conditionals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ fn issue11371() {
}

// This should not lint and suggest `if let Some(..) = X {}`, as `X` is being mutated
#[expect(clippy::static_mut_vars)]
static mut X: Option<i32> = Some(123);
unsafe {
#[expect(static_mut_refs)]
Expand Down
Loading
Loading