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_errors::codes::*;
5use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan, msg, struct_span_code_err};
6use rustc_hir::def::*;
7use rustc_hir::def_id::{DefId, LocalDefId};
8use rustc_hir::{self as hir, BindingMode, ByRef, HirId, MatchSource};
9use rustc_infer::infer::TyCtxtInferExt;
10use rustc_lint_defs::builtin::{
11    BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS,
12};
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::diagnostics::Uncovered;
18use rustc_pattern_analysis::rustc::{
19    Constructor, DeconstructedPat, MatchArm, RedundancyExplanation, RevealedTy,
20    RustcPatCtxt as PatCtxt, Usefulness, UsefulnessReport, WitnessPat,
21};
22use rustc_span::edit_distance::find_best_match_for_name;
23use rustc_span::hygiene::DesugaringKind;
24use rustc_span::{Ident, Span, bug};
25use rustc_trait_selection::infer::InferCtxtExt;
26use tracing::instrument;
27
28use crate::diagnostics::*;
29
30pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> {
31    let typeck_results = tcx.typeck(def_id);
32    let (thir, expr) = tcx.thir_body(def_id)?;
33    let thir = thir.borrow();
34    let pattern_arena = TypedArena::default();
35    let dropless_arena = DroplessArena::default();
36    let mut visitor = MatchVisitor {
37        tcx,
38        thir: &*thir,
39        typeck_results,
40        typing_env: ty::TypingEnv::post_typeck_until_borrowck_for_mir_build(tcx, def_id),
41        hir_source: tcx.local_def_id_to_hir_id(def_id),
42        let_source: LetSource::None,
43        pattern_arena: &pattern_arena,
44        dropless_arena: &dropless_arena,
45        error: Ok(()),
46    };
47    visitor.visit_expr(&thir[expr]);
48
49    let origin = match tcx.def_kind(def_id) {
50        DefKind::AssocFn | DefKind::Fn => "function argument",
51        DefKind::Closure => "closure argument",
52        // other types of MIR don't have function parameters, and we don't need to
53        // categorize those for the irrefutable check.
54        _ if thir.params.is_empty() => "",
55        kind => ::rustc_span::macros::bug_impl(None,
    format_args!("unexpected function parameters in THIR: {0:?} {1:?}", kind,
        def_id), Location::caller())bug!("unexpected function parameters in THIR: {kind:?} {def_id:?}"),
56    };
57
58    for param in thir.params.iter() {
59        if let Some(ref pattern) = param.pat {
60            visitor.check_binding_is_irrefutable(pattern, origin, None, None, None);
61        }
62    }
63    visitor.error
64}
65
66#[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]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for RefutableFlag { }
#[automatically_derived]
impl ::core::clone::Clone for RefutableFlag {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for RefutableFlag { }
#[automatically_derived]
impl ::core::cmp::PartialEq for RefutableFlag {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
            ::core::intrinsics::discriminant_value(other)
    }
}PartialEq)]
67enum RefutableFlag {
68    Irrefutable,
69    Refutable,
70}
71use RefutableFlag::*;
72
73#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for LetSource { }
#[automatically_derived]
impl ::core::clone::Clone for LetSource {
    #[inline]
    fn clone(&self) -> Self { *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::marker::StructuralPartialEq for LetSource { }
#[automatically_derived]
impl ::core::cmp::PartialEq for LetSource {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
            ::core::intrinsics::discriminant_value(other)
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for LetSource { }Eq)]
74enum LetSource {
75    None,
76    PlainLet,
77    IfLet,
78    IfLetGuard,
79    LetElse,
80    WhileLet,
81    Else,
82    ElseIfLet,
83}
84
85struct MatchVisitor<'p, 'tcx> {
86    tcx: TyCtxt<'tcx>,
87    typing_env: ty::TypingEnv<'tcx>,
88    typeck_results: &'tcx ty::TypeckResults<'tcx>,
89    thir: &'p Thir<'tcx>,
90    hir_source: HirId,
91    let_source: LetSource,
92    pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
93    dropless_arena: &'p DroplessArena,
94    /// Tracks if we encountered an error while checking this body. That the first function to
95    /// report it stores it here. Some functions return `Result` to allow callers to short-circuit
96    /// on error, but callers don't need to store it here again.
97    error: Result<(), ErrorGuaranteed>,
98}
99
100// Visitor for a thir body. This calls `check_match` and `check_let` as appropriate.
101impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> {
102    fn thir(&self) -> &'p Thir<'tcx> {
103        self.thir
104    }
105
106    {}
#[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("/rustc-dev/1d81eb4ad9cd207e3e638bd32b17ec4fce8412a6/compiler/rustc_mir_build/src/thir/pattern/check_match.rs"),
                                    ::tracing_core::__macro_support::Option::Some(106u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_mir_build::thir::pattern::check_match"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("arm")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("arm");
                                                        NAME.as_str()
                                                    }], ::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};
                                meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&arm)
                                                            as &dyn ::tracing::field::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))]
