]> git.proxmox.com Git - rustc.git/blame - src/librustc_mir_build/hair/pattern/check_match.rs
New upstream version 1.44.1+dfsg1
[rustc.git] / src / librustc_mir_build / hair / pattern / check_match.rs
CommitLineData
0531ce1d
XL
1use super::_match::Usefulness::*;
2use super::_match::WitnessPreference::*;
e74abb32 3use super::_match::{expand_pattern, is_useful, MatchCheckCtxt, Matrix, PatStack};
c30ab7b3 4
e74abb32 5use super::{PatCtxt, PatKind, PatternError};
223e47cc 6
74b04a01 7use rustc_ast::ast::Mutability;
dfeec247
XL
8use rustc_errors::{error_code, struct_span_err, Applicability, DiagnosticBuilder};
9use rustc_hir as hir;
10use rustc_hir::def::*;
11use rustc_hir::def_id::DefId;
12use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
13use rustc_hir::{HirId, Pat};
ba9703b0 14use rustc_middle::ty::{self, Ty, TyCtxt};
dfeec247
XL
15use rustc_session::lint::builtin::BINDINGS_WITH_VARIANT_NAME;
16use rustc_session::lint::builtin::{IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS};
17use rustc_session::parse::feature_err;
18use rustc_session::Session;
74b04a01 19use rustc_span::{sym, Span};
1a4d82fc 20
ff7c6d11 21use std::slice;
1a4d82fc 22
e74abb32
XL
23crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) {
24 let body_id = match tcx.hir().as_local_hir_id(def_id) {
25 None => return,
26 Some(id) => tcx.hir().body_owned_by(id),
ff7c6d11
XL
27 };
28
dfeec247
XL
29 let mut visitor =
30 MatchVisitor { tcx, tables: tcx.body_tables(body_id), param_env: tcx.param_env(def_id) };
416331ca 31 visitor.visit_body(tcx.hir().body(body_id));
ff7c6d11
XL
32}
33
416331ca 34fn create_e0004(sess: &Session, sp: Span, error_message: String) -> DiagnosticBuilder<'_> {
c30ab7b3 35 struct_span_err!(sess, sp, E0004, "{}", &error_message)
223e47cc
LB
36}
37
dc9dc135
XL
38struct MatchVisitor<'a, 'tcx> {
39 tcx: TyCtxt<'tcx>,
32a655c1 40 tables: &'a ty::TypeckTables<'tcx>,
7cac9316 41 param_env: ty::ParamEnv<'tcx>,
1a4d82fc
JJ
42}
43
e1599b0c 44impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> {
ba9703b0 45 type Map = intravisit::ErasedMap<'tcx>;
dfeec247 46
ba9703b0 47 fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
32a655c1 48 NestedVisitorMap::None
476ff2be
SL
49 }
50
dfeec247 51 fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
c30ab7b3
SL
52 intravisit::walk_expr(self, ex);
53
e74abb32 54 if let hir::ExprKind::Match(ref scrut, ref arms, source) = ex.kind {
416331ca 55 self.check_match(scrut, arms, source);
c30ab7b3 56 }
1a4d82fc 57 }
c30ab7b3 58
dfeec247 59 fn visit_local(&mut self, loc: &'tcx hir::Local<'tcx>) {
c30ab7b3
SL
60 intravisit::walk_local(self, loc);
61
e74abb32
XL
62 let (msg, sp) = match loc.source {
63 hir::LocalSource::Normal => ("local binding", Some(loc.span)),
64 hir::LocalSource::ForLoopDesugar => ("`for` loop binding", None),
65 hir::LocalSource::AsyncFn => ("async fn binding", None),
66 hir::LocalSource::AwaitDesugar => ("`await` future binding", None),
67 };
68 self.check_irrefutable(&loc.pat, msg, sp);
e74abb32 69 self.check_patterns(false, &loc.pat);
1a4d82fc 70 }
c30ab7b3 71
dfeec247
XL
72 fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
73 intravisit::walk_param(self, param);
74 self.check_irrefutable(&param.pat, "function argument", None);
75 self.check_patterns(false, &param.pat);
c30ab7b3 76 }
1a4d82fc
JJ
77}
78
e74abb32 79impl PatCtxt<'_, '_> {
cc61c64b
XL
80 fn report_inlining_errors(&self, pat_span: Span) {
81 for error in &self.errors {
82 match *error {
c30ab7b3 83 PatternError::StaticInPattern(span) => {
2c00a5a8
XL
84 self.span_e0158(span, "statics cannot be referenced in patterns")
85 }
dc9dc135 86 PatternError::AssocConstInPattern(span) => {
2c00a5a8 87 self.span_e0158(span, "associated consts cannot be referenced in patterns")
c30ab7b3 88 }
ba9703b0
XL
89 PatternError::ConstParamInPattern(span) => {
90 self.span_e0158(span, "const parameters cannot be referenced in patterns")
91 }
0531ce1d
XL
92 PatternError::FloatBug => {
93 // FIXME(#31407) this is only necessary because float parsing is buggy
ba9703b0 94 ::rustc_middle::mir::interpret::struct_error(
94b46f34 95 self.tcx.at(pat_span),
0531ce1d 96 "could not evaluate float literal (see issue #31407)",
e74abb32
XL
97 )
98 .emit();
0531ce1d
XL
99 }
100 PatternError::NonConstPath(span) => {
ba9703b0 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
e1599b0c 116impl<'tcx> MatchVisitor<'_, 'tcx> {
dfeec247 117 fn check_patterns(&mut self, has_guard: bool, pat: &Pat<'_>) {
74b04a01
XL
118 if !self.tcx.features().move_ref_pattern {
119 check_legality_of_move_bindings(self, has_guard, pat);
120 }
121 pat.walk_always(|pat| check_borrow_conflicts_in_at_patterns(self, pat));
dfeec247
XL
122 if !self.tcx.features().bindings_after_at {
123 check_legality_of_bindings_in_at_patterns(self, pat);
124 }
125 check_for_bindings_named_same_as_variants(self, pat);
cc61c64b 126 }
1a4d82fc 127
dfeec247
XL
128 fn lower_pattern<'p>(
129 &self,
130 cx: &mut MatchCheckCtxt<'p, 'tcx>,
131 pat: &'tcx hir::Pat<'tcx>,
132 have_errors: &mut bool,
133 ) -> (&'p super::Pat<'tcx>, Ty<'tcx>) {
134 let mut patcx = PatCtxt::new(self.tcx, self.param_env, self.tables);
135 patcx.include_lint_checks();
136 let pattern = patcx.lower_pattern(pat);
137 let pattern_ty = pattern.ty;
138 let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(cx, pattern));
139 if !patcx.errors.is_empty() {
140 *have_errors = true;
141 patcx.report_inlining_errors(pat.span);
142 }
143 (pattern, pattern_ty)
144 }
145
146 fn check_in_cx(&self, hir_id: HirId, f: impl FnOnce(MatchCheckCtxt<'_, 'tcx>)) {
74b04a01 147 let module = self.tcx.parent_module(hir_id);
ba9703b0 148 MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module.to_def_id(), |cx| f(cx));
dfeec247
XL
149 }
150
151 fn check_match(
152 &mut self,
153 scrut: &hir::Expr<'_>,
154 arms: &'tcx [hir::Arm<'tcx>],
155 source: hir::MatchSource,
156 ) {
c30ab7b3 157 for arm in arms {
dfeec247 158 // Check the arm for some things unrelated to exhaustiveness.
e74abb32 159 self.check_patterns(arm.guard.is_some(), &arm.pat);
c30ab7b3 160 }
1a4d82fc 161
dfeec247 162 self.check_in_cx(scrut.hir_id, |ref mut cx| {
c30ab7b3 163 let mut have_errors = false;
1a4d82fc 164
60c5eb7d 165 let inlined_arms: Vec<_> = arms
e74abb32 166 .iter()
dfeec247
XL
167 .map(|hir::Arm { pat, guard, .. }| {
168 (self.lower_pattern(cx, pat, &mut have_errors).0, pat.hir_id, guard.is_some())
b7449926 169 })
e74abb32 170 .collect();
1a4d82fc 171
c30ab7b3
SL
172 // Bail out early if inlining failed.
173 if have_errors {
174 return;
1a4d82fc
JJ
175 }
176
177 // Fourth, check for unreachable arms.
60c5eb7d 178 let matrix = check_arms(cx, &inlined_arms, source);
1a4d82fc 179
60c5eb7d 180 // Fifth, check if the match is exhaustive.
9fa01778 181 let scrut_ty = self.tables.node_type(scrut.hir_id);
60c5eb7d
XL
182 // Note: An empty match isn't the same as an empty matrix for diagnostics purposes,
183 // since an empty matrix can occur when there are arms, if those arms all have guards.
184 let is_empty_match = inlined_arms.is_empty();
185 check_exhaustive(cx, scrut_ty, scrut.span, &matrix, scrut.hir_id, is_empty_match);
c30ab7b3
SL
186 })
187 }
188
dfeec247
XL
189 fn check_irrefutable(&self, pat: &'tcx Pat<'tcx>, origin: &str, sp: Option<Span>) {
190 self.check_in_cx(pat.hir_id, |ref mut cx| {
191 let (pattern, pattern_ty) = self.lower_pattern(cx, pat, &mut false);
60c5eb7d 192 let pats: Matrix<'_, '_> = vec![PatStack::from_pattern(pattern)].into_iter().collect();
c30ab7b3 193
e74abb32 194 let witnesses = match check_not_useful(cx, pattern_ty, &pats, pat.hir_id) {
e1599b0c
XL
195 Ok(_) => return,
196 Err(err) => err,
c30ab7b3
SL
197 };
198
e1599b0c 199 let joined_patterns = joined_uncovered_patterns(&witnesses);
532ac7d7 200 let mut err = struct_span_err!(
e74abb32
XL
201 self.tcx.sess,
202 pat.span,
203 E0005,
e1599b0c 204 "refutable pattern in {}: {} not covered",
e74abb32
XL
205 origin,
206 joined_patterns
c30ab7b3 207 );
e74abb32
XL
208 let suggest_if_let = match &pat.kind {
209 hir::PatKind::Path(hir::QPath::Resolved(None, path))
210 if path.segments.len() == 1 && path.segments[0].args.is_none() =>
211 {
212 const_not_var(&mut err, cx.tcx, pat, path);
213 false
214 }
215 _ => {
216 err.span_label(
217 pat.span,
218 pattern_not_covered_label(&witnesses, &joined_patterns),
219 );
220 true
221 }
222 };
223
224 if let (Some(span), true) = (sp, suggest_if_let) {
225 err.note(
226 "`let` bindings require an \"irrefutable pattern\", like a `struct` or \
227 an `enum` with only one variant",
228 );
229 if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
230 err.span_suggestion(
231 span,
232 "you might want to use `if let` to ignore the variant that isn't matched",
233 format!("if {} {{ /* */ }}", &snippet[..snippet.len() - 1]),
234 Applicability::HasPlaceholders,
235 );
abe05a73 236 }
e74abb32
XL
237 err.note(
238 "for more information, visit \
239 https://doc.rust-lang.org/book/ch18-02-refutability.html",
240 );
241 }
242
e1599b0c 243 adt_defined_here(cx, &mut err, pattern_ty, &witnesses);
ba9703b0 244 err.note(&format!("the matched value is of type `{}`", pattern_ty));
532ac7d7 245 err.emit();
c30ab7b3 246 });
1a4d82fc
JJ
247 }
248}
249
e74abb32
XL
250/// A path pattern was interpreted as a constant, not a new variable.
251/// This caused an irrefutable match failure in e.g. `let`.
dfeec247
XL
252fn const_not_var(
253 err: &mut DiagnosticBuilder<'_>,
254 tcx: TyCtxt<'_>,
255 pat: &Pat<'_>,
256 path: &hir::Path<'_>,
257) {
e74abb32
XL
258 let descr = path.res.descr();
259 err.span_label(
260 pat.span,
261 format!("interpreted as {} {} pattern, not a new variable", path.res.article(), descr,),
262 );
263
264 err.span_suggestion(
265 pat.span,
266 "introduce a variable instead",
267 format!("{}_var", path.segments[0].ident).to_lowercase(),
268 // Cannot use `MachineApplicable` as it's not really *always* correct
269 // because there may be such an identifier in scope or the user maybe
270 // really wanted to match against the constant. This is quite unlikely however.
271 Applicability::MaybeIncorrect,
272 );
273
274 if let Some(span) = tcx.hir().res_span(path.res) {
275 err.span_label(span, format!("{} defined here", descr));
276 }
277}
278
dfeec247
XL
279fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pat<'_>) {
280 pat.walk_always(|p| {
e74abb32 281 if let hir::PatKind::Binding(_, _, ident, None) = p.kind {
dfeec247
XL
282 if let Some(ty::BindByValue(hir::Mutability::Not)) =
283 cx.tables.extract_binding_mode(cx.tcx.sess, p.hir_id, p.span)
284 {
285 let pat_ty = cx.tables.pat_ty(p).peel_refs();
e74abb32
XL
286 if let ty::Adt(edef, _) = pat_ty.kind {
287 if edef.is_enum()
288 && edef.variants.iter().any(|variant| {
289 variant.ident == ident && variant.ctor_kind == CtorKind::Const
290 })
291 {
74b04a01
XL
292 cx.tcx.struct_span_lint_hir(
293 BINDINGS_WITH_VARIANT_NAME,
294 p.hir_id,
295 p.span,
296 |lint| {
297 let ty_path = cx.tcx.def_path_str(edef.did);
298 lint.build(&format!(
dfeec247 299 "pattern binding `{}` is named the same as one \
74b04a01 300 of the variants of the type `{}`",
dfeec247 301 ident, ty_path
74b04a01
XL
302 ))
303 .code(error_code!(E0170))
304 .span_suggestion(
305 p.span,
306 "to match on the variant, qualify the path",
307 format!("{}::{}", ty_path, ident),
308 Applicability::MachineApplicable,
309 )
310 .emit();
311 },
312 )
8faf50e0 313 }
1a4d82fc
JJ
314 }
315 }
1a4d82fc 316 }
1a4d82fc
JJ
317 });
318}
319
c30ab7b3 320/// Checks for common cases of "catchall" patterns that may not be intended as such.
dfeec247
XL
321fn pat_is_catchall(pat: &super::Pat<'_>) -> bool {
322 use super::PatKind::*;
323 match &*pat.kind {
324 Binding { subpattern: None, .. } => true,
325 Binding { subpattern: Some(s), .. } | Deref { subpattern: s } => pat_is_catchall(s),
326 Leaf { subpatterns: s } => s.iter().all(|p| pat_is_catchall(&p.pattern)),
e74abb32 327 _ => false,
c30ab7b3 328 }
1a4d82fc
JJ
329}
330
dfeec247 331fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<Span>) {
74b04a01
XL
332 tcx.struct_span_lint_hir(UNREACHABLE_PATTERNS, id, span, |lint| {
333 let mut err = lint.build("unreachable pattern");
334 if let Some(catchall) = catchall {
335 // We had a catchall pattern, hint at that.
336 err.span_label(span, "unreachable pattern");
337 err.span_label(catchall, "matches any value");
338 }
339 err.emit();
340 });
dfeec247
XL
341}
342
343fn irrefutable_let_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, source: hir::MatchSource) {
74b04a01
XL
344 tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, |lint| {
345 let msg = match source {
346 hir::MatchSource::IfLetDesugar { .. } => "irrefutable if-let pattern",
347 hir::MatchSource::WhileLetDesugar => "irrefutable while-let pattern",
348 _ => bug!(),
349 };
350 lint.build(msg).emit()
351 });
dfeec247
XL
352}
353
354/// Check for unreachable patterns.
60c5eb7d
XL
355fn check_arms<'p, 'tcx>(
356 cx: &mut MatchCheckCtxt<'p, 'tcx>,
dfeec247 357 arms: &[(&'p super::Pat<'tcx>, HirId, bool)],
532ac7d7 358 source: hir::MatchSource,
60c5eb7d 359) -> Matrix<'p, 'tcx> {
c30ab7b3
SL
360 let mut seen = Matrix::empty();
361 let mut catchall = None;
dfeec247 362 for (arm_index, (pat, id, has_guard)) in arms.iter().copied().enumerate() {
60c5eb7d 363 let v = PatStack::from_pattern(pat);
ba9703b0 364 match is_useful(cx, &seen, &v, LeaveOutWitness, id, has_guard, true) {
60c5eb7d
XL
365 NotUseful => {
366 match source {
367 hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar => bug!(),
368
369 hir::MatchSource::IfLetDesugar { .. } | hir::MatchSource::WhileLetDesugar => {
dfeec247 370 // Check which arm we're on.
60c5eb7d
XL
371 match arm_index {
372 // The arm with the user-specified pattern.
dfeec247 373 0 => unreachable_pattern(cx.tcx, pat.span, id, None),
60c5eb7d 374 // The arm with the wildcard pattern.
dfeec247 375 1 => irrefutable_let_pattern(cx.tcx, pat.span, id, source),
60c5eb7d 376 _ => bug!(),
9fa01778 377 }
60c5eb7d 378 }
54a0048b 379
60c5eb7d 380 hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
dfeec247 381 unreachable_pattern(cx.tcx, pat.span, id, catchall);
1a4d82fc 382 }
60c5eb7d
XL
383
384 // Unreachable patterns in try and await expressions occur when one of
385 // the arms are an uninhabited type. Which is OK.
386 hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
1a4d82fc 387 }
1a4d82fc 388 }
60c5eb7d
XL
389 Useful(unreachable_subpatterns) => {
390 for pat in unreachable_subpatterns {
dfeec247 391 unreachable_pattern(cx.tcx, pat.span, id, None);
c30ab7b3 392 }
223e47cc 393 }
60c5eb7d
XL
394 UsefulWithWitness(_) => bug!(),
395 }
396 if !has_guard {
397 seen.push(v);
dfeec247 398 if catchall.is_none() && pat_is_catchall(pat) {
60c5eb7d
XL
399 catchall = Some(pat.span);
400 }
223e47cc
LB
401 }
402 }
60c5eb7d 403 seen
223e47cc
LB
404}
405
60c5eb7d
XL
406fn check_not_useful<'p, 'tcx>(
407 cx: &mut MatchCheckCtxt<'p, 'tcx>,
e1599b0c 408 ty: Ty<'tcx>,
60c5eb7d 409 matrix: &Matrix<'p, 'tcx>,
e74abb32
XL
410 hir_id: HirId,
411) -> Result<(), Vec<super::Pat<'tcx>>> {
60c5eb7d
XL
412 let wild_pattern = cx.pattern_arena.alloc(super::Pat::wildcard_from_ty(ty));
413 let v = PatStack::from_pattern(wild_pattern);
ba9703b0
XL
414
415 // false is given for `is_under_guard` argument due to the wildcard
416 // pattern not having a guard
417 match is_useful(cx, matrix, &v, ConstructWitness, hir_id, false, true) {
e1599b0c
XL
418 NotUseful => Ok(()), // This is good, wildcard pattern isn't reachable.
419 UsefulWithWitness(pats) => Err(if pats.is_empty() {
60c5eb7d 420 bug!("Exhaustiveness check returned no witnesses")
e1599b0c
XL
421 } else {
422 pats.into_iter().map(|w| w.single_pattern()).collect()
423 }),
60c5eb7d 424 Useful(_) => bug!(),
e1599b0c
XL
425 }
426}
427
60c5eb7d
XL
428fn check_exhaustive<'p, 'tcx>(
429 cx: &mut MatchCheckCtxt<'p, 'tcx>,
532ac7d7
XL
430 scrut_ty: Ty<'tcx>,
431 sp: Span,
60c5eb7d 432 matrix: &Matrix<'p, 'tcx>,
e74abb32 433 hir_id: HirId,
60c5eb7d 434 is_empty_match: bool,
532ac7d7 435) {
60c5eb7d
XL
436 // In the absence of the `exhaustive_patterns` feature, empty matches are not detected by
437 // `is_useful` to exhaustively match uninhabited types, so we manually check here.
438 if is_empty_match && !cx.tcx.features().exhaustive_patterns {
439 let scrutinee_is_visibly_uninhabited = match scrut_ty.kind {
440 ty::Never => true,
441 ty::Adt(def, _) => {
442 def.is_enum()
443 && def.variants.is_empty()
444 && !cx.is_foreign_non_exhaustive_enum(scrut_ty)
445 }
446 _ => false,
447 };
448 if scrutinee_is_visibly_uninhabited {
449 // If the type *is* uninhabited, an empty match is vacuously exhaustive.
450 return;
451 }
452 }
453
e74abb32 454 let witnesses = match check_not_useful(cx, scrut_ty, matrix, hir_id) {
e1599b0c
XL
455 Ok(_) => return,
456 Err(err) => err,
32a655c1 457 };
7cac9316 458
60c5eb7d
XL
459 let non_empty_enum = match scrut_ty.kind {
460 ty::Adt(def, _) => def.is_enum() && !def.variants.is_empty(),
461 _ => false,
462 };
463 // In the case of an empty match, replace the '`_` not covered' diagnostic with something more
464 // informative.
465 let mut err;
466 if is_empty_match && !non_empty_enum {
467 err = create_e0004(
468 cx.tcx.sess,
469 sp,
470 format!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty),
471 );
472 } else {
473 let joined_patterns = joined_uncovered_patterns(&witnesses);
474 err = create_e0004(
475 cx.tcx.sess,
476 sp,
477 format!("non-exhaustive patterns: {} not covered", joined_patterns),
478 );
479 err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns));
480 };
481
e1599b0c
XL
482 adt_defined_here(cx, &mut err, scrut_ty, &witnesses);
483 err.help(
484 "ensure that all possible cases are being handled, \
e74abb32 485 possibly by adding wildcards or more match arms",
60c5eb7d 486 );
ba9703b0 487 err.note(&format!("the matched value is of type `{}`", scrut_ty));
60c5eb7d 488 err.emit();
e1599b0c 489}
7cac9316 490
e74abb32 491fn joined_uncovered_patterns(witnesses: &[super::Pat<'_>]) -> String {
e1599b0c
XL
492 const LIMIT: usize = 3;
493 match witnesses {
494 [] => bug!(),
495 [witness] => format!("`{}`", witness),
496 [head @ .., tail] if head.len() < LIMIT => {
497 let head: Vec<_> = head.iter().map(<_>::to_string).collect();
498 format!("`{}` and `{}`", head.join("`, `"), tail)
1a4d82fc 499 }
e1599b0c
XL
500 _ => {
501 let (head, tail) = witnesses.split_at(LIMIT);
502 let head: Vec<_> = head.iter().map(<_>::to_string).collect();
503 format!("`{}` and {} more", head.join("`, `"), tail.len())
9fa01778 504 }
1a4d82fc
JJ
505 }
506}
507
e74abb32 508fn pattern_not_covered_label(witnesses: &[super::Pat<'_>], joined_patterns: &str) -> String {
60c5eb7d 509 format!("pattern{} {} not covered", rustc_errors::pluralize!(witnesses.len()), joined_patterns)
e1599b0c
XL
510}
511
512/// Point at the definition of non-covered `enum` variants.
513fn adt_defined_here(
514 cx: &MatchCheckCtxt<'_, '_>,
515 err: &mut DiagnosticBuilder<'_>,
516 ty: Ty<'_>,
e74abb32 517 witnesses: &[super::Pat<'_>],
e1599b0c
XL
518) {
519 let ty = ty.peel_refs();
e74abb32 520 if let ty::Adt(def, _) = ty.kind {
e1599b0c
XL
521 if let Some(sp) = cx.tcx.hir().span_if_local(def.did) {
522 err.span_label(sp, format!("`{}` defined here", ty));
523 }
524
525 if witnesses.len() < 4 {
526 for sp in maybe_point_at_variant(ty, &witnesses) {
527 err.span_label(sp, "not covered");
528 }
529 }
530 }
531}
532
e74abb32 533fn maybe_point_at_variant(ty: Ty<'_>, patterns: &[super::Pat<'_>]) -> Vec<Span> {
532ac7d7 534 let mut covered = vec![];
e74abb32 535 if let ty::Adt(def, _) = ty.kind {
532ac7d7 536 // Don't point at variants that have already been covered due to other patterns to avoid
e1599b0c 537 // visual clutter.
532ac7d7 538 for pattern in patterns {
e74abb32 539 use PatKind::{AscribeUserType, Deref, Leaf, Or, Variant};
e1599b0c
XL
540 match &*pattern.kind {
541 AscribeUserType { subpattern, .. } | Deref { subpattern } => {
542 covered.extend(maybe_point_at_variant(ty, slice::from_ref(&subpattern)));
543 }
544 Variant { adt_def, variant_index, subpatterns, .. } if adt_def.did == def.did => {
532ac7d7
XL
545 let sp = def.variants[*variant_index].ident.span;
546 if covered.contains(&sp) {
547 continue;
548 }
549 covered.push(sp);
e1599b0c 550
e74abb32
XL
551 let pats = subpatterns
552 .iter()
532ac7d7 553 .map(|field_pattern| field_pattern.pattern.clone())
e1599b0c
XL
554 .collect::<Box<[_]>>();
555 covered.extend(maybe_point_at_variant(ty, &pats));
532ac7d7 556 }
e1599b0c 557 Leaf { subpatterns } => {
e74abb32
XL
558 let pats = subpatterns
559 .iter()
e1599b0c
XL
560 .map(|field_pattern| field_pattern.pattern.clone())
561 .collect::<Box<[_]>>();
562 covered.extend(maybe_point_at_variant(ty, &pats));
563 }
564 Or { pats } => {
565 let pats = pats.iter().cloned().collect::<Box<[_]>>();
566 covered.extend(maybe_point_at_variant(ty, &pats));
567 }
568 _ => {}
532ac7d7
XL
569 }
570 }
571 }
572 covered
573}
574
74b04a01
XL
575/// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.
576fn is_binding_by_move(cx: &MatchVisitor<'_, '_>, hir_id: HirId, span: Span) -> bool {
577 !cx.tables.node_type(hir_id).is_copy_modulo_regions(cx.tcx, cx.param_env, span)
578}
579
dfeec247
XL
580/// Check the legality of legality of by-move bindings.
581fn check_legality_of_move_bindings(cx: &mut MatchVisitor<'_, '_>, has_guard: bool, pat: &Pat<'_>) {
582 let sess = cx.tcx.sess;
583 let tables = cx.tables;
584
585 // Find all by-ref spans.
586 let mut by_ref_spans = Vec::new();
e74abb32 587 pat.each_binding(|_, hir_id, span, _| {
dfeec247
XL
588 if let Some(ty::BindByReference(_)) = tables.extract_binding_mode(sess, hir_id, span) {
589 by_ref_spans.push(span);
e74abb32
XL
590 }
591 });
592
dfeec247
XL
593 // Find bad by-move spans:
594 let by_move_spans = &mut Vec::new();
595 let mut check_move = |p: &Pat<'_>, sub: Option<&Pat<'_>>| {
e74abb32
XL
596 // Check legality of moving out of the enum.
597 //
598 // `x @ Foo(..)` is legal, but `x @ Foo(y)` isn't.
476ff2be 599 if sub.map_or(false, |p| p.contains_bindings()) {
dfeec247 600 struct_span_err!(sess, p.span, E0007, "cannot bind by-move with sub-bindings")
7cac9316 601 .span_label(p.span, "binds an already bound by-move value by moving it")
5bcae85e 602 .emit();
dfeec247
XL
603 } else if !has_guard && !by_ref_spans.is_empty() {
604 by_move_spans.push(p.span);
223e47cc
LB
605 }
606 };
dfeec247 607 pat.walk_always(|p| {
e74abb32 608 if let hir::PatKind::Binding(.., sub) = &p.kind {
dfeec247 609 if let Some(ty::BindByValue(_)) = tables.extract_binding_mode(sess, p.hir_id, p.span) {
74b04a01 610 if is_binding_by_move(cx, p.hir_id, p.span) {
dfeec247 611 check_move(p, sub.as_deref());
c30ab7b3 612 }
223e47cc 613 }
e74abb32 614 }
e74abb32
XL
615 });
616
dfeec247
XL
617 // Found some bad by-move spans, error!
618 if !by_move_spans.is_empty() {
74b04a01
XL
619 let mut err = feature_err(
620 &sess.parse_sess,
621 sym::move_ref_pattern,
622 by_move_spans.clone(),
623 "binding by-move and by-ref in the same pattern is unstable",
0bf4aa26 624 );
dfeec247
XL
625 for span in by_ref_spans.iter() {
626 err.span_label(*span, "by-ref pattern here");
48663c56 627 }
dfeec247 628 for span in by_move_spans.iter() {
0bf4aa26
XL
629 err.span_label(*span, "by-move pattern here");
630 }
631 err.emit();
632 }
1a4d82fc
JJ
633}
634
74b04a01 635/// Check that there are no borrow or move conflicts in `binding @ subpat` patterns.
dfeec247
XL
636///
637/// For example, this would reject:
638/// - `ref x @ Some(ref mut y)`,
74b04a01
XL
639/// - `ref mut x @ Some(ref y)`,
640/// - `ref mut x @ Some(ref mut y)`,
641/// - `ref mut? x @ Some(y)`, and
642/// - `x @ Some(ref mut? y)`.
dfeec247
XL
643///
644/// This analysis is *not* subsumed by NLL.
645fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_>, pat: &Pat<'_>) {
74b04a01
XL
646 // Extract `sub` in `binding @ sub`.
647 let (name, sub) = match &pat.kind {
648 hir::PatKind::Binding(.., name, Some(sub)) => (*name, sub),
649 _ => return,
dfeec247 650 };
74b04a01 651 let binding_span = pat.span.with_hi(name.span.hi());
dfeec247 652
74b04a01
XL
653 let tables = cx.tables;
654 let sess = cx.tcx.sess;
1a4d82fc 655
74b04a01
XL
656 // Get the binding move, extract the mutability if by-ref.
657 let mut_outer = match tables.extract_binding_mode(sess, pat.hir_id, pat.span) {
658 Some(ty::BindByValue(_)) if is_binding_by_move(cx, pat.hir_id, pat.span) => {
659 // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`.
660 let mut conflicts_ref = Vec::new();
661 sub.each_binding(|_, hir_id, span, _| {
662 match tables.extract_binding_mode(sess, hir_id, span) {
663 Some(ty::BindByValue(_)) | None => {}
664 Some(ty::BindByReference(_)) => conflicts_ref.push(span),
dfeec247 665 }
74b04a01
XL
666 });
667 if !conflicts_ref.is_empty() {
668 let occurs_because = format!(
669 "move occurs because `{}` has type `{}` which does not implement the `Copy` trait",
670 name,
671 tables.node_type(pat.hir_id),
672 );
673 sess.struct_span_err(pat.span, "borrow of moved value")
674 .span_label(binding_span, format!("value moved into `{}` here", name))
675 .span_label(binding_span, occurs_because)
676 .span_labels(conflicts_ref, "value borrowed here after move")
677 .emit();
dfeec247 678 }
74b04a01
XL
679 return;
680 }
681 Some(ty::BindByValue(_)) | None => return,
682 Some(ty::BindByReference(m)) => m,
683 };
dfeec247 684
74b04a01
XL
685 // We now have `ref $mut_outer binding @ sub` (semantically).
686 // Recurse into each binding in `sub` and find mutability or move conflicts.
687 let mut conflicts_move = Vec::new();
688 let mut conflicts_mut_mut = Vec::new();
689 let mut conflicts_mut_ref = Vec::new();
690 sub.each_binding(|_, hir_id, span, name| {
691 match tables.extract_binding_mode(sess, hir_id, span) {
692 Some(ty::BindByReference(mut_inner)) => match (mut_outer, mut_inner) {
693 (Mutability::Not, Mutability::Not) => {} // Both sides are `ref`.
694 (Mutability::Mut, Mutability::Mut) => conflicts_mut_mut.push((span, name)), // 2x `ref mut`.
695 _ => conflicts_mut_ref.push((span, name)), // `ref` + `ref mut` in either direction.
696 },
697 Some(ty::BindByValue(_)) if is_binding_by_move(cx, hir_id, span) => {
698 conflicts_move.push((span, name)) // `ref mut?` + by-move conflict.
dfeec247 699 }
74b04a01 700 Some(ty::BindByValue(_)) | None => {} // `ref mut?` + by-copy is fine.
dfeec247
XL
701 }
702 });
74b04a01
XL
703
704 // Report errors if any.
705 if !conflicts_mut_mut.is_empty() {
706 // Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
707 let mut err = sess
708 .struct_span_err(pat.span, "cannot borrow value as mutable more than once at a time");
709 err.span_label(binding_span, format!("first mutable borrow, by `{}`, occurs here", name));
710 for (span, name) in conflicts_mut_mut {
711 err.span_label(span, format!("another mutable borrow, by `{}`, occurs here", name));
712 }
713 for (span, name) in conflicts_mut_ref {
714 err.span_label(span, format!("also borrowed as immutable, by `{}`, here", name));
715 }
716 for (span, name) in conflicts_move {
717 err.span_label(span, format!("also moved into `{}` here", name));
718 }
719 err.emit();
720 } else if !conflicts_mut_ref.is_empty() {
721 // Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
722 let (primary, also) = match mut_outer {
723 Mutability::Mut => ("mutable", "immutable"),
724 Mutability::Not => ("immutable", "mutable"),
725 };
726 let msg =
727 format!("cannot borrow value as {} because it is also borrowed as {}", also, primary);
728 let mut err = sess.struct_span_err(pat.span, &msg);
729 err.span_label(binding_span, format!("{} borrow, by `{}`, occurs here", primary, name));
730 for (span, name) in conflicts_mut_ref {
731 err.span_label(span, format!("{} borrow, by `{}`, occurs here", also, name));
732 }
733 for (span, name) in conflicts_move {
734 err.span_label(span, format!("also moved into `{}` here", name));
735 }
736 err.emit();
737 } else if !conflicts_move.is_empty() {
738 // Report by-ref and by-move conflicts, e.g. `ref x @ y`.
739 let mut err =
740 sess.struct_span_err(pat.span, "cannot move out of value because it is borrowed");
741 err.span_label(binding_span, format!("value borrowed, by `{}`, here", name));
742 for (span, name) in conflicts_move {
743 err.span_label(span, format!("value moved into `{}` here", name));
744 }
745 err.emit();
746 }
1a4d82fc
JJ
747}
748
dfeec247
XL
749/// Forbids bindings in `@` patterns. This used to be is necessary for memory safety,
750/// because of the way rvalues were handled in the borrow check. (See issue #14587.)
751fn check_legality_of_bindings_in_at_patterns(cx: &MatchVisitor<'_, '_>, pat: &Pat<'_>) {
752 AtBindingPatternVisitor { cx, bindings_allowed: true }.visit_pat(pat);
753
754 struct AtBindingPatternVisitor<'a, 'b, 'tcx> {
755 cx: &'a MatchVisitor<'b, 'tcx>,
756 bindings_allowed: bool,
476ff2be
SL
757 }
758
dfeec247 759 impl<'v> Visitor<'v> for AtBindingPatternVisitor<'_, '_, '_> {
ba9703b0 760 type Map = intravisit::ErasedMap<'v>;
dfeec247 761
ba9703b0 762 fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
dfeec247
XL
763 NestedVisitorMap::None
764 }
3157f602 765
dfeec247
XL
766 fn visit_pat(&mut self, pat: &Pat<'_>) {
767 match pat.kind {
768 hir::PatKind::Binding(.., ref subpat) => {
769 if !self.bindings_allowed {
770 feature_err(
771 &self.cx.tcx.sess.parse_sess,
772 sym::bindings_after_at,
773 pat.span,
774 "pattern bindings after an `@` are unstable",
775 )
776 .emit();
777 }
778
779 if subpat.is_some() {
780 let bindings_were_allowed = self.bindings_allowed;
781 self.bindings_allowed = false;
782 intravisit::walk_pat(self, pat);
783 self.bindings_allowed = bindings_were_allowed;
784 }
3157f602 785 }
dfeec247 786 _ => intravisit::walk_pat(self, pat),
1a4d82fc 787 }
223e47cc 788 }
223e47cc
LB
789 }
790}