]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_mir_build/src/thir/pattern/check_match.rs
New upstream version 1.60.0+dfsg1
[rustc.git] / compiler / rustc_mir_build / src / thir / pattern / check_match.rs
CommitLineData
c295e0f8 1use super::deconstruct_pat::{Constructor, DeconstructedPat};
fc512014 2use super::usefulness::{
c295e0f8 3 compute_match_usefulness, MatchArm, MatchCheckCtxt, Reachability, UsefulnessReport,
fc512014 4};
17df50a5 5use super::{PatCtxt, PatternError};
223e47cc 6
f035d41b 7use rustc_arena::TypedArena;
3dfed10e 8use rustc_ast::Mutability;
dfeec247
XL
9use rustc_errors::{error_code, struct_span_err, Applicability, DiagnosticBuilder};
10use rustc_hir as hir;
11use rustc_hir::def::*;
12use rustc_hir::def_id::DefId;
5099ac24 13use rustc_hir::intravisit::{self, Visitor};
dfeec247 14use rustc_hir::{HirId, Pat};
c295e0f8
XL
15use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
16use rustc_session::lint::builtin::{
17 BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS,
18};
dfeec247 19use rustc_session::Session;
5099ac24 20use rustc_span::source_map::Spanned;
94222f64 21use rustc_span::{DesugaringKind, ExpnKind, Span};
1a4d82fc 22
e74abb32 23crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) {
f9f354fc 24 let body_id = match def_id.as_local() {
e74abb32 25 None => return,
3dfed10e 26 Some(id) => tcx.hir().body_owned_by(tcx.hir().local_def_id_to_hir_id(id)),
ff7c6d11
XL
27 };
28
c295e0f8 29 let pattern_arena = TypedArena::default();
f9f354fc
XL
30 let mut visitor = MatchVisitor {
31 tcx,
3dfed10e 32 typeck_results: tcx.typeck_body(body_id),
f9f354fc 33 param_env: tcx.param_env(def_id),
c295e0f8 34 pattern_arena: &pattern_arena,
f9f354fc 35 };
416331ca 36 visitor.visit_body(tcx.hir().body(body_id));
ff7c6d11
XL
37}
38
416331ca 39fn create_e0004(sess: &Session, sp: Span, error_message: String) -> DiagnosticBuilder<'_> {
c30ab7b3 40 struct_span_err!(sess, sp, E0004, "{}", &error_message)
223e47cc
LB
41}
42
c295e0f8
XL
43#[derive(PartialEq)]
44enum RefutableFlag {
45 Irrefutable,
46 Refutable,
47}
48use RefutableFlag::*;
49
50struct MatchVisitor<'a, 'p, 'tcx> {
dc9dc135 51 tcx: TyCtxt<'tcx>,
3dfed10e 52 typeck_results: &'a ty::TypeckResults<'tcx>,
7cac9316 53 param_env: ty::ParamEnv<'tcx>,
c295e0f8 54 pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
1a4d82fc
JJ
55}
56
c295e0f8 57impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> {
dfeec247 58 fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
c30ab7b3 59 intravisit::walk_expr(self, ex);
94222f64
XL
60 match &ex.kind {
61 hir::ExprKind::Match(scrut, arms, source) => self.check_match(scrut, arms, *source),
a2a8927a
XL
62 hir::ExprKind::Let(hir::Let { pat, init, span, .. }) => {
63 self.check_let(pat, init, *span)
64 }
94222f64 65 _ => {}
c30ab7b3 66 }
1a4d82fc 67 }
c30ab7b3 68
dfeec247 69 fn visit_local(&mut self, loc: &'tcx hir::Local<'tcx>) {
c30ab7b3
SL
70 intravisit::walk_local(self, loc);
71
e74abb32
XL
72 let (msg, sp) = match loc.source {
73 hir::LocalSource::Normal => ("local binding", Some(loc.span)),
e74abb32
XL
74 hir::LocalSource::AsyncFn => ("async fn binding", None),
75 hir::LocalSource::AwaitDesugar => ("`await` future binding", None),
29967ef6 76 hir::LocalSource::AssignDesugar(_) => ("destructuring assignment binding", None),
e74abb32
XL
77 };
78 self.check_irrefutable(&loc.pat, msg, sp);
1a4d82fc 79 }
c30ab7b3 80
dfeec247
XL
81 fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
82 intravisit::walk_param(self, param);
83 self.check_irrefutable(&param.pat, "function argument", None);
c30ab7b3 84 }
1a4d82fc
JJ
85}
86
e74abb32 87impl PatCtxt<'_, '_> {
136023e0 88 fn report_inlining_errors(&self) {
cc61c64b
XL
89 for error in &self.errors {
90 match *error {
c30ab7b3 91 PatternError::StaticInPattern(span) => {
2c00a5a8
XL
92 self.span_e0158(span, "statics cannot be referenced in patterns")
93 }
dc9dc135 94 PatternError::AssocConstInPattern(span) => {
2c00a5a8 95 self.span_e0158(span, "associated consts cannot be referenced in patterns")
c30ab7b3 96 }
ba9703b0
XL
97 PatternError::ConstParamInPattern(span) => {
98 self.span_e0158(span, "const parameters cannot be referenced in patterns")
99 }
0531ce1d 100 PatternError::NonConstPath(span) => {
29967ef6 101 rustc_middle::mir::interpret::struct_error(
94b46f34 102 self.tcx.at(span),
0531ce1d 103 "runtime values cannot be referenced in patterns",
e74abb32
XL
104 )
105 .emit();
1a4d82fc
JJ
106 }
107 }
c30ab7b3
SL
108 }
109 }
2c00a5a8
XL
110
111 fn span_e0158(&self, span: Span, text: &str) {
dfeec247 112 struct_span_err!(self.tcx.sess, span, E0158, "{}", text).emit();
2c00a5a8 113 }
cc61c64b
XL
114}
115
c295e0f8
XL
116impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
117 fn check_patterns(&self, pat: &Pat<'_>, rf: RefutableFlag) {
74b04a01 118 pat.walk_always(|pat| check_borrow_conflicts_in_at_patterns(self, pat));
c295e0f8 119 check_for_bindings_named_same_as_variants(self, pat, rf);
cc61c64b 120 }
1a4d82fc 121
c295e0f8 122 fn lower_pattern(
dfeec247
XL
123 &self,
124 cx: &mut MatchCheckCtxt<'p, 'tcx>,
125 pat: &'tcx hir::Pat<'tcx>,
126 have_errors: &mut bool,
c295e0f8 127 ) -> &'p DeconstructedPat<'p, 'tcx> {
3dfed10e 128 let mut patcx = PatCtxt::new(self.tcx, self.param_env, self.typeck_results);
dfeec247
XL
129 patcx.include_lint_checks();
130 let pattern = patcx.lower_pattern(pat);
c295e0f8 131 let pattern: &_ = cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, &pattern));
dfeec247
XL
132 if !patcx.errors.is_empty() {
133 *have_errors = true;
136023e0 134 patcx.report_inlining_errors();
dfeec247 135 }
c295e0f8 136 pattern
dfeec247
XL
137 }
138
c295e0f8 139 fn new_cx(&self, hir_id: HirId) -> MatchCheckCtxt<'p, 'tcx> {
f9f354fc
XL
140 MatchCheckCtxt {
141 tcx: self.tcx,
142 param_env: self.param_env,
143 module: self.tcx.parent_module(hir_id).to_def_id(),
144 pattern_arena: &self.pattern_arena,
145 }
dfeec247
XL
146 }
147
a2a8927a 148 fn check_let(&mut self, pat: &'tcx hir::Pat<'tcx>, scrutinee: &hir::Expr<'_>, span: Span) {
c295e0f8 149 self.check_patterns(pat, Refutable);
a2a8927a 150 let mut cx = self.new_cx(scrutinee.hir_id);
c295e0f8
XL
151 let tpat = self.lower_pattern(&mut cx, pat, &mut false);
152 check_let_reachability(&mut cx, pat.hir_id, tpat, span);
94222f64
XL
153 }
154
dfeec247
XL
155 fn check_match(
156 &mut self,
157 scrut: &hir::Expr<'_>,
3c0e092e 158 hir_arms: &'tcx [hir::Arm<'tcx>],
dfeec247
XL
159 source: hir::MatchSource,
160 ) {
94222f64
XL
161 let mut cx = self.new_cx(scrut.hir_id);
162
3c0e092e 163 for arm in hir_arms {
dfeec247 164 // Check the arm for some things unrelated to exhaustiveness.
c295e0f8 165 self.check_patterns(&arm.pat, Refutable);
fc512014 166 if let Some(hir::Guard::IfLet(ref pat, _)) = arm.guard {
c295e0f8
XL
167 self.check_patterns(pat, Refutable);
168 let tpat = self.lower_pattern(&mut cx, pat, &mut false);
169 check_let_reachability(&mut cx, pat.hir_id, tpat, tpat.span());
fc512014
XL
170 }
171 }
172
f9f354fc 173 let mut have_errors = false;
1a4d82fc 174
3c0e092e 175 let arms: Vec<_> = hir_arms
f9f354fc 176 .iter()
fc512014 177 .map(|hir::Arm { pat, guard, .. }| MatchArm {
c295e0f8 178 pat: self.lower_pattern(&mut cx, pat, &mut have_errors),
fc512014
XL
179 hir_id: pat.hir_id,
180 has_guard: guard.is_some(),
f9f354fc
XL
181 })
182 .collect();
1a4d82fc 183
fc512014 184 // Bail out early if lowering failed.
f9f354fc
XL
185 if have_errors {
186 return;
187 }
1a4d82fc 188
fc512014
XL
189 let scrut_ty = self.typeck_results.expr_ty_adjusted(scrut);
190 let report = compute_match_usefulness(&cx, &arms, scrut.hir_id, scrut_ty);
191
c295e0f8 192 match source {
3c0e092e
XL
193 // Don't report arm reachability of desugared `match $iter.into_iter() { iter => .. }`
194 // when the iterator is an uninhabited type. unreachable_code will trigger instead.
195 hir::MatchSource::ForLoopDesugar if arms.len() == 1 => {}
c295e0f8
XL
196 hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
197 report_arm_reachability(&cx, &report)
94222f64 198 }
c295e0f8
XL
199 // Unreachable patterns in try and await expressions occur when one of
200 // the arms are an uninhabited type. Which is OK.
201 hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
202 }
f9f354fc 203
fc512014 204 // Check if the match is exhaustive.
fc512014
XL
205 let is_empty_match = arms.is_empty();
206 let witnesses = report.non_exhaustiveness_witnesses;
207 if !witnesses.is_empty() {
3c0e092e
XL
208 if source == hir::MatchSource::ForLoopDesugar && hir_arms.len() == 2 {
209 // the for loop pattern is not irrefutable
210 let pat = hir_arms[1].pat.for_loop_some().unwrap();
211 self.check_irrefutable(pat, "`for` loop binding", None);
212 } else {
213 non_exhaustive_match(&cx, scrut_ty, scrut.span, witnesses, is_empty_match);
214 }
fc512014 215 }
c30ab7b3
SL
216 }
217
dfeec247 218 fn check_irrefutable(&self, pat: &'tcx Pat<'tcx>, origin: &str, sp: Option<Span>) {
f9f354fc 219 let mut cx = self.new_cx(pat.hir_id);
e74abb32 220
c295e0f8
XL
221 let pattern = self.lower_pattern(&mut cx, pat, &mut false);
222 let pattern_ty = pattern.ty();
fc512014
XL
223 let arms = vec![MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false }];
224 let report = compute_match_usefulness(&cx, &arms, pat.hir_id, pattern_ty);
225
226 // Note: we ignore whether the pattern is unreachable (i.e. whether the type is empty). We
227 // only care about exhaustiveness here.
228 let witnesses = report.non_exhaustiveness_witnesses;
229 if witnesses.is_empty() {
230 // The pattern is irrefutable.
3c0e092e 231 self.check_patterns(pat, Irrefutable);
fc512014
XL
232 return;
233 }
f9f354fc 234
c295e0f8 235 let joined_patterns = joined_uncovered_patterns(&cx, &witnesses);
f9f354fc
XL
236 let mut err = struct_span_err!(
237 self.tcx.sess,
238 pat.span,
239 E0005,
240 "refutable pattern in {}: {} not covered",
241 origin,
242 joined_patterns
243 );
244 let suggest_if_let = match &pat.kind {
245 hir::PatKind::Path(hir::QPath::Resolved(None, path))
246 if path.segments.len() == 1 && path.segments[0].args.is_none() =>
247 {
248 const_not_var(&mut err, cx.tcx, pat, path);
249 false
250 }
251 _ => {
252 err.span_label(pat.span, pattern_not_covered_label(&witnesses, &joined_patterns));
253 true
254 }
255 };
256
257 if let (Some(span), true) = (sp, suggest_if_let) {
258 err.note(
259 "`let` bindings require an \"irrefutable pattern\", like a `struct` or \
260 an `enum` with only one variant",
261 );
262 if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
263 err.span_suggestion(
264 span,
265 "you might want to use `if let` to ignore the variant that isn't matched",
266 format!("if {} {{ /* */ }}", &snippet[..snippet.len() - 1]),
267 Applicability::HasPlaceholders,
e74abb32
XL
268 );
269 }
f9f354fc
XL
270 err.note(
271 "for more information, visit \
272 https://doc.rust-lang.org/book/ch18-02-refutability.html",
273 );
274 }
e74abb32 275
f9f354fc
XL
276 adt_defined_here(&cx, &mut err, pattern_ty, &witnesses);
277 err.note(&format!("the matched value is of type `{}`", pattern_ty));
278 err.emit();
1a4d82fc
JJ
279 }
280}
281
e74abb32
XL
282/// A path pattern was interpreted as a constant, not a new variable.
283/// This caused an irrefutable match failure in e.g. `let`.
dfeec247
XL
284fn const_not_var(
285 err: &mut DiagnosticBuilder<'_>,
286 tcx: TyCtxt<'_>,
287 pat: &Pat<'_>,
288 path: &hir::Path<'_>,
289) {
e74abb32
XL
290 let descr = path.res.descr();
291 err.span_label(
292 pat.span,
293 format!("interpreted as {} {} pattern, not a new variable", path.res.article(), descr,),
294 );
295
296 err.span_suggestion(
297 pat.span,
298 "introduce a variable instead",
299 format!("{}_var", path.segments[0].ident).to_lowercase(),
300 // Cannot use `MachineApplicable` as it's not really *always* correct
301 // because there may be such an identifier in scope or the user maybe
302 // really wanted to match against the constant. This is quite unlikely however.
303 Applicability::MaybeIncorrect,
304 );
305
306 if let Some(span) = tcx.hir().res_span(path.res) {
307 err.span_label(span, format!("{} defined here", descr));
308 }
309}
310
c295e0f8
XL
311fn check_for_bindings_named_same_as_variants(
312 cx: &MatchVisitor<'_, '_, '_>,
313 pat: &Pat<'_>,
314 rf: RefutableFlag,
315) {
dfeec247 316 pat.walk_always(|p| {
e74abb32 317 if let hir::PatKind::Binding(_, _, ident, None) = p.kind {
dfeec247 318 if let Some(ty::BindByValue(hir::Mutability::Not)) =
3dfed10e 319 cx.typeck_results.extract_binding_mode(cx.tcx.sess, p.hir_id, p.span)
dfeec247 320 {
3dfed10e 321 let pat_ty = cx.typeck_results.pat_ty(p).peel_refs();
1b1a35ee 322 if let ty::Adt(edef, _) = pat_ty.kind() {
e74abb32
XL
323 if edef.is_enum()
324 && edef.variants.iter().any(|variant| {
5099ac24 325 variant.ident(cx.tcx) == ident && variant.ctor_kind == CtorKind::Const
e74abb32
XL
326 })
327 {
c295e0f8 328 let variant_count = edef.variants.len();
74b04a01
XL
329 cx.tcx.struct_span_lint_hir(
330 BINDINGS_WITH_VARIANT_NAME,
331 p.hir_id,
332 p.span,
333 |lint| {
334 let ty_path = cx.tcx.def_path_str(edef.did);
c295e0f8 335 let mut err = lint.build(&format!(
dfeec247 336 "pattern binding `{}` is named the same as one \
c295e0f8 337 of the variants of the type `{}`",
dfeec247 338 ident, ty_path
c295e0f8
XL
339 ));
340 err.code(error_code!(E0170));
341 // If this is an irrefutable pattern, and there's > 1 variant,
342 // then we can't actually match on this. Applying the below
343 // suggestion would produce code that breaks on `check_irrefutable`.
344 if rf == Refutable || variant_count == 1 {
345 err.span_suggestion(
346 p.span,
347 "to match on the variant, qualify the path",
348 format!("{}::{}", ty_path, ident),
349 Applicability::MachineApplicable,
350 );
351 }
352 err.emit();
74b04a01
XL
353 },
354 )
8faf50e0 355 }
1a4d82fc
JJ
356 }
357 }
1a4d82fc 358 }
1a4d82fc
JJ
359 });
360}
361
c30ab7b3 362/// Checks for common cases of "catchall" patterns that may not be intended as such.
c295e0f8
XL
363fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
364 use Constructor::*;
365 match pat.ctor() {
366 Wildcard => true,
367 Single => pat.iter_fields().all(|pat| pat_is_catchall(pat)),
e74abb32 368 _ => false,
c30ab7b3 369 }
1a4d82fc
JJ
370}
371
dfeec247 372fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<Span>) {
74b04a01
XL
373 tcx.struct_span_lint_hir(UNREACHABLE_PATTERNS, id, span, |lint| {
374 let mut err = lint.build("unreachable pattern");
375 if let Some(catchall) = catchall {
376 // We had a catchall pattern, hint at that.
377 err.span_label(span, "unreachable pattern");
378 err.span_label(catchall, "matches any value");
379 }
380 err.emit();
381 });
dfeec247
XL
382}
383
94222f64
XL
384fn irrefutable_let_pattern(tcx: TyCtxt<'_>, id: HirId, span: Span) {
385 macro_rules! emit_diag {
386 (
387 $lint:expr,
388 $source_name:expr,
389 $note_sufix:expr,
390 $help_sufix:expr
391 ) => {{
392 let mut diag = $lint.build(concat!("irrefutable ", $source_name, " pattern"));
393 diag.note(concat!("this pattern will always match, so the ", $note_sufix));
394 diag.help(concat!("consider ", $help_sufix));
6a06907d 395 diag.emit()
94222f64
XL
396 }};
397 }
398
399 let source = let_source(tcx, id);
400 let span = match source {
401 LetSource::LetElse(span) => span,
402 _ => span,
403 };
404 tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, |lint| match source {
405 LetSource::GenericLet => {
406 emit_diag!(lint, "`let`", "`let` is useless", "removing `let`");
407 }
408 LetSource::IfLet => {
409 emit_diag!(
410 lint,
411 "`if let`",
412 "`if let` is useless",
413 "replacing the `if let` with a `let`"
414 );
6a06907d 415 }
94222f64
XL
416 LetSource::IfLetGuard => {
417 emit_diag!(
418 lint,
419 "`if let` guard",
420 "guard is useless",
421 "removing the guard and adding a `let` inside the match arm"
422 );
6a06907d 423 }
94222f64
XL
424 LetSource::LetElse(..) => {
425 emit_diag!(
426 lint,
427 "`let...else`",
428 "`else` clause is useless",
429 "removing the `else` clause"
430 );
6a06907d 431 }
94222f64
XL
432 LetSource::WhileLet => {
433 emit_diag!(
434 lint,
435 "`while let`",
436 "loop will never exit",
437 "instead using a `loop { ... }` with a `let` inside it"
438 );
6a06907d 439 }
74b04a01 440 });
dfeec247
XL
441}
442
94222f64 443fn check_let_reachability<'p, 'tcx>(
60c5eb7d 444 cx: &mut MatchCheckCtxt<'p, 'tcx>,
fc512014 445 pat_id: HirId,
c295e0f8 446 pat: &'p DeconstructedPat<'p, 'tcx>,
94222f64 447 span: Span,
fc512014 448) {
5099ac24
FG
449 if is_let_chain(cx.tcx, pat_id) {
450 return;
451 }
452
fc512014 453 let arms = [MatchArm { pat, hir_id: pat_id, has_guard: false }];
c295e0f8
XL
454 let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty());
455
456 // Report if the pattern is unreachable, which can only occur when the type is uninhabited.
457 // This also reports unreachable sub-patterns though, so we can't just replace it with an
458 // `is_uninhabited` check.
459 report_arm_reachability(&cx, &report);
fc512014
XL
460
461 if report.non_exhaustiveness_witnesses.is_empty() {
6a06907d 462 // The match is exhaustive, i.e. the `if let` pattern is irrefutable.
94222f64 463 irrefutable_let_pattern(cx.tcx, pat_id, span);
fc512014
XL
464 }
465}
466
467/// Report unreachable arms, if any.
c295e0f8 468fn report_arm_reachability<'p, 'tcx>(
fc512014
XL
469 cx: &MatchCheckCtxt<'p, 'tcx>,
470 report: &UsefulnessReport<'p, 'tcx>,
c295e0f8 471) {
6a06907d 472 use Reachability::*;
c30ab7b3 473 let mut catchall = None;
c295e0f8 474 for (arm, is_useful) in report.arm_usefulness.iter() {
fc512014 475 match is_useful {
c295e0f8 476 Unreachable => unreachable_pattern(cx.tcx, arm.pat.span(), arm.hir_id, catchall),
6a06907d 477 Reachable(unreachables) if unreachables.is_empty() => {}
fc512014 478 // The arm is reachable, but contains unreachable subpatterns (from or-patterns).
6a06907d
XL
479 Reachable(unreachables) => {
480 let mut unreachables = unreachables.clone();
29967ef6
XL
481 // Emit lints in the order in which they occur in the file.
482 unreachables.sort_unstable();
483 for span in unreachables {
fc512014 484 unreachable_pattern(cx.tcx, span, arm.hir_id, None);
c30ab7b3 485 }
223e47cc 486 }
60c5eb7d 487 }
fc512014 488 if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) {
c295e0f8 489 catchall = Some(arm.pat.span());
223e47cc
LB
490 }
491 }
492}
493
fc512014
XL
494/// Report that a match is not exhaustive.
495fn non_exhaustive_match<'p, 'tcx>(
496 cx: &MatchCheckCtxt<'p, 'tcx>,
532ac7d7
XL
497 scrut_ty: Ty<'tcx>,
498 sp: Span,
c295e0f8 499 witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
60c5eb7d 500 is_empty_match: bool,
532ac7d7 501) {
1b1a35ee 502 let non_empty_enum = match scrut_ty.kind() {
60c5eb7d
XL
503 ty::Adt(def, _) => def.is_enum() && !def.variants.is_empty(),
504 _ => false,
505 };
506 // In the case of an empty match, replace the '`_` not covered' diagnostic with something more
507 // informative.
508 let mut err;
509 if is_empty_match && !non_empty_enum {
510 err = create_e0004(
511 cx.tcx.sess,
512 sp,
513 format!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty),
514 );
515 } else {
c295e0f8 516 let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
60c5eb7d
XL
517 err = create_e0004(
518 cx.tcx.sess,
519 sp,
520 format!("non-exhaustive patterns: {} not covered", joined_patterns),
521 );
522 err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns));
523 };
524
17df50a5
XL
525 let is_variant_list_non_exhaustive = match scrut_ty.kind() {
526 ty::Adt(def, _) if def.is_variant_list_non_exhaustive() && !def.did.is_local() => true,
527 _ => false,
528 };
529
e1599b0c
XL
530 adt_defined_here(cx, &mut err, scrut_ty, &witnesses);
531 err.help(
532 "ensure that all possible cases are being handled, \
f035d41b 533 possibly by adding wildcards or more match arms",
60c5eb7d 534 );
17df50a5
XL
535 err.note(&format!(
536 "the matched value is of type `{}`{}",
537 scrut_ty,
538 if is_variant_list_non_exhaustive { ", which is marked as non-exhaustive" } else { "" }
539 ));
f035d41b
XL
540 if (scrut_ty == cx.tcx.types.usize || scrut_ty == cx.tcx.types.isize)
541 && !is_empty_match
542 && witnesses.len() == 1
c295e0f8 543 && matches!(witnesses[0].ctor(), Constructor::NonExhaustive)
f035d41b
XL
544 {
545 err.note(&format!(
546 "`{}` does not have a fixed maximum value, \
547 so a wildcard `_` is necessary to match exhaustively",
548 scrut_ty,
549 ));
fc512014 550 if cx.tcx.sess.is_nightly_build() {
f035d41b
XL
551 err.help(&format!(
552 "add `#![feature(precise_pointer_size_matching)]` \
553 to the crate attributes to enable precise `{}` matching",
554 scrut_ty,
555 ));
556 }
557 }
5869c6ff 558 if let ty::Ref(_, sub_ty, _) = scrut_ty.kind() {
5099ac24 559 if cx.tcx.is_ty_uninhabited_from(cx.module, *sub_ty, cx.param_env) {
5869c6ff
XL
560 err.note("references are always considered inhabited");
561 }
562 }
60c5eb7d 563 err.emit();
e1599b0c 564}
7cac9316 565
c295e0f8
XL
566crate fn joined_uncovered_patterns<'p, 'tcx>(
567 cx: &MatchCheckCtxt<'p, 'tcx>,
568 witnesses: &[DeconstructedPat<'p, 'tcx>],
569) -> String {
e1599b0c 570 const LIMIT: usize = 3;
c295e0f8 571 let pat_to_str = |pat: &DeconstructedPat<'p, 'tcx>| pat.to_pat(cx).to_string();
e1599b0c
XL
572 match witnesses {
573 [] => bug!(),
c295e0f8 574 [witness] => format!("`{}`", witness.to_pat(cx)),
e1599b0c 575 [head @ .., tail] if head.len() < LIMIT => {
c295e0f8
XL
576 let head: Vec<_> = head.iter().map(pat_to_str).collect();
577 format!("`{}` and `{}`", head.join("`, `"), tail.to_pat(cx))
1a4d82fc 578 }
e1599b0c
XL
579 _ => {
580 let (head, tail) = witnesses.split_at(LIMIT);
c295e0f8 581 let head: Vec<_> = head.iter().map(pat_to_str).collect();
e1599b0c 582 format!("`{}` and {} more", head.join("`, `"), tail.len())
9fa01778 583 }
1a4d82fc
JJ
584 }
585}
586
c295e0f8
XL
587crate fn pattern_not_covered_label(
588 witnesses: &[DeconstructedPat<'_, '_>],
589 joined_patterns: &str,
590) -> String {
60c5eb7d 591 format!("pattern{} {} not covered", rustc_errors::pluralize!(witnesses.len()), joined_patterns)
e1599b0c
XL
592}
593
594/// Point at the definition of non-covered `enum` variants.
c295e0f8
XL
595fn adt_defined_here<'p, 'tcx>(
596 cx: &MatchCheckCtxt<'p, 'tcx>,
e1599b0c 597 err: &mut DiagnosticBuilder<'_>,
c295e0f8
XL
598 ty: Ty<'tcx>,
599 witnesses: &[DeconstructedPat<'p, 'tcx>],
e1599b0c
XL
600) {
601 let ty = ty.peel_refs();
1b1a35ee 602 if let ty::Adt(def, _) = ty.kind() {
e1599b0c
XL
603 if let Some(sp) = cx.tcx.hir().span_if_local(def.did) {
604 err.span_label(sp, format!("`{}` defined here", ty));
605 }
606
607 if witnesses.len() < 4 {
c295e0f8 608 for sp in maybe_point_at_variant(cx, def, witnesses.iter()) {
e1599b0c
XL
609 err.span_label(sp, "not covered");
610 }
611 }
612 }
613}
614
c295e0f8
XL
615fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>(
616 cx: &MatchCheckCtxt<'p, 'tcx>,
617 def: &AdtDef,
618 patterns: impl Iterator<Item = &'a DeconstructedPat<'p, 'tcx>>,
619) -> Vec<Span> {
620 use Constructor::*;
532ac7d7 621 let mut covered = vec![];
c295e0f8
XL
622 for pattern in patterns {
623 if let Variant(variant_index) = pattern.ctor() {
624 if let ty::Adt(this_def, _) = pattern.ty().kind() {
625 if this_def.did != def.did {
626 continue;
e1599b0c 627 }
532ac7d7 628 }
5099ac24 629 let sp = def.variants[*variant_index].ident(cx.tcx).span;
c295e0f8
XL
630 if covered.contains(&sp) {
631 // Don't point at variants that have already been covered due to other patterns to avoid
632 // visual clutter.
633 continue;
634 }
635 covered.push(sp);
532ac7d7 636 }
c295e0f8 637 covered.extend(maybe_point_at_variant(cx, def, pattern.iter_fields()));
532ac7d7
XL
638 }
639 covered
640}
641
74b04a01 642/// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.
c295e0f8 643fn is_binding_by_move(cx: &MatchVisitor<'_, '_, '_>, hir_id: HirId, span: Span) -> bool {
3dfed10e 644 !cx.typeck_results.node_type(hir_id).is_copy_modulo_regions(cx.tcx.at(span), cx.param_env)
74b04a01
XL
645}
646
74b04a01 647/// Check that there are no borrow or move conflicts in `binding @ subpat` patterns.
dfeec247
XL
648///
649/// For example, this would reject:
650/// - `ref x @ Some(ref mut y)`,
74b04a01
XL
651/// - `ref mut x @ Some(ref y)`,
652/// - `ref mut x @ Some(ref mut y)`,
653/// - `ref mut? x @ Some(y)`, and
654/// - `x @ Some(ref mut? y)`.
dfeec247
XL
655///
656/// This analysis is *not* subsumed by NLL.
c295e0f8 657fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_, '_>, pat: &Pat<'_>) {
74b04a01
XL
658 // Extract `sub` in `binding @ sub`.
659 let (name, sub) = match &pat.kind {
660 hir::PatKind::Binding(.., name, Some(sub)) => (*name, sub),
661 _ => return,
dfeec247 662 };
74b04a01 663 let binding_span = pat.span.with_hi(name.span.hi());
dfeec247 664
3dfed10e 665 let typeck_results = cx.typeck_results;
74b04a01 666 let sess = cx.tcx.sess;
1a4d82fc 667
74b04a01 668 // Get the binding move, extract the mutability if by-ref.
3dfed10e 669 let mut_outer = match typeck_results.extract_binding_mode(sess, pat.hir_id, pat.span) {
74b04a01
XL
670 Some(ty::BindByValue(_)) if is_binding_by_move(cx, pat.hir_id, pat.span) => {
671 // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`.
672 let mut conflicts_ref = Vec::new();
673 sub.each_binding(|_, hir_id, span, _| {
3dfed10e 674 match typeck_results.extract_binding_mode(sess, hir_id, span) {
74b04a01
XL
675 Some(ty::BindByValue(_)) | None => {}
676 Some(ty::BindByReference(_)) => conflicts_ref.push(span),
dfeec247 677 }
74b04a01
XL
678 });
679 if !conflicts_ref.is_empty() {
680 let occurs_because = format!(
681 "move occurs because `{}` has type `{}` which does not implement the `Copy` trait",
682 name,
3dfed10e 683 typeck_results.node_type(pat.hir_id),
74b04a01
XL
684 );
685 sess.struct_span_err(pat.span, "borrow of moved value")
686 .span_label(binding_span, format!("value moved into `{}` here", name))
687 .span_label(binding_span, occurs_because)
688 .span_labels(conflicts_ref, "value borrowed here after move")
689 .emit();
dfeec247 690 }
74b04a01
XL
691 return;
692 }
693 Some(ty::BindByValue(_)) | None => return,
694 Some(ty::BindByReference(m)) => m,
695 };
dfeec247 696
74b04a01
XL
697 // We now have `ref $mut_outer binding @ sub` (semantically).
698 // Recurse into each binding in `sub` and find mutability or move conflicts.
699 let mut conflicts_move = Vec::new();
700 let mut conflicts_mut_mut = Vec::new();
701 let mut conflicts_mut_ref = Vec::new();
702 sub.each_binding(|_, hir_id, span, name| {
3dfed10e 703 match typeck_results.extract_binding_mode(sess, hir_id, span) {
74b04a01
XL
704 Some(ty::BindByReference(mut_inner)) => match (mut_outer, mut_inner) {
705 (Mutability::Not, Mutability::Not) => {} // Both sides are `ref`.
706 (Mutability::Mut, Mutability::Mut) => conflicts_mut_mut.push((span, name)), // 2x `ref mut`.
707 _ => conflicts_mut_ref.push((span, name)), // `ref` + `ref mut` in either direction.
708 },
709 Some(ty::BindByValue(_)) if is_binding_by_move(cx, hir_id, span) => {
710 conflicts_move.push((span, name)) // `ref mut?` + by-move conflict.
dfeec247 711 }
74b04a01 712 Some(ty::BindByValue(_)) | None => {} // `ref mut?` + by-copy is fine.
dfeec247
XL
713 }
714 });
74b04a01
XL
715
716 // Report errors if any.
717 if !conflicts_mut_mut.is_empty() {
718 // Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
719 let mut err = sess
720 .struct_span_err(pat.span, "cannot borrow value as mutable more than once at a time");
721 err.span_label(binding_span, format!("first mutable borrow, by `{}`, occurs here", name));
722 for (span, name) in conflicts_mut_mut {
723 err.span_label(span, format!("another mutable borrow, by `{}`, occurs here", name));
724 }
725 for (span, name) in conflicts_mut_ref {
726 err.span_label(span, format!("also borrowed as immutable, by `{}`, here", name));
727 }
728 for (span, name) in conflicts_move {
729 err.span_label(span, format!("also moved into `{}` here", name));
730 }
731 err.emit();
732 } else if !conflicts_mut_ref.is_empty() {
733 // Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
734 let (primary, also) = match mut_outer {
735 Mutability::Mut => ("mutable", "immutable"),
736 Mutability::Not => ("immutable", "mutable"),
737 };
738 let msg =
739 format!("cannot borrow value as {} because it is also borrowed as {}", also, primary);
740 let mut err = sess.struct_span_err(pat.span, &msg);
741 err.span_label(binding_span, format!("{} borrow, by `{}`, occurs here", primary, name));
742 for (span, name) in conflicts_mut_ref {
743 err.span_label(span, format!("{} borrow, by `{}`, occurs here", also, name));
744 }
745 for (span, name) in conflicts_move {
746 err.span_label(span, format!("also moved into `{}` here", name));
747 }
748 err.emit();
749 } else if !conflicts_move.is_empty() {
750 // Report by-ref and by-move conflicts, e.g. `ref x @ y`.
751 let mut err =
752 sess.struct_span_err(pat.span, "cannot move out of value because it is borrowed");
753 err.span_label(binding_span, format!("value borrowed, by `{}`, here", name));
754 for (span, name) in conflicts_move {
755 err.span_label(span, format!("value moved into `{}` here", name));
756 }
757 err.emit();
758 }
1a4d82fc
JJ
759}
760
94222f64
XL
761#[derive(Clone, Copy, Debug)]
762pub enum LetSource {
763 GenericLet,
764 IfLet,
765 IfLetGuard,
766 LetElse(Span),
767 WhileLet,
768}
dfeec247 769
94222f64
XL
770fn let_source(tcx: TyCtxt<'_>, pat_id: HirId) -> LetSource {
771 let hir = tcx.hir();
5099ac24 772
94222f64 773 let parent = hir.get_parent_node(pat_id);
5099ac24
FG
774 let parent_node = hir.get(parent);
775
776 match parent_node {
94222f64
XL
777 hir::Node::Arm(hir::Arm {
778 guard: Some(hir::Guard::IfLet(&hir::Pat { hir_id, .. }, _)),
779 ..
780 }) if hir_id == pat_id => {
781 return LetSource::IfLetGuard;
782 }
783 hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Let(..), span, .. }) => {
784 let expn_data = span.ctxt().outer_expn_data();
785 if let ExpnKind::Desugaring(DesugaringKind::LetElse) = expn_data.kind {
786 return LetSource::LetElse(expn_data.call_site);
787 }
dfeec247 788 }
94222f64
XL
789 _ => {}
790 }
5099ac24 791
94222f64
XL
792 let parent_parent = hir.get_parent_node(parent);
793 let parent_parent_node = hir.get(parent_parent);
3157f602 794
94222f64
XL
795 let parent_parent_parent = hir.get_parent_node(parent_parent);
796 let parent_parent_parent_parent = hir.get_parent_node(parent_parent_parent);
797 let parent_parent_parent_parent_node = hir.get(parent_parent_parent_parent);
dfeec247 798
94222f64
XL
799 if let hir::Node::Expr(hir::Expr {
800 kind: hir::ExprKind::Loop(_, _, hir::LoopSource::While, _),
801 ..
802 }) = parent_parent_parent_parent_node
803 {
5099ac24 804 return LetSource::WhileLet;
223e47cc 805 }
5099ac24
FG
806
807 if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::If(..), .. }) = parent_parent_node {
808 return LetSource::IfLet;
809 }
810
811 LetSource::GenericLet
812}
813
814// Since this function is called within a let context, it is reasonable to assume that any parent
815// `&&` infers a let chain
816fn is_let_chain(tcx: TyCtxt<'_>, pat_id: HirId) -> bool {
817 let hir = tcx.hir();
818 let parent = hir.get_parent_node(pat_id);
819 let parent_parent = hir.get_parent_node(parent);
820 matches!(
821 hir.get(parent_parent),
822 hir::Node::Expr(
823 hir::Expr {
824 kind: hir::ExprKind::Binary(Spanned { node: hir::BinOpKind::And, .. }, ..),
825 ..
826 },
827 ..
828 )
829 )
223e47cc 830}