Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 0e28202

Browse files
committed
Insert missing expr/pat for leading comma tuples
1 parent 7d1bf70 commit 0e28202

File tree

2 files changed

+56
-19
lines changed

2 files changed

+56
-19
lines changed

crates/hir-def/src/body/lower.rs

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -542,9 +542,18 @@ impl ExprCollector<'_> {
542542
self.alloc_expr(Expr::BinaryOp { lhs, rhs, op }, syntax_ptr)
543543
}
544544
ast::Expr::TupleExpr(e) => {
545-
let exprs = e.fields().map(|expr| self.collect_expr(expr)).collect();
545+
let mut exprs: Vec<_> = e.fields().map(|expr| self.collect_expr(expr)).collect();
546+
// if there is a leading comma, the user is most likely to type out a leading expression
547+
// so we insert a missing expression at the beginning for IDE features
548+
if comma_follows_token(e.l_paren_token()) {
549+
exprs.insert(0, self.missing_expr());
550+
}
551+
546552
self.alloc_expr(
547-
Expr::Tuple { exprs, is_assignee_expr: self.is_lowering_assignee_expr },
553+
Expr::Tuple {
554+
exprs: exprs.into_boxed_slice(),
555+
is_assignee_expr: self.is_lowering_assignee_expr,
556+
},
548557
syntax_ptr,
549558
)
550559
}
@@ -1180,7 +1189,11 @@ impl ExprCollector<'_> {
11801189
ast::Pat::TupleStructPat(p) => {
11811190
let path =
11821191
p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
1183-
let (args, ellipsis) = self.collect_tuple_pat(p.fields(), binding_list);
1192+
let (args, ellipsis) = self.collect_tuple_pat(
1193+
p.fields(),
1194+
comma_follows_token(p.l_paren_token()),
1195+
binding_list,
1196+
);
11841197
Pat::TupleStruct { path, args, ellipsis }
11851198
}
11861199
ast::Pat::RefPat(p) => {
@@ -1199,7 +1212,11 @@ impl ExprCollector<'_> {
11991212
}
12001213
ast::Pat::ParenPat(p) => return self.collect_pat_opt(p.pat(), binding_list),
12011214
ast::Pat::TuplePat(p) => {
1202-
let (args, ellipsis) = self.collect_tuple_pat(p.fields(), binding_list);
1215+
let (args, ellipsis) = self.collect_tuple_pat(
1216+
p.fields(),
1217+
comma_follows_token(p.l_paren_token()),
1218+
binding_list,
1219+
);
12031220
Pat::Tuple { args, ellipsis }
12041221
}
12051222
ast::Pat::WildcardPat(_) => Pat::Wild,
@@ -1323,18 +1340,24 @@ impl ExprCollector<'_> {
13231340
fn collect_tuple_pat(
13241341
&mut self,
13251342
args: AstChildren<ast::Pat>,
1343+
has_leading_comma: bool,
13261344
binding_list: &mut BindingList,
13271345
) -> (Box<[PatId]>, Option<usize>) {
13281346
// Find the location of the `..`, if there is one. Note that we do not
13291347
// consider the possibility of there being multiple `..` here.
13301348
let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::RestPat(_)));
13311349
// We want to skip the `..` pattern here, since we account for it above.
1332-
let args = args
1350+
let mut args: Vec<_> = args
13331351
.filter(|p| !matches!(p, ast::Pat::RestPat(_)))
13341352
.map(|p| self.collect_pat(p, binding_list))
13351353
.collect();
1354+
// if there is a leading comma, the user is most likely to type out a leading pattern
1355+
// so we insert a missing pattern at the beginning for IDE features
1356+
if has_leading_comma {
1357+
args.insert(0, self.missing_pat());
1358+
}
13361359

1337-
(args, ellipsis)
1360+
(args.into_boxed_slice(), ellipsis)
13381361
}
13391362

13401363
// endregion: patterns
@@ -1493,3 +1516,8 @@ impl ExprCollector<'_> {
14931516
self.body.labels.alloc(label)
14941517
}
14951518
}
1519+
1520+
fn comma_follows_token(t: Option<syntax::SyntaxToken>) -> bool {
1521+
(|| syntax::algo::skip_trivia_token(t?.next_token()?, syntax::Direction::Next))()
1522+
.map_or(false, |it| it.kind() == syntax::T![,])
1523+
}

crates/ide/src/signature_help.rs

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1996,27 +1996,27 @@ fn main() {
19961996

19971997
#[test]
19981998
fn test_tuple_expr_expected() {
1999-
// FIXME: Seems like we discard valuable results in typeck here
20001999
check(
20012000
r#"
20022001
fn main() {
20032002
let _: (&str, u32, u32)= ($0, 1, 3);
20042003
}
20052004
"#,
20062005
expect![[r#"
2007-
(&str, u32)
2008-
^^^^ ---
2006+
(&str, u32, u32)
2007+
^^^^ --- ---
20092008
"#]],
20102009
);
2010+
// FIXME: Should typeck report a 4-ary tuple for the expression here?
20112011
check(
20122012
r#"
20132013
fn main() {
2014-
let _: (&str, u32, u32, u32)= ($0, 1, 3);
2014+
let _: (&str, u32, u32, u32) = ($0, 1, 3);
20152015
}
20162016
"#,
20172017
expect![[r#"
2018-
(&str, u32)
2019-
^^^^ ---
2018+
(&str, u32, u32)
2019+
^^^^ --- ---
20202020
"#]],
20212021
);
20222022
check(
@@ -2026,15 +2026,25 @@ fn main() {
20262026
}
20272027
"#,
20282028
expect![[r#"
2029-
(&str, u32, u32)
2030-
^^^^ --- ---
2029+
(&str, u32, u32, i32)
2030+
^^^^ --- --- ---
20312031
"#]],
20322032
);
20332033
}
20342034

20352035
#[test]
20362036
fn test_tuple_pat_free() {
2037-
// FIXME: Seems like we discard valuable results in typeck here
2037+
check(
2038+
r#"
2039+
fn main() {
2040+
let ($0, 1, 3);
2041+
}
2042+
"#,
2043+
expect![[r#"
2044+
({unknown}, i32, i32)
2045+
^^^^^^^^^ --- ---
2046+
"#]],
2047+
);
20382048
check(
20392049
r#"
20402050
fn main() {
@@ -2123,10 +2133,9 @@ fn main() {
21232133
let ($0, 1, 3): (i32, i32, i32);
21242134
}
21252135
"#,
2126-
// FIXME: tuple pat should be of size 3 ideally
21272136
expect![[r#"
2128-
(i32, i32)
2129-
^^^ ---
2137+
(i32, i32, i32)
2138+
^^^ --- ---
21302139
"#]],
21312140
);
21322141
check(
@@ -2182,7 +2191,7 @@ fn main() {
21822191
let ($0 1, 3) = (1, 2, 3);
21832192
}
21842193
"#,
2185-
// FIXME: tuple pat should be of size 3 ideally
2194+
// FIXME: Should typeck report a 3-ary tuple for the pattern here?
21862195
expect![[r#"
21872196
(i32, i32)
21882197
^^^ ---

0 commit comments

Comments
 (0)