Skip to content

Commit 243398d

Browse files
committed
Move trailing comma handling to Parser
1 parent f76e5ed commit 243398d

File tree

8 files changed

+189
-50
lines changed

8 files changed

+189
-50
lines changed

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -567,9 +567,15 @@ object Parsers {
567567
in.currentRegion.withCommasExpected {
568568
val ts = new ListBuffer[T]
569569
if (readFirst) ts += part()
570-
while in.token == COMMA do
570+
var done = false
571+
while in.token == COMMA && !done do
572+
val start = in.offset
571573
in.nextToken()
572-
ts += part()
574+
if in.isAfterLineEnd && (in.token == RPAREN || in.token == RBRACKET || in.token == RBRACE || in.token == OUTDENT) then
575+
// skip the trailing comma
576+
done = true
577+
else
578+
ts += part()
573579
ts.toList
574580
}
575581

@@ -3271,7 +3277,7 @@ object Parsers {
32713277
*/
32723278
def patDefOrDcl(start: Offset, mods: Modifiers): Tree = atSpan(start, nameStart) {
32733279
val first = pattern2()
3274-
var lhs = first match {
3280+
val lhs = first match {
32753281
case id: Ident if in.token == COMMA =>
32763282
in.nextToken()
32773283
id :: commaSeparated(() => termIdent())

compiler/src/dotty/tools/dotc/parsing/Scanners.scala

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -698,13 +698,6 @@ object Scanners {
698698
case r: Indented if isEnclosedInParens(r.outer) =>
699699
insert(OUTDENT, offset)
700700
case _ =>
701-
lookAhead()
702-
if isAfterLineEnd
703-
&& (token == RPAREN || token == RBRACKET || token == RBRACE || token == OUTDENT)
704-
then
705-
() /* skip the trailing comma */
706-
else
707-
reset()
708701
case END =>
709702
if !isEndMarker then token = IDENTIFIER
710703
case COLON =>

docs/_docs/internals/syntax.md

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ FunType ::= FunTypeArgs (‘=>’ | ‘?=>’) Type
168168
FunTypeArgs ::= InfixType
169169
| ‘(’ [ FunArgTypes ] ‘)’
170170
| FunParamClause
171-
FunParamClause ::= ‘(’ TypedFunParam {‘,’ TypedFunParam } ‘)’
171+
FunParamClause ::= ‘(’ TypedFunParam {‘,’ TypedFunParam } [ ‘,’ nl ] ‘)’
172172
TypedFunParam ::= id ‘:’ Type
173173
MatchType ::= InfixType `match` <<< TypeCaseClauses >>>
174174
InfixType ::= RefinedType {id [nl] RefinedType} InfixOp(t1, op, t2)
@@ -181,7 +181,7 @@ SimpleType ::= SimpleLiteral
181181
SimpleType1 ::= id Ident(name)
182182
| Singleton ‘.’ id Select(t, name)
183183
| Singleton ‘.’ ‘type’ SingletonTypeTree(p)
184-
| ‘(’ Types ‘)’ Tuple(ts)
184+
| ‘(’ Types [ ‘,’ nl ] ‘)’ Tuple(ts)
185185
| Refinement RefinedTypeTree(EmptyTree, refinement)
186186
| ‘$’ ‘{’ Block ‘}’ -- unless inside quoted pattern
187187
| ‘$’ ‘{’ Pattern ‘}’ -- only inside quoted pattern
@@ -196,7 +196,7 @@ FunArgType ::= Type
196196
FunArgTypes ::= FunArgType { ‘,’ FunArgType }
197197
ParamType ::= [‘=>’] ParamValueType
198198
ParamValueType ::= Type [‘*’] PostfixOp(t, "*")
199-
TypeArgs ::= ‘[’ Types ‘]’ ts
199+
TypeArgs ::= ‘[’ Types [ ‘,’ nl ] ‘]’ ts
200200
Refinement ::= ‘{’ [RefineDcl] {semi [RefineDcl]} ‘}’ ds
201201
TypeBounds ::= [‘>:’ Type] [‘<:’ Type] TypeBoundsTree(lo, hi)
202202
TypeParamBounds ::= TypeBounds {‘:’ Type} ContextBounds(typeBounds, tps)
@@ -249,7 +249,7 @@ SimpleExpr ::= SimpleRef
249249
| quoteId -- only inside splices
250250
| ‘new’ ConstrApp {‘with’ ConstrApp} [TemplateBody] New(constr | templ)
251251
| ‘new’ TemplateBody
252-
| ‘(’ ExprsInParens ‘)’ Parens(exprs)
252+
| ‘(’ ExprsInParens [ ‘,’ nl ] ‘)’ Parens(exprs)
253253
| SimpleExpr ‘.’ id Select(expr, id)
254254
| SimpleExpr ‘.’ MatchClause
255255
| SimpleExpr TypeArgs TypeApply(expr, args)
@@ -264,8 +264,8 @@ Quoted ::= ‘'’ ‘{’ Block ‘}’
264264
ExprsInParens ::= ExprInParens {‘,’ ExprInParens}
265265
ExprInParens ::= PostfixExpr ‘:’ Type -- normal Expr allows only RefinedType here
266266
| Expr
267-
ParArgumentExprs ::= ‘(’ [‘using’] ExprsInParens ‘)’ exprs
268-
| ‘(’ [ExprsInParens ‘,’] PostfixExpr ‘*’ ‘)’ exprs :+ Typed(expr, Ident(wildcardStar))
267+
ParArgumentExprs ::= ‘(’ [‘using’] ExprsInParens [ ‘,’ nl ] ‘)’ exprs
268+
| ‘(’ [ExprsInParens ‘,’] PostfixExpr ‘*’ [ ‘,’ nl ] ‘)’ exprs :+ Typed(expr, Ident(wildcardStar))
269269
ArgumentExprs ::= ParArgumentExprs
270270
| BlockExpr
271271
BlockExpr ::= <<< CaseClauses | Block >>>
@@ -309,44 +309,44 @@ SimplePattern1 ::= SimpleRef
309309
PatVar ::= varid
310310
| ‘_’
311311
Patterns ::= Pattern {‘,’ Pattern}
312-
ArgumentPatterns ::= ‘(’ [Patterns] ‘)’ Apply(fn, pats)
313-
| ‘(’ [Patterns ‘,’] PatVar ‘*’ ‘)’
312+
ArgumentPatterns ::= ‘(’ [Patterns] [ ‘,’ nl ] ‘)’ Apply(fn, pats)
313+
| ‘(’ [Patterns ‘,’] PatVar ‘*’ [ ‘,’ nl ] ‘)’
314314
```
315315

316316
### Type and Value Parameters
317317
```ebnf
318-
ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} ‘]’
318+
ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} [ ‘,’ nl ] ‘]’
319319
ClsTypeParam ::= {Annotation} [‘+’ | ‘-’] TypeDef(Modifiers, name, tparams, bounds)
320320
id [HkTypeParamClause] TypeParamBounds Bound(below, above, context)
321321
322-
DefTypeParamClause::= ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’
322+
DefTypeParamClause::= ‘[’ DefTypeParam {‘,’ DefTypeParam} [ ‘,’ nl ] ‘]’
323323
DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds
324324
325-
TypTypeParamClause::= ‘[’ TypTypeParam {‘,’ TypTypeParam} ‘]’
325+
TypTypeParamClause::= ‘[’ TypTypeParam {‘,’ TypTypeParam} [ ‘,’ nl ] ‘]’
326326
TypTypeParam ::= {Annotation} id [HkTypeParamClause] TypeBounds
327327
328-
HkTypeParamClause ::= ‘[’ HkTypeParam {‘,’ HkTypeParam} ‘]’
328+
HkTypeParamClause ::= ‘[’ HkTypeParam {‘,’ HkTypeParam} [ ‘,’ nl ] ‘]’
329329
HkTypeParam ::= {Annotation} [‘+’ | ‘-’] (id [HkTypeParamClause] | ‘_’)
330330
TypeBounds
331331
332-
ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’]
333-
ClsParamClause ::= [nl] ‘(’ ClsParams ‘)’
334-
| [nl] ‘(’ ‘using’ (ClsParams | FunArgTypes) ‘)’
332+
ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams [ ‘,’ nl ] ‘)’]
333+
ClsParamClause ::= [nl] ‘(’ ClsParams [ ‘,’ nl ] ‘)’
334+
| [nl] ‘(’ ‘using’ (ClsParams | FunArgTypes) [ ‘,’ nl ] ‘)’
335335
ClsParams ::= ClsParam {‘,’ ClsParam}
336336
ClsParam ::= {Annotation} ValDef(mods, id, tpe, expr) -- point of mods on val/var
337337
[{Modifier} (‘val’ | ‘var’) | ‘inline’] Param
338338
Param ::= id ‘:’ ParamType [‘=’ Expr]
339339
340-
DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’]
341-
DefParamClause ::= [nl] ‘(’ DefParams ‘)’ | UsingParamClause
342-
UsingParamClause ::= [nl] ‘(’ ‘using’ (DefParams | FunArgTypes) ‘)’
340+
DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams [ ‘,’ nl ] ‘)’]
341+
DefParamClause ::= [nl] ‘(’ DefParams [ ‘,’ nl ] ‘)’ | UsingParamClause
342+
UsingParamClause ::= [nl] ‘(’ ‘using’ (DefParams | FunArgTypes) [ ‘,’ nl ] ‘)’
343343
DefParams ::= DefParam {‘,’ DefParam}
344344
DefParam ::= {Annotation} [‘inline’] Param ValDef(mods, id, tpe, expr) -- point of mods at id.
345345
```
346346

347347
### Bindings and Imports
348348
```ebnf
349-
Bindings ::= ‘(’ [Binding {‘,’ Binding}] ‘)’
349+
Bindings ::= ‘(’ [Binding {‘,’ Binding}] [ ‘,’ nl ] ‘)’
350350
Binding ::= (id | ‘_’) [‘:’ Type] ValDef(_, id, tpe, EmptyTree)
351351
352352
Modifier ::= LocalModifier
@@ -371,7 +371,7 @@ ImportExpr ::= SimpleRef {‘.’ id} ‘.’ ImportSpec
371371
| SimpleRef ‘as’ id Import(EmptyTree, ImportSelector(ref, id))
372372
ImportSpec ::= NamedSelector
373373
| WildcardSelector
374-
| ‘{’ ImportSelectors) ‘}’
374+
| ‘{’ ImportSelectors [ ‘,’ nl ] ‘}’
375375
NamedSelector ::= id [‘as’ (id | ‘_’)]
376376
WildCardSelector ::= ‘*' | ‘given’ [InfixType]
377377
ImportSelectors ::= NamedSelector [‘,’ ImportSelectors]

