about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-03-30 17:50:36 +0000
committerbors <bors@rust-lang.org>2024-03-30 17:50:36 +0000
commitcebf879de895deae27be8593692ce54023d7f48e (patch)
treead316fbf2a6e0fcc93ca0b8b4408962dde11601c
parente0e7ee183f673b13ba9378450db5175fe2f257b7 (diff)
parentf2e91ab1b9aaab23b81fc5ea5de43367cb9b9e6d (diff)
downloadrust-cebf879de895deae27be8593692ce54023d7f48e.tar.gz
rust-cebf879de895deae27be8593692ce54023d7f48e.zip
Auto merge of #12312 - pitaj:legacy_numeric_constants, r=xFrednet
new lint `legacy_numeric_constants`

Rework of #10997

- uses diagnostic items
- does not lint imports of the float modules (`use std::f32`)
- does not lint usage of float constants that look like `f32::MIN`

I chose to make the float changes because the following pattern is actually pretty useful
```rust
use std::f32;
let omega = freq * 2 * f32::consts::PI;
```
and the float modules are not TBD-deprecated like the integer modules.

Closes #10995

---

changelog: New lint [`legacy_numeric_constants`]
[#12312](https://github.com/rust-lang/rust-clippy/pull/12312)
-rw-r--r--CHANGELOG.md1
-rw-r--r--book/src/lint_configuration.md1
-rw-r--r--clippy_config/src/conf.rs2
-rw-r--r--clippy_config/src/msrvs.rs2
-rw-r--r--clippy_lints/src/casts/cast_possible_truncation.rs4
-rw-r--r--clippy_lints/src/declared_lints.rs1
-rw-r--r--clippy_lints/src/legacy_numeric_constants.rs293
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--clippy_utils/src/check_proc_macro.rs12
-rw-r--r--tests/ui-toml/absolute_paths/absolute_paths.rs2
-rw-r--r--tests/ui/checked_conversions.fixed1
-rw-r--r--tests/ui/checked_conversions.rs1
-rw-r--r--tests/ui/checked_conversions.stderr34
-rw-r--r--tests/ui/legacy_numeric_constants.fixed117
-rw-r--r--tests/ui/legacy_numeric_constants.rs117
-rw-r--r--tests/ui/legacy_numeric_constants.stderr184
-rw-r--r--tests/ui/legacy_numeric_constants_unfixable.rs78
-rw-r--r--tests/ui/legacy_numeric_constants_unfixable.stderr83
-rw-r--r--tests/ui/manual_saturating_arithmetic.fixed2
-rw-r--r--tests/ui/manual_saturating_arithmetic.rs2
-rw-r--r--tests/ui/suspicious_arithmetic_impl.rs1
-rw-r--r--tests/ui/suspicious_arithmetic_impl.stderr18
22 files changed, 921 insertions, 37 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 87bffe7f74d..f7e7ed86eed 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5379,6 +5379,7 @@ Released 2018-09-13
 [`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays
 [`large_stack_frames`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_frames
 [`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value
+[`legacy_numeric_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants
 [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty
 [`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero
 [`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return
diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md
index a9234899746..4a2727c5197 100644
--- a/book/src/lint_configuration.md
+++ b/book/src/lint_configuration.md
@@ -616,6 +616,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
 * [`if_then_some_else_none`](https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none)
 * [`index_refutable_slice`](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice)
 * [`iter_kv_map`](https://rust-lang.github.io/rust-clippy/master/index.html#iter_kv_map)
+* [`legacy_numeric_constants`](https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants)
 * [`manual_bits`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits)
 * [`manual_c_str_literals`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals)
 * [`manual_clamp`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp)
diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs
index 3218fe7f456..3b5de2c16ff 100644
--- a/clippy_config/src/conf.rs
+++ b/clippy_config/src/conf.rs
@@ -262,7 +262,7 @@ define_Conf! {
     ///
     /// Suppress lints whenever the suggested change would cause breakage for other crates.
     (avoid_breaking_exported_api: bool = true),
-    /// 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, ASSIGNING_CLONES.
+    /// 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, ASSIGNING_CLONES, LEGACY_NUMERIC_CONSTANTS.
     ///
     /// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml`
     #[default_text = ""]
diff --git a/clippy_config/src/msrvs.rs b/clippy_config/src/msrvs.rs
index bf4da5f14fe..32107ded305 100644
--- a/clippy_config/src/msrvs.rs
+++ b/clippy_config/src/msrvs.rs
@@ -36,7 +36,7 @@ msrv_aliases! {
     1,47,0 { TAU, IS_ASCII_DIGIT_CONST, ARRAY_IMPL_ANY_LEN }
     1,46,0 { CONST_IF_MATCH }
     1,45,0 { STR_STRIP_PREFIX }
-    1,43,0 { LOG2_10, LOG10_2 }
+    1,43,0 { LOG2_10, LOG10_2, NUMERIC_ASSOCIATED_CONSTANTS }
     1,42,0 { MATCHES_MACRO, SLICE_PATTERNS, PTR_SLICE_RAW_PARTS }
     1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR_ELSE }
     1,40,0 { MEM_TAKE, NON_EXHAUSTIVE, OPTION_AS_DEREF }
diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs
index 2c0a3d48296..32e45bd0a79 100644
--- a/clippy_lints/src/casts/cast_possible_truncation.rs
+++ b/clippy_lints/src/casts/cast_possible_truncation.rs
@@ -41,7 +41,7 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b
                 })
             },
             BinOpKind::Rem | BinOpKind::BitAnd => get_constant_bits(cx, right)
-                .unwrap_or(u64::max_value())
+                .unwrap_or(u64::MAX)
                 .min(apply_reductions(cx, nbits, left, signed)),
             BinOpKind::Shr => apply_reductions(cx, nbits, left, signed)
                 .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
             } else {
                 None
             };
