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