docs/_docs/reference/syntax.md

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ FunType ::= FunTypeArgs (‘=>’ | ‘?=>’) Type
168168
FunTypeArgs ::= InfixType
169169
| ‘(’ [ FunArgTypes ] ‘)’
170170
| FunParamClause
171-
FunParamClause ::= ‘(’ TypedFunParam {‘,’ TypedFunParam } ‘)’
171+
FunParamClause ::= ‘(’ TypedFunParam {‘,’ TypedFunParam } [ ‘,’ nl ] ‘)’
172172
TypedFunParam ::= id ‘:’ Type
173173
MatchType ::= InfixType `match` <<< TypeCaseClauses >>>
174174
InfixType ::= RefinedType {id [nl] RefinedType}
@@ -180,7 +180,7 @@ SimpleType ::= SimpleLiteral
180180
| id
181181
| Singleton ‘.’ id
182182
| Singleton ‘.’ ‘type’
183-
| ‘(’ Types ‘)’
183+
| ‘(’ Types [ ‘,’ nl ] ‘)’
184184
| Refinement
185185
| ‘$’ ‘{’ Block ‘}’ -- unless inside quoted pattern
186186
| ‘$’ ‘{’ Pattern ‘}’ -- only inside quoted pattern
@@ -195,7 +195,7 @@ FunArgType ::= Type
195195
FunArgTypes ::= FunArgType { ‘,’ FunArgType }
196196
ParamType ::= [‘=>’] ParamValueType
197197
ParamValueType ::= Type [‘*’]
198-
TypeArgs ::= ‘[’ Types ‘]’
198+
TypeArgs ::= ‘[’ Types [ ‘,’ nl ] ‘]’
199199
Refinement ::= ‘{’ [RefineDcl] {semi [RefineDcl]} ‘}’
200200
TypeBounds ::= [‘>:’ Type] [‘<:’ Type]
201201
TypeParamBounds ::= TypeBounds {‘:’ Type}
@@ -247,7 +247,7 @@ SimpleExpr ::= SimpleRef
247247
| quoteId -- only inside splices
248248
| ‘new’ ConstrApp {‘with’ ConstrApp} [TemplateBody]
249249
| ‘new’ TemplateBody
250-
| ‘(’ ExprsInParens ‘)’
250+
| ‘(’ ExprsInParens [ ‘,’ nl ] ‘)’
251251
| SimpleExpr ‘.’ id
252252
| SimpleExpr ‘.’ MatchClause
253253
| SimpleExpr TypeArgs
@@ -257,8 +257,8 @@ Quoted ::= ‘'’ ‘{’ Block ‘}’
257257
ExprsInParens ::= ExprInParens {‘,’ ExprInParens}
258258
ExprInParens ::= PostfixExpr ‘:’ Type
259259
| Expr
260-
ParArgumentExprs ::= ‘(’ [‘using’] ExprsInParens ‘)’
261-
| ‘(’ [ExprsInParens ‘,’] PostfixExpr ‘*’ ‘)’
260+
ParArgumentExprs ::= ‘(’ [‘using’] ExprsInParens [ ‘,’ nl ] ‘)’
261+
| ‘(’ [ExprsInParens ‘,’] PostfixExpr ‘*’ [ ‘,’ nl ] ‘)’
262262
ArgumentExprs ::= ParArgumentExprs
263263
| BlockExpr
264264
BlockExpr ::= <<< (CaseClauses | Block) >>>
@@ -301,41 +301,41 @@ SimplePattern1 ::= SimpleRef
301301
PatVar ::= varid
302302
| ‘_’
303303
Patterns ::= Pattern {‘,’ Pattern}
304-
ArgumentPatterns ::= ‘(’ [Patterns] ‘)’
305-
| ‘(’ [Patterns ‘,’] PatVar ‘*’ ‘)’
304+
ArgumentPatterns ::= ‘(’ [Patterns] [ ‘,’ nl ] ‘)’
305+
| ‘(’ [Patterns ‘,’] PatVar ‘*’ [ ‘,’ nl ] ‘)’
306306
```
307307

308308
### Type and Value Parameters
309309
```
310-
ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} ‘]’
310+
ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} [ ‘,’ nl ] ‘]’
311311
ClsTypeParam ::= {Annotation} [‘+’ | ‘-’] id [HkTypeParamClause] TypeParamBounds
312312
313-
DefTypeParamClause::= ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’
313+
DefTypeParamClause::= ‘[’ DefTypeParam {‘,’ DefTypeParam} [ ‘,’ nl ] ‘]’
314314
DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds
315315
316-
TypTypeParamClause::= ‘[’ TypTypeParam {‘,’ TypTypeParam} ‘]’
316+
TypTypeParamClause::= ‘[’ TypTypeParam {‘,’ TypTypeParam} [ ‘,’ nl ] ‘]’
317317
TypTypeParam ::= {Annotation} id [HkTypeParamClause] TypeBounds
318318
319-
HkTypeParamClause ::= ‘[’ HkTypeParam {‘,’ HkTypeParam} ‘]’
319+
HkTypeParamClause ::= ‘[’ HkTypeParam {‘,’ HkTypeParam} [ ‘,’ nl ] ‘]’
320320
HkTypeParam ::= {Annotation} [‘+’ | ‘-’] (id [HkTypeParamClause] | ‘_’) TypeBounds
321321
322-
ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’]
323-
ClsParamClause ::= [nl] ‘(’ ClsParams ‘)’
324-
| [nl] ‘(’ ‘using’ (ClsParams | FunArgTypes) ‘)’
322+
ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams [ ‘,’ nl ] ‘)’]
323+
ClsParamClause ::= [nl] ‘(’ ClsParams [ ‘,’ nl ] ‘)’
324+
| [nl] ‘(’ ‘using’ (ClsParams | FunArgTypes) [ ‘,’ nl ] ‘)’
325325
ClsParams ::= ClsParam {‘,’ ClsParam}
326326
ClsParam ::= {Annotation} [{Modifier} (‘val’ | ‘var’) | ‘inline’] Param
327327
Param ::= id ‘:’ ParamType [‘=’ Expr]
328328
329-
DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’]
330-
DefParamClause ::= [nl] ‘(’ DefParams ‘)’ | UsingParamClause
331-
UsingParamClause ::= [nl] ‘(’ ‘using’ (DefParams | FunArgTypes) ‘)’
329+
DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams [ ‘,’ nl ] ‘)’]
330+
DefParamClause ::= [nl] ‘(’ DefParams [ ‘,’ nl ] ‘)’ | UsingParamClause
331+
UsingParamClause ::= [nl] ‘(’ ‘using’ (DefParams | FunArgTypes) [ ‘,’ nl ] ‘)’
332332
DefParams ::= DefParam {‘,’ DefParam}
333333
DefParam ::= {Annotation} [‘inline’] Param
334334
```
335335

336336
### Bindings and Imports
337337
```
338-
Bindings ::= ‘(’ [Binding {‘,’ Binding}] ‘)’
338+
Bindings ::= ‘(’ [Binding {‘,’ Binding}] [ ‘,’ nl ] ‘)’
339339
Binding ::= (id | ‘_’) [‘:’ Type]
340340
341341
Modifier ::= LocalModifier
@@ -360,7 +360,7 @@ ImportExpr ::= SimpleRef {‘.’ id} ‘.’ ImportSpec
360360
| SimpleRef ‘as’ id
361361
ImportSpec ::= NamedSelector
362362
| WildcardSelector
363-
| ‘{’ ImportSelectors) ‘}’
363+
| ‘{’ ImportSelectors [ ‘,’ nl ] ‘}’
364364
NamedSelector ::= id [‘as’ (id | ‘_’)]
365365
WildCardSelector ::= ‘*' | ‘given’ [InfixType]
366366
ImportSelectors ::= NamedSelector [‘,’ ImportSelectors]