107    fn visit_arm(&mut self, arm: &'p Arm<'tcx>) {
108        self.with_hir_source(arm.hir_id, |this| {
109            if let Some(expr) = arm.guard {
110                this.with_let_source(LetSource::IfLetGuard, |this| {
111                    this.visit_expr(&this.thir[expr])
112                });
113            }
114            this.visit_pat(&arm.pattern);
115            this.visit_expr(&self.thir[arm.body]);
116        });
117    }
118
119    {}
#[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("/rustc-dev/1d81eb4ad9cd207e3e638bd32b17ec4fce8412a6/compiler/rustc_mir_build/src/thir/pattern/check_match.rs"),
                                    ::tracing_core::__macro_support::Option::Some(119u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_mir_build::thir::pattern::check_match"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("ex")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("ex");
                                                        NAME.as_str()
                                                    }], ::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};
                                meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&ex)
                                                            as &dyn ::tracing::field::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, ref arms, match_source } => {
                    self.check_match(scrutinee, arms, match_source, ex.span);
                }
                ExprKind::LoopMatch {
                    match_data: LoopMatchMatchData {
                        scrutinee, ref arms, span
                        }, .. } => {
                    self.check_match(scrutinee, arms, MatchSource::Normal,
                        span);
                }
                ExprKind::Let { 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 let [Some((_, Irrefutable))] = chain_refutabilities[..] {
                        self.lint_single_let(ex.span, None, None);
                    }
                    return;
                }
                _ => {}
            };
            self.with_let_source(LetSource::None,
                |this| visit::walk_expr(this, ex));
        }
    }
}#[instrument(level = "trace", skip(self))]
120    fn visit_expr(&mut self, ex: &'p Expr<'tcx>) {
121        match ex.kind {
122            ExprKind::Scope { value, hir_id, .. } => {
123                self.with_hir_source(hir_id, |this| {
124                    this.visit_expr(&this.thir[value]);
125                });
126                return;
127            }
128            ExprKind::If { cond, then, else_opt, if_then_scope: _ } => {
129                // Give a specific `let_source` for the condition.
130                let let_source = match ex.span.desugaring_kind() {
131                    Some(DesugaringKind::WhileLoop) => LetSource::WhileLet,
132                    _ => match self.let_source {
133                        LetSource::Else => LetSource::ElseIfLet,
134                        _ => LetSource::IfLet,
135                    },
136                };
137                self.with_let_source(let_source, |this| this.visit_expr(&self.thir[cond]));
138                self.with_let_source(LetSource::None, |this| {
139                    this.visit_expr(&this.thir[then]);
140                });
141                if let Some(else_) = else_opt {
142                    self.with_let_source(LetSource::Else, |this| {
143                        this.visit_expr(&this.thir[else_])
144                    });
145                }
146                return;
147            }
148            ExprKind::Match { scrutinee, ref arms, match_source } => {
149                self.check_match(scrutinee, arms, match_source, ex.span);
150            }
151            ExprKind::LoopMatch {
152                match_data: LoopMatchMatchData { scrutinee, ref arms, span },
153                ..
154            } => {
155                self.check_match(scrutinee, arms, MatchSource::Normal, span);
156            }
157            ExprKind::Let { ref pat, expr } => {
158                self.check_let(pat, Some(expr), ex.span);
159            }
160            ExprKind::LogicalOp { op: LogicalOp::And, .. }
161                if !matches!(self.let_source, LetSource::None) =>
162            {
163                let mut chain_refutabilities = Vec::new();
164                let Ok(()) = self.visit_land(ex, &mut chain_refutabilities) else { return };
165                // Lint only single irrefutable let binding.
166                if let [Some((_, Irrefutable))] = chain_refutabilities[..] {
167                    self.lint_single_let(ex.span, None, None);
168                }
169                return;
170            }
171            _ => {}
172        };
173        self.with_let_source(LetSource::None, |this| visit::walk_expr(this, ex));
174    }
175
176    fn visit_stmt(&mut self, stmt: &'p Stmt<'tcx>) {
177        match stmt.kind {
178            StmtKind::Let { ref pattern, initializer, else_block, hir_id, span, .. } => {
179                self.with_hir_source(hir_id, |this| {
180                    let let_source =
181                        if else_block.is_some() { LetSource::LetElse } else { LetSource::PlainLet };
182                    this.with_let_source(let_source, |this| {
183                        this.check_let(pattern, initializer, span)
184                    });
185                    visit::walk_stmt(this, stmt);
186                });
187            }
188            StmtKind::Expr { .. } => {
189                visit::walk_stmt(self, stmt);
190            }
191        }
192    }
193}
194
195impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
196    {}
#[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("/rustc-dev/1d81eb4ad9cd207e3e638bd32b17ec4fce8412a6/compiler/rustc_mir_build/src/thir/pattern/check_match.rs"),
                                    ::tracing_core::__macro_support::Option::Some(196u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_mir_build::thir::pattern::check_match"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("let_source")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("let_source");
                                                        NAME.as_str()
                                                    }], ::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};
                                meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&let_source)
                                                            as &dyn ::tracing::field::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;
            f(self);
            self.let_source = old_let_source;
        }
    }
}#[instrument(level = "trace", skip(self, f))]
197    fn with_let_source(&mut self, let_source: LetSource, f: impl FnOnce(&mut Self)) {
198        let old_let_source = self.let_source;
199        self.let_source = let_source;
200        f(self);
201        self.let_source = old_let_source;
202    }
203
204    fn with_hir_source<T>(&mut self, new_hir_source: HirId, f: impl FnOnce(&mut Self) -> T) -> T {
205        let old_hir_source = self.hir_source;
206        self.hir_source = new_hir_source;
207        let ret = f(self);
208        self.hir_source = old_hir_source;
209        ret
210    }
211
212    /// Visit a nested chain of `&&`. Used for if-let chains. This must call `visit_expr` on the
213    /// subexpressions we are not handling ourselves.
214    fn visit_land(
215        &mut self,
216        ex: &'p Expr<'tcx>,
217        accumulator: &mut Vec<Option<(Span, RefutableFlag)>>,
218    ) -> Result<(), ErrorGuaranteed> {
219        match ex.kind {
220            ExprKind::Scope { value, hir_id, .. } => {
221                self.with_hir_source(hir_id, |this| this.visit_land(&this.thir[value], accumulator))
222            }
223            ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => {
224                // We recurse into the lhs only, because `&&` chains associate to the left.
225                let res_lhs = self.visit_land(&self.thir[lhs], accumulator);
226                let res_rhs = self.visit_land_rhs(&self.thir[rhs])?;
227                accumulator.push(res_rhs);
228                res_lhs
229            }
230            _ => {
231                let res = self.visit_land_rhs(ex)?;
232                accumulator.push(res);
233                Ok(())
234            }
235        }
236    }
237
238    /// Visit the right-hand-side of a `&&`. Used for if-let chains. Returns `Some` if the
239    /// expression was ultimately a `let ... = ...`, and `None` if it was a normal boolean
240    /// expression. This must call `visit_expr` on the subexpressions we are not handling ourselves.
241    fn visit_land_rhs(
242        &mut self,
243        ex: &'p Expr<'tcx>,
244    ) -> Result<Option<(Span, RefutableFlag)>, ErrorGuaranteed> {
245        match ex.kind {
246            ExprKind::Scope { value, hir_id, .. } => {
247                self.with_hir_source(hir_id, |this| this.visit_land_rhs(&this.thir[value]))
248            }
249            ExprKind::Let { ref pat, expr } => {
250                let expr = &self.thir()[expr];
251                self.with_let_source(LetSource::None, |this| {
252                    this.visit_expr(expr);
253                });
254                Ok(Some((ex.span, self.is_let_irrefutable(pat, Some(expr))?)))
255            }
256            _ => {
257                self.with_let_source(LetSource::None, |this| {
258                    this.visit_expr(ex);
259                });
260                Ok(None)
261            }
262        }
263    }
264
265    fn lower_pattern(
266        &mut self,
267        cx: &PatCtxt<'p, 'tcx>,
268        pat: &'p Pat<'tcx>,
269    ) -> Result<&'p DeconstructedPat<'p, 'tcx>, ErrorGuaranteed> {
270        if let Err(err) = pat.pat_error_reported() {
271            self.error = Err(err);
272            Err(err)
273        } else {
274            // Check the pattern for some things unrelated to exhaustiveness.
275            let refutable = if cx.refutable { Refutable } else { Irrefutable };
276            let mut err = Ok(());
277            pat.walk_always(|pat| {
278                check_borrow_conflicts_in_at_patterns(self, pat);
279                check_for_bindings_named_same_as_variants(self, pat, refutable);
280                err = err.and(check_never_pattern(cx, pat));
281            });
282            err?;
283            Ok(self.pattern_arena.alloc(cx.lower_pat(pat)))
284        }
285    }
286
287    /// Inspects the match scrutinee expression to determine whether the place it evaluates to may
288    /// hold invalid data.
289    fn is_known_valid_scrutinee(&self, scrutinee: &Expr<'tcx>) -> bool {
290        use ExprKind::*;
291        match &scrutinee.kind {
292            // Pointers can validly point to a place with invalid data. It is undecided whether
293            // references can too, so we conservatively assume they can.
294            Deref { .. } => false,
295            // Inherit validity of the parent place, unless the parent is an union.
296            Field { lhs, .. } => {
297                let lhs = &self.thir()[*lhs];
298                match lhs.ty.kind() {
299                    ty::Adt(def, _) if def.is_union() => false,
300                    _ => self.is_known_valid_scrutinee(lhs),
301                }
302            }
303            // Essentially a field access.
304            Index { lhs, .. } => {
305                let lhs = &self.thir()[*lhs];
306                self.is_known_valid_scrutinee(lhs)
307            }
308
309            // No-op.
310            Scope { value, .. } => self.is_known_valid_scrutinee(&self.thir()[*value]),
311
312            // Casts don't cause a load.
313            NeverToAny { source }
314            | Cast { source }
315            | ValueExpr { source }
316            | PointerCoercion { source, .. }
317            | PlaceTypeAscription { source, .. }
318            | ValueTypeAscription { source, .. }
319            | PlaceUnwrapUnsafeBinder { source }
320            | ValueUnwrapUnsafeBinder { source }
321            | WrapUnsafeBinder { source } => self.is_known_valid_scrutinee(&self.thir()[*source]),
322
323            // These diverge.
324            Become { .. }
325            | Break { .. }
326            | Continue { .. }
327            | ConstContinue { .. }
328            | Return { .. } => true,
329
330            // These are statements that evaluate to `()`.
331            Assign { .. } | AssignOp { .. } | InlineAsm { .. } | Let { .. } => true,
332
333            // These evaluate to a value.
334            RawBorrow { .. }
335            | Adt { .. }
336            | Array { .. }
337            | Binary { .. }
338            | Block { .. }
339            | Borrow { .. }
340            | Call { .. }
341            | ByUse { .. }
342            | Closure { .. }
343            | ConstBlock { .. }
344            | ConstParam { .. }
345            | If { .. }
346            | Literal { .. }
347            | LogicalOp { .. }
348            | Loop { .. }
349            | LoopMatch { .. }
350            | Match { .. }
351            | NamedConst { .. }
352            | NonHirLiteral { .. }
353            | Repeat { .. }
354            | StaticRef { .. }
355            | ThreadLocalRef { .. }
356            | Tuple { .. }
357            | Unary { .. }
358            | UpvarRef { .. }
359            | VarRef { .. }
360            | ZstLiteral { .. }
361            | Yield { .. }
362            | Reborrow { .. } => true,
363        }
364    }
365
366    fn new_cx(
367        &self,
368        refutability: RefutableFlag,
369        whole_match_span: Option<Span>,
370        scrutinee: Option<&Expr<'tcx>>,
371        scrut_span: Span,
372    ) -> PatCtxt<'p, 'tcx> {
373        let refutable = match refutability {
374            Irrefutable => false,
375            Refutable => true,
376        };
377        // If we don't have a scrutinee we're either a function parameter or a `let x;`. Both cases
378        // require validity.
379        let known_valid_scrutinee =
380            scrutinee.map(|scrut| self.is_known_valid_scrutinee(scrut)).unwrap_or(true);
381        PatCtxt {
382            tcx: self.tcx,
383            typeck_results: self.typeck_results,
384            typing_env: self.typing_env,
385            module: self.tcx.parent_module(self.hir_source),
386            dropless_arena: self.dropless_arena,
387            match_lint_level: self.hir_source,
388            whole_match_span,
389            scrut_span,
390            refutable,
391            known_valid_scrutinee,
392            internal_state: Default::default(),
393        }
394    }
395
396    fn analyze_patterns(
397        &mut self,
398        cx: &PatCtxt<'p, 'tcx>,
399        arms: &[MatchArm<'p, 'tcx>],
400        scrut_ty: Ty<'tcx>,
401    ) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
402        let report =
403            rustc_pattern_analysis::rustc::analyze_match(&cx, &arms, scrut_ty).map_err(|err| {
404                self.error = Err(err);
405                err
406            })?;
407
408        // Warn unreachable subpatterns.
409        for (arm, is_useful) in report.arm_usefulness.iter() {
410            if let Usefulness::Useful(redundant_subpats) = is_useful
411                && !redundant_subpats.is_empty()
412            {
413                let mut redundant_subpats = redundant_subpats.clone();
414                // Emit lints in the order in which they occur in the file.
415                redundant_subpats.sort_unstable_by_key(|(pat, _)| pat.data().span.lo_hi());
416                for (pat, explanation) in redundant_subpats {
417                    report_unreachable_pattern(cx, arm.arm_data, pat, &explanation, None)
418                }
419            }
420        }
421        Ok(report)
422    }
423
424    {}
#[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("/rustc-dev/1d81eb4ad9cd207e3e638bd32b17ec4fce8412a6/compiler/rustc_mir_build/src/thir/pattern/check_match.rs"),
                                    ::tracing_core::__macro_support::Option::Some(424u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_mir_build::thir::pattern::check_match"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("pat")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("pat");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("scrutinee")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("scrutinee");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("span")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("span");
                                                        NAME.as_str()
                                                    }], ::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};
                                meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&pat)
                                                            as &dyn ::tracing::field::Value)),
                                                (::tracing::__macro_support::Option::Some(&::tracing::field::debug(&scrutinee)
                                                            as &dyn ::tracing::field::Value)),
                                                (::tracing::__macro_support::Option::Some(&::tracing::field::debug(&span)
                                                            as &dyn ::tracing::field::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 {
                if let hir::Node::LetStmt(&hir::LetStmt {
                        source: hir::LocalSource::AssignDesugar, .. }) =
                        self.tcx.hir_node(self.hir_source) {
                    self.check_binding_is_irrefutable(pat, "assignment",
                        Some(Inform { descr: "destructuring assignments" }), scrut,
                        None);
                } else {
                    self.check_binding_is_irrefutable(pat, "local binding",
                        Some(Inform { descr: "`let` bindings" }), scrut,
                        Some(span));
                }
            } else if let Ok(Irrefutable) =
                    self.is_let_irrefutable(pat, scrut) {
                if span.from_expansion() {
                    self.lint_single_let(span, None, None);
                    return;
                }
                let let_else_span =
                    self.check_irrefutable_option_some(pat, scrut, span);
                let sm = self.tcx.sess.source_map();
                let next_token_start =
                    sm.span_extend_while_whitespace(span.clone()).hi();
                let line_span =
                    sm.span_extend_to_line(span.clone()).with_lo(next_token_start);
                let else_keyword_span = sm.span_until_whitespace(line_span);
                self.lint_single_let(span, Some(else_keyword_span),
                    let_else_span);
            }
        }
    }
}#[instrument(level = "trace", skip(self))]
425    fn check_let(&mut self, pat: &'p Pat<'tcx>, scrutinee: Option<ExprId>, span: Span) {
426        assert!(self.let_source != LetSource::None);
427        let scrut = scrutinee.map(|id| &self.thir[id]);
428        if let LetSource::PlainLet = self.let_source {
429            // `lhs = rhs` destructuring assignments are lowered to a `let` tagged
430            // `AssignDesugar`; report them as assignments, not `let` bindings (#157553).
431            if let hir::Node::LetStmt(&hir::LetStmt {
432                source: hir::LocalSource::AssignDesugar,
433                ..
434            }) = self.tcx.hir_node(self.hir_source)
435            {
436                self.check_binding_is_irrefutable(
437                    pat,
438                    "assignment",
439                    Some(Inform { descr: "destructuring assignments" }),
440                    scrut,
441                    None,
442                );
443            } else {
444                self.check_binding_is_irrefutable(
445                    pat,
446                    "local binding",
447                    Some(Inform { descr: "`let` bindings" }),
448                    scrut,
449                    Some(span),
450                );
451            }
452        } else if let Ok(Irrefutable) = self.is_let_irrefutable(pat, scrut) {
453            if span.from_expansion() {
454                self.lint_single_let(span, None, None);
455                return;
456            }
457            let let_else_span = self.check_irrefutable_option_some(pat, scrut, span);
458
459            let sm = self.tcx.sess.source_map();
460            let next_token_start = sm.span_extend_while_whitespace(span.clone()).hi();
461            let line_span = sm.span_extend_to_line(span.clone()).with_lo(next_token_start);
462            let else_keyword_span = sm.span_until_whitespace(line_span);
463            self.lint_single_let(span, Some(else_keyword_span), let_else_span);
464        }
465    }
466
467    /// Check case `let x = Some(y);`, user likely intended to destructure `Option`
468    fn check_irrefutable_option_some(
469        &self,
470        pat: &'p Pat<'tcx>,
471        initializer: Option<&Expr<'tcx>>,
472        span: Span,
473    ) -> Option<LetElseReplacementSuggestion> {
474        if let sm = self.tcx.sess.source_map()
475            && let Some(initializer) = initializer
476            && let Some(s_ty) = initializer.ty.ty_adt_def()
477            && self.tcx.is_diagnostic_item(rustc_span::sym::Option, s_ty.did())
478            && let ExprKind::Scope { value, .. } = initializer.kind
479            && let initializer_expr = &self.thir[value]
480            && let ExprKind::Adt(AdtExpr { fields, .. }) = &initializer_expr.kind
481            && let Some(field) = fields.first()
482            && let inner = &self.thir[field.expr]
483            && let Some(inner_ty) = inner.ty.ty_adt_def()
484            && self.tcx.is_diagnostic_item(rustc_span::sym::Option, inner_ty.did())
485            && let Ok(rhs) = sm.span_to_snippet(inner.span)
486            && let Ok(lhs) = sm.span_to_snippet(pat.span)
487        {
488            let lhs = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("Some({0})", lhs))
    })format!("Some({})", lhs);
