diff options
Diffstat (limited to 'crates/hir_ty/src/diagnostics')
-rw-r--r-- | crates/hir_ty/src/diagnostics/expr.rs | 2 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/match_check.rs | 68 |
2 files changed, 69 insertions, 1 deletions
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs index c6015d236..0a7e6ee52 100644 --- a/crates/hir_ty/src/diagnostics/expr.rs +++ b/crates/hir_ty/src/diagnostics/expr.rs | |||
@@ -346,6 +346,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
346 | // fit the match expression, we skip this diagnostic. Skipping the entire | 346 | // fit the match expression, we skip this diagnostic. Skipping the entire |
347 | // diagnostic rather than just not including this match arm is preferred | 347 | // diagnostic rather than just not including this match arm is preferred |
348 | // to avoid the chance of false positives. | 348 | // to avoid the chance of false positives. |
349 | #[cfg(test)] | ||
350 | match_check::tests::report_bail_out(db, self.owner, arm.pat, self.sink); | ||
349 | return; | 351 | return; |
350 | } | 352 | } |
351 | 353 | ||
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs index aebadd391..5f0cc4145 100644 --- a/crates/hir_ty/src/diagnostics/match_check.rs +++ b/crates/hir_ty/src/diagnostics/match_check.rs | |||
@@ -339,9 +339,60 @@ impl PatternFoldable for PatKind { | |||
339 | } | 339 | } |
340 | 340 | ||
341 | #[cfg(test)] | 341 | #[cfg(test)] |
342 | mod tests { | 342 | pub(super) mod tests { |
343 | mod report { | ||
344 | use std::any::Any; | ||
345 | |||
346 | use hir_def::{expr::PatId, DefWithBodyId}; | ||
347 | use hir_expand::{HirFileId, InFile}; | ||
348 | use syntax::SyntaxNodePtr; | ||
349 | |||
350 | use crate::{ | ||
351 | db::HirDatabase, | ||
352 | diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink}, | ||
353 | }; | ||
354 | |||
355 | /// In tests, match check bails out loudly. | ||
356 | /// This helps to catch incorrect tests that pass due to false negatives. | ||
357 | pub(crate) fn report_bail_out( | ||
358 | db: &dyn HirDatabase, | ||
359 | def: DefWithBodyId, | ||
360 | pat: PatId, | ||
361 | sink: &mut DiagnosticSink, | ||
362 | ) { | ||
363 | let (_, source_map) = db.body_with_source_map(def); | ||
364 | if let Ok(source_ptr) = source_map.pat_syntax(pat) { | ||
365 | let pat_syntax_ptr = source_ptr.value.either(Into::into, Into::into); | ||
366 | sink.push(BailedOut { file: source_ptr.file_id, pat_syntax_ptr }); | ||
367 | } | ||
368 | } | ||
369 | |||
370 | #[derive(Debug)] | ||
371 | struct BailedOut { | ||
372 | file: HirFileId, | ||
373 | pat_syntax_ptr: SyntaxNodePtr, | ||
374 | } | ||
375 | |||
376 | impl Diagnostic for BailedOut { | ||
377 | fn code(&self) -> DiagnosticCode { | ||
378 | DiagnosticCode("internal:match-check-bailed-out") | ||
379 | } | ||
380 | fn message(&self) -> String { | ||
381 | format!("Internal: match check bailed out") | ||
382 | } | ||
383 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
384 | InFile { file_id: self.file, value: self.pat_syntax_ptr.clone() } | ||
385 | } | ||
386 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
387 | self | ||
388 | } | ||
389 | } | ||
390 | } | ||
391 | |||
343 | use crate::diagnostics::tests::check_diagnostics; | 392 | use crate::diagnostics::tests::check_diagnostics; |
344 | 393 | ||
394 | pub(crate) use self::report::report_bail_out; | ||
395 | |||
345 | #[test] | 396 | #[test] |
346 | fn empty_tuple() { | 397 | fn empty_tuple() { |
347 | check_diagnostics( | 398 | check_diagnostics( |
@@ -589,14 +640,18 @@ enum Either2 { C, D } | |||
589 | fn main() { | 640 | fn main() { |
590 | match Either::A { | 641 | match Either::A { |
591 | Either2::C => (), | 642 | Either2::C => (), |
643 | // ^^^^^^^^^^ Internal: match check bailed out | ||
592 | Either2::D => (), | 644 | Either2::D => (), |
593 | } | 645 | } |
594 | match (true, false) { | 646 | match (true, false) { |
595 | (true, false, true) => (), | 647 | (true, false, true) => (), |
648 | // ^^^^^^^^^^^^^^^^^^^ Internal: match check bailed out | ||
596 | (true) => (), | 649 | (true) => (), |
597 | } | 650 | } |
598 | match (true, false) { (true,) => {} } | 651 | match (true, false) { (true,) => {} } |
652 | // ^^^^^^^ Internal: match check bailed out | ||
599 | match (0) { () => () } | 653 | match (0) { () => () } |
654 | // ^^ Internal: match check bailed out | ||
600 | match Unresolved::Bar { Unresolved::Baz => () } | 655 | match Unresolved::Bar { Unresolved::Baz => () } |
601 | } | 656 | } |
602 | "#, | 657 | "#, |
@@ -609,7 +664,9 @@ fn main() { | |||
609 | r#" | 664 | r#" |
610 | fn main() { | 665 | fn main() { |
611 | match false { true | () => {} } | 666 | match false { true | () => {} } |
667 | // ^^^^^^^^^ Internal: match check bailed out | ||
612 | match (false,) { (true | (),) => {} } | 668 | match (false,) { (true | (),) => {} } |
669 | // ^^^^^^^^^^^^ Internal: match check bailed out | ||
613 | } | 670 | } |
614 | "#, | 671 | "#, |
615 | ); | 672 | ); |
@@ -642,10 +699,12 @@ enum Either { A, B } | |||
642 | fn main() { | 699 | fn main() { |
643 | match loop {} { | 700 | match loop {} { |
644 | Either::A => (), | 701 | Either::A => (), |
702 | // ^^^^^^^^^ Internal: match check bailed out | ||
645 | Either::B => (), | 703 | Either::B => (), |
646 | } | 704 | } |
647 | match loop {} { | 705 | match loop {} { |
648 | Either::A => (), | 706 | Either::A => (), |
707 | // ^^^^^^^^^ Internal: match check bailed out | ||
649 | } | 708 | } |
650 | match loop { break Foo::A } { | 709 | match loop { break Foo::A } { |
651 | //^^^^^^^^^^^^^^^^^^^^^ Missing match arm | 710 | //^^^^^^^^^^^^^^^^^^^^^ Missing match arm |
@@ -853,6 +912,11 @@ fn main() { | |||
853 | match Option::<Never>::None { | 912 | match Option::<Never>::None { |
854 | None => (), | 913 | None => (), |
855 | Some(never) => match never {}, | 914 | Some(never) => match never {}, |
915 | // ^^^^^^^^^^^ Internal: match check bailed out | ||
916 | } | ||
917 | match Option::<Never>::None { | ||
918 | //^^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
919 | Option::Some(_never) => {}, | ||
856 | } | 920 | } |
857 | } | 921 | } |
858 | "#, | 922 | "#, |
@@ -1000,6 +1064,7 @@ fn main(v: S) { | |||
1000 | match v { S{ a } => {} } | 1064 | match v { S{ a } => {} } |
1001 | match v { S{ a: _x } => {} } | 1065 | match v { S{ a: _x } => {} } |
1002 | match v { S{ a: 'a' } => {} } | 1066 | match v { S{ a: 'a' } => {} } |
1067 | //^^^^^^^^^^^ Internal: match check bailed out | ||
1003 | match v { S{..} => {} } | 1068 | match v { S{..} => {} } |
1004 | match v { _ => {} } | 1069 | match v { _ => {} } |
1005 | match v { } | 1070 | match v { } |
@@ -1045,6 +1110,7 @@ fn main() { | |||
1045 | fn main() { | 1110 | fn main() { |
1046 | match 5 { | 1111 | match 5 { |
1047 | 10 => (), | 1112 | 10 => (), |
1113 | // ^^ Internal: match check bailed out | ||
1048 | 11..20 => (), | 1114 | 11..20 => (), |
1049 | } | 1115 | } |
1050 | } | 1116 | } |