• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use super::deconstruct_pat::{Constructor, DeconstructedPat};
2 use super::usefulness::{
3     compute_match_usefulness, MatchArm, MatchCheckCtxt, Reachability, UsefulnessReport,
4 };
5 
6 use crate::errors::*;
7 
8 use rustc_arena::TypedArena;
9 use rustc_ast::Mutability;
10 use rustc_data_structures::stack::ensure_sufficient_stack;
11 use rustc_errors::{
12     struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
13 };
14 use rustc_hir as hir;
15 use rustc_hir::def::*;
16 use rustc_hir::def_id::LocalDefId;
17 use rustc_hir::HirId;
18 use rustc_middle::thir::visit::{self, Visitor};
19 use rustc_middle::thir::*;
20 use rustc_middle::ty::print::with_no_trimmed_paths;
21 use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
22 use rustc_session::lint::builtin::{
23     BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS,
24 };
25 use rustc_session::Session;
26 use rustc_span::hygiene::DesugaringKind;
27 use rustc_span::Span;
28 
check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed>29 pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> {
30     let (thir, expr) = tcx.thir_body(def_id)?;
31     let thir = thir.borrow();
32     let pattern_arena = TypedArena::default();
33     let mut visitor = MatchVisitor {
34         tcx,
35         thir: &*thir,
36         param_env: tcx.param_env(def_id),
37         lint_level: tcx.hir().local_def_id_to_hir_id(def_id),
38         let_source: LetSource::None,
39         pattern_arena: &pattern_arena,
40         error: Ok(()),
41     };
42     visitor.visit_expr(&thir[expr]);
43 
44     for param in thir.params.iter() {
45         if let Some(box ref pattern) = param.pat {
46             visitor.check_irrefutable(pattern, "function argument", None);
47         }
48     }
49     visitor.error
50 }
51 
create_e0004( sess: &Session, sp: Span, error_message: String, ) -> DiagnosticBuilder<'_, ErrorGuaranteed>52 fn create_e0004(
53     sess: &Session,
54     sp: Span,
55     error_message: String,
56 ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
57     struct_span_err!(sess, sp, E0004, "{}", &error_message)
58 }
59 
60 #[derive(PartialEq)]
61 enum RefutableFlag {
62     Irrefutable,
63     Refutable,
64 }
65 use RefutableFlag::*;
66 
67 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
68 enum LetSource {
69     None,
70     IfLet,
71     IfLetGuard,
72     LetElse,
73     WhileLet,
74 }
75 
76 struct MatchVisitor<'a, 'p, 'tcx> {
77     tcx: TyCtxt<'tcx>,
78     param_env: ty::ParamEnv<'tcx>,
79     thir: &'a Thir<'tcx>,
80     lint_level: HirId,
81     let_source: LetSource,
82     pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
83     error: Result<(), ErrorGuaranteed>,
84 }
85 
86 impl<'a, 'tcx> Visitor<'a, 'tcx> for MatchVisitor<'a, '_, 'tcx> {
thir(&self) -> &'a Thir<'tcx>87     fn thir(&self) -> &'a Thir<'tcx> {
88         self.thir
89     }
90 
91     #[instrument(level = "trace", skip(self))]
visit_arm(&mut self, arm: &Arm<'tcx>)92     fn visit_arm(&mut self, arm: &Arm<'tcx>) {
93         self.with_lint_level(arm.lint_level, |this| {
94             match arm.guard {
95                 Some(Guard::If(expr)) => {
96                     this.with_let_source(LetSource::IfLetGuard, |this| {
97                         this.visit_expr(&this.thir[expr])
98                     });
99                 }
100                 Some(Guard::IfLet(ref pat, expr)) => {
101                     this.with_let_source(LetSource::IfLetGuard, |this| {
102                         this.check_let(pat, expr, LetSource::IfLetGuard, pat.span);
103                         this.visit_pat(pat);
104                         this.visit_expr(&this.thir[expr]);
105                     });
106                 }
107                 None => {}
108             }
109             this.visit_pat(&arm.pattern);
110             this.visit_expr(&self.thir[arm.body]);
111         });
112     }
113 
114     #[instrument(level = "trace", skip(self))]
visit_expr(&mut self, ex: &Expr<'tcx>)115     fn visit_expr(&mut self, ex: &Expr<'tcx>) {
116         match ex.kind {
117             ExprKind::Scope { value, lint_level, .. } => {
118                 self.with_lint_level(lint_level, |this| {
119                     this.visit_expr(&this.thir[value]);
120                 });
121                 return;
122             }
123             ExprKind::If { cond, then, else_opt, if_then_scope: _ } => {
124                 // Give a specific `let_source` for the condition.
125                 let let_source = match ex.span.desugaring_kind() {
126                     Some(DesugaringKind::WhileLoop) => LetSource::WhileLet,
127                     _ => LetSource::IfLet,
128                 };
129                 self.with_let_source(let_source, |this| this.visit_expr(&self.thir[cond]));
130                 self.with_let_source(LetSource::None, |this| {
131                     this.visit_expr(&this.thir[then]);
132                     if let Some(else_) = else_opt {
133                         this.visit_expr(&this.thir[else_]);
134                     }
135                 });
136                 return;
137             }
138             ExprKind::Match { scrutinee, box ref arms } => {
139                 let source = match ex.span.desugaring_kind() {
140                     Some(DesugaringKind::ForLoop) => hir::MatchSource::ForLoopDesugar,
141                     Some(DesugaringKind::QuestionMark) => hir::MatchSource::TryDesugar,
142                     Some(DesugaringKind::Await) => hir::MatchSource::AwaitDesugar,
143                     _ => hir::MatchSource::Normal,
144                 };
145                 self.check_match(scrutinee, arms, source, ex.span);
146             }
147             ExprKind::Let { box ref pat, expr } => {
148                 self.check_let(pat, expr, self.let_source, ex.span);
149             }
150             ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => {
151                 self.check_let_chain(self.let_source, ex.span, lhs, rhs);
152             }
153             _ => {}
154         };
155         self.with_let_source(LetSource::None, |this| visit::walk_expr(this, ex));
156     }
157 
visit_stmt(&mut self, stmt: &Stmt<'tcx>)158     fn visit_stmt(&mut self, stmt: &Stmt<'tcx>) {
159         let old_lint_level = self.lint_level;
160         match stmt.kind {
161             StmtKind::Let {
162                 box ref pattern, initializer, else_block, lint_level, span, ..
163             } => {
164                 if let LintLevel::Explicit(lint_level) = lint_level {
165                     self.lint_level = lint_level;
166                 }
167 
168                 if let Some(initializer) = initializer && else_block.is_some() {
169                     self.check_let(pattern, initializer, LetSource::LetElse, span);
170                 }
171 
172                 if else_block.is_none() {
173                     self.check_irrefutable(pattern, "local binding", Some(span));
174                 }
175             }
176             _ => {}
177         }
178         visit::walk_stmt(self, stmt);
179         self.lint_level = old_lint_level;
180     }
181 }
182 
183 impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
184     #[instrument(level = "trace", skip(self, f))]
with_let_source(&mut self, let_source: LetSource, f: impl FnOnce(&mut Self))185     fn with_let_source(&mut self, let_source: LetSource, f: impl FnOnce(&mut Self)) {
186         let old_let_source = self.let_source;
187         self.let_source = let_source;
188         ensure_sufficient_stack(|| f(self));
189         self.let_source = old_let_source;
190     }
191 
with_lint_level(&mut self, new_lint_level: LintLevel, f: impl FnOnce(&mut Self))192     fn with_lint_level(&mut self, new_lint_level: LintLevel, f: impl FnOnce(&mut Self)) {
193         if let LintLevel::Explicit(hir_id) = new_lint_level {
194             let old_lint_level = self.lint_level;
195             self.lint_level = hir_id;
196             f(self);
197             self.lint_level = old_lint_level;
198         } else {
199             f(self);
200         }
201     }
202 
check_patterns(&self, pat: &Pat<'tcx>, rf: RefutableFlag)203     fn check_patterns(&self, pat: &Pat<'tcx>, rf: RefutableFlag) {
204         pat.walk_always(|pat| check_borrow_conflicts_in_at_patterns(self, pat));
205         check_for_bindings_named_same_as_variants(self, pat, rf);
206     }
207 
lower_pattern( &self, cx: &mut MatchCheckCtxt<'p, 'tcx>, pattern: &Pat<'tcx>, ) -> &'p DeconstructedPat<'p, 'tcx>208     fn lower_pattern(
209         &self,
210         cx: &mut MatchCheckCtxt<'p, 'tcx>,
211         pattern: &Pat<'tcx>,
212     ) -> &'p DeconstructedPat<'p, 'tcx> {
213         cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, &pattern))
214     }
215 
new_cx(&self, hir_id: HirId, refutable: bool) -> MatchCheckCtxt<'p, 'tcx>216     fn new_cx(&self, hir_id: HirId, refutable: bool) -> MatchCheckCtxt<'p, 'tcx> {
217         MatchCheckCtxt {
218             tcx: self.tcx,
219             param_env: self.param_env,
220             module: self.tcx.parent_module(hir_id).to_def_id(),
221             pattern_arena: &self.pattern_arena,
222             refutable,
223         }
224     }
225 
226     #[instrument(level = "trace", skip(self))]
check_let(&mut self, pat: &Pat<'tcx>, scrutinee: ExprId, source: LetSource, span: Span)227     fn check_let(&mut self, pat: &Pat<'tcx>, scrutinee: ExprId, source: LetSource, span: Span) {
228         if let LetSource::None = source {
229             return;
230         }
231         self.check_patterns(pat, Refutable);
232         let mut cx = self.new_cx(self.lint_level, true);
233         let tpat = self.lower_pattern(&mut cx, pat);
234         self.check_let_reachability(&mut cx, self.lint_level, source, tpat, span);
235     }
236 
check_match( &mut self, scrut: ExprId, arms: &[ArmId], source: hir::MatchSource, expr_span: Span, )237     fn check_match(
238         &mut self,
239         scrut: ExprId,
240         arms: &[ArmId],
241         source: hir::MatchSource,
242         expr_span: Span,
243     ) {
244         let mut cx = self.new_cx(self.lint_level, true);
245 
246         for &arm in arms {
247             // Check the arm for some things unrelated to exhaustiveness.
248             let arm = &self.thir.arms[arm];
249             self.with_lint_level(arm.lint_level, |this| {
250                 this.check_patterns(&arm.pattern, Refutable);
251             });
252         }
253 
254         let tarms: Vec<_> = arms
255             .iter()
256             .map(|&arm| {
257                 let arm = &self.thir.arms[arm];
258                 let hir_id = match arm.lint_level {
259                     LintLevel::Explicit(hir_id) => hir_id,
260                     LintLevel::Inherited => self.lint_level,
261                 };
262                 let pat = self.lower_pattern(&mut cx, &arm.pattern);
263                 MatchArm { pat, hir_id, has_guard: arm.guard.is_some() }
264             })
265             .collect();
266 
267         let scrut = &self.thir[scrut];
268         let scrut_ty = scrut.ty;
269         let report = compute_match_usefulness(&cx, &tarms, self.lint_level, scrut_ty);
270 
271         match source {
272             // Don't report arm reachability of desugared `match $iter.into_iter() { iter => .. }`
273             // when the iterator is an uninhabited type. unreachable_code will trigger instead.
274             hir::MatchSource::ForLoopDesugar if arms.len() == 1 => {}
275             hir::MatchSource::ForLoopDesugar
276             | hir::MatchSource::Normal
277             | hir::MatchSource::FormatArgs => report_arm_reachability(&cx, &report),
278             // Unreachable patterns in try and await expressions occur when one of
279             // the arms are an uninhabited type. Which is OK.
280             hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
281         }
282 
283         // Check if the match is exhaustive.
284         let witnesses = report.non_exhaustiveness_witnesses;
285         if !witnesses.is_empty() {
286             if source == hir::MatchSource::ForLoopDesugar && arms.len() == 2 {
287                 // the for loop pattern is not irrefutable
288                 let pat = &self.thir[arms[1]].pattern;
289                 // `pat` should be `Some(<pat_field>)` from a desugared for loop.
290                 debug_assert_eq!(pat.span.desugaring_kind(), Some(DesugaringKind::ForLoop));
291                 let PatKind::Variant { ref subpatterns, .. } = pat.kind else { bug!() };
292                 let [pat_field] = &subpatterns[..] else { bug!() };
293                 self.check_irrefutable(&pat_field.pattern, "`for` loop binding", None);
294             } else {
295                 self.error = Err(non_exhaustive_match(
296                     &cx, self.thir, scrut_ty, scrut.span, witnesses, arms, expr_span,
297                 ));
298             }
299         }
300     }
301 
check_let_reachability( &mut self, cx: &mut MatchCheckCtxt<'p, 'tcx>, pat_id: HirId, source: LetSource, pat: &'p DeconstructedPat<'p, 'tcx>, span: Span, )302     fn check_let_reachability(
303         &mut self,
304         cx: &mut MatchCheckCtxt<'p, 'tcx>,
305         pat_id: HirId,
306         source: LetSource,
307         pat: &'p DeconstructedPat<'p, 'tcx>,
308         span: Span,
309     ) {
310         if is_let_irrefutable(cx, pat_id, pat) {
311             irrefutable_let_patterns(cx.tcx, pat_id, source, 1, span);
312         }
313     }
314 
315     #[instrument(level = "trace", skip(self))]
check_let_chain( &mut self, let_source: LetSource, top_expr_span: Span, mut lhs: ExprId, rhs: ExprId, )316     fn check_let_chain(
317         &mut self,
318         let_source: LetSource,
319         top_expr_span: Span,
320         mut lhs: ExprId,
321         rhs: ExprId,
322     ) {
323         if let LetSource::None = let_source {
324             return;
325         }
326 
327         // Lint level enclosing the next `lhs`.
328         let mut cur_lint_level = self.lint_level;
329 
330         // Obtain the refutabilities of all exprs in the chain,
331         // and record chain members that aren't let exprs.
332         let mut chain_refutabilities = Vec::new();
333 
334         let add = |expr: ExprId, mut local_lint_level| {
335             // `local_lint_level` is the lint level enclosing the pattern inside `expr`.
336             let mut expr = &self.thir[expr];
337             debug!(?expr, ?local_lint_level, "add");
338             // Fast-forward through scopes.
339             while let ExprKind::Scope { value, lint_level, .. } = expr.kind {
340                 if let LintLevel::Explicit(hir_id) = lint_level {
341                     local_lint_level = hir_id
342                 }
343                 expr = &self.thir[value];
344             }
345             debug!(?expr, ?local_lint_level, "after scopes");
346             match expr.kind {
347                 ExprKind::Let { box ref pat, expr: _ } => {
348                     let mut ncx = self.new_cx(local_lint_level, true);
349                     let tpat = self.lower_pattern(&mut ncx, pat);
350                     let refutable = !is_let_irrefutable(&mut ncx, local_lint_level, tpat);
351                     Some((expr.span, refutable))
352                 }
353                 _ => None,
354             }
355         };
356 
357         // Let chains recurse on the left, so we start by adding the rightmost.
358         chain_refutabilities.push(add(rhs, cur_lint_level));
359 
360         loop {
361             while let ExprKind::Scope { value, lint_level, .. } = self.thir[lhs].kind {
362                 if let LintLevel::Explicit(hir_id) = lint_level {
363                     cur_lint_level = hir_id
364                 }
365                 lhs = value;
366             }
367             if let ExprKind::LogicalOp { op: LogicalOp::And, lhs: new_lhs, rhs: expr } =
368                 self.thir[lhs].kind
369             {
370                 chain_refutabilities.push(add(expr, cur_lint_level));
371                 lhs = new_lhs;
372             } else {
373                 chain_refutabilities.push(add(lhs, cur_lint_level));
374                 break;
375             }
376         }
377         debug!(?chain_refutabilities);
378         chain_refutabilities.reverse();
379 
380         // Third, emit the actual warnings.
381         if chain_refutabilities.iter().all(|r| matches!(*r, Some((_, false)))) {
382             // The entire chain is made up of irrefutable `let` statements
383             irrefutable_let_patterns(
384                 self.tcx,
385                 self.lint_level,
386                 let_source,
387                 chain_refutabilities.len(),
388                 top_expr_span,
389             );
390             return;
391         }
392 
393         if let Some(until) = chain_refutabilities.iter().position(|r| !matches!(*r, Some((_, false)))) && until > 0 {
394             // The chain has a non-zero prefix of irrefutable `let` statements.
395 
396             // Check if the let source is while, for there is no alternative place to put a prefix,
397             // and we shouldn't lint.
398             // For let guards inside a match, prefixes might use bindings of the match pattern,
399             // so can't always be moved out.
400             // FIXME: Add checking whether the bindings are actually used in the prefix,
401             // and lint if they are not.
402             if !matches!(let_source, LetSource::WhileLet | LetSource::IfLetGuard) {
403                 // Emit the lint
404                 let prefix = &chain_refutabilities[..until];
405                 let span_start = prefix[0].unwrap().0;
406                 let span_end = prefix.last().unwrap().unwrap().0;
407                 let span = span_start.to(span_end);
408                 let count = prefix.len();
409                 self.tcx.emit_spanned_lint(IRREFUTABLE_LET_PATTERNS, self.lint_level, span, LeadingIrrefutableLetPatterns { count });
410             }
411         }
412 
413         if let Some(from) = chain_refutabilities.iter().rposition(|r| !matches!(*r, Some((_, false)))) && from != (chain_refutabilities.len() - 1) {
414             // The chain has a non-empty suffix of irrefutable `let` statements
415             let suffix = &chain_refutabilities[from + 1..];
416             let span_start = suffix[0].unwrap().0;
417             let span_end = suffix.last().unwrap().unwrap().0;
418             let span = span_start.to(span_end);
419             let count = suffix.len();
420             self.tcx.emit_spanned_lint(IRREFUTABLE_LET_PATTERNS, self.lint_level, span, TrailingIrrefutableLetPatterns { count });
421         }
422     }
423 
424     #[instrument(level = "trace", skip(self))]
check_irrefutable(&mut self, pat: &Pat<'tcx>, origin: &str, sp: Option<Span>)425     fn check_irrefutable(&mut self, pat: &Pat<'tcx>, origin: &str, sp: Option<Span>) {
426         let mut cx = self.new_cx(self.lint_level, false);
427 
428         let pattern = self.lower_pattern(&mut cx, pat);
429         let pattern_ty = pattern.ty();
430         let arm = MatchArm { pat: pattern, hir_id: self.lint_level, has_guard: false };
431         let report = compute_match_usefulness(&cx, &[arm], self.lint_level, pattern_ty);
432 
433         // Note: we ignore whether the pattern is unreachable (i.e. whether the type is empty). We
434         // only care about exhaustiveness here.
435         let witnesses = report.non_exhaustiveness_witnesses;
436         if witnesses.is_empty() {
437             // The pattern is irrefutable.
438             self.check_patterns(pat, Irrefutable);
439             return;
440         }
441 
442         let inform = sp.is_some().then_some(Inform);
443         let mut let_suggestion = None;
444         let mut misc_suggestion = None;
445         let mut interpreted_as_const = None;
446 
447         if let PatKind::Constant { .. }
448             | PatKind::AscribeUserType {
449                 subpattern: box Pat { kind: PatKind::Constant { .. }, .. },
450                 ..
451               } = pat.kind
452             && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(pat.span)
453         {
454             // If the pattern to match is an integer literal:
455             if snippet.chars().all(|c| c.is_digit(10)) {
456                 // Then give a suggestion, the user might've meant to create a binding instead.
457                 misc_suggestion = Some(MiscPatternSuggestion::AttemptedIntegerLiteral {
458                     start_span: pat.span.shrink_to_lo()
459                 });
460             } else if snippet.chars().all(|c| c.is_alphanumeric() || c == '_') {
461                 interpreted_as_const = Some(InterpretedAsConst {
462                     span: pat.span,
463                     variable: snippet,
464                 });
465             }
466         }
467 
468         if let Some(span) = sp
469             && self.tcx.sess.source_map().is_span_accessible(span)
470             && interpreted_as_const.is_none()
471         {
472             let mut bindings = vec![];
473             pat.each_binding(|name, _, _, _| bindings.push(name));
474 
475             let semi_span = span.shrink_to_hi();
476             let start_span = span.shrink_to_lo();
477             let end_span = semi_span.shrink_to_lo();
478             let count = witnesses.len();
479 
480             let_suggestion = Some(if bindings.is_empty() {
481                 SuggestLet::If { start_span, semi_span, count }
482             } else {
483                 SuggestLet::Else { end_span, count }
484             });
485         };
486 
487         let adt_defined_here = try {
488             let ty = pattern_ty.peel_refs();
489             let ty::Adt(def, _) = ty.kind() else { None? };
490             let adt_def_span = cx.tcx.hir().get_if_local(def.did())?.ident()?.span;
491             let mut variants = vec![];
492 
493             for span in maybe_point_at_variant(&cx, *def, witnesses.iter().take(5)) {
494                 variants.push(Variant { span });
495             }
496             AdtDefinedHere { adt_def_span, ty, variants }
497         };
498 
499         // Emit an extra note if the first uncovered witness would be uninhabited
500         // if we disregard visibility.
501         let witness_1_is_privately_uninhabited =
502             if cx.tcx.features().exhaustive_patterns
503                 && let Some(witness_1) = witnesses.get(0)
504                 && let ty::Adt(adt, substs) = witness_1.ty().kind()
505                 && adt.is_enum()
506                 && let Constructor::Variant(variant_index) = witness_1.ctor()
507             {
508                 let variant = adt.variant(*variant_index);
509                 let inhabited = variant.inhabited_predicate(cx.tcx, *adt).subst(cx.tcx, substs);
510                 assert!(inhabited.apply(cx.tcx, cx.param_env, cx.module));
511                 !inhabited.apply_ignore_module(cx.tcx, cx.param_env)
512             } else {
513                 false
514             };
515 
516         self.error = Err(self.tcx.sess.emit_err(PatternNotCovered {
517             span: pat.span,
518             origin,
519             uncovered: Uncovered::new(pat.span, &cx, witnesses),
520             inform,
521             interpreted_as_const,
522             witness_1_is_privately_uninhabited: witness_1_is_privately_uninhabited.then_some(()),
523             _p: (),
524             pattern_ty,
525             let_suggestion,
526             misc_suggestion,
527             adt_defined_here,
528         }));
529     }
530 }
531 
check_for_bindings_named_same_as_variants( cx: &MatchVisitor<'_, '_, '_>, pat: &Pat<'_>, rf: RefutableFlag, )532 fn check_for_bindings_named_same_as_variants(
533     cx: &MatchVisitor<'_, '_, '_>,
534     pat: &Pat<'_>,
535     rf: RefutableFlag,
536 ) {
537     pat.walk_always(|p| {
538         if let PatKind::Binding {
539                 name,
540                 mode: BindingMode::ByValue,
541                 mutability: Mutability::Not,
542                 subpattern: None,
543                 ty,
544                 ..
545             } = p.kind
546             && let ty::Adt(edef, _) = ty.peel_refs().kind()
547             && edef.is_enum()
548             && edef.variants().iter().any(|variant| {
549                 variant.name == name && variant.ctor_kind() == Some(CtorKind::Const)
550             })
551         {
552             let variant_count = edef.variants().len();
553             let ty_path = with_no_trimmed_paths!({
554                 cx.tcx.def_path_str(edef.did())
555             });
556             cx.tcx.emit_spanned_lint(
557                 BINDINGS_WITH_VARIANT_NAME,
558                 cx.lint_level,
559                 p.span,
560                 BindingsWithVariantName {
561                     // If this is an irrefutable pattern, and there's > 1 variant,
562                     // then we can't actually match on this. Applying the below
563                     // suggestion would produce code that breaks on `check_irrefutable`.
564                     suggestion: if rf == Refutable || variant_count == 1 {
565                         Some(p.span)
566                     } else { None },
567                     ty_path,
568                     name,
569                 },
570             )
571         }
572     });
573 }
574 
575 /// Checks for common cases of "catchall" patterns that may not be intended as such.
pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool576 fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
577     use Constructor::*;
578     match pat.ctor() {
579         Wildcard => true,
580         Single => pat.iter_fields().all(|pat| pat_is_catchall(pat)),
581         _ => false,
582     }
583 }
584 
unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<Span>)585 fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<Span>) {
586     tcx.emit_spanned_lint(
587         UNREACHABLE_PATTERNS,
588         id,
589         span,
590         UnreachablePattern { span: if catchall.is_some() { Some(span) } else { None }, catchall },
591     );
592 }
593 
irrefutable_let_patterns( tcx: TyCtxt<'_>, id: HirId, source: LetSource, count: usize, span: Span, )594 fn irrefutable_let_patterns(
595     tcx: TyCtxt<'_>,
596     id: HirId,
597     source: LetSource,
598     count: usize,
599     span: Span,
600 ) {
601     macro_rules! emit_diag {
602         ($lint:tt) => {{
603             tcx.emit_spanned_lint(IRREFUTABLE_LET_PATTERNS, id, span, $lint { count });
604         }};
605     }
606 
607     match source {
608         LetSource::None => bug!(),
609         LetSource::IfLet => emit_diag!(IrrefutableLetPatternsIfLet),
610         LetSource::IfLetGuard => emit_diag!(IrrefutableLetPatternsIfLetGuard),
611         LetSource::LetElse => emit_diag!(IrrefutableLetPatternsLetElse),
612         LetSource::WhileLet => emit_diag!(IrrefutableLetPatternsWhileLet),
613     }
614 }
615 
is_let_irrefutable<'p, 'tcx>( cx: &mut MatchCheckCtxt<'p, 'tcx>, pat_id: HirId, pat: &'p DeconstructedPat<'p, 'tcx>, ) -> bool616 fn is_let_irrefutable<'p, 'tcx>(
617     cx: &mut MatchCheckCtxt<'p, 'tcx>,
618     pat_id: HirId,
619     pat: &'p DeconstructedPat<'p, 'tcx>,
620 ) -> bool {
621     let arms = [MatchArm { pat, hir_id: pat_id, has_guard: false }];
622     let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty());
623 
624     // Report if the pattern is unreachable, which can only occur when the type is uninhabited.
625     // This also reports unreachable sub-patterns though, so we can't just replace it with an
626     // `is_uninhabited` check.
627     report_arm_reachability(&cx, &report);
628 
629     // If the list of witnesses is empty, the match is exhaustive,
630     // i.e. the `if let` pattern is irrefutable.
631     report.non_exhaustiveness_witnesses.is_empty()
632 }
633 
634 /// Report unreachable arms, if any.
report_arm_reachability<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, report: &UsefulnessReport<'p, 'tcx>, )635 fn report_arm_reachability<'p, 'tcx>(
636     cx: &MatchCheckCtxt<'p, 'tcx>,
637     report: &UsefulnessReport<'p, 'tcx>,
638 ) {
639     use Reachability::*;
640     let mut catchall = None;
641     for (arm, is_useful) in report.arm_usefulness.iter() {
642         match is_useful {
643             Unreachable => unreachable_pattern(cx.tcx, arm.pat.span(), arm.hir_id, catchall),
644             Reachable(unreachables) if unreachables.is_empty() => {}
645             // The arm is reachable, but contains unreachable subpatterns (from or-patterns).
646             Reachable(unreachables) => {
647                 let mut unreachables = unreachables.clone();
648                 // Emit lints in the order in which they occur in the file.
649                 unreachables.sort_unstable();
650                 for span in unreachables {
651                     unreachable_pattern(cx.tcx, span, arm.hir_id, None);
652                 }
653             }
654         }
655         if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) {
656             catchall = Some(arm.pat.span());
657         }
658     }
659 }
660 
661 /// Report that a match is not exhaustive.
non_exhaustive_match<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, thir: &Thir<'tcx>, scrut_ty: Ty<'tcx>, sp: Span, witnesses: Vec<DeconstructedPat<'p, 'tcx>>, arms: &[ArmId], expr_span: Span, ) -> ErrorGuaranteed662 fn non_exhaustive_match<'p, 'tcx>(
663     cx: &MatchCheckCtxt<'p, 'tcx>,
664     thir: &Thir<'tcx>,
665     scrut_ty: Ty<'tcx>,
666     sp: Span,
667     witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
668     arms: &[ArmId],
669     expr_span: Span,
670 ) -> ErrorGuaranteed {
671     let is_empty_match = arms.is_empty();
672     let non_empty_enum = match scrut_ty.kind() {
673         ty::Adt(def, _) => def.is_enum() && !def.variants().is_empty(),
674         _ => false,
675     };
676     // In the case of an empty match, replace the '`_` not covered' diagnostic with something more
677     // informative.
678     let mut err;
679     let pattern;
680     let patterns_len;
681     if is_empty_match && !non_empty_enum {
682         return cx.tcx.sess.emit_err(NonExhaustivePatternsTypeNotEmpty {
683             cx,
684             expr_span,
685             span: sp,
686             ty: scrut_ty,
687         });
688     } else {
689         // FIXME: migration of this diagnostic will require list support
690         let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
691         err = create_e0004(
692             cx.tcx.sess,
693             sp,
694             format!("non-exhaustive patterns: {} not covered", joined_patterns),
695         );
696         err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns));
697         patterns_len = witnesses.len();
698         pattern = if witnesses.len() < 4 {
699             witnesses
700                 .iter()
701                 .map(|witness| witness.to_pat(cx).to_string())
702                 .collect::<Vec<String>>()
703                 .join(" | ")
704         } else {
705             "_".to_string()
706         };
707     };
708 
709     let is_variant_list_non_exhaustive = matches!(scrut_ty.kind(),
710         ty::Adt(def, _) if def.is_variant_list_non_exhaustive() && !def.did().is_local());
711 
712     adt_defined_here(cx, &mut err, scrut_ty, &witnesses);
713     err.note(format!(
714         "the matched value is of type `{}`{}",
715         scrut_ty,
716         if is_variant_list_non_exhaustive { ", which is marked as non-exhaustive" } else { "" }
717     ));
718     if (scrut_ty == cx.tcx.types.usize || scrut_ty == cx.tcx.types.isize)
719         && !is_empty_match
720         && witnesses.len() == 1
721         && matches!(witnesses[0].ctor(), Constructor::NonExhaustive)
722     {
723         err.note(format!(
724             "`{}` does not have a fixed maximum value, so a wildcard `_` is necessary to match \
725              exhaustively",
726             scrut_ty,
727         ));
728         if cx.tcx.sess.is_nightly_build() {
729             err.help(format!(
730                 "add `#![feature(precise_pointer_size_matching)]` to the crate attributes to \
731                  enable precise `{}` matching",
732                 scrut_ty,
733             ));
734         }
735     }
736     if let ty::Ref(_, sub_ty, _) = scrut_ty.kind() {
737         if !sub_ty.is_inhabited_from(cx.tcx, cx.module, cx.param_env) {
738             err.note("references are always considered inhabited");
739         }
740     }
741 
742     let mut suggestion = None;
743     let sm = cx.tcx.sess.source_map();
744     match arms {
745         [] if sp.eq_ctxt(expr_span) => {
746             // Get the span for the empty match body `{}`.
747             let (indentation, more) = if let Some(snippet) = sm.indentation_before(sp) {
748                 (format!("\n{}", snippet), "    ")
749             } else {
750                 (" ".to_string(), "")
751             };
752             suggestion = Some((
753                 sp.shrink_to_hi().with_hi(expr_span.hi()),
754                 format!(
755                     " {{{indentation}{more}{pattern} => todo!(),{indentation}}}",
756                     indentation = indentation,
757                     more = more,
758                     pattern = pattern,
759                 ),
760             ));
761         }
762         [only] => {
763             let only = &thir[*only];
764             let (pre_indentation, is_multiline) = if let Some(snippet) = sm.indentation_before(only.span)
765                 && let Ok(with_trailing) = sm.span_extend_while(only.span, |c| c.is_whitespace() || c == ',')
766                 && sm.is_multiline(with_trailing)
767             {
768                 (format!("\n{}", snippet), true)
769             } else {
770                 (" ".to_string(), false)
771             };
772             let only_body = &thir[only.body];
773             let comma = if matches!(only_body.kind, ExprKind::Block { .. })
774                 && only.span.eq_ctxt(only_body.span)
775                 && is_multiline
776             {
777                 ""
778             } else {
779                 ","
780             };
781             suggestion = Some((
782                 only.span.shrink_to_hi(),
783                 format!("{}{}{} => todo!()", comma, pre_indentation, pattern),
784             ));
785         }
786         [.., prev, last] => {
787             let prev = &thir[*prev];
788             let last = &thir[*last];
789             if prev.span.eq_ctxt(last.span) {
790                 let last_body = &thir[last.body];
791                 let comma = if matches!(last_body.kind, ExprKind::Block { .. })
792                     && last.span.eq_ctxt(last_body.span)
793                 {
794                     ""
795                 } else {
796                     ","
797                 };
798                 let spacing = if sm.is_multiline(prev.span.between(last.span)) {
799                     sm.indentation_before(last.span).map(|indent| format!("\n{indent}"))
800                 } else {
801                     Some(" ".to_string())
802                 };
803                 if let Some(spacing) = spacing {
804                     suggestion = Some((
805                         last.span.shrink_to_hi(),
806                         format!("{}{}{} => todo!()", comma, spacing, pattern),
807                     ));
808                 }
809             }
810         }
811         _ => {}
812     }
813 
814     let msg = format!(
815         "ensure that all possible cases are being handled by adding a match arm with a wildcard \
816          pattern{}{}",
817         if patterns_len > 1 && patterns_len < 4 && suggestion.is_some() {
818             ", a match arm with multiple or-patterns"
819         } else {
820             // we are either not suggesting anything, or suggesting `_`
821             ""
822         },
823         match patterns_len {
824             // non-exhaustive enum case
825             0 if suggestion.is_some() => " as shown",
826             0 => "",
827             1 if suggestion.is_some() => " or an explicit pattern as shown",
828             1 => " or an explicit pattern",
829             _ if suggestion.is_some() => " as shown, or multiple match arms",
830             _ => " or multiple match arms",
831         },
832     );
833 
834     let all_arms_have_guards = arms.iter().all(|arm_id| thir[*arm_id].guard.is_some());
835     if !is_empty_match && all_arms_have_guards {
836         err.subdiagnostic(NonExhaustiveMatchAllArmsGuarded);
837     }
838     if let Some((span, sugg)) = suggestion {
839         err.span_suggestion_verbose(span, msg, sugg, Applicability::HasPlaceholders);
840     } else {
841         err.help(msg);
842     }
843     err.emit()
844 }
845 
joined_uncovered_patterns<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, witnesses: &[DeconstructedPat<'p, 'tcx>], ) -> String846 pub(crate) fn joined_uncovered_patterns<'p, 'tcx>(
847     cx: &MatchCheckCtxt<'p, 'tcx>,
848     witnesses: &[DeconstructedPat<'p, 'tcx>],
849 ) -> String {
850     const LIMIT: usize = 3;
851     let pat_to_str = |pat: &DeconstructedPat<'p, 'tcx>| pat.to_pat(cx).to_string();
852     match witnesses {
853         [] => bug!(),
854         [witness] => format!("`{}`", witness.to_pat(cx)),
855         [head @ .., tail] if head.len() < LIMIT => {
856             let head: Vec<_> = head.iter().map(pat_to_str).collect();
857             format!("`{}` and `{}`", head.join("`, `"), tail.to_pat(cx))
858         }
859         _ => {
860             let (head, tail) = witnesses.split_at(LIMIT);
861             let head: Vec<_> = head.iter().map(pat_to_str).collect();
862             format!("`{}` and {} more", head.join("`, `"), tail.len())
863         }
864     }
865 }
866 
pattern_not_covered_label( witnesses: &[DeconstructedPat<'_, '_>], joined_patterns: &str, ) -> String867 pub(crate) fn pattern_not_covered_label(
868     witnesses: &[DeconstructedPat<'_, '_>],
869     joined_patterns: &str,
870 ) -> String {
871     format!("pattern{} {} not covered", rustc_errors::pluralize!(witnesses.len()), joined_patterns)
872 }
873 
874 /// Point at the definition of non-covered `enum` variants.
adt_defined_here<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, err: &mut Diagnostic, ty: Ty<'tcx>, witnesses: &[DeconstructedPat<'p, 'tcx>], )875 fn adt_defined_here<'p, 'tcx>(
876     cx: &MatchCheckCtxt<'p, 'tcx>,
877     err: &mut Diagnostic,
878     ty: Ty<'tcx>,
879     witnesses: &[DeconstructedPat<'p, 'tcx>],
880 ) {
881     let ty = ty.peel_refs();
882     if let ty::Adt(def, _) = ty.kind() {
883         let mut spans = vec![];
884         if witnesses.len() < 5 {
885             for sp in maybe_point_at_variant(cx, *def, witnesses.iter()) {
886                 spans.push(sp);
887             }
888         }
889         let def_span = cx
890             .tcx
891             .hir()
892             .get_if_local(def.did())
893             .and_then(|node| node.ident())
894             .map(|ident| ident.span)
895             .unwrap_or_else(|| cx.tcx.def_span(def.did()));
896         let mut span: MultiSpan =
897             if spans.is_empty() { def_span.into() } else { spans.clone().into() };
898 
899         span.push_span_label(def_span, "");
900         for pat in spans {
901             span.push_span_label(pat, "not covered");
902         }
903         err.span_note(span, format!("`{}` defined here", ty));
904     }
905 }
906 
maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>( cx: &MatchCheckCtxt<'p, 'tcx>, def: AdtDef<'tcx>, patterns: impl Iterator<Item = &'a DeconstructedPat<'p, 'tcx>>, ) -> Vec<Span>907 fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>(
908     cx: &MatchCheckCtxt<'p, 'tcx>,
909     def: AdtDef<'tcx>,
910     patterns: impl Iterator<Item = &'a DeconstructedPat<'p, 'tcx>>,
911 ) -> Vec<Span> {
912     use Constructor::*;
913     let mut covered = vec![];
914     for pattern in patterns {
915         if let Variant(variant_index) = pattern.ctor() {
916             if let ty::Adt(this_def, _) = pattern.ty().kind() && this_def.did() != def.did() {
917                 continue;
918             }
919             let sp = def.variant(*variant_index).ident(cx.tcx).span;
920             if covered.contains(&sp) {
921                 // Don't point at variants that have already been covered due to other patterns to avoid
922                 // visual clutter.
923                 continue;
924             }
925             covered.push(sp);
926         }
927         covered.extend(maybe_point_at_variant(cx, def, pattern.iter_fields()));
928     }
929     covered
930 }
931 
932 /// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.
933 /// Check that there are no borrow or move conflicts in `binding @ subpat` patterns.
934 ///
935 /// For example, this would reject:
936 /// - `ref x @ Some(ref mut y)`,
937 /// - `ref mut x @ Some(ref y)`,
938 /// - `ref mut x @ Some(ref mut y)`,
939 /// - `ref mut? x @ Some(y)`, and
940 /// - `x @ Some(ref mut? y)`.
941 ///
942 /// This analysis is *not* subsumed by NLL.
check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, '_, 'tcx>, pat: &Pat<'tcx>)943 fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, '_, 'tcx>, pat: &Pat<'tcx>) {
944     // Extract `sub` in `binding @ sub`.
945     let PatKind::Binding { name, mode, ty, subpattern: Some(box ref sub), .. } = pat.kind else { return };
946 
947     let is_binding_by_move = |ty: Ty<'tcx>| !ty.is_copy_modulo_regions(cx.tcx, cx.param_env);
948 
949     let sess = cx.tcx.sess;
950 
951     // Get the binding move, extract the mutability if by-ref.
952     let mut_outer = match mode {
953         BindingMode::ByValue if is_binding_by_move(ty) => {
954             // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`.
955             let mut conflicts_ref = Vec::new();
956             sub.each_binding(|_, mode, _, span| match mode {
957                 BindingMode::ByValue => {}
958                 BindingMode::ByRef(_) => conflicts_ref.push(span),
959             });
960             if !conflicts_ref.is_empty() {
961                 sess.emit_err(BorrowOfMovedValue {
962                     binding_span: pat.span,
963                     conflicts_ref,
964                     name,
965                     ty,
966                     suggest_borrowing: Some(pat.span.shrink_to_lo()),
967                 });
968             }
969             return;
970         }
971         BindingMode::ByValue => return,
972         BindingMode::ByRef(m) => m.mutability(),
973     };
974 
975     // We now have `ref $mut_outer binding @ sub` (semantically).
976     // Recurse into each binding in `sub` and find mutability or move conflicts.
977     let mut conflicts_move = Vec::new();
978     let mut conflicts_mut_mut = Vec::new();
979     let mut conflicts_mut_ref = Vec::new();
980     sub.each_binding(|name, mode, ty, span| {
981         match mode {
982             BindingMode::ByRef(mut_inner) => match (mut_outer, mut_inner.mutability()) {
983                 // Both sides are `ref`.
984                 (Mutability::Not, Mutability::Not) => {}
985                 // 2x `ref mut`.
986                 (Mutability::Mut, Mutability::Mut) => {
987                     conflicts_mut_mut.push(Conflict::Mut { span, name })
988                 }
989                 (Mutability::Not, Mutability::Mut) => {
990                     conflicts_mut_ref.push(Conflict::Mut { span, name })
991                 }
992                 (Mutability::Mut, Mutability::Not) => {
993                     conflicts_mut_ref.push(Conflict::Ref { span, name })
994                 }
995             },
996             BindingMode::ByValue if is_binding_by_move(ty) => {
997                 conflicts_move.push(Conflict::Moved { span, name }) // `ref mut?` + by-move conflict.
998             }
999             BindingMode::ByValue => {} // `ref mut?` + by-copy is fine.
1000         }
1001     });
1002 
1003     let report_mut_mut = !conflicts_mut_mut.is_empty();
1004     let report_mut_ref = !conflicts_mut_ref.is_empty();
1005     let report_move_conflict = !conflicts_move.is_empty();
1006 
1007     let mut occurrences = match mut_outer {
1008         Mutability::Mut => vec![Conflict::Mut { span: pat.span, name }],
1009         Mutability::Not => vec![Conflict::Ref { span: pat.span, name }],
1010     };
1011     occurrences.extend(conflicts_mut_mut);
1012     occurrences.extend(conflicts_mut_ref);
1013     occurrences.extend(conflicts_move);
1014 
1015     // Report errors if any.
1016     if report_mut_mut {
1017         // Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
1018         sess.emit_err(MultipleMutBorrows { span: pat.span, occurrences });
1019     } else if report_mut_ref {
1020         // Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
1021         match mut_outer {
1022             Mutability::Mut => {
1023                 sess.emit_err(AlreadyMutBorrowed { span: pat.span, occurrences });
1024             }
1025             Mutability::Not => {
1026                 sess.emit_err(AlreadyBorrowed { span: pat.span, occurrences });
1027             }
1028         };
1029     } else if report_move_conflict {
1030         // Report by-ref and by-move conflicts, e.g. `ref x @ y`.
1031         sess.emit_err(MovedWhileBorrowed { span: pat.span, occurrences });
1032     }
1033 }
1034