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