]> git.proxmox.com Git - rustc.git/blame_incremental - compiler/rustc_mir_build/src/thir/pattern/check_match.rs
bump version to 1.81.0+dfsg1-2~bpo12+pve1
[rustc.git] / compiler / rustc_mir_build / src / thir / pattern / check_match.rs
... / ...
CommitLineData
1use crate::errors::*;
2
3use rustc_arena::{DroplessArena, TypedArena};
4use rustc_ast::Mutability;
5use rustc_data_structures::fx::FxIndexSet;
6use rustc_data_structures::stack::ensure_sufficient_stack;
7use rustc_errors::{codes::*, struct_span_code_err, Applicability, ErrorGuaranteed, MultiSpan};
8use rustc_hir::def::*;
9use rustc_hir::def_id::LocalDefId;
10use rustc_hir::{self as hir, BindingMode, ByRef, HirId};
11use rustc_middle::bug;
12use rustc_middle::middle::limits::get_limit_size;
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, RustcPatCtxt as PatCtxt, Usefulness, UsefulnessReport,
20 WitnessPat,
21};
22use rustc_session::lint::builtin::{
23 BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS,
24};
25use rustc_span::hygiene::DesugaringKind;
26use rustc_span::{sym, Span};
27use tracing::instrument;
28
29pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> {
30 let typeck_results = tcx.typeck(def_id);
31 let (thir, expr) = tcx.thir_body(def_id)?;
32 let thir = thir.borrow();
33 let pattern_arena = TypedArena::default();
34 let dropless_arena = DroplessArena::default();
35 let mut visitor = MatchVisitor {
36 tcx,
37 thir: &*thir,
38 typeck_results,
39 param_env: tcx.param_env(def_id),
40 lint_level: tcx.local_def_id_to_hir_id(def_id),
41 let_source: LetSource::None,
42 pattern_arena: &pattern_arena,
43 dropless_arena: &dropless_arena,
44 error: Ok(()),
45 };
46 visitor.visit_expr(&thir[expr]);
47
48 let origin = match tcx.def_kind(def_id) {
49 DefKind::AssocFn | DefKind::Fn => "function argument",
50 DefKind::Closure => "closure argument",
51 // other types of MIR don't have function parameters, and we don't need to
52 // categorize those for the irrefutable check.
53 _ if thir.params.is_empty() => "",
54 kind => bug!("unexpected function parameters in THIR: {kind:?} {def_id:?}"),
55 };
56
57 for param in thir.params.iter() {
58 if let Some(box ref pattern) = param.pat {
59 visitor.check_binding_is_irrefutable(pattern, origin, None, None);
60 }
61 }
62 visitor.error
63}
64
65#[derive(Debug, Copy, Clone, PartialEq)]
66enum RefutableFlag {
67 Irrefutable,
68 Refutable,
69}
70use RefutableFlag::*;
71
72#[derive(Clone, Copy, Debug, PartialEq, Eq)]
73enum LetSource {
74 None,
75 PlainLet,
76 IfLet,
77 IfLetGuard,
78 LetElse,
79 WhileLet,
80}
81
82struct MatchVisitor<'p, 'tcx> {
83 tcx: TyCtxt<'tcx>,
84 param_env: ty::ParamEnv<'tcx>,
85 typeck_results: &'tcx ty::TypeckResults<'tcx>,
86 thir: &'p Thir<'tcx>,
87 lint_level: HirId,
88 let_source: LetSource,
89 pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
90 dropless_arena: &'p DroplessArena,
91 /// Tracks if we encountered an error while checking this body. That the first function to
92 /// report it stores it here. Some functions return `Result` to allow callers to short-circuit
93 /// on error, but callers don't need to store it here again.
94 error: Result<(), ErrorGuaranteed>,
95}
96
97// Visitor for a thir body. This calls `check_match`, `check_let` and `check_let_chain` as
98// appropriate.
99impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> {
100 fn thir(&self) -> &'p Thir<'tcx> {
101 self.thir
102 }
103
104 #[instrument(level = "trace", skip(self))]
105 fn visit_arm(&mut self, arm: &'p Arm<'tcx>) {
106 self.with_lint_level(arm.lint_level, |this| {
107 if let Some(expr) = arm.guard {
108 this.with_let_source(LetSource::IfLetGuard, |this| {
109 this.visit_expr(&this.thir[expr])
110 });
111 }
112 this.visit_pat(&arm.pattern);
113 this.visit_expr(&self.thir[arm.body]);
114 });
115 }
116
117 #[instrument(level = "trace", skip(self))]
118 fn visit_expr(&mut self, ex: &'p Expr<'tcx>) {
119 match ex.kind {
120 ExprKind::Scope { value, lint_level, .. } => {
121 self.with_lint_level(lint_level, |this| {
122 this.visit_expr(&this.thir[value]);
123 });
124 return;
125 }
126 ExprKind::If { cond, then, else_opt, if_then_scope: _ } => {
127 // Give a specific `let_source` for the condition.
128 let let_source = match ex.span.desugaring_kind() {
129 Some(DesugaringKind::WhileLoop) => LetSource::WhileLet,
130 _ => LetSource::IfLet,
131 };
132 self.with_let_source(let_source, |this| this.visit_expr(&self.thir[cond]));
133 self.with_let_source(LetSource::None, |this| {
134 this.visit_expr(&this.thir[then]);
135 if let Some(else_) = else_opt {
136 this.visit_expr(&this.thir[else_]);
137 }
138 });
139 return;
140 }
141 ExprKind::Match { scrutinee, scrutinee_hir_id: _, box ref arms, match_source } => {
142 self.check_match(scrutinee, arms, match_source, ex.span);
143 }
144 ExprKind::Let { box ref pat, expr } => {
145 self.check_let(pat, Some(expr), ex.span);
146 }
147 ExprKind::LogicalOp { op: LogicalOp::And, .. }
148 if !matches!(self.let_source, LetSource::None) =>
149 {
150 let mut chain_refutabilities = Vec::new();
151 let Ok(()) = self.visit_land(ex, &mut chain_refutabilities) else { return };
152 // If at least one of the operands is a `let ... = ...`.
153 if chain_refutabilities.iter().any(|x| x.is_some()) {
154 self.check_let_chain(chain_refutabilities, ex.span);
155 }
156 return;
157 }
158 _ => {}
159 };
160 self.with_let_source(LetSource::None, |this| visit::walk_expr(this, ex));
161 }
162
163 fn visit_stmt(&mut self, stmt: &'p Stmt<'tcx>) {
164 match stmt.kind {
165 StmtKind::Let {
166 box ref pattern, initializer, else_block, lint_level, span, ..
167 } => {
168 self.with_lint_level(lint_level, |this| {
169 let let_source =
170 if else_block.is_some() { LetSource::LetElse } else { LetSource::PlainLet };
171 this.with_let_source(let_source, |this| {
172 this.check_let(pattern, initializer, span)
173 });
174 visit::walk_stmt(this, stmt);
175 });
176 }
177 StmtKind::Expr { .. } => {
178 visit::walk_stmt(self, stmt);
179 }
180 }
181 }
182}
183
184impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
185 #[instrument(level = "trace", skip(self, f))]
186 fn with_let_source(&mut self, let_source: LetSource, f: impl FnOnce(&mut Self)) {
187 let old_let_source = self.let_source;
188 self.let_source = let_source;
189 ensure_sufficient_stack(|| f(self));
190 self.let_source = old_let_source;
191 }
192
193 fn with_lint_level<T>(
194 &mut self,
195 new_lint_level: LintLevel,
196 f: impl FnOnce(&mut Self) -> T,
197 ) -> T {
198 if let LintLevel::Explicit(hir_id) = new_lint_level {
199 let old_lint_level = self.lint_level;
200 self.lint_level = hir_id;
201 let ret = f(self);
202 self.lint_level = old_lint_level;
203 ret
204 } else {
205 f(self)
206 }
207 }
208
209 /// Visit a nested chain of `&&`. Used for if-let chains. This must call `visit_expr` on the
210 /// subexpressions we are not handling ourselves.
211 fn visit_land(
212 &mut self,
213 ex: &'p Expr<'tcx>,
214 accumulator: &mut Vec<Option<(Span, RefutableFlag)>>,
215 ) -> Result<(), ErrorGuaranteed> {
216 match ex.kind {
217 ExprKind::Scope { value, lint_level, .. } => self.with_lint_level(lint_level, |this| {
218 this.visit_land(&this.thir[value], accumulator)
219 }),
220 ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => {
221 // We recurse into the lhs only, because `&&` chains associate to the left.
222 let res_lhs = self.visit_land(&self.thir[lhs], accumulator);
223 let res_rhs = self.visit_land_rhs(&self.thir[rhs])?;
224 accumulator.push(res_rhs);
225 res_lhs
226 }
227 _ => {
228 let res = self.visit_land_rhs(ex)?;
229 accumulator.push(res);
230 Ok(())
231 }
232 }
233 }
234
235 /// Visit the right-hand-side of a `&&`. Used for if-let chains. Returns `Some` if the
236 /// expression was ultimately a `let ... = ...`, and `None` if it was a normal boolean
237 /// expression. This must call `visit_expr` on the subexpressions we are not handling ourselves.
238 fn visit_land_rhs(
239 &mut self,
240 ex: &'p Expr<'tcx>,
241 ) -> Result<Option<(Span, RefutableFlag)>, ErrorGuaranteed> {
242 match ex.kind {
243 ExprKind::Scope { value, lint_level, .. } => {
244 self.with_lint_level(lint_level, |this| this.visit_land_rhs(&this.thir[value]))
245 }
246 ExprKind::Let { box ref pat, expr } => {
247 let expr = &self.thir()[expr];
248 self.with_let_source(LetSource::None, |this| {
249 this.visit_expr(expr);
250 });
251 Ok(Some((ex.span, self.is_let_irrefutable(pat, Some(expr))?)))
252 }
253 _ => {
254 self.with_let_source(LetSource::None, |this| {
255 this.visit_expr(ex);
256 });
257 Ok(None)
258 }
259 }
260 }
261
262 fn lower_pattern(
263 &mut self,
264 cx: &PatCtxt<'p, 'tcx>,
265 pat: &'p Pat<'tcx>,
266 ) -> Result<&'p DeconstructedPat<'p, 'tcx>, ErrorGuaranteed> {
267 if let Err(err) = pat.pat_error_reported() {
268 self.error = Err(err);
269 Err(err)
270 } else {
271 // Check the pattern for some things unrelated to exhaustiveness.
272 let refutable = if cx.refutable { Refutable } else { Irrefutable };
273 let mut err = Ok(());
274 pat.walk_always(|pat| {
275 check_borrow_conflicts_in_at_patterns(self, pat);
276 check_for_bindings_named_same_as_variants(self, pat, refutable);
277 err = err.and(check_never_pattern(cx, pat));
278 });
279 err?;
280 Ok(self.pattern_arena.alloc(cx.lower_pat(pat)))
281 }
282 }
283
284 /// Inspects the match scrutinee expression to determine whether the place it evaluates to may
285 /// hold invalid data.
286 fn is_known_valid_scrutinee(&self, scrutinee: &Expr<'tcx>) -> bool {
287 use ExprKind::*;
288 match &scrutinee.kind {
289 // Pointers can validly point to a place with invalid data. It is undecided whether
290 // references can too, so we conservatively assume they can.
291 Deref { .. } => false,
292 // Inherit validity of the parent place, unless the parent is an union.
293 Field { lhs, .. } => {
294 let lhs = &self.thir()[*lhs];
295 match lhs.ty.kind() {
296 ty::Adt(def, _) if def.is_union() => false,
297 _ => self.is_known_valid_scrutinee(lhs),
298 }
299 }
300 // Essentially a field access.
301 Index { lhs, .. } => {
302 let lhs = &self.thir()[*lhs];
303 self.is_known_valid_scrutinee(lhs)
304 }
305
306 // No-op.
307 Scope { value, .. } => self.is_known_valid_scrutinee(&self.thir()[*value]),
308
309 // Casts don't cause a load.
310 NeverToAny { source }
311 | Cast { source }
312 | Use { source }
313 | PointerCoercion { source, .. }
314 | PlaceTypeAscription { source, .. }
315 | ValueTypeAscription { source, .. } => {
316 self.is_known_valid_scrutinee(&self.thir()[*source])
317 }
318
319 // These diverge.
320 Become { .. } | Break { .. } | Continue { .. } | Return { .. } => true,
321
322 // These are statements that evaluate to `()`.
323 Assign { .. } | AssignOp { .. } | InlineAsm { .. } | Let { .. } => true,
324
325 // These evaluate to a value.
326 AddressOf { .. }
327 | Adt { .. }
328 | Array { .. }
329 | Binary { .. }
330 | Block { .. }
331 | Borrow { .. }
332 | Box { .. }
333 | Call { .. }
334 | Closure { .. }
335 | ConstBlock { .. }
336 | ConstParam { .. }
337 | If { .. }
338 | Literal { .. }
339 | LogicalOp { .. }
340 | Loop { .. }
341 | Match { .. }
342 | NamedConst { .. }
343 | NonHirLiteral { .. }
344 | OffsetOf { .. }
345 | Repeat { .. }
346 | StaticRef { .. }
347 | ThreadLocalRef { .. }
348 | Tuple { .. }
349 | Unary { .. }
350 | UpvarRef { .. }
351 | VarRef { .. }
352 | ZstLiteral { .. }
353 | Yield { .. } => true,
354 }
355 }
356
357 fn new_cx(
358 &self,
359 refutability: RefutableFlag,
360 whole_match_span: Option<Span>,
361 scrutinee: Option<&Expr<'tcx>>,
362 scrut_span: Span,
363 ) -> PatCtxt<'p, 'tcx> {
364 let refutable = match refutability {
365 Irrefutable => false,
366 Refutable => true,
367 };
368 // If we don't have a scrutinee we're either a function parameter or a `let x;`. Both cases
369 // require validity.
370 let known_valid_scrutinee =
371 scrutinee.map(|scrut| self.is_known_valid_scrutinee(scrut)).unwrap_or(true);
372 PatCtxt {
373 tcx: self.tcx,
374 typeck_results: self.typeck_results,
375 param_env: self.param_env,
376 module: self.tcx.parent_module(self.lint_level).to_def_id(),
377 dropless_arena: self.dropless_arena,
378 match_lint_level: self.lint_level,
379 whole_match_span,
380 scrut_span,
381 refutable,
382 known_valid_scrutinee,
383 }
384 }
385
386 fn analyze_patterns(
387 &mut self,
388 cx: &PatCtxt<'p, 'tcx>,
389 arms: &[MatchArm<'p, 'tcx>],
390 scrut_ty: Ty<'tcx>,
391 ) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
392 let pattern_complexity_limit =
393 get_limit_size(cx.tcx.hir().krate_attrs(), cx.tcx.sess, sym::pattern_complexity);
394 let report =
395 rustc_pattern_analysis::analyze_match(&cx, &arms, scrut_ty, pattern_complexity_limit)
396 .map_err(|err| {
397 self.error = Err(err);
398 err
399 })?;
400
401 // Warn unreachable subpatterns.
402 for (arm, is_useful) in report.arm_usefulness.iter() {
403 if let Usefulness::Useful(redundant_subpats) = is_useful
404 && !redundant_subpats.is_empty()
405 {
406 let mut redundant_subpats = redundant_subpats.clone();
407 // Emit lints in the order in which they occur in the file.
408 redundant_subpats.sort_unstable_by_key(|pat| pat.data().span);
409 for pat in redundant_subpats {
410 report_unreachable_pattern(cx, arm.arm_data, pat.data().span, None)
411 }
412 }
413 }
414 Ok(report)
415 }
416
417 #[instrument(level = "trace", skip(self))]
418 fn check_let(&mut self, pat: &'p Pat<'tcx>, scrutinee: Option<ExprId>, span: Span) {
419 assert!(self.let_source != LetSource::None);
420 let scrut = scrutinee.map(|id| &self.thir[id]);
421 if let LetSource::PlainLet = self.let_source {
422 self.check_binding_is_irrefutable(pat, "local binding", scrut, Some(span))
423 } else {
424 let Ok(refutability) = self.is_let_irrefutable(pat, scrut) else { return };
425 if matches!(refutability, Irrefutable) {
426 report_irrefutable_let_patterns(
427 self.tcx,
428 self.lint_level,
429 self.let_source,
430 1,
431 span,
432 );
433 }
434 }
435 }
436
437 fn check_match(
438 &mut self,
439 scrut: ExprId,
440 arms: &[ArmId],
441 source: hir::MatchSource,
442 expr_span: Span,
443 ) {
444 let scrut = &self.thir[scrut];
445 let cx = self.new_cx(Refutable, Some(expr_span), Some(scrut), scrut.span);
446
447 let mut tarms = Vec::with_capacity(arms.len());
448 for &arm in arms {
449 let arm = &self.thir.arms[arm];
450 let got_error = self.with_lint_level(arm.lint_level, |this| {
451 let Ok(pat) = this.lower_pattern(&cx, &arm.pattern) else { return true };
452 let arm =
453 MatchArm { pat, arm_data: this.lint_level, has_guard: arm.guard.is_some() };
454 tarms.push(arm);
455 false
456 });
457 if got_error {
458 return;
459 }
460 }
461
462 let Ok(report) = self.analyze_patterns(&cx, &tarms, scrut.ty) else { return };
463
464 match source {
465 // Don't report arm reachability of desugared `match $iter.into_iter() { iter => .. }`
466 // when the iterator is an uninhabited type. unreachable_code will trigger instead.
467 hir::MatchSource::ForLoopDesugar if arms.len() == 1 => {}
468 hir::MatchSource::ForLoopDesugar
469 | hir::MatchSource::Postfix
470 | hir::MatchSource::Normal
471 | hir::MatchSource::FormatArgs => report_arm_reachability(&cx, &report),
472 // Unreachable patterns in try and await expressions occur when one of
473 // the arms are an uninhabited type. Which is OK.
474 hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar(_) => {}
475 }
476
477 // Check if the match is exhaustive.
478 let witnesses = report.non_exhaustiveness_witnesses;
479 if !witnesses.is_empty() {
480 if source == hir::MatchSource::ForLoopDesugar && arms.len() == 2 {
481 // the for loop pattern is not irrefutable
482 let pat = &self.thir[arms[1]].pattern;
483 // `pat` should be `Some(<pat_field>)` from a desugared for loop.
484 debug_assert_eq!(pat.span.desugaring_kind(), Some(DesugaringKind::ForLoop));
485 let PatKind::Variant { ref subpatterns, .. } = pat.kind else { bug!() };
486 let [pat_field] = &subpatterns[..] else { bug!() };
487 self.check_binding_is_irrefutable(
488 &pat_field.pattern,
489 "`for` loop binding",
490 None,
491 None,
492 );
493 } else {
494 // span after scrutinee, or after `.match`. That is, the braces, arms,
495 // and any whitespace preceding the braces.
496 let braces_span = match source {
497 hir::MatchSource::Normal => scrut
498 .span
499 .find_ancestor_in_same_ctxt(expr_span)
500 .map(|scrut_span| scrut_span.shrink_to_hi().with_hi(expr_span.hi())),
501 hir::MatchSource::Postfix => {
502 // This is horrendous, and we should deal with it by just
503 // stashing the span of the braces somewhere (like in the match source).
504 scrut.span.find_ancestor_in_same_ctxt(expr_span).and_then(|scrut_span| {
505 let sm = self.tcx.sess.source_map();
506 let brace_span = sm.span_extend_to_next_char(scrut_span, '{', true);
507 if sm.span_to_snippet(sm.next_point(brace_span)).as_deref() == Ok("{") {
508 let sp = brace_span.shrink_to_hi().with_hi(expr_span.hi());
509 // We also need to extend backwards for whitespace
510 sm.span_extend_prev_while(sp, |c| c.is_whitespace()).ok()
511 } else {
512 None
513 }
514 })
515 }
516 hir::MatchSource::ForLoopDesugar
517 | hir::MatchSource::TryDesugar(_)
518 | hir::MatchSource::AwaitDesugar
519 | hir::MatchSource::FormatArgs => None,
520 };
521 self.error = Err(report_non_exhaustive_match(
522 &cx,
523 self.thir,
524 scrut.ty,
525 scrut.span,
526 witnesses,
527 arms,
528 braces_span,
529 ));
530 }
531 }
532 }
533
534 #[instrument(level = "trace", skip(self))]
535 fn check_let_chain(
536 &mut self,
537 chain_refutabilities: Vec<Option<(Span, RefutableFlag)>>,
538 whole_chain_span: Span,
539 ) {
540 assert!(self.let_source != LetSource::None);
541
542 if chain_refutabilities.iter().all(|r| matches!(*r, Some((_, Irrefutable)))) {
543 // The entire chain is made up of irrefutable `let` statements
544 report_irrefutable_let_patterns(
545 self.tcx,
546 self.lint_level,
547 self.let_source,
548 chain_refutabilities.len(),
549 whole_chain_span,
550 );
551 return;
552 }
553
554 if let Some(until) =
555 chain_refutabilities.iter().position(|r| !matches!(*r, Some((_, Irrefutable))))
556 && until > 0
557 {
558 // The chain has a non-zero prefix of irrefutable `let` statements.
559
560 // Check if the let source is while, for there is no alternative place to put a prefix,
561 // and we shouldn't lint.
562 // For let guards inside a match, prefixes might use bindings of the match pattern,
563 // so can't always be moved out.
564 // FIXME: Add checking whether the bindings are actually used in the prefix,
565 // and lint if they are not.
566 if !matches!(self.let_source, LetSource::WhileLet | LetSource::IfLetGuard) {
567 // Emit the lint
568 let prefix = &chain_refutabilities[..until];
569 let span_start = prefix[0].unwrap().0;
570 let span_end = prefix.last().unwrap().unwrap().0;
571 let span = span_start.to(span_end);
572 let count = prefix.len();
573 self.tcx.emit_node_span_lint(
574 IRREFUTABLE_LET_PATTERNS,
575 self.lint_level,
576 span,
577 LeadingIrrefutableLetPatterns { count },
578 );
579 }
580 }
581
582 if let Some(from) =
583 chain_refutabilities.iter().rposition(|r| !matches!(*r, Some((_, Irrefutable))))
584 && from != (chain_refutabilities.len() - 1)
585 {
586 // The chain has a non-empty suffix of irrefutable `let` statements
587 let suffix = &chain_refutabilities[from + 1..];
588 let span_start = suffix[0].unwrap().0;
589 let span_end = suffix.last().unwrap().unwrap().0;
590 let span = span_start.to(span_end);
591 let count = suffix.len();
592 self.tcx.emit_node_span_lint(
593 IRREFUTABLE_LET_PATTERNS,
594 self.lint_level,
595 span,
596 TrailingIrrefutableLetPatterns { count },
597 );
598 }
599 }
600
601 fn analyze_binding(
602 &mut self,
603 pat: &'p Pat<'tcx>,
604 refutability: RefutableFlag,
605 scrut: Option<&Expr<'tcx>>,
606 ) -> Result<(PatCtxt<'p, 'tcx>, UsefulnessReport<'p, 'tcx>), ErrorGuaranteed> {
607 let cx = self.new_cx(refutability, None, scrut, pat.span);
608 let pat = self.lower_pattern(&cx, pat)?;
609 let arms = [MatchArm { pat, arm_data: self.lint_level, has_guard: false }];
610 let report = self.analyze_patterns(&cx, &arms, pat.ty().inner())?;
611 Ok((cx, report))
612 }
613
614 fn is_let_irrefutable(
615 &mut self,
616 pat: &'p Pat<'tcx>,
617 scrut: Option<&Expr<'tcx>>,
618 ) -> Result<RefutableFlag, ErrorGuaranteed> {
619 let (cx, report) = self.analyze_binding(pat, Refutable, scrut)?;
620 // Report if the pattern is unreachable, which can only occur when the type is uninhabited.
621 report_arm_reachability(&cx, &report);
622 // If the list of witnesses is empty, the match is exhaustive, i.e. the `if let` pattern is
623 // irrefutable.
624 Ok(if report.non_exhaustiveness_witnesses.is_empty() { Irrefutable } else { Refutable })
625 }
626
627 #[instrument(level = "trace", skip(self))]
628 fn check_binding_is_irrefutable(
629 &mut self,
630 pat: &'p Pat<'tcx>,
631 origin: &str,
632 scrut: Option<&Expr<'tcx>>,
633 sp: Option<Span>,
634 ) {
635 let pattern_ty = pat.ty;
636
637 let Ok((cx, report)) = self.analyze_binding(pat, Irrefutable, scrut) else { return };
638 let witnesses = report.non_exhaustiveness_witnesses;
639 if witnesses.is_empty() {
640 // The pattern is irrefutable.
641 return;
642 }
643
644 let inform = sp.is_some().then_some(Inform);
645 let mut let_suggestion = None;
646 let mut misc_suggestion = None;
647 let mut interpreted_as_const = None;
648
649 if let PatKind::Constant { .. }
650 | PatKind::AscribeUserType {
651 subpattern: box Pat { kind: PatKind::Constant { .. }, .. },
652 ..
653 } = pat.kind
654 && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(pat.span)
655 {
656 // If the pattern to match is an integer literal:
657 if snippet.chars().all(|c| c.is_digit(10)) {
658 // Then give a suggestion, the user might've meant to create a binding instead.
659 misc_suggestion = Some(MiscPatternSuggestion::AttemptedIntegerLiteral {
660 start_span: pat.span.shrink_to_lo(),
661 });
662 } else if snippet.chars().all(|c| c.is_alphanumeric() || c == '_') {
663 interpreted_as_const =
664 Some(InterpretedAsConst { span: pat.span, variable: snippet });
665 }
666 }
667
668 if let Some(span) = sp
669 && self.tcx.sess.source_map().is_span_accessible(span)
670 && interpreted_as_const.is_none()
671 && scrut.is_some()
672 {
673 let mut bindings = vec![];
674 pat.each_binding(|name, _, _, _| bindings.push(name));
675
676 let semi_span = span.shrink_to_hi();
677 let start_span = span.shrink_to_lo();
678 let end_span = semi_span.shrink_to_lo();
679 let count = witnesses.len();
680
681 let_suggestion = Some(if bindings.is_empty() {
682 SuggestLet::If { start_span, semi_span, count }
683 } else {
684 SuggestLet::Else { end_span, count }
685 });
686 };
687
688 let adt_defined_here = report_adt_defined_here(self.tcx, pattern_ty, &witnesses, false);
689
690 // Emit an extra note if the first uncovered witness would be uninhabited
691 // if we disregard visibility.
692 let witness_1_is_privately_uninhabited = if (self.tcx.features().exhaustive_patterns
693 || self.tcx.features().min_exhaustive_patterns)
694 && let Some(witness_1) = witnesses.get(0)
695 && let ty::Adt(adt, args) = witness_1.ty().kind()
696 && adt.is_enum()
697 && let Constructor::Variant(variant_index) = witness_1.ctor()
698 {
699 let variant = adt.variant(*variant_index);
700 let inhabited = variant.inhabited_predicate(self.tcx, *adt).instantiate(self.tcx, args);
701 assert!(inhabited.apply(self.tcx, cx.param_env, cx.module));
702 !inhabited.apply_ignore_module(self.tcx, cx.param_env)
703 } else {
704 false
705 };
706
707 self.error = Err(self.tcx.dcx().emit_err(PatternNotCovered {
708 span: pat.span,
709 origin,
710 uncovered: Uncovered::new(pat.span, &cx, witnesses),
711 inform,
712 interpreted_as_const,
713 witness_1_is_privately_uninhabited: witness_1_is_privately_uninhabited.then_some(()),
714 _p: (),
715 pattern_ty,
716 let_suggestion,
717 misc_suggestion,
718 adt_defined_here,
719 }));
720 }
721}
722
723/// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.
724/// Check that there are no borrow or move conflicts in `binding @ subpat` patterns.
725///
726/// For example, this would reject:
727/// - `ref x @ Some(ref mut y)`,
728/// - `ref mut x @ Some(ref y)`,
729/// - `ref mut x @ Some(ref mut y)`,
730/// - `ref mut? x @ Some(y)`, and
731/// - `x @ Some(ref mut? y)`.
732///
733/// This analysis is *not* subsumed by NLL.
734fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: &Pat<'tcx>) {
735 // Extract `sub` in `binding @ sub`.
736 let PatKind::Binding { name, mode, ty, subpattern: Some(box ref sub), .. } = pat.kind else {
737 return;
738 };
739
740 let is_binding_by_move = |ty: Ty<'tcx>| !ty.is_copy_modulo_regions(cx.tcx, cx.param_env);
741
742 let sess = cx.tcx.sess;
743
744 // Get the binding move, extract the mutability if by-ref.
745 let mut_outer = match mode.0 {
746 ByRef::No if is_binding_by_move(ty) => {
747 // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`.
748 let mut conflicts_ref = Vec::new();
749 sub.each_binding(|_, mode, _, span| {
750 if matches!(mode, ByRef::Yes(_)) {
751 conflicts_ref.push(span)
752 }
753 });
754 if !conflicts_ref.is_empty() {
755 sess.dcx().emit_err(BorrowOfMovedValue {
756 binding_span: pat.span,
757 conflicts_ref,
758 name,
759 ty,
760 suggest_borrowing: Some(pat.span.shrink_to_lo()),
761 });
762 }
763 return;
764 }
765 ByRef::No => return,
766 ByRef::Yes(m) => m,
767 };
768
769 // We now have `ref $mut_outer binding @ sub` (semantically).
770 // Recurse into each binding in `sub` and find mutability or move conflicts.
771 let mut conflicts_move = Vec::new();
772 let mut conflicts_mut_mut = Vec::new();
773 let mut conflicts_mut_ref = Vec::new();
774 sub.each_binding(|name, mode, ty, span| {
775 match mode {
776 ByRef::Yes(mut_inner) => match (mut_outer, mut_inner) {
777 // Both sides are `ref`.
778 (Mutability::Not, Mutability::Not) => {}
779 // 2x `ref mut`.
780 (Mutability::Mut, Mutability::Mut) => {
781 conflicts_mut_mut.push(Conflict::Mut { span, name })
782 }
783 (Mutability::Not, Mutability::Mut) => {
784 conflicts_mut_ref.push(Conflict::Mut { span, name })
785 }
786 (Mutability::Mut, Mutability::Not) => {
787 conflicts_mut_ref.push(Conflict::Ref { span, name })
788 }
789 },
790 ByRef::No if is_binding_by_move(ty) => {
791 conflicts_move.push(Conflict::Moved { span, name }) // `ref mut?` + by-move conflict.
792 }
793 ByRef::No => {} // `ref mut?` + by-copy is fine.
794 }
795 });
796
797 let report_mut_mut = !conflicts_mut_mut.is_empty();
798 let report_mut_ref = !conflicts_mut_ref.is_empty();
799 let report_move_conflict = !conflicts_move.is_empty();
800
801 let mut occurrences = match mut_outer {
802 Mutability::Mut => vec![Conflict::Mut { span: pat.span, name }],
803 Mutability::Not => vec![Conflict::Ref { span: pat.span, name }],
804 };
805 occurrences.extend(conflicts_mut_mut);
806 occurrences.extend(conflicts_mut_ref);
807 occurrences.extend(conflicts_move);
808
809 // Report errors if any.
810 if report_mut_mut {
811 // Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
812 sess.dcx().emit_err(MultipleMutBorrows { span: pat.span, occurrences });
813 } else if report_mut_ref {
814 // Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
815 match mut_outer {
816 Mutability::Mut => {
817 sess.dcx().emit_err(AlreadyMutBorrowed { span: pat.span, occurrences });
818 }
819 Mutability::Not => {
820 sess.dcx().emit_err(AlreadyBorrowed { span: pat.span, occurrences });
821 }
822 };
823 } else if report_move_conflict {
824 // Report by-ref and by-move conflicts, e.g. `ref x @ y`.
825 sess.dcx().emit_err(MovedWhileBorrowed { span: pat.span, occurrences });
826 }
827}
828
829fn check_for_bindings_named_same_as_variants(
830 cx: &MatchVisitor<'_, '_>,
831 pat: &Pat<'_>,
832 rf: RefutableFlag,
833) {
834 if let PatKind::Binding {
835 name,
836 mode: BindingMode(ByRef::No, Mutability::Not),
837 subpattern: None,
838 ty,
839 ..
840 } = pat.kind
841 && let ty::Adt(edef, _) = ty.peel_refs().kind()
842 && edef.is_enum()
843 && edef
844 .variants()
845 .iter()
846 .any(|variant| variant.name == name && variant.ctor_kind() == Some(CtorKind::Const))
847 {
848 let variant_count = edef.variants().len();
849 let ty_path = with_no_trimmed_paths!(cx.tcx.def_path_str(edef.did()));
850 cx.tcx.emit_node_span_lint(
851 BINDINGS_WITH_VARIANT_NAME,
852 cx.lint_level,
853 pat.span,
854 BindingsWithVariantName {
855 // If this is an irrefutable pattern, and there's > 1 variant,
856 // then we can't actually match on this. Applying the below
857 // suggestion would produce code that breaks on `check_binding_is_irrefutable`.
858 suggestion: if rf == Refutable || variant_count == 1 {
859 Some(pat.span)
860 } else {
861 None
862 },
863 ty_path,
864 name,
865 },
866 )
867 }
868}
869
870/// Check that never patterns are only used on inhabited types.
871fn check_never_pattern<'tcx>(
872 cx: &PatCtxt<'_, 'tcx>,
873 pat: &Pat<'tcx>,
874) -> Result<(), ErrorGuaranteed> {
875 if let PatKind::Never = pat.kind {
876 if !cx.is_uninhabited(pat.ty) {
877 return Err(cx.tcx.dcx().emit_err(NonEmptyNeverPattern { span: pat.span, ty: pat.ty }));
878 }
879 }
880 Ok(())
881}
882
883fn report_irrefutable_let_patterns(
884 tcx: TyCtxt<'_>,
885 id: HirId,
886 source: LetSource,
887 count: usize,
888 span: Span,
889) {
890 macro_rules! emit_diag {
891 ($lint:tt) => {{
892 tcx.emit_node_span_lint(IRREFUTABLE_LET_PATTERNS, id, span, $lint { count });
893 }};
894 }
895
896 match source {
897 LetSource::None | LetSource::PlainLet => bug!(),
898 LetSource::IfLet => emit_diag!(IrrefutableLetPatternsIfLet),
899 LetSource::IfLetGuard => emit_diag!(IrrefutableLetPatternsIfLetGuard),
900 LetSource::LetElse => emit_diag!(IrrefutableLetPatternsLetElse),
901 LetSource::WhileLet => emit_diag!(IrrefutableLetPatternsWhileLet),
902 }
903}
904
905/// Report unreachable arms, if any.
906fn report_unreachable_pattern<'p, 'tcx>(
907 cx: &PatCtxt<'p, 'tcx>,
908 hir_id: HirId,
909 span: Span,
910 catchall: Option<Span>,
911) {
912 cx.tcx.emit_node_span_lint(
913 UNREACHABLE_PATTERNS,
914 hir_id,
915 span,
916 UnreachablePattern { span: if catchall.is_some() { Some(span) } else { None }, catchall },
917 );
918}
919
920/// Report unreachable arms, if any.
921fn report_arm_reachability<'p, 'tcx>(cx: &PatCtxt<'p, 'tcx>, report: &UsefulnessReport<'p, 'tcx>) {
922 let mut catchall = None;
923 for (arm, is_useful) in report.arm_usefulness.iter() {
924 if matches!(is_useful, Usefulness::Redundant) {
925 report_unreachable_pattern(cx, arm.arm_data, arm.pat.data().span, catchall)
926 }
927 if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) {
928 catchall = Some(arm.pat.data().span);
929 }
930 }
931}
932
933/// Checks for common cases of "catchall" patterns that may not be intended as such.
934fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
935 match pat.ctor() {
936 Constructor::Wildcard => true,
937 Constructor::Struct | Constructor::Ref => {
938 pat.iter_fields().all(|ipat| pat_is_catchall(&ipat.pat))
939 }
940 _ => false,
941 }
942}
943
944/// Report that a match is not exhaustive.
945fn report_non_exhaustive_match<'p, 'tcx>(
946 cx: &PatCtxt<'p, 'tcx>,
947 thir: &Thir<'tcx>,
948 scrut_ty: Ty<'tcx>,
949 sp: Span,
950 witnesses: Vec<WitnessPat<'p, 'tcx>>,
951 arms: &[ArmId],
952 braces_span: Option<Span>,
953) -> ErrorGuaranteed {
954 let is_empty_match = arms.is_empty();
955 let non_empty_enum = match scrut_ty.kind() {
956 ty::Adt(def, _) => def.is_enum() && !def.variants().is_empty(),
957 _ => false,
958 };
959 // In the case of an empty match, replace the '`_` not covered' diagnostic with something more
960 // informative.
961 if is_empty_match && !non_empty_enum {
962 return cx.tcx.dcx().emit_err(NonExhaustivePatternsTypeNotEmpty {
963 cx,
964 scrut_span: sp,
965 braces_span,
966 ty: scrut_ty,
967 });
968 }
969
970 // FIXME: migration of this diagnostic will require list support
971 let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
972 let mut err = struct_span_code_err!(
973 cx.tcx.dcx(),
974 sp,
975 E0004,
976 "non-exhaustive patterns: {joined_patterns} not covered"
977 );
978 err.span_label(
979 sp,
980 format!(
981 "pattern{} {} not covered",
982 rustc_errors::pluralize!(witnesses.len()),
983 joined_patterns
984 ),
985 );
986
987 // Point at the definition of non-covered `enum` variants.
988 if let Some(AdtDefinedHere { adt_def_span, ty, variants }) =
989 report_adt_defined_here(cx.tcx, scrut_ty, &witnesses, true)
990 {
991 let mut multi_span = MultiSpan::from_span(adt_def_span);
992 multi_span.push_span_label(adt_def_span, "");
993 for Variant { span } in variants {
994 multi_span.push_span_label(span, "not covered");
995 }
996 err.span_note(multi_span, format!("`{ty}` defined here"));
997 }
998 err.note(format!("the matched value is of type `{}`", scrut_ty));
999
1000 if !is_empty_match {
1001 let mut non_exhaustive_tys = FxIndexSet::default();
1002 // Look at the first witness.
1003 collect_non_exhaustive_tys(cx, &witnesses[0], &mut non_exhaustive_tys);
1004
1005 for ty in non_exhaustive_tys {
1006 if ty.is_ptr_sized_integral() {
1007 if ty == cx.tcx.types.usize {
1008 err.note(format!(
1009 "`{ty}` does not have a fixed maximum value, so half-open ranges are necessary to match \
1010 exhaustively",
1011 ));
1012 } else if ty == cx.tcx.types.isize {
1013 err.note(format!(
1014 "`{ty}` does not have fixed minimum and maximum values, so half-open ranges are necessary to match \
1015 exhaustively",
1016 ));
1017 }
1018 } else if ty == cx.tcx.types.str_ {
1019 err.note("`&str` cannot be matched exhaustively, so a wildcard `_` is necessary");
1020 } else if cx.is_foreign_non_exhaustive_enum(cx.reveal_opaque_ty(ty)) {
1021 err.note(format!("`{ty}` is marked as non-exhaustive, so a wildcard `_` is necessary to match exhaustively"));
1022 }
1023 }
1024 }
1025
1026 if let ty::Ref(_, sub_ty, _) = scrut_ty.kind() {
1027 if !sub_ty.is_inhabited_from(cx.tcx, cx.module, cx.param_env) {
1028 err.note("references are always considered inhabited");
1029 }
1030 }
1031
1032 // Whether we suggest the actual missing patterns or `_`.
1033 let suggest_the_witnesses = witnesses.len() < 4;
1034 let suggested_arm = if suggest_the_witnesses {
1035 let pattern = witnesses
1036 .iter()
1037 .map(|witness| cx.hoist_witness_pat(witness).to_string())
1038 .collect::<Vec<String>>()
1039 .join(" | ");
1040 if witnesses.iter().all(|p| p.is_never_pattern()) && cx.tcx.features().never_patterns {
1041 // Arms with a never pattern don't take a body.
1042 pattern
1043 } else {
1044 format!("{pattern} => todo!()")
1045 }
1046 } else {
1047 format!("_ => todo!()")
1048 };
1049 let mut suggestion = None;
1050 let sm = cx.tcx.sess.source_map();
1051 match arms {
1052 [] if let Some(braces_span) = braces_span => {
1053 // Get the span for the empty match body `{}`.
1054 let (indentation, more) = if let Some(snippet) = sm.indentation_before(sp) {
1055 (format!("\n{snippet}"), " ")
1056 } else {
1057 (" ".to_string(), "")
1058 };
1059 suggestion = Some((
1060 braces_span,
1061 format!(" {{{indentation}{more}{suggested_arm},{indentation}}}",),
1062 ));
1063 }
1064 [only] => {
1065 let only = &thir[*only];
1066 let (pre_indentation, is_multiline) = if let Some(snippet) =
1067 sm.indentation_before(only.span)
1068 && let Ok(with_trailing) =
1069 sm.span_extend_while(only.span, |c| c.is_whitespace() || c == ',')
1070 && sm.is_multiline(with_trailing)
1071 {
1072 (format!("\n{snippet}"), true)
1073 } else {
1074 (" ".to_string(), false)
1075 };
1076 let only_body = &thir[only.body];
1077 let comma = if matches!(only_body.kind, ExprKind::Block { .. })
1078 && only.span.eq_ctxt(only_body.span)
1079 && is_multiline
1080 {
1081 ""
1082 } else {
1083 ","
1084 };
1085 suggestion = Some((
1086 only.span.shrink_to_hi(),
1087 format!("{comma}{pre_indentation}{suggested_arm}"),
1088 ));
1089 }
1090 [.., prev, last] => {
1091 let prev = &thir[*prev];
1092 let last = &thir[*last];
1093 if prev.span.eq_ctxt(last.span) {
1094 let last_body = &thir[last.body];
1095 let comma = if matches!(last_body.kind, ExprKind::Block { .. })
1096 && last.span.eq_ctxt(last_body.span)
1097 {
1098 ""
1099 } else {
1100 ","
1101 };
1102 let spacing = if sm.is_multiline(prev.span.between(last.span)) {
1103 sm.indentation_before(last.span).map(|indent| format!("\n{indent}"))
1104 } else {
1105 Some(" ".to_string())
1106 };
1107 if let Some(spacing) = spacing {
1108 suggestion = Some((
1109 last.span.shrink_to_hi(),
1110 format!("{comma}{spacing}{suggested_arm}"),
1111 ));
1112 }
1113 }
1114 }
1115 _ => {}
1116 }
1117
1118 let msg = format!(
1119 "ensure that all possible cases are being handled by adding a match arm with a wildcard \
1120 pattern{}{}",
1121 if witnesses.len() > 1 && suggest_the_witnesses && suggestion.is_some() {
1122 ", a match arm with multiple or-patterns"
1123 } else {
1124 // we are either not suggesting anything, or suggesting `_`
1125 ""
1126 },
1127 match witnesses.len() {
1128 // non-exhaustive enum case
1129 0 if suggestion.is_some() => " as shown",
1130 0 => "",
1131 1 if suggestion.is_some() => " or an explicit pattern as shown",
1132 1 => " or an explicit pattern",
1133 _ if suggestion.is_some() => " as shown, or multiple match arms",
1134 _ => " or multiple match arms",
1135 },
1136 );
1137
1138 let all_arms_have_guards = arms.iter().all(|arm_id| thir[*arm_id].guard.is_some());
1139 if !is_empty_match && all_arms_have_guards {
1140 err.subdiagnostic(NonExhaustiveMatchAllArmsGuarded);
1141 }
1142 if let Some((span, sugg)) = suggestion {
1143 err.span_suggestion_verbose(span, msg, sugg, Applicability::HasPlaceholders);
1144 } else {
1145 err.help(msg);
1146 }
1147 err.emit()
1148}
1149
1150fn joined_uncovered_patterns<'p, 'tcx>(
1151 cx: &PatCtxt<'p, 'tcx>,
1152 witnesses: &[WitnessPat<'p, 'tcx>],
1153) -> String {
1154 const LIMIT: usize = 3;
1155 let pat_to_str = |pat: &WitnessPat<'p, 'tcx>| cx.hoist_witness_pat(pat).to_string();
1156 match witnesses {
1157 [] => bug!(),
1158 [witness] => format!("`{}`", cx.hoist_witness_pat(witness)),
1159 [head @ .., tail] if head.len() < LIMIT => {
1160 let head: Vec<_> = head.iter().map(pat_to_str).collect();
1161 format!("`{}` and `{}`", head.join("`, `"), cx.hoist_witness_pat(tail))
1162 }
1163 _ => {
1164 let (head, tail) = witnesses.split_at(LIMIT);
1165 let head: Vec<_> = head.iter().map(pat_to_str).collect();
1166 format!("`{}` and {} more", head.join("`, `"), tail.len())
1167 }
1168 }
1169}
1170
1171fn collect_non_exhaustive_tys<'tcx>(
1172 cx: &PatCtxt<'_, 'tcx>,
1173 pat: &WitnessPat<'_, 'tcx>,
1174 non_exhaustive_tys: &mut FxIndexSet<Ty<'tcx>>,
1175) {
1176 if matches!(pat.ctor(), Constructor::NonExhaustive) {
1177 non_exhaustive_tys.insert(pat.ty().inner());
1178 }
1179 if let Constructor::IntRange(range) = pat.ctor() {
1180 if cx.is_range_beyond_boundaries(range, *pat.ty()) {
1181 // The range denotes the values before `isize::MIN` or the values after `usize::MAX`/`isize::MAX`.
1182 non_exhaustive_tys.insert(pat.ty().inner());
1183 }
1184 }
1185 pat.iter_fields()
1186 .for_each(|field_pat| collect_non_exhaustive_tys(cx, field_pat, non_exhaustive_tys))
1187}
1188
1189fn report_adt_defined_here<'tcx>(
1190 tcx: TyCtxt<'tcx>,
1191 ty: Ty<'tcx>,
1192 witnesses: &[WitnessPat<'_, 'tcx>],
1193 point_at_non_local_ty: bool,
1194) -> Option<AdtDefinedHere<'tcx>> {
1195 let ty = ty.peel_refs();
1196 let ty::Adt(def, _) = ty.kind() else {
1197 return None;
1198 };
1199 let adt_def_span =
1200 tcx.hir().get_if_local(def.did()).and_then(|node| node.ident()).map(|ident| ident.span);
1201 let adt_def_span = if point_at_non_local_ty {
1202 adt_def_span.unwrap_or_else(|| tcx.def_span(def.did()))
1203 } else {
1204 adt_def_span?
1205 };
1206
1207 let mut variants = vec![];
1208 for span in maybe_point_at_variant(tcx, *def, witnesses.iter().take(5)) {
1209 variants.push(Variant { span });
1210 }
1211 Some(AdtDefinedHere { adt_def_span, ty, variants })
1212}
1213
1214fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'p>(
1215 tcx: TyCtxt<'tcx>,
1216 def: AdtDef<'tcx>,
1217 patterns: impl Iterator<Item = &'a WitnessPat<'p, 'tcx>>,
1218) -> Vec<Span> {
1219 let mut covered = vec![];
1220 for pattern in patterns {
1221 if let Constructor::Variant(variant_index) = pattern.ctor() {
1222 if let ty::Adt(this_def, _) = pattern.ty().kind()
1223 && this_def.did() != def.did()
1224 {
1225 continue;
1226 }
1227 let sp = def.variant(*variant_index).ident(tcx).span;
1228 if covered.contains(&sp) {
1229 // Don't point at variants that have already been covered due to other patterns to avoid
1230 // visual clutter.
1231 continue;
1232 }
1233 covered.push(sp);
1234 }
1235 covered.extend(maybe_point_at_variant(tcx, def, pattern.iter_fields()));
1236 }
1237 covered
1238}