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