tests/neg/t11900.check

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
-- Error: tests/neg/t11900.scala:44:16 ---------------------------------------------------------------------------------
2+
44 | a => a + 1, // error: weird comma
3+
| ^
4+
| end of statement expected but ',' found
5+
-- Error: tests/neg/t11900.scala:48:16 ---------------------------------------------------------------------------------
6+
48 | println("a"), // error: weird comma
7+
| ^
8+
| end of statement expected but ',' found
9+
-- Error: tests/neg/t11900.scala:52:16 ---------------------------------------------------------------------------------
10+
52 | println("b"), // error: weird comma
11+
| ^
12+
| end of statement expected but ',' found
13+
-- [E032] Syntax Error: tests/neg/t11900.scala:64:8 --------------------------------------------------------------------
14+
64 | _*, // error
15+
| ^
16+
| pattern expected
17+
|
18+
| longer explanation available when compiling with `-explain`

tests/neg/t11900.scala

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
2+
trait t11900 {
3+
// cf pos/trailing-commas
4+
//
5+
import scala.collection.{
6+
immutable,
7+
mutable,
8+
}
9+
10+
def h[A,
11+
]: List[A] = Nil
12+
13+
def u(
14+
x: Int,
15+
y: Int,
16+
)(using List[Int],
17+
Set[Int],
18+
)(using l: List[Int],
19+
s : Set[Int],
20+
): Int = 1
21+
22+
def g = List(
23+
1,
24+
2,
25+
3,
26+
)
27+
28+
def star =
29+
List(1, 2, 3, 4, 5) match {
30+
case List(
31+
1,
32+
2,
33+
3,
34+
) => false
35+
case List(
36+
1,
37+
2,
38+
_*,
39+
) => true
40+
}
41+
42+
def f =
43+
List(1, 2, 3).map {
44+
a => a + 1, // error: weird comma
45+
}
46+
47+
class A() {
48+
println("a"), // error: weird comma
49+
}
50+
51+
def b() = {
52+
println("b"), // error: weird comma
53+
}
54+
55+
def starcrossed =
56+
List(1, 2, 3, 4, 5) match {
57+
case List(
58+
1,
59+
2,
60+
3,
61+
) => false
62+
case List(
63+
1,
64+
_*, // error
65+
2,
66+
) => true
67+
}
68+
69+
def p(p: (Int,
70+
String,
71+
)
72+
): Unit
73+
74+
def q: (Int,
75+
String,
76+
)
77+
78+
val z = 42
79+
}

tests/neg/trailingCommas.scala

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,27 @@ object `package` {
5656
case class Foo(foo: Any)
5757
case class Bar(foo: Any)
5858
}
59+
60+
// Unparenthesized lists
61+
trait Deriv1[T]
62+
object Deriv1 {
63+
def derived[T]: Deriv1[T] = new Deriv1[T] {}
64+
}
65+
66+
trait Deriv2[T]
67+
object Deriv2 {
68+
def derived[T]: Deriv2[T] = new Deriv2[T] {}
69+
}
70+
71+
class Derives1 derives Deriv1, Deriv2,
72+
object End // error: an identifier expected, but 'object' found
73+
74+
class Derives2 derives Deriv1,
75+
Deriv2,
76+
object End2 // error: an identifier expected, but 'object' found
77+
78+
val a,
79+
b,
80+
c,
81+
= (1, 2, 3) // error
82+
val x, y, z, = (1, 2, 3) // error

0 commit comments

Comments
 (0)