about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir-def/src/body/lower.rs40
-rw-r--r--crates/ide/src/signature_help.rs35
2 files changed, 56 insertions, 19 deletions
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index acc9943481a..ebe05afca6a 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -542,9 +542,18 @@ impl ExprCollector<'_> {
                 self.alloc_expr(Expr::BinaryOp { lhs, rhs, op }, syntax_ptr)
             }
             ast::Expr::TupleExpr(e) => {
-                let exprs = e.fields().map(|expr| self.collect_expr(expr)).collect();
+                let mut exprs: Vec<_> = e.fields().map(|expr| self.collect_expr(expr)).collect();
+                // if there is a leading comma, the user is most likely to type out a leading expression
+                // so we insert a missing expression at the beginning for IDE features
+                if comma_follows_token(e.l_paren_token()) {
+                    exprs.insert(0, self.missing_expr());
+                }
+
                 self.alloc_expr(
-                    Expr::Tuple { exprs, is_assignee_expr: self.is_lowering_assignee_expr },
+                    Expr::Tuple {
+                        exprs: exprs.into_boxed_slice(),
+                        is_assignee_expr: self.is_lowering_assignee_expr,
+                    },
                     syntax_ptr,
                 )
             }
@@ -1180,7 +1189,11 @@ impl ExprCollector<'_> {
             ast::Pat::TupleStructPat(p) => {
                 let path =
                     p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
-                let (args, ellipsis) = self.collect_tuple_pat(p.fields(), binding_list);
+                let (args, ellipsis) = self.collect_tuple_pat(
+                    p.fields(),
+                    comma_follows_token(p.l_paren_token()),
+                    binding_list,
+                );
                 Pat::TupleStruct { path, args, ellipsis }
             }
             ast::Pat::RefPat(p) => {
@@ -1199,7 +1212,11 @@ impl ExprCollector<'_> {
             }
             ast::Pat::ParenPat(p) => return self.collect_pat_opt(p.pat(), binding_list),
             ast::Pat::TuplePat(p) => {
-                let (args, ellipsis) = self.collect_tuple_pat(p.fields(), binding_list);
+                let (args, ellipsis) = self.collect_tuple_pat(
+                    p.fields(),
+                    comma_follows_token(p.l_paren_token()),
+                    binding_list,
+                );
                 Pat::Tuple { args, ellipsis }
             }
             ast::Pat::WildcardPat(_) => Pat::Wild,
@@ -1323,18 +1340,24 @@ impl ExprCollector<'_> {
     fn collect_tuple_pat(
         &mut self,
         args: AstChildren<ast::Pat>,
+        has_leading_comma: bool,
         binding_list: &mut BindingList,
     ) -> (Box<[PatId]>, Option<usize>) {
         // Find the location of the `..`, if there is one. Note that we do not
         // consider the possibility of there being multiple `..` here.
         let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::RestPat(_)));
         // We want to skip the `..` pattern here, since we account for it above.
-        let args = args
+        let mut args: Vec<_> = args
             .filter(|p| !matches!(p, ast::Pat::RestPat(_)))
             .map(|p| self.collect_pat(p, binding_list))
             .collect();
+        // if there is a leading comma, the user is most likely to type out a leading pattern
+        // so we insert a missing pattern at the beginning for IDE features
+        if has_leading_comma {
+            args.insert(0, self.missing_pat());
+        }
 
-        (args, ellipsis)
+        (args.into_boxed_slice(), ellipsis)
     }
 
     // endregion: patterns
@@ -1493,3 +1516,8 @@ impl ExprCollector<'_> {
         self.body.labels.alloc(label)
     }
 }
+
+fn comma_follows_token(t: Option<syntax::SyntaxToken>) -> bool {
+    (|| syntax::algo::skip_trivia_token(t?.next_token()?, syntax::Direction::Next))()
+        .map_or(false, |it| it.kind() == syntax::T![,])
+}
diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs
index 9bd9a948e7f..7795be54e26 100644
--- a/crates/ide/src/signature_help.rs
+++ b/crates/ide/src/signature_help.rs
@@ -1996,7 +1996,6 @@ fn main() {
 
     #[test]
     fn test_tuple_expr_expected() {
-        // FIXME: Seems like we discard valuable results in typeck here
         check(
             r#"
 fn main() {
@@ -2004,19 +2003,20 @@ fn main() {
 }
 "#,
             expect![[r#"
-                (&str, u32)
-                 ^^^^  ---
+                (&str, u32, u32)
+                 ^^^^  ---  ---
             "#]],
         );
+        // FIXME: Should typeck report a 4-ary tuple for the expression here?
         check(
             r#"
 fn main() {
-    let _: (&str, u32, u32, u32)= ($0, 1, 3);
+    let _: (&str, u32, u32, u32) = ($0, 1, 3);
 }
 "#,
             expect![[r#"
-                (&str, u32)
-                 ^^^^  ---
+                (&str, u32, u32)
+                 ^^^^  ---  ---
             "#]],
         );
         check(
@@ -2026,15 +2026,25 @@ fn main() {
 }
 "#,
             expect![[r#"
-                (&str, u32, u32)
-                 ^^^^  ---  ---
+                (&str, u32, u32, i32)
+                 ^^^^  ---  ---  ---
             "#]],
         );
     }
 
     #[test]
     fn test_tuple_pat_free() {
-        // FIXME: Seems like we discard valuable results in typeck here
+        check(
+            r#"
+fn main() {
+    let ($0, 1, 3);
+}
+"#,
+            expect![[r#"
+                ({unknown}, i32, i32)
+                 ^^^^^^^^^  ---  ---
+            "#]],
+        );
         check(
             r#"
 fn main() {
@@ -2123,10 +2133,9 @@ fn main() {
     let ($0, 1, 3): (i32, i32, i32);
 }
 "#,
-            // FIXME: tuple pat should be of size 3 ideally
             expect![[r#"
-                (i32, i32)
-                 ^^^  ---
+                (i32, i32, i32)
+                 ^^^  ---  ---
             "#]],
         );
         check(
@@ -2182,7 +2191,7 @@ fn main() {
     let ($0 1, 3) = (1, 2, 3);
 }
 "#,
-            // FIXME: tuple pat should be of size 3 ideally
+            // FIXME: Should typeck report a 3-ary tuple for the pattern here?
             expect![[r#"
                 (i32, i32)
                  ^^^  ---