diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir_ty/src/diagnostics/match_check.rs | 168 |
1 files changed, 83 insertions, 85 deletions
diff --git a/crates/ra_hir_ty/src/diagnostics/match_check.rs b/crates/ra_hir_ty/src/diagnostics/match_check.rs index 722c0e9ee..899025a87 100644 --- a/crates/ra_hir_ty/src/diagnostics/match_check.rs +++ b/crates/ra_hir_ty/src/diagnostics/match_check.rs | |||
@@ -272,7 +272,7 @@ impl From<&PatId> for PatIdOrWild { | |||
272 | } | 272 | } |
273 | 273 | ||
274 | #[derive(Debug, Clone, Copy, PartialEq)] | 274 | #[derive(Debug, Clone, Copy, PartialEq)] |
275 | pub enum MatchCheckErr { | 275 | pub(super) enum MatchCheckErr { |
276 | NotImplemented, | 276 | NotImplemented, |
277 | MalformedMatchArm, | 277 | MalformedMatchArm, |
278 | /// Used when type inference cannot resolve the type of | 278 | /// Used when type inference cannot resolve the type of |
@@ -287,21 +287,21 @@ pub enum MatchCheckErr { | |||
287 | /// | 287 | /// |
288 | /// The `std::result::Result` type is used here rather than a custom enum | 288 | /// The `std::result::Result` type is used here rather than a custom enum |
289 | /// to allow the use of `?`. | 289 | /// to allow the use of `?`. |
290 | pub type MatchCheckResult<T> = Result<T, MatchCheckErr>; | 290 | pub(super) type MatchCheckResult<T> = Result<T, MatchCheckErr>; |
291 | 291 | ||
292 | #[derive(Debug)] | 292 | #[derive(Debug)] |
293 | /// A row in a Matrix. | 293 | /// A row in a Matrix. |
294 | /// | 294 | /// |
295 | /// This type is modeled from the struct of the same name in `rustc`. | 295 | /// This type is modeled from the struct of the same name in `rustc`. |
296 | pub(crate) struct PatStack(PatStackInner); | 296 | pub(super) struct PatStack(PatStackInner); |
297 | type PatStackInner = SmallVec<[PatIdOrWild; 2]>; | 297 | type PatStackInner = SmallVec<[PatIdOrWild; 2]>; |
298 | 298 | ||
299 | impl PatStack { | 299 | impl PatStack { |
300 | pub(crate) fn from_pattern(pat_id: PatId) -> PatStack { | 300 | pub(super) fn from_pattern(pat_id: PatId) -> PatStack { |
301 | Self(smallvec!(pat_id.into())) | 301 | Self(smallvec!(pat_id.into())) |
302 | } | 302 | } |
303 | 303 | ||
304 | pub(crate) fn from_wild() -> PatStack { | 304 | pub(super) fn from_wild() -> PatStack { |
305 | Self(smallvec!(PatIdOrWild::Wild)) | 305 | Self(smallvec!(PatIdOrWild::Wild)) |
306 | } | 306 | } |
307 | 307 | ||
@@ -510,14 +510,14 @@ impl PatStack { | |||
510 | /// A collection of PatStack. | 510 | /// A collection of PatStack. |
511 | /// | 511 | /// |
512 | /// This type is modeled from the struct of the same name in `rustc`. | 512 | /// This type is modeled from the struct of the same name in `rustc`. |
513 | pub(crate) struct Matrix(Vec<PatStack>); | 513 | pub(super) struct Matrix(Vec<PatStack>); |
514 | 514 | ||
515 | impl Matrix { | 515 | impl Matrix { |
516 | pub(crate) fn empty() -> Self { | 516 | pub(super) fn empty() -> Self { |
517 | Self(vec![]) | 517 | Self(vec![]) |
518 | } | 518 | } |
519 | 519 | ||
520 | pub(crate) fn push(&mut self, cx: &MatchCheckCtx, row: PatStack) { | 520 | pub(super) fn push(&mut self, cx: &MatchCheckCtx, row: PatStack) { |
521 | if let Some(Pat::Or(pat_ids)) = row.get_head().map(|pat_id| pat_id.as_pat(cx)) { | 521 | if let Some(Pat::Or(pat_ids)) = row.get_head().map(|pat_id| pat_id.as_pat(cx)) { |
522 | // Or patterns are expanded here | 522 | // Or patterns are expanded here |
523 | for pat_id in pat_ids { | 523 | for pat_id in pat_ids { |
@@ -579,16 +579,16 @@ impl Matrix { | |||
579 | /// not matched by an prior match arms. | 579 | /// not matched by an prior match arms. |
580 | /// | 580 | /// |
581 | /// We may eventually need an `Unknown` variant here. | 581 | /// We may eventually need an `Unknown` variant here. |
582 | pub enum Usefulness { | 582 | pub(super) enum Usefulness { |
583 | Useful, | 583 | Useful, |
584 | NotUseful, | 584 | NotUseful, |
585 | } | 585 | } |
586 | 586 | ||
587 | pub struct MatchCheckCtx<'a> { | 587 | pub(super) struct MatchCheckCtx<'a> { |
588 | pub match_expr: Idx<Expr>, | 588 | pub(super) match_expr: Idx<Expr>, |
589 | pub body: Arc<Body>, | 589 | pub(super) body: Arc<Body>, |
590 | pub infer: Arc<InferenceResult>, | 590 | pub(super) infer: Arc<InferenceResult>, |
591 | pub db: &'a dyn HirDatabase, | 591 | pub(super) db: &'a dyn HirDatabase, |
592 | } | 592 | } |
593 | 593 | ||
594 | /// Given a set of patterns `matrix`, and pattern to consider `v`, determines | 594 | /// Given a set of patterns `matrix`, and pattern to consider `v`, determines |
@@ -599,7 +599,7 @@ pub struct MatchCheckCtx<'a> { | |||
599 | /// expected that you have already type checked the match arms. All patterns in | 599 | /// expected that you have already type checked the match arms. All patterns in |
600 | /// matrix should be the same type as v, as well as they should all be the same | 600 | /// matrix should be the same type as v, as well as they should all be the same |
601 | /// type as the match expression. | 601 | /// type as the match expression. |
602 | pub(crate) fn is_useful( | 602 | pub(super) fn is_useful( |
603 | cx: &MatchCheckCtx, | 603 | cx: &MatchCheckCtx, |
604 | matrix: &Matrix, | 604 | matrix: &Matrix, |
605 | v: &PatStack, | 605 | v: &PatStack, |
@@ -837,23 +837,23 @@ fn enum_variant_matches(cx: &MatchCheckCtx, pat_id: PatId, enum_variant_id: Enum | |||
837 | 837 | ||
838 | #[cfg(test)] | 838 | #[cfg(test)] |
839 | mod tests { | 839 | mod tests { |
840 | pub(super) use insta::assert_snapshot; | 840 | use insta::assert_snapshot; |
841 | pub(super) use ra_db::fixture::WithFixture; | 841 | use ra_db::fixture::WithFixture; |
842 | 842 | ||
843 | pub(super) use crate::{diagnostics::MissingMatchArms, test_db::TestDB}; | 843 | use crate::{diagnostics::MissingMatchArms, test_db::TestDB}; |
844 | 844 | ||
845 | pub(super) fn check_diagnostic_message(ra_fixture: &str) -> String { | 845 | fn check_diagnostic_message(ra_fixture: &str) -> String { |
846 | TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>().0 | 846 | TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>().0 |
847 | } | 847 | } |
848 | 848 | ||
849 | pub(super) fn check_diagnostic(ra_fixture: &str) { | 849 | fn check_diagnostic(ra_fixture: &str) { |
850 | let diagnostic_count = | 850 | let diagnostic_count = |
851 | TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>().1; | 851 | TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>().1; |
852 | 852 | ||
853 | assert_eq!(1, diagnostic_count, "no diagnostic reported"); | 853 | assert_eq!(1, diagnostic_count, "no diagnostic reported"); |
854 | } | 854 | } |
855 | 855 | ||
856 | pub(super) fn check_no_diagnostic(ra_fixture: &str) { | 856 | fn check_no_diagnostic(ra_fixture: &str) { |
857 | let (s, diagnostic_count) = | 857 | let (s, diagnostic_count) = |
858 | TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>(); | 858 | TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>(); |
859 | 859 | ||
@@ -2036,28 +2036,25 @@ mod tests { | |||
2036 | ", | 2036 | ", |
2037 | ); | 2037 | ); |
2038 | } | 2038 | } |
2039 | } | ||
2040 | 2039 | ||
2041 | #[cfg(test)] | 2040 | mod false_negatives { |
2042 | mod false_negatives { | 2041 | //! The implementation of match checking here is a work in progress. As we roll this out, we |
2043 | //! The implementation of match checking here is a work in progress. As we roll this out, we | 2042 | //! prefer false negatives to false positives (ideally there would be no false positives). This |
2044 | //! prefer false negatives to false positives (ideally there would be no false positives). This | 2043 | //! test module should document known false negatives. Eventually we will have a complete |
2045 | //! test module should document known false negatives. Eventually we will have a complete | 2044 | //! implementation of match checking and this module will be empty. |
2046 | //! implementation of match checking and this module will be empty. | 2045 | //! |
2047 | //! | 2046 | //! The reasons for documenting known false negatives: |
2048 | //! The reasons for documenting known false negatives: | 2047 | //! |
2049 | //! | 2048 | //! 1. It acts as a backlog of work that can be done to improve the behavior of the system. |
2050 | //! 1. It acts as a backlog of work that can be done to improve the behavior of the system. | 2049 | //! 2. It ensures the code doesn't panic when handling these cases. |
2051 | //! 2. It ensures the code doesn't panic when handling these cases. | 2050 | use super::*; |
2052 | 2051 | ||
2053 | use super::tests::*; | 2052 | #[test] |
2054 | 2053 | fn integers() { | |
2055 | #[test] | 2054 | // This is a false negative. |
2056 | fn integers() { | 2055 | // We don't currently check integer exhaustiveness. |
2057 | // This is a false negative. | 2056 | check_no_diagnostic( |
2058 | // We don't currently check integer exhaustiveness. | 2057 | r" |
2059 | check_no_diagnostic( | ||
2060 | r" | ||
2061 | fn test_fn() { | 2058 | fn test_fn() { |
2062 | match 5 { | 2059 | match 5 { |
2063 | 10 => (), | 2060 | 10 => (), |
@@ -2065,15 +2062,15 @@ mod false_negatives { | |||
2065 | } | 2062 | } |
2066 | } | 2063 | } |
2067 | ", | 2064 | ", |
2068 | ); | 2065 | ); |
2069 | } | 2066 | } |
2070 | 2067 | ||
2071 | #[test] | 2068 | #[test] |
2072 | fn internal_or() { | 2069 | fn internal_or() { |
2073 | // This is a false negative. | 2070 | // This is a false negative. |
2074 | // We do not currently handle patterns with internal `or`s. | 2071 | // We do not currently handle patterns with internal `or`s. |
2075 | check_no_diagnostic( | 2072 | check_no_diagnostic( |
2076 | r" | 2073 | r" |
2077 | fn test_fn() { | 2074 | fn test_fn() { |
2078 | enum Either { | 2075 | enum Either { |
2079 | A(bool), | 2076 | A(bool), |
@@ -2084,17 +2081,17 @@ mod false_negatives { | |||
2084 | } | 2081 | } |
2085 | } | 2082 | } |
2086 | ", | 2083 | ", |
2087 | ); | 2084 | ); |
2088 | } | 2085 | } |
2089 | 2086 | ||
2090 | #[test] | 2087 | #[test] |
2091 | fn expr_loop_missing_arm() { | 2088 | fn expr_loop_missing_arm() { |
2092 | // This is a false negative. | 2089 | // This is a false negative. |
2093 | // We currently infer the type of `loop { break Foo::A }` to `!`, which | 2090 | // We currently infer the type of `loop { break Foo::A }` to `!`, which |
2094 | // causes us to skip the diagnostic since `Either::A` doesn't type check | 2091 | // causes us to skip the diagnostic since `Either::A` doesn't type check |
2095 | // with `!`. | 2092 | // with `!`. |
2096 | check_diagnostic( | 2093 | check_diagnostic( |
2097 | r" | 2094 | r" |
2098 | enum Either { | 2095 | enum Either { |
2099 | A, | 2096 | A, |
2100 | B, | 2097 | B, |
@@ -2105,45 +2102,45 @@ mod false_negatives { | |||
2105 | } | 2102 | } |
2106 | } | 2103 | } |
2107 | ", | 2104 | ", |
2108 | ); | 2105 | ); |
2109 | } | 2106 | } |
2110 | 2107 | ||
2111 | #[test] | 2108 | #[test] |
2112 | fn tuple_of_bools_with_ellipsis_at_end_missing_arm() { | 2109 | fn tuple_of_bools_with_ellipsis_at_end_missing_arm() { |
2113 | // This is a false negative. | 2110 | // This is a false negative. |
2114 | // We don't currently handle tuple patterns with ellipsis. | 2111 | // We don't currently handle tuple patterns with ellipsis. |
2115 | check_no_diagnostic( | 2112 | check_no_diagnostic( |
2116 | r" | 2113 | r" |
2117 | fn test_fn() { | 2114 | fn test_fn() { |
2118 | match (false, true, false) { | 2115 | match (false, true, false) { |
2119 | (false, ..) => {}, | 2116 | (false, ..) => {}, |
2120 | } | 2117 | } |
2121 | } | 2118 | } |
2122 | ", | 2119 | ", |
2123 | ); | 2120 | ); |
2124 | } | 2121 | } |
2125 | 2122 | ||
2126 | #[test] | 2123 | #[test] |
2127 | fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() { | 2124 | fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() { |
2128 | // This is a false negative. | 2125 | // This is a false negative. |
2129 | // We don't currently handle tuple patterns with ellipsis. | 2126 | // We don't currently handle tuple patterns with ellipsis. |
2130 | check_no_diagnostic( | 2127 | check_no_diagnostic( |
2131 | r" | 2128 | r" |
2132 | fn test_fn() { | 2129 | fn test_fn() { |
2133 | match (false, true, false) { | 2130 | match (false, true, false) { |
2134 | (.., false) => {}, | 2131 | (.., false) => {}, |
2135 | } | 2132 | } |
2136 | } | 2133 | } |
2137 | ", | 2134 | ", |
2138 | ); | 2135 | ); |
2139 | } | 2136 | } |
2140 | 2137 | ||
2141 | #[test] | 2138 | #[test] |
2142 | fn struct_missing_arm() { | 2139 | fn struct_missing_arm() { |
2143 | // This is a false negative. | 2140 | // This is a false negative. |
2144 | // We don't currently handle structs. | 2141 | // We don't currently handle structs. |
2145 | check_no_diagnostic( | 2142 | check_no_diagnostic( |
2146 | r" | 2143 | r" |
2147 | struct Foo { | 2144 | struct Foo { |
2148 | a: bool, | 2145 | a: bool, |
2149 | } | 2146 | } |
@@ -2153,6 +2150,7 @@ mod false_negatives { | |||
2153 | } | 2150 | } |
2154 | } | 2151 | } |
2155 | ", | 2152 | ", |
2156 | ); | 2153 | ); |
2154 | } | ||
2157 | } | 2155 | } |
2158 | } | 2156 | } |