-            apply_reductions(cx, nbits, left, signed).min(max_bits.unwrap_or(u64::max_value()))
+            apply_reductions(cx, nbits, left, signed).min(max_bits.unwrap_or(u64::MAX))
         },
         ExprKind::MethodCall(method, _, [lo, hi], _) => {
             if method.ident.as_str() == "clamp" {
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index eba048ad90b..5ff7d8e5134 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -254,6 +254,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::large_include_file::LARGE_INCLUDE_FILE_INFO,
     crate::large_stack_arrays::LARGE_STACK_ARRAYS_INFO,
     crate::large_stack_frames::LARGE_STACK_FRAMES_INFO,
+    crate::legacy_numeric_constants::LEGACY_NUMERIC_CONSTANTS_INFO,
     crate::len_zero::COMPARISON_TO_EMPTY_INFO,
     crate::len_zero::LEN_WITHOUT_IS_EMPTY_INFO,
     crate::len_zero::LEN_ZERO_INFO,
diff --git a/clippy_lints/src/legacy_numeric_constants.rs b/clippy_lints/src/legacy_numeric_constants.rs
new file mode 100644
index 00000000000..c5f1afe68c3
--- /dev/null
+++ b/clippy_lints/src/legacy_numeric_constants.rs
@@ -0,0 +1,293 @@
+use clippy_config::msrvs::{Msrv, NUMERIC_ASSOCIATED_CONSTANTS};
+use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
+use clippy_utils::{get_parent_expr, is_from_proc_macro};
+use hir::def_id::DefId;
+use rustc_errors::{Applicability, SuggestionStyle};
+use rustc_hir as hir;
+use rustc_hir::{ExprKind, Item, ItemKind, QPath, UseKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::impl_lint_pass;
+use rustc_span::symbol::kw;
+use rustc_span::{sym, Symbol};
+
+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 superceded 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.72.0"]
+    pub LEGACY_NUMERIC_CONSTANTS,
+    style,
+    "checks for usage of legacy std numeric constants and methods"
+}
+pub struct LegacyNumericConstants {
+    msrv: Msrv,
+}
+
+impl LegacyNumericConstants {
+    #[must_use]
+    pub fn new(msrv: Msrv) -> Self {
+        Self { 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>) {
+        let Self { msrv } = self;
+
+        if !msrv.meets(NUMERIC_ASSOCIATED_CONSTANTS) || in_external_macro(cx.sess(), item.span) {
+            return;
+        }
+
+        // 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
+            && let Some(def_id) = path.res[0].opt_def_id()
+        {
+            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 item.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>) {
+        let Self { msrv } = self;
+
+        if !msrv.meets(NUMERIC_ASSOCIATED_CONSTANTS) || in_external_macro(cx.sess(), expr.span) {
+            return;
+        }
+        let ExprKind::Path(qpath) = expr.kind else {
+            return;
+        };
+
+        // `std::<integer>::<CONST>` check
+        let (span, sugg, msg) = if let QPath::Resolved(None, path) = qpath
+            && let Some(def_id) = path.res.opt_def_id()
+            && is_numeric_const(cx, def_id)
+            && let def_path = cx.get_def_path(def_id)
+            && let [.., mod_name, name] = &*def_path
+            // 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])
+        {
+            (
+                expr.span,
+                format!("{mod_name}::{name}"),
+                "usage of a legacy numeric constant",
+            )
+        // `<integer>::xxx_value` check
+        } else if let QPath::TypeRelative(_, last_segment) = qpath
+            && let Some(def_id) = cx.qpath_res(&qpath, expr.hir_id).opt_def_id()
+            && is_integer_method(cx, def_id)
+            && let Some(par_expr) = get_parent_expr(cx, expr)
+            && let ExprKind::Call(_, _) = par_expr.kind
+        {
+            let name = last_segment.ident.name.as_str();
+
+            (
+                last_segment.ident.span.with_hi(par_expr.span.hi()),
+                name[..=2].to_ascii_uppercase(),
+                "usage of a legacy numeric method",
+            )
+        } else {
+            return;
+        };
+
+        if is_from_proc_macro(cx, expr) {
+            return;
+        }
+
+        span_lint_hir_and_then(cx, LEGACY_NUMERIC_CONSTANTS, expr.hir_id, span, msg, |diag| {
+            diag.span_suggestion_with_style(
+                span,
+                "use the associated constant instead",
+                sugg,
+                Applicability::MaybeIncorrect,
+                SuggestionStyle::ShowAlways,
+            );
+        });
+    }
+
+    extract_msrv_attr!(LateContext);
+}
+
+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
+        )
+    )
+}
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 169e51e2cb9..b92364a9d14 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -189,6 +189,7 @@ mod large_futures;
 mod large_include_file;
 mod large_stack_arrays;
 mod large_stack_frames;
+mod legacy_numeric_constants;
 mod len_zero;
 mod let_if_seq;
 mod let_underscore;
@@ -1083,6 +1084,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
             allow_one_hash_in_raw_strings,
         })
     });
