Skip to main content

rustc_mir_build/thir/pattern/
check_match.rs

1use rustc_arena::{DroplessArena, TypedArena};
2use rustc_ast::Mutability;
3use rustc_data_structures::fx::FxIndexSet;
4use rustc_data_structures::stack::ensure_sufficient_stack;
5use rustc_errors::codes::*;
6use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan, struct_span_code_err};
7use rustc_hir::def::*;
8use rustc_hir::def_id::{DefId, LocalDefId};
9use rustc_hir::{self as hir, BindingMode, ByRef, HirId, MatchSource};
10use rustc_infer::infer::TyCtxtInferExt;
11use rustc_lint::Level;
12use rustc_middle::bug;
13use rustc_middle::thir::visit::Visitor;
14use rustc_middle::thir::*;
15use rustc_middle::ty::print::with_no_trimmed_paths;
16use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
17use rustc_pattern_analysis::errors::Uncovered;
18use rustc_pattern_analysis::rustc::{
19    Constructor, DeconstructedPat, MatchArm, RedundancyExplanation, RevealedTy,
20    RustcPatCtxt as PatCtxt, Usefulness, UsefulnessReport, WitnessPat,
21};
22use rustc_session::lint::builtin::{
23    BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS,
24};
25use rustc_span::edit_distance::find_best_match_for_name;
26use rustc_span::hygiene::DesugaringKind;
27use rustc_span::{Ident, Span};
28use rustc_trait_selection::infer::InferCtxtExt;
29use tracing::instrument;
30
31use crate::errors::*;
32use crate::fluent_generated as fluent;
33
34pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> {
35    let typeck_results = tcx.typeck(def_id);
36    let (thir, expr) = tcx.thir_body(def_id)?;
37    let thir = thir.borrow();
38    let pattern_arena = TypedArena::default();
39    let dropless_arena = DroplessArena::default();
40    let mut visitor = MatchVisitor {
41        tcx,
42        thir: &*thir,
43        typeck_results,
44        // FIXME(#132279): We're in a body, should handle opaques.
45        typing_env: ty::TypingEnv::non_body_analysis(tcx, def_id),
46        hir_source: tcx.local_def_id_to_hir_id(def_id),
47        let_source: LetSource::None,
48        pattern_arena: &pattern_arena,
49        dropless_arena: &dropless_arena,
50        error: Ok(()),
51    };
52    visitor.visit_expr(&thir[expr]);
53
54    let origin = match tcx.def_kind(def_id) {
55        DefKind::AssocFn | DefKind::Fn => "function argument",
56        DefKind::Closure => "closure argument",
57        // other types of MIR don't have function parameters, and we don't need to
58        // categorize those for the irrefutable check.
59        _ if thir.params.is_empty() => "",
60        kind => ::rustc_middle::util::bug::bug_fmt(format_args!("unexpected function parameters in THIR: {0:?} {1:?}",
        kind, def_id))bug!("unexpected function parameters in THIR: {kind:?} {def_id:?}"),
61    };
62
63    for param in thir.params.iter() {
64        if let Some(box ref pattern) = param.pat {
65            visitor.check_binding_is_irrefutable(pattern, origin, None, None);
66        }
67    }
68    visitor.error
69}
70
71#[derive(#[automatically_derived]
impl ::core::fmt::Debug for RefutableFlag {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                RefutableFlag::Irrefutable => "Irrefutable",
                RefutableFlag::Refutable => "Refutable",
            })
    }
}Debug, #[automatically_derived]
impl ::core::marker::Copy for RefutableFlag { }Copy, #[automatically_derived]
impl ::core::clone::Clone for RefutableFlag {
    #[inline]
    fn clone(&self) -> RefutableFlag { *self }
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for RefutableFlag {
    #[inline]
    fn eq(&self, other: &RefutableFlag) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq)]
