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
::{get_parent_expr, is_from_proc_macro}
;
4 use hir
::def_id
::DefId
;
5 use rustc_errors
::{Applicability, SuggestionStyle}
;
7 use rustc_hir
::{ExprKind, Item, ItemKind, QPath, 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}
;
14 declare_clippy_lint
! {
16 /// Checks for usage of `<integer>::max_value()`, `std::<integer>::MAX`,
17 /// `std::<float>::EPSILON`, etc.
19 /// ### Why is this bad?
20 /// All of these have been superseded 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.
25 /// let eps = std::f32::EPSILON;
29 /// let eps = f32::EPSILON;
31 #[clippy::version = "1.72.0"]
32 pub LEGACY_NUMERIC_CONSTANTS
,
34 "checks for usage of legacy std numeric constants and methods"
36 pub struct LegacyNumericConstants
{
40 impl LegacyNumericConstants
{
42 pub fn new(msrv
: Msrv
) -> Self {
47 impl_lint_pass
!(LegacyNumericConstants
=> [LEGACY_NUMERIC_CONSTANTS
]);
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;
53 if !msrv
.meets(NUMERIC_ASSOCIATED_CONSTANTS
) || in_external_macro(cx
.sess(), item
.span
) {
57 // Integer modules are "TBD" deprecated, and the contents are too,
58 // so lint on the `use` statement directly.
59 if let ItemKind
::Use(path
, kind @
(UseKind
::Single
| UseKind
::Glob
)) = item
.kind
60 && let Some(def_id
) = path
.res
[0].opt_def_id()
62 let module
= if is_integer_module(cx
, def_id
) {
64 } else if is_numeric_const(cx
, def_id
) {
72 LEGACY_NUMERIC_CONSTANTS
,
75 "importing legacy numeric constants"
77 "importing a legacy numeric constant"
80 if item
.ident
.name
== kw
::Underscore
{
81 diag
.help("remove this import");
85 let def_path
= cx
.get_def_path(def_id
);
87 if module
&& let [.., module_name
] = &*def_path
{
88 if kind
== UseKind
::Glob
{
89 diag
.help(format
!("remove this import and use associated constants `{module_name}::<CONST>` from the primitive type instead"));
91 diag
.help("remove this import").note(format
!(
92 "then `{module_name}::<CONST>` will resolve to the respective associated constant"
95 } else if let [.., module_name
, name
] = &*def_path
{
97 format
!("remove this import and use the associated constant `{module_name}::{name}` from the primitive type instead")
105 fn check_expr(&mut self, cx
: &LateContext
<'tcx
>, expr
: &'tcx rustc_hir
::Expr
<'tcx
>) {
106 let Self { msrv }
= self;
108 if !msrv
.meets(NUMERIC_ASSOCIATED_CONSTANTS
) || in_external_macro(cx
.sess(), expr
.span
) {
111 let ExprKind
::Path(qpath
) = expr
.kind
else {
115 // `std::<integer>::<CONST>` check
116 let (span
, sugg
, msg
) = if let QPath
::Resolved(None
, path
) = qpath
117 && let Some(def_id
) = path
.res
.opt_def_id()
118 && is_numeric_const(cx
, def_id
)
119 && let def_path
= cx
.get_def_path(def_id
)
120 && let [.., mod_name
, name
] = &*def_path
121 // Skip linting if this usage looks identical to the associated constant,
122 // since this would only require removing a `use` import (which is already linted).
123 && !is_numeric_const_path_canonical(path
, [*mod_name
, *name
])
127 format
!("{mod_name}::{name}"),
128 "usage of a legacy numeric constant",
130 // `<integer>::xxx_value` check
131 } else if let QPath
::TypeRelative(_
, last_segment
) = qpath
132 && let Some(def_id
) = cx
.qpath_res(&qpath
, expr
.hir_id
).opt_def_id()
133 && is_integer_method(cx
, def_id
)
134 && let Some(par_expr
) = get_parent_expr(cx
, expr
)
135 && let ExprKind
::Call(_
, _
) = par_expr
.kind
137 let name
= last_segment
.ident
.name
.as_str();
140 last_segment
.ident
.span
.with_hi(par_expr
.span
.hi()),
141 name
[..=2].to_ascii_uppercase(),
142 "usage of a legacy numeric method",
148 if is_from_proc_macro(cx
, expr
) {
152 span_lint_hir_and_then(cx
, LEGACY_NUMERIC_CONSTANTS
, expr
.hir_id
, span
, msg
, |diag
| {
153 diag
.span_suggestion_with_style(
155 "use the associated constant instead",
157 Applicability
::MaybeIncorrect
,
158 SuggestionStyle
::ShowAlways
,
163 extract_msrv_attr
!(LateContext
);
166 fn is_integer_module(cx
: &LateContext
<'_
>, did
: DefId
) -> bool
{
168 cx
.tcx
.get_diagnostic_name(did
),
170 sym
::isize_legacy_mod
171 | sym
::i128_legacy_mod
172 | sym
::i64_legacy_mod
173 | sym
::i32_legacy_mod
174 | sym
::i16_legacy_mod
176 | sym
::usize_legacy_mod
177 | sym
::u128_legacy_mod
178 | sym
::u64_legacy_mod
179 | sym
::u32_legacy_mod
180 | sym
::u16_legacy_mod
186 fn is_numeric_const(cx
: &LateContext
<'_
>, did
: DefId
) -> bool
{
188 cx
.tcx
.get_diagnostic_name(did
),
190 sym
::isize_legacy_const_max
191 | sym
::isize_legacy_const_min
192 | sym
::i128_legacy_const_max
193 | sym
::i128_legacy_const_min
194 | sym
::i16_legacy_const_max
195 | sym
::i16_legacy_const_min
196 | sym
::i32_legacy_const_max
197 | sym
::i32_legacy_const_min
198 | sym
::i64_legacy_const_max
199 | sym
::i64_legacy_const_min
200 | sym
::i8_legacy_const_max
201 | sym
::i8_legacy_const_min
202 | sym
::usize_legacy_const_max
203 | sym
::usize_legacy_const_min
204 | sym
::u128_legacy_const_max
205 | sym
::u128_legacy_const_min
206 | sym
::u16_legacy_const_max
207 | sym
::u16_legacy_const_min
208 | sym
::u32_legacy_const_max
209 | sym
::u32_legacy_const_min
210 | sym
::u64_legacy_const_max
211 | sym
::u64_legacy_const_min
212 | sym
::u8_legacy_const_max
213 | sym
::u8_legacy_const_min
214 | sym
::f32_legacy_const_digits
215 | sym
::f32_legacy_const_epsilon
216 | sym
::f32_legacy_const_infinity
217 | sym
::f32_legacy_const_mantissa_dig
218 | sym
::f32_legacy_const_max
219 | sym
::f32_legacy_const_max_10_exp
220 | sym
::f32_legacy_const_max_exp
221 | sym
::f32_legacy_const_min
222 | sym
::f32_legacy_const_min_10_exp
223 | sym
::f32_legacy_const_min_exp
224 | sym
::f32_legacy_const_min_positive
225 | sym
::f32_legacy_const_nan
226 | sym
::f32_legacy_const_neg_infinity
227 | sym
::f32_legacy_const_radix
228 | sym
::f64_legacy_const_digits
229 | sym
::f64_legacy_const_epsilon
230 | sym
::f64_legacy_const_infinity
231 | sym
::f64_legacy_const_mantissa_dig
232 | sym
::f64_legacy_const_max
233 | sym
::f64_legacy_const_max_10_exp
234 | sym
::f64_legacy_const_max_exp
235 | sym
::f64_legacy_const_min
236 | sym
::f64_legacy_const_min_10_exp
237 | sym
::f64_legacy_const_min_exp
238 | sym
::f64_legacy_const_min_positive
239 | sym
::f64_legacy_const_nan
240 | sym
::f64_legacy_const_neg_infinity
241 | sym
::f64_legacy_const_radix
246 // Whether path expression looks like `i32::MAX`
247 fn is_numeric_const_path_canonical(expr_path
: &hir
::Path
<'_
>, [mod_name
, name
]: [Symbol
; 2]) -> bool
{
250 ident
: one
, args
: None
, ..
253 ident
: two
, args
: None
, ..
255 ] = expr_path
.segments
260 one
.name
== mod_name
&& two
.name
== name
263 fn is_integer_method(cx
: &LateContext
<'_
>, did
: DefId
) -> bool
{
265 cx
.tcx
.get_diagnostic_name(did
),
267 sym
::isize_legacy_fn_max_value
268 | sym
::isize_legacy_fn_min_value
269 | sym
::i128_legacy_fn_max_value
270 | sym
::i128_legacy_fn_min_value
271 | sym
::i16_legacy_fn_max_value
272 | sym
::i16_legacy_fn_min_value
273 | sym
::i32_legacy_fn_max_value
274 | sym
::i32_legacy_fn_min_value
275 | sym
::i64_legacy_fn_max_value
276 | sym
::i64_legacy_fn_min_value
277 | sym
::i8_legacy_fn_max_value
278 | sym
::i8_legacy_fn_min_value
279 | sym
::usize_legacy_fn_max_value
280 | sym
::usize_legacy_fn_min_value
281 | sym
::u128_legacy_fn_max_value
282 | sym
::u128_legacy_fn_min_value
283 | sym
::u16_legacy_fn_max_value
284 | sym
::u16_legacy_fn_min_value
285 | sym
::u32_legacy_fn_max_value
286 | sym
::u32_legacy_fn_min_value
287 | sym
::u64_legacy_fn_max_value
288 | sym
::u64_legacy_fn_min_value
289 | sym
::u8_legacy_fn_max_value
290 | sym
::u8_legacy_fn_min_value