Skip to content

Commit f605385

Browse files
Centri3pitaj
authored andcommitted
new lint legacy_numeric_constants
1 parent 5a50481 commit f605385

22 files changed

+759
-37
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5320,6 +5320,7 @@ Released 2018-09-13
53205320
[`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays
53215321
[`large_stack_frames`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_frames
53225322
[`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value
5323+
[`legacy_numeric_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants
53235324
[`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty
53245325
[`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero
53255326
[`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return

book/src/lint_configuration.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
152152
* [`manual_hash_one`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one)
153153
* [`iter_kv_map`](https://rust-lang.github.io/rust-clippy/master/index.html#iter_kv_map)
154154
* [`manual_c_str_literals`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals)
155+
* [`legacy_numeric_constants`](https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants)
155156

156157

157158
## `cognitive-complexity-threshold`

clippy_config/src/conf.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ define_Conf! {
260260
///
261261
/// Suppress lints whenever the suggested change would cause breakage for other crates.
262262
(avoid_breaking_exported_api: bool = true),
263-
/// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE, ITER_KV_MAP, MANUAL_C_STR_LITERALS.
263+
/// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE, ITER_KV_MAP, MANUAL_C_STR_LITERALS, LEGACY_NUMERIC_CONSTANTS.
264264
///
265265
/// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml`
266266
#[default_text = ""]

clippy_config/src/msrvs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ msrv_aliases! {
3434
1,47,0 { TAU, IS_ASCII_DIGIT_CONST, ARRAY_IMPL_ANY_LEN }
3535
1,46,0 { CONST_IF_MATCH }
3636
1,45,0 { STR_STRIP_PREFIX }
37-
1,43,0 { LOG2_10, LOG10_2 }
37+
1,43,0 { LOG2_10, LOG10_2, NUMERIC_ASSOCIATED_CONSTANTS }
3838
1,42,0 { MATCHES_MACRO, SLICE_PATTERNS, PTR_SLICE_RAW_PARTS }
3939
1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR_ELSE }
4040
1,40,0 { MEM_TAKE, NON_EXHAUSTIVE, OPTION_AS_DEREF }

clippy_lints/src/casts/cast_possible_truncation.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b
4141
})
4242
},
4343
BinOpKind::Rem | BinOpKind::BitAnd => get_constant_bits(cx, right)
44-
.unwrap_or(u64::max_value())
44+
.unwrap_or(u64::MAX)
4545
.min(apply_reductions(cx, nbits, left, signed)),
4646
BinOpKind::Shr => apply_reductions(cx, nbits, left, signed)
4747
.saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).unwrap_or_default())),
@@ -56,7 +56,7 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b
5656
} else {
5757
None
5858
};
59-
apply_reductions(cx, nbits, left, signed).min(max_bits.unwrap_or(u64::max_value()))
59+
apply_reductions(cx, nbits, left, signed).min(max_bits.unwrap_or(u64::MAX))
6060
},
6161
ExprKind::MethodCall(method, _, [lo, hi], _) => {
6262
if method.ident.as_str() == "clamp" {

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
248248
crate::large_include_file::LARGE_INCLUDE_FILE_INFO,
249249
crate::large_stack_arrays::LARGE_STACK_ARRAYS_INFO,
250250
crate::large_stack_frames::LARGE_STACK_FRAMES_INFO,
251+
crate::legacy_numeric_constants::LEGACY_NUMERIC_CONSTANTS_INFO,
251252
crate::len_zero::COMPARISON_TO_EMPTY_INFO,
252253
crate::len_zero::LEN_WITHOUT_IS_EMPTY_INFO,
253254
crate::len_zero::LEN_ZERO_INFO,
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
use clippy_config::msrvs::{Msrv, NUMERIC_ASSOCIATED_CONSTANTS};
2+
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
3+
use clippy_utils::source::snippet_opt;
4+
use clippy_utils::{get_parent_expr, is_from_proc_macro, last_path_segment, std_or_core};
5+
use rustc_errors::Applicability;
6+
use rustc_hir::def::{DefKind, Res};
7+
use rustc_hir::{ExprKind, Item, ItemKind, PrimTy, QPath, TyKind, UseKind};
8+
use rustc_lint::{LateContext, LateLintPass, LintContext};
9+
use rustc_middle::lint::in_external_macro;
10+
use rustc_session::impl_lint_pass;
11+
use rustc_span::symbol::kw;
12+
use rustc_span::{sym, Symbol};
13+
14+
declare_clippy_lint! {
15+
/// ### What it does
16+
/// Checks for usage of `<integer>::max_value()`, `std::<integer>::MAX`,
17+
/// `std::<float>::EPSILON`, etc.
18+
///
19+
/// ### Why is this bad?
20+
/// All of these have been superceded by the associated constants on their respective types,
21+
/// such as `i128::MAX`. These legacy items may be deprecated in a future version of rust.
22+
///
23+
/// ### Example
24+
/// ```rust
25+
/// let eps = std::f32::EPSILON;
26+
/// ```
27+
/// Use instead:
28+
/// ```rust
29+
/// let eps = f32::EPSILON;
30+
/// ```
31+
#[clippy::version = "1.72.0"]
32+
pub LEGACY_NUMERIC_CONSTANTS,
33+
style,
34+
"checks for usage of legacy std numeric constants and methods"
35+
}
36+
pub struct LegacyNumericConstants {
37+
msrv: Msrv,
38+
}
39+
40+
impl LegacyNumericConstants {
41+
#[must_use]
42+
pub fn new(msrv: Msrv) -> Self {
43+
Self { msrv }
44+
}
45+
}
46+
47+
impl_lint_pass!(LegacyNumericConstants => [LEGACY_NUMERIC_CONSTANTS]);
48+
49+
impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants {
50+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
51+
let Self { msrv } = self;
52+
53+
if msrv.meets(NUMERIC_ASSOCIATED_CONSTANTS)
54+
&& !in_external_macro(cx.sess(), item.span)
55+
&& let ItemKind::Use(path, kind @ (UseKind::Single | UseKind::Glob)) = item.kind
56+
// These modules are "TBD" deprecated, and the contents are too, so lint on the `use`
57+
// statement directly
58+
&& let Some(def_id) = path.res[0].opt_def_id()
59+
&& let def_path = cx.get_def_path(def_id)
60+
&& is_path_in_numeric_module(&def_path, true)
61+
{
62+
let plurality = matches!(
63+
kind,
64+
UseKind::Glob | UseKind::Single if matches!(path.res[0], Res::Def(DefKind::Mod, _)),
65+
);
66+
67+
span_lint_and_then(
68+
cx,
69+
LEGACY_NUMERIC_CONSTANTS,
70+
path.span,
71+
if plurality {
72+
"importing legacy numeric constants"
73+
} else {
74+
"importing a legacy numeric constant"
75+
},
76+
|diag| {
77+
if item.ident.name == kw::Underscore {
78+
diag.help("remove this import");
79+
return;
80+
}
81+
82+
if plurality && let [.., module_name] = &*def_path {
83+
diag.help("remove this import and use the associated constants on the primitive type instead")
84+
.note(format!(
85+
"then `{module_name}::<CONST>` will resolve to the respective associated constant"
86+
));
87+
} else if let [.., module_name, name] = &*def_path {
88+
diag.help(
89+
format!("remove this import and use the associated constant `{module_name}::{name}` from the primitive type instead")
90+
);
91+
}
92+
},
93+
);
94+
}
95+
}
96+
97+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
98+
let Self { msrv } = self;
99+
100+
if !msrv.meets(NUMERIC_ASSOCIATED_CONSTANTS) || in_external_macro(cx.sess(), expr.span) {
101+
return;
102+
}
103+
let ExprKind::Path(qpath) = expr.kind else {
104+
return;
105+
};
106+
107+
// `std::<integer>::<CONST>` check
108+
let (span, sugg, msg) = if let QPath::Resolved(None, path) = qpath
109+
&& let Some(def_id) = path.res.opt_def_id()
110+
&& let path = cx.get_def_path(def_id)
111+
&& is_path_in_numeric_module(&path, false)
112+
&& let [.., module_name, name] = &*path
113+
&& let Some(snippet) = snippet_opt(cx, expr.span)
114+
&& let is_float_module = (*module_name == sym::f32 || *module_name == sym::f64)
115+
// Skip linting if this usage looks identical to the associated constant, since this
116+
// would only require removing a `use` import. We don't ignore ones from `f32` or `f64`, however.
117+
&& let identical = snippet == format!("{module_name}::{name}")
118+
&& (!identical || is_float_module)
119+
{
120+
(
121+
expr.span,
122+
if identical {
123+
None
124+
} else {
125+
Some(format!("{module_name}::{name}"))
126+
},
127+
"usage of a legacy numeric constant",
128+
)
129+
// `<integer>::xxx_value` check
130+
} else if let QPath::TypeRelative(ty, _) = qpath
131+
&& let TyKind::Path(ty_qpath) = ty.kind
132+
&& let Res::PrimTy(PrimTy::Int(_) | PrimTy::Uint(_)) = cx.qpath_res(&ty_qpath, ty.hir_id)
133+
&& let last_segment = last_path_segment(&qpath)
134+
&& let name = last_segment.ident.name.as_str()
135+
&& (name == "max_value" || name == "min_value")
136+
// Also remove the `()`
137+
&& let Some(par_expr) = get_parent_expr(cx, expr)
138+
&& let ExprKind::Call(_, _) = par_expr.kind
139+
{
140+
(
141+
last_segment.ident.span.with_hi(par_expr.span.hi()),
142+
Some(name[..=2].to_ascii_uppercase()),
143+
"usage of a legacy numeric method",
144+
)
145+
} else {
146+
return;
147+
};
148+
149+
if is_from_proc_macro(cx, expr) {
150+
return;
151+
}
152+
153+
span_lint_hir_and_then(cx, LEGACY_NUMERIC_CONSTANTS, expr.hir_id, span, msg, |diag| {
154+
if let Some(sugg) = sugg {
155+
diag.span_suggestion(
156+
span,
157+
"use the associated constant instead",
158+
sugg,
159+
Applicability::MaybeIncorrect,
160+
);
161+
} else if let Some(std_or_core) = std_or_core(cx)
162+
&& let QPath::Resolved(None, path) = qpath
163+
{
164+
diag.help(format!(
165+
"remove the import that brings `{std_or_core}::{}` into scope",
166+
// Must be `<module>::<CONST>` if `needs_import_removed` is true yet is
167+
// being linted
168+
path.segments[0].ident.name,
169+
));
170+
}
171+
});
172+
}
173+
174+
extract_msrv_attr!(LateContext);
175+
}
176+
177+
fn is_path_in_numeric_module(path: &[Symbol], ignore_float_modules: bool) -> bool {
178+
if let [
179+
sym::core,
180+
module @ (sym::u8
181+
| sym::i8
182+
| sym::u16
183+
| sym::i16
184+
| sym::u32
185+
| sym::i32
186+
| sym::u64
187+
| sym::i64
188+
| sym::u128
189+
| sym::i128
190+
| sym::usize
191+
| sym::isize
192+
| sym::f32
193+
| sym::f64),
194+
..,
195+
] = path
196+
&& !path.get(2).is_some_and(|&s| s == sym!(consts))
197+
{
198+
// If `ignore_float_modules` is `true`, return `None` for `_::f32` or `_::f64`, but not
199+
// _::f64::MAX` or similar.
200+
if ignore_float_modules && (*module == sym::f32 || *module == sym::f64) && path.get(2).is_none() {
201+
return false;
202+
}
203+
204+
return true;
205+
}
206+
207+
false
208+
}

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ mod large_futures;
179179
mod large_include_file;
180180
mod large_stack_arrays;
181181
mod large_stack_frames;
182+
mod legacy_numeric_constants;
182183
mod len_zero;
183184
mod let_if_seq;
184185
mod let_underscore;
@@ -1070,6 +1071,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
10701071
allow_one_hash_in_raw_strings,
10711072
})
10721073
});
1074+
store.register_late_pass(move |_| Box::new(legacy_numeric_constants::LegacyNumericConstants::new(msrv())));
10731075
store.register_late_pass(|_| Box::new(manual_range_patterns::ManualRangePatterns));
10741076
store.register_early_pass(|| Box::new(visibility::Visibility));
10751077
store.register_late_pass(move |_| Box::new(tuple_array_conversions::TupleArrayConversions { msrv: msrv() }));