72enum RefutableFlag {
73    Irrefutable,
74    Refutable,
75}
76use RefutableFlag::*;
77
78#[derive(#[automatically_derived]
impl ::core::clone::Clone for LetSource {
    #[inline]
    fn clone(&self) -> LetSource { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for LetSource { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for LetSource {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                LetSource::None => "None",
                LetSource::PlainLet => "PlainLet",
                LetSource::IfLet => "IfLet",
                LetSource::IfLetGuard => "IfLetGuard",
                LetSource::LetElse => "LetElse",
                LetSource::WhileLet => "WhileLet",
                LetSource::Else => "Else",
                LetSource::ElseIfLet => "ElseIfLet",
            })
    }
}Debug, #[automatically_derived]
impl ::core::cmp::PartialEq for LetSource {
    #[inline]
    fn eq(&self, other: &LetSource) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for LetSource {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_receiver_is_total_eq(&self) {}
}Eq)]
79enum LetSource {
80    None,
81    PlainLet,
82    IfLet,
83    IfLetGuard,
84    LetElse,
85    WhileLet,
86    Else,
87    ElseIfLet,
88}
89
90struct MatchVisitor<'p, 'tcx> {
91    tcx: TyCtxt<'tcx>,
92    typing_env: ty::TypingEnv<'tcx>,
93    typeck_results: &'tcx ty::TypeckResults<'tcx>,
94    thir: &'p Thir<'tcx>,
95    hir_source: HirId,
96    let_source: LetSource,
97    pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
98    dropless_arena: &'p DroplessArena,
99    /// Tracks if we encountered an error while checking this body. That the first function to
100    /// report it stores it here. Some functions return `Result` to allow callers to short-circuit
101    /// on error, but callers don't need to store it here again.
102    error: Result<(), ErrorGuaranteed>,
103}
104
105// Visitor for a thir body. This calls `check_match`, `check_let` and `check_let_chain` as
106// appropriate.
107impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> {
108    fn thir(&self) -> &'p Thir<'tcx> {
109        self.thir
110    }
111
112    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("visit_arm",
                                    "rustc_mir_build::thir::pattern::check_match",
                                    ::tracing::Level::TRACE,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/thir/pattern/check_match.rs"),
                                    ::tracing_core::__macro_support::Option::Some(112u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_mir_build::thir::pattern::check_match"),
                                    ::tracing_core::field::FieldSet::new(&["arm"],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::TRACE <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::TRACE <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                let mut iter = meta.fields().iter();
                                meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&arm)
                                                            as &dyn Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: () = loop {};
            return __tracing_attr_fake_return;
        }
        {
            self.with_hir_source(arm.hir_id,
                |this|
                    {
                        if let Some(expr) = arm.guard {
                            this.with_let_source(LetSource::IfLetGuard,
                                |this| { this.visit_expr(&this.thir[expr]) });
                        }
                        this.visit_pat(&arm.pattern);
                        this.visit_expr(&self.thir[arm.body]);
                    });
        }
    }
}#[instrument(level = "trace", skip(self))]
113    fn visit_arm(&mut self, arm: &'p Arm<'tcx>) {
114        self.with_hir_source(arm.hir_id, |this| {
115            if let Some(expr) = arm.guard {
116                this.with_let_source(LetSource::IfLetGuard, |this| {
117                    this.visit_expr(&this.thir[expr])
118                });
119            }
120            this.visit_pat(&arm.pattern);
121            this.visit_expr(&self.thir[arm.body]);
122        });
123    }
124
125    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("visit_expr",
                                    "rustc_mir_build::thir::pattern::check_match",
                                    ::tracing::Level::TRACE,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/thir/pattern/check_match.rs"),
                                    ::tracing_core::__macro_support::Option::Some(125u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_mir_build::thir::pattern::check_match"),
                                    ::tracing_core::field::FieldSet::new(&["ex"],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::TRACE <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::TRACE <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                let mut iter = meta.fields().iter();
                                meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&ex)
                                                            as &dyn Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: () = loop {};
            return __tracing_attr_fake_return;
        }
        {
            match ex.kind {
                ExprKind::Scope { value, hir_id, .. } => {
                    self.with_hir_source(hir_id,
                        |this| { this.visit_expr(&this.thir[value]); });
                    return;
                }
                ExprKind::If { cond, then, else_opt, if_then_scope: _ } => {
                    let let_source =
                        match ex.span.desugaring_kind() {
                            Some(DesugaringKind::WhileLoop) => LetSource::WhileLet,
                            _ =>
                                match self.let_source {
                                    LetSource::Else => LetSource::ElseIfLet,
                                    _ => LetSource::IfLet,
                                },
                        };
                    self.with_let_source(let_source,
                        |this| this.visit_expr(&self.thir[cond]));
                    self.with_let_source(LetSource::None,
                        |this| { this.visit_expr(&this.thir[then]); });
                    if let Some(else_) = else_opt {
                        self.with_let_source(LetSource::Else,
                            |this| { this.visit_expr(&this.thir[else_]) });
                    }
                    return;
                }
                ExprKind::Match { scrutinee, box ref arms, match_source } => {
                    self.check_match(scrutinee, arms, match_source, ex.span);
                }
                ExprKind::LoopMatch {
                    match_data: box LoopMatchMatchData {
                        scrutinee, box ref arms, span
                        }, .. } => {
                    self.check_match(scrutinee, arms, MatchSource::Normal,
                        span);
                }
                ExprKind::Let { box ref pat, expr } => {
                    self.check_let(pat, Some(expr), ex.span);
                }
                ExprKind::LogicalOp { op: LogicalOp::And, .. } if
                    !#[allow(non_exhaustive_omitted_patterns)] match self.let_source
                            {
                            LetSource::None => true,
                            _ => false,
                        } => {
                    let mut chain_refutabilities = Vec::new();
                    let Ok(()) =
                        self.visit_land(ex,
                            &mut chain_refutabilities) else { return };
                    if chain_refutabilities.iter().any(|x| x.is_some()) {
                        self.check_let_chain(chain_refutabilities, ex.span);
                    }
                    return;
                }
                _ => {}
            };
            self.with_let_source(LetSource::None,
                |this| visit::walk_expr(this, ex));
        }
    }
}#[instrument(level = "trace", skip(self))]
126    fn visit_expr(&mut self, ex: &'p Expr<'tcx>) {
127        match ex.kind {
128            ExprKind::Scope { value, hir_id, .. } => {
129                self.with_hir_source(hir_id, |this| {
130                    this.visit_expr(&this.thir[value]);
131                });
132                return;
133            }
134            ExprKind::If { cond, then, else_opt, if_then_scope: _ } => {
135                // Give a specific `let_source` for the condition.
136                let let_source = match ex.span.desugaring_kind() {
137                    Some(DesugaringKind::WhileLoop) => LetSource::WhileLet,
138                    _ => match self.let_source {
139                        LetSource::Else => LetSource::ElseIfLet,
140                        _ => LetSource::IfLet,
141                    },
142                };
143                self.with_let_source(let_source, |this| this.visit_expr(&self.thir[cond]));
144                self.with_let_source(LetSource::None, |this| {
145                    this.visit_expr(&this.thir[then]);
146                });
147                if let Some(else_) = else_opt {
148                    self.with_let_source(LetSource::Else, |this| {
149                        this.visit_expr(&this.thir[else_])
150                    });
151                }
152                return;
153            }
154            ExprKind::Match { scrutinee, box ref arms, match_source } => {
155                self.check_match(scrutinee, arms, match_source, ex.span);
156            }
157            ExprKind::LoopMatch {
158                match_data: box LoopMatchMatchData { scrutinee, box ref arms, span },
159                ..
160            } => {
161                self.check_match(scrutinee, arms, MatchSource::Normal, span);
162            }
163            ExprKind::Let { box ref pat, expr } => {
164                self.check_let(pat, Some(expr), ex.span);
165            }
166            ExprKind::LogicalOp { op: LogicalOp::And, .. }
167                if !matches!(self.let_source, LetSource::None) =>
168            {
169                let mut chain_refutabilities = Vec::new();
170                let Ok(()) = self.visit_land(ex, &mut chain_refutabilities) else { return };
171                // If at least one of the operands is a `let ... = ...`.
172                if chain_refutabilities.iter().any(|x| x.is_some()) {
173                    self.check_let_chain(chain_refutabilities, ex.span);
174                }
175                return;
176            }
177            _ => {}
178        };
179        self.with_let_source(LetSource::None, |this| visit::walk_expr(this, ex));
180    }
181
182    fn visit_stmt(&mut self, stmt: &'p Stmt<'tcx>) {
183        match stmt.kind {
184            StmtKind::Let { box ref pattern, initializer, else_block, hir_id, span, .. } => {
185                self.with_hir_source(hir_id, |this| {
186                    let let_source =
187                        if else_block.is_some() { LetSource::LetElse } else { LetSource::PlainLet };
188                    this.with_let_source(let_source, |this| {
189                        this.check_let(pattern, initializer, span)
190                    });
191                    visit::walk_stmt(this, stmt);
192                });
193            }
194            StmtKind::Expr { .. } => {
195                visit::walk_stmt(self, stmt);
196            }
197        }
198    }
199}
200
201impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
202    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("with_let_source",
                                    "rustc_mir_build::thir::pattern::check_match",
                                    ::tracing::Level::TRACE,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/thir/pattern/check_match.rs"),
                                    ::tracing_core::__macro_support::Option::Some(202u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_mir_build::thir::pattern::check_match"),
                                    ::tracing_core::field::FieldSet::new(&["let_source"],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::TRACE <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::TRACE <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                let mut iter = meta.fields().iter();
                                meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&let_source)
                                                            as &dyn Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: () = loop {};
            return __tracing_attr_fake_return;
        }
        {
            let old_let_source = self.let_source;
            self.let_source = let_source;
            ensure_sufficient_stack(|| f(self));
            self.let_source = old_let_source;
        }
    }
}#[instrument(level = "trace", skip(self, f))]
203    fn with_let_source(&mut self, let_source: LetSource, f: impl FnOnce(&mut Self)) {
204        let old_let_source = self.let_source;
205        self.let_source = let_source;
206        ensure_sufficient_stack(|| f(self));
207        self.let_source = old_let_source;
208    }
209
210    fn with_hir_source<T>(&mut self, new_hir_source: HirId, f: impl FnOnce(&mut Self) -> T) -> T {
211        let old_hir_source = self.hir_source;
212        self.hir_source = new_hir_source;
213        let ret = f(self);
214        self.hir_source = old_hir_source;
215        ret
216    }
217
218    /// Visit a nested chain of `&&`. Used for if-let chains. This must call `visit_expr` on the
219    /// subexpressions we are not handling ourselves.
220    fn visit_land(
221        &mut self,
222        ex: &'p Expr<'tcx>,
223        accumulator: &mut Vec<Option<(Span, RefutableFlag)>>,
224    ) -> Result<(), ErrorGuaranteed> {
225        match ex.kind {
226            ExprKind::Scope { value, hir_id, .. } => {
227                self.with_hir_source(hir_id, |this| this.visit_land(&this.thir[value], accumulator))
228            }
229            ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => {
230                // We recurse into the lhs only, because `&&` chains associate to the left.
231                let res_lhs = self.visit_land(&self.thir[lhs], accumulator);
232                let res_rhs = self.visit_land_rhs(&self.thir[rhs])?;
233                accumulator.push(res_rhs);
234                res_lhs
235            }
236            _ => {
237                let res = self.visit_land_rhs(ex)?;
238                accumulator.push(res);
239                Ok(())
240            }
241        }
242    }
243
244    /// Visit the right-hand-side of a `&&`. Used for if-let chains. Returns `Some` if the
245    /// expression was ultimately a `let ... = ...`, and `None` if it was a normal boolean
246    /// expression. This must call `visit_expr` on the subexpressions we are not handling ourselves.
247    fn visit_land_rhs(
248        &mut self,
249        ex: &'p Expr<'tcx>,
250    ) -> Result<Option<(Span, RefutableFlag)>, ErrorGuaranteed> {
251        match ex.kind {
252            ExprKind::Scope { value, hir_id, .. } => {
253                self.with_hir_source(hir_id, |this| this.visit_land_rhs(&this.thir[value]))
254            }
255            ExprKind::Let { box ref pat, expr } => {
256                let expr = &self.thir()[expr];
257                self.with_let_source(LetSource::None, |this| {
258                    this.visit_expr(expr);
259                });
260                Ok(Some((ex.span, self.is_let_irrefutable(pat, Some(expr))?)))
261            }
262            _ => {
263                self.with_let_source(LetSource::None, |this| {
264                    this.visit_expr(ex);
265                });
266                Ok(None)
267            }
268        }
269    }
270
271    fn lower_pattern(
272        &mut self,
273        cx: &PatCtxt<'p, 'tcx>,
274        pat: &'p Pat<'tcx>,
275    ) -> Result<&'p DeconstructedPat<'p, 'tcx>, ErrorGuaranteed> {
276        if let Err(err) = pat.pat_error_reported() {
277            self.error = Err(err);
278            Err(err)
279        } else {
280            // Check the pattern for some things unrelated to exhaustiveness.
281            let refutable = if cx.refutable { Refutable } else { Irrefutable };
282            let mut err = Ok(());
283            pat.walk_always(|pat| {
284                check_borrow_conflicts_in_at_patterns(self, pat);
285                check_for_bindings_named_same_as_variants(self, pat, refutable);
286                err = err.and(check_never_pattern(cx, pat));
287            });
288            err?;
289            Ok(self.pattern_arena.alloc(cx.lower_pat(pat)))
290        }
291    }
292
293    /// Inspects the match scrutinee expression to determine whether the place it evaluates to may
294    /// hold invalid data.
295    fn is_known_valid_scrutinee(&self, scrutinee: &Expr<'tcx>) -> bool {
296        use ExprKind::*;
297        match &scrutinee.kind {
298            // Pointers can validly point to a place with invalid data. It is undecided whether
299            // references can too, so we conservatively assume they can.
300            Deref { .. } => false,
301            // Inherit validity of the parent place, unless the parent is an union.
302            Field { lhs, .. } => {
303                let lhs = &self.thir()[*lhs];
304                match lhs.ty.kind() {
305                    ty::Adt(def, _) if def.is_union() => false,
306                    _ => self.is_known_valid_scrutinee(lhs),
307                }
308            }
309            // Essentially a field access.
310            Index { lhs, .. } => {
311                let lhs = &self.thir()[*lhs];
312                self.is_known_valid_scrutinee(lhs)
313            }
314
315            // No-op.
316            Scope { value, .. } => self.is_known_valid_scrutinee(&self.thir()[*value]),
317
318            // Casts don't cause a load.
319            NeverToAny { source }
320            | Cast { source }
321            | Use { source }
322            | PointerCoercion { source, .. }
323            | PlaceTypeAscription { source, .. }
324            | ValueTypeAscription { source, .. }
325            | PlaceUnwrapUnsafeBinder { source }
326            | ValueUnwrapUnsafeBinder { source }
327            | WrapUnsafeBinder { source } => self.is_known_valid_scrutinee(&self.thir()[*source]),
328
329            // These diverge.
330            Become { .. }
331            | Break { .. }
332            | Continue { .. }
333            | ConstContinue { .. }
334            | Return { .. } => true,
335
336            // These are statements that evaluate to `()`.
337            Assign { .. } | AssignOp { .. } | InlineAsm { .. } | Let { .. } => true,
338
339            // These evaluate to a value.
340            RawBorrow { .. }
341            | Adt { .. }
342            | Array { .. }
343            | Binary { .. }
344            | Block { .. }
345            | Borrow { .. }
346            | Box { .. }
347            | Call { .. }
348            | ByUse { .. }
349            | Closure { .. }
350            | ConstBlock { .. }
351            | ConstParam { .. }
352            | If { .. }
353            | Literal { .. }
354            | LogicalOp { .. }
355            | Loop { .. }
356            | LoopMatch { .. }
357            | Match { .. }
358            | NamedConst { .. }
359            | NonHirLiteral { .. }
360            | Repeat { .. }
361            | StaticRef { .. }
362            | ThreadLocalRef { .. }
363            | Tuple { .. }
364            | Unary { .. }
365            | UpvarRef { .. }
366            | VarRef { .. }
367            | ZstLiteral { .. }
368            | Yield { .. } => true,
369        }
370    }
371
372    fn new_cx(
373        &self,
374        refutability: RefutableFlag,
375        whole_match_span: Option<Span>,
376        scrutinee: Option<&Expr<'tcx>>,
377        scrut_span: Span,
378    ) -> PatCtxt<'p, 'tcx> {
379        let refutable = match refutability {
380            Irrefutable => false,
381            Refutable => true,
382        };
383        // If we don't have a scrutinee we're either a function parameter or a `let x;`. Both cases
384        // require validity.
385        let known_valid_scrutinee =
386            scrutinee.map(|scrut| self.is_known_valid_scrutinee(scrut)).unwrap_or(true);
387        PatCtxt {
388            tcx: self.tcx,
389            typeck_results: self.typeck_results,
390            typing_env: self.typing_env,
391            module: self.tcx.parent_module(self.hir_source).to_def_id(),
392            dropless_arena: self.dropless_arena,
393            match_lint_level: self.hir_source,
394            whole_match_span,
395            scrut_span,
396            refutable,
397            known_valid_scrutinee,
398            internal_state: Default::default(),
399        }
400    }
401
402    fn analyze_patterns(
403        &mut self,
404        cx: &PatCtxt<'p, 'tcx>,
405        arms: &[MatchArm<'p, 'tcx>],
406        scrut_ty: Ty<'tcx>,
407    ) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
408        let report =
409            rustc_pattern_analysis::rustc::analyze_match(&cx, &arms, scrut_ty).map_err(|err| {
410                self.error = Err(err);
411                err
412            })?;
413
414        // Warn unreachable subpatterns.
415        for (arm, is_useful) in report.arm_usefulness.iter() {
416            if let Usefulness::Useful(redundant_subpats) = is_useful
417                && !redundant_subpats.is_empty()
418            {
419                let mut redundant_subpats = redundant_subpats.clone();
420                // Emit lints in the order in which they occur in the file.
421                redundant_subpats.sort_unstable_by_key(|(pat, _)| pat.data().span);
422                for (pat, explanation) in redundant_subpats {
423                    report_unreachable_pattern(cx, arm.arm_data, pat, &explanation, None)
424                }
425            }
426        }
427        Ok(report)
428    }
429
430    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("check_let",
                                    "rustc_mir_build::thir::pattern::check_match",
                                    ::tracing::Level::TRACE,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/thir/pattern/check_match.rs"),
                                    ::tracing_core::__macro_support::Option::Some(430u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_mir_build::thir::pattern::check_match"),
                                    ::tracing_core::field::FieldSet::new(&["pat", "scrutinee",
                                                    "span"], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::TRACE <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::TRACE <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                let mut iter = meta.fields().iter();
                                meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&pat)
                                                            as &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&scrutinee)
                                                            as &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&span)
                                                            as &dyn Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: () = loop {};
            return __tracing_attr_fake_return;
        }
        {
            if !(self.let_source != LetSource::None) {
                ::core::panicking::panic("assertion failed: self.let_source != LetSource::None")
            };
            let scrut = scrutinee.map(|id| &self.thir[id]);
            if let LetSource::PlainLet = self.let_source {
                self.check_binding_is_irrefutable(pat, "local binding", scrut,
                    Some(span))
            } else {
                let Ok(refutability) =
                    self.is_let_irrefutable(pat, scrut) else { return };
                if #[allow(non_exhaustive_omitted_patterns)] match refutability
                        {
                        Irrefutable => true,
                        _ => false,
                    } {
                    report_irrefutable_let_patterns(self.tcx, self.hir_source,
                        self.let_source, 1, span);
                }
            }
        }
    }
}#[instrument(level = "trace", skip(self))]
431    fn check_let(&mut self, pat: &'p Pat<'tcx>, scrutinee: Option<ExprId>, span: Span) {
432        assert!(self.let_source != LetSource::None);
433        let scrut = scrutinee.map(|id| &self.thir[id]);
434        if let LetSource::PlainLet = self.let_source {
435            self.check_binding_is_irrefutable(pat, "local binding", scrut, Some(span))
436        } else {
437            let Ok(refutability) = self.is_let_irrefutable(pat, scrut) else { return };
438            if matches!(refutability, Irrefutable) {
439                report_irrefutable_let_patterns(
440                    self.tcx,
441                    self.hir_source,
442                    self.let_source,
443                    1,
444                    span,
445                );
446            }
447        }
448    }
449
450    fn check_match(
451        &mut self,
452        scrut: ExprId,
453        arms: &[ArmId],
454        source: hir::MatchSource,
455        expr_span: Span,
456    ) {
457        let scrut = &self.thir[scrut];
458        let cx = self.new_cx(Refutable, Some(expr_span), Some(scrut), scrut.span);
459
460        let mut tarms = Vec::with_capacity(arms.len());
461        for &arm in arms {
462            let arm = &self.thir.arms[arm];
463            let got_error = self.with_hir_source(arm.hir_id, |this| {
464                let Ok(pat) = this.lower_pattern(&cx, &arm.pattern) else { return true };
465                let arm =
466                    MatchArm { pat, arm_data: this.hir_source, has_guard: arm.guard.is_some() };
467                tarms.push(arm);
468                false
469            });
470            if got_error {
471                return;
472            }
473        }
474
475        let Ok(report) = self.analyze_patterns(&cx, &tarms, scrut.ty) else { return };
476
477        match source {
478            // Don't report arm reachability of desugared `match $iter.into_iter() { iter => .. }`
479            // when the iterator is an uninhabited type. unreachable_code will trigger instead.
480            hir::MatchSource::ForLoopDesugar if arms.len() == 1 => {}
481            hir::MatchSource::ForLoopDesugar
482            | hir::MatchSource::Postfix
483            | hir::MatchSource::Normal
484            | hir::MatchSource::FormatArgs => {
485                let is_match_arm =
486                    #[allow(non_exhaustive_omitted_patterns)] match source {
    hir::MatchSource::Postfix | hir::MatchSource::Normal => true,
    _ => false,
}matches!(source, hir::MatchSource::Postfix | hir::MatchSource::Normal);
487                report_arm_reachability(&cx, &report, is_match_arm);
488            }
489            // Unreachable patterns in try and await expressions occur when one of
490            // the arms are an uninhabited type. Which is OK.
491            hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar(_) => {}
492        }
493
494        // Check if the match is exhaustive.
495        let witnesses = report.non_exhaustiveness_witnesses;
496        if !witnesses.is_empty() {
497            if source == hir::MatchSource::ForLoopDesugar
498                && let [_, snd_arm] = *arms
499            {
500                // the for loop pattern is not irrefutable
501                let pat = &self.thir[snd_arm].pattern;
502                // `pat` should be `Some(<pat_field>)` from a desugared for loop.
503                if true {
    match (&pat.span.desugaring_kind(), &Some(DesugaringKind::ForLoop)) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    };
};debug_assert_eq!(pat.span.desugaring_kind(), Some(DesugaringKind::ForLoop));
504                let PatKind::Variant { ref subpatterns, .. } = pat.kind else { ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!() };
505                let [pat_field] = &subpatterns[..] else { ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!() };
506                self.check_binding_is_irrefutable(
507                    &pat_field.pattern,
508                    "`for` loop binding",
509                    None,
510                    None,
511                );
512            } else {
513                // span after scrutinee, or after `.match`. That is, the braces, arms,
514                // and any whitespace preceding the braces.
515                let braces_span = match source {
516                    hir::MatchSource::Normal => scrut
517                        .span
518                        .find_ancestor_in_same_ctxt(expr_span)
519                        .map(|scrut_span| scrut_span.shrink_to_hi().with_hi(expr_span.hi())),
520                    hir::MatchSource::Postfix => {
521                        // This is horrendous, and we should deal with it by just
522                        // stashing the span of the braces somewhere (like in the match source).
523                        scrut.span.find_ancestor_in_same_ctxt(expr_span).and_then(|scrut_span| {
524                            let sm = self.tcx.sess.source_map();
525                            let brace_span = sm.span_extend_to_next_char(scrut_span, '{', true);
526                            if sm.span_to_snippet(sm.next_point(brace_span)).as_deref() == Ok("{") {
527                                let sp = brace_span.shrink_to_hi().with_hi(expr_span.hi());
528                                // We also need to extend backwards for whitespace
529                                sm.span_extend_prev_while(sp, |c| c.is_whitespace()).ok()
530                            } else {
531                                None
532                            }
533                        })
534                    }
535                    hir::MatchSource::ForLoopDesugar
536                    | hir::MatchSource::TryDesugar(_)
537                    | hir::MatchSource::AwaitDesugar
538                    | hir::MatchSource::FormatArgs => None,
539                };
540                self.error = Err(report_non_exhaustive_match(
541                    &cx,
542                    self.thir,
543                    scrut.ty,
544                    scrut.span,
545                    witnesses,
546                    arms,
547                    braces_span,
548                ));
549            }
550        }
551    }
552
553    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("check_let_chain",
                                    "rustc_mir_build::thir::pattern::check_match",
                                    ::tracing::Level::TRACE,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/thir/pattern/check_match.rs"),
                                    ::tracing_core::__macro_support::Option::Some(553u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_mir_build::thir::pattern::check_match"),
                                    ::tracing_core::field::FieldSet::new(&["chain_refutabilities",
                                                    "whole_chain_span"],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::TRACE <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::TRACE <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                let mut iter = meta.fields().iter();
                                meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&chain_refutabilities)
                                                            as &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&whole_chain_span)
                                                            as &dyn Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: () = loop {};
            return __tracing_attr_fake_return;
        }
        {
            if !(self.let_source != LetSource::None) {
                ::core::panicking::panic("assertion failed: self.let_source != LetSource::None")
            };
            if chain_refutabilities.iter().all(|r|
                        #[allow(non_exhaustive_omitted_patterns)] match *r {
                            Some((_, Irrefutable)) => true,
                            _ => false,
                        }) {
                report_irrefutable_let_patterns(self.tcx, self.hir_source,
                    self.let_source, chain_refutabilities.len(),
                    whole_chain_span);
                return;
            }
            if let Some(until) =
                        chain_refutabilities.iter().position(|r|
                                !#[allow(non_exhaustive_omitted_patterns)] match *r {
                                        Some((_, Irrefutable)) => true,
                                        _ => false,
                                    }) && until > 0 {
                if !#[allow(non_exhaustive_omitted_patterns)] match self.let_source
                            {
                            LetSource::WhileLet | LetSource::IfLetGuard |
                                LetSource::ElseIfLet => true,
                            _ => false,
                        } {
                    let prefix = &chain_refutabilities[..until];
                    let span_start = prefix[0].unwrap().0;
                    let span_end = prefix.last().unwrap().unwrap().0;
                    let span = span_start.to(span_end);
                    let count = prefix.len();
                    self.tcx.emit_node_span_lint(IRREFUTABLE_LET_PATTERNS,
                        self.hir_source, span,
                        LeadingIrrefutableLetPatterns { count });
                }
            }
            if let Some(from) =
                        chain_refutabilities.iter().rposition(|r|
                                !#[allow(non_exhaustive_omitted_patterns)] match *r {
                                        Some((_, Irrefutable)) => true,
                                        _ => false,
                                    }) && from != (chain_refutabilities.len() - 1) {
                let suffix = &chain_refutabilities[from + 1..];
                let span_start = suffix[0].unwrap().0;
                let span_end = suffix.last().unwrap().unwrap().0;
                let span = span_start.to(span_end);
                let count = suffix.len();
                self.tcx.emit_node_span_lint(IRREFUTABLE_LET_PATTERNS,
                    self.hir_source, span,
                    TrailingIrrefutableLetPatterns { count });
            }
        }
    }
}#[instrument(level = "trace", skip(self))]
554    fn check_let_chain(
555        &mut self,
556        chain_refutabilities: Vec<Option<(Span, RefutableFlag)>>,
557        whole_chain_span: Span,
558    ) {
559        assert!(self.let_source != LetSource::None);
560
561        if chain_refutabilities.iter().all(|r| matches!(*r, Some((_, Irrefutable)))) {
562            // The entire chain is made up of irrefutable `let` statements
563            report_irrefutable_let_patterns(
564                self.tcx,
565                self.hir_source,
566                self.let_source,
567                chain_refutabilities.len(),
568                whole_chain_span,
569            );
570            return;
571        }
572
573        if let Some(until) =
574            chain_refutabilities.iter().position(|r| !matches!(*r, Some((_, Irrefutable))))
575            && until > 0
576        {
577            // The chain has a non-zero prefix of irrefutable `let` statements.
578
579            // Check if the let source is while, for there is no alternative place to put a prefix,
580            // and we shouldn't lint.
581            // For let guards inside a match, prefixes might use bindings of the match pattern,
582            // so can't always be moved out.
583            // For `else if let`, an extra indentation level would be required to move the bindings.
584            // FIXME: Add checking whether the bindings are actually used in the prefix,
585            // and lint if they are not.
586            if !matches!(
587                self.let_source,
588                LetSource::WhileLet | LetSource::IfLetGuard | LetSource::ElseIfLet
589            ) {
590                // Emit the lint
591                let prefix = &chain_refutabilities[..until];
592                let span_start = prefix[0].unwrap().0;
593                let span_end = prefix.last().unwrap().unwrap().0;
594                let span = span_start.to(span_end);
595                let count = prefix.len();
596                self.tcx.emit_node_span_lint(
597                    IRREFUTABLE_LET_PATTERNS,
598                    self.hir_source,
599                    span,
600                    LeadingIrrefutableLetPatterns { count },
601                );
602            }
603        }
604
605        if let Some(from) =
606            chain_refutabilities.iter().rposition(|r| !matches!(*r, Some((_, Irrefutable))))
607            && from != (chain_refutabilities.len() - 1)
608        {
609            // The chain has a non-empty suffix of irrefutable `let` statements
610            let suffix = &chain_refutabilities[from + 1..];
611            let span_start = suffix[0].unwrap().0;
612            let span_end = suffix.last().unwrap().unwrap().0;
613            let span = span_start.to(span_end);
614            let count = suffix.len();
615            self.tcx.emit_node_span_lint(
616                IRREFUTABLE_LET_PATTERNS,
617                self.hir_source,
618                span,
619                TrailingIrrefutableLetPatterns { count },
620            );
621        }
622    }
623
624    fn analyze_binding(
625        &mut self,
626        pat: &'p Pat<'tcx>,
627        refutability: RefutableFlag,
628        scrut: Option<&Expr<'tcx>>,
629    ) -> Result<(PatCtxt<'p, 'tcx>, UsefulnessReport<'p, 'tcx>), ErrorGuaranteed> {
630        let cx = self.new_cx(refutability, None, scrut, pat.span);
631        let pat = self.lower_pattern(&cx, pat)?;
632        let arms = [MatchArm { pat, arm_data: self.hir_source, has_guard: false }];
633        let report = self.analyze_patterns(&cx, &arms, pat.ty().inner())?;
634        Ok((cx, report))
635    }
636
637    fn is_let_irrefutable(
638        &mut self,
639        pat: &'p Pat<'tcx>,
640        scrut: Option<&Expr<'tcx>>,
641    ) -> Result<RefutableFlag, ErrorGuaranteed> {
642        let (cx, report) = self.analyze_binding(pat, Refutable, scrut)?;
643        // Report if the pattern is unreachable, which can only occur when the type is uninhabited.
644        report_arm_reachability(&cx, &report, false);
645        // If the list of witnesses is empty, the match is exhaustive, i.e. the `if let` pattern is
646        // irrefutable.
647        Ok(if report.non_exhaustiveness_witnesses.is_empty() { Irrefutable } else { Refutable })
648    }
649
650    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("check_binding_is_irrefutable",
                                    "rustc_mir_build::thir::pattern::check_match",
                                    ::tracing::Level::TRACE,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/thir/pattern/check_match.rs"),
                                    ::tracing_core::__macro_support::Option::Some(650u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_mir_build::thir::pattern::check_match"),
                                    ::tracing_core::field::FieldSet::new(&["pat", "origin",
                                                    "scrut", "sp"],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::TRACE <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::TRACE <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                let mut iter = meta.fields().iter();
                                meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&pat)
                                                            as &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&origin as
                                                            &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&scrut)
                                                            as &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&sp)
                                                            as &dyn Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: () = loop {};
            return __tracing_attr_fake_return;
        }
        {
            let pattern_ty = pat.ty;
            let Ok((cx, report)) =
                self.analyze_binding(pat, Irrefutable, scrut) else { return };
            let witnesses = report.non_exhaustiveness_witnesses;
            if witnesses.is_empty() { return; }
            let inform = sp.is_some().then_some(Inform);
            let mut let_suggestion = None;
            let mut misc_suggestion = None;
            let mut interpreted_as_const = None;
            let mut interpreted_as_const_sugg = None;
            if let Some(def_id) =
                    is_const_pat_that_looks_like_binding(self.tcx, pat) {
                let span = self.tcx.def_span(def_id);
                let variable = self.tcx.item_name(def_id).to_string();
                interpreted_as_const =
                    Some(InterpretedAsConst {
                            span,
                            variable: variable.clone(),
                        });
                interpreted_as_const_sugg =
                    Some(InterpretedAsConstSugg { span: pat.span, variable });
            } else if let PatKind::Constant { .. } = pat.kind &&
                    let Ok(snippet) =
                        self.tcx.sess.source_map().span_to_snippet(pat.span) {
                if snippet.chars().all(|c| c.is_digit(10)) {
                    misc_suggestion =
                        Some(MiscPatternSuggestion::AttemptedIntegerLiteral {
                                start_span: pat.span.shrink_to_lo(),
                            });
                }
            }
            if let Some(span) = sp &&
                            self.tcx.sess.source_map().is_span_accessible(span) &&
                        interpreted_as_const.is_none() && scrut.is_some() {
                let mut bindings = ::alloc::vec::Vec::new();
                pat.each_binding(|name, _, _, _| bindings.push(name));
                let semi_span = span.shrink_to_hi();
                let start_span = span.shrink_to_lo();
                let end_span = semi_span.shrink_to_lo();
                let count = witnesses.len();
                let_suggestion =
                    Some(if bindings.is_empty() {
                            SuggestLet::If { start_span, semi_span, count }
                        } else { SuggestLet::Else { end_span, count } });
            };
            let adt_defined_here =
                report_adt_defined_here(self.tcx, pattern_ty, &witnesses,
                    false);
            let witness_1_is_privately_uninhabited =
                if let Some(witness_1) = witnesses.get(0) &&
                                let ty::Adt(adt, args) = witness_1.ty().kind() &&
                            adt.is_enum() &&
                        let Constructor::Variant(variant_index) = witness_1.ctor() {
                    let variant_inhabited =
                        adt.variant(*variant_index).inhabited_predicate(self.tcx,
                                *adt).instantiate(self.tcx, args);
                    variant_inhabited.apply(self.tcx, cx.typing_env, cx.module)
                        &&
                        !variant_inhabited.apply_ignore_module(self.tcx,
                                cx.typing_env)
                } else { false };
            let witness_1 = cx.print_witness_pat(witnesses.get(0).unwrap());
            self.error =
                Err(self.tcx.dcx().emit_err(PatternNotCovered {
                            span: pat.span,
                            origin,
                            uncovered: Uncovered::new(pat.span, &cx, witnesses),
                            inform,
                            interpreted_as_const,
                            interpreted_as_const_sugg,
                            witness_1_is_privately_uninhabited,
                            witness_1,
                            _p: (),
                            pattern_ty,
                            let_suggestion,
                            misc_suggestion,
                            adt_defined_here,
                        }));
        }
    }
}#[instrument(level = "trace", skip(self))]
651    fn check_binding_is_irrefutable(
652        &mut self,
653        pat: &'p Pat<'tcx>,
654        origin: &str,
655        scrut: Option<&Expr<'tcx>>,
656        sp: Option<Span>,
657    ) {
658        let pattern_ty = pat.ty;
659
660        let Ok((cx, report)) = self.analyze_binding(pat, Irrefutable, scrut) else { return };
661        let witnesses = report.non_exhaustiveness_witnesses;
662        if witnesses.is_empty() {
663            // The pattern is irrefutable.
664            return;
665        }
666
667        let inform = sp.is_some().then_some(Inform);
668        let mut let_suggestion = None;
669        let mut misc_suggestion = None;
670        let mut interpreted_as_const = None;
671        let mut interpreted_as_const_sugg = None;
672
673        if let Some(def_id) = is_const_pat_that_looks_like_binding(self.tcx, pat) {
674            let span = self.tcx.def_span(def_id);
675            let variable = self.tcx.item_name(def_id).to_string();
676            // When we encounter a constant as the binding name, point at the `const` definition.
677            interpreted_as_const = Some(InterpretedAsConst { span, variable: variable.clone() });
678            interpreted_as_const_sugg = Some(InterpretedAsConstSugg { span: pat.span, variable });
679        } else if let PatKind::Constant { .. } = pat.kind
680            && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(pat.span)
681        {
682            // If the pattern to match is an integer literal:
683            if snippet.chars().all(|c| c.is_digit(10)) {
684                // Then give a suggestion, the user might've meant to create a binding instead.
685                misc_suggestion = Some(MiscPatternSuggestion::AttemptedIntegerLiteral {
686                    start_span: pat.span.shrink_to_lo(),
687                });
688            }
689        }
690
691        if let Some(span) = sp
692            && self.tcx.sess.source_map().is_span_accessible(span)
693            && interpreted_as_const.is_none()
694            && scrut.is_some()
695        {
696            let mut bindings = vec![];
697            pat.each_binding(|name, _, _, _| bindings.push(name));
698
699            let semi_span = span.shrink_to_hi();
700            let start_span = span.shrink_to_lo();
701            let end_span = semi_span.shrink_to_lo();
702            let count = witnesses.len();
703
704            let_suggestion = Some(if bindings.is_empty() {
705                SuggestLet::If { start_span, semi_span, count }
706            } else {
707                SuggestLet::Else { end_span, count }
708            });
709        };
710
711        let adt_defined_here = report_adt_defined_here(self.tcx, pattern_ty, &witnesses, false);
712
713        // Emit an extra note if the first uncovered witness would be uninhabited
714        // if we disregard visibility.
715        let witness_1_is_privately_uninhabited = if let Some(witness_1) = witnesses.get(0)
716            && let ty::Adt(adt, args) = witness_1.ty().kind()
717            && adt.is_enum()
718            && let Constructor::Variant(variant_index) = witness_1.ctor()
719        {
720            let variant_inhabited = adt
721                .variant(*variant_index)
722                .inhabited_predicate(self.tcx, *adt)
723                .instantiate(self.tcx, args);
724            variant_inhabited.apply(self.tcx, cx.typing_env, cx.module)
725                && !variant_inhabited.apply_ignore_module(self.tcx, cx.typing_env)
726        } else {
727            false
728        };
729
730        let witness_1 = cx.print_witness_pat(witnesses.get(0).unwrap());
731
732        self.error = Err(self.tcx.dcx().emit_err(PatternNotCovered {
733            span: pat.span,
734            origin,
735            uncovered: Uncovered::new(pat.span, &cx, witnesses),
736            inform,
737            interpreted_as_const,
738            interpreted_as_const_sugg,
739            witness_1_is_privately_uninhabited,
740            witness_1,
741            _p: (),
742            pattern_ty,
743            let_suggestion,
744            misc_suggestion,
745            adt_defined_here,
746        }));
747    }
748}
749
750/// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.
751/// Check that there are no borrow or move conflicts in `binding @ subpat` patterns.
752///
753/// For example, this would reject:
754/// - `ref x @ Some(ref mut y)`,
755/// - `ref mut x @ Some(ref y)`,
756/// - `ref mut x @ Some(ref mut y)`,
757/// - `ref mut? x @ Some(y)`, and
758/// - `x @ Some(ref mut? y)`.
759///
760/// This analysis is *not* subsumed by NLL.
761fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: &Pat<'tcx>) {
762    // Extract `sub` in `binding @ sub`.
763    let PatKind::Binding { name, mode, ty, subpattern: Some(box ref sub), .. } = pat.kind else {
764        return;
765    };
766
767    let is_binding_by_move = |ty: Ty<'tcx>| !cx.tcx.type_is_copy_modulo_regions(cx.typing_env, ty);
768
769    let sess = cx.tcx.sess;
770
771    // Get the binding move, extract the mutability if by-ref.
772    let mut_outer = match mode.0 {
773        ByRef::No if is_binding_by_move(ty) => {
774            // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`.
775            let mut conflicts_ref = Vec::new();
776            sub.each_binding(|_, mode, _, span| {
777                if #[allow(non_exhaustive_omitted_patterns)] match mode {
    ByRef::Yes(..) => true,
    _ => false,
}matches!(mode, ByRef::Yes(..)) {
778                    conflicts_ref.push(span)
779                }
780            });
781            if !conflicts_ref.is_empty() {
782                sess.dcx().emit_err(BorrowOfMovedValue {
783                    binding_span: pat.span,
784                    conflicts_ref,
785                    name: Ident::new(name, pat.span),
786                    ty,
787                    suggest_borrowing: Some(pat.span.shrink_to_lo()),
788                });
789            }
790            return;
791        }
792        ByRef::No => return,
793        ByRef::Yes(_, m) => m,
794    };
795
796    // We now have `ref $mut_outer binding @ sub` (semantically).
797    // Recurse into each binding in `sub` and find mutability or move conflicts.
798    let mut conflicts_move = Vec::new();
799    let mut conflicts_mut_mut = Vec::new();
800    let mut conflicts_mut_ref = Vec::new();
801    sub.each_binding(|name, mode, ty, span| {
802        match mode {
803            ByRef::Yes(_, mut_inner) => match (mut_outer, mut_inner) {
804                // Both sides are `ref`.
805                (Mutability::Not, Mutability::Not) => {}
806                // 2x `ref mut`.
807                (Mutability::Mut, Mutability::Mut) => {
808                    conflicts_mut_mut.push(Conflict::Mut { span, name })
809                }
810                (Mutability::Not, Mutability::Mut) => {
811                    conflicts_mut_ref.push(Conflict::Mut { span, name })
812                }
813                (Mutability::Mut, Mutability::Not) => {
814                    conflicts_mut_ref.push(Conflict::Ref { span, name })
815                }
816            },
817            ByRef::No if is_binding_by_move(ty) => {
818                conflicts_move.push(Conflict::Moved { span, name }) // `ref mut?` + by-move conflict.
819            }
820            ByRef::No => {} // `ref mut?` + by-copy is fine.
821        }
822    });
823
824    let report_mut_mut = !conflicts_mut_mut.is_empty();
825    let report_mut_ref = !conflicts_mut_ref.is_empty();
826    let report_move_conflict = !conflicts_move.is_empty();
827
828    let mut occurrences = match mut_outer {
829        Mutability::Mut => <[_]>::into_vec(::alloc::boxed::box_new([Conflict::Mut {
                    span: pat.span,
                    name,
                }]))vec![Conflict::Mut { span: pat.span, name }],
830        Mutability::Not => <[_]>::into_vec(::alloc::boxed::box_new([Conflict::Ref {
                    span: pat.span,
                    name,
                }]))vec![Conflict::Ref { span: pat.span, name }],
831    };
832    occurrences.extend(conflicts_mut_mut);
833    occurrences.extend(conflicts_mut_ref);
834    occurrences.extend(conflicts_move);
835
836    // Report errors if any.
837    if report_mut_mut {
838        // Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
839        sess.dcx().emit_err(MultipleMutBorrows { span: pat.span, occurrences });
840    } else if report_mut_ref {
841        // Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
842        match mut_outer {
843            Mutability::Mut => {
844                sess.dcx().emit_err(AlreadyMutBorrowed { span: pat.span, occurrences });
845            }
846            Mutability::Not => {
847                sess.dcx().emit_err(AlreadyBorrowed { span: pat.span, occurrences });
848            }
849        };
850    } else if report_move_conflict {
851        // Report by-ref and by-move conflicts, e.g. `ref x @ y`.
852        sess.dcx().emit_err(MovedWhileBorrowed { span: pat.span, occurrences });
853    }
854}
855
856fn check_for_bindings_named_same_as_variants(
857    cx: &MatchVisitor<'_, '_>,
858    pat: &Pat<'_>,
859    rf: RefutableFlag,
860) {
861    if let PatKind::Binding {
862        name,
863        mode: BindingMode(ByRef::No, Mutability::Not),
864        subpattern: None,
865        ty,
866        ..
867    } = pat.kind
868        && let ty::Adt(edef, _) = ty.peel_refs().kind()
869        && edef.is_enum()
870        && edef
871            .variants()
872            .iter()
873            .any(|variant| variant.name == name && variant.ctor_kind() == Some(CtorKind::Const))
874    {
875        let variant_count = edef.variants().len();
876        let ty_path = { let _guard = NoTrimmedGuard::new(); cx.tcx.def_path_str(edef.did()) }with_no_trimmed_paths!(cx.tcx.def_path_str(edef.did()));
877        cx.tcx.emit_node_span_lint(
878            BINDINGS_WITH_VARIANT_NAME,
879            cx.hir_source,
880            pat.span,
881            BindingsWithVariantName {
882                // If this is an irrefutable pattern, and there's > 1 variant,
883                // then we can't actually match on this. Applying the below
884                // suggestion would produce code that breaks on `check_binding_is_irrefutable`.
885                suggestion: if rf == Refutable || variant_count == 1 {
886                    Some(pat.span)
887                } else {
888                    None
889                },
890                ty_path,
891                name: Ident::new(name, pat.span),
892            },
893        )
894    }
895}
896
897/// Check that never patterns are only used on inhabited types.
898fn check_never_pattern<'tcx>(
899    cx: &PatCtxt<'_, 'tcx>,
900    pat: &Pat<'tcx>,
901) -> Result<(), ErrorGuaranteed> {
902    if let PatKind::Never = pat.kind {
903        if !cx.is_uninhabited(pat.ty) {
904            return Err(cx.tcx.dcx().emit_err(NonEmptyNeverPattern { span: pat.span, ty: pat.ty }));
905        }
906    }
907    Ok(())
908}
909
910fn report_irrefutable_let_patterns(
911    tcx: TyCtxt<'_>,
912    id: HirId,
913    source: LetSource,
914    count: usize,
915    span: Span,
916) {
917    macro_rules! emit_diag {
918        ($lint:tt) => {{
919            tcx.emit_node_span_lint(IRREFUTABLE_LET_PATTERNS, id, span, $lint { count });
920        }};
921    }
922
923    match source {
924        LetSource::None | LetSource::PlainLet | LetSource::Else => ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!(),
925        LetSource::IfLet | LetSource::ElseIfLet => {
    tcx.emit_node_span_lint(IRREFUTABLE_LET_PATTERNS, id, span,
        IrrefutableLetPatternsIfLet { count });
}emit_diag!(IrrefutableLetPatternsIfLet),
926        LetSource::IfLetGuard => {
    tcx.emit_node_span_lint(IRREFUTABLE_LET_PATTERNS, id, span,
        IrrefutableLetPatternsIfLetGuard { count });
}emit_diag!(IrrefutableLetPatternsIfLetGuard),
927        LetSource::LetElse => {
    tcx.emit_node_span_lint(IRREFUTABLE_LET_PATTERNS, id, span,
        IrrefutableLetPatternsLetElse { count });
}emit_diag!(IrrefutableLetPatternsLetElse),
928        LetSource::WhileLet => {
    tcx.emit_node_span_lint(IRREFUTABLE_LET_PATTERNS, id, span,
        IrrefutableLetPatternsWhileLet { count });
}emit_diag!(IrrefutableLetPatternsWhileLet),
929    }
930}
931
932/// Report unreachable arms, if any.
933fn report_unreachable_pattern<'p, 'tcx>(
934    cx: &PatCtxt<'p, 'tcx>,
935    hir_id: HirId,
936    pat: &DeconstructedPat<'p, 'tcx>,
937    explanation: &RedundancyExplanation<'p, 'tcx>,
938    whole_arm_span: Option<Span>,
939) {
940    static CAP_COVERED_BY_MANY: usize = 4;
941    let pat_span = pat.data().span;
942    let mut lint = UnreachablePattern {
943        span: Some(pat_span),
944        matches_no_values: None,
945        matches_no_values_ty: **pat.ty(),
946        uninhabited_note: None,
947        covered_by_catchall: None,
948        covered_by_one: None,
949        covered_by_many: None,
950        covered_by_many_n_more_count: 0,
951        wanted_constant: None,
952        accessible_constant: None,
953        inaccessible_constant: None,
954        pattern_let_binding: None,
955        suggest_remove: None,
956    };
957    match explanation.covered_by.as_slice() {
958        [] => {
959            // Empty pattern; we report the uninhabited type that caused the emptiness.
960            lint.span = None; // Don't label the pattern itself
961            lint.uninhabited_note = Some(()); // Give a link about empty types
962            lint.matches_no_values = Some(pat_span);
963            lint.suggest_remove = whole_arm_span; // Suggest to remove the match arm
964            pat.walk(&mut |subpat| {
965                let ty = **subpat.ty();
966                if cx.is_uninhabited(ty) {
967                    lint.matches_no_values_ty = ty;
968                    false // No need to dig further.
969                } else if #[allow(non_exhaustive_omitted_patterns)] match subpat.ctor() {
    Constructor::Ref | Constructor::UnionField => true,
    _ => false,
}matches!(subpat.ctor(), Constructor::Ref | Constructor::UnionField) {
970                    false // Don't explore further since they are not by-value.
971                } else {
972                    true
973                }
974            });
975        }
976        [covering_pat] if pat_is_catchall(covering_pat) => {
977            // A binding pattern that matches all, a single binding name.
978            let pat = covering_pat.data();
979            lint.covered_by_catchall = Some(pat.span);
980            find_fallback_pattern_typo(cx, hir_id, pat, &mut lint);
981        }
982        [covering_pat] => {
983            lint.covered_by_one = Some(covering_pat.data().span);
984        }
985        covering_pats => {
986            let mut iter = covering_pats.iter();
987            let mut multispan = MultiSpan::from_span(pat_span);
988            for p in iter.by_ref().take(CAP_COVERED_BY_MANY) {
989                multispan.push_span_label(
990                    p.data().span,
991                    fluent::mir_build_unreachable_matches_same_values,
992                );
993            }
994            let remain = iter.count();
995            if remain == 0 {
996                multispan.push_span_label(
997                    pat_span,
998                    fluent::mir_build_unreachable_making_this_unreachable,
999                );
1000            } else {
1001                lint.covered_by_many_n_more_count = remain;
1002                multispan.push_span_label(
1003                    pat_span,
1004                    fluent::mir_build_unreachable_making_this_unreachable_n_more,
1005                );
1006            }
1007            lint.covered_by_many = Some(multispan);
1008        }
1009    }
1010    cx.tcx.emit_node_span_lint(UNREACHABLE_PATTERNS, hir_id, pat_span, lint);
1011}
1012
1013/// Detect typos that were meant to be a `const` but were interpreted as a new pattern binding.
1014fn find_fallback_pattern_typo<'tcx>(
1015    cx: &PatCtxt<'_, 'tcx>,
1016    hir_id: HirId,
1017    pat: &Pat<'tcx>,
1018    lint: &mut UnreachablePattern<'_>,
1019) {
1020    if let Level::Allow = cx.tcx.lint_level_at_node(UNREACHABLE_PATTERNS, hir_id).level {
1021        // This is because we use `with_no_trimmed_paths` later, so if we never emit the lint we'd
1022        // ICE. At the same time, we don't really need to do all of this if we won't emit anything.
1023        return;
1024    }
1025    if let PatKind::Binding { name, subpattern: None, ty, .. } = pat.kind {
1026        // See if the binding might have been a `const` that was mistyped or out of scope.
1027        let mut accessible = ::alloc::vec::Vec::new()vec![];
1028        let mut accessible_path = ::alloc::vec::Vec::new()vec![];
1029        let mut inaccessible = ::alloc::vec::Vec::new()vec![];
1030        let mut imported = ::alloc::vec::Vec::new()vec![];
1031        let mut imported_spans = ::alloc::vec::Vec::new()vec![];
1032        let (infcx, param_env) = cx.tcx.infer_ctxt().build_with_typing_env(cx.typing_env);
1033        let parent = cx.tcx.hir_get_parent_item(hir_id);
1034
1035        for item in cx.tcx.hir_crate_items(()).free_items() {
1036            if let DefKind::Use = cx.tcx.def_kind(item.owner_id) {
1037                // Look for consts being re-exported.
1038                let item = cx.tcx.hir_expect_item(item.owner_id.def_id);
1039                let hir::ItemKind::Use(path, _) = item.kind else {
1040                    continue;
1041                };
1042                if let Some(value_ns) = path.res.value_ns
1043                    && let Res::Def(DefKind::Const, id) = value_ns
1044                    && infcx.can_eq(param_env, ty, cx.tcx.type_of(id).instantiate_identity())
1045                {
1046                    if cx.tcx.visibility(id).is_accessible_from(parent, cx.tcx) {
1047                        // The original const is accessible, suggest using it directly.
1048                        let item_name = cx.tcx.item_name(id);
1049                        accessible.push(item_name);
1050                        accessible_path.push({ let _guard = NoTrimmedGuard::new(); cx.tcx.def_path_str(id) }with_no_trimmed_paths!(cx.tcx.def_path_str(id)));
1051                    } else if cx.tcx.visibility(item.owner_id).is_accessible_from(parent, cx.tcx) {
1052                        // The const is accessible only through the re-export, point at
1053                        // the `use`.
1054                        let ident = item.kind.ident().unwrap();
1055                        imported.push(ident.name);
1056                        imported_spans.push(ident.span);
1057                    }
1058                }
1059            }
1060            if let DefKind::Const = cx.tcx.def_kind(item.owner_id)
1061                && infcx.can_eq(param_env, ty, cx.tcx.type_of(item.owner_id).instantiate_identity())
1062            {
1063                // Look for local consts.
1064                let item_name = cx.tcx.item_name(item.owner_id);
1065                let vis = cx.tcx.visibility(item.owner_id);
1066                if vis.is_accessible_from(parent, cx.tcx) {
1067                    accessible.push(item_name);
1068                    // FIXME: the line below from PR #135310 is a workaround for the ICE in issue
1069                    // #135289, where a macro in a dependency can create unreachable patterns in the
1070                    // current crate. Path trimming expects diagnostics for a typoed const, but no
1071                    // diagnostics are emitted and we ICE. See
1072                    // `tests/ui/resolve/const-with-typo-in-pattern-binding-ice-135289.rs` for a
1073                    // test that reproduces the ICE if we don't use `with_no_trimmed_paths!`.
1074                    let path = { let _guard = NoTrimmedGuard::new(); cx.tcx.def_path_str(item.owner_id) }with_no_trimmed_paths!(cx.tcx.def_path_str(item.owner_id));
1075                    accessible_path.push(path);
1076                } else if name == item_name {
1077                    // The const exists somewhere in this crate, but it can't be imported
1078                    // from this pattern's scope. We'll just point at its definition.
1079                    inaccessible.push(cx.tcx.def_span(item.owner_id));
1080                }
1081            }
1082        }
1083        if let Some((i, &const_name)) =
1084            accessible.iter().enumerate().find(|&(_, &const_name)| const_name == name)
1085        {
1086            // The pattern name is an exact match, so the pattern needed to be imported.
1087            lint.wanted_constant = Some(WantedConstant {
1088                span: pat.span,
1089                is_typo: false,
1090                const_name: const_name.to_string(),
1091                const_path: accessible_path[i].clone(),
1092            });
1093        } else if let Some(name) = find_best_match_for_name(&accessible, name, None) {
1094            // The pattern name is likely a typo.
1095            lint.wanted_constant = Some(WantedConstant {
1096                span: pat.span,
1097                is_typo: true,
1098                const_name: name.to_string(),
1099                const_path: name.to_string(),
1100            });
1101        } else if let Some(i) =
1102            imported.iter().enumerate().find(|&(_, &const_name)| const_name == name).map(|(i, _)| i)
1103        {
1104            // The const with the exact name wasn't re-exported from an import in this
1105            // crate, we point at the import.
1106            lint.accessible_constant = Some(imported_spans[i]);
1107        } else if let Some(name) = find_best_match_for_name(&imported, name, None) {
1108            // The typoed const wasn't re-exported by an import in this crate, we suggest
1109            // the right name (which will likely require another follow up suggestion).
1110            lint.wanted_constant = Some(WantedConstant {
1111                span: pat.span,
1112                is_typo: true,
1113                const_path: name.to_string(),
1114                const_name: name.to_string(),
1115            });
1116        } else if !inaccessible.is_empty() {
1117            for span in inaccessible {
1118                // The const with the exact name match isn't accessible, we just point at it.
1119                lint.inaccessible_constant = Some(span);
1120            }
1121        } else {
1122            // Look for local bindings for people that might have gotten confused with how
1123            // `let` and `const` works.
1124            for (_, node) in cx.tcx.hir_parent_iter(hir_id) {
1125                match node {
1126                    hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Let(let_stmt), .. }) => {
1127                        if let hir::PatKind::Binding(_, _, binding_name, _) = let_stmt.pat.kind {
1128                            if name == binding_name.name {
1129                                lint.pattern_let_binding = Some(binding_name.span);
1130                            }
1131                        }
1132                    }
1133                    hir::Node::Block(hir::Block { stmts, .. }) => {
1134                        for stmt in *stmts {
1135                            if let hir::StmtKind::Let(let_stmt) = stmt.kind
1136                                && let hir::PatKind::Binding(_, _, binding_name, _) =
1137                                    let_stmt.pat.kind
1138                                && name == binding_name.name
1139                            {
1140                                lint.pattern_let_binding = Some(binding_name.span);
1141                            }
1142                        }
1143                    }
1144                    hir::Node::Item(_) => break,
1145                    _ => {}
1146                }
1147            }
1148        }
1149    }
1150}
1151
1152/// Report unreachable arms, if any.
1153fn report_arm_reachability<'p, 'tcx>(
1154    cx: &PatCtxt<'p, 'tcx>,
1155    report: &UsefulnessReport<'p, 'tcx>,
1156    is_match_arm: bool,
1157) {
1158    let sm = cx.tcx.sess.source_map();
1159    for (arm, is_useful) in report.arm_usefulness.iter() {
1160        if let Usefulness::Redundant(explanation) = is_useful {
1161            let hir_id = arm.arm_data;
1162            let arm_span = cx.tcx.hir_span(hir_id);
1163            let whole_arm_span = if is_match_arm {
1164                // If the arm is followed by a comma, extend the span to include it.
1165                let with_whitespace = sm.span_extend_while_whitespace(arm_span);
1166                if let Some(comma) = sm.span_look_ahead(with_whitespace, ",", Some(1)) {
1167                    Some(arm_span.to(comma))
1168                } else {
1169                    Some(arm_span)
1170                }
1171            } else {
1172                None
1173            };
1174            report_unreachable_pattern(cx, hir_id, arm.pat, explanation, whole_arm_span)
1175        }
1176    }
1177}
1178
1179/// Checks for common cases of "catchall" patterns that may not be intended as such.
1180fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
1181    match pat.ctor() {
1182        Constructor::Wildcard => true,
1183        Constructor::Struct | Constructor::Ref => {
1184            pat.iter_fields().all(|ipat| pat_is_catchall(&ipat.pat))
1185        }
1186        _ => false,
1187    }
1188}
1189
1190/// If the given pattern is a named constant that looks like it could have been
1191/// intended to be a binding, returns the `DefId` of the named constant.
1192///
1193/// Diagnostics use this to give more detailed suggestions for non-exhaustive
1194/// matches.
1195fn is_const_pat_that_looks_like_binding<'tcx>(tcx: TyCtxt<'tcx>, pat: &Pat<'tcx>) -> Option<DefId> {
1196    // The pattern must be a named constant, and the name that appears in
1197    // the pattern's source text must resemble a plain identifier without any
1198    // `::` namespace separators or other non-identifier characters.
1199    if let Some(def_id) = try { pat.extra.as_deref()?.expanded_const? }
1200        && #[allow(non_exhaustive_omitted_patterns)] match tcx.def_kind(def_id) {
    DefKind::Const => true,
    _ => false,
}matches!(tcx.def_kind(def_id), DefKind::Const)
1201        && let Ok(snippet) = tcx.sess.source_map().span_to_snippet(pat.span)
1202        && snippet.chars().all(|c| c.is_alphanumeric() || c == '_')
1203    {
1204        Some(def_id)
1205    } else {
1206        None
1207    }
1208}
1209
1210/// Report that a match is not exhaustive.
1211fn report_non_exhaustive_match<'p, 'tcx>(
1212    cx: &PatCtxt<'p, 'tcx>,
1213    thir: &Thir<'tcx>,
1214    scrut_ty: Ty<'tcx>,
1215    sp: Span,
1216    witnesses: Vec<WitnessPat<'p, 'tcx>>,
1217    arms: &[ArmId],
1218    braces_span: Option<Span>,
1219) -> ErrorGuaranteed {
1220    let is_empty_match = arms.is_empty();
1221    let non_empty_enum = match scrut_ty.kind() {
1222        ty::Adt(def, _) => def.is_enum() && !def.variants().is_empty(),
1223        _ => false,
1224    };
1225    // In the case of an empty match, replace the '`_` not covered' diagnostic with something more
1226    // informative.
1227    if is_empty_match && !non_empty_enum {
1228        return cx.tcx.dcx().emit_err(NonExhaustivePatternsTypeNotEmpty {
1229            cx,
1230            scrut_span: sp,
1231            braces_span,
1232            ty: scrut_ty,
1233        });
1234    }
1235
1236    // FIXME: migration of this diagnostic will require list support
1237    let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
1238    let mut err = {
    cx.tcx.dcx().struct_span_err(sp,
            ::alloc::__export::must_use({
                    ::alloc::fmt::format(format_args!("non-exhaustive patterns: {0} not covered",
                            joined_patterns))
                })).with_code(E0004)
}struct_span_code_err!(
1239        cx.tcx.dcx(),
1240        sp,
1241        E0004,
1242        "non-exhaustive patterns: {joined_patterns} not covered"
1243    );
1244    err.span_label(
1245        sp,
1246        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("pattern{0} {1} not covered",
                if witnesses.len() == 1 { "" } else { "s" }, joined_patterns))
    })format!(
1247            "pattern{} {} not covered",
1248            rustc_errors::pluralize!(witnesses.len()),
1249            joined_patterns
1250        ),
1251    );
1252
1253    // Point at the definition of non-covered `enum` variants.
1254    if let Some(AdtDefinedHere { adt_def_span, ty, variants }) =
1255        report_adt_defined_here(cx.tcx, scrut_ty, &witnesses, true)
1256    {
1257        let mut multi_span = MultiSpan::from_span(adt_def_span);
1258        multi_span.push_span_label(adt_def_span, "");
1259        for Variant { span } in variants {
1260            multi_span.push_span_label(span, "not covered");
1261        }
1262        err.span_note(multi_span, ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}` defined here", ty))
    })format!("`{ty}` defined here"));
1263    }
1264    err.note(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("the matched value is of type `{0}`",
                scrut_ty))
    })format!("the matched value is of type `{}`", scrut_ty));
1265
1266    if !is_empty_match {
1267        let mut special_tys = FxIndexSet::default();
1268        // Look at the first witness.
1269        collect_special_tys(cx, &witnesses[0], &mut special_tys);
1270
1271        for ty in special_tys {
1272            if ty.is_ptr_sized_integral() {
1273                if ty.inner() == cx.tcx.types.usize {
1274                    err.note(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}::MAX` is not treated as exhaustive, so half-open ranges are necessary to match exhaustively",
                ty))
    })format!(
1275                        "`{ty}::MAX` is not treated as exhaustive, \
1276                        so half-open ranges are necessary to match exhaustively",
1277                    ));
1278                } else if ty.inner() == cx.tcx.types.isize {
1279                    err.note(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}::MIN` and `{0}::MAX` are not treated as exhaustive, so half-open ranges are necessary to match exhaustively",
                ty))
    })format!(
1280                        "`{ty}::MIN` and `{ty}::MAX` are not treated as exhaustive, \
1281                        so half-open ranges are necessary to match exhaustively",
1282                    ));
1283                }
1284            } else if ty.inner() == cx.tcx.types.str_ {
1285                err.note("`&str` cannot be matched exhaustively, so a wildcard `_` is necessary");
1286            } else if cx.is_foreign_non_exhaustive_enum(ty) {
1287                err.note(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}` is marked as non-exhaustive, so a wildcard `_` is necessary to match exhaustively",
                ty))
    })format!("`{ty}` is marked as non-exhaustive, so a wildcard `_` is necessary to match exhaustively"));
