]>
Commit | Line | Data |
---|---|---|
0531ce1d XL |
1 | use super::_match::{MatchCheckCtxt, Matrix, expand_pattern, is_useful}; |
2 | use super::_match::Usefulness::*; | |
3 | use super::_match::WitnessPreference::*; | |
c30ab7b3 | 4 | |
0531ce1d | 5 | use super::{Pattern, PatternContext, PatternError, PatternKind}; |
223e47cc | 6 | |
54a0048b SL |
7 | use rustc::middle::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor}; |
8 | use rustc::middle::expr_use_visitor::{LoanCause, MutateMode}; | |
9 | use rustc::middle::expr_use_visitor as euv; | |
83c7162d | 10 | use rustc::middle::mem_categorization::cmt_; |
ea8adc8c | 11 | use rustc::middle::region; |
c30ab7b3 | 12 | use rustc::session::Session; |
32a655c1 | 13 | use rustc::ty::{self, Ty, TyCtxt}; |
532ac7d7 | 14 | use rustc::ty::subst::{InternalSubsts, SubstsRef}; |
32a655c1 | 15 | use rustc::lint; |
8faf50e0 | 16 | use rustc_errors::{Applicability, DiagnosticBuilder}; |
e9174d1e | 17 | |
c30ab7b3 | 18 | use rustc::hir::def::*; |
ff7c6d11 XL |
19 | use rustc::hir::def_id::DefId; |
20 | use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; | |
c30ab7b3 | 21 | use rustc::hir::{self, Pat, PatKind}; |
1a4d82fc | 22 | |
0731742a | 23 | use smallvec::smallvec; |
ff7c6d11 | 24 | use std::slice; |
1a4d82fc | 25 | |
c30ab7b3 | 26 | use syntax::ptr::P; |
0bf4aa26 | 27 | use syntax_pos::{Span, DUMMY_SP, MultiSpan}; |
1a4d82fc | 28 | |
dc9dc135 | 29 | pub(crate) fn check_match<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) { |
532ac7d7 | 30 | let body_id = if let Some(id) = tcx.hir().as_local_hir_id(def_id) { |
0731742a | 31 | tcx.hir().body_owned_by(id) |
ff7c6d11 | 32 | } else { |
532ac7d7 | 33 | return; |
ff7c6d11 XL |
34 | }; |
35 | ||
532ac7d7 XL |
36 | MatchVisitor { |
37 | tcx, | |
dc9dc135 | 38 | body_owner: def_id, |
532ac7d7 XL |
39 | tables: tcx.body_tables(body_id), |
40 | region_scope_tree: &tcx.region_scope_tree(def_id), | |
41 | param_env: tcx.param_env(def_id), | |
42 | identity_substs: InternalSubsts::identity_for_item(tcx, def_id), | |
43 | }.visit_body(tcx.hir().body(body_id)); | |
ff7c6d11 XL |
44 | } |
45 | ||
c30ab7b3 SL |
46 | fn create_e0004<'a>(sess: &'a Session, sp: Span, error_message: String) -> DiagnosticBuilder<'a> { |
47 | struct_span_err!(sess, sp, E0004, "{}", &error_message) | |
223e47cc LB |
48 | } |
49 | ||
dc9dc135 XL |
50 | struct MatchVisitor<'a, 'tcx> { |
51 | tcx: TyCtxt<'tcx>, | |
52 | body_owner: DefId, | |
32a655c1 | 53 | tables: &'a ty::TypeckTables<'tcx>, |
7cac9316 | 54 | param_env: ty::ParamEnv<'tcx>, |
532ac7d7 | 55 | identity_substs: SubstsRef<'tcx>, |
ea8adc8c | 56 | region_scope_tree: &'a region::ScopeTree, |
1a4d82fc JJ |
57 | } |
58 | ||
476ff2be SL |
59 | impl<'a, 'tcx> Visitor<'tcx> for MatchVisitor<'a, 'tcx> { |
60 | fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { | |
32a655c1 | 61 | NestedVisitorMap::None |
476ff2be SL |
62 | } |
63 | ||
64 | fn visit_expr(&mut self, ex: &'tcx hir::Expr) { | |
c30ab7b3 SL |
65 | intravisit::walk_expr(self, ex); |
66 | ||
67 | match ex.node { | |
8faf50e0 | 68 | hir::ExprKind::Match(ref scrut, ref arms, source) => { |
32a655c1 | 69 | self.check_match(scrut, arms, source); |
c30ab7b3 SL |
70 | } |
71 | _ => {} | |
72 | } | |
1a4d82fc | 73 | } |
c30ab7b3 | 74 | |
476ff2be | 75 | fn visit_local(&mut self, loc: &'tcx hir::Local) { |
c30ab7b3 SL |
76 | intravisit::walk_local(self, loc); |
77 | ||
7cac9316 XL |
78 | self.check_irrefutable(&loc.pat, match loc.source { |
79 | hir::LocalSource::Normal => "local binding", | |
80 | hir::LocalSource::ForLoopDesugar => "`for` loop binding", | |
48663c56 XL |
81 | hir::LocalSource::AsyncFn => "async fn binding", |
82 | hir::LocalSource::AwaitDesugar => "`await` future binding", | |
7cac9316 | 83 | }); |
c30ab7b3 SL |
84 | |
85 | // Check legality of move bindings and `@` patterns. | |
ff7c6d11 | 86 | self.check_patterns(false, slice::from_ref(&loc.pat)); |
1a4d82fc | 87 | } |
c30ab7b3 | 88 | |
32a655c1 SL |
89 | fn visit_body(&mut self, body: &'tcx hir::Body) { |
90 | intravisit::walk_body(self, body); | |
1a4d82fc | 91 | |
32a655c1 | 92 | for arg in &body.arguments { |
7cac9316 | 93 | self.check_irrefutable(&arg.pat, "function argument"); |
ff7c6d11 | 94 | self.check_patterns(false, slice::from_ref(&arg.pat)); |
c30ab7b3 SL |
95 | } |
96 | } | |
1a4d82fc JJ |
97 | } |
98 | ||
2c00a5a8 | 99 | |
3b2f2976 | 100 | impl<'a, 'tcx> PatternContext<'a, 'tcx> { |
cc61c64b XL |
101 | fn report_inlining_errors(&self, pat_span: Span) { |
102 | for error in &self.errors { | |
103 | match *error { | |
c30ab7b3 | 104 | PatternError::StaticInPattern(span) => { |
2c00a5a8 XL |
105 | self.span_e0158(span, "statics cannot be referenced in patterns") |
106 | } | |
dc9dc135 | 107 | PatternError::AssocConstInPattern(span) => { |
2c00a5a8 | 108 | self.span_e0158(span, "associated consts cannot be referenced in patterns") |
c30ab7b3 | 109 | } |
0531ce1d XL |
110 | PatternError::FloatBug => { |
111 | // FIXME(#31407) this is only necessary because float parsing is buggy | |
8faf50e0 | 112 | ::rustc::mir::interpret::struct_error( |
94b46f34 | 113 | self.tcx.at(pat_span), |
0531ce1d XL |
114 | "could not evaluate float literal (see issue #31407)", |
115 | ).emit(); | |
116 | } | |
117 | PatternError::NonConstPath(span) => { | |
8faf50e0 | 118 | ::rustc::mir::interpret::struct_error( |
94b46f34 | 119 | self.tcx.at(span), |
0531ce1d XL |
120 | "runtime values cannot be referenced in patterns", |
121 | ).emit(); | |
1a4d82fc JJ |
122 | } |
123 | } | |
c30ab7b3 SL |
124 | } |
125 | } | |
2c00a5a8 XL |
126 | |
127 | fn span_e0158(&self, span: Span, text: &str) { | |
128 | span_err!(self.tcx.sess, span, E0158, "{}", text) | |
129 | } | |
cc61c64b XL |
130 | } |
131 | ||
132 | impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { | |
133 | fn check_patterns(&self, has_guard: bool, pats: &[P<Pat>]) { | |
134 | check_legality_of_move_bindings(self, has_guard, pats); | |
135 | for pat in pats { | |
136 | check_legality_of_bindings_in_at_patterns(self, pat); | |
137 | } | |
138 | } | |
1a4d82fc | 139 | |
c30ab7b3 SL |
140 | fn check_match( |
141 | &self, | |
142 | scrut: &hir::Expr, | |
ea8adc8c | 143 | arms: &'tcx [hir::Arm], |
32a655c1 | 144 | source: hir::MatchSource) |
c30ab7b3 SL |
145 | { |
146 | for arm in arms { | |
147 | // First, check legality of move bindings. | |
148 | self.check_patterns(arm.guard.is_some(), &arm.pats); | |
149 | ||
150 | // Second, if there is a guard on each arm, make sure it isn't | |
151 | // assigning or borrowing anything mutably. | |
152 | if let Some(ref guard) = arm.guard { | |
48663c56 | 153 | if !self.tcx.features().bind_by_move_pattern_guards { |
94b46f34 XL |
154 | check_for_mutation_in_guard(self, &guard); |
155 | } | |
c30ab7b3 | 156 | } |
1a4d82fc | 157 | |
c30ab7b3 SL |
158 | // Third, perform some lints. |
159 | for pat in &arm.pats { | |
0731742a | 160 | check_for_bindings_named_same_as_variants(self, pat); |
1a4d82fc | 161 | } |
c30ab7b3 | 162 | } |
1a4d82fc | 163 | |
dc9dc135 | 164 | let module = self.tcx.hir().get_module_parent(scrut.hir_id); |
0731742a | 165 | MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module, |ref mut cx| { |
c30ab7b3 | 166 | let mut have_errors = false; |
1a4d82fc | 167 | |
c30ab7b3 SL |
168 | let inlined_arms : Vec<(Vec<_>, _)> = arms.iter().map(|arm| ( |
169 | arm.pats.iter().map(|pat| { | |
3b2f2976 XL |
170 | let mut patcx = PatternContext::new(self.tcx, |
171 | self.param_env.and(self.identity_substs), | |
172 | self.tables); | |
c30ab7b3 SL |
173 | let pattern = expand_pattern(cx, patcx.lower_pattern(&pat)); |
174 | if !patcx.errors.is_empty() { | |
cc61c64b | 175 | patcx.report_inlining_errors(pat.span); |
c30ab7b3 SL |
176 | have_errors = true; |
177 | } | |
178 | (pattern, &**pat) | |
179 | }).collect(), | |
b7449926 XL |
180 | arm.guard.as_ref().map(|g| match g { |
181 | hir::Guard::If(ref e) => &**e, | |
182 | }) | |
c30ab7b3 | 183 | )).collect(); |
1a4d82fc | 184 | |
c30ab7b3 SL |
185 | // Bail out early if inlining failed. |
186 | if have_errors { | |
187 | return; | |
1a4d82fc JJ |
188 | } |
189 | ||
190 | // Fourth, check for unreachable arms. | |
c30ab7b3 | 191 | check_arms(cx, &inlined_arms, source); |
223e47cc | 192 | |
32a655c1 SL |
193 | // Then, if the match has no arms, check whether the scrutinee |
194 | // is uninhabited. | |
9fa01778 | 195 | let pat_ty = self.tables.node_type(scrut.hir_id); |
dc9dc135 | 196 | let module = self.tcx.hir().get_module_parent(scrut.hir_id); |
532ac7d7 XL |
197 | let mut def_span = None; |
198 | let mut missing_variants = vec![]; | |
1a4d82fc | 199 | if inlined_arms.is_empty() { |
0531ce1d | 200 | let scrutinee_is_uninhabited = if self.tcx.features().exhaustive_patterns { |
abe05a73 | 201 | self.tcx.is_ty_uninhabited_from(module, pat_ty) |
32a655c1 | 202 | } else { |
0731742a XL |
203 | match pat_ty.sty { |
204 | ty::Never => true, | |
532ac7d7 XL |
205 | ty::Adt(def, _) => { |
206 | def_span = self.tcx.hir().span_if_local(def.did); | |
207 | if def.variants.len() < 4 && !def.variants.is_empty() { | |
208 | // keep around to point at the definition of non-covered variants | |
209 | missing_variants = def.variants.iter() | |
210 | .map(|variant| variant.ident) | |
211 | .collect(); | |
212 | } | |
48663c56 XL |
213 | |
214 | let is_non_exhaustive_and_non_local = | |
215 | def.is_variant_list_non_exhaustive() && !def.did.is_local(); | |
216 | ||
217 | !(is_non_exhaustive_and_non_local) && def.variants.is_empty() | |
532ac7d7 | 218 | }, |
0731742a XL |
219 | _ => false |
220 | } | |
32a655c1 SL |
221 | }; |
222 | if !scrutinee_is_uninhabited { | |
1a4d82fc | 223 | // We know the type is inhabited, so this must be wrong |
532ac7d7 XL |
224 | let mut err = create_e0004( |
225 | self.tcx.sess, | |
226 | scrut.span, | |
227 | format!("non-exhaustive patterns: {}", match missing_variants.len() { | |
228 | 0 => format!("type `{}` is non-empty", pat_ty), | |
229 | 1 => format!( | |
230 | "pattern `{}` of type `{}` is not handled", | |
231 | missing_variants[0].name, | |
232 | pat_ty, | |
233 | ), | |
234 | _ => format!("multiple patterns of type `{}` are not handled", pat_ty), | |
235 | }), | |
236 | ); | |
237 | err.help("ensure that all possible cases are being handled, \ | |
238 | possibly by adding wildcards or more match arms"); | |
239 | if let Some(sp) = def_span { | |
240 | err.span_label(sp, format!("`{}` defined here", pat_ty)); | |
241 | } | |
242 | // point at the definition of non-covered enum variants | |
243 | for variant in &missing_variants { | |
244 | err.span_label(variant.span, "variant not covered"); | |
245 | } | |
9cc50fc6 | 246 | err.emit(); |
1a4d82fc | 247 | } |
5bcae85e | 248 | // If the type *is* uninhabited, it's vacuously exhaustive |
1a4d82fc | 249 | return; |
223e47cc | 250 | } |
1a4d82fc | 251 | |
9fa01778 | 252 | let matrix: Matrix<'_, '_> = inlined_arms |
1a4d82fc JJ |
253 | .iter() |
254 | .filter(|&&(_, guard)| guard.is_none()) | |
62682a34 | 255 | .flat_map(|arm| &arm.0) |
0731742a | 256 | .map(|pat| smallvec![pat.0]) |
1a4d82fc | 257 | .collect(); |
9fa01778 | 258 | let scrut_ty = self.tables.node_type(scrut.hir_id); |
7cac9316 | 259 | check_exhaustive(cx, scrut_ty, scrut.span, &matrix); |
c30ab7b3 SL |
260 | }) |
261 | } | |
262 | ||
ea8adc8c | 263 | fn check_irrefutable(&self, pat: &'tcx Pat, origin: &str) { |
dc9dc135 | 264 | let module = self.tcx.hir().get_module_parent(pat.hir_id); |
0731742a | 265 | MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module, |ref mut cx| { |
3b2f2976 XL |
266 | let mut patcx = PatternContext::new(self.tcx, |
267 | self.param_env.and(self.identity_substs), | |
268 | self.tables); | |
32a655c1 SL |
269 | let pattern = patcx.lower_pattern(pat); |
270 | let pattern_ty = pattern.ty; | |
9fa01778 | 271 | let pats: Matrix<'_, '_> = vec![smallvec![ |
32a655c1 | 272 | expand_pattern(cx, pattern) |
c30ab7b3 SL |
273 | ]].into_iter().collect(); |
274 | ||
32a655c1 SL |
275 | let wild_pattern = Pattern { |
276 | ty: pattern_ty, | |
277 | span: DUMMY_SP, | |
278 | kind: box PatternKind::Wild, | |
279 | }; | |
280 | let witness = match is_useful(cx, &pats, &[&wild_pattern], ConstructWitness) { | |
c30ab7b3 SL |
281 | UsefulWithWitness(witness) => witness, |
282 | NotUseful => return, | |
283 | Useful => bug!() | |
284 | }; | |
285 | ||
32a655c1 | 286 | let pattern_string = witness[0].single_pattern().to_string(); |
532ac7d7 | 287 | let mut err = struct_span_err!( |
c30ab7b3 SL |
288 | self.tcx.sess, pat.span, E0005, |
289 | "refutable pattern in {}: `{}` not covered", | |
290 | origin, pattern_string | |
291 | ); | |
abe05a73 XL |
292 | let label_msg = match pat.node { |
293 | PatKind::Path(hir::QPath::Resolved(None, ref path)) | |
8faf50e0 | 294 | if path.segments.len() == 1 && path.segments[0].args.is_none() => { |
13cf67c4 | 295 | format!("interpreted as {} {} pattern, not new variable", |
48663c56 | 296 | path.res.article(), path.res.descr()) |
abe05a73 XL |
297 | } |
298 | _ => format!("pattern `{}` not covered", pattern_string), | |
299 | }; | |
532ac7d7 XL |
300 | err.span_label(pat.span, label_msg); |
301 | if let ty::Adt(def, _) = pattern_ty.sty { | |
302 | if let Some(sp) = self.tcx.hir().span_if_local(def.did){ | |
303 | err.span_label(sp, format!("`{}` defined here", pattern_ty)); | |
304 | } | |
305 | } | |
306 | err.emit(); | |
c30ab7b3 | 307 | }); |
1a4d82fc JJ |
308 | } |
309 | } | |
310 | ||
9fa01778 | 311 | fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pat) { |
54a0048b | 312 | pat.walk(|p| { |
532ac7d7 | 313 | if let PatKind::Binding(_, _, ident, None) = p.node { |
8faf50e0 XL |
314 | if let Some(&bm) = cx.tables.pat_binding_modes().get(p.hir_id) { |
315 | if bm != ty::BindByValue(hir::MutImmutable) { | |
316 | // Nothing to check. | |
317 | return true; | |
318 | } | |
319 | let pat_ty = cx.tables.pat_ty(p); | |
b7449926 | 320 | if let ty::Adt(edef, _) = pat_ty.sty { |
8faf50e0 | 321 | if edef.is_enum() && edef.variants.iter().any(|variant| { |
0731742a | 322 | variant.ident == ident && variant.ctor_kind == CtorKind::Const |
8faf50e0 | 323 | }) { |
532ac7d7 | 324 | let ty_path = cx.tcx.def_path_str(edef.did); |
8faf50e0 XL |
325 | let mut err = struct_span_warn!(cx.tcx.sess, p.span, E0170, |
326 | "pattern binding `{}` is named the same as one \ | |
327 | of the variants of the type `{}`", | |
328 | ident, ty_path); | |
9fa01778 | 329 | err.span_suggestion( |
8faf50e0 XL |
330 | p.span, |
331 | "to match on the variant, qualify the path", | |
332 | format!("{}::{}", ty_path, ident), | |
333 | Applicability::MachineApplicable | |
334 | ); | |
335 | err.emit(); | |
336 | } | |
1a4d82fc | 337 | } |
8faf50e0 XL |
338 | } else { |
339 | cx.tcx.sess.delay_span_bug(p.span, "missing binding mode"); | |
1a4d82fc | 340 | } |
1a4d82fc JJ |
341 | } |
342 | true | |
343 | }); | |
344 | } | |
345 | ||
c30ab7b3 | 346 | /// Checks for common cases of "catchall" patterns that may not be intended as such. |
476ff2be | 347 | fn pat_is_catchall(pat: &Pat) -> bool { |
c30ab7b3 SL |
348 | match pat.node { |
349 | PatKind::Binding(.., None) => true, | |
476ff2be SL |
350 | PatKind::Binding(.., Some(ref s)) => pat_is_catchall(s), |
351 | PatKind::Ref(ref s, _) => pat_is_catchall(s), | |
c30ab7b3 | 352 | PatKind::Tuple(ref v, _) => v.iter().all(|p| { |
476ff2be | 353 | pat_is_catchall(&p) |
c30ab7b3 SL |
354 | }), |
355 | _ => false | |
356 | } | |
1a4d82fc JJ |
357 | } |
358 | ||
223e47cc | 359 | // Check for unreachable patterns |
532ac7d7 XL |
360 | fn check_arms<'a, 'tcx>( |
361 | cx: &mut MatchCheckCtxt<'a, 'tcx>, | |
362 | arms: &[(Vec<(&'a Pattern<'tcx>, &hir::Pat)>, Option<&hir::Expr>)], | |
363 | source: hir::MatchSource, | |
364 | ) { | |
c30ab7b3 SL |
365 | let mut seen = Matrix::empty(); |
366 | let mut catchall = None; | |
32a655c1 | 367 | for (arm_index, &(ref pats, guard)) in arms.iter().enumerate() { |
c30ab7b3 | 368 | for &(pat, hir_pat) in pats { |
0731742a | 369 | let v = smallvec![pat]; |
1a4d82fc | 370 | |
cc61c64b | 371 | match is_useful(cx, &seen, &v, LeaveOutWitness) { |
1a4d82fc JJ |
372 | NotUseful => { |
373 | match source { | |
48663c56 | 374 | hir::MatchSource::IfDesugar { .. } => bug!(), |
e9174d1e | 375 | hir::MatchSource::IfLetDesugar { .. } => { |
532ac7d7 | 376 | cx.tcx.lint_hir( |
0731742a | 377 | lint::builtin::IRREFUTABLE_LET_PATTERNS, |
532ac7d7 | 378 | hir_pat.hir_id, |
0731742a XL |
379 | pat.span, |
380 | "irrefutable if-let pattern", | |
381 | ); | |
b7449926 | 382 | } |
1a4d82fc | 383 | |
e9174d1e | 384 | hir::MatchSource::WhileLetDesugar => { |
32a655c1 SL |
385 | // check which arm we're on. |
386 | match arm_index { | |
387 | // The arm with the user-specified pattern. | |
388 | 0 => { | |
532ac7d7 | 389 | cx.tcx.lint_hir( |
8faf50e0 | 390 | lint::builtin::UNREACHABLE_PATTERNS, |
532ac7d7 | 391 | hir_pat.hir_id, pat.span, |
3b2f2976 | 392 | "unreachable pattern"); |
32a655c1 SL |
393 | }, |
394 | // The arm with the wildcard pattern. | |
395 | 1 => { | |
532ac7d7 | 396 | cx.tcx.lint_hir( |
0731742a | 397 | lint::builtin::IRREFUTABLE_LET_PATTERNS, |
532ac7d7 | 398 | hir_pat.hir_id, |
0731742a XL |
399 | pat.span, |
400 | "irrefutable while-let pattern", | |
401 | ); | |
32a655c1 SL |
402 | }, |
403 | _ => bug!(), | |
404 | } | |
9fa01778 | 405 | } |
85aaf69f | 406 | |
32a655c1 | 407 | hir::MatchSource::ForLoopDesugar | |
e9174d1e | 408 | hir::MatchSource::Normal => { |
532ac7d7 | 409 | let mut err = cx.tcx.struct_span_lint_hir( |
3b2f2976 | 410 | lint::builtin::UNREACHABLE_PATTERNS, |
532ac7d7 | 411 | hir_pat.hir_id, |
3b2f2976 XL |
412 | pat.span, |
413 | "unreachable pattern", | |
414 | ); | |
a7813a04 | 415 | // if we had a catchall pattern, hint at that |
c30ab7b3 | 416 | if let Some(catchall) = catchall { |
ff7c6d11 XL |
417 | err.span_label(pat.span, "unreachable pattern"); |
418 | err.span_label(catchall, "matches any value"); | |
a7813a04 | 419 | } |
3b2f2976 | 420 | err.emit(); |
9fa01778 | 421 | } |
54a0048b | 422 | |
48663c56 XL |
423 | // Unreachable patterns in try and await expressions occur when one of |
424 | // the arms are an uninhabited type. Which is OK. | |
425 | hir::MatchSource::AwaitDesugar | | |
32a655c1 | 426 | hir::MatchSource::TryDesugar => {} |
1a4d82fc JJ |
427 | } |
428 | } | |
429 | Useful => (), | |
54a0048b | 430 | UsefulWithWitness(_) => bug!() |
1a4d82fc JJ |
431 | } |
432 | if guard.is_none() { | |
c30ab7b3 | 433 | seen.push(v); |
476ff2be | 434 | if catchall.is_none() && pat_is_catchall(hir_pat) { |
c30ab7b3 SL |
435 | catchall = Some(pat.span); |
436 | } | |
223e47cc | 437 | } |
223e47cc LB |
438 | } |
439 | } | |
440 | } | |
441 | ||
dc9dc135 | 442 | fn check_exhaustive<'p, 'a, 'tcx>( |
532ac7d7 XL |
443 | cx: &mut MatchCheckCtxt<'a, 'tcx>, |
444 | scrut_ty: Ty<'tcx>, | |
445 | sp: Span, | |
446 | matrix: &Matrix<'p, 'tcx>, | |
447 | ) { | |
32a655c1 SL |
448 | let wild_pattern = Pattern { |
449 | ty: scrut_ty, | |
450 | span: DUMMY_SP, | |
451 | kind: box PatternKind::Wild, | |
452 | }; | |
453 | match is_useful(cx, matrix, &[&wild_pattern], ConstructWitness) { | |
1a4d82fc | 454 | UsefulWithWitness(pats) => { |
7453a54e | 455 | let witnesses = if pats.is_empty() { |
32a655c1 | 456 | vec![&wild_pattern] |
7453a54e | 457 | } else { |
c30ab7b3 | 458 | pats.iter().map(|w| w.single_pattern()).collect() |
1a4d82fc | 459 | }; |
7cac9316 XL |
460 | |
461 | const LIMIT: usize = 3; | |
462 | let joined_patterns = match witnesses.len() { | |
463 | 0 => bug!(), | |
464 | 1 => format!("`{}`", witnesses[0]), | |
8faf50e0 | 465 | 2..=LIMIT => { |
7cac9316 XL |
466 | let (tail, head) = witnesses.split_last().unwrap(); |
467 | let head: Vec<_> = head.iter().map(|w| w.to_string()).collect(); | |
468 | format!("`{}` and `{}`", head.join("`, `"), tail) | |
9fa01778 | 469 | } |
85aaf69f | 470 | _ => { |
7cac9316 XL |
471 | let (head, tail) = witnesses.split_at(LIMIT); |
472 | let head: Vec<_> = head.iter().map(|w| w.to_string()).collect(); | |
473 | format!("`{}` and {} more", head.join("`, `"), tail.len()) | |
474 | } | |
475 | }; | |
476 | ||
477 | let label_text = match witnesses.len() { | |
478 | 1 => format!("pattern {} not covered", joined_patterns), | |
9fa01778 | 479 | _ => format!("patterns {} not covered", joined_patterns), |
7cac9316 | 480 | }; |
532ac7d7 XL |
481 | let mut err = create_e0004(cx.tcx.sess, sp, format!( |
482 | "non-exhaustive patterns: {} not covered", | |
483 | joined_patterns, | |
484 | )); | |
485 | err.span_label(sp, label_text); | |
486 | // point at the definition of non-covered enum variants | |
487 | if let ty::Adt(def, _) = scrut_ty.sty { | |
488 | if let Some(sp) = cx.tcx.hir().span_if_local(def.did){ | |
489 | err.span_label(sp, format!("`{}` defined here", scrut_ty)); | |
490 | } | |
491 | } | |
492 | let patterns = witnesses.iter().map(|p| (**p).clone()).collect::<Vec<Pattern<'_>>>(); | |
493 | if patterns.len() < 4 { | |
494 | for sp in maybe_point_at_variant(cx, scrut_ty, patterns.as_slice()) { | |
495 | err.span_label(sp, "not covered"); | |
496 | } | |
497 | } | |
498 | err.help("ensure that all possible cases are being handled, \ | |
499 | possibly by adding wildcards or more match arms"); | |
500 | err.emit(); | |
1a4d82fc JJ |
501 | } |
502 | NotUseful => { | |
223e47cc | 503 | // This is good, wildcard pattern isn't reachable |
9fa01778 | 504 | } |
54a0048b | 505 | _ => bug!() |
1a4d82fc JJ |
506 | } |
507 | } | |
508 | ||
532ac7d7 XL |
509 | fn maybe_point_at_variant( |
510 | cx: &mut MatchCheckCtxt<'a, 'tcx>, | |
511 | ty: Ty<'tcx>, | |
512 | patterns: &[Pattern<'_>], | |
513 | ) -> Vec<Span> { | |
514 | let mut covered = vec![]; | |
515 | if let ty::Adt(def, _) = ty.sty { | |
516 | // Don't point at variants that have already been covered due to other patterns to avoid | |
517 | // visual clutter | |
518 | for pattern in patterns { | |
519 | let pk: &PatternKind<'_> = &pattern.kind; | |
520 | if let PatternKind::Variant { adt_def, variant_index, subpatterns, .. } = pk { | |
521 | if adt_def.did == def.did { | |
522 | let sp = def.variants[*variant_index].ident.span; | |
523 | if covered.contains(&sp) { | |
524 | continue; | |
525 | } | |
526 | covered.push(sp); | |
527 | let subpatterns = subpatterns.iter() | |
528 | .map(|field_pattern| field_pattern.pattern.clone()) | |
529 | .collect::<Vec<_>>(); | |
530 | covered.extend( | |
531 | maybe_point_at_variant(cx, ty, subpatterns.as_slice()), | |
532 | ); | |
533 | } | |
534 | } | |
535 | if let PatternKind::Leaf { subpatterns } = pk { | |
536 | let subpatterns = subpatterns.iter() | |
537 | .map(|field_pattern| field_pattern.pattern.clone()) | |
538 | .collect::<Vec<_>>(); | |
539 | covered.extend(maybe_point_at_variant(cx, ty, subpatterns.as_slice())); | |
540 | } | |
541 | } | |
542 | } | |
543 | covered | |
544 | } | |
545 | ||
1a4d82fc | 546 | // Legality of move bindings checking |
532ac7d7 XL |
547 | fn check_legality_of_move_bindings( |
548 | cx: &MatchVisitor<'_, '_>, | |
549 | has_guard: bool, | |
550 | pats: &[P<Pat>], | |
551 | ) { | |
223e47cc | 552 | let mut by_ref_span = None; |
85aaf69f | 553 | for pat in pats { |
94b46f34 | 554 | pat.each_binding(|_, hir_id, span, _path| { |
8faf50e0 XL |
555 | if let Some(&bm) = cx.tables.pat_binding_modes().get(hir_id) { |
556 | if let ty::BindByReference(..) = bm { | |
557 | by_ref_span = Some(span); | |
558 | } | |
559 | } else { | |
560 | cx.tcx.sess.delay_span_bug(pat.span, "missing binding mode"); | |
223e47cc | 561 | } |
1a4d82fc | 562 | }) |
223e47cc | 563 | } |
0bf4aa26 XL |
564 | let span_vec = &mut Vec::new(); |
565 | let check_move = |p: &Pat, sub: Option<&Pat>, span_vec: &mut Vec<Span>| { | |
223e47cc | 566 | // check legality of moving out of the enum |
1a4d82fc JJ |
567 | |
568 | // x @ Foo(..) is legal, but x @ Foo(y) isn't. | |
476ff2be | 569 | if sub.map_or(false, |p| p.contains_bindings()) { |
5bcae85e SL |
570 | struct_span_err!(cx.tcx.sess, p.span, E0007, |
571 | "cannot bind by-move with sub-bindings") | |
7cac9316 | 572 | .span_label(p.span, "binds an already bound by-move value by moving it") |
5bcae85e | 573 | .emit(); |
48663c56 | 574 | } else if has_guard && !cx.tcx.features().bind_by_move_pattern_guards { |
0bf4aa26 XL |
575 | let mut err = struct_span_err!(cx.tcx.sess, p.span, E0008, |
576 | "cannot bind by-move into a pattern guard"); | |
577 | err.span_label(p.span, "moves value into pattern guard"); | |
48663c56 | 578 | if cx.tcx.sess.opts.unstable_features.is_nightly_build() { |
0bf4aa26 XL |
579 | err.help("add #![feature(bind_by_move_pattern_guards)] to the \ |
580 | crate attributes to enable"); | |
581 | } | |
582 | err.emit(); | |
583 | } else if let Some(_by_ref_span) = by_ref_span { | |
584 | span_vec.push(p.span); | |
223e47cc LB |
585 | } |
586 | }; | |
587 | ||
85aaf69f | 588 | for pat in pats { |
54a0048b | 589 | pat.walk(|p| { |
532ac7d7 | 590 | if let PatKind::Binding(_, _, _, ref sub) = p.node { |
8faf50e0 XL |
591 | if let Some(&bm) = cx.tables.pat_binding_modes().get(p.hir_id) { |
592 | match bm { | |
593 | ty::BindByValue(..) => { | |
9fa01778 | 594 | let pat_ty = cx.tables.node_type(p.hir_id); |
0731742a | 595 | if !pat_ty.is_copy_modulo_regions(cx.tcx, cx.param_env, pat.span) { |
0bf4aa26 | 596 | check_move(p, sub.as_ref().map(|p| &**p), span_vec); |
8faf50e0 | 597 | } |
3b2f2976 | 598 | } |
8faf50e0 | 599 | _ => {} |
3b2f2976 | 600 | } |
8faf50e0 XL |
601 | } else { |
602 | cx.tcx.sess.delay_span_bug(pat.span, "missing binding mode"); | |
c30ab7b3 | 603 | } |
223e47cc | 604 | } |
1a4d82fc JJ |
605 | true |
606 | }); | |
607 | } | |
0bf4aa26 XL |
608 | if !span_vec.is_empty(){ |
609 | let span = MultiSpan::from_spans(span_vec.clone()); | |
610 | let mut err = struct_span_err!( | |
611 | cx.tcx.sess, | |
612 | span, | |
613 | E0009, | |
614 | "cannot bind by-move and by-ref in the same pattern", | |
615 | ); | |
48663c56 XL |
616 | if let Some(by_ref_span) = by_ref_span { |
617 | err.span_label(by_ref_span, "both by-ref and by-move used"); | |
618 | } | |
0bf4aa26 XL |
619 | for span in span_vec.iter(){ |
620 | err.span_label(*span, "by-move pattern here"); | |
621 | } | |
622 | err.emit(); | |
623 | } | |
1a4d82fc JJ |
624 | } |
625 | ||
9fa01778 XL |
626 | /// Ensures that a pattern guard doesn't borrow by mutable reference or assign. |
627 | // | |
628 | // FIXME: this should be done by borrowck. | |
629 | fn check_for_mutation_in_guard(cx: &MatchVisitor<'_, '_>, guard: &hir::Guard) { | |
041b39d2 | 630 | let mut checker = MutationChecker { |
3b2f2976 | 631 | cx, |
041b39d2 | 632 | }; |
b7449926 XL |
633 | match guard { |
634 | hir::Guard::If(expr) => | |
635 | ExprUseVisitor::new(&mut checker, | |
636 | cx.tcx, | |
dc9dc135 | 637 | cx.body_owner, |
b7449926 XL |
638 | cx.param_env, |
639 | cx.region_scope_tree, | |
640 | cx.tables, | |
641 | None).walk_expr(expr), | |
642 | }; | |
1a4d82fc JJ |
643 | } |
644 | ||
dc9dc135 | 645 | struct MutationChecker<'a, 'tcx> { |
041b39d2 | 646 | cx: &'a MatchVisitor<'a, 'tcx>, |
1a4d82fc JJ |
647 | } |
648 | ||
041b39d2 | 649 | impl<'a, 'tcx> Delegate<'tcx> for MutationChecker<'a, 'tcx> { |
9fa01778 | 650 | fn matched_pat(&mut self, _: &Pat, _: &cmt_<'_>, _: euv::MatchMode) {} |
532ac7d7 | 651 | fn consume(&mut self, _: hir::HirId, _: Span, _: &cmt_<'_>, _: ConsumeMode) {} |
9fa01778 | 652 | fn consume_pat(&mut self, _: &Pat, _: &cmt_<'_>, _: ConsumeMode) {} |
1a4d82fc | 653 | fn borrow(&mut self, |
532ac7d7 | 654 | _: hir::HirId, |
1a4d82fc | 655 | span: Span, |
9fa01778 | 656 | _: &cmt_<'_>, |
7cac9316 | 657 | _: ty::Region<'tcx>, |
9e0c209e | 658 | kind:ty:: BorrowKind, |
1a4d82fc JJ |
659 | _: LoanCause) { |
660 | match kind { | |
9e0c209e | 661 | ty::MutBorrow => { |
0bf4aa26 XL |
662 | let mut err = struct_span_err!(self.cx.tcx.sess, span, E0301, |
663 | "cannot mutably borrow in a pattern guard"); | |
664 | err.span_label(span, "borrowed mutably in pattern guard"); | |
48663c56 | 665 | if self.cx.tcx.sess.opts.unstable_features.is_nightly_build() { |
0bf4aa26 XL |
666 | err.help("add #![feature(bind_by_move_pattern_guards)] to the \ |
667 | crate attributes to enable"); | |
668 | } | |
669 | err.emit(); | |
1a4d82fc | 670 | } |
9e0c209e | 671 | ty::ImmBorrow | ty::UniqueImmBorrow => {} |
1a4d82fc JJ |
672 | } |
673 | } | |
532ac7d7 XL |
674 | fn decl_without_init(&mut self, _: hir::HirId, _: Span) {} |
675 | fn mutate(&mut self, _: hir::HirId, span: Span, _: &cmt_<'_>, mode: MutateMode) { | |
1a4d82fc | 676 | match mode { |
9cc50fc6 | 677 | MutateMode::JustWrite | MutateMode::WriteAndRead => { |
5bcae85e | 678 | struct_span_err!(self.cx.tcx.sess, span, E0302, "cannot assign in a pattern guard") |
7cac9316 | 679 | .span_label(span, "assignment in pattern guard") |
5bcae85e | 680 | .emit(); |
1a4d82fc | 681 | } |
9cc50fc6 | 682 | MutateMode::Init => {} |
1a4d82fc JJ |
683 | } |
684 | } | |
685 | } | |
686 | ||
687 | /// Forbids bindings in `@` patterns. This is necessary for memory safety, | |
688 | /// because of the way rvalues are handled in the borrow check. (See issue | |
689 | /// #14587.) | |
9fa01778 | 690 | fn check_legality_of_bindings_in_at_patterns(cx: &MatchVisitor<'_, '_>, pat: &Pat) { |
1a4d82fc JJ |
691 | AtBindingPatternVisitor { cx: cx, bindings_allowed: true }.visit_pat(pat); |
692 | } | |
693 | ||
dc9dc135 | 694 | struct AtBindingPatternVisitor<'a, 'b, 'tcx> { |
c30ab7b3 | 695 | cx: &'a MatchVisitor<'b, 'tcx>, |
1a4d82fc JJ |
696 | bindings_allowed: bool |
697 | } | |
698 | ||
699 | impl<'a, 'b, 'tcx, 'v> Visitor<'v> for AtBindingPatternVisitor<'a, 'b, 'tcx> { | |
476ff2be SL |
700 | fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> { |
701 | NestedVisitorMap::None | |
702 | } | |
703 | ||
1a4d82fc | 704 | fn visit_pat(&mut self, pat: &Pat) { |
1a4d82fc | 705 | match pat.node { |
9e0c209e | 706 | PatKind::Binding(.., ref subpat) => { |
3157f602 | 707 | if !self.bindings_allowed { |
c30ab7b3 SL |
708 | struct_span_err!(self.cx.tcx.sess, pat.span, E0303, |
709 | "pattern bindings are not allowed after an `@`") | |
7cac9316 | 710 | .span_label(pat.span, "not allowed after `@`") |
c30ab7b3 | 711 | .emit(); |
3157f602 XL |
712 | } |
713 | ||
714 | if subpat.is_some() { | |
715 | let bindings_were_allowed = self.bindings_allowed; | |
716 | self.bindings_allowed = false; | |
717 | intravisit::walk_pat(self, pat); | |
718 | self.bindings_allowed = bindings_were_allowed; | |
719 | } | |
1a4d82fc | 720 | } |
92a42be0 | 721 | _ => intravisit::walk_pat(self, pat), |
223e47cc | 722 | } |
223e47cc LB |
723 | } |
724 | } |