Skip to content

Commit d6837ae

Browse files
authored
Merge pull request scala#5245 from dwijnand/trailing-commas
SI-4986 The glorious return of Comma McTraily
2 parents c5c391c + d55bad8 commit d6837ae

File tree

6 files changed

+368
-0
lines changed

6 files changed

+368
-0
lines changed

src/compiler/scala/tools/nsc/ast/parser/Scanners.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,17 @@ trait Scanners extends ScannersCommon {
386386
next copyFrom this
387387
this copyFrom prev
388388
}
389+
} else if (token == COMMA) {
390+
// SIP-27 Trailing Comma (multi-line only) support
391+
// If a comma is followed by a new line & then a closing paren, bracket or brace
392+
// then it is a trailing comma and is ignored
393+
val saved = new ScannerData {} copyFrom this
394+
fetchToken()
395+
if (afterLineEnd() && (token == RPAREN || token == RBRACKET || token == RBRACE)) {
396+
/* skip the trailing comma */
397+
} else if (token == EOF) { // e.g. when the REPL is parsing "val List(x, y, _*,"
398+
/* skip the trailing comma */
399+
} else this copyFrom saved
389400
}
390401

391402
// print("["+this+"]")

test/files/neg/trailing-commas.check

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
trailing-commas.scala:10: error: illegal start of simple expression
2+
trait ArgumentExprs1 { f(23, "bar", )(Ev0, Ev1) }
3+
^
4+
trailing-commas.scala:10: error: ')' expected but '}' found.
5+
trait ArgumentExprs1 { f(23, "bar", )(Ev0, Ev1) }
6+
^
7+
trailing-commas.scala:11: error: illegal start of simple expression
8+
trait ArgumentExprs2 { f(23, "bar")(Ev0, Ev1, ) }
9+
^
10+
trailing-commas.scala:11: error: ')' expected but '}' found.
11+
trait ArgumentExprs2 { f(23, "bar")(Ev0, Ev1, ) }
12+
^
13+
trailing-commas.scala:12: error: illegal start of simple expression
14+
trait ArgumentExprs3 { new C(23, "bar", )(Ev0, Ev1) }
15+
^
16+
trailing-commas.scala:12: error: ')' expected but '}' found.
17+
trait ArgumentExprs3 { new C(23, "bar", )(Ev0, Ev1) }
18+
^
19+
trailing-commas.scala:13: error: illegal start of simple expression
20+
trait ArgumentExprs4 { new C(23, "bar")(Ev0, Ev1, ) }
21+
^
22+
trailing-commas.scala:13: error: ')' expected but '}' found.
23+
trait ArgumentExprs4 { new C(23, "bar")(Ev0, Ev1, ) }
24+
^
25+
trailing-commas.scala:15: error: identifier expected but ')' found.
26+
trait Params1 { def f(foo: Int, bar: String, )(implicit ev0: Ev0, ev1: Ev1, ) = 1 }
27+
^
28+
trailing-commas.scala:15: error: ':' expected but '}' found.
29+
trait Params1 { def f(foo: Int, bar: String, )(implicit ev0: Ev0, ev1: Ev1, ) = 1 }
30+
^
31+
trailing-commas.scala:16: error: identifier expected but ')' found.
32+
trait Params2 { def f(foo: Int, bar: String, )(implicit ev0: Ev0, ev1: Ev1, ) = 1 }
33+
^
34+
trailing-commas.scala:16: error: ':' expected but '}' found.
35+
trait Params2 { def f(foo: Int, bar: String, )(implicit ev0: Ev0, ev1: Ev1, ) = 1 }
36+
^
37+
trailing-commas.scala:17: error: identifier expected but ')' found.
38+
trait ClassParams1 { final class C(foo: Int, bar: String, )(implicit ev0: Ev0, ev1: Ev1) }
39+
^
40+
trailing-commas.scala:17: error: ':' expected but '}' found.
41+
trait ClassParams1 { final class C(foo: Int, bar: String, )(implicit ev0: Ev0, ev1: Ev1) }
42+
^
43+
trailing-commas.scala:18: error: identifier expected but ')' found.
44+
trait ClassParams2 { final class C(foo: Int, bar: String)(implicit ev0: Ev0, ev1: Ev1, ) }
45+
^
46+
trailing-commas.scala:18: error: ':' expected but '}' found.
47+
trait ClassParams2 { final class C(foo: Int, bar: String)(implicit ev0: Ev0, ev1: Ev1, ) }
48+
^
49+
trailing-commas.scala:20: error: illegal start of simple expression
50+
trait SimpleExpr { (23, "bar", ) }
51+
^
52+
trailing-commas.scala:20: error: ')' expected but '}' found.
53+
trait SimpleExpr { (23, "bar", ) }
54+
^
55+
trailing-commas.scala:22: error: identifier expected but ']' found.
56+
trait TypeArgs { def f: C[Int, String, ] }
57+
^
58+
trailing-commas.scala:22: error: ']' expected but '}' found.
59+
trait TypeArgs { def f: C[Int, String, ] }
60+
^
61+
trailing-commas.scala:23: error: identifier expected but ']' found.
62+
trait TypeParamClause { type C[A, B, ] }
63+
^
64+
trailing-commas.scala:23: error: ']' expected but '}' found.
65+
trait TypeParamClause { type C[A, B, ] }
66+
^
67+
trailing-commas.scala:24: error: identifier expected but ']' found.
68+
trait FunTypeParamClause { def f[A, B, ] }
69+
^
70+
trailing-commas.scala:24: error: ']' expected but '}' found.
71+
trait FunTypeParamClause { def f[A, B, ] }
72+
^
73+
trailing-commas.scala:26: error: identifier expected but ')' found.
74+
trait SimpleType { def f: (Int, String, ) }
75+
^
76+
trailing-commas.scala:26: error: ')' expected but '}' found.
77+
trait SimpleType { def f: (Int, String, ) }
78+
^
79+
trailing-commas.scala:27: error: identifier expected but ')' found.
80+
trait FunctionArgTypes { def f: (Int, String, ) => Boolean }
81+
^
82+
trailing-commas.scala:27: error: ')' expected but '}' found.
83+
trait FunctionArgTypes { def f: (Int, String, ) => Boolean }
84+
^
85+
trailing-commas.scala:29: error: illegal start of simple pattern
86+
trait SimplePattern { val (foo, bar, ) = null: Any }
87+
^
88+
trailing-commas.scala:31: error: identifier expected but '}' found.
89+
trait ImportSelectors { import foo.{ Ev0, Ev1, } }
90+
^
91+
trailing-commas.scala:33: error: identifier expected but '}' found.
92+
trait Import { import foo.Ev0, foo.Ev1, }
93+
^
94+
trailing-commas.scala:35: error: illegal start of simple pattern
95+
trait ValDcl { val foo, bar, = 23 }
96+
^
97+
trailing-commas.scala:35: error: '=' expected but '}' found.
98+
trait ValDcl { val foo, bar, = 23 }
99+
^
100+
trailing-commas.scala:36: error: illegal start of simple pattern
101+
trait VarDcl { var foo, bar, = 23 }
102+
^
103+
trailing-commas.scala:36: error: '=' expected but '}' found.
104+
trait VarDcl { var foo, bar, = 23 }
105+
^
106+
trailing-commas.scala:37: error: illegal start of simple pattern
107+
trait VarDef { var foo, bar, = _ }
108+
^
109+
trailing-commas.scala:37: error: '=' expected but '}' found.
110+
trait VarDef { var foo, bar, = _ }
111+
^
112+
trailing-commas.scala:38: error: illegal start of simple pattern
113+
trait PatDef { val Foo(foo), Bar(bar), = bippy }
114+
^
115+
trailing-commas.scala:38: error: '=' expected but '}' found.
116+
trait PatDef { val Foo(foo), Bar(bar), = bippy }
117+
^
118+
trailing-commas.scala:45: error: illegal start of simple expression
119+
trait SimpleExpr2 { (23, ) }
120+
^
121+
trailing-commas.scala:45: error: ')' expected but '}' found.
122+
trait SimpleExpr2 { (23, ) }
123+
^
124+
trailing-commas.scala:48: error: identifier expected but ')' found.
125+
trait SimpleType2 { def f: (Int, ) }
126+
^
127+
trailing-commas.scala:48: error: ')' expected but '}' found.
128+
trait SimpleType2 { def f: (Int, ) }
129+
^
130+
43 errors found

