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::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 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 _ 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 error: Result<(), ErrorGuaranteed>,
103}
104
105impl<'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 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::Let { box ref pat, expr } => {
158 self.check_let(pat, Some(expr), ex.span);
159 }
160 ExprKind::LogicalOp { op: LogicalOp::And, .. }
161 if !matches!(self.let_source, LetSource::None) =>
162 {
163 let mut chain_refutabilities = Vec::new();
164 let Ok(()) = self.visit_land(ex, &mut chain_refutabilities) else { return };
165 if chain_refutabilities.iter().any(|x| x.is_some()) {
167 self.check_let_chain(chain_refutabilities, ex.span);
168 }
169 return;
170 }
171 _ => {}
172 };
173 self.with_let_source(LetSource::None, |this| visit::walk_expr(this, ex));
174 }
175
176 fn visit_stmt(&mut self, stmt: &'p Stmt<'tcx>) {
177 match stmt.kind {
178 StmtKind::Let {
179 box ref pattern, initializer, else_block, lint_level, span, ..
180 } => {
181 self.with_lint_level(lint_level, |this| {
182 let let_source =
183 if else_block.is_some() { LetSource::LetElse } else { LetSource::PlainLet };
184 this.with_let_source(let_source, |this| {
185 this.check_let(pattern, initializer, span)
186 });
187 visit::walk_stmt(this, stmt);
188 });
189 }
190 StmtKind::Expr { .. } => {
191 visit::walk_stmt(self, stmt);
192 }
193 }
194 }
195}
196
197impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
198 #[instrument(level = "trace", skip(self, f))]
199 fn with_let_source(&mut self, let_source: LetSource, f: impl FnOnce(&mut Self)) {
200 let old_let_source = self.let_source;
201 self.let_source = let_source;
202 ensure_sufficient_stack(|| f(self));
203 self.let_source = old_let_source;
204 }
205
206 fn with_lint_level<T>(
207 &mut self,
208 new_lint_level: LintLevel,
209 f: impl FnOnce(&mut Self) -> T,
210 ) -> T {
211 if let LintLevel::Explicit(hir_id) = new_lint_level {
212 let old_lint_level = self.lint_level;
213 self.lint_level = hir_id;
214 let ret = f(self);
215 self.lint_level = old_lint_level;
216 ret
217 } else {
218 f(self)
219 }
220 }
221
222 fn visit_land(
225 &mut self,
226 ex: &'p Expr<'tcx>,
227 accumulator: &mut Vec<Option<(Span, RefutableFlag)>>,
228 ) -> Result<(), ErrorGuaranteed> {
229 match ex.kind {
230 ExprKind::Scope { value, lint_level, .. } => self.with_lint_level(lint_level, |this| {
231 this.visit_land(&this.thir[value], accumulator)
232 }),
233 ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => {
234 let res_lhs = self.visit_land(&self.thir[lhs], accumulator);
236 let res_rhs = self.visit_land_rhs(&self.thir[rhs])?;
237 accumulator.push(res_rhs);
238 res_lhs
239 }
240 _ => {
241 let res = self.visit_land_rhs(ex)?;
242 accumulator.push(res);
243 Ok(())
244 }
245 }
246 }
247
248 fn visit_land_rhs(
252 &mut self,
253 ex: &'p Expr<'tcx>,
254 ) -> Result<Option<(Span, RefutableFlag)>, ErrorGuaranteed> {
255 match ex.kind {
256 ExprKind::Scope { value, lint_level, .. } => {
257 self.with_lint_level(lint_level, |this| this.visit_land_rhs(&this.thir[value]))
258 }
259 ExprKind::Let { box ref pat, expr } => {
260 let expr = &self.thir()[expr];
261 self.with_let_source(LetSource::None, |this| {
262 this.visit_expr(expr);
263 });
264 Ok(Some((ex.span, self.is_let_irrefutable(pat, Some(expr))?)))
265 }
266 _ => {
267 self.with_let_source(LetSource::None, |this| {
268 this.visit_expr(ex);
269 });
270 Ok(None)
271 }
272 }
273 }
274
275 fn lower_pattern(
276 &mut self,
277 cx: &PatCtxt<'p, 'tcx>,
278 pat: &'p Pat<'tcx>,
279 ) -> Result<&'p DeconstructedPat<'p, 'tcx>, ErrorGuaranteed> {
280 if let Err(err) = pat.pat_error_reported() {
281 self.error = Err(err);
282 Err(err)
283 } else {
284 let refutable = if cx.refutable { Refutable } else { Irrefutable };
286 let mut err = Ok(());
287 pat.walk_always(|pat| {
288 check_borrow_conflicts_in_at_patterns(self, pat);
289 check_for_bindings_named_same_as_variants(self, pat, refutable);
290 err = err.and(check_never_pattern(cx, pat));
291 });
292 err?;
293 Ok(self.pattern_arena.alloc(cx.lower_pat(pat)))
294 }
295 }
296
297 fn is_known_valid_scrutinee(&self, scrutinee: &Expr<'tcx>) -> bool {
300 use ExprKind::*;
301 match &scrutinee.kind {
302 Deref { .. } => false,
305 Field { lhs, .. } => {
307 let lhs = &self.thir()[*lhs];
308 match lhs.ty.kind() {
309 ty::Adt(def, _) if def.is_union() => false,
310 _ => self.is_known_valid_scrutinee(lhs),
311 }
312 }
313 Index { lhs, .. } => {
315 let lhs = &self.thir()[*lhs];
316 self.is_known_valid_scrutinee(lhs)
317 }
318
319 Scope { value, .. } => self.is_known_valid_scrutinee(&self.thir()[*value]),
321
322 NeverToAny { source }
324 | Cast { source }
325 | Use { source }
326 | PointerCoercion { source, .. }
327 | PlaceTypeAscription { source, .. }
328 | ValueTypeAscription { source, .. }
329 | PlaceUnwrapUnsafeBinder { source }
330 | ValueUnwrapUnsafeBinder { source }
331 | WrapUnsafeBinder { source } => self.is_known_valid_scrutinee(&self.thir()[*source]),
332
333 Become { .. } | Break { .. } | Continue { .. } | Return { .. } => true,
335
336 Assign { .. } | AssignOp { .. } | InlineAsm { .. } | Let { .. } => true,
338
339 RawBorrow { .. }
341 | Adt { .. }
342 | Array { .. }
343 | Binary { .. }
344 | Block { .. }
345 | Borrow { .. }
346 | Box { .. }
347 | Call { .. }
348 | ByUse { .. }
349 | Closure { .. }
350 | ConstBlock { .. }
351 | ConstParam { .. }
352 | If { .. }
353 | Literal { .. }
354 | LogicalOp { .. }
355 | Loop { .. }
356 | 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 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 report =
408 rustc_pattern_analysis::rustc::analyze_match(&cx, &arms, scrut_ty).map_err(|err| {
409 self.error = Err(err);
410 err
411 })?;
412
413 for (arm, is_useful) in report.arm_usefulness.iter() {
415 if let Usefulness::Useful(redundant_subpats) = is_useful
416 && !redundant_subpats.is_empty()
417 {
418 let mut redundant_subpats = redundant_subpats.clone();
419 redundant_subpats.sort_unstable_by_key(|(pat, _)| pat.data().span);
421 for (pat, explanation) in redundant_subpats {
422 report_unreachable_pattern(cx, arm.arm_data, pat, &explanation, None)
423 }
424 }
425 }
426 Ok(report)
427 }
428
429 #[instrument(level = "trace", skip(self))]
430 fn check_let(&mut self, pat: &'p Pat<'tcx>, scrutinee: Option<ExprId>, span: Span) {
431 assert!(self.let_source != LetSource::None);
432 let scrut = scrutinee.map(|id| &self.thir[id]);
433 if let LetSource::PlainLet = self.let_source {
434 self.check_binding_is_irrefutable(pat, "local binding", scrut, Some(span))
435 } else {
436 let Ok(refutability) = self.is_let_irrefutable(pat, scrut) else { return };
437 if matches!(refutability, Irrefutable) {
438 report_irrefutable_let_patterns(
439 self.tcx,
440 self.lint_level,
441 self.let_source,
442 1,
443 span,
444 );
445 }
446 }
447 }
448
449 fn check_match(
450 &mut self,
451 scrut: ExprId,
452 arms: &[ArmId],
453 source: hir::MatchSource,
454 expr_span: Span,
455 ) {
456 let scrut = &self.thir[scrut];
457 let cx = self.new_cx(Refutable, Some(expr_span), Some(scrut), scrut.span);
458
459 let mut tarms = Vec::with_capacity(arms.len());
460 for &arm in arms {
461 let arm = &self.thir.arms[arm];
462 let got_error = self.with_lint_level(arm.lint_level, |this| {
463 let Ok(pat) = this.lower_pattern(&cx, &arm.pattern) else { return true };
464 let arm =
465 MatchArm { pat, arm_data: this.lint_level, has_guard: arm.guard.is_some() };
466 tarms.push(arm);
467 false
468 });
469 if got_error {
470 return;
471 }
472 }
473
474 let Ok(report) = self.analyze_patterns(&cx, &tarms, scrut.ty) else { return };
475
476 match source {
477 hir::MatchSource::ForLoopDesugar if arms.len() == 1 => {}
480 hir::MatchSource::ForLoopDesugar
481 | hir::MatchSource::Postfix
482 | hir::MatchSource::Normal
483 | hir::MatchSource::FormatArgs => {
484 let is_match_arm =
485 matches!(source, hir::MatchSource::Postfix | hir::MatchSource::Normal);
486 report_arm_reachability(&cx, &report, is_match_arm);
487 }
488 hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar(_) => {}
491 }
492
493 let witnesses = report.non_exhaustiveness_witnesses;
495 if !witnesses.is_empty() {
496 if source == hir::MatchSource::ForLoopDesugar
497 && let [_, snd_arm] = *arms
498 {
499 let pat = &self.thir[snd_arm].pattern;
501 debug_assert_eq!(pat.span.desugaring_kind(), Some(DesugaringKind::ForLoop));
503 let PatKind::Variant { ref subpatterns, .. } = pat.kind else { bug!() };
504 let [pat_field] = &subpatterns[..] else { bug!() };
505 self.check_binding_is_irrefutable(
506 &pat_field.pattern,
507 "`for` loop binding",
508 None,
509 None,
510 );
511 } else {
512 let braces_span = match source {
515 hir::MatchSource::Normal => scrut
516 .span
517 .find_ancestor_in_same_ctxt(expr_span)
518 .map(|scrut_span| scrut_span.shrink_to_hi().with_hi(expr_span.hi())),
519 hir::MatchSource::Postfix => {
520 scrut.span.find_ancestor_in_same_ctxt(expr_span).and_then(|scrut_span| {
523 let sm = self.tcx.sess.source_map();
524 let brace_span = sm.span_extend_to_next_char(scrut_span, '{', true);
525 if sm.span_to_snippet(sm.next_point(brace_span)).as_deref() == Ok("{") {
526 let sp = brace_span.shrink_to_hi().with_hi(expr_span.hi());
527 sm.span_extend_prev_while(sp, |c| c.is_whitespace()).ok()
529 } else {
530 None
531 }
532 })
533 }
534 hir::MatchSource::ForLoopDesugar
535 | hir::MatchSource::TryDesugar(_)
536 | hir::MatchSource::AwaitDesugar
537 | hir::MatchSource::FormatArgs => None,
538 };
539 self.error = Err(report_non_exhaustive_match(
540 &cx,
541 self.thir,
542 scrut.ty,
543 scrut.span,
544 witnesses,
545 arms,
546 braces_span,
547 ));
548 }
549 }
550 }
551
552 #[instrument(level = "trace", skip(self))]
553 fn check_let_chain(
554 &mut self,
555 chain_refutabilities: Vec<Option<(Span, RefutableFlag)>>,
556 whole_chain_span: Span,
557 ) {
558 assert!(self.let_source != LetSource::None);
559
560 if chain_refutabilities.iter().all(|r| matches!(*r, Some((_, Irrefutable)))) {
561 report_irrefutable_let_patterns(
563 self.tcx,
564 self.lint_level,
565 self.let_source,
566 chain_refutabilities.len(),
567 whole_chain_span,
568 );
569 return;
570 }
571
572 if let Some(until) =
573 chain_refutabilities.iter().position(|r| !matches!(*r, Some((_, Irrefutable))))
574 && until > 0
575 {
576 if !matches!(
586 self.let_source,
587 LetSource::WhileLet | LetSource::IfLetGuard | LetSource::ElseIfLet
588 ) {
589 let prefix = &chain_refutabilities[..until];
591 let span_start = prefix[0].unwrap().0;
592 let span_end = prefix.last().unwrap().unwrap().0;
593 let span = span_start.to(span_end);
594 let count = prefix.len();
595 self.tcx.emit_node_span_lint(
596 IRREFUTABLE_LET_PATTERNS,
597 self.lint_level,
598 span,
599 LeadingIrrefutableLetPatterns { count },
600 );
601 }
602 }
603
604 if let Some(from) =
605 chain_refutabilities.iter().rposition(|r| !matches!(*r, Some((_, Irrefutable))))
606 && from != (chain_refutabilities.len() - 1)
607 {
608 let suffix = &chain_refutabilities[from + 1..];
610 let span_start = suffix[0].unwrap().0;
611 let span_end = suffix.last().unwrap().unwrap().0;
612 let span = span_start.to(span_end);
613 let count = suffix.len();
614 self.tcx.emit_node_span_lint(
615 IRREFUTABLE_LET_PATTERNS,
616 self.lint_level,
617 span,
618 TrailingIrrefutableLetPatterns { count },
619 );
620 }
621 }
622
623 fn analyze_binding(
624 &mut self,
625 pat: &'p Pat<'tcx>,
626 refutability: RefutableFlag,
627 scrut: Option<&Expr<'tcx>>,
628 ) -> Result<(PatCtxt<'p, 'tcx>, UsefulnessReport<'p, 'tcx>), ErrorGuaranteed> {
629 let cx = self.new_cx(refutability, None, scrut, pat.span);
630 let pat = self.lower_pattern(&cx, pat)?;
631 let arms = [MatchArm { pat, arm_data: self.lint_level, has_guard: false }];
632 let report = self.analyze_patterns(&cx, &arms, pat.ty().inner())?;
633 Ok((cx, report))
634 }
635
636 fn is_let_irrefutable(
637 &mut self,
638 pat: &'p Pat<'tcx>,
639 scrut: Option<&Expr<'tcx>>,
640 ) -> Result<RefutableFlag, ErrorGuaranteed> {
641 let (cx, report) = self.analyze_binding(pat, Refutable, scrut)?;
642 report_arm_reachability(&cx, &report, false);
644 Ok(if report.non_exhaustiveness_witnesses.is_empty() { Irrefutable } else { Refutable })
647 }
648
649 #[instrument(level = "trace", skip(self))]
650 fn check_binding_is_irrefutable(
651 &mut self,
652 pat: &'p Pat<'tcx>,
653 origin: &str,
654 scrut: Option<&Expr<'tcx>>,
655 sp: Option<Span>,
656 ) {
657 let pattern_ty = pat.ty;
658
659 let Ok((cx, report)) = self.analyze_binding(pat, Irrefutable, scrut) else { return };
660 let witnesses = report.non_exhaustiveness_witnesses;
661 if witnesses.is_empty() {
662 return;
664 }
665
666 let inform = sp.is_some().then_some(Inform);
667 let mut let_suggestion = None;
668 let mut misc_suggestion = None;
669 let mut interpreted_as_const = None;
670 let mut interpreted_as_const_sugg = None;
671
672 let mut unpeeled_pat = pat;
675 while let PatKind::AscribeUserType { ref subpattern, .. } = unpeeled_pat.kind {
676 unpeeled_pat = subpattern;
677 }
678
679 if let PatKind::ExpandedConstant { def_id, is_inline: false, .. } = unpeeled_pat.kind
680 && let DefKind::Const = self.tcx.def_kind(def_id)
681 && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(pat.span)
682 && snippet.chars().all(|c| c.is_alphanumeric() || c == '_')
684 {
685 let span = self.tcx.def_span(def_id);
686 let variable = self.tcx.item_name(def_id).to_string();
687 interpreted_as_const = Some(span);
689 interpreted_as_const_sugg = Some(InterpretedAsConst { span: pat.span, variable });
690 } else if let PatKind::Constant { .. } = unpeeled_pat.kind
691 && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(pat.span)
692 {
693 if snippet.chars().all(|c| c.is_digit(10)) {
695 misc_suggestion = Some(MiscPatternSuggestion::AttemptedIntegerLiteral {
697 start_span: pat.span.shrink_to_lo(),
698 });
699 }
700 }
701
702 if let Some(span) = sp
703 && self.tcx.sess.source_map().is_span_accessible(span)
704 && interpreted_as_const.is_none()
705 && scrut.is_some()
706 {
707 let mut bindings = vec![];
708 pat.each_binding(|name, _, _, _| bindings.push(name));
709
710 let semi_span = span.shrink_to_hi();
711 let start_span = span.shrink_to_lo();
712 let end_span = semi_span.shrink_to_lo();
713 let count = witnesses.len();
714
715 let_suggestion = Some(if bindings.is_empty() {
716 SuggestLet::If { start_span, semi_span, count }
717 } else {
718 SuggestLet::Else { end_span, count }
719 });
720 };
721
722 let adt_defined_here = report_adt_defined_here(self.tcx, pattern_ty, &witnesses, false);
723
724 let witness_1_is_privately_uninhabited = if let Some(witness_1) = witnesses.get(0)
727 && let ty::Adt(adt, args) = witness_1.ty().kind()
728 && adt.is_enum()
729 && let Constructor::Variant(variant_index) = witness_1.ctor()
730 {
731 let variant_inhabited = adt
732 .variant(*variant_index)
733 .inhabited_predicate(self.tcx, *adt)
734 .instantiate(self.tcx, args);
735 variant_inhabited.apply(self.tcx, cx.typing_env, cx.module)
736 && !variant_inhabited.apply_ignore_module(self.tcx, cx.typing_env)
737 } else {
738 false
739 };
740
741 self.error = Err(self.tcx.dcx().emit_err(PatternNotCovered {
742 span: pat.span,
743 origin,
744 uncovered: Uncovered::new(pat.span, &cx, witnesses),
745 inform,
746 interpreted_as_const,
747 interpreted_as_const_sugg,
748 witness_1_is_privately_uninhabited,
749 _p: (),
750 pattern_ty,
751 let_suggestion,
752 misc_suggestion,
753 adt_defined_here,
754 }));
755 }
756}
757
758fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: &Pat<'tcx>) {
770 let PatKind::Binding { name, mode, ty, subpattern: Some(box ref sub), .. } = pat.kind else {
772 return;
773 };
774
775 let is_binding_by_move = |ty: Ty<'tcx>| !cx.tcx.type_is_copy_modulo_regions(cx.typing_env, ty);
776
777 let sess = cx.tcx.sess;
778
779 let mut_outer = match mode.0 {
781 ByRef::No if is_binding_by_move(ty) => {
782 let mut conflicts_ref = Vec::new();
784 sub.each_binding(|_, mode, _, span| {
785 if matches!(mode, ByRef::Yes(_)) {
786 conflicts_ref.push(span)
787 }
788 });
789 if !conflicts_ref.is_empty() {
790 sess.dcx().emit_err(BorrowOfMovedValue {
791 binding_span: pat.span,
792 conflicts_ref,
793 name: Ident::new(name, pat.span),
794 ty,
795 suggest_borrowing: Some(pat.span.shrink_to_lo()),
796 });
797 }
798 return;
799 }
800 ByRef::No => return,
801 ByRef::Yes(m) => m,
802 };
803
804 let mut conflicts_move = Vec::new();
807 let mut conflicts_mut_mut = Vec::new();
808 let mut conflicts_mut_ref = Vec::new();
809 sub.each_binding(|name, mode, ty, span| {
810 match mode {
811 ByRef::Yes(mut_inner) => match (mut_outer, mut_inner) {
812 (Mutability::Not, Mutability::Not) => {}
814 (Mutability::Mut, Mutability::Mut) => {
816 conflicts_mut_mut.push(Conflict::Mut { span, name })
817 }
818 (Mutability::Not, Mutability::Mut) => {
819 conflicts_mut_ref.push(Conflict::Mut { span, name })
820 }
821 (Mutability::Mut, Mutability::Not) => {
822 conflicts_mut_ref.push(Conflict::Ref { span, name })
823 }
824 },
825 ByRef::No if is_binding_by_move(ty) => {
826 conflicts_move.push(Conflict::Moved { span, name }) }
828 ByRef::No => {} }
830 });
831
832 let report_mut_mut = !conflicts_mut_mut.is_empty();
833 let report_mut_ref = !conflicts_mut_ref.is_empty();
834 let report_move_conflict = !conflicts_move.is_empty();
835
836 let mut occurrences = match mut_outer {
837 Mutability::Mut => vec![Conflict::Mut { span: pat.span, name }],
838 Mutability::Not => vec![Conflict::Ref { span: pat.span, name }],
839 };
840 occurrences.extend(conflicts_mut_mut);
841 occurrences.extend(conflicts_mut_ref);
842 occurrences.extend(conflicts_move);
843
844 if report_mut_mut {
846 sess.dcx().emit_err(MultipleMutBorrows { span: pat.span, occurrences });
848 } else if report_mut_ref {
849 match mut_outer {
851 Mutability::Mut => {
852 sess.dcx().emit_err(AlreadyMutBorrowed { span: pat.span, occurrences });
853 }
854 Mutability::Not => {
855 sess.dcx().emit_err(AlreadyBorrowed { span: pat.span, occurrences });
856 }
857 };
858 } else if report_move_conflict {
859 sess.dcx().emit_err(MovedWhileBorrowed { span: pat.span, occurrences });
861 }
862}
863
864fn check_for_bindings_named_same_as_variants(
865 cx: &MatchVisitor<'_, '_>,
866 pat: &Pat<'_>,
867 rf: RefutableFlag,
868) {
869 if let PatKind::Binding {
870 name,
871 mode: BindingMode(ByRef::No, Mutability::Not),
872 subpattern: None,
873 ty,
874 ..
875 } = pat.kind
876 && let ty::Adt(edef, _) = ty.peel_refs().kind()
877 && edef.is_enum()
878 && edef
879 .variants()
880 .iter()
881 .any(|variant| variant.name == name && variant.ctor_kind() == Some(CtorKind::Const))
882 {
883 let variant_count = edef.variants().len();
884 let ty_path = with_no_trimmed_paths!(cx.tcx.def_path_str(edef.did()));
885 cx.tcx.emit_node_span_lint(
886 BINDINGS_WITH_VARIANT_NAME,
887 cx.lint_level,
888 pat.span,
889 BindingsWithVariantName {
890 suggestion: if rf == Refutable || variant_count == 1 {
894 Some(pat.span)
895 } else {
896 None
897 },
898 ty_path,
899 name: Ident::new(name, pat.span),
900 },
901 )
902 }
903}
904
905fn check_never_pattern<'tcx>(
907 cx: &PatCtxt<'_, 'tcx>,
908 pat: &Pat<'tcx>,
909) -> Result<(), ErrorGuaranteed> {
910 if let PatKind::Never = pat.kind {
911 if !cx.is_uninhabited(pat.ty) {
912 return Err(cx.tcx.dcx().emit_err(NonEmptyNeverPattern { span: pat.span, ty: pat.ty }));
913 }
914 }
915 Ok(())
916}
917
918fn report_irrefutable_let_patterns(
919 tcx: TyCtxt<'_>,
920 id: HirId,
921 source: LetSource,
922 count: usize,
923 span: Span,
924) {
925 macro_rules! emit_diag {
926 ($lint:tt) => {{
927 tcx.emit_node_span_lint(IRREFUTABLE_LET_PATTERNS, id, span, $lint { count });
928 }};
929 }
930
931 match source {
932 LetSource::None | LetSource::PlainLet | LetSource::Else => bug!(),
933 LetSource::IfLet | LetSource::ElseIfLet => emit_diag!(IrrefutableLetPatternsIfLet),
934 LetSource::IfLetGuard => emit_diag!(IrrefutableLetPatternsIfLetGuard),
935 LetSource::LetElse => emit_diag!(IrrefutableLetPatternsLetElse),
936 LetSource::WhileLet => emit_diag!(IrrefutableLetPatternsWhileLet),
937 }
938}
939
940fn report_unreachable_pattern<'p, 'tcx>(
942 cx: &PatCtxt<'p, 'tcx>,
943 hir_id: HirId,
944 pat: &DeconstructedPat<'p, 'tcx>,
945 explanation: &RedundancyExplanation<'p, 'tcx>,
946 whole_arm_span: Option<Span>,
947) {
948 static CAP_COVERED_BY_MANY: usize = 4;
949 let pat_span = pat.data().span;
950 let mut lint = UnreachablePattern {
951 span: Some(pat_span),
952 matches_no_values: None,
953 matches_no_values_ty: **pat.ty(),
954 uninhabited_note: None,
955 covered_by_catchall: None,
956 covered_by_one: None,
957 covered_by_many: None,
958 covered_by_many_n_more_count: 0,
959 wanted_constant: None,
960 accessible_constant: None,
961 inaccessible_constant: None,
962 pattern_let_binding: None,
963 suggest_remove: None,
964 };
965 match explanation.covered_by.as_slice() {
966 [] => {
967 lint.span = None; lint.uninhabited_note = Some(()); lint.matches_no_values = Some(pat_span);
971 lint.suggest_remove = whole_arm_span; pat.walk(&mut |subpat| {
973 let ty = **subpat.ty();
974 if cx.is_uninhabited(ty) {
975 lint.matches_no_values_ty = ty;
976 false } else if matches!(subpat.ctor(), Constructor::Ref | Constructor::UnionField) {
978 false } else {
980 true
981 }
982 });
983 }
984 [covering_pat] if pat_is_catchall(covering_pat) => {
985 let pat = covering_pat.data();
987 lint.covered_by_catchall = Some(pat.span);
988 find_fallback_pattern_typo(cx, hir_id, pat, &mut lint);
989 }
990 [covering_pat] => {
991 lint.covered_by_one = Some(covering_pat.data().span);
992 }
993 covering_pats => {
994 let mut iter = covering_pats.iter();
995 let mut multispan = MultiSpan::from_span(pat_span);
996 for p in iter.by_ref().take(CAP_COVERED_BY_MANY) {
997 multispan.push_span_label(
998 p.data().span,
999 fluent::mir_build_unreachable_matches_same_values,
1000 );
1001 }
1002 let remain = iter.count();
1003 if remain == 0 {
1004 multispan.push_span_label(
1005 pat_span,
1006 fluent::mir_build_unreachable_making_this_unreachable,
1007 );
1008 } else {
1009 lint.covered_by_many_n_more_count = remain;
1010 multispan.push_span_label(
1011 pat_span,
1012 fluent::mir_build_unreachable_making_this_unreachable_n_more,
1013 );
1014 }
1015 lint.covered_by_many = Some(multispan);
1016 }
1017 }
1018 cx.tcx.emit_node_span_lint(UNREACHABLE_PATTERNS, hir_id, pat_span, lint);
1019}
1020
1021fn find_fallback_pattern_typo<'tcx>(
1023 cx: &PatCtxt<'_, 'tcx>,
1024 hir_id: HirId,
1025 pat: &Pat<'tcx>,
1026 lint: &mut UnreachablePattern<'_>,
1027) {
1028 if let (Level::Allow, _) = cx.tcx.lint_level_at_node(UNREACHABLE_PATTERNS, hir_id) {
1029 return;
1032 }
1033 if let PatKind::Binding { name, subpattern: None, ty, .. } = pat.kind {
1034 let mut accessible = vec![];
1036 let mut accessible_path = vec![];
1037 let mut inaccessible = vec![];
1038 let mut imported = vec![];
1039 let mut imported_spans = vec![];
1040 let (infcx, param_env) = cx.tcx.infer_ctxt().build_with_typing_env(cx.typing_env);
1041 let parent = cx.tcx.hir_get_parent_item(hir_id);
1042
1043 for item in cx.tcx.hir_crate_items(()).free_items() {
1044 if let DefKind::Use = cx.tcx.def_kind(item.owner_id) {
1045 let item = cx.tcx.hir_expect_item(item.owner_id.def_id);
1047 let hir::ItemKind::Use(path, _) = item.kind else {
1048 continue;
1049 };
1050 for res in &path.res {
1051 if let Res::Def(DefKind::Const, id) = res
1052 && infcx.can_eq(param_env, ty, cx.tcx.type_of(id).instantiate_identity())
1053 {
1054 if cx.tcx.visibility(id).is_accessible_from(parent, cx.tcx) {
1055 let item_name = cx.tcx.item_name(*id);
1057 accessible.push(item_name);
1058 accessible_path.push(with_no_trimmed_paths!(cx.tcx.def_path_str(id)));
1059 } else if cx
1060 .tcx
1061 .visibility(item.owner_id)
1062 .is_accessible_from(parent, cx.tcx)
1063 {
1064 let ident = item.kind.ident().unwrap();
1067 imported.push(ident.name);
1068 imported_spans.push(ident.span);
1069 }
1070 }
1071 }
1072 }
1073 if let DefKind::Const = cx.tcx.def_kind(item.owner_id)
1074 && infcx.can_eq(param_env, ty, cx.tcx.type_of(item.owner_id).instantiate_identity())
1075 {
1076 let item_name = cx.tcx.item_name(item.owner_id.into());
1078 let vis = cx.tcx.visibility(item.owner_id);
1079 if vis.is_accessible_from(parent, cx.tcx) {
1080 accessible.push(item_name);
1081 let path = with_no_trimmed_paths!(cx.tcx.def_path_str(item.owner_id));
1088 accessible_path.push(path);
1089 } else if name == item_name {
1090 inaccessible.push(cx.tcx.def_span(item.owner_id));
1093 }
1094 }
1095 }
1096 if let Some((i, &const_name)) =
1097 accessible.iter().enumerate().find(|&(_, &const_name)| const_name == name)
1098 {
1099 lint.wanted_constant = Some(WantedConstant {
1101 span: pat.span,
1102 is_typo: false,
1103 const_name: const_name.to_string(),
1104 const_path: accessible_path[i].clone(),
1105 });
1106 } else if let Some(name) = find_best_match_for_name(&accessible, name, None) {
1107 lint.wanted_constant = Some(WantedConstant {
1109 span: pat.span,
1110 is_typo: true,
1111 const_name: name.to_string(),
1112 const_path: name.to_string(),
1113 });
1114 } else if let Some(i) =
1115 imported.iter().enumerate().find(|&(_, &const_name)| const_name == name).map(|(i, _)| i)
1116 {
1117 lint.accessible_constant = Some(imported_spans[i]);
1120 } else if let Some(name) = find_best_match_for_name(&imported, name, None) {
1121 lint.wanted_constant = Some(WantedConstant {
1124 span: pat.span,
1125 is_typo: true,
1126 const_path: name.to_string(),
1127 const_name: name.to_string(),
1128 });
1129 } else if !inaccessible.is_empty() {
1130 for span in inaccessible {
1131 lint.inaccessible_constant = Some(span);
1133 }
1134 } else {
1135 for (_, node) in cx.tcx.hir_parent_iter(hir_id) {
1138 match node {
1139 hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Let(let_stmt), .. }) => {
1140 if let hir::PatKind::Binding(_, _, binding_name, _) = let_stmt.pat.kind {
1141 if name == binding_name.name {
1142 lint.pattern_let_binding = Some(binding_name.span);
1143 }
1144 }
1145 }
1146 hir::Node::Block(hir::Block { stmts, .. }) => {
1147 for stmt in *stmts {
1148 if let hir::StmtKind::Let(let_stmt) = stmt.kind {
1149 if let hir::PatKind::Binding(_, _, binding_name, _) =
1150 let_stmt.pat.kind
1151 {
1152 if name == binding_name.name {
1153 lint.pattern_let_binding = Some(binding_name.span);
1154 }
1155 }
1156 }
1157 }
1158 }
1159 hir::Node::Item(_) => break,
1160 _ => {}
1161 }
1162 }
1163 }
1164 }
1165}
1166
1167fn report_arm_reachability<'p, 'tcx>(
1169 cx: &PatCtxt<'p, 'tcx>,
1170 report: &UsefulnessReport<'p, 'tcx>,
1171 is_match_arm: bool,
1172) {
1173 let sm = cx.tcx.sess.source_map();
1174 for (arm, is_useful) in report.arm_usefulness.iter() {
1175 if let Usefulness::Redundant(explanation) = is_useful {
1176 let hir_id = arm.arm_data;
1177 let arm_span = cx.tcx.hir().span(hir_id);
1178 let whole_arm_span = if is_match_arm {
1179 let with_whitespace = sm.span_extend_while_whitespace(arm_span);
1181 if let Some(comma) = sm.span_look_ahead(with_whitespace, ",", Some(1)) {
1182 Some(arm_span.to(comma))
1183 } else {
1184 Some(arm_span)
1185 }
1186 } else {
1187 None
1188 };
1189 report_unreachable_pattern(cx, hir_id, arm.pat, explanation, whole_arm_span)
1190 }
1191 }
1192}
1193
1194fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
1196 match pat.ctor() {
1197 Constructor::Wildcard => true,
1198 Constructor::Struct | Constructor::Ref => {
1199 pat.iter_fields().all(|ipat| pat_is_catchall(&ipat.pat))
1200 }
1201 _ => false,
1202 }
1203}
1204
1205fn report_non_exhaustive_match<'p, 'tcx>(
1207 cx: &PatCtxt<'p, 'tcx>,
1208 thir: &Thir<'tcx>,
1209 scrut_ty: Ty<'tcx>,
1210 sp: Span,
1211 witnesses: Vec<WitnessPat<'p, 'tcx>>,
1212 arms: &[ArmId],
1213 braces_span: Option<Span>,
1214) -> ErrorGuaranteed {
1215 let is_empty_match = arms.is_empty();
1216 let non_empty_enum = match scrut_ty.kind() {
1217 ty::Adt(def, _) => def.is_enum() && !def.variants().is_empty(),
1218 _ => false,
1219 };
1220 if is_empty_match && !non_empty_enum {
1223 return cx.tcx.dcx().emit_err(NonExhaustivePatternsTypeNotEmpty {
1224 cx,
1225 scrut_span: sp,
1226 braces_span,
1227 ty: scrut_ty,
1228 });
1229 }
1230
1231 let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
1233 let mut err = struct_span_code_err!(
1234 cx.tcx.dcx(),
1235 sp,
1236 E0004,
1237 "non-exhaustive patterns: {joined_patterns} not covered"
1238 );
1239 err.span_label(
1240 sp,
1241 format!(
1242 "pattern{} {} not covered",
1243 rustc_errors::pluralize!(witnesses.len()),
1244 joined_patterns
1245 ),
1246 );
1247
1248 if let Some(AdtDefinedHere { adt_def_span, ty, variants }) =
1250 report_adt_defined_here(cx.tcx, scrut_ty, &witnesses, true)
1251 {
1252 let mut multi_span = MultiSpan::from_span(adt_def_span);
1253 multi_span.push_span_label(adt_def_span, "");
1254 for Variant { span } in variants {
1255 multi_span.push_span_label(span, "not covered");
1256 }
1257 err.span_note(multi_span, format!("`{ty}` defined here"));
1258 }
1259 err.note(format!("the matched value is of type `{}`", scrut_ty));
1260
1261 if !is_empty_match {
1262 let mut special_tys = FxIndexSet::default();
1263 collect_special_tys(cx, &witnesses[0], &mut special_tys);
1265
1266 for ty in special_tys {
1267 if ty.is_ptr_sized_integral() {
1268 if ty.inner() == cx.tcx.types.usize {
1269 err.note(format!(
1270 "`{ty}` does not have a fixed maximum value, so half-open ranges are \
1271 necessary to match exhaustively",
1272 ));
1273 } else if ty.inner() == cx.tcx.types.isize {
1274 err.note(format!(
1275 "`{ty}` does not have fixed minimum and maximum values, so half-open \
1276 ranges are necessary to match exhaustively",
1277 ));
1278 }
1279 } else if ty.inner() == cx.tcx.types.str_ {
1280 err.note("`&str` cannot be matched exhaustively, so a wildcard `_` is necessary");
1281 } else if cx.is_foreign_non_exhaustive_enum(ty) {
1282 err.note(format!("`{ty}` is marked as non-exhaustive, so a wildcard `_` is necessary to match exhaustively"));
1283 } else if cx.is_uninhabited(ty.inner()) {
1284 err.note(format!("`{ty}` is uninhabited but is not being matched by value, so a wildcard `_` is required"));
1287 }
1288 }
1289 }
1290
1291 if let ty::Ref(_, sub_ty, _) = scrut_ty.kind() {
1292 if !sub_ty.is_inhabited_from(cx.tcx, cx.module, cx.typing_env) {
1293 err.note("references are always considered inhabited");
1294 }
1295 }
1296
1297 for &arm in arms {
1298 let arm = &thir.arms[arm];
1299 if let PatKind::ExpandedConstant { def_id, is_inline: false, .. } = arm.pattern.kind
1300 && let Ok(snippet) = cx.tcx.sess.source_map().span_to_snippet(arm.pattern.span)
1301 && snippet.chars().all(|c| c.is_alphanumeric() || c == '_')
1303 {
1304 let const_name = cx.tcx.item_name(def_id);
1305 err.span_label(
1306 arm.pattern.span,
1307 format!(
1308 "this pattern doesn't introduce a new catch-all binding, but rather pattern \
1309 matches against the value of constant `{const_name}`",
1310 ),
1311 );
1312 err.span_note(cx.tcx.def_span(def_id), format!("constant `{const_name}` defined here"));
1313 err.span_suggestion_verbose(
1314 arm.pattern.span.shrink_to_hi(),
1315 "if you meant to introduce a binding, use a different name",
1316 "_var".to_string(),
1317 Applicability::MaybeIncorrect,
1318 );
1319 }
1320 }
1321
1322 let suggest_the_witnesses = witnesses.len() < 4;
1324 let suggested_arm = if suggest_the_witnesses {
1325 let pattern = witnesses
1326 .iter()
1327 .map(|witness| cx.print_witness_pat(witness))
1328 .collect::<Vec<String>>()
1329 .join(" | ");
1330 if witnesses.iter().all(|p| p.is_never_pattern()) && cx.tcx.features().never_patterns() {
1331 pattern
1333 } else {
1334 format!("{pattern} => todo!()")
1335 }
1336 } else {
1337 format!("_ => todo!()")
1338 };
1339 let mut suggestion = None;
1340 let sm = cx.tcx.sess.source_map();
1341 match arms {
1342 [] if let Some(braces_span) = braces_span => {
1343 let (indentation, more) = if let Some(snippet) = sm.indentation_before(sp) {
1345 (format!("\n{snippet}"), " ")
1346 } else {
1347 (" ".to_string(), "")
1348 };
1349 suggestion = Some((
1350 braces_span,
1351 format!(" {{{indentation}{more}{suggested_arm},{indentation}}}",),
1352 ));
1353 }
1354 [only] => {
1355 let only = &thir[*only];
1356 let (pre_indentation, is_multiline) = if let Some(snippet) =
1357 sm.indentation_before(only.span)
1358 && let Ok(with_trailing) =
1359 sm.span_extend_while(only.span, |c| c.is_whitespace() || c == ',')
1360 && sm.is_multiline(with_trailing)
1361 {
1362 (format!("\n{snippet}"), true)
1363 } else {
1364 (" ".to_string(), false)
1365 };
1366 let only_body = &thir[only.body];
1367 let comma = if matches!(only_body.kind, ExprKind::Block { .. })
1368 && only.span.eq_ctxt(only_body.span)
1369 && is_multiline
1370 {
1371 ""
1372 } else {
1373 ","
1374 };
1375 suggestion = Some((
1376 only.span.shrink_to_hi(),
1377 format!("{comma}{pre_indentation}{suggested_arm}"),
1378 ));
1379 }
1380 [.., prev, last] => {
1381 let prev = &thir[*prev];
1382 let last = &thir[*last];
1383 if prev.span.eq_ctxt(last.span) {
1384 let last_body = &thir[last.body];
1385 let comma = if matches!(last_body.kind, ExprKind::Block { .. })
1386 && last.span.eq_ctxt(last_body.span)
1387 {
1388 ""
1389 } else {
1390 ","
1391 };
1392 let spacing = if sm.is_multiline(prev.span.between(last.span)) {
1393 sm.indentation_before(last.span).map(|indent| format!("\n{indent}"))
1394 } else {
1395 Some(" ".to_string())
1396 };
1397 if let Some(spacing) = spacing {
1398 suggestion = Some((
1399 last.span.shrink_to_hi(),
1400 format!("{comma}{spacing}{suggested_arm}"),
1401 ));
1402 }
1403 }
1404 }
1405 _ => {}
1406 }
1407
1408 let msg = format!(
1409 "ensure that all possible cases are being handled by adding a match arm with a wildcard \
1410 pattern{}{}",
1411 if witnesses.len() > 1 && suggest_the_witnesses && suggestion.is_some() {
1412 ", a match arm with multiple or-patterns"
1413 } else {
1414 ""
1416 },
1417 match witnesses.len() {
1418 0 if suggestion.is_some() => " as shown",
1420 0 => "",
1421 1 if suggestion.is_some() => " or an explicit pattern as shown",
1422 1 => " or an explicit pattern",
1423 _ if suggestion.is_some() => " as shown, or multiple match arms",
1424 _ => " or multiple match arms",
1425 },
1426 );
1427
1428 let all_arms_have_guards = arms.iter().all(|arm_id| thir[*arm_id].guard.is_some());
1429 if !is_empty_match && all_arms_have_guards {
1430 err.subdiagnostic(NonExhaustiveMatchAllArmsGuarded);
1431 }
1432 if let Some((span, sugg)) = suggestion {
1433 err.span_suggestion_verbose(span, msg, sugg, Applicability::HasPlaceholders);
1434 } else {
1435 err.help(msg);
1436 }
1437 err.emit()
1438}
1439
1440fn joined_uncovered_patterns<'p, 'tcx>(
1441 cx: &PatCtxt<'p, 'tcx>,
1442 witnesses: &[WitnessPat<'p, 'tcx>],
1443) -> String {
1444 const LIMIT: usize = 3;
1445 let pat_to_str = |pat: &WitnessPat<'p, 'tcx>| cx.print_witness_pat(pat);
1446 match witnesses {
1447 [] => bug!(),
1448 [witness] => format!("`{}`", cx.print_witness_pat(witness)),
1449 [head @ .., tail] if head.len() < LIMIT => {
1450 let head: Vec<_> = head.iter().map(pat_to_str).collect();
1451 format!("`{}` and `{}`", head.join("`, `"), cx.print_witness_pat(tail))
1452 }
1453 _ => {
1454 let (head, tail) = witnesses.split_at(LIMIT);
1455 let head: Vec<_> = head.iter().map(pat_to_str).collect();
1456 format!("`{}` and {} more", head.join("`, `"), tail.len())
1457 }
1458 }
1459}
1460
1461fn collect_special_tys<'tcx>(
1463 cx: &PatCtxt<'_, 'tcx>,
1464 pat: &WitnessPat<'_, 'tcx>,
1465 special_tys: &mut FxIndexSet<RevealedTy<'tcx>>,
1466) {
1467 if matches!(pat.ctor(), Constructor::NonExhaustive | Constructor::Never) {
1468 special_tys.insert(*pat.ty());
1469 }
1470 if let Constructor::IntRange(range) = pat.ctor() {
1471 if cx.is_range_beyond_boundaries(range, *pat.ty()) {
1472 special_tys.insert(*pat.ty());
1474 }
1475 }
1476 pat.iter_fields().for_each(|field_pat| collect_special_tys(cx, field_pat, special_tys))
1477}
1478
1479fn report_adt_defined_here<'tcx>(
1480 tcx: TyCtxt<'tcx>,
1481 ty: Ty<'tcx>,
1482 witnesses: &[WitnessPat<'_, 'tcx>],
1483 point_at_non_local_ty: bool,
1484) -> Option<AdtDefinedHere<'tcx>> {
1485 let ty = ty.peel_refs();
1486 let ty::Adt(def, _) = ty.kind() else {
1487 return None;
1488 };
1489 let adt_def_span =
1490 tcx.hir_get_if_local(def.did()).and_then(|node| node.ident()).map(|ident| ident.span);
1491 let adt_def_span = if point_at_non_local_ty {
1492 adt_def_span.unwrap_or_else(|| tcx.def_span(def.did()))
1493 } else {
1494 adt_def_span?
1495 };
1496
1497 let mut variants = vec![];
1498 for span in maybe_point_at_variant(tcx, *def, witnesses.iter().take(5)) {
1499 variants.push(Variant { span });
1500 }
1501 Some(AdtDefinedHere { adt_def_span, ty, variants })
1502}
1503
1504fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'p>(
1505 tcx: TyCtxt<'tcx>,
1506 def: AdtDef<'tcx>,
1507 patterns: impl Iterator<Item = &'a WitnessPat<'p, 'tcx>>,
1508) -> Vec<Span> {
1509 let mut covered = vec![];
1510 for pattern in patterns {
1511 if let Constructor::Variant(variant_index) = pattern.ctor() {
1512 if let ty::Adt(this_def, _) = pattern.ty().kind()
1513 && this_def.did() != def.did()
1514 {
1515 continue;
1516 }
1517 let sp = def.variant(*variant_index).ident(tcx).span;
1518 if covered.contains(&sp) {
1519 continue;
1522 }
1523 covered.push(sp);
1524 }
1525 covered.extend(maybe_point_at_variant(tcx, def, pattern.iter_fields()));
1526 }
1527 covered
1528}