1288            } else if cx.is_uninhabited(ty.inner()) {
1289                // The type is uninhabited yet there is a witness: we must be in the `MaybeInvalid`
1290                // case.
1291                err.note(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}` is uninhabited but is not being matched by value, so a wildcard `_` is required",
                ty))
    })format!("`{ty}` is uninhabited but is not being matched by value, so a wildcard `_` is required"));
1292            }
1293        }
1294    }
1295
1296    if let ty::Ref(_, sub_ty, _) = scrut_ty.kind() {
1297        if !sub_ty.is_inhabited_from(cx.tcx, cx.module, cx.typing_env) {
1298            err.note("references are always considered inhabited");
1299        }
1300    }
1301
1302    for &arm in arms {
1303        let arm = &thir.arms[arm];
1304        if let Some(def_id) = is_const_pat_that_looks_like_binding(cx.tcx, &arm.pattern) {
1305            let const_name = cx.tcx.item_name(def_id);
1306            err.span_label(
1307                arm.pattern.span,
1308                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("this pattern doesn\'t introduce a new catch-all binding, but rather pattern matches against the value of constant `{0}`",
                const_name))
    })format!(
1309                    "this pattern doesn't introduce a new catch-all binding, but rather pattern \
1310                     matches against the value of constant `{const_name}`",
1311                ),
1312            );
1313            err.span_note(cx.tcx.def_span(def_id), ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("constant `{0}` defined here",
                const_name))
    })format!("constant `{const_name}` defined here"));
1314            err.span_suggestion_verbose(
1315                arm.pattern.span.shrink_to_hi(),
1316                "if you meant to introduce a binding, use a different name",
1317                "_var".to_string(),
1318                Applicability::MaybeIncorrect,
1319            );
1320        }
1321    }
1322
1323    // Whether we suggest the actual missing patterns or `_`.
1324    let suggest_the_witnesses = witnesses.len() < 4;
1325    let suggested_arm = if suggest_the_witnesses {
1326        let pattern = witnesses
1327            .iter()
1328            .map(|witness| cx.print_witness_pat(witness))
1329            .collect::<Vec<String>>()
1330            .join(" | ");
1331        if witnesses.iter().all(|p| p.is_never_pattern()) && cx.tcx.features().never_patterns() {
1332            // Arms with a never pattern don't take a body.
1333            pattern
1334        } else {
1335            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0} => todo!()", pattern))
    })format!("{pattern} => todo!()")
1336        }
1337    } else {
1338        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("_ => todo!()"))
    })format!("_ => todo!()")
1339    };
1340    let mut suggestion = None;
1341    let sm = cx.tcx.sess.source_map();
1342    match arms {
1343        [] if let Some(braces_span) = braces_span => {
1344            // Get the span for the empty match body `{}`.
1345            let (indentation, more) = if let Some(snippet) = sm.indentation_before(sp) {
1346                (::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("\n{0}", snippet))
    })format!("\n{snippet}"), "    ")
1347            } else {
1348                (" ".to_string(), "")
1349            };
1350            suggestion = Some((
1351                braces_span,
1352                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!(" {{{0}{1}{2},{0}}}", indentation,
                more, suggested_arm))
    })format!(" {{{indentation}{more}{suggested_arm},{indentation}}}",),
1353            ));
1354        }
1355        [only] => {
1356            let only = &thir[*only];
1357            let (pre_indentation, is_multiline) = if let Some(snippet) =
1358                sm.indentation_before(only.span)
1359                && let Ok(with_trailing) =
1360                    sm.span_extend_while(only.span, |c| c.is_whitespace() || c == ',')
1361                && sm.is_multiline(with_trailing)
1362            {
1363                (::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("\n{0}", snippet))
    })format!("\n{snippet}"), true)
1364            } else {
1365                (" ".to_string(), false)
1366            };
1367            let only_body = &thir[only.body];
1368            let comma = if #[allow(non_exhaustive_omitted_patterns)] match only_body.kind {
    ExprKind::Block { .. } => true,
    _ => false,
}matches!(only_body.kind, ExprKind::Block { .. })
1369                && only.span.eq_ctxt(only_body.span)
1370                && is_multiline
1371            {
1372                ""
1373            } else {
1374                ","
1375            };
1376            suggestion = Some((
1377                only.span.shrink_to_hi(),
1378                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}{1}{2}", comma, pre_indentation,
                suggested_arm))
    })format!("{comma}{pre_indentation}{suggested_arm}"),
1379            ));
1380        }
1381        [.., prev, last] => {
1382            let prev = &thir[*prev];
1383            let last = &thir[*last];
1384            if prev.span.eq_ctxt(last.span) {
1385                let last_body = &thir[last.body];
1386                let comma = if #[allow(non_exhaustive_omitted_patterns)] match last_body.kind {
    ExprKind::Block { .. } => true,
    _ => false,
}matches!(last_body.kind, ExprKind::Block { .. })
1387                    && last.span.eq_ctxt(last_body.span)
1388                {
1389                    ""
1390                } else {
1391                    ","
1392                };
1393                let spacing = if sm.is_multiline(prev.span.between(last.span)) {
1394                    sm.indentation_before(last.span).map(|indent| ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("\n{0}", indent))
    })format!("\n{indent}"))
1395                } else {
1396                    Some(" ".to_string())
1397                };
1398                if let Some(spacing) = spacing {
1399                    suggestion = Some((
1400                        last.span.shrink_to_hi(),
1401                        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}{1}{2}", comma, spacing,
                suggested_arm))
    })format!("{comma}{spacing}{suggested_arm}"),
1402                    ));
1403                }
1404            }
1405        }
1406        _ => {}
1407    }
1408
1409    let msg = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("ensure that all possible cases are being handled by adding a match arm with a wildcard pattern{0}{1}",
                if witnesses.len() > 1 && suggest_the_witnesses &&
                        suggestion.is_some() {
                    ", a match arm with multiple or-patterns"
                } else { "" },
                match witnesses.len() {
                    0 if suggestion.is_some() => " as shown",
                    0 => "",
                    1 if suggestion.is_some() =>
                        " or an explicit pattern as shown",
                    1 => " or an explicit pattern",
                    _ if suggestion.is_some() =>
                        " as shown, or multiple match arms",
                    _ => " or multiple match arms",
                }))
    })format!(
1410        "ensure that all possible cases are being handled by adding a match arm with a wildcard \
1411         pattern{}{}",
1412        if witnesses.len() > 1 && suggest_the_witnesses && suggestion.is_some() {
1413            ", a match arm with multiple or-patterns"
1414        } else {
1415            // we are either not suggesting anything, or suggesting `_`
1416            ""
1417        },
1418        match witnesses.len() {
1419            // non-exhaustive enum case
1420            0 if suggestion.is_some() => " as shown",
1421            0 => "",
1422            1 if suggestion.is_some() => " or an explicit pattern as shown",
1423            1 => " or an explicit pattern",
1424            _ if suggestion.is_some() => " as shown, or multiple match arms",
1425            _ => " or multiple match arms",
1426        },
1427    );
1428
1429    let all_arms_have_guards = arms.iter().all(|arm_id| thir[*arm_id].guard.is_some());
1430    if !is_empty_match && all_arms_have_guards {
1431        err.subdiagnostic(NonExhaustiveMatchAllArmsGuarded);
1432    }
1433    if let Some((span, sugg)) = suggestion {
1434        err.span_suggestion_verbose(span, msg, sugg, Applicability::HasPlaceholders);
1435    } else {
1436        err.help(msg);
1437    }
1438    err.emit()
1439}
1440
1441fn joined_uncovered_patterns<'p, 'tcx>(
1442    cx: &PatCtxt<'p, 'tcx>,
1443    witnesses: &[WitnessPat<'p, 'tcx>],
1444) -> String {
1445    const LIMIT: usize = 3;
1446    let pat_to_str = |pat: &WitnessPat<'p, 'tcx>| cx.print_witness_pat(pat);
1447    match witnesses {
1448        [] => ::rustc_middle::util::bug::bug_fmt(format_args!("impossible case reached"))bug!(),
1449        [witness] => ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}`",
                cx.print_witness_pat(witness)))
    })format!("`{}`", cx.print_witness_pat(witness)),