489            Some(LetElseReplacementSuggestion { span, lhs, rhs })
490        } else {
491            None
492        }
493    }
494
495    fn check_match(
496        &mut self,
497        scrut: ExprId,
498        arms: &[ArmId],
499        source: hir::MatchSource,
500        expr_span: Span,
501    ) {
502        let scrut = &self.thir[scrut];
503        let cx = self.new_cx(Refutable, Some(expr_span), Some(scrut), scrut.span);
504
505        let mut tarms = Vec::with_capacity(arms.len());
506        for &arm in arms {
507            let arm = &self.thir.arms[arm];
508            let got_error = self.with_hir_source(arm.hir_id, |this| {
509                let Ok(pat) = this.lower_pattern(&cx, &arm.pattern) else { return true };
510                let arm =
511                    MatchArm { pat, arm_data: this.hir_source, has_guard: arm.guard.is_some() };
512                tarms.push(arm);
513                false
514            });
515            if got_error {
516                return;
517            }
518        }
519
520        let Ok(report) = self.analyze_patterns(&cx, &tarms, scrut.ty) else { return };
521
522        match source {
523            // Don't report arm reachability of desugared `match $iter.into_iter() { iter => .. }`
524            // when the iterator is an uninhabited type. unreachable_code will trigger instead.
525            hir::MatchSource::ForLoopDesugar if arms.len() == 1 => {}
526            hir::MatchSource::ForLoopDesugar
527            | hir::MatchSource::Postfix
528            | hir::MatchSource::Normal
529            | hir::MatchSource::FormatArgs => {
530                let is_match_arm =
531                    #[allow(non_exhaustive_omitted_patterns)] match source {
    hir::MatchSource::Postfix | hir::MatchSource::Normal => true,
    _ => false,
}matches!(source, hir::MatchSource::Postfix | hir::MatchSource::Normal);
532                report_arm_reachability(&cx, &report, is_match_arm);
533            }
534            // Unreachable patterns in try and await expressions occur when one of
535            // the arms are an uninhabited type. Which is OK.
536            hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar(_) => {}
537        }
538
539        // Check if the match is exhaustive.
540        let witnesses = report.non_exhaustiveness_witnesses;
541        if !witnesses.is_empty() {
542            if source == hir::MatchSource::ForLoopDesugar
543                && let [_, snd_arm] = *arms
544            {
545                // the for loop pattern is not irrefutable
546                let pat = &self.thir[snd_arm].pattern;
547                // `pat` should be `Some(<pat_field>)` from a desugared for loop.
548                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));
549                let PatKind::Variant { ref subpatterns, .. } = pat.kind else { ::rustc_span::macros::bug_impl(None, format_args!("impossible case reached"),
    Location::caller())bug!() };
550                let [pat_field] = &subpatterns[..] else { ::rustc_span::macros::bug_impl(None, format_args!("impossible case reached"),
    Location::caller())bug!() };