test/files/neg/trailing-commas.scala

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package foo
2+
3+
// Note: Using traits to get distinct errors
4+
// (instead of sharing one single "')' expected but '}' found." at the end)
5+
6+
7+
8+
//// Multi-line only cases: make sure trailing commas are only supported when multi-line
9+
10+
trait ArgumentExprs1 { f(23, "bar", )(Ev0, Ev1) }
11+
trait ArgumentExprs2 { f(23, "bar")(Ev0, Ev1, ) }
12+
trait ArgumentExprs3 { new C(23, "bar", )(Ev0, Ev1) }
13+
trait ArgumentExprs4 { new C(23, "bar")(Ev0, Ev1, ) }
14+
15+
trait Params1 { def f(foo: Int, bar: String, )(implicit ev0: Ev0, ev1: Ev1, ) = 1 }
16+
trait Params2 { def f(foo: Int, bar: String, )(implicit ev0: Ev0, ev1: Ev1, ) = 1 }
17+
trait ClassParams1 { final class C(foo: Int, bar: String, )(implicit ev0: Ev0, ev1: Ev1) }
18+
trait ClassParams2 { final class C(foo: Int, bar: String)(implicit ev0: Ev0, ev1: Ev1, ) }
19+
20+
trait SimpleExpr { (23, "bar", ) }
21+
22+
trait TypeArgs { def f: C[Int, String, ] }
23+
trait TypeParamClause { type C[A, B, ] }
24+
trait FunTypeParamClause { def f[A, B, ] }
25+
26+
trait SimpleType { def f: (Int, String, ) }
27+
trait FunctionArgTypes { def f: (Int, String, ) => Boolean }
28+
29+
trait SimplePattern { val (foo, bar, ) = null: Any }
30+
31+
trait ImportSelectors { import foo.{ Ev0, Ev1, } }
32+
33+
trait Import { import foo.Ev0, foo.Ev1, }
34+
35+
trait ValDcl { val foo, bar, = 23 }
36+
trait VarDcl { var foo, bar, = 23 }
37+
trait VarDef { var foo, bar, = _ }
38+
trait PatDef { val Foo(foo), Bar(bar), = bippy }
39+
40+
41+
42+
//// The Tuple 1 cases
43+
44+
// the Tuple1 value case: make sure that the possible "(23, )" syntax for Tuple1 doesn't compile to "23"
45+
trait SimpleExpr2 { (23, ) }
46+
47+
// the Tuple1 type case: make sure that the possible "(Int, )" syntax for Tuple1[Int] doesn't compile to "Int"
48+
trait SimpleType2 { def f: (Int, ) }
49+
50+
51+
52+
//// Test utilities
53+
object `package` {
54+
sealed trait Ev0; implicit object Ev0 extends Ev0
55+
sealed trait Ev1; implicit object Ev1 extends Ev1
56+
}

