]> git.lizzy.rs Git - rust.git/commitdiff
Handle ellipsis in tuple patterns in match exhaustiveness checking
authorLukas Wirth <lukastw97@gmail.com>
Tue, 24 Nov 2020 15:43:28 +0000 (16:43 +0100)
committerLukas Wirth <lukastw97@gmail.com>
Tue, 24 Nov 2020 15:43:28 +0000 (16:43 +0100)
crates/hir_ty/src/diagnostics/match_check.rs

index 5bd03f2ac00d1c6046ded5f1960a0c8773c3d4b0..78e207f865dfd74e3f41b93342a72856452a4261 100644 (file)
 //!   U(P, p) := U(P, (r_1, p_2, .., p_n))
 //!            || U(P, (r_2, p_2, .., p_n))
 //!   ```
-use std::sync::Arc;
+use std::{iter, sync::Arc};
 
 use arena::Idx;
 use hir_def::{
@@ -366,16 +366,17 @@ fn specialize_constructor(
 
         let head_pat = head.as_pat(cx);
         let result = match (head_pat, constructor) {
-            (Pat::Tuple { args: ref pat_ids, ellipsis }, Constructor::Tuple { arity: _ }) => {
-                if ellipsis.is_some() {
-                    // If there are ellipsis here, we should add the correct number of
-                    // Pat::Wild patterns to `pat_ids`. We should be able to use the
-                    // constructors arity for this, but at the time of writing we aren't
-                    // correctly calculating this arity when ellipsis are present.
-                    return Err(MatchCheckErr::NotImplemented);
+            (Pat::Tuple { args: pat_ids, ellipsis }, &Constructor::Tuple { arity }) => {
+                if let Some(ellipsis) = ellipsis {
+                    let (pre, post) = pat_ids.split_at(ellipsis);
+                    let n_wild_pats = arity.saturating_sub(pat_ids.len());
+                    let pre_iter = pre.iter().map(Into::into);
+                    let wildcards = iter::repeat(PatIdOrWild::Wild).take(n_wild_pats);
+                    let post_iter = post.iter().map(Into::into);
+                    Some(self.replace_head_with(pre_iter.chain(wildcards).chain(post_iter)))
+                } else {
+                    Some(self.replace_head_with(pat_ids.iter()))
                 }
-
-                Some(self.replace_head_with(pat_ids.iter()))
             }
             (Pat::Lit(lit_expr), Constructor::Bool(constructor_val)) => {
                 match cx.body.exprs[lit_expr] {
@@ -767,10 +768,11 @@ fn all_constructors(&self, cx: &MatchCheckCtx) -> Vec<Constructor> {
 fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Option<Constructor>> {
     let res = match pat.as_pat(cx) {
         Pat::Wild => None,
-        // FIXME somehow create the Tuple constructor with the proper arity. If there are
-        // ellipsis, the arity is not equal to the number of patterns.
-        Pat::Tuple { args: pats, ellipsis } if ellipsis.is_none() => {
-            Some(Constructor::Tuple { arity: pats.len() })
+        Pat::Tuple { .. } => {
+            let pat_id = pat.as_id().expect("we already know this pattern is not a wild");
+            Some(Constructor::Tuple {
+                arity: cx.infer.type_of_pat[pat_id].as_tuple().ok_or(MatchCheckErr::Unknown)?.len(),
+            })
         }
         Pat::Lit(lit_expr) => match cx.body.exprs[lit_expr] {
             Expr::Literal(Literal::Bool(val)) => Some(Constructor::Bool(val)),
@@ -1352,6 +1354,31 @@ fn main() {
         );
     }
 
+    #[test]
+    fn tuple_of_bools_with_ellipsis_at_end_missing_arm() {
+        check_diagnostics(
+            r#"
+fn main() {
+    match (false, true, false) {
+        //^^^^^^^^^^^^^^^^^^^^ Missing match arm
+        (false, ..) => (),
+    }
+}"#,
+        );
+    }
+
+    #[test]
+    fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() {
+        check_diagnostics(
+            r#"
+fn main() {
+    match (false, true, false) {
+        //^^^^^^^^^^^^^^^^^^^^ Missing match arm
+        (.., false) => (),
+    }
+}"#,
+        );
+    }
     mod false_negatives {
         //! The implementation of match checking here is a work in progress. As we roll this out, we
         //! prefer false negatives to false positives (ideally there would be no false positives). This
@@ -1394,34 +1421,6 @@ enum Either { A(bool), B }
             );
         }
 
-        #[test]
-        fn tuple_of_bools_with_ellipsis_at_end_missing_arm() {
-            // We don't currently handle tuple patterns with ellipsis.
-            check_diagnostics(
-                r#"
-fn main() {
-    match (false, true, false) {
-        (false, ..) => (),
-    }
-}
-"#,
-            );
-        }
-
-        #[test]
-        fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() {
-            // We don't currently handle tuple patterns with ellipsis.
-            check_diagnostics(
-                r#"
-fn main() {
-    match (false, true, false) {
-        (.., false) => (),
-    }
-}
-"#,
-            );
-        }
-
         #[test]
         fn struct_missing_arm() {
             // We don't currently handle structs.