551                self.check_binding_is_irrefutable(
552                    &pat_field.pattern,
553                    "`for` loop binding",
554                    None,
555                    None,
556                    None,
557                );
558            } else {
559                // span after scrutinee, or after `.match`. That is, the braces, arms,
560                // and any whitespace preceding the braces.
561                let braces_span = match source {
562                    hir::MatchSource::Normal => scrut
563                        .span
564                        .find_ancestor_in_same_ctxt(expr_span)
565                        .map(|scrut_span| scrut_span.shrink_to_hi().with_hi(expr_span.hi())),
566                    hir::MatchSource::Postfix => {
567                        // This is horrendous, and we should deal with it by just
568                        // stashing the span of the braces somewhere (like in the match source).
569                        scrut.span.find_ancestor_in_same_ctxt(expr_span).and_then(|scrut_span| {
570                            let sm = self.tcx.sess.source_map();
571                            let brace_span = sm.span_extend_to_next_char(scrut_span, '{', true);
572                            if sm.span_to_snippet(sm.next_point(brace_span)).as_deref() == Ok("{") {
573                                let sp = brace_span.shrink_to_hi().with_hi(expr_span.hi());
574                                // We also need to extend backwards for whitespace
575                                sm.span_extend_prev_while(sp, |c| c.is_whitespace()).ok()
576                            } else {
577                                None
578                            }
579                        })
580                    }
581                    hir::MatchSource::ForLoopDesugar
582                    | hir::MatchSource::TryDesugar(_)
583                    | hir::MatchSource::AwaitDesugar
584                    | hir::MatchSource::FormatArgs => None,
585                };
586
587                // Check if the match would be exhaustive if all guards were removed.
588                // If so, we leave a note that guards don't count towards exhaustivity.
589                let would_be_exhaustive_without_guards = {
590                    let any_arm_has_guard = tarms.iter().any(|arm| arm.has_guard);
591                    any_arm_has_guard && {
592                        let guardless_arms: Vec<_> =
593                            tarms.iter().map(|arm| MatchArm { has_guard: false, ..*arm }).collect();
594                        rustc_pattern_analysis::rustc::analyze_match(&cx, &guardless_arms, scrut.ty)
595                            .is_ok_and(|report| report.non_exhaustiveness_witnesses.is_empty())
596                    }
597                };
598                self.error = Err(report_non_exhaustive_match(
599                    &cx,
600                    self.thir,
601                    scrut.ty,
602                    scrut.span,
603                    witnesses,
604                    arms,
605                    braces_span,
606                    would_be_exhaustive_without_guards,
607                ));
608            }
609        }
610    }
611
612    {}
#[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("lint_single_let",
                                    "rustc_mir_build::thir::pattern::check_match",
                                    ::tracing::Level::TRACE,
                                    ::tracing_core::__macro_support::Option::Some("/rustc-dev/1d81eb4ad9cd207e3e638bd32b17ec4fce8412a6/compiler/rustc_mir_build/src/thir/pattern/check_match.rs"),
                                    ::tracing_core::__macro_support::Option::Some(612u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_mir_build::thir::pattern::check_match"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("let_span")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("let_span");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("else_keyword_span")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("else_keyword_span");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("let_else_span")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("let_else_span");
                                                        NAME.as_str()
                                                    }], ::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};
                                meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&let_span)
                                                            as &dyn ::tracing::field::Value)),
                                                (::tracing::__macro_support::Option::Some(&::tracing::field::debug(&else_keyword_span)
                                                            as &dyn ::tracing::field::Value)),
                                                (::tracing::__macro_support::Option::Some(&::tracing::field::debug(&let_else_span)
                                                            as &dyn ::tracing::field::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;
        }
        {
            report_irrefutable_let_patterns(self.tcx, self.hir_source,
                self.let_source, 1, let_span, else_keyword_span,
                let_else_span);
        }
    }
}#[instrument(level = "trace", skip(self))]
613    fn lint_single_let(
614        &mut self,
615        let_span: Span,
616        else_keyword_span: Option<Span>,
617        let_else_span: Option<LetElseReplacementSuggestion>,
618    ) {
619        report_irrefutable_let_patterns(
620            self.tcx,
621            self.hir_source,
622            self.let_source,
623            1,
624            let_span,
625            else_keyword_span,
626            let_else_span,
627        );
628    }
629
630    fn analyze_binding(
631        &mut self,
632        pat: &'p Pat<'tcx>,
633        refutability: RefutableFlag,
634        scrut: Option<&Expr<'tcx>>,
635    ) -> Result<(PatCtxt<'p, 'tcx>, UsefulnessReport<'p, 'tcx>), ErrorGuaranteed> {
636        let cx = self.new_cx(refutability, None, scrut, pat.span);
637        let pat = self.lower_pattern(&cx, pat)?;
638        let arms = [MatchArm { pat, arm_data: self.hir_source, has_guard: false }];
639        let report = self.analyze_patterns(&cx, &arms, pat.ty().inner())?;
640        Ok((cx, report))
641    }
642
643    fn is_let_irrefutable(
644        &mut self,
645        pat: &'p Pat<'tcx>,
646        scrut: Option<&Expr<'tcx>>,
647    ) -> Result<RefutableFlag, ErrorGuaranteed> {
648        let (cx, report) = self.analyze_binding(pat, Refutable, scrut)?;
649        // Report if the pattern is unreachable, which can only occur when the type is uninhabited.
650        report_arm_reachability(&cx, &report, false);
651        // If the list of witnesses is empty, the match is exhaustive, i.e. the `if let` pattern is
652        // irrefutable.
653        Ok(if report.non_exhaustiveness_witnesses.is_empty() { Irrefutable } else { Refutable })
654    }
655
656    {}
#[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("/rustc-dev/1d81eb4ad9cd207e3e638bd32b17ec4fce8412a6/compiler/rustc_mir_build/src/thir/pattern/check_match.rs"),
                                    ::tracing_core::__macro_support::Option::Some(656u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_mir_build::thir::pattern::check_match"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("pat")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("pat");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("origin")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("origin");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("inform")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("inform");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("scrut")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("scrut");
                                                        NAME.as_str()
                                                    },
                                                    {
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("sp")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("sp");
                                                        NAME.as_str()
                                                    }], ::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};
                                meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&pat)
                                                            as &dyn ::tracing::field::Value)),
                                                (::tracing::__macro_support::Option::Some(&origin as
                                                            &dyn ::tracing::field::Value)),
                                                (::tracing::__macro_support::Option::Some(&::tracing::field::debug(&inform)
                                                            as &dyn ::tracing::field::Value)),
                                                (::tracing::__macro_support::Option::Some(&::tracing::field::debug(&scrut)
                                                            as &dyn ::tracing::field::Value)),
                                                (::tracing::__macro_support::Option::Some(&::tracing::field::debug(&sp)
                                                            as &dyn ::tracing::field::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 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 name = self.tcx.item_name(def_id);
                interpreted_as_const =
                    Some(InterpretedAsConst {
                            span,
                            variable: name.to_ident_string(),
                        });
                interpreted_as_const_sugg =
                    Some(InterpretedAsConstSugg {
                            span: pat.span,
                            variable: name.to_string(),
                        });
            } 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).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))]
