diff options
author | Dawer <[email protected]> | 2021-05-10 09:22:13 +0100 |
---|---|---|
committer | Dawer <[email protected]> | 2021-05-31 20:03:47 +0100 |
commit | 49e016169fc8413e2734a655cbd55ebba2907b76 (patch) | |
tree | 7688ba28eea44454e4d541890e1d311a3a87fffb /crates/hir_ty/src/diagnostics | |
parent | 9b841a9a044d9d71cece62a3e44880325bc15f78 (diff) |
Check pattern types.
Diffstat (limited to 'crates/hir_ty/src/diagnostics')
-rw-r--r-- | crates/hir_ty/src/diagnostics/expr.rs | 77 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/pattern.rs | 36 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/pattern/usefulness.rs | 2 |
3 files changed, 85 insertions, 30 deletions
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs index 929c4a9cc..e4e9ab5c0 100644 --- a/crates/hir_ty/src/diagnostics/expr.rs +++ b/crates/hir_ty/src/diagnostics/expr.rs | |||
@@ -379,32 +379,58 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
379 | let (body, source_map): (Arc<Body>, Arc<BodySourceMap>) = | 379 | let (body, source_map): (Arc<Body>, Arc<BodySourceMap>) = |
380 | db.body_with_source_map(self.owner); | 380 | db.body_with_source_map(self.owner); |
381 | 381 | ||
382 | let _match_expr_ty = if infer.type_of_expr[match_expr].is_unknown() { | 382 | let match_expr_ty = if infer.type_of_expr[match_expr].is_unknown() { |
383 | return; | 383 | return; |
384 | } else { | 384 | } else { |
385 | &infer.type_of_expr[match_expr] | 385 | &infer.type_of_expr[match_expr] |
386 | }; | 386 | }; |
387 | // eprintln!("ExprValidator::validate_match2({:?})", _match_expr_ty.kind(&Interner)); | ||
388 | 387 | ||
389 | let pattern_arena = RefCell::new(PatternArena::new()); | 388 | let pattern_arena = RefCell::new(PatternArena::new()); |
390 | 389 | ||
391 | let mut have_errors = false; | 390 | let mut m_arms = Vec::new(); |
392 | let m_arms: Vec<_> = arms | 391 | let mut has_lowering_errors = false; |
393 | .iter() | 392 | for arm in arms { |
394 | .map(|arm| usefulness::MatchArm { | 393 | if let Some(pat_ty) = infer.type_of_pat.get(arm.pat) { |
395 | pat: self.lower_pattern( | 394 | // We only include patterns whose type matches the type |
396 | arm.pat, | 395 | // of the match expression. If we had a InvalidMatchArmPattern |
397 | &mut pattern_arena.borrow_mut(), | 396 | // diagnostic or similar we could raise that in an else |
398 | db, | 397 | // block here. |
399 | &body, | 398 | // |
400 | &mut have_errors, | 399 | // When comparing the types, we also have to consider that rustc |
401 | ), | 400 | // will automatically de-reference the match expression type if |
402 | has_guard: arm.guard.is_some(), | 401 | // necessary. |
403 | }) | 402 | // |
404 | .collect(); | 403 | // FIXME we should use the type checker for this. |
405 | 404 | if pat_ty == match_expr_ty | |
406 | // Bail out early if lowering failed. | 405 | || match_expr_ty |
407 | if have_errors { | 406 | .as_reference() |
407 | .map(|(match_expr_ty, ..)| match_expr_ty == pat_ty) | ||
408 | .unwrap_or(false) | ||
409 | { | ||
410 | // If we had a NotUsefulMatchArm diagnostic, we could | ||
411 | // check the usefulness of each pattern as we added it | ||
412 | // to the matrix here. | ||
413 | let m_arm = usefulness::MatchArm { | ||
414 | pat: self.lower_pattern( | ||
415 | arm.pat, | ||
416 | &mut pattern_arena.borrow_mut(), | ||
417 | db, | ||
418 | &body, | ||
419 | &mut has_lowering_errors, | ||
420 | ), | ||
421 | has_guard: arm.guard.is_some(), | ||
422 | }; | ||
423 | m_arms.push(m_arm); | ||
424 | if !has_lowering_errors { | ||
425 | continue; | ||
426 | } | ||
427 | } | ||
428 | } | ||
429 | |||
430 | // If we can't resolve the type of a pattern, or the pattern type doesn't | ||
431 | // fit the match expression, we skip this diagnostic. Skipping the entire | ||
432 | // diagnostic rather than just not including this match arm is preferred | ||
433 | // to avoid the chance of false positives. | ||
408 | return; | 434 | return; |
409 | } | 435 | } |
410 | 436 | ||
@@ -418,18 +444,11 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
418 | }; | 444 | }; |
419 | let report = usefulness::compute_match_usefulness(&cx, &m_arms); | 445 | let report = usefulness::compute_match_usefulness(&cx, &m_arms); |
420 | 446 | ||
421 | // TODO Report unreacheble arms | 447 | // FIXME Report unreacheble arms |
422 | // let mut catchall = None; | 448 | // https://github.com/rust-lang/rust/blob/25c15cdbe/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L200-L201 |
423 | // for (arm_index, (arm, is_useful)) in report.arm_usefulness.iter().enumerate() { | ||
424 | // match is_useful{ | ||
425 | // Unreachable => { | ||
426 | // } | ||
427 | // Reachable(_) => {} | ||
428 | // } | ||
429 | // } | ||
430 | 449 | ||
431 | let witnesses = report.non_exhaustiveness_witnesses; | 450 | let witnesses = report.non_exhaustiveness_witnesses; |
432 | eprintln!("compute_match_usefulness(..) -> {:?}", &witnesses); | 451 | // eprintln!("compute_match_usefulness(..) -> {:?}", &witnesses); |
433 | if !witnesses.is_empty() { | 452 | if !witnesses.is_empty() { |
434 | if let Ok(source_ptr) = source_map.expr_syntax(id) { | 453 | if let Ok(source_ptr) = source_map.expr_syntax(id) { |
435 | let root = source_ptr.file_syntax(db.upcast()); | 454 | let root = source_ptr.file_syntax(db.upcast()); |
diff --git a/crates/hir_ty/src/diagnostics/pattern.rs b/crates/hir_ty/src/diagnostics/pattern.rs index d98fb0867..38e4b53b7 100644 --- a/crates/hir_ty/src/diagnostics/pattern.rs +++ b/crates/hir_ty/src/diagnostics/pattern.rs | |||
@@ -511,4 +511,40 @@ fn main() { | |||
511 | "#, | 511 | "#, |
512 | ); | 512 | ); |
513 | } | 513 | } |
514 | |||
515 | /// These failing tests are narrowed down from "hir_ty::diagnostics::match_check::tests" | ||
516 | // TODO fix | ||
517 | mod failing { | ||
518 | use super::*; | ||
519 | |||
520 | #[test] | ||
521 | fn never() { | ||
522 | check_diagnostics( | ||
523 | r#" | ||
524 | enum Never {} | ||
525 | |||
526 | fn enum_ref(never: &Never) { | ||
527 | match never {} | ||
528 | } | ||
529 | "#, | ||
530 | ); | ||
531 | } | ||
532 | |||
533 | #[test] | ||
534 | fn unknown_type() { | ||
535 | check_diagnostics( | ||
536 | r#" | ||
537 | enum Option<T> { Some(T), None } | ||
538 | |||
539 | fn main() { | ||
540 | // `Never` is deliberately not defined so that it's an uninferred type. | ||
541 | match Option::<Never>::None { | ||
542 | None => {} | ||
543 | Some(never) => {} | ||
544 | } | ||
545 | } | ||
546 | "#, | ||
547 | ); | ||
548 | } | ||
549 | } | ||
514 | } | 550 | } |
diff --git a/crates/hir_ty/src/diagnostics/pattern/usefulness.rs b/crates/hir_ty/src/diagnostics/pattern/usefulness.rs index ef2be7530..01a7fb0d9 100644 --- a/crates/hir_ty/src/diagnostics/pattern/usefulness.rs +++ b/crates/hir_ty/src/diagnostics/pattern/usefulness.rs | |||
@@ -28,7 +28,7 @@ pub(crate) struct MatchCheckCtx<'a> { | |||
28 | pub(crate) body: Arc<Body>, | 28 | pub(crate) body: Arc<Body>, |
29 | pub(crate) infer: &'a InferenceResult, | 29 | pub(crate) infer: &'a InferenceResult, |
30 | pub(crate) db: &'a dyn HirDatabase, | 30 | pub(crate) db: &'a dyn HirDatabase, |
31 | /// Patterns from self.body.pats plus generated by the check. | 31 | /// Lowered patterns from self.body.pats plus generated by the check. |
32 | pub(crate) pattern_arena: &'a RefCell<PatternArena>, | 32 | pub(crate) pattern_arena: &'a RefCell<PatternArena>, |
33 | } | 33 | } |
34 | 34 | ||