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 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 _ 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 error: Result<(), ErrorGuaranteed>,
104}
105
106impl<'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 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 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 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 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 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 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 fn is_known_valid_scrutinee(&self, scrutinee: &Expr<'tcx>) -> bool {
301 use ExprKind::*;
302 match &scrutinee.kind {
303 Deref { .. } => false,
306 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 Index { lhs, .. } => {
316 let lhs = &self.thir()[*lhs];
317 self.is_known_valid_scrutinee(lhs)
318 }
319
320 Scope { value, .. } => self.is_known_valid_scrutinee(&self.thir()[*value]),
322
323 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 Become { .. } | Break { .. } | Continue { .. } | Return { .. } => true,
336
337 Assign { .. } | AssignOp { .. } | InlineAsm { .. } | Let { .. } => true,
339
340 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 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 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 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 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 hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar(_) => {}
498 }
499
500 let witnesses = report.non_exhaustiveness_witnesses;
502 if !witnesses.is_empty() {
503 if source == hir::MatchSource::ForLoopDesugar
504 && let [_, snd_arm] = *arms
505 {
506 let pat = &self.thir[snd_arm].pattern;
508 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 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 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 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 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 if !matches!(
593 self.let_source,
594 LetSource::WhileLet | LetSource::IfLetGuard | LetSource::ElseIfLet
595 ) {
596 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 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_arm_reachability(&cx, &report, false);
651 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 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 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 && 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 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 snippet.chars().all(|c| c.is_digit(10)) {
702 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 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
765fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: &Pat<'tcx>) {
777 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 let mut_outer = match mode.0 {
788 ByRef::No if is_binding_by_move(ty) => {
789 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 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 (Mutability::Not, Mutability::Not) => {}
825 (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 }) }
839 ByRef::No => {} }
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 if report_mut_mut {
857 sess.dcx().emit_err(MultipleMutBorrows { span: pat.span, occurrences });
859 } else if report_mut_ref {
860 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 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 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
916fn 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
951fn 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 lint.span = None; lint.uninhabited_note = Some(()); lint.matches_no_values = Some(pat_span);
982 lint.suggest_remove = whole_arm_span; pat.walk(&mut |subpat| {
984 let ty = **subpat.ty();
985 if cx.is_uninhabited(ty) {
986 lint.matches_no_values_ty = ty;
987 false } else if matches!(subpat.ctor(), Constructor::Ref | Constructor::UnionField) {
989 false } else {
991 true
992 }
993 });
994 }
995 [covering_pat] if pat_is_catchall(covering_pat) => {
996 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
1032fn 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 return;
1043 }
1044 if let PatKind::Binding { name, subpattern: None, ty, .. } = pat.kind {
1045 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 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 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 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 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 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 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 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 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 lint.accessible_constant = Some(imported_spans[i]);
1131 } else if let Some(name) = find_best_match_for_name(&imported, name, None) {
1132 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 lint.inaccessible_constant = Some(span);
1144 }
1145 } else {
1146 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
1178fn 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 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
1205fn 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
1216fn 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 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 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 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 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 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 && 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 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 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 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 ""
1427 },
1428 match witnesses.len() {
1429 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
1472fn 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 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 continue;
1533 }
1534 covered.push(sp);
1535 }
1536 covered.extend(maybe_point_at_variant(tcx, def, pattern.iter_fields()));
1537 }
1538 covered
1539}