657    fn check_binding_is_irrefutable(
658        &mut self,
659        pat: &'p Pat<'tcx>,
660        origin: &str,
661        inform: Option<Inform>,
662        scrut: Option<&Expr<'tcx>>,
663        sp: Option<Span>,
664    ) {
665        let pattern_ty = pat.ty;
666
667        let Ok((cx, report)) = self.analyze_binding(pat, Irrefutable, scrut) else { return };
668        let witnesses = report.non_exhaustiveness_witnesses;
669        if witnesses.is_empty() {
670            // The pattern is irrefutable.
671            return;
672        }
673
674        let mut let_suggestion = None;
675        let mut misc_suggestion = None;
676        let mut interpreted_as_const = None;
677        let mut interpreted_as_const_sugg = None;
678
679        if let Some(def_id) = is_const_pat_that_looks_like_binding(self.tcx, pat) {
680            let span = self.tcx.def_span(def_id);
681            let name = self.tcx.item_name(def_id);
682            // When we encounter a constant as the binding name, point at the `const` definition.
683            interpreted_as_const =
684                Some(InterpretedAsConst { span, variable: name.to_ident_string() });
685            // The suggested name is suffixed, so it is never a keyword and never needs `r#`.
686            interpreted_as_const_sugg =
687                Some(InterpretedAsConstSugg { span: pat.span, variable: name.to_string() });
688        } else if let PatKind::Constant { .. } = pat.kind
689            && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(pat.span)
690        {
691            // If the pattern to match is an integer literal:
692            if snippet.chars().all(|c| c.is_digit(10)) {
693                // Then give a suggestion, the user might've meant to create a binding instead.
694                misc_suggestion = Some(MiscPatternSuggestion::AttemptedIntegerLiteral {
695                    start_span: pat.span.shrink_to_lo(),
696                });
697            }
698        }
699
700        if let Some(span) = sp
701            && self.tcx.sess.source_map().is_span_accessible(span)
702            && interpreted_as_const.is_none()
703            && scrut.is_some()
704        {
705            let mut bindings = vec![];
706            pat.each_binding(|name, _, _, _| bindings.push(name));
707
708            let semi_span = span.shrink_to_hi();
709            let start_span = span.shrink_to_lo();
710            let end_span = semi_span.shrink_to_lo();
711            let count = witnesses.len();
712
713            let_suggestion = Some(if bindings.is_empty() {
714                SuggestLet::If { start_span, semi_span, count }
715            } else {
716                SuggestLet::Else { end_span, count }
717            });
718        };
719
720        let adt_defined_here = report_adt_defined_here(self.tcx, pattern_ty, &witnesses, false);
721
722        // Emit an extra note if the first uncovered witness would be uninhabited
723        // if we disregard visibility.
724        let witness_1_is_privately_uninhabited = if let Some(witness_1) = witnesses.get(0)
725            && let ty::Adt(adt, args) = witness_1.ty().kind()
726            && adt.is_enum()
727            && let Constructor::Variant(variant_index) = witness_1.ctor()
728        {
729            let variant_inhabited = adt
730                .variant(*variant_index)
731                .inhabited_predicate(self.tcx)
732                .instantiate(self.tcx, args);
733            variant_inhabited.apply(self.tcx, cx.typing_env, cx.module)
734                && !variant_inhabited.apply_ignore_module(self.tcx, cx.typing_env)
735        } else {
736            false
737        };
738
739        let witness_1 = cx.print_witness_pat(witnesses.get(0).unwrap());
740
741        self.error = Err(self.tcx.dcx().emit_err(PatternNotCovered {
742            span: pat.span,
743            origin,
744            uncovered: Uncovered::new(pat.span, &cx, witnesses),
745            inform,
746            interpreted_as_const,
747            interpreted_as_const_sugg,
748            witness_1_is_privately_uninhabited,
749            witness_1,
750            _p: (),
751            pattern_ty,
752            let_suggestion,
753            misc_suggestion,
754            adt_defined_here,
755        }));
756    }
757}
758
759/// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.
760/// Check that there are no borrow or move conflicts in `binding @ subpat` patterns.
761///
762/// For example, this would reject:
763/// - `ref x @ Some(ref mut y)`,
764/// - `ref mut x @ Some(ref y)`,
765/// - `ref mut x @ Some(ref mut y)`,
766/// - `ref mut? x @ Some(y)`, and
767/// - `x @ Some(ref mut? y)`.
768///
769/// This analysis is *not* subsumed by NLL.
770fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: &Pat<'tcx>) {
771    // Extract `sub` in `binding @ sub`.
772    let PatKind::Binding { name, mode, ty, subpattern: Some(ref sub), .. } = pat.kind else {
773        return;
774    };
775
776    let is_binding_by_move = |ty: Ty<'tcx>| !cx.tcx.type_is_copy_modulo_regions(cx.typing_env, ty);
777
778    let sess = cx.tcx.sess;
779
780    // Get the binding move, extract the mutability if by-ref.
781    let mut_outer = match mode.0 {
782        ByRef::No if is_binding_by_move(ty) => {
783            // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`.
784            let mut conflicts_ref = Vec::new();
785            sub.each_binding(|_, mode, _, span| {
786                if #[allow(non_exhaustive_omitted_patterns)] match mode {
    ByRef::Yes(..) => true,
    _ => false,
}matches!(mode, ByRef::Yes(..)) {
787                    conflicts_ref.push(span)
788                }
789            });
790            if !conflicts_ref.is_empty() {
791                sess.dcx().emit_err(BorrowOfMovedValue {
792                    binding_span: pat.span,
793                    conflicts_ref,
794                    name: Ident::new(name, pat.span),
795                    ty,
796                    suggest_borrowing: Some(pat.span.shrink_to_lo()),
797                });
798            }
799            return;
800        }
801        ByRef::No => return,
802        ByRef::Yes(_, m) => m,
803    };
804
805    // We now have `ref $mut_outer binding @ sub` (semantically).
806    // Recurse into each binding in `sub` and find mutability or move conflicts.
807    let mut conflicts_move = Vec::new();
808    let mut conflicts_mut_mut = Vec::new();
809    let mut conflicts_mut_ref = Vec::new();
810    sub.each_binding(|name, mode, ty, span| {
811        match mode {
812            ByRef::Yes(_, mut_inner) => match (mut_outer, mut_inner) {
813                // Both sides are `ref`.
814                (Mutability::Not, Mutability::Not) => {}
815                // 2x `ref mut`.
816                (Mutability::Mut, Mutability::Mut) => {
817                    conflicts_mut_mut.push(Conflict::Mut { span, name })
818                }
819                (Mutability::Not, Mutability::Mut) => {
820                    conflicts_mut_ref.push(Conflict::Mut { span, name })
821                }
822                (Mutability::Mut, Mutability::Not) => {
823                    conflicts_mut_ref.push(Conflict::Ref { span, name })
824                }
825            },
826            ByRef::No if is_binding_by_move(ty) => {
827                conflicts_move.push(Conflict::Moved { span, name }) // `ref mut?` + by-move conflict.
828            }
829            ByRef::No => {} // `ref mut?` + by-copy is fine.
830        }
831    });
832
833    let report_mut_mut = !conflicts_mut_mut.is_empty();
834    let report_mut_ref = !conflicts_mut_ref.is_empty();
835    let report_move_conflict = !conflicts_move.is_empty();
836
837    let mut occurrences = match mut_outer {
838        Mutability::Mut => ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [Conflict::Mut { span: pat.span, name }]))vec![Conflict::Mut { span: pat.span, name }],
839        Mutability::Not => ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [Conflict::Ref { span: pat.span, name }]))vec![Conflict::Ref { span: pat.span, name }],
840    };
841    occurrences.extend(conflicts_mut_mut);
842    occurrences.extend(conflicts_mut_ref);
843    occurrences.extend(conflicts_move);
844
845    // Report errors if any.
846    if report_mut_mut {
847        // Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
848        sess.dcx().emit_err(MultipleMutBorrows { span: pat.span, occurrences });
849    } else if report_mut_ref {
850        // Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
851        match mut_outer {
852            Mutability::Mut => {
853                sess.dcx().emit_err(AlreadyMutBorrowed { span: pat.span, occurrences });
854            }
855            Mutability::Not => {
856                sess.dcx().emit_err(AlreadyBorrowed { span: pat.span, occurrences });
857            }
858        };
859    } else if report_move_conflict {
860        // Report by-ref and by-move conflicts, e.g. `ref x @ y`.
861        sess.dcx().emit_err(MovedWhileBorrowed { span: pat.span, occurrences });
862    }
863}
864
865fn check_for_bindings_named_same_as_variants(
866    cx: &MatchVisitor<'_, '_>,
867    pat: &Pat<'_>,
868    rf: RefutableFlag,
869) {
870    if let PatKind::Binding {
871        name,
872        mode: BindingMode(ByRef::No, Mutability::Not),
873        subpattern: None,
874        ty,
875        ..
876    } = pat.kind
877        && let ty::Adt(edef, _) = ty.peel_refs().kind()
878        && edef.is_enum()
879        && edef
880            .variants()
881            .iter()
882            .any(|variant| variant.name == name && variant.ctor_kind() == Some(CtorKind::Const))
883    {
884        let variant_count = edef.variants().len();
885        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()));
886        cx.tcx.emit_node_span_lint(
887            BINDINGS_WITH_VARIANT_NAME,
888            cx.hir_source,
889            pat.span,
890            BindingsWithVariantName {
891                // If this is an irrefutable pattern, and there's > 1 variant,
892                // then we can't actually match on this. Applying the below
893                // suggestion would produce code that breaks on `check_binding_is_irrefutable`.
894                suggestion: if rf == Refutable || variant_count == 1 {
895                    Some(pat.span)
896                } else {
897                    None
898                },
899                ty_path,
900                name: Ident::new(name, pat.span),
901            },
902        )
903    }
904}
905
906/// Check that never patterns are only used on inhabited types.
907fn check_never_pattern<'tcx>(
908    cx: &PatCtxt<'_, 'tcx>,
909    pat: &Pat<'tcx>,
910) -> Result<(), ErrorGuaranteed> {
911    if let PatKind::Never = pat.kind {
912        if !cx.is_uninhabited(pat.ty) {
913            return Err(cx.tcx.dcx().emit_err(NonEmptyNeverPattern { span: pat.span, ty: pat.ty }));
914        }
915    }
916    Ok(())
917}
918
919fn report_irrefutable_let_patterns(
920    tcx: TyCtxt<'_>,
921    id: HirId,
922    source: LetSource,
923    count: usize,
924    span: Span,
925    else_keyword_span: Option<Span>,
926    let_else_span: Option<LetElseReplacementSuggestion>,
927) {
928    macro_rules! emit_diag {
929        ($lint:tt) => {{
930            tcx.emit_node_span_lint(IRREFUTABLE_LET_PATTERNS, id, span, $lint { count });
931        }};
932    }
933
934    match source {
935        LetSource::None | LetSource::PlainLet | LetSource::Else => ::rustc_span::macros::bug_impl(None, format_args!("impossible case reached"),
    Location::caller())bug!(),
936        LetSource::IfLet | LetSource::ElseIfLet => {
    tcx.emit_node_span_lint(IRREFUTABLE_LET_PATTERNS, id, span,
        IrrefutableLetPatternsIfLet { count });
}emit_diag!(IrrefutableLetPatternsIfLet),
937        LetSource::IfLetGuard => {
    tcx.emit_node_span_lint(IRREFUTABLE_LET_PATTERNS, id, span,
        IrrefutableLetPatternsIfLetGuard { count });
}emit_diag!(IrrefutableLetPatternsIfLetGuard),
938        LetSource::LetElse => {
939            let spans = match else_keyword_span {
940                Some(else_keyword_span) => {
941                    let mut spans = MultiSpan::from_span(else_keyword_span);
942                    spans.push_span_label(
943                        span,
944                        rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("assigning to binding pattern will always succeed"))msg!("assigning to binding pattern will always succeed"),
945                    );
946                    spans
947                }
948                None => span.into(),
949            };
950
951            tcx.emit_node_span_lint(
952                IRREFUTABLE_LET_PATTERNS,
953                id,
954                spans,
955                IrrefutableLetPatternsLetElse { be_replaced: let_else_span },
956            );
957        }
958        LetSource::WhileLet => {
    tcx.emit_node_span_lint(IRREFUTABLE_LET_PATTERNS, id, span,
        IrrefutableLetPatternsWhileLet { count });
}emit_diag!(IrrefutableLetPatternsWhileLet),
959    }
960}
961
962/// Report unreachable arms, if any.
963fn report_unreachable_pattern<'p, 'tcx>(
964    cx: &PatCtxt<'p, 'tcx>,
965    hir_id: HirId,
966    pat: &DeconstructedPat<'p, 'tcx>,
967    explanation: &RedundancyExplanation<'p, 'tcx>,
968    whole_arm_span: Option<Span>,
969) {
970    static CAP_COVERED_BY_MANY: usize = 4;
971    let pat_span = pat.data().span;
972    let mut lint = UnreachablePatternInner {
973        span: Some(pat_span),
974        matches_no_values: None,
975        matches_no_values_ty: **pat.ty(),
976        uninhabited_note: None,
977        covered_by_catchall: None,
978        covered_by_one: None,
979        covered_by_many: None,
980        wanted_constant: None,
981        accessible_constant: None,
982        inaccessible_constant: None,
983        pattern_let_binding: None,
984        suggest_remove: None,
985    };
986    let mut covered_by_many_n_more_count = None;
987    match explanation.covered_by.as_slice() {
988        [] => {
989            // Empty pattern; we report the uninhabited type that caused the emptiness.
990            lint.span = None; // Don't label the pattern itself
991            lint.uninhabited_note = Some(()); // Give a link about empty types
992            lint.matches_no_values = Some(pat_span);
993            lint.suggest_remove = whole_arm_span; // Suggest to remove the match arm
994            pat.walk(&mut |subpat| {
995                let ty = **subpat.ty();
996                if cx.is_uninhabited(ty) {
997                    lint.matches_no_values_ty = ty;
998                    false // No need to dig further.
999                } else if #[allow(non_exhaustive_omitted_patterns)] match subpat.ctor() {
    Constructor::Ref | Constructor::UnionField => true,
    _ => false,
}matches!(subpat.ctor(), Constructor::Ref | Constructor::UnionField) {
1000                    false // Don't explore further since they are not by-value.
1001                } else {
1002                    true
1003                }
1004            });
1005        }
1006        [covering_pat] if pat_is_catchall(covering_pat) => {
1007            // A binding pattern that matches all, a single binding name.
1008            let pat = covering_pat.data();
1009            lint.covered_by_catchall = Some(pat.span);
1010            find_fallback_pattern_typo(cx, hir_id, pat, &mut lint);
1011        }
1012        [covering_pat] => {
1013            lint.covered_by_one = Some(covering_pat.data().span);
1014        }
1015        covering_pats => {
1016            let mut iter = covering_pats.iter();
1017            let mut multispan = MultiSpan::from_span(pat_span);
1018            for p in iter.by_ref().take(CAP_COVERED_BY_MANY) {
1019                multispan.push_span_label(p.data().span, rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("matches some of the same values"))msg!("matches some of the same values"));
1020            }
1021            let remain = iter.count();
1022            if remain == 0 {
1023                multispan.push_span_label(pat_span, rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("collectively making this unreachable"))msg!("collectively making this unreachable"));
1024            } else {
1025                covered_by_many_n_more_count = Some(remain);
1026                multispan.push_span_label(
1027                    pat_span,
1028                    rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("...and {$covered_by_many_n_more_count} other patterns collectively make this unreachable"))msg!("...and {$covered_by_many_n_more_count} other patterns collectively make this unreachable"),
1029                );
1030            }
1031            lint.covered_by_many = Some(multispan);
1032        }
1033    }
1034    cx.tcx.emit_node_span_lint(
1035        UNREACHABLE_PATTERNS,
1036        hir_id,
1037        pat_span,
1038        UnreachablePattern { inner: lint, covered_by_many_n_more_count },
1039    );
1040}
1041
1042/// Detect typos that were meant to be a `const` but were interpreted as a new pattern binding.
1043fn find_fallback_pattern_typo<'tcx>(
1044    cx: &PatCtxt<'_, 'tcx>,
1045    hir_id: HirId,
1046    pat: &Pat<'tcx>,
1047    lint: &mut UnreachablePatternInner<'_>,
1048) {
1049    if cx.tcx.lint_level_spec_at_node(UNREACHABLE_PATTERNS, hir_id).is_allow() {
1050        // This is because we use `with_no_trimmed_paths` later, so if we never emit the lint we'd
1051        // ICE. At the same time, we don't really need to do all of this if we won't emit anything.
1052        return;
1053    }
1054    if let PatKind::Binding { name, subpattern: None, ty, .. } = pat.kind {
1055        // See if the binding might have been a `const` that was mistyped or out of scope.
1056        let mut accessible = ::alloc::vec::Vec::new()vec![];
1057        let mut accessible_path = ::alloc::vec::Vec::new()vec![];
1058        let mut inaccessible = ::alloc::vec::Vec::new()vec![];
1059        let mut imported = ::alloc::vec::Vec::new()vec![];
1060        let mut imported_spans = ::alloc::vec::Vec::new()vec![];
1061        let (infcx, param_env) = cx.tcx.infer_ctxt().build_with_typing_env(cx.typing_env);
1062        let parent = cx.tcx.hir_get_parent_item(hir_id);
1063
1064        for item in cx.tcx.hir_crate_items(()).free_items() {
1065            if let DefKind::Use = cx.tcx.def_kind(item.owner_id) {
1066                // Look for consts being re-exported.
1067                let item = cx.tcx.hir_expect_item(item.owner_id.def_id);
1068                let hir::ItemKind::Use(hir::UseTree { prefix: path, .. }) = item.kind else {
1069                    continue;
1070                };
1071                if let Some(value_ns) = path.res.value_ns
1072                    && let Res::Def(DefKind::Const, id) = value_ns
1073                    && infcx.can_eq(
1074                        param_env,
1075                        ty,
1076                        cx.tcx.type_of(id).instantiate_identity().skip_norm_wip(),
1077                    )
1078                {
1079                    if cx.tcx.visibility(id).is_accessible_from(parent, cx.tcx) {
1080                        // The original const is accessible, suggest using it directly.
1081                        let item_name = cx.tcx.item_name(id);
1082                        accessible.push(item_name);
1083                        accessible_path.push({ let _guard = NoTrimmedGuard::new(); cx.tcx.def_path_str(id) }with_no_trimmed_paths!(cx.tcx.def_path_str(id)));
1084                    } else if cx.tcx.visibility(item.owner_id).is_accessible_from(parent, cx.tcx) {
1085                        // The const is accessible only through the re-export, point at
1086                        // the `use`.
1087                        let ident = item.kind.ident().unwrap();
1088                        imported.push(ident.name);
1089                        imported_spans.push(ident.span);
1090                    }
1091                }
1092            }
1093            if let DefKind::Const = cx.tcx.def_kind(item.owner_id)
1094                && infcx.can_eq(
1095                    param_env,
1096                    ty,
1097                    cx.tcx.type_of(item.owner_id).instantiate_identity().skip_norm_wip(),
1098                )
1099            {
1100                // Look for local consts.
1101                let item_name = cx.tcx.item_name(item.owner_id);
1102                let vis = cx.tcx.visibility(item.owner_id);
1103                if vis.is_accessible_from(parent, cx.tcx) {
1104                    accessible.push(item_name);
1105                    // FIXME: the line below from PR #135310 is a workaround for the ICE in issue
1106                    // #135289, where a macro in a dependency can create unreachable patterns in the
1107                    // current crate. Path trimming expects diagnostics for a typoed const, but no
1108                    // diagnostics are emitted and we ICE. See
1109                    // `tests/ui/resolve/const-with-typo-in-pattern-binding-ice-135289.rs` for a
1110                    // test that reproduces the ICE if we don't use `with_no_trimmed_paths!`.
1111                    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));
1112                    accessible_path.push(path);
1113                } else if name == item_name {
1114                    // The const exists somewhere in this crate, but it can't be imported
1115                    // from this pattern's scope. We'll just point at its definition.
1116                    inaccessible.push(cx.tcx.def_span(item.owner_id));
1117                }
1118            }
1119        }
1120        if let Some((i, &const_name)) =
1121            accessible.iter().enumerate().find(|&(_, &const_name)| const_name == name)
1122        {
1123            // The pattern name is an exact match, so the pattern needed to be imported.
1124            lint.wanted_constant = Some(WantedConstant {
1125                span: pat.span,
1126                is_typo: false,
1127                const_name: const_name.to_string(),
1128                const_path: accessible_path[i].clone(),
1129            });
1130        } else if let Some(name) = find_best_match_for_name(&accessible, name, None) {
1131            // The pattern name is likely a typo.
1132            lint.wanted_constant = Some(WantedConstant {
1133                span: pat.span,
1134                is_typo: true,
1135                const_name: name.to_string(),
1136                const_path: name.to_string(),
1137            });
1138        } else if let Some(i) =
1139            imported.iter().enumerate().find(|&(_, &const_name)| const_name == name).map(|(i, _)| i)
1140        {
1141            // The const with the exact name wasn't re-exported from an import in this
1142            // crate, we point at the import.
1143            lint.accessible_constant = Some(imported_spans[i]);
1144        } else if let Some(name) = find_best_match_for_name(&imported, name, None) {
1145            // The typoed const wasn't re-exported by an import in this crate, we suggest
1146            // the right name (which will likely require another follow up suggestion).
1147            lint.wanted_constant = Some(WantedConstant {
1148                span: pat.span,
1149                is_typo: true,
1150                const_path: name.to_string(),
1151                const_name: name.to_string(),
1152            });
1153        } else if !inaccessible.is_empty() {
1154            for span in inaccessible {
1155                // The const with the exact name match isn't accessible, we just point at it.
1156                lint.inaccessible_constant = Some(span);
1157            }
1158        } else {
1159            // Look for local bindings for people that might have gotten confused with how
1160            // `let` and `const` works.
1161            for (_, node) in cx.tcx.hir_parent_iter(hir_id) {
1162                match node {
1163                    hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Let(let_stmt), .. }) => {
1164                        if let hir::PatKind::Binding(_, _, binding_name, _) = let_stmt.pat.kind {
1165                            if name == binding_name.name {
1166                                lint.pattern_let_binding = Some(binding_name.span);
1167                            }
1168                        }
1169                    }
1170                    hir::Node::Block(hir::Block { stmts, .. }) => {
1171                        for stmt in *stmts {
1172                            if let hir::StmtKind::Let(let_stmt) = stmt.kind
1173                                && let hir::PatKind::Binding(_, _, binding_name, _) =
1174                                    let_stmt.pat.kind
1175                                && name == binding_name.name
1176                            {
1177                                lint.pattern_let_binding = Some(binding_name.span);
1178                            }
1179                        }
1180                    }
1181                    hir::Node::Item(_) => break,
1182                    _ => {}
1183                }
1184            }
1185        }
1186    }
1187}
1188
1189/// Report unreachable arms, if any.
1190fn report_arm_reachability<'p, 'tcx>(
1191    cx: &PatCtxt<'p, 'tcx>,
1192    report: &UsefulnessReport<'p, 'tcx>,
1193    is_match_arm: bool,
1194) {
1195    let sm = cx.tcx.sess.source_map();
1196    for (arm, is_useful) in report.arm_usefulness.iter() {
1197        if let Usefulness::Redundant(explanation) = is_useful {
1198            let hir_id = arm.arm_data;
1199            let arm_span = cx.tcx.hir_span(hir_id);
1200            let whole_arm_span = if is_match_arm {
1201                // If the arm is followed by a comma, extend the span to include it.
1202                if let Some(comma) = sm.span_followed_by(arm_span, ",") {
1203                    Some(arm_span.to(comma))
1204                } else {
1205                    Some(arm_span)
1206                }
1207            } else {
1208                None
1209            };
1210            report_unreachable_pattern(cx, hir_id, arm.pat, explanation, whole_arm_span)
1211        }
1212    }
1213}
1214
1215/// Checks for common cases of "catchall" patterns that may not be intended as such.
1216fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
1217    match pat.ctor() {
1218        Constructor::Wildcard => true,
1219        Constructor::Struct | Constructor::Ref => {
1220            pat.iter_fields().all(|ipat| pat_is_catchall(&ipat.pat))
1221        }
1222        _ => false,
1223    }
1224}
1225
1226/// If the given pattern is a named constant that looks like it could have been
1227/// intended to be a binding, returns the `DefId` of the named constant.
1228///
1229/// Diagnostics use this to give more detailed suggestions for non-exhaustive
1230/// matches.
1231fn is_const_pat_that_looks_like_binding<'tcx>(tcx: TyCtxt<'tcx>, pat: &Pat<'tcx>) -> Option<DefId> {
1232    // The pattern must be a named constant, and the name that appears in
1233    // the pattern's source text must resemble a plain identifier without any
1234    // `::` namespace separators or other non-identifier characters.
1235    if let ty::AliasConstKind::Free { def_id } = pat.extra.as_deref()?.expanded_const?
1236        && let Ok(snippet) = tcx.sess.source_map().span_to_snippet(pat.span)
1237        && snippet
1238            .strip_prefix("r#")
1239            .unwrap_or(&snippet)
1240            .chars()
1241            .all(|c| c.is_alphanumeric() || c == '_')
1242    {
1243        Some(def_id)
1244    } else {
1245        None
1246    }
1247}
1248
1249/// Report that a match is not exhaustive.
1250fn report_non_exhaustive_match<'p, 'tcx>(
1251    cx: &PatCtxt<'p, 'tcx>,
1252    thir: &Thir<'tcx>,
1253    scrut_ty: Ty<'tcx>,
1254    sp: Span,
1255    witnesses: Vec<WitnessPat<'p, 'tcx>>,
1256    arms: &[ArmId],
1257    braces_span: Option<Span>,
1258    would_be_exhaustive_without_guards: bool,
1259) -> ErrorGuaranteed {
1260    let is_empty_match = arms.is_empty();
1261    let non_empty_enum = match scrut_ty.kind() {
1262        ty::Adt(def, _) => def.is_enum() && !def.variants().is_empty(),
1263        _ => false,
1264    };
1265    // In the case of an empty match, replace the '`_` not covered' diagnostic with something more
1266    // informative.
1267    if is_empty_match && !non_empty_enum {
1268        return cx.tcx.dcx().emit_err(NonExhaustivePatternsTypeNotEmpty {
1269            cx,
1270            scrut_span: sp,
1271            braces_span,
1272            ty: scrut_ty,
1273        });
1274    }
1275
1276    // FIXME: migration of this diagnostic will require list support
1277    let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
1278    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!(
1279        cx.tcx.dcx(),
1280        sp,
1281        E0004,
1282        "non-exhaustive patterns: {joined_patterns} not covered"
1283    );
1284    err.span_label(
1285        sp,
1286        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("pattern{0} {1} not covered",
                if witnesses.len() == 1 { "" } else { "s" }, joined_patterns))
    })format!(
1287            "pattern{} {} not covered",
1288            rustc_errors::pluralize!(witnesses.len()),
1289            joined_patterns
1290        ),
1291    );
1292
1293    // Point at the definition of non-covered `enum` variants.
1294    if let Some(AdtDefinedHere { adt_def_span, ty, variants }) =
1295        report_adt_defined_here(cx.tcx, scrut_ty, &witnesses, true)
1296    {
1297        let mut multi_span = MultiSpan::from_span(adt_def_span);
1298        multi_span.push_span_context(adt_def_span);
1299        for Variant { span } in variants {
1300            multi_span.push_span_label(span, "not covered");
1301        }
1302        err.span_note(multi_span, ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}` defined here", ty))
    })format!("`{ty}` defined here"));
1303    }
1304    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));
1305
1306    if !is_empty_match {
1307        let mut special_tys = FxIndexSet::default();
1308        // Look at the first witness.
1309        collect_special_tys(cx, &witnesses[0], &mut special_tys);
1310
1311        for ty in special_tys {
1312            if ty.is_ptr_sized_integral() {
1313                if ty.inner() == cx.tcx.types.usize {
1314                    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!(
1315                        "`{ty}::MAX` is not treated as exhaustive, \
1316                        so half-open ranges are necessary to match exhaustively",
1317                    ));
1318                } else if ty.inner() == cx.tcx.types.isize {
1319                    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!(
1320                        "`{ty}::MIN` and `{ty}::MAX` are not treated as exhaustive, \
1321                        so half-open ranges are necessary to match exhaustively",
1322                    ));
1323                }
1324            } else if ty.inner() == cx.tcx.types.str_ {
1325                err.note("`&str` cannot be matched exhaustively, so a wildcard `_` is necessary");
1326            } else if cx.is_foreign_non_exhaustive_enum(ty) {
1327                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"));
1328            } else if cx.is_uninhabited(ty.inner()) {
1329                // The type is uninhabited yet there is a witness: we must be in the `MaybeInvalid`
1330                // case.
1331                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"));
1332            }
1333        }
1334    }
1335
1336    if let ty::Ref(_, sub_ty, _) = scrut_ty.kind() {
1337        if !sub_ty.is_inhabited_from(cx.tcx, cx.module, cx.typing_env) {
1338            err.note("references are always considered inhabited");
1339        }
1340    }
1341
1342    for &arm in arms {
1343        let arm = &thir.arms[arm];
1344        if let Some(def_id) = is_const_pat_that_looks_like_binding(cx.tcx, &arm.pattern) {
1345            let const_name = cx.tcx.item_name(def_id);
1346            err.span_label(
1347                arm.pattern.span,
1348                ::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!(
1349                    "this pattern doesn't introduce a new catch-all binding, but rather pattern \
1350                     matches against the value of constant `{const_name}`",
1351                ),
1352            );
1353            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"));
1354            err.span_suggestion_verbose(
1355                arm.pattern.span.shrink_to_hi(),
1356                "if you meant to introduce a binding, use a different name",
1357                "_var".to_string(),
1358                Applicability::MaybeIncorrect,
1359            );
1360        }
1361    }
1362
1363    // Whether we suggest the actual missing patterns or `_`.
1364    let suggest_the_witnesses = witnesses.len() < 4;
1365    let suggested_arm = if suggest_the_witnesses {
1366        let pattern = witnesses
1367            .iter()
1368            .map(|witness| cx.print_witness_pat(witness))
1369            .collect::<Vec<String>>()
1370            .join(" | ");
1371        if witnesses.iter().all(|p| p.is_never_pattern()) && cx.tcx.features().never_patterns() {
1372            // Arms with a never pattern don't take a body.
1373            pattern
1374        } else {
1375            // ignore-tidy-todo
1376            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0} => todo!()", pattern))
    })format!("{pattern} => todo!()")
1377        }
1378    } else {
1379        // ignore-tidy-todo
1380        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("_ => todo!()"))
    })format!("_ => todo!()")
1381    };
1382    let mut suggestion = None;
1383    let sm = cx.tcx.sess.source_map();
1384    match arms {
1385        [] if let Some(braces_span) = braces_span => {
1386            // Get the span for the empty match body `{}`.
1387            let (indentation, more) = if let Some(snippet) = sm.indentation_before(sp) {
1388                (::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("\n{0}", snippet))
    })format!("\n{snippet}"), "    ")
1389            } else {
1390                (" ".to_string(), "")
1391            };
1392            suggestion = Some((
1393                braces_span,
1394                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!(" {{{0}{1}{2},{0}}}", indentation,
                more, suggested_arm))
    })format!(" {{{indentation}{more}{suggested_arm},{indentation}}}",),
1395            ));
1396        }
1397        [only] => {
1398            let only = &thir[*only];
1399            let (pre_indentation, is_multiline) = if let Some(snippet) =
1400                sm.indentation_before(only.span)
1401                && let Ok(with_trailing) =
1402                    sm.span_extend_while(only.span, |c| c.is_whitespace() || c == ',')
1403                && sm.is_multiline(with_trailing)
1404            {
1405                (::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("\n{0}", snippet))
    })format!("\n{snippet}"), true)
1406            } else {
1407                (" ".to_string(), false)
1408            };
1409            let only_body = &thir[only.body];
1410            let comma = if #[allow(non_exhaustive_omitted_patterns)] match only_body.kind {
    ExprKind::Block { .. } => true,
    _ => false,
}matches!(only_body.kind, ExprKind::Block { .. })
1411                && only.span.eq_ctxt(only_body.span)
1412                && is_multiline
1413            {
1414                ""
1415            } else {
1416                ","
1417            };
1418            suggestion = Some((
1419                only.span.shrink_to_hi(),
1420                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}{1}{2}", comma, pre_indentation,
                suggested_arm))
    })format!("{comma}{pre_indentation}{suggested_arm}"),
1421            ));
1422        }
1423        [.., prev, last] => {
1424            let prev = &thir[*prev];
1425            let last = &thir[*last];
1426            if prev.span.eq_ctxt(last.span) {
1427                let last_body = &thir[last.body];
1428                let comma = if #[allow(non_exhaustive_omitted_patterns)] match last_body.kind {
    ExprKind::Block { .. } => true,
    _ => false,
}matches!(last_body.kind, ExprKind::Block { .. })
1429                    && last.span.eq_ctxt(last_body.span)
1430                {
1431                    ""
1432                } else {
1433                    ","
1434                };
1435                let spacing = if sm.is_multiline(prev.span.between(last.span)) {
1436                    sm.indentation_before(last.span).map(|indent| ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("\n{0}", indent))
    })format!("\n{indent}"))
1437                } else {
1438                    Some(" ".to_string())
1439                };
1440                if let Some(spacing) = spacing {
1441                    suggestion = Some((
1442                        last.span.shrink_to_hi(),
1443                        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}{1}{2}", comma, spacing,
                suggested_arm))
    })format!("{comma}{spacing}{suggested_arm}"),
1444                    ));
1445                }
1446            }
1447        }
1448        _ => {}
1449    }
1450
1451    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!(
1452        "ensure that all possible cases are being handled by adding a match arm with a wildcard \
1453         pattern{}{}",
1454        if witnesses.len() > 1 && suggest_the_witnesses && suggestion.is_some() {
1455            ", a match arm with multiple or-patterns"
1456        } else {
1457            // we are either not suggesting anything, or suggesting `_`
1458            ""
1459        },
1460        match witnesses.len() {
1461            // non-exhaustive enum case
1462            0 if suggestion.is_some() => " as shown",
1463            0 => "",
1464            1 if suggestion.is_some() => " or an explicit pattern as shown",
1465            1 => " or an explicit pattern",
1466            _ if suggestion.is_some() => " as shown, or multiple match arms",
1467            _ => " or multiple match arms",
1468        },
1469    );
1470
1471    if would_be_exhaustive_without_guards {
1472        err.subdiagnostic(NonExhaustiveMatchAllArmsGuarded);
1473    }
1474    if let Some((span, sugg)) = suggestion {
1475        err.span_suggestion_verbose(span, msg, sugg, Applicability::HasPlaceholders);
1476    } else {
1477        err.help(msg);
1478    }
1479    err.emit_err()
1480}
1481
1482fn joined_uncovered_patterns<'p, 'tcx>(
1483    cx: &PatCtxt<'p, 'tcx>,
1484    witnesses: &[WitnessPat<'p, 'tcx>],
1485) -> String {
1486    const LIMIT: usize = 3;
1487    let pat_to_str = |pat: &WitnessPat<'p, 'tcx>| cx.print_witness_pat(pat);
1488    match witnesses {
1489        [] => ::rustc_span::macros::bug_impl(None, format_args!("impossible case reached"),
    Location::caller())bug!(),
1490        [witness] => ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}`",
                cx.print_witness_pat(witness)))
    })format!("`{}`", cx.print_witness_pat(witness)),
