diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index 383e80851f0d7..f46cf0dc0ff1b 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -7,6 +7,7 @@ use crate::errors::*;
 
 use rustc_arena::TypedArena;
 use rustc_ast::Mutability;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::{
     struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
@@ -660,6 +661,17 @@ fn report_arm_reachability<'p, 'tcx>(
     }
 }
 
+fn collect_non_exhaustive_tys<'p, 'tcx>(
+    pat: &DeconstructedPat<'p, 'tcx>,
+    non_exhaustive_tys: &mut FxHashSet<Ty<'tcx>>,
+) {
+    if matches!(pat.ctor(), Constructor::NonExhaustive) {
+        non_exhaustive_tys.insert(pat.ty());
+    }
+    pat.iter_fields()
+        .for_each(|field_pat| collect_non_exhaustive_tys(field_pat, non_exhaustive_tys))
+}
+
 /// Report that a match is not exhaustive.
 fn non_exhaustive_match<'p, 'tcx>(
     cx: &MatchCheckCtxt<'p, 'tcx>,
@@ -717,22 +729,27 @@ fn non_exhaustive_match<'p, 'tcx>(
         scrut_ty,
         if is_variant_list_non_exhaustive { ", which is marked as non-exhaustive" } else { "" }
     ));
-    if (scrut_ty == cx.tcx.types.usize || scrut_ty == cx.tcx.types.isize)
-        && !is_empty_match
-        && witnesses.len() == 1
-        && matches!(witnesses[0].ctor(), Constructor::NonExhaustive)
-    {
-        err.note(format!(
-            "`{scrut_ty}` does not have a fixed maximum value, so a wildcard `_` is necessary to match \
-             exhaustively",
-        ));
-        if cx.tcx.sess.is_nightly_build() {
-            err.help(format!(
-                "add `#![feature(precise_pointer_size_matching)]` to the crate attributes to \
-                 enable precise `{scrut_ty}` matching",
-            ));
+
+    if !is_empty_match && witnesses.len() == 1 {
+        let mut non_exhaustive_tys = FxHashSet::default();
+        collect_non_exhaustive_tys(&witnesses[0], &mut non_exhaustive_tys);
+
+        for ty in non_exhaustive_tys {
+            if ty == cx.tcx.types.usize || ty == cx.tcx.types.isize {
+                err.note(format!(
+                    "`{ty}` does not have a fixed maximum value, so a wildcard `_` is necessary to match \
+                     exhaustively",
+                ));
+                if cx.tcx.sess.is_nightly_build() {
+                    err.help(format!(
+                        "add `#![feature(precise_pointer_size_matching)]` to the crate attributes to \
+                         enable precise `{ty}` matching",
+                    ));
+                }
+            }
         }
     }
+
     if let ty::Ref(_, sub_ty, _) = scrut_ty.kind() {
         if !sub_ty.is_inhabited_from(cx.tcx, cx.module, cx.param_env) {
             err.note("references are always considered inhabited");
diff --git a/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.deny.stderr b/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.deny.stderr
index 0e0f0c3e11ee8..df330c60b1e81 100644
--- a/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.deny.stderr
+++ b/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.deny.stderr
@@ -77,6 +77,8 @@ LL |     m!((0usize, true), (0..5, true) | (5..=usize::MAX, true) | (0..=usize::
    |        ^^^^^^^^^^^^^^ pattern `(_, _)` not covered
    |
    = note: the matched value is of type `(usize, bool)`
+   = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
 LL |         match $s { $($t)+ => {}, (_, _) => todo!() }
@@ -131,6 +133,8 @@ LL |     m!((0isize, true), (isize::MIN..5, true)
    |        ^^^^^^^^^^^^^^ pattern `(_, _)` not covered
    |
    = note: the matched value is of type `(isize, bool)`
+   = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
 LL |         match $s { $($t)+ => {}, (_, _) => todo!() }
diff --git a/tests/ui/pattern/usefulness/issue-85222-types-containing-non-exhaustive-types.rs b/tests/ui/pattern/usefulness/issue-85222-types-containing-non-exhaustive-types.rs
new file mode 100644
index 0000000000000..8f58227ee2c64
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-85222-types-containing-non-exhaustive-types.rs
@@ -0,0 +1,67 @@
+struct A<T> {
+    a: T,
+}
+
+struct B<T, U>(T, U);
+
+fn main() {
+    match 0 {
+        //~^ ERROR non-exhaustive patterns: `_` not covered [E0004]
+        0 => (),
+        1..=usize::MAX => (),
+    }
+
+    match (0usize, 0usize) {
+        //~^ ERROR non-exhaustive patterns: `(_, _)` not covered [E0004]
+        (0, 0) => (),
+        (1..=usize::MAX, 1..=usize::MAX) => (),
+    }
+
+    match (0isize, 0usize) {
+        //~^ ERROR non-exhaustive patterns: `(_, _)` not covered [E0004]
+        (isize::MIN..=isize::MAX, 0) => (),
+        (isize::MIN..=isize::MAX, 1..=usize::MAX) => (),
+    }
+
+    // Should not report note about usize not having fixed max value
+    match Some(1usize) {
+        //~^ ERROR non-exhaustive patterns: `Some(_)` not covered
+        None => {}
+    }
+
+    match Some(4) {
+        //~^ ERROR non-exhaustive patterns: `Some(_)` not covered
+        Some(0) => (),
+        Some(1..=usize::MAX) => (),
+        None => (),
+    }
+
+    match Some(Some(Some(0))) {
+        //~^ ERROR non-exhaustive patterns: `Some(Some(Some(_)))` not covered
+        Some(Some(Some(0))) => (),
+        Some(Some(Some(1..=usize::MAX))) => (),
+        Some(Some(None)) => (),
+        Some(None) => (),
+        None => (),
+    }
+
+    match (A { a: 0usize }) {
+        //~^ ERROR non-exhaustive patterns: `A { .. }` not covered [E0004]
+        A { a: 0 } => (),
+        A { a: 1..=usize::MAX } => (),
+    }
+
+    match B(0isize, 0usize) {
+        //~^ ERROR non-exhaustive patterns: `B(_, _)` not covered [E0004]
+        B(isize::MIN..=isize::MAX, 0) => (),
+        B(isize::MIN..=isize::MAX, 1..=usize::MAX) => (),
+    }
+
+    // Should report only the note about usize not having fixed max value and not report
+    // report the note about isize
+    match B(0isize, 0usize) {
+        //~^ ERROR non-exhaustive patterns: `B(_, _)` not covered [E0004]
+        B(_, 0) => (),
+        B(_, 1..=usize::MAX) => (),
+    }
+}
diff --git a/tests/ui/pattern/usefulness/issue-85222-types-containing-non-exhaustive-types.stderr b/tests/ui/pattern/usefulness/issue-85222-types-containing-non-exhaustive-types.stderr
new file mode 100644
index 0000000000000..ea1d99e20ae1f
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-85222-types-containing-non-exhaustive-types.stderr
@@ -0,0 +1,170 @@
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:8:11
+   |
+LL |     match 0 {
+   |           ^ pattern `_` not covered
+   |
+   = note: the matched value is of type `usize`
+   = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         1..=usize::MAX => (),
+LL ~         _ => todo!(),
+   |
+
+error[E0004]: non-exhaustive patterns: `(_, _)` not covered
+  --> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:14:11
+   |
+LL |     match (0usize, 0usize) {
+   |           ^^^^^^^^^^^^^^^^ pattern `(_, _)` not covered
+   |
+   = note: the matched value is of type `(usize, usize)`
+   = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         (1..=usize::MAX, 1..=usize::MAX) => (),
+LL ~         (_, _) => todo!(),
+   |
+
+error[E0004]: non-exhaustive patterns: `(_, _)` not covered
+  --> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:20:11
+   |
+LL |     match (0isize, 0usize) {
+   |           ^^^^^^^^^^^^^^^^ pattern `(_, _)` not covered
+   |
+   = note: the matched value is of type `(isize, usize)`
+   = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         (isize::MIN..=isize::MAX, 1..=usize::MAX) => (),
+LL ~         (_, _) => todo!(),
+   |
+
+error[E0004]: non-exhaustive patterns: `Some(_)` not covered
+  --> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:27:11
+   |
+LL |     match Some(1usize) {
+   |           ^^^^^^^^^^^^ pattern `Some(_)` not covered
+   |
+note: `Option<usize>` defined here
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+  ::: $SRC_DIR/core/src/option.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Option<usize>`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         None => {},
+LL +         Some(_) => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `Some(_)` not covered
+  --> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:32:11
+   |
+LL |     match Some(4) {
+   |           ^^^^^^^ pattern `Some(_)` not covered
+   |
+note: `Option<usize>` defined here
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+  ::: $SRC_DIR/core/src/option.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Option<usize>`
+   = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         None => (),
+LL ~         Some(_) => todo!(),
+   |
+
+error[E0004]: non-exhaustive patterns: `Some(Some(Some(_)))` not covered
+  --> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:39:11
+   |
+LL |     match Some(Some(Some(0))) {
+   |           ^^^^^^^^^^^^^^^^^^^ pattern `Some(Some(Some(_)))` not covered
+   |
+note: `Option<Option<Option<usize>>>` defined here
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+  ::: $SRC_DIR/core/src/option.rs:LL:COL
+   |
+   = note: not covered
+   |
+   = note: not covered
+   |
+   = note: not covered
+   = note: the matched value is of type `Option<Option<Option<usize>>>`
+   = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         None => (),
+LL ~         Some(Some(Some(_))) => todo!(),
+   |
+
+error[E0004]: non-exhaustive patterns: `A { .. }` not covered
+  --> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:48:11
+   |
+LL |     match (A { a: 0usize }) {
+   |           ^^^^^^^^^^^^^^^^^ pattern `A { .. }` not covered
+   |
+note: `A<usize>` defined here
+  --> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:1:8
+   |
+LL | struct A<T> {
+   |        ^
+   = note: the matched value is of type `A<usize>`
+   = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         A { a: 1..=usize::MAX } => (),
+LL ~         A { .. } => todo!(),
+   |
+
+error[E0004]: non-exhaustive patterns: `B(_, _)` not covered
+  --> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:54:11
+   |
+LL |     match B(0isize, 0usize) {
+   |           ^^^^^^^^^^^^^^^^^ pattern `B(_, _)` not covered
+   |
+note: `B<isize, usize>` defined here
+  --> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:5:8
+   |
+LL | struct B<T, U>(T, U);
+   |        ^
+   = note: the matched value is of type `B<isize, usize>`
+   = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         B(isize::MIN..=isize::MAX, 1..=usize::MAX) => (),
+LL ~         B(_, _) => todo!(),
+   |
+
+error[E0004]: non-exhaustive patterns: `B(_, _)` not covered
+  --> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:62:11
+   |
+LL |     match B(0isize, 0usize) {
+   |           ^^^^^^^^^^^^^^^^^ pattern `B(_, _)` not covered
+   |
+note: `B<isize, usize>` defined here
+  --> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:5:8
+   |
+LL | struct B<T, U>(T, U);
+   |        ^
+   = note: the matched value is of type `B<isize, usize>`
+   = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         B(_, 1..=usize::MAX) => (),
+LL ~         B(_, _) => todo!(),
+   |
+
+error: aborting due to 9 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr b/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr
index b8af566de7c68..d798ec722ddd2 100644
--- a/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr
+++ b/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr
@@ -10,6 +10,8 @@ note: `Foo` defined here
 LL | struct Foo {
    |        ^^^
    = note: the matched value is of type `Foo`
+   = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
 LL ~         Foo { first: false, second: Some([1, 2, 3, 4]) } => (),
diff --git a/tests/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr b/tests/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr
index e2a65ff852404..50c7fc889f421 100644
--- a/tests/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr
+++ b/tests/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr
@@ -10,6 +10,8 @@ note: `Foo` defined here
 LL | struct Foo(isize, isize);
    |        ^^^
    = note: the matched value is of type `Foo`
+   = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
 LL ~         Foo(2, b) => println!("{}", b),