test/files/pos/trailing-commas.scala

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
package foo
2+
3+
trait ArgumentExprs1 {
4+
def f(foo: Int, bar: String)(implicit ev0: Ev0, ev1: Ev1) = 1
5+
f(
6+
23,
7+
"bar",
8+
)(
9+
Ev0,
10+
Ev1,
11+
)
12+
13+
// test arg exprs in the presence of varargs
14+
def g(x: Int, y: Int*) = 1
15+
g(1,2,
16+
)
17+
g(1,List(2, 3): _*,
18+
)
19+
}
20+
21+
trait ArgumentExprs2 {
22+
class C(foo: Int, bar: String)(implicit ev0: Ev0, ev1: Ev1)
23+
new C(
24+
23,
25+
"bar",
26+
)(
27+
Ev0,
28+
Ev1,
29+
)
30+
}
31+
32+
trait Params {
33+
def f(
34+
foo: Int,
35+
bar: String,
36+
)(implicit
37+
ev0: Ev0,
38+
ev1: Ev1,
39+
)
40+
}
41+
42+
trait ClassParams {
43+
class C(
44+
foo: Int,
45+
bar: String,
46+
)(implicit
47+
ev0: Ev0,
48+
ev1: Ev1,
49+
)
50+
51+
// test class params in the precense of varargs
52+
case class D(i: Int*,
53+
)
54+
}
55+
56+
trait SimpleExpr1 {
57+
def f: (Int, String) = (
58+
23,
59+
"bar",
60+
)
61+
62+
// the Tuple1 value case, the trailing comma is ignored so the type is Int and the value 23
63+
def g: Int = (
64+
23,
65+
)
66+
}
67+
68+
trait TypeArgs {
69+
class C[A, B]
70+
def f: C[
71+
Int,
72+
String,
73+
]
74+
}
75+
76+
trait TypeParamClause {
77+
class C[
78+
A,
79+
B,
80+
]
81+
}
82+
83+
trait FunTypeParamClause {
84+
def f[
85+
A,
86+
B,
87+
]
88+
}
89+
90+
trait SimpleType {
91+
def f: (
92+
Int,
93+
String,
94+
)
95+
96+
// the Tuple1 type case, the trailing comma is ignored so the type is Int and the value 23
97+
def g: (
98+
Int,
99+
) = 23
100+
}
101+
102+
trait FunctionArgTypes {
103+
def f: (
104+
Int,
105+
String,
106+
) => Boolean
107+
}
108+
109+
trait SimplePattern {
110+
val (
111+
foo,
112+
bar,
113+
) = null: Any
114+
115+
// test '@' syntax in patterns
116+
Some(1) match {
117+
case Some(x @ 1,
118+
) => x
119+
}
120+
121+
// test ': _*' syntax in patterns
122+
List(1, 2, 3) match {
123+
case List(1, 2, _ @ _*,
124+
) => 1
125+
}
126+
127+
// test varargs in patterns
128+
val List(x, y, _*,
129+
) = 42 :: 17 :: Nil
130+
}
131+
132+
trait ImportSelectors {
133+
import foo.{
134+
Ev0,
135+
Ev1,
136+
}
137+
}
138+
139+
trait Bindings {
140+
def g(f: (Int, String) => Boolean)
141+
142+
g((
143+
foo,
144+
bar,
145+
) => true)
146+
}
147+
148+
// Import, ids, ValDcl, VarDcl, VarDef, PatDef use commas, but not inside paren, bracket or brace,
149+
// so they don't support an optional trailing comma
150+
151+
// test utilities
152+
object `package` {
153+
sealed trait Ev0; implicit object Ev0 extends Ev0
154+
sealed trait Ev1; implicit object Ev1 extends Ev1
155+
}

test/files/run/trailing-commas.check

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
scala> // test varargs in patterns
3+
4+
scala> val List(x, y, _*,
5+
) = 42 :: 17 :: Nil
6+
x: Int = 42
7+
y: Int = 17
8+
9+
scala> :quit

test/files/run/trailing-commas.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
object Test extends scala.tools.partest.ReplTest {
2+
def code = """
3+
// test varargs in patterns
4+
val List(x, y, _*,
5+
) = 42 :: 17 :: Nil
6+
"""
7+
}

0 commit comments

Comments
 (0)