clippy_utils/src/check_proc_macro.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use rustc_hir::{
2424
use rustc_lint::{LateContext, LintContext};
2525
use rustc_middle::ty::TyCtxt;
2626
use rustc_session::Session;
27-
use rustc_span::symbol::Ident;
27+
use rustc_span::symbol::{kw, Ident};
2828
use rustc_span::{Span, Symbol};
2929
use rustc_target::spec::abi::Abi;
3030

@@ -99,9 +99,13 @@ fn qpath_search_pat(path: &QPath<'_>) -> (Pat, Pat) {
9999
let start = if ty.is_some() {
100100
Pat::Str("<")
101101
} else {
102-
path.segments
103-
.first()
104-
.map_or(Pat::Str(""), |seg| Pat::Sym(seg.ident.name))
102+
path.segments.first().map_or(Pat::Str(""), |seg| {
103+
if seg.ident.name == kw::PathRoot {
104+
Pat::Str("::")
105+
} else {
106+
Pat::Sym(seg.ident.name)
107+
}
108+
})
105109
};
106110
let end = path.segments.last().map_or(Pat::Str(""), |seg| {
107111
if seg.args.is_some() {

tests/ui-toml/absolute_paths/absolute_paths.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//@revisions: allow_crates disallow_crates
44
//@[allow_crates] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/allow_crates
55
//@[disallow_crates] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/disallow_crates
6-
#![allow(clippy::no_effect, unused)]
6+
#![allow(clippy::no_effect, clippy::legacy_numeric_constants, unused)]
77
#![warn(clippy::absolute_paths)]
88
#![feature(decl_macro)]
99

tests/ui/checked_conversions.fixed

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![allow(
22
clippy::cast_lossless,
3+
clippy::legacy_numeric_constants,
34
unused,
45
// Int::max_value will be deprecated in the future
56
deprecated,

tests/ui/checked_conversions.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![allow(
22
clippy::cast_lossless,
3+
clippy::legacy_numeric_constants,
34
unused,
45
// Int::max_value will be deprecated in the future
56
deprecated,

0 commit comments

Comments
 (0)