diff --git a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl
index 4faaffce7bb..162cfce0a24 100644
--- a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl
@@ -317,4 +317,10 @@ mir_build_indirect_structural_match =
     to use a constant of type `{$non_sm_ty}` in a pattern, `{$non_sm_ty}` must be annotated with `#[derive(PartialEq, Eq)]`
 
 mir_build_nontrivial_structural_match = 
-    to use a constant of type `{$non_sm_ty}` in a pattern, the constant's initializer must be trivial or `{$non_sm_ty}` must be annotated with `#[derive(PartialEq, Eq)]`
\ No newline at end of file
+    to use a constant of type `{$non_sm_ty}` in a pattern, the constant's initializer must be trivial or `{$non_sm_ty}` must be annotated with `#[derive(PartialEq, Eq)]`
+
+mir_build_overlapping_range_endpoints = multiple patterns overlap on their endpoints
+    .range = ... with this range
+    .note = you likely meant to write mutually exclusive ranges
+
+mir_build_overlapping_range = this range overlaps on `{$range}`...
diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs
index ac903010c8d..5f320708c84 100644
--- a/compiler/rustc_middle/src/thir.rs
+++ b/compiler/rustc_middle/src/thir.rs
@@ -9,6 +9,7 @@
 //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/thir.html
 
 use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
+use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::RangeEnd;
@@ -575,6 +576,12 @@ impl<'tcx> Pat<'tcx> {
     }
 }
 
+impl<'tcx> IntoDiagnosticArg for Pat<'tcx> {
+    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+        format!("{}", self).into_diagnostic_arg()
+    }
+}
+
 #[derive(Clone, Debug, HashStable)]
 pub struct Ascription<'tcx> {
     pub annotation: CanonicalUserTypeAnnotation<'tcx>,
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index 840a1faf95a..6bb15730b00 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -4,6 +4,7 @@ use rustc_errors::{
     error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, MultiSpan,
 };
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
+use rustc_middle::thir::Pat;
 use rustc_middle::ty::{self, Ty};
 use rustc_span::{symbol::Ident, Span};
 
@@ -665,3 +666,22 @@ pub struct IndirectStructuralMatch<'tcx> {
 pub struct NontrivialStructuralMatch<'tcx> {
     pub non_sm_ty: Ty<'tcx>,
 }
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_overlapping_range_endpoints)]
+#[note]
+pub struct OverlappingRangeEndpoints<'tcx> {
+    #[label(range)]
+    pub range: Span,
+    #[subdiagnostic]
+    pub overlap: Overlap<'tcx>,
+}
+
+#[derive(Debug)]
+#[derive(Subdiagnostic)]
+#[label(mir_build_overlapping_range)]
+pub struct Overlap<'tcx> {
+    #[primary_span]
+    pub span: Span,
+    pub range: Pat<'tcx>,
+}
diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
index a95349d7670..323df1b8147 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
@@ -67,6 +67,7 @@ use self::SliceKind::*;
 
 use super::compare_const_vals;
 use super::usefulness::{MatchCheckCtxt, PatCtxt};
+use crate::errors::{Overlap, OverlappingRangeEndpoints};
 
 /// Recursively expand this pattern into its subpatterns. Only useful for or-patterns.
 fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> {
@@ -96,7 +97,7 @@ fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> {
 /// `IntRange` is never used to encode an empty range or a "range" that wraps
 /// around the (offset) space: i.e., `range.lo <= range.hi`.
 #[derive(Clone, PartialEq, Eq)]
-pub(super) struct IntRange {
+pub(crate) struct IntRange {
     range: RangeInclusive<u128>,
     /// Keeps the bias used for encoding the range. It depends on the type of the range and
     /// possibly the pointer size of the current architecture. The algorithm ensures we never
@@ -284,32 +285,24 @@ impl IntRange {
             return;
         }
 
-        let overlaps: Vec<_> = pats
+        // Get the first overlap. We get only the first rather than all of them
+        // because displaying multiple overlaps requires a way to eagerly translate
+        // lintdiagnostics, but that doesn't exist.
+        let overlap = pats
             .filter_map(|pat| Some((pat.ctor().as_int_range()?, pat.span())))
             .filter(|(range, _)| self.suspicious_intersection(range))
-            .map(|(range, span)| (self.intersection(&range).unwrap(), span))
-            .collect();
+            .map(|(range, span)| Overlap {
+                range: self.intersection(&range).unwrap().to_pat(pcx.cx.tcx, pcx.ty),
+                span,
+            })
+            .next();
 
-        if !overlaps.is_empty() {
-            pcx.cx.tcx.struct_span_lint_hir(
+        if let Some(overlap) = overlap {
+            pcx.cx.tcx.emit_spanned_lint(
                 lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
                 hir_id,
                 pcx.span,
-                "multiple patterns overlap on their endpoints",
-                |lint| {
-                    for (int_range, span) in overlaps {
-                        lint.span_label(
-                            span,
-                            &format!(
-                                "this range overlaps on `{}`...",
-                                int_range.to_pat(pcx.cx.tcx, pcx.ty)
-                            ),
-                        );
-                    }
-                    lint.span_label(pcx.span, "... with this range");
-                    lint.note("you likely meant to write mutually exclusive ranges");
-                    lint
-                },
+                OverlappingRangeEndpoints { overlap, range: pcx.span },
             );
         }
     }
diff --git a/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr b/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr
index ea0e8f6e49e..a1c802add17 100644
--- a/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr
+++ b/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr
@@ -59,7 +59,6 @@ error: multiple patterns overlap on their endpoints
 LL |         0..=10 => {}
    |         ------ this range overlaps on `10_u8`...
 LL |         20..=30 => {}
-   |         ------- this range overlaps on `20_u8`...
 LL |         10..=20 => {}
    |         ^^^^^^^ ... with this range
    |