| use clippy_config::Conf; |
| use clippy_utils::diagnostics::span_lint_and_then; |
| use clippy_utils::is_from_proc_macro; |
| use clippy_utils::msrvs::{self, Msrv}; |
| use clippy_utils::source::SpanRangeExt; |
| use hir::def_id::DefId; |
| use rustc_errors::Applicability; |
| use rustc_hir as hir; |
| use rustc_hir::{ExprKind, Item, ItemKind, QPath, UseKind}; |
| use rustc_lint::{LateContext, LateLintPass, LintContext}; |
| use rustc_session::impl_lint_pass; |
| use rustc_span::symbol::kw; |
| use rustc_span::{Symbol, sym}; |
| |
| declare_clippy_lint! { |
| /// ### What it does |
| /// Checks for usage of `<integer>::max_value()`, `std::<integer>::MAX`, |
| /// `std::<float>::EPSILON`, etc. |
| /// |
| /// ### Why is this bad? |
| /// All of these have been superseded by the associated constants on their respective types, |
| /// such as `i128::MAX`. These legacy items may be deprecated in a future version of rust. |
| /// |
| /// ### Example |
| /// ```rust |
| /// let eps = std::f32::EPSILON; |
| /// ``` |
| /// Use instead: |
| /// ```rust |
| /// let eps = f32::EPSILON; |
| /// ``` |
| #[clippy::version = "1.79.0"] |
| pub LEGACY_NUMERIC_CONSTANTS, |
| style, |
| "checks for usage of legacy std numeric constants and methods" |
| } |
| pub struct LegacyNumericConstants { |
| msrv: Msrv, |
| } |
| |
| impl LegacyNumericConstants { |
| pub fn new(conf: &'static Conf) -> Self { |
| Self { msrv: conf.msrv } |
| } |
| } |
| |
| impl_lint_pass!(LegacyNumericConstants => [LEGACY_NUMERIC_CONSTANTS]); |
| |
| impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { |
| fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { |
| // Integer modules are "TBD" deprecated, and the contents are too, |
| // so lint on the `use` statement directly. |
| if let ItemKind::Use(path, kind @ (UseKind::Single(_) | UseKind::Glob)) = item.kind |
| && !item.span.in_external_macro(cx.sess().source_map()) |
| // use `present_items` because it could be in either type_ns or value_ns |
| && let Some(res) = path.res.present_items().next() |
| && let Some(def_id) = res.opt_def_id() |
| && self.msrv.meets(cx, msrvs::NUMERIC_ASSOCIATED_CONSTANTS) |
| { |
| let module = if is_integer_module(cx, def_id) { |
| true |
| } else if is_numeric_const(cx, def_id) { |
| false |
| } else { |
| return; |
| }; |
| |
| span_lint_and_then( |
| cx, |
| LEGACY_NUMERIC_CONSTANTS, |
| path.span, |
| if module { |
| "importing legacy numeric constants" |
| } else { |
| "importing a legacy numeric constant" |
| }, |
| |diag| { |
| if let UseKind::Single(ident) = kind |
| && ident.name == kw::Underscore |
| { |
| diag.help("remove this import"); |
| return; |
| } |
| |
| let def_path = cx.get_def_path(def_id); |
| |
| if module && let [.., module_name] = &*def_path { |
| if kind == UseKind::Glob { |
| diag.help(format!("remove this import and use associated constants `{module_name}::<CONST>` from the primitive type instead")); |
| } else { |
| diag.help("remove this import").note(format!( |
| "then `{module_name}::<CONST>` will resolve to the respective associated constant" |
| )); |
| } |
| } else if let [.., module_name, name] = &*def_path { |
| diag.help( |
| format!("remove this import and use the associated constant `{module_name}::{name}` from the primitive type instead") |
| ); |
| } |
| }, |
| ); |
| } |
| } |
| |
| fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { |
| // `std::<integer>::<CONST>` check |
| let (sugg, msg) = if let ExprKind::Path(qpath) = &expr.kind |
| && let QPath::Resolved(None, path) = qpath |
| && let Some(def_id) = path.res.opt_def_id() |
| && is_numeric_const(cx, def_id) |
| && let [.., mod_name, name] = &*cx.get_def_path(def_id) |
| // Skip linting if this usage looks identical to the associated constant, |
| // since this would only require removing a `use` import (which is already linted). |
| && !is_numeric_const_path_canonical(path, [*mod_name, *name]) |
| { |
| (format!("{mod_name}::{name}"), "usage of a legacy numeric constant") |
| // `<integer>::xxx_value` check |
| } else if let ExprKind::Call(func, []) = &expr.kind |
| && let ExprKind::Path(qpath) = &func.kind |
| && let QPath::TypeRelative(ty, last_segment) = qpath |
| && let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() |
| && is_integer_method(cx, def_id) |
| && let Some(mod_name) = ty.span.get_source_text(cx) |
| && ty.span.eq_ctxt(last_segment.ident.span) |
| { |
| let name = last_segment.ident.name.as_str()[..=2].to_ascii_uppercase(); |
| (format!("{mod_name}::{name}"), "usage of a legacy numeric method") |
| } else { |
| return; |
| }; |
| |
| if !expr.span.in_external_macro(cx.sess().source_map()) |
| && self.msrv.meets(cx, msrvs::NUMERIC_ASSOCIATED_CONSTANTS) |
| && !is_from_proc_macro(cx, expr) |
| { |
| span_lint_and_then(cx, LEGACY_NUMERIC_CONSTANTS, expr.span, msg, |diag| { |
| diag.span_suggestion_verbose( |
| expr.span, |
| "use the associated constant instead", |
| sugg, |
| Applicability::MaybeIncorrect, |
| ); |
| }); |
| } |
| } |
| } |
| |
| fn is_integer_module(cx: &LateContext<'_>, did: DefId) -> bool { |
| matches!( |
| cx.tcx.get_diagnostic_name(did), |
| Some( |
| sym::isize_legacy_mod |
| | sym::i128_legacy_mod |
| | sym::i64_legacy_mod |
| | sym::i32_legacy_mod |
| | sym::i16_legacy_mod |
| | sym::i8_legacy_mod |
| | sym::usize_legacy_mod |
| | sym::u128_legacy_mod |
| | sym::u64_legacy_mod |
| | sym::u32_legacy_mod |
| | sym::u16_legacy_mod |
| | sym::u8_legacy_mod |
| ) |
| ) |
| } |
| |
| fn is_numeric_const(cx: &LateContext<'_>, did: DefId) -> bool { |
| matches!( |
| cx.tcx.get_diagnostic_name(did), |
| Some( |
| sym::isize_legacy_const_max |
| | sym::isize_legacy_const_min |
| | sym::i128_legacy_const_max |
| | sym::i128_legacy_const_min |
| | sym::i16_legacy_const_max |
| | sym::i16_legacy_const_min |
| | sym::i32_legacy_const_max |
| | sym::i32_legacy_const_min |
| | sym::i64_legacy_const_max |
| | sym::i64_legacy_const_min |
| | sym::i8_legacy_const_max |
| | sym::i8_legacy_const_min |
| | sym::usize_legacy_const_max |
| | sym::usize_legacy_const_min |
| | sym::u128_legacy_const_max |
| | sym::u128_legacy_const_min |
| | sym::u16_legacy_const_max |
| | sym::u16_legacy_const_min |
| | sym::u32_legacy_const_max |
| | sym::u32_legacy_const_min |
| | sym::u64_legacy_const_max |
| | sym::u64_legacy_const_min |
| | sym::u8_legacy_const_max |
| | sym::u8_legacy_const_min |
| | sym::f32_legacy_const_digits |
| | sym::f32_legacy_const_epsilon |
| | sym::f32_legacy_const_infinity |
| | sym::f32_legacy_const_mantissa_dig |
| | sym::f32_legacy_const_max |
| | sym::f32_legacy_const_max_10_exp |
| | sym::f32_legacy_const_max_exp |
| | sym::f32_legacy_const_min |
| | sym::f32_legacy_const_min_10_exp |
| | sym::f32_legacy_const_min_exp |
| | sym::f32_legacy_const_min_positive |
| | sym::f32_legacy_const_nan |
| | sym::f32_legacy_const_neg_infinity |
| | sym::f32_legacy_const_radix |
| | sym::f64_legacy_const_digits |
| | sym::f64_legacy_const_epsilon |
| | sym::f64_legacy_const_infinity |
| | sym::f64_legacy_const_mantissa_dig |
| | sym::f64_legacy_const_max |
| | sym::f64_legacy_const_max_10_exp |
| | sym::f64_legacy_const_max_exp |
| | sym::f64_legacy_const_min |
| | sym::f64_legacy_const_min_10_exp |
| | sym::f64_legacy_const_min_exp |
| | sym::f64_legacy_const_min_positive |
| | sym::f64_legacy_const_nan |
| | sym::f64_legacy_const_neg_infinity |
| | sym::f64_legacy_const_radix |
| ) |
| ) |
| } |
| |
| // Whether path expression looks like `i32::MAX` |
| fn is_numeric_const_path_canonical(expr_path: &hir::Path<'_>, [mod_name, name]: [Symbol; 2]) -> bool { |
| let [ |
| hir::PathSegment { |
| ident: one, args: None, .. |
| }, |
| hir::PathSegment { |
| ident: two, args: None, .. |
| }, |
| ] = expr_path.segments |
| else { |
| return false; |
| }; |
| |
| one.name == mod_name && two.name == name |
| } |
| |
| fn is_integer_method(cx: &LateContext<'_>, did: DefId) -> bool { |
| matches!( |
| cx.tcx.get_diagnostic_name(did), |
| Some( |
| sym::isize_legacy_fn_max_value |
| | sym::isize_legacy_fn_min_value |
| | sym::i128_legacy_fn_max_value |
| | sym::i128_legacy_fn_min_value |
| | sym::i16_legacy_fn_max_value |
| | sym::i16_legacy_fn_min_value |
| | sym::i32_legacy_fn_max_value |
| | sym::i32_legacy_fn_min_value |
| | sym::i64_legacy_fn_max_value |
| | sym::i64_legacy_fn_min_value |
| | sym::i8_legacy_fn_max_value |
| | sym::i8_legacy_fn_min_value |
| | sym::usize_legacy_fn_max_value |
| | sym::usize_legacy_fn_min_value |
| | sym::u128_legacy_fn_max_value |
| | sym::u128_legacy_fn_min_value |
| | sym::u16_legacy_fn_max_value |
| | sym::u16_legacy_fn_min_value |
| | sym::u32_legacy_fn_max_value |
| | sym::u32_legacy_fn_min_value |
| | sym::u64_legacy_fn_max_value |
| | sym::u64_legacy_fn_min_value |
| | sym::u8_legacy_fn_max_value |
| | sym::u8_legacy_fn_min_value |
| ) |
| ) |
| } |