Skip to content

Commit b95e71a

Browse files
authored
Merge pull request #3463 from CucumisSativus/trailing_commas
Fix #3408 Implement SIP-27: Trailing Commas
2 parents 31a2f52 + 21f529b commit b95e71a

File tree

6 files changed

+236
-4
lines changed

6 files changed

+236
-4
lines changed

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1961,10 +1961,10 @@ object Parsers {
19611961

19621962
/** ImportSelectors ::= `{' {ImportSelector `,'} (ImportSelector | `_') `}'
19631963
*/
1964-
def importSelectors(): List[Tree] =
1965-
if (in.token == RBRACE) Nil
1964+
def importSelectors(): List[Tree] = {
1965+
val sel = importSelector()
1966+
if (in.token == RBRACE) sel :: Nil
19661967
else {
1967-
val sel = importSelector()
19681968
sel :: {
19691969
if (!isWildcardArg(sel) && in.token == COMMA) {
19701970
in.nextToken()
@@ -1973,7 +1973,7 @@ object Parsers {
19731973
else Nil
19741974
}
19751975
}
1976-
1976+
}
19771977
/** ImportSelector ::= id [`=>' id | `=>' `_']
19781978
*/
19791979
def importSelector(): Tree = {

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,16 @@ object Scanners {
390390
val nextLastOffset = lastCharOffset
391391
lookahead()
392392
if (token != ELSE) reset(nextLastOffset)
393+
} else if (token == COMMA){
394+
val nextLastOffset = lastCharOffset
395+
lookahead()
396+
if (isAfterLineEnd() && (token == RPAREN || token == RBRACKET || token == RBRACE)) {
397+
/* skip the trailing comma */
398+
} else if (token == EOF) { // e.g. when the REPL is parsing "val List(x, y, _*,"
399+
/* skip the trailing comma */
400+
} else reset(nextLastOffset)
393401
}
402+
394403
}
395404

396405
/** Is current token first one after a newline? */
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package foo
2+
3+
// Multi-line only cases: make sure trailing commas are only supported when multi-line
4+
5+
trait ArgumentExprs1 { validMethod(23, "bar", )(Ev0, Ev1) } // error // error
6+
trait ArgumentExprs2 { validMethod(23, "bar")(Ev0, Ev1, ) } // error // error
7+
trait ArgumentExprs3 { new ValidClass(23, "bar", )(Ev0, Ev1) } // error // error
8+
trait ArgumentExprs4 { new ValidClass(23, "bar")(Ev0, Ev1, ) } // error // error
9+
10+
trait Params1 { def f(foo: Int, bar: String, )(implicit ev0: Ev0, ev1: Ev1, ) = 1 } // error // error // error
11+
12+
trait Params2 { def f(foo: Int, bar: String, )(implicit ev0: Ev0, ev1: Ev1, ) = 1 } // error // error // error
13+
14+
trait ClassParams1 { final class C(foo: Int, bar: String, )(implicit ev0: Ev0, ev1: Ev1) } // error
15+
trait ClassParams2 { final class C(foo: Int, bar: String)(implicit ev0: Ev0, ev1: Ev1, ) } // error
16+
17+
trait SimpleExpr { (23, "bar", ) } // error
18+
trait TypeArgs { def f: ValidGeneric[Int, String, ] } // error
19+
20+
trait TypeParamClause { type C[A, B, ] } // error
21+
trait FunTypeParamClause { def f[A, B, ] } // error
22+
23+
trait SimpleType { def f: (Int, String, ) } // error
24+
trait FunctionArgTypes { def f: (Int, String, ) => Boolean } // error
25+
26+
trait SimplePattern { val (foo, bar, ) = null: Any } // error
27+
28+
trait ImportSelectors { import foo.{ Ev0, Ev1, } } // error
29+
30+
trait Import { import foo.Ev0, foo.Ev1, } // error
31+
32+
trait ValDcl { val foo, bar, = 23 } // error
33+
34+
trait VarDef { var foo, bar, = _ } // error
35+
36+
trait PatDef { val Foo(foo), Bar(bar), = bippy } // error
37+
38+
39+
// The Tuple 1 cases
40+
41+
// the Tuple1 value case: make sure that the possible "(23, )" syntax for Tuple1 doesn't compile to "23"
42+
trait SimpleExpr2 { (23, ) } // error
43+
44+
// the Tuple1 type case: make sure that the possible "(Int, )" syntax for Tuple1[Int] doesn't compile to "Int"
45+
trait SimpleType2 { def f: (Int, ) } // error
46+
47+
// Test utilities
48+
object `package` {
49+
sealed trait Ev0; implicit object Ev0 extends Ev0
50+
sealed trait Ev1; implicit object Ev1 extends Ev1
51+
52+
class ValidClass(foo: Int, bar: String)(implicit ev0: Ev0, ev1: Ev1)
53+
class ValidGeneric[A, B]
54+
def validMethod(foo: Int, bar: String)(implicit ev0: Ev0, ev1: Ev1): Int = 1
55+
56+
case class Foo(foo: Any)
57+
case class Bar(foo: Any)
58+
}
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+
): Unit
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+
]: Unit
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, x : _*,
124+
) => 1
125+
}
126+
127+
// test varargs in patterns
128+
val List(x, y, z: _*,
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): Unit
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+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
42
2+
17
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
object Test {
2+
val List(x, y, z: _ *,
3+
) = 42 :: 17 :: Nil
4+
def main(args: Array[String]): Unit = {
5+
Console.println(x)
6+
Console.println(y)
7+
}
8+
}

0 commit comments

Comments
 (0)