rustc_mir_build/thir/pattern/
check_match.rs

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