Skip to content

Commit 25d319d

Browse files
authored
New lint useless-nonzero-new_unchecked (rust-lang#13993)
changelog: [`useless-nonzero-new_unchecked`]: new lint Close rust-lang#13991 ### What it does Checks for `NonZero*::new_unchecked(<literal>)` being used in a `const` context. ### Why is this bad? Using `NonZero*::new_unchecked()` is an `unsafe` function and requires an `unsafe` context. When used with an integer literal in a `const` context, `NonZero*::new().unwrap()` will provide the same result with identical runtime performances while not requiring `unsafe`. ### Example ```no_run const PLAYERS: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(3) }; ``` Use instead: ```no_run const PLAYERS: NonZeroUsize = NonZeroUsize::new(3).unwrap(); ```
2 parents 98761e4 + 35dbaf8 commit 25d319d

8 files changed

+233
-1
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -6218,6 +6218,7 @@ Released 2018-09-13
62186218
[`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion
62196219
[`useless_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_format
62206220
[`useless_let_if_seq`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_let_if_seq
6221+
[`useless_nonzero_new_unchecked`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_nonzero_new_unchecked
62216222
[`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute
62226223
[`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec
62236224
[`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
496496
crate::methods::UNWRAP_OR_DEFAULT_INFO,
497497
crate::methods::UNWRAP_USED_INFO,
498498
crate::methods::USELESS_ASREF_INFO,
499+
crate::methods::USELESS_NONZERO_NEW_UNCHECKED_INFO,
499500
crate::methods::VEC_RESIZE_TO_ZERO_INFO,
500501
crate::methods::VERBOSE_FILE_READS_INFO,
501502
crate::methods::WAKER_CLONE_WAKE_INFO,

clippy_lints/src/methods/mod.rs

+30
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ mod unnecessary_to_owned;
130130
mod unused_enumerate_index;
131131
mod unwrap_expect_used;
132132
mod useless_asref;
133+
mod useless_nonzero_new_unchecked;
133134
mod utils;
134135
mod vec_resize_to_zero;
135136
mod verbose_file_reads;
@@ -4311,6 +4312,33 @@ declare_clippy_lint! {
43114312
"using `Iterator::last` on a `DoubleEndedIterator`"
43124313
}
43134314

4315+
declare_clippy_lint! {
4316+
/// ### What it does
4317+
///
4318+
/// Checks for `NonZero*::new_unchecked()` being used in a `const` context.
4319+
///
4320+
/// ### Why is this bad?
4321+
///
4322+
/// Using `NonZero*::new_unchecked()` is an `unsafe` function and requires an `unsafe` context. When used in a
4323+
/// context evaluated at compilation time, `NonZero*::new().unwrap()` will provide the same result with identical
4324+
/// runtime performances while not requiring `unsafe`.
4325+
///
4326+
/// ### Example
4327+
/// ```no_run
4328+
/// use std::num::NonZeroUsize;
4329+
/// const PLAYERS: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(3) };
4330+
/// ```
4331+
/// Use instead:
4332+
/// ```no_run
4333+
/// use std::num::NonZeroUsize;
4334+
/// const PLAYERS: NonZeroUsize = NonZeroUsize::new(3).unwrap();
4335+
/// ```
4336+
#[clippy::version = "1.86.0"]
4337+
pub USELESS_NONZERO_NEW_UNCHECKED,
4338+
complexity,
4339+
"using `NonZero::new_unchecked()` in a `const` context"
4340+
}
4341+
43144342
pub struct Methods {
43154343
avoid_breaking_exported_api: bool,
43164344
msrv: Msrv,
@@ -4477,6 +4505,7 @@ impl_lint_pass!(Methods => [
44774505
MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES,
44784506
UNNECESSARY_MAP_OR,
44794507
DOUBLE_ENDED_ITERATOR_LAST,
4508+
USELESS_NONZERO_NEW_UNCHECKED,
44804509
]);
44814510

44824511
/// Extracts a method call name, args, and `Span` of the method name.
@@ -4505,6 +4534,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
45054534
from_iter_instead_of_collect::check(cx, expr, args, func);
45064535
unnecessary_fallible_conversions::check_function(cx, expr, func);
45074536
manual_c_str_literals::check(cx, expr, func, args, &self.msrv);
4537+
useless_nonzero_new_unchecked::check(cx, expr, func, args, &self.msrv);
45084538
},
45094539
ExprKind::MethodCall(method_call, receiver, args, _) => {
45104540
let method_span = method_call.ident.span;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
2+
use clippy_utils::is_inside_always_const_context;
3+
use clippy_utils::msrvs::{self, Msrv};
4+
use clippy_utils::source::snippet_with_applicability;
5+
use clippy_utils::ty::is_type_diagnostic_item;
6+
use rustc_errors::Applicability;
7+
use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, Node, QPath, UnsafeSource};
8+
use rustc_lint::LateContext;
9+
use rustc_span::sym;
10+
11+
use super::USELESS_NONZERO_NEW_UNCHECKED;
12+
13+
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, func: &Expr<'tcx>, args: &[Expr<'_>], msrv: &Msrv) {
14+
if msrv.meets(msrvs::CONST_UNWRAP)
15+
&& let ExprKind::Path(QPath::TypeRelative(ty, segment)) = func.kind
16+
&& segment.ident.name == sym::new_unchecked
17+
&& let [init_arg] = args
18+
&& is_inside_always_const_context(cx.tcx, expr.hir_id)
19+
&& is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::NonZero)
20+
{
21+
let mut app = Applicability::MachineApplicable;
22+
let ty_str = snippet_with_applicability(cx, ty.span, "_", &mut app);
23+
let msg = format!("`{ty_str}::new()` and `Option::unwrap()` can be safely used in a `const` context");
24+
let sugg = format!(
25+
"{ty_str}::new({}).unwrap()",
26+
snippet_with_applicability(cx, init_arg.span, "_", &mut app)
27+
);
28+
29+
if let Node::Block(Block {
30+
stmts: [],
31+
span: block_span,
32+
rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
33+
..
34+
}) = cx.tcx.parent_hir_node(expr.hir_id)
35+
{
36+
if !block_span.from_expansion() {
37+
// The expression is the only component of an `unsafe` block. Propose
38+
// to replace the block altogether.
39+
span_lint_and_sugg(
40+
cx,
41+
USELESS_NONZERO_NEW_UNCHECKED,
42+
*block_span,
43+
msg,
44+
"use instead",
45+
sugg,
46+
app,
47+
);
48+
}
49+
} else {
50+
// The expression is enclosed in a larger `unsafe` context. Indicate that
51+
// this may no longer be needed for the fixed expression.
52+
span_lint_and_then(cx, USELESS_NONZERO_NEW_UNCHECKED, expr.span, msg, |diagnostic| {
53+
diagnostic
54+
.span_suggestion(expr.span, "use instead", sugg, app)
55+
.note("the fixed expression does not require an `unsafe` context");
56+
});
57+
}
58+
}
59+
}

clippy_utils/src/msrvs.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ macro_rules! msrv_aliases {
1818

1919
// names may refer to stabilized feature flags or library items
2020
msrv_aliases! {
21-
1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY }
21+
1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_UNWRAP }
2222
1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP }
2323
1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION }
2424
1,80,0 { BOX_INTO_ITER }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#![warn(clippy::useless_nonzero_new_unchecked)]
2+
3+
use std::num::{NonZero, NonZeroUsize};
4+
5+
#[clippy::msrv = "1.83"]
6+
const fn func() -> NonZeroUsize {
7+
const { NonZeroUsize::new(3).unwrap() }
8+
//~^ ERROR: `Option::unwrap()` can be safely used in a `const` context
9+
}
10+
11+
#[clippy::msrv = "1.82"]
12+
const fn func_older() -> NonZeroUsize {
13+
unsafe { NonZeroUsize::new_unchecked(3) }
14+
}
15+
16+
const fn func_performance_hit_if_linted() -> NonZeroUsize {
17+
unsafe { NonZeroUsize::new_unchecked(3) }
18+
}
19+
20+
const fn func_may_panic_at_run_time_if_linted(x: usize) -> NonZeroUsize {
21+
unsafe { NonZeroUsize::new_unchecked(x) }
22+
}
23+
24+
macro_rules! uns {
25+
($expr:expr) => {
26+
unsafe { $expr }
27+
};
28+
}
29+
30+
macro_rules! nzu {
31+
() => {
32+
NonZeroUsize::new_unchecked(1)
33+
};
34+
}
35+
36+
fn main() {
37+
const _A: NonZeroUsize = NonZeroUsize::new(3).unwrap();
38+
//~^ ERROR: `Option::unwrap()` can be safely used in a `const` context
39+
40+
static _B: NonZero<u8> = NonZero::<u8>::new(42).unwrap();
41+
//~^ ERROR: `Option::unwrap()` can be safely used in a `const` context
42+
43+
const _C: usize = unsafe { NonZeroUsize::new(3).unwrap().get() };
44+
//~^ ERROR: `Option::unwrap()` can be safely used in a `const` context
45+
46+
const AUX: usize = 3;
47+
const _D: NonZeroUsize = NonZeroUsize::new(AUX).unwrap();
48+
//~^ ERROR: `Option::unwrap()` can be safely used in a `const` context
49+
50+
const _X: NonZeroUsize = uns!(NonZeroUsize::new_unchecked(3));
51+
const _Y: NonZeroUsize = unsafe { nzu!() };
52+
}
+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#![warn(clippy::useless_nonzero_new_unchecked)]
2+
3+
use std::num::{NonZero, NonZeroUsize};
4+
5+
#[clippy::msrv = "1.83"]
6+
const fn func() -> NonZeroUsize {
7+
const { unsafe { NonZeroUsize::new_unchecked(3) } }
8+
//~^ ERROR: `Option::unwrap()` can be safely used in a `const` context
9+
}
10+
11+
#[clippy::msrv = "1.82"]
12+
const fn func_older() -> NonZeroUsize {
13+
unsafe { NonZeroUsize::new_unchecked(3) }
14+
}
15+
16+
const fn func_performance_hit_if_linted() -> NonZeroUsize {
17+
unsafe { NonZeroUsize::new_unchecked(3) }
18+
}
19+
20+
const fn func_may_panic_at_run_time_if_linted(x: usize) -> NonZeroUsize {
21+
unsafe { NonZeroUsize::new_unchecked(x) }
22+
}
23+
24+
macro_rules! uns {
25+
($expr:expr) => {
26+
unsafe { $expr }
27+
};
28+
}
29+
30+
macro_rules! nzu {
31+
() => {
32+
NonZeroUsize::new_unchecked(1)
33+
};
34+
}
35+
36+
fn main() {
37+
const _A: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(3) };
38+
//~^ ERROR: `Option::unwrap()` can be safely used in a `const` context
39+
40+
static _B: NonZero<u8> = unsafe { NonZero::<u8>::new_unchecked(42) };
41+
//~^ ERROR: `Option::unwrap()` can be safely used in a `const` context
42+
43+
const _C: usize = unsafe { NonZeroUsize::new_unchecked(3).get() };
44+
//~^ ERROR: `Option::unwrap()` can be safely used in a `const` context
45+
46+
const AUX: usize = 3;
47+
const _D: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(AUX) };
48+
//~^ ERROR: `Option::unwrap()` can be safely used in a `const` context
49+
50+
const _X: NonZeroUsize = uns!(NonZeroUsize::new_unchecked(3));
51+
const _Y: NonZeroUsize = unsafe { nzu!() };
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
error: `NonZeroUsize::new()` and `Option::unwrap()` can be safely used in a `const` context
2+
--> tests/ui/useless_nonzero_new_unchecked.rs:7:13
3+
|
4+
LL | const { unsafe { NonZeroUsize::new_unchecked(3) } }
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use instead: `NonZeroUsize::new(3).unwrap()`
6+
|
7+
= note: `-D clippy::useless-nonzero-new-unchecked` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::useless_nonzero_new_unchecked)]`
9+
10+
error: `NonZeroUsize::new()` and `Option::unwrap()` can be safely used in a `const` context
11+
--> tests/ui/useless_nonzero_new_unchecked.rs:37:30
12+
|
13+
LL | const _A: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(3) };
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use instead: `NonZeroUsize::new(3).unwrap()`
15+
16+
error: `NonZero::<u8>::new()` and `Option::unwrap()` can be safely used in a `const` context
17+
--> tests/ui/useless_nonzero_new_unchecked.rs:40:30
18+
|
19+
LL | static _B: NonZero<u8> = unsafe { NonZero::<u8>::new_unchecked(42) };
20+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use instead: `NonZero::<u8>::new(42).unwrap()`
21+
22+
error: `NonZeroUsize::new()` and `Option::unwrap()` can be safely used in a `const` context
23+
--> tests/ui/useless_nonzero_new_unchecked.rs:43:32
24+
|
25+
LL | const _C: usize = unsafe { NonZeroUsize::new_unchecked(3).get() };
26+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use instead: `NonZeroUsize::new(3).unwrap()`
27+
|
28+
= note: the fixed expression does not require an `unsafe` context
29+
30+
error: `NonZeroUsize::new()` and `Option::unwrap()` can be safely used in a `const` context
31+
--> tests/ui/useless_nonzero_new_unchecked.rs:47:30
32+
|
33+
LL | const _D: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(AUX) };
34+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use instead: `NonZeroUsize::new(AUX).unwrap()`
35+
36+
error: aborting due to 5 previous errors
37+

0 commit comments

Comments
 (0)