@@ -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+ }
0 commit comments