+    store.register_late_pass(move |_| Box::new(legacy_numeric_constants::LegacyNumericConstants::new(msrv())));
     store.register_late_pass(|_| Box::new(manual_range_patterns::ManualRangePatterns));
     store.register_early_pass(|| Box::new(visibility::Visibility));
     store.register_late_pass(move |_| Box::new(tuple_array_conversions::TupleArrayConversions { msrv: msrv() }));
diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs
index d751aeaf902..42267310513 100644
--- a/clippy_utils/src/check_proc_macro.rs
+++ b/clippy_utils/src/check_proc_macro.rs
@@ -24,7 +24,7 @@ use rustc_hir::{
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
-use rustc_span::symbol::Ident;
+use rustc_span::symbol::{kw, Ident};
 use rustc_span::{Span, Symbol};
 use rustc_target::spec::abi::Abi;
 
@@ -99,9 +99,13 @@ fn qpath_search_pat(path: &QPath<'_>) -> (Pat, Pat) {
             let start = if ty.is_some() {
                 Pat::Str("<")
             } else {
-                path.segments
-                    .first()
-                    .map_or(Pat::Str(""), |seg| Pat::Sym(seg.ident.name))
+                path.segments.first().map_or(Pat::Str(""), |seg| {
+                    if seg.ident.name == kw::PathRoot {
+                        Pat::Str("::")
+                    } else {
+                        Pat::Sym(seg.ident.name)
+                    }
+                })
             };
             let end = path.segments.last().map_or(Pat::Str(""), |seg| {
                 if seg.args.is_some() {
diff --git a/tests/ui-toml/absolute_paths/absolute_paths.rs b/tests/ui-toml/absolute_paths/absolute_paths.rs
index 0e6a54452ee..a828701bcee 100644
--- a/tests/ui-toml/absolute_paths/absolute_paths.rs
+++ b/tests/ui-toml/absolute_paths/absolute_paths.rs
@@ -3,7 +3,7 @@
 //@revisions: allow_crates disallow_crates
 //@[allow_crates] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/allow_crates
 //@[disallow_crates] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/disallow_crates
-#![allow(clippy::no_effect, unused)]
+#![allow(clippy::no_effect, clippy::legacy_numeric_constants, unused)]
 #![warn(clippy::absolute_paths)]
 #![feature(decl_macro)]
 
diff --git a/tests/ui/checked_conversions.fixed b/tests/ui/checked_conversions.fixed
index 0e05a27429b..1e8da331614 100644
--- a/tests/ui/checked_conversions.fixed
+++ b/tests/ui/checked_conversions.fixed
@@ -1,5 +1,6 @@
 #![allow(
     clippy::cast_lossless,
+    clippy::legacy_numeric_constants,
     unused,
     // Int::max_value will be deprecated in the future
     deprecated,
diff --git a/tests/ui/checked_conversions.rs b/tests/ui/checked_conversions.rs
index ac782699265..67a9adc049e 100644
--- a/tests/ui/checked_conversions.rs
+++ b/tests/ui/checked_conversions.rs
@@ -1,5 +1,6 @@
 #![allow(
     clippy::cast_lossless,
+    clippy::legacy_numeric_constants,
     unused,
     // Int::max_value will be deprecated in the future
     deprecated,
diff --git a/tests/ui/checked_conversions.stderr b/tests/ui/checked_conversions.stderr
index 223e379cce9..453cd7fcf01 100644
--- a/tests/ui/checked_conversions.stderr
+++ b/tests/ui/checked_conversions.stderr
@@ -1,5 +1,5 @@
 error: checked cast can be simplified
-  --> tests/ui/checked_conversions.rs:14:13
+  --> tests/ui/checked_conversions.rs:15:13
    |
 LL |     let _ = value <= (u32::max_value() as i64) && value >= 0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
@@ -8,97 +8,97 @@ LL |     let _ = value <= (u32::max_value() as i64) && value >= 0;
    = help: to override `-D warnings` add `#[allow(clippy::checked_conversions)]`
 
 error: checked cast can be simplified
-  --> tests/ui/checked_conversions.rs:15:13
+  --> tests/ui/checked_conversions.rs:16:13
    |
 LL |     let _ = value <= (u32::MAX as i64) && value >= 0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> tests/ui/checked_conversions.rs:19:13
+  --> tests/ui/checked_conversions.rs:20:13
    |
 LL |     let _ = value <= i64::from(u16::max_value()) && value >= 0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> tests/ui/checked_conversions.rs:20:13
+  --> tests/ui/checked_conversions.rs:21:13
    |
 LL |     let _ = value <= i64::from(u16::MAX) && value >= 0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> tests/ui/checked_conversions.rs:24:13
+  --> tests/ui/checked_conversions.rs:25:13
    |
 LL |     let _ = value <= (u8::max_value() as isize) && value >= 0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> tests/ui/checked_conversions.rs:25:13
+  --> tests/ui/checked_conversions.rs:26:13
    |
 LL |     let _ = value <= (u8::MAX as isize) && value >= 0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> tests/ui/checked_conversions.rs:31:13
+  --> tests/ui/checked_conversions.rs:32:13
    |
 LL |     let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> tests/ui/checked_conversions.rs:32:13
+  --> tests/ui/checked_conversions.rs:33:13
    |
 LL |     let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> tests/ui/checked_conversions.rs:36:13
+  --> tests/ui/checked_conversions.rs:37:13
    |
 LL |     let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> tests/ui/checked_conversions.rs:37:13
+  --> tests/ui/checked_conversions.rs:38:13
    |
 LL |     let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> tests/ui/checked_conversions.rs:43:13
+  --> tests/ui/checked_conversions.rs:44:13
    |
 LL |     let _ = value <= i32::max_value() as u32;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> tests/ui/checked_conversions.rs:44:13
+  --> tests/ui/checked_conversions.rs:45:13
    |
 LL |     let _ = value <= i32::MAX as u32;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> tests/ui/checked_conversions.rs:48:13
+  --> tests/ui/checked_conversions.rs:49:13
    |
 LL |     let _ = value <= isize::max_value() as usize && value as i32 == 5;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> tests/ui/checked_conversions.rs:49:13
+  --> tests/ui/checked_conversions.rs:50:13
    |
 LL |     let _ = value <= isize::MAX as usize && value as i32 == 5;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> tests/ui/checked_conversions.rs:53:13
+  --> tests/ui/checked_conversions.rs:54:13
    |
 LL |     let _ = value <= u16::max_value() as u32 && value as i32 == 5;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> tests/ui/checked_conversions.rs:54:13
+  --> tests/ui/checked_conversions.rs:55:13
    |
 LL |     let _ = value <= u16::MAX as u32 && value as i32 == 5;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
 
 error: checked cast can be simplified
-  --> tests/ui/checked_conversions.rs:87:13
+  --> tests/ui/checked_conversions.rs:88:13
    |
 LL |     let _ = value <= (u32::MAX as i64) && value >= 0;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
diff --git a/tests/ui/legacy_numeric_constants.fixed b/tests/ui/legacy_numeric_constants.fixed
new file mode 100644
index 00000000000..a6ef8f8c119
--- /dev/null
+++ b/tests/ui/legacy_numeric_constants.fixed
@@ -0,0 +1,117 @@
+//@aux-build:proc_macros.rs
+#![allow(clippy::no_effect, deprecated, unused)]
+#![allow(clippy::legacy_numeric_constants)] // For imports.
+
+#[macro_use]
+extern crate proc_macros;
+
+pub mod a {
+    pub use std::u128;
+}
+
+macro_rules! b {
+    () => {
+        mod b {
+            #[warn(clippy::legacy_numeric_constants)]
+            fn b() {
+                let x = u64::MAX;
+                //~^ ERROR: usage of a legacy numeric constant
+                //~| HELP: use the associated constant instead
+            }
+        }
+    };
+}
+
+use std::u32::MAX;
+use std::u8::MIN;
+use std::{f64, u32};
+
+#[warn(clippy::legacy_numeric_constants)]
+fn main() {
+    f32::EPSILON;
+    //~^ ERROR: usage of a legacy numeric constant
+    //~| HELP: use the associated constant instead
+    u8::MIN;
+    //~^ ERROR: usage of a legacy numeric constant
+    //~| HELP: use the associated constant instead
+    usize::MIN;
+    //~^ ERROR: usage of a legacy numeric constant
+    //~| HELP: use the associated constant instead
+    u32::MAX;
+    //~^ ERROR: usage of a legacy numeric constant
+    //~| HELP: use the associated constant instead
+    u32::MAX;
+    //~^ ERROR: usage of a legacy numeric constant
+    //~| HELP: use the associated constant instead
+    u32::MAX;
+    //~^ ERROR: usage of a legacy numeric constant
+    //~| HELP: use the associated constant instead
+    i32::MAX;
+    //~^ ERROR: usage of a legacy numeric method
+    //~| HELP: use the associated constant instead
+    u8::MAX;
+    //~^ ERROR: usage of a legacy numeric method
+    //~| HELP: use the associated constant instead
+    u8::MIN;
+    //~^ ERROR: usage of a legacy numeric method
+    //~| HELP: use the associated constant instead
+    u8::MIN;
+    //~^ ERROR: usage of a legacy numeric constant
+    //~| HELP: use the associated constant instead
+    ::std::primitive::u8::MIN;
+    //~^ ERROR: usage of a legacy numeric method
+    //~| HELP: use the associated constant instead
+    std::primitive::i32::MAX;
+    //~^ ERROR: usage of a legacy numeric method
+    //~| HELP: use the associated constant instead
+    u128::MAX;
+    //~^ ERROR: usage of a legacy numeric constant
+    //~| HELP: use the associated constant instead
+    u32::MAX;
+    u128::MAX;
+    f32::EPSILON;
+    ::std::primitive::u8::MIN;
+    std::f32::consts::E;
+    f64::consts::E;
+    u8::MIN;
+    std::f32::consts::E;
+    f64::consts::E;
+    b!();
+
+    [(0, "", i128::MAX)];
+    //~^ ERROR: usage of a legacy numeric constant
+    //~| HELP: use the associated constant instead
+}
+
+#[warn(clippy::legacy_numeric_constants)]
+fn ext() {
+    external! {
+        ::std::primitive::u8::MIN;
+        ::std::u8::MIN;
+        ::std::primitive::u8::min_value();
+        use std::u64;
+        use std::u8::MIN;
+    }
+}
+
+#[allow(clippy::legacy_numeric_constants)]
+fn allow() {
+    ::std::primitive::u8::MIN;
+    ::std::u8::MIN;
+    ::std::primitive::u8::min_value();
+    use std::u64;
+    use std::u8::MIN;
+}
+
+#[warn(clippy::legacy_numeric_constants)]
+#[clippy::msrv = "1.42.0"]
+fn msrv_too_low() {
+    std::u32::MAX;
+}
+
+#[warn(clippy::legacy_numeric_constants)]
+#[clippy::msrv = "1.43.0"]
+fn msrv_juust_right() {
+    u32::MAX;
+    //~^ ERROR: usage of a legacy numeric constant
+}
diff --git a/tests/ui/legacy_numeric_constants.rs b/tests/ui/legacy_numeric_constants.rs
new file mode 100644
index 00000000000..cd633545372
--- /dev/null
+++ b/tests/ui/legacy_numeric_constants.rs
@@ -0,0 +1,117 @@
+//@aux-build:proc_macros.rs
+#![allow(clippy::no_effect, deprecated, unused)]
+#![allow(clippy::legacy_numeric_constants)] // For imports.
+
+#[macro_use]
+extern crate proc_macros;
+
+pub mod a {
+    pub use std::u128;
+}
+
+macro_rules! b {
+    () => {
+        mod b {
+            #[warn(clippy::legacy_numeric_constants)]
+            fn b() {
+                let x = std::u64::MAX;
+                //~^ ERROR: usage of a legacy numeric constant
+                //~| HELP: use the associated constant instead
+            }
+        }
+    };
+}
+
+use std::u32::MAX;
+use std::u8::MIN;
+use std::{f64, u32};
+
+#[warn(clippy::legacy_numeric_constants)]
+fn main() {
+    std::f32::EPSILON;
+    //~^ ERROR: usage of a legacy numeric constant
+    //~| HELP: use the associated constant instead
+    std::u8::MIN;
+    //~^ ERROR: usage of a legacy numeric constant
+    //~| HELP: use the associated constant instead
+    std::usize::MIN;
+    //~^ ERROR: usage of a legacy numeric constant
+    //~| HELP: use the associated constant instead
+    std::u32::MAX;
+    //~^ ERROR: usage of a legacy numeric constant
+    //~| HELP: use the associated constant instead
+    core::u32::MAX;
+    //~^ ERROR: usage of a legacy numeric constant
+    //~| HELP: use the associated constant instead
+    MAX;
+    //~^ ERROR: usage of a legacy numeric constant
+    //~| HELP: use the associated constant instead
+    i32::max_value();
+    //~^ ERROR: usage of a legacy numeric method
+    //~| HELP: use the associated constant instead
+    u8::max_value();
+    //~^ ERROR: usage of a legacy numeric method
+    //~| HELP: use the associated constant instead
+    u8::min_value();
+    //~^ ERROR: usage of a legacy numeric method
+    //~| HELP: use the associated constant instead
+    ::std::u8::MIN;
+    //~^ ERROR: usage of a legacy numeric constant
+    //~| HELP: use the associated constant instead
+    ::std::primitive::u8::min_value();
+    //~^ ERROR: usage of a legacy numeric method
+    //~| HELP: use the associated constant instead
+    std::primitive::i32::max_value();
+    //~^ ERROR: usage of a legacy numeric method
+    //~| HELP: use the associated constant instead
+    self::a::u128::MAX;
+    //~^ ERROR: usage of a legacy numeric constant
+    //~| HELP: use the associated constant instead
+    u32::MAX;
+    u128::MAX;
+    f32::EPSILON;
+    ::std::primitive::u8::MIN;
+    std::f32::consts::E;
+    f64::consts::E;
+    u8::MIN;
+    std::f32::consts::E;
+    f64::consts::E;
+    b!();
+
+    [(0, "", std::i128::MAX)];
+    //~^ ERROR: usage of a legacy numeric constant
+    //~| HELP: use the associated constant instead
+}
+
+#[warn(clippy::legacy_numeric_constants)]
+fn ext() {
+    external! {
+        ::std::primitive::u8::MIN;
+        ::std::u8::MIN;
+        ::std::primitive::u8::min_value();
+        use std::u64;
+        use std::u8::MIN;
+    }
+}
+
+#[allow(clippy::legacy_numeric_constants)]
+fn allow() {
+    ::std::primitive::u8::MIN;
+    ::std::u8::MIN;
+    ::std::primitive::u8::min_value();
+    use std::u64;
+    use std::u8::MIN;
+}
+
+#[warn(clippy::legacy_numeric_constants)]
+#[clippy::msrv = "1.42.0"]
+fn msrv_too_low() {
+    std::u32::MAX;
+}
+
+#[warn(clippy::legacy_numeric_constants)]
+#[clippy::msrv = "1.43.0"]
+fn msrv_juust_right() {
+    std::u32::MAX;
+    //~^ ERROR: usage of a legacy numeric constant
+}
diff --git a/tests/ui/legacy_numeric_constants.stderr b/tests/ui/legacy_numeric_constants.stderr
new file mode 100644
index 00000000000..267b9ac8e4d
--- /dev/null
+++ b/tests/ui/legacy_numeric_constants.stderr
@@ -0,0 +1,184 @@
+error: usage of a legacy numeric constant
+  --> tests/ui/legacy_numeric_constants.rs:31:5
+   |
+LL |     std::f32::EPSILON;
+   |     ^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::legacy-numeric-constants` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::legacy_numeric_constants)]`
+help: use the associated constant instead
+   |
+LL |     f32::EPSILON;
+   |     ~~~~~~~~~~~~
+
+error: usage of a legacy numeric constant
+  --> tests/ui/legacy_numeric_constants.rs:34:5
+   |
+LL |     std::u8::MIN;
+   |     ^^^^^^^^^^^^
+   |
+help: use the associated constant instead
+   |
+LL |     u8::MIN;
+   |     ~~~~~~~
+
+error: usage of a legacy numeric constant
+  --> tests/ui/legacy_numeric_constants.rs:37:5
+   |
+LL |     std::usize::MIN;
+   |     ^^^^^^^^^^^^^^^
+   |
+help: use the associated constant instead
+   |
+LL |     usize::MIN;
+   |     ~~~~~~~~~~
+
+error: usage of a legacy numeric constant
+  --> tests/ui/legacy_numeric_constants.rs:40:5
+   |
+LL |     std::u32::MAX;
+   |     ^^^^^^^^^^^^^
+   |
+help: use the associated constant instead
+   |
+LL |     u32::MAX;
+   |     ~~~~~~~~
+
+error: usage of a legacy numeric constant
+  --> tests/ui/legacy_numeric_constants.rs:43:5
+   |
+LL |     core::u32::MAX;
+   |     ^^^^^^^^^^^^^^
+   |
+help: use the associated constant instead
+   |
+LL |     u32::MAX;
+   |     ~~~~~~~~
+
+error: usage of a legacy numeric constant
+  --> tests/ui/legacy_numeric_constants.rs:46:5
+   |
+LL |     MAX;
+   |     ^^^
+   |
+help: use the associated constant instead
+   |
+LL |     u32::MAX;
+   |     ~~~~~~~~
+
+error: usage of a legacy numeric method
+  --> tests/ui/legacy_numeric_constants.rs:49:10
+   |
+LL |     i32::max_value();
+   |          ^^^^^^^^^^^
+   |
+help: use the associated constant instead
+   |
+LL |     i32::MAX;
+   |          ~~~
+
+error: usage of a legacy numeric method
+  --> tests/ui/legacy_numeric_constants.rs:52:9
+   |
+LL |     u8::max_value();
+   |         ^^^^^^^^^^^
+   |
+help: use the associated constant instead
+   |
+LL |     u8::MAX;
+   |         ~~~
+
+error: usage of a legacy numeric method
+  --> tests/ui/legacy_numeric_constants.rs:55:9
+   |
+LL |     u8::min_value();
+   |         ^^^^^^^^^^^
+   |
+help: use the associated constant instead
+   |
+LL |     u8::MIN;
+   |         ~~~
+
+error: usage of a legacy numeric constant
+  --> tests/ui/legacy_numeric_constants.rs:58:5
+   |
+LL |     ::std::u8::MIN;
+   |     ^^^^^^^^^^^^^^
+   |
+help: use the associated constant instead
+   |
+LL |     u8::MIN;
+   |     ~~~~~~~
+
+error: usage of a legacy numeric method
+  --> tests/ui/legacy_numeric_constants.rs:61:27
+   |
+LL |     ::std::primitive::u8::min_value();
+   |                           ^^^^^^^^^^^
+   |
+help: use the associated constant instead
+   |
+LL |     ::std::primitive::u8::MIN;
+   |                           ~~~
+
+error: usage of a legacy numeric method
+  --> tests/ui/legacy_numeric_constants.rs:64:26
+   |
+LL |     std::primitive::i32::max_value();
+   |                          ^^^^^^^^^^^
+   |
+help: use the associated constant instead
+   |
+LL |     std::primitive::i32::MAX;
+   |                          ~~~
+
+error: usage of a legacy numeric constant
+  --> tests/ui/legacy_numeric_constants.rs:67:5
+   |
+LL |     self::a::u128::MAX;
+   |     ^^^^^^^^^^^^^^^^^^
+   |
+help: use the associated constant instead
+   |
+LL |     u128::MAX;
+   |     ~~~~~~~~~
+
+error: usage of a legacy numeric constant
+  --> tests/ui/legacy_numeric_constants.rs:17:25
+   |
+LL |                 let x = std::u64::MAX;
+   |                         ^^^^^^^^^^^^^
+...
+LL |     b!();
+   |     ---- in this macro invocation
+   |
+   = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: use the associated constant instead
+   |
+LL |                 let x = u64::MAX;
+   |                         ~~~~~~~~
+
+error: usage of a legacy numeric constant
+  --> tests/ui/legacy_numeric_constants.rs:81:14
+   |
+LL |     [(0, "", std::i128::MAX)];
+   |              ^^^^^^^^^^^^^^
+   |
+help: use the associated constant instead
+   |
+LL |     [(0, "", i128::MAX)];
+   |              ~~~~~~~~~
+
+error: usage of a legacy numeric constant
+  --> tests/ui/legacy_numeric_constants.rs:115:5
+   |
+LL |     std::u32::MAX;
+   |     ^^^^^^^^^^^^^
+   |
+help: use the associated constant instead
+   |
+LL |     u32::MAX;
+   |     ~~~~~~~~
+
+error: aborting due to 16 previous errors
+
diff --git a/tests/ui/legacy_numeric_constants_unfixable.rs b/tests/ui/legacy_numeric_constants_unfixable.rs
new file mode 100644
index 00000000000..86738ede210
--- /dev/null
+++ b/tests/ui/legacy_numeric_constants_unfixable.rs
@@ -0,0 +1,78 @@
+//@no-rustfix
+//@aux-build:proc_macros.rs
+#![allow(clippy::no_effect, deprecated, unused)]
+#![warn(clippy::legacy_numeric_constants)]
+
+#[macro_use]
+extern crate proc_macros;
+
+use std::u128 as _;
+//~^ ERROR: importing legacy numeric constants
+//~| HELP: remove this import
+pub mod a {
+    pub use std::{mem, u128};
+    //~^ ERROR: importing legacy numeric constants
+    //~| HELP: remove this import
+}
+
+macro_rules! b {
+    () => {
+        mod b {
+            use std::u32;
+            //~^ ERROR: importing legacy numeric constants
+            //~| HELP: remove this import
+        }
+    };
+}
+
+fn main() {
+    use std::u32::MAX;
+    //~^ ERROR: importing a legacy numeric constant
+    //~| HELP: remove this import and use the associated constant `u32::MAX`
+    use std::u8::MIN;
+    //~^ ERROR: importing a legacy numeric constant
+    //~| HELP: remove this import and use the associated constant `u8::MIN`
+    f64::MAX;
+    use std::u32;
+    //~^ ERROR: importing legacy numeric constants
+    //~| HELP: remove this import
+    u32::MAX;
+    use std::f32::MIN_POSITIVE;
+    //~^ ERROR: importing a legacy numeric constant
+    //~| HELP: remove this import and use the associated constant `f32::MIN_POSITIVE`
+    use std::f64;
+    use std::i16::*;
+    //~^ ERROR: importing legacy numeric constants
+    //~| HELP: remove this import and use associated constants `i16::<CONST>`
+    u128::MAX;
+    f32::EPSILON;
+    f64::EPSILON;
+    ::std::primitive::u8::MIN;
+    std::f32::consts::E;
+    f64::consts::E;
+    u8::MIN;
+    std::f32::consts::E;
+    f64::consts::E;
+    b!();
+}
+
+fn ext() {
+    external! {
+        ::std::primitive::u8::MIN;
+        ::std::u8::MIN;
+        ::std::primitive::u8::min_value();
+        use std::u64;
+        use std::u8::MIN;
+    }
+}
+
+#[clippy::msrv = "1.42.0"]
+fn msrv_too_low() {
+    use std::u32::MAX;
+}
+
+#[clippy::msrv = "1.43.0"]
+fn msrv_juust_right() {
+    use std::u32::MAX;
+    //~^ ERROR: importing a legacy numeric constant
+}
diff --git a/tests/ui/legacy_numeric_constants_unfixable.stderr b/tests/ui/legacy_numeric_constants_unfixable.stderr
new file mode 100644
index 00000000000..2edcf718836
--- /dev/null
+++ b/tests/ui/legacy_numeric_constants_unfixable.stderr
@@ -0,0 +1,83 @@
+error: importing legacy numeric constants
+  --> tests/ui/legacy_numeric_constants_unfixable.rs:9:5
+   |
+LL | use std::u128 as _;
+   |     ^^^^^^^^^
+   |
+   = help: remove this import
+   = note: `-D clippy::legacy-numeric-constants` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::legacy_numeric_constants)]`
+
+error: importing legacy numeric constants
+  --> tests/ui/legacy_numeric_constants_unfixable.rs:13:24
+   |
+LL |     pub use std::{mem, u128};
+   |                        ^^^^
+   |
+   = help: remove this import
+   = note: then `u128::<CONST>` will resolve to the respective associated constant
+
+error: importing a legacy numeric constant
+  --> tests/ui/legacy_numeric_constants_unfixable.rs:29:9
+   |
+LL |     use std::u32::MAX;
+   |         ^^^^^^^^^^^^^
+   |
+   = help: remove this import and use the associated constant `u32::MAX` from the primitive type instead
+
+error: importing a legacy numeric constant
+  --> tests/ui/legacy_numeric_constants_unfixable.rs:32:9
+   |
+LL |     use std::u8::MIN;
+   |         ^^^^^^^^^^^^
+   |
+   = help: remove this import and use the associated constant `u8::MIN` from the primitive type instead
+
+error: importing legacy numeric constants
+  --> tests/ui/legacy_numeric_constants_unfixable.rs:36:9
+   |
+LL |     use std::u32;
+   |         ^^^^^^^^
+   |
+   = help: remove this import
+   = note: then `u32::<CONST>` will resolve to the respective associated constant
+
+error: importing a legacy numeric constant
+  --> tests/ui/legacy_numeric_constants_unfixable.rs:40:9
+   |
+LL |     use std::f32::MIN_POSITIVE;
+   |         ^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: remove this import and use the associated constant `f32::MIN_POSITIVE` from the primitive type instead
+
+error: importing legacy numeric constants
+  --> tests/ui/legacy_numeric_constants_unfixable.rs:44:9
+   |
+LL |     use std::i16::*;
+   |         ^^^^^^^^
+   |
+   = help: remove this import and use associated constants `i16::<CONST>` from the primitive type instead
+
+error: importing legacy numeric constants
+  --> tests/ui/legacy_numeric_constants_unfixable.rs:21:17
+   |
+LL |             use std::u32;
+   |                 ^^^^^^^^
+...
+LL |     b!();
+   |     ---- in this macro invocation
+   |
+   = help: remove this import
+   = note: then `u32::<CONST>` will resolve to the respective associated constant
+   = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: importing a legacy numeric constant
+  --> tests/ui/legacy_numeric_constants_unfixable.rs:76:9
+   |
+LL |     use std::u32::MAX;
+   |         ^^^^^^^^^^^^^
+   |
+   = help: remove this import and use the associated constant `u32::MAX` from the primitive type instead
+
+error: aborting due to 9 previous errors
+
diff --git a/tests/ui/manual_saturating_arithmetic.fixed b/tests/ui/manual_saturating_arithmetic.fixed
index 8218f10881a..41a32df32ee 100644
--- a/tests/ui/manual_saturating_arithmetic.fixed
+++ b/tests/ui/manual_saturating_arithmetic.fixed
@@ -1,4 +1,4 @@
-#![allow(unused_imports)]
+#![allow(clippy::legacy_numeric_constants, unused_imports)]
 
 use std::{i128, i32, u128, u32};
 
diff --git a/tests/ui/manual_saturating_arithmetic.rs b/tests/ui/manual_saturating_arithmetic.rs
index 60022b54b02..3a6b32d8069 100644
--- a/tests/ui/manual_saturating_arithmetic.rs
+++ b/tests/ui/manual_saturating_arithmetic.rs
@@ -1,4 +1,4 @@
-#![allow(unused_imports)]
+#![allow(clippy::legacy_numeric_constants, unused_imports)]
 
 use std::{i128, i32, u128, u32};
 
diff --git a/tests/ui/suspicious_arithmetic_impl.rs b/tests/ui/suspicious_arithmetic_impl.rs
index 1bd4cd5fb50..07280351e76 100644
--- a/tests/ui/suspicious_arithmetic_impl.rs
+++ b/tests/ui/suspicious_arithmetic_impl.rs
@@ -1,3 +1,4 @@
+#![allow(clippy::legacy_numeric_constants)]
 #![warn(clippy::suspicious_arithmetic_impl)]
 use std::ops::{
     Add, AddAssign, BitAnd, BitOr, BitOrAssign, BitXor, Div, DivAssign, Mul, MulAssign, Rem, Shl, Shr, Sub,
diff --git a/tests/ui/suspicious_arithmetic_impl.stderr b/tests/ui/suspicious_arithmetic_impl.stderr
index 193cd64149b..1bfca49a635 100644
--- a/tests/ui/suspicious_arithmetic_impl.stderr
+++ b/tests/ui/suspicious_arithmetic_impl.stderr
@@ -1,5 +1,5 @@
 error: suspicious use of `-` in `Add` impl
-  --> tests/ui/suspicious_arithmetic_impl.rs:13:20
+  --> tests/ui/suspicious_arithmetic_impl.rs:14:20
    |
 LL |         Foo(self.0 - other.0)
    |                    ^
@@ -8,7 +8,7 @@ LL |         Foo(self.0 - other.0)
    = help: to override `-D warnings` add `#[allow(clippy::suspicious_arithmetic_impl)]`
 
 error: suspicious use of `-` in `AddAssign` impl
-  --> tests/ui/suspicious_arithmetic_impl.rs:21:23
+  --> tests/ui/suspicious_arithmetic_impl.rs:22:23
    |
 LL |         *self = *self - other;
    |                       ^
@@ -17,43 +17,43 @@ LL |         *self = *self - other;
    = help: to override `-D warnings` add `#[allow(clippy::suspicious_op_assign_impl)]`
 
 error: suspicious use of `/` in `MulAssign` impl
-  --> tests/ui/suspicious_arithmetic_impl.rs:36:16
+  --> tests/ui/suspicious_arithmetic_impl.rs:37:16
    |
 LL |         self.0 /= other.0;
    |                ^^
 
 error: suspicious use of `/` in `Rem` impl
-  --> tests/ui/suspicious_arithmetic_impl.rs:75:20
+  --> tests/ui/suspicious_arithmetic_impl.rs:76:20
    |
 LL |         Foo(self.0 / other.0)
    |                    ^
 
 error: suspicious use of `|` in `BitAnd` impl
-  --> tests/ui/suspicious_arithmetic_impl.rs:84:20
+  --> tests/ui/suspicious_arithmetic_impl.rs:85:20
    |
 LL |         Foo(self.0 | other.0)
    |                    ^
 
 error: suspicious use of `^` in `BitOr` impl
-  --> tests/ui/suspicious_arithmetic_impl.rs:93:20
+  --> tests/ui/suspicious_arithmetic_impl.rs:94:20
    |
 LL |         Foo(self.0 ^ other.0)
    |                    ^
 
 error: suspicious use of `&` in `BitXor` impl
-  --> tests/ui/suspicious_arithmetic_impl.rs:102:20
+  --> tests/ui/suspicious_arithmetic_impl.rs:103:20
    |
 LL |         Foo(self.0 & other.0)
    |                    ^
 
 error: suspicious use of `>>` in `Shl` impl
-  --> tests/ui/suspicious_arithmetic_impl.rs:111:20
+  --> tests/ui/suspicious_arithmetic_impl.rs:112:20
    |
 LL |         Foo(self.0 >> other.0)
    |                    ^^
 
 error: suspicious use of `<<` in `Shr` impl
-  --> tests/ui/suspicious_arithmetic_impl.rs:120:20
+  --> tests/ui/suspicious_arithmetic_impl.rs:121:20
    |
 LL |         Foo(self.0 << other.0)
    |                    ^^