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