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