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