Skip to content

Commit

Permalink
[once_cell_lazy]: adjust error message, docs, and auxiliary code.
Browse files Browse the repository at this point in the history
  • Loading branch information
J-ZhengLi committed Jun 21, 2024
1 parent 7fba7f9 commit 0e04c25
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 108 deletions.
70 changes: 26 additions & 44 deletions clippy_lints/src/once_cell_lazy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,23 @@ use rustc_span::Span;

declare_clippy_lint! {
/// ### What it does
/// Lints when a `once_cell::sync::Lazy` type is declared as static variables.
/// Lints when a `static` is declared with `once_cell::sync::Lazy` type,
/// which can mostly be done with `std::sync::LazyLock` without relying on
/// additional dependency.
///
/// Note: This lint will not trigger in crate with `no_std` context, or with MSRV < 1.80.0.
///
/// ### Why restrict this?
/// Such useage have been superseded by the `std::sync::LazyLock` type,
/// replacing them would reduce dependency of `once_cell` crate.
/// Such usage can be replaced with the `std::sync::LazyLock` type,
/// to reduce dependence of the `once_cell` crate.
///
/// ### Example
/// ```rust
/// use once_cell_lazy::once_cell_lazy;
/// use once_cell::sync::Lazy;
///
/// once_cell_lazy! {
/// static ref FOO: String = "foo".to_uppercase();
/// }
/// static BAR: Lazy<String> = Lazy::new(|| "BAR".to_lowercase());
/// ```ignore
/// static FOO: once_cell::sync::Lazy<String> = once_cell::sync::Lazy::new(|| "FOO".to_lowercase());
/// ```
/// Use instead:
/// ```rust
/// static FOO: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| "foo".to_uppercase());
/// static BAR: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| "BAR".to_lowercase());
/// ```ignore
/// static FOO: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| "FOO".to_lowercase());
/// ```
#[clippy::version = "1.81.0"]
pub ONCE_CELL_LAZY,
Expand All @@ -48,15 +45,13 @@ declare_clippy_lint! {
/// `Unspecified` or `MaybeIncorret`.
static FUNCTION_REPLACEMENTS: &[(&str, Option<&str>)] = &[
("once_cell::sync::Lazy::force", Some("std::sync::LazyLock::force")),
("once_cell::sync::Lazy::force_mut", None),
("once_cell::sync::Lazy::get", None),
("once_cell::sync::Lazy::get_mut", None),
("once_cell::sync::Lazy::new", Some("std::sync::LazyLock::new")),
// Note that `Lazy::into_value` is not in the list,
// because we only check for `static`s in this lint, and `into_value` attempts to take ownership
// of the parameter, which means it would fail natively.
// But do keep in mind that the equivalant replacement is called `LazyLock::into_inner`
// if somehow we decided to expand this lint to catch "non-static"s.
// Note: `Lazy::{into_value, get_mut, force_mut}` are not in the list.
// Because the lint only checks for `static`s, and using these functions with statics
// will either be a hard error or triggers `static_mut_ref` that will be hard errors.
// But keep in mind that if somehow we decide to expand this lint to catch non-statics,
// add those functions into the list.
];

pub struct OnceCellLazy {
Expand All @@ -78,6 +73,8 @@ impl OnceCellLazy {

impl_lint_pass!(OnceCellLazy => [ONCE_CELL_LAZY]);

/// Return if current MSRV does not meet the requirement for `lazy_cell` feature,
/// or current context has `no_std` attribute.
macro_rules! ensure_prerequisite {
($msrv:expr, $cx:ident) => {
if !$msrv.meets(clippy_config::msrvs::LAZY_CELL) || clippy_utils::is_no_std_crate($cx) {
Expand All @@ -90,7 +87,7 @@ impl<'hir> LateLintPass<'hir> for OnceCellLazy {
extract_msrv_attr!(LateContext);

fn check_crate(&mut self, cx: &LateContext<'hir>) {
// Do not link if current crate does not support `LazyLock`.
// Do not lint if current crate does not support `LazyLock`.
ensure_prerequisite!(self.msrv, cx);

// Convert hardcoded fn replacement list into a map with def_id
Expand All @@ -105,8 +102,8 @@ impl<'hir> LateLintPass<'hir> for OnceCellLazy {
fn check_item(&mut self, cx: &LateContext<'hir>, item: &Item<'hir>) {
ensure_prerequisite!(self.msrv, cx);

if let Some(lazy_kind) = LazyInfo::from_item(cx, item) {
self.lazy_type_defs.insert(item.owner_id.to_def_id(), lazy_kind);
if let Some(lazy_info) = LazyInfo::from_item(cx, item) {
self.lazy_type_defs.insert(item.owner_id.to_def_id(), lazy_info);
}
}

Expand Down Expand Up @@ -137,8 +134,7 @@ impl<'hir> LateLintPass<'hir> for OnceCellLazy {
}

struct LazyInfo {
/// Span of the [`hir::Ty`] where this `Lazy` type was declared,
/// without including args.
/// Span of the [`hir::Ty`] without including args.
/// i.e.:
/// ```ignore
/// static FOO: Lazy<String> = Lazy::new(...);
Expand All @@ -148,22 +144,8 @@ struct LazyInfo {
/// `Span` and `DefId` of calls on `Lazy` type.
/// i.e.:
/// ```ignore
/// static FOO: Lazy<String> = {
/// if cond {
/// Lazy::new(...)
/// // ^^^^^^^^^
/// } else {
/// Lazy::new(...)
/// // ^^^^^^^^^
/// }
/// }
/// ```
///
/// Or:
///
/// ```ignore
/// let x = Lazy::get(&FOO);
/// // ^^^^^^^^
/// static FOO: Lazy<String> = Lazy::new(...);
/// // ^^^^^^^^^
/// ```
calls_span_and_id: FxIndexMap<Span, DefId>,
}
Expand Down Expand Up @@ -228,9 +210,9 @@ impl LazyInfo {
cx,
ONCE_CELL_LAZY,
self.ty_span_no_args,
"this type has been superceded by `LazyLock`",
"this type has been superceded by `std::sync::LazyLock`",
|diag| {
diag.multipart_suggestion("consider using the `LazyLock` from standard library", suggs, appl);
diag.multipart_suggestion("use `std::sync::LazyLock` instead", suggs, appl);
},
);
}
Expand Down
43 changes: 15 additions & 28 deletions tests/ui/lazy_lock_like/auxiliary/once_cell.rs
Original file line number Diff line number Diff line change
@@ -1,52 +1,39 @@
//! **FAKE** once_cell crate.
pub mod sync {
use core::cell::Cell;
use std::marker::PhantomData;

pub struct OnceCell<T>(PhantomData<T>);
impl<T> OnceCell<T> {
pub const fn new() -> OnceCell<T> {
OnceCell(PhantomData)
}
}
impl<T> Default for OnceCell<T> {
fn default() -> Self {
Self::new()
}
}

pub struct Lazy<T, F = fn() -> T> {
cell: OnceCell<T>,
init: Cell<Option<F>>,
cell: PhantomData<T>,
init: F,
}
unsafe impl<T, F: Send> Sync for Lazy<T, F> where OnceCell<T>: Sync {}
unsafe impl<T, F: Send> Sync for Lazy<T, F> {}
impl<T, F> Lazy<T, F> {
pub const fn new(f: F) -> Lazy<T, F> {
Lazy {
cell: OnceCell::new(),
init: Cell::new(Some(f)),
cell: PhantomData,
init: f,
}
}

pub fn into_value(this: Lazy<T, F>) -> Result<T, i32> {
Err(1)
pub fn into_value(this: Lazy<T, F>) -> Result<T, F> {
unimplemented!()
}

pub fn force(_this: &Lazy<T, F>) -> i32 {
0
pub fn force(_this: &Lazy<T, F>) -> &T {
unimplemented!()
}

pub fn force_mut(_this: &mut Lazy<T, F>) -> i32 {
0
pub fn force_mut(_this: &mut Lazy<T, F>) -> &mut T {
unimplemented!()
}

pub fn get(_this: &Lazy<T, F>) -> i32 {
0
pub fn get(_this: &Lazy<T, F>) -> Option<&T> {
unimplemented!()
}

pub fn get_mut(_this: &mut Lazy<T, F>) -> i32 {
0
pub fn get_mut(_this: &mut Lazy<T, F>) -> Option<&mut T> {
unimplemented!()
}
}
}
14 changes: 7 additions & 7 deletions tests/ui/lazy_lock_like/lazy_lock_like_fixable.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ use once_cell::sync::Lazy;
fn main() {}

static LAZY_FOO: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| "foo".to_uppercase());
//~^ ERROR: this type has been superceded by `LazyLock`
//~^ ERROR: this type has been superceded by `std::sync::LazyLock`
static LAZY_BAR: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| {
//~^ ERROR: this type has been superceded by `LazyLock`
//~^ ERROR: this type has been superceded by `std::sync::LazyLock`
let x = "bar";
x.to_uppercase()
});
static LAZY_BAZ: std::sync::LazyLock<String> = { std::sync::LazyLock::new(|| "baz".to_uppercase()) };
//~^ ERROR: this type has been superceded by `LazyLock`
//~^ ERROR: this type has been superceded by `std::sync::LazyLock`
static LAZY_QUX: std::sync::LazyLock<String> = {
//~^ ERROR: this type has been superceded by `LazyLock`
//~^ ERROR: this type has been superceded by `std::sync::LazyLock`
if "qux".len() == 3 {
std::sync::LazyLock::new(|| "qux".to_uppercase())
} else if "qux".is_ascii() {
Expand All @@ -38,11 +38,11 @@ mod once_cell_lazy_with_fns {
use super::Lazy; // nice

static LAZY_FOO: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| "foo".to_uppercase());
//~^ ERROR: this type has been superceded by `LazyLock`
//~^ ERROR: this type has been superceded by `std::sync::LazyLock`
static LAZY_BAR: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| "bar".to_uppercase());
//~^ ERROR: this type has been superceded by `LazyLock`
//~^ ERROR: this type has been superceded by `std::sync::LazyLock`
static mut LAZY_BAZ: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| "baz".to_uppercase());
//~^ ERROR: this type has been superceded by `LazyLock`
//~^ ERROR: this type has been superceded by `std::sync::LazyLock`

fn calling_replaceable_fns() {
let _ = std::sync::LazyLock::force(&LAZY_FOO);
Expand Down
14 changes: 7 additions & 7 deletions tests/ui/lazy_lock_like/lazy_lock_like_fixable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ use once_cell::sync::Lazy;
fn main() {}

static LAZY_FOO: Lazy<String> = Lazy::new(|| "foo".to_uppercase());
//~^ ERROR: this type has been superceded by `LazyLock`
//~^ ERROR: this type has been superceded by `std::sync::LazyLock`
static LAZY_BAR: Lazy<String> = Lazy::new(|| {
//~^ ERROR: this type has been superceded by `LazyLock`
//~^ ERROR: this type has been superceded by `std::sync::LazyLock`
let x = "bar";
x.to_uppercase()
});
static LAZY_BAZ: Lazy<String> = { Lazy::new(|| "baz".to_uppercase()) };
//~^ ERROR: this type has been superceded by `LazyLock`
//~^ ERROR: this type has been superceded by `std::sync::LazyLock`
static LAZY_QUX: Lazy<String> = {
//~^ ERROR: this type has been superceded by `LazyLock`
//~^ ERROR: this type has been superceded by `std::sync::LazyLock`
if "qux".len() == 3 {
Lazy::new(|| "qux".to_uppercase())
} else if "qux".is_ascii() {
Expand All @@ -38,11 +38,11 @@ mod once_cell_lazy_with_fns {
use super::Lazy; // nice

static LAZY_FOO: Lazy<String> = Lazy::new(|| "foo".to_uppercase());
//~^ ERROR: this type has been superceded by `LazyLock`
//~^ ERROR: this type has been superceded by `std::sync::LazyLock`
static LAZY_BAR: Lazy<String> = Lazy::new(|| "bar".to_uppercase());
//~^ ERROR: this type has been superceded by `LazyLock`
//~^ ERROR: this type has been superceded by `std::sync::LazyLock`
static mut LAZY_BAZ: Lazy<String> = Lazy::new(|| "baz".to_uppercase());
//~^ ERROR: this type has been superceded by `LazyLock`
//~^ ERROR: this type has been superceded by `std::sync::LazyLock`

fn calling_replaceable_fns() {
let _ = Lazy::force(&LAZY_FOO);
Expand Down
28 changes: 14 additions & 14 deletions tests/ui/lazy_lock_like/lazy_lock_like_fixable.stderr
Original file line number Diff line number Diff line change
@@ -1,45 +1,45 @@
error: this type has been superceded by `LazyLock`
error: this type has been superceded by `std::sync::LazyLock`
--> tests/ui/lazy_lock_like/lazy_lock_like_fixable.rs:10:18
|
LL | static LAZY_FOO: Lazy<String> = Lazy::new(|| "foo".to_uppercase());
| ^^^^
|
= note: `-D clippy::once-cell-lazy` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::once_cell_lazy)]`
help: consider using the `LazyLock` from standard library
help: use `std::sync::LazyLock` instead
|
LL | static LAZY_FOO: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| "foo".to_uppercase());
| ~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~

error: this type has been superceded by `LazyLock`
error: this type has been superceded by `std::sync::LazyLock`
--> tests/ui/lazy_lock_like/lazy_lock_like_fixable.rs:12:18
|
LL | static LAZY_BAR: Lazy<String> = Lazy::new(|| {
| ^^^^
|
help: consider using the `LazyLock` from standard library
help: use `std::sync::LazyLock` instead
|
LL | static LAZY_BAR: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| {
| ~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~

error: this type has been superceded by `LazyLock`
error: this type has been superceded by `std::sync::LazyLock`
--> tests/ui/lazy_lock_like/lazy_lock_like_fixable.rs:17:18
|
LL | static LAZY_BAZ: Lazy<String> = { Lazy::new(|| "baz".to_uppercase()) };
| ^^^^
|
help: consider using the `LazyLock` from standard library
help: use `std::sync::LazyLock` instead
|
LL | static LAZY_BAZ: std::sync::LazyLock<String> = { std::sync::LazyLock::new(|| "baz".to_uppercase()) };
| ~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~

error: this type has been superceded by `LazyLock`
error: this type has been superceded by `std::sync::LazyLock`
--> tests/ui/lazy_lock_like/lazy_lock_like_fixable.rs:19:18
|
LL | static LAZY_QUX: Lazy<String> = {
| ^^^^
|
help: consider using the `LazyLock` from standard library
help: use `std::sync::LazyLock` instead
|
LL ~ static LAZY_QUX: std::sync::LazyLock<String> = {
LL |
Expand All @@ -51,13 +51,13 @@ LL | } else {
LL ~ std::sync::LazyLock::new(|| "qux".to_string())
|

error: this type has been superceded by `LazyLock`
error: this type has been superceded by `std::sync::LazyLock`
--> tests/ui/lazy_lock_like/lazy_lock_like_fixable.rs:40:22
|
LL | static LAZY_FOO: Lazy<String> = Lazy::new(|| "foo".to_uppercase());
| ^^^^
|
help: consider using the `LazyLock` from standard library
help: use `std::sync::LazyLock` instead
|
LL ~ static LAZY_FOO: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| "foo".to_uppercase());
LL |
Expand All @@ -66,13 +66,13 @@ LL | fn calling_replaceable_fns() {
LL ~ let _ = std::sync::LazyLock::force(&LAZY_FOO);
|

error: this type has been superceded by `LazyLock`
error: this type has been superceded by `std::sync::LazyLock`
--> tests/ui/lazy_lock_like/lazy_lock_like_fixable.rs:42:22
|
LL | static LAZY_BAR: Lazy<String> = Lazy::new(|| "bar".to_uppercase());
| ^^^^
|
help: consider using the `LazyLock` from standard library
help: use `std::sync::LazyLock` instead
|
LL ~ static LAZY_BAR: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| "bar".to_uppercase());
LL |
Expand All @@ -81,13 +81,13 @@ LL | let _ = Lazy::force(&LAZY_FOO);
LL ~ let _ = std::sync::LazyLock::force(&LAZY_BAR);
|

error: this type has been superceded by `LazyLock`
error: this type has been superceded by `std::sync::LazyLock`
--> tests/ui/lazy_lock_like/lazy_lock_like_fixable.rs:44:26
|
LL | static mut LAZY_BAZ: Lazy<String> = Lazy::new(|| "baz".to_uppercase());
| ^^^^
|
help: consider using the `LazyLock` from standard library
help: use `std::sync::LazyLock` instead
|
LL ~ static mut LAZY_BAZ: std::sync::LazyLock<String> = std::sync::LazyLock::new(|| "baz".to_uppercase());
LL |
Expand Down
4 changes: 3 additions & 1 deletion tests/ui/lazy_lock_like/lazy_lock_like_unfixable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ use once_cell::sync::Lazy;
fn main() {}

static LAZY_FOO: Lazy<String> = Lazy::new(|| "foo".to_uppercase());
//~^ ERROR: this type has been superceded by `LazyLock`
//~^ ERROR: this type has been superceded by `std::sync::LazyLock`
static LAZY_BAR: Lazy<String> = Lazy::new(|| "bar".to_uppercase());
//~^ ERROR: this type has been superceded by `std::sync::LazyLock`
static mut LAZY_BAZ: Lazy<String> = Lazy::new(|| "baz".to_uppercase());
//~^ ERROR: this type has been superceded by `std::sync::LazyLock`

fn calling_irreplaceable_fns() {
let _ = Lazy::get(&LAZY_BAR);
Expand Down
Loading

0 comments on commit 0e04c25

Please sign in to comment.