1450        [head @ .., tail] if head.len() < LIMIT => {
1451            let head: Vec<_> = head.iter().map(pat_to_str).collect();
1452            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}` and `{1}`",
                head.join("`, `"), cx.print_witness_pat(tail)))
    })format!("`{}` and `{}`", head.join("`, `"), cx.print_witness_pat(tail))
1453        }
1454        _ => {
1455            let (head, tail) = witnesses.split_at(LIMIT);
1456            let head: Vec<_> = head.iter().map(pat_to_str).collect();
1457            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}` and {1} more",
                head.join("`, `"), tail.len()))
    })format!("`{}` and {} more", head.join("`, `"), tail.len())
1458        }
1459    }
1460}
1461
1462/// Collect types that require specific explanations when they show up in witnesses.
1463fn collect_special_tys<'tcx>(
1464    cx: &PatCtxt<'_, 'tcx>,
1465    pat: &WitnessPat<'_, 'tcx>,
1466    special_tys: &mut FxIndexSet<RevealedTy<'tcx>>,
1467) {
1468    if #[allow(non_exhaustive_omitted_patterns)] match pat.ctor() {
    Constructor::NonExhaustive | Constructor::Never => true,
    _ => false,
}matches!(pat.ctor(), Constructor::NonExhaustive | Constructor::Never) {
1469        special_tys.insert(*pat.ty());
1470    }
1471    if let Constructor::IntRange(range) = pat.ctor() {
1472        if cx.is_range_beyond_boundaries(range, *pat.ty()) {
1473            // The range denotes the values before `isize::MIN` or the values after `usize::MAX`/`isize::MAX`.
1474            special_tys.insert(*pat.ty());
1475        }
1476    }
1477    pat.iter_fields().for_each(|field_pat| collect_special_tys(cx, field_pat, special_tys))
1478}
1479
1480fn report_adt_defined_here<'tcx>(
1481    tcx: TyCtxt<'tcx>,
1482    ty: Ty<'tcx>,
1483    witnesses: &[WitnessPat<'_, 'tcx>],
1484    point_at_non_local_ty: bool,
1485) -> Option<AdtDefinedHere<'tcx>> {
1486    let ty = ty.peel_refs();
1487    let ty::Adt(def, _) = ty.kind() else {
1488        return None;
1489    };
1490    let adt_def_span =
1491        tcx.hir_get_if_local(def.did()).and_then(|node| node.ident()).map(|ident| ident.span);
1492    let adt_def_span = if point_at_non_local_ty {
1493        adt_def_span.unwrap_or_else(|| tcx.def_span(def.did()))
1494    } else {
1495        adt_def_span?
1496    };
1497
1498    let mut variants = ::alloc::vec::Vec::new()vec![];
1499    for span in maybe_point_at_variant(tcx, *def, witnesses.iter().take(5)) {
1500        variants.push(Variant { span });
1501    }
1502    Some(AdtDefinedHere { adt_def_span, ty, variants })
1503}
1504
1505fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'p>(
1506    tcx: TyCtxt<'tcx>,
1507    def: AdtDef<'tcx>,
1508    patterns: impl Iterator<Item = &'a WitnessPat<'p, 'tcx>>,
1509) -> Vec<Span> {
1510    let mut covered = ::alloc::vec::Vec::new()vec![];
1511    for pattern in patterns {
1512        if let Constructor::Variant(variant_index) = pattern.ctor() {
1513            if let ty::Adt(this_def, _) = pattern.ty().kind()
1514                && this_def.did() != def.did()
1515            {
1516                continue;
1517            }
1518            let sp = def.variant(*variant_index).ident(tcx).span;
1519            if covered.contains(&sp) {
1520                // Don't point at variants that have already been covered due to other patterns to avoid
1521                // visual clutter.
1522                continue;
1523            }
1524            covered.push(sp);
1525        }
1526        covered.extend(maybe_point_at_variant(tcx, def, pattern.iter_fields()));
1527    }
1528    covered
1529}