1491        [head @ .., tail] if head.len() < LIMIT => {
1492            let head: Vec<_> = head.iter().map(pat_to_str).collect();
1493            ::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))
1494        }
1495        _ => {
1496            let (head, tail) = witnesses.split_at(LIMIT);
1497            let head: Vec<_> = head.iter().map(pat_to_str).collect();
1498            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}` and {1} more",
                head.join("`, `"), tail.len()))
    })format!("`{}` and {} more", head.join("`, `"), tail.len())
1499        }
1500    }
1501}
1502
1503/// Collect types that require specific explanations when they show up in witnesses.
1504fn collect_special_tys<'tcx>(
1505    cx: &PatCtxt<'_, 'tcx>,
1506    pat: &WitnessPat<'_, 'tcx>,
1507    special_tys: &mut FxIndexSet<RevealedTy<'tcx>>,
1508) {
1509    if #[allow(non_exhaustive_omitted_patterns)] match pat.ctor() {
    Constructor::NonExhaustive | Constructor::Never => true,
    _ => false,
}matches!(pat.ctor(), Constructor::NonExhaustive | Constructor::Never) {
1510        special_tys.insert(*pat.ty());
1511    }
1512    if let Constructor::IntRange(range) = pat.ctor() {
1513        if cx.is_range_beyond_boundaries(range, *pat.ty()) {
1514            // The range denotes the values before `isize::MIN` or the values after `usize::MAX`/`isize::MAX`.
1515            special_tys.insert(*pat.ty());
1516        }
1517    }
1518    pat.iter_fields().for_each(|field_pat| collect_special_tys(cx, field_pat, special_tys))
1519}
1520
1521fn report_adt_defined_here<'tcx>(
1522    tcx: TyCtxt<'tcx>,
1523    ty: Ty<'tcx>,
1524    witnesses: &[WitnessPat<'_, 'tcx>],
1525    point_at_non_local_ty: bool,
1526) -> Option<AdtDefinedHere<'tcx>> {
1527    let ty = ty.peel_refs();
1528    let ty::Adt(def, _) = ty.kind() else {
1529        return None;
1530    };
1531    let adt_def_span =
1532        tcx.hir_get_if_local(def.did()).and_then(|node| node.ident()).map(|ident| ident.span);
1533    let adt_def_span = if point_at_non_local_ty {
1534        adt_def_span.unwrap_or_else(|| tcx.def_span(def.did()))
1535    } else {
1536        adt_def_span?
1537    };
1538
1539    let mut variants = ::alloc::vec::Vec::new()vec![];
1540    for span in maybe_point_at_variant(tcx, *def, witnesses.iter().take(5)) {
1541        variants.push(Variant { span });
1542    }
1543    Some(AdtDefinedHere { adt_def_span, ty, variants })
1544}
1545
1546fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'p>(
1547    tcx: TyCtxt<'tcx>,
1548    def: AdtDef<'tcx>,
1549    patterns: impl Iterator<Item = &'a WitnessPat<'p, 'tcx>>,
1550) -> Vec<Span> {
1551    let mut covered = ::alloc::vec::Vec::new()vec![];
1552    for pattern in patterns {
1553        if let Constructor::Variant(variant_index) = pattern.ctor() {
1554            if let ty::Adt(this_def, _) = pattern.ty().kind()
1555                && this_def.did() != def.did()
1556            {
1557                continue;
1558            }
1559            let sp = def.variant(*variant_index).ident(tcx).span;
1560            if covered.contains(&sp) {
1561                // Don't point at variants that have already been covered due to other patterns to avoid
1562                // visual clutter.
1563                continue;
1564            }
1565            covered.push(sp);
1566        }
1567        covered.extend(maybe_point_at_variant(tcx, def, pattern.iter_fields()));
1568    }
1569    covered
1570}