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