Skip to content

Commit

Permalink
SI-4986 SIP-27 Trailing Comma (multi-line only) support
Browse files Browse the repository at this point in the history
  • Loading branch information
dwijnand committed Dec 11, 2016
1 parent 3de1c0c commit 7966edd
Show file tree
Hide file tree
Showing 4 changed files with 322 additions and 0 deletions.
8 changes: 8 additions & 0 deletions src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,14 @@ trait Scanners extends ScannersCommon {
next copyFrom this
this copyFrom prev
}
} else if (token == COMMA) {
// SIP-27 Trailing Comma (multi-line only) support
// If a comma is followed by a new line & then a closing paren, bracket or brace
// then it is a trailing comma and is ignored
val saved = new ScannerData {} copyFrom this
fetchToken()
if (token != RPAREN && token != RBRACKET && token != RBRACE || !afterLineEnd())
this copyFrom saved
}

// print("["+this+"]")
Expand Down
130 changes: 130 additions & 0 deletions test/files/neg/trailing-commas.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
trailing-commas.scala:10: error: illegal start of simple expression
trait ArgumentExprs1 { f(23, "bar", )(Ev0, Ev1) }
^
trailing-commas.scala:10: error: ')' expected but '}' found.
trait ArgumentExprs1 { f(23, "bar", )(Ev0, Ev1) }
^
trailing-commas.scala:11: error: illegal start of simple expression
trait ArgumentExprs2 { f(23, "bar")(Ev0, Ev1, ) }
^
trailing-commas.scala:11: error: ')' expected but '}' found.
trait ArgumentExprs2 { f(23, "bar")(Ev0, Ev1, ) }
^
trailing-commas.scala:12: error: illegal start of simple expression
trait ArgumentExprs3 { new C(23, "bar", )(Ev0, Ev1) }
^
trailing-commas.scala:12: error: ')' expected but '}' found.
trait ArgumentExprs3 { new C(23, "bar", )(Ev0, Ev1) }
^
trailing-commas.scala:13: error: illegal start of simple expression
trait ArgumentExprs4 { new C(23, "bar")(Ev0, Ev1, ) }
^
trailing-commas.scala:13: error: ')' expected but '}' found.
trait ArgumentExprs4 { new C(23, "bar")(Ev0, Ev1, ) }
^
trailing-commas.scala:15: error: identifier expected but ')' found.
trait Params1 { def f(foo: Int, bar: String, )(implicit ev0: Ev0, ev1: Ev1, ) = 1 }
^
trailing-commas.scala:15: error: ':' expected but '}' found.
trait Params1 { def f(foo: Int, bar: String, )(implicit ev0: Ev0, ev1: Ev1, ) = 1 }
^
trailing-commas.scala:16: error: identifier expected but ')' found.
trait Params2 { def f(foo: Int, bar: String, )(implicit ev0: Ev0, ev1: Ev1, ) = 1 }
^
trailing-commas.scala:16: error: ':' expected but '}' found.
trait Params2 { def f(foo: Int, bar: String, )(implicit ev0: Ev0, ev1: Ev1, ) = 1 }
^
trailing-commas.scala:17: error: identifier expected but ')' found.
trait ClassParams1 { final class C(foo: Int, bar: String, )(implicit ev0: Ev0, ev1: Ev1) }
^
trailing-commas.scala:17: error: ':' expected but '}' found.
trait ClassParams1 { final class C(foo: Int, bar: String, )(implicit ev0: Ev0, ev1: Ev1) }
^
trailing-commas.scala:18: error: identifier expected but ')' found.
trait ClassParams2 { final class C(foo: Int, bar: String)(implicit ev0: Ev0, ev1: Ev1, ) }
^
trailing-commas.scala:18: error: ':' expected but '}' found.
trait ClassParams2 { final class C(foo: Int, bar: String)(implicit ev0: Ev0, ev1: Ev1, ) }
^
trailing-commas.scala:20: error: illegal start of simple expression
trait SimpleExpr { (23, "bar", ) }
^
trailing-commas.scala:20: error: ')' expected but '}' found.
trait SimpleExpr { (23, "bar", ) }
^
trailing-commas.scala:22: error: identifier expected but ']' found.
trait TypeArgs { def f: C[Int, String, ] }
^
trailing-commas.scala:22: error: ']' expected but '}' found.
trait TypeArgs { def f: C[Int, String, ] }
^
trailing-commas.scala:23: error: identifier expected but ']' found.
trait TypeParamClause { type C[A, B, ] }
^
trailing-commas.scala:23: error: ']' expected but '}' found.
trait TypeParamClause { type C[A, B, ] }
^
trailing-commas.scala:24: error: identifier expected but ']' found.
trait FunTypeParamClause { def f[A, B, ] }
^
trailing-commas.scala:24: error: ']' expected but '}' found.
trait FunTypeParamClause { def f[A, B, ] }
^
trailing-commas.scala:26: error: identifier expected but ')' found.
trait SimpleType { def f: (Int, String, ) }
^
trailing-commas.scala:26: error: ')' expected but '}' found.
trait SimpleType { def f: (Int, String, ) }
^
trailing-commas.scala:27: error: identifier expected but ')' found.
trait FunctionArgTypes { def f: (Int, String, ) => Boolean }
^
trailing-commas.scala:27: error: ')' expected but '}' found.
trait FunctionArgTypes { def f: (Int, String, ) => Boolean }
^
trailing-commas.scala:29: error: illegal start of simple pattern
trait SimplePattern { val (foo, bar, ) = null: Any }
^
trailing-commas.scala:31: error: identifier expected but '}' found.
trait ImportSelectors { import foo.{ Ev0, Ev1, } }
^
trailing-commas.scala:33: error: identifier expected but '}' found.
trait Import { import foo.Ev0, foo.Ev1, }
^
trailing-commas.scala:35: error: illegal start of simple pattern
trait ValDcl { val foo, bar, = 23 }
^
trailing-commas.scala:35: error: '=' expected but '}' found.
trait ValDcl { val foo, bar, = 23 }
^
trailing-commas.scala:36: error: illegal start of simple pattern
trait VarDcl { var foo, bar, = 23 }
^
trailing-commas.scala:36: error: '=' expected but '}' found.
trait VarDcl { var foo, bar, = 23 }
^
trailing-commas.scala:37: error: illegal start of simple pattern
trait VarDef { var foo, bar, = _ }
^
trailing-commas.scala:37: error: '=' expected but '}' found.
trait VarDef { var foo, bar, = _ }
^
trailing-commas.scala:38: error: illegal start of simple pattern
trait PatDef { val Foo(foo), Bar(bar), = bippy }
^
trailing-commas.scala:38: error: '=' expected but '}' found.
trait PatDef { val Foo(foo), Bar(bar), = bippy }
^
trailing-commas.scala:45: error: illegal start of simple expression
trait SimpleExpr2 { (23, ) }
^
trailing-commas.scala:45: error: ')' expected but '}' found.
trait SimpleExpr2 { (23, ) }
^
trailing-commas.scala:48: error: identifier expected but ')' found.
trait SimpleType2 { def f: (Int, ) }
^
trailing-commas.scala:48: error: ')' expected but '}' found.
trait SimpleType2 { def f: (Int, ) }
^
43 errors found
56 changes: 56 additions & 0 deletions test/files/neg/trailing-commas.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package foo

// Note: Using traits to get distinct errors
// (instead of sharing one single "')' expected but '}' found." at the end)



//// Multi-line only cases: make sure trailing commas are only supported when multi-line

trait ArgumentExprs1 { f(23, "bar", )(Ev0, Ev1) }
trait ArgumentExprs2 { f(23, "bar")(Ev0, Ev1, ) }
trait ArgumentExprs3 { new C(23, "bar", )(Ev0, Ev1) }
trait ArgumentExprs4 { new C(23, "bar")(Ev0, Ev1, ) }

trait Params1 { def f(foo: Int, bar: String, )(implicit ev0: Ev0, ev1: Ev1, ) = 1 }
trait Params2 { def f(foo: Int, bar: String, )(implicit ev0: Ev0, ev1: Ev1, ) = 1 }
trait ClassParams1 { final class C(foo: Int, bar: String, )(implicit ev0: Ev0, ev1: Ev1) }
trait ClassParams2 { final class C(foo: Int, bar: String)(implicit ev0: Ev0, ev1: Ev1, ) }

trait SimpleExpr { (23, "bar", ) }

trait TypeArgs { def f: C[Int, String, ] }
trait TypeParamClause { type C[A, B, ] }
trait FunTypeParamClause { def f[A, B, ] }

trait SimpleType { def f: (Int, String, ) }
trait FunctionArgTypes { def f: (Int, String, ) => Boolean }

trait SimplePattern { val (foo, bar, ) = null: Any }

trait ImportSelectors { import foo.{ Ev0, Ev1, } }

trait Import { import foo.Ev0, foo.Ev1, }

trait ValDcl { val foo, bar, = 23 }
trait VarDcl { var foo, bar, = 23 }
trait VarDef { var foo, bar, = _ }
trait PatDef { val Foo(foo), Bar(bar), = bippy }



//// The Tuple 1 cases

// the Tuple1 value case: make sure that the possible "(23, )" syntax for Tuple1 doesn't compile to "23"
trait SimpleExpr2 { (23, ) }

// the Tuple1 type case: make sure that the possible "(Int, )" syntax for Tuple1[Int] doesn't compile to "Int"
trait SimpleType2 { def f: (Int, ) }



//// Test utilities
object `package` {
sealed trait Ev0; implicit object Ev0 extends Ev0
sealed trait Ev1; implicit object Ev1 extends Ev1
}
128 changes: 128 additions & 0 deletions test/files/pos/trailing-commas.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package foo

trait ArgumentExprs1 {
def f(foo: Int, bar: String)(implicit ev0: Ev0, ev1: Ev1) = 1
f(
23,
"bar",
)(
Ev0,
Ev1,
)
}

trait ArgumentExprs2 {
class C(foo: Int, bar: String)(implicit ev0: Ev0, ev1: Ev1)
new C(
23,
"bar",
)(
Ev0,
Ev1,
)
}

trait Params {
def f(
foo: Int,
bar: String,
)(implicit
ev0: Ev0,
ev1: Ev1,
)
}

trait ClassParams {
class C(
foo: Int,
bar: String,
)(implicit
ev0: Ev0,
ev1: Ev1,
)
}

trait SimpleExpr1 {
def f: (Int, String) = (
23,
"bar",
)

// the Tuple1 value case, the trailing comma is ignored so the type is Int and the value 23
def g: Int = (
23,
)
}

trait TypeArgs {
class C[A, B]
def f: C[
Int,
String,
]
}

trait TypeParamClause {
class C[
A,
B,
]
}

trait FunTypeParamClause {
def f[
A,
B,
]
}

trait SimpleType {
def f: (
Int,
String,
)

// the Tuple1 type case, the trailing comma is ignored so the type is Int and the value 23
def g: (
Int,
) = 23
}

trait FunctionArgTypes {
def f: (
Int,
String,
) => Boolean
}

trait SimplePattern {
val (
foo,
bar,
) = null: Any
}

trait ImportSelectors {
import foo.{
Ev0,
Ev1,
}
}

trait Bindings {
def g(f: (Int, String) => Boolean)

g((
foo,
bar,
) => true)
}

// Import, ids, ValDcl, VarDcl, VarDef, PatDef use commas, but not inside paren, bracket or brace,
// so they don't support an optional trailing comma

// test utilities
object `package` {
sealed trait Ev0; implicit object Ev0 extends Ev0
sealed trait Ev1; implicit object Ev1 extends Ev1
}

0 comments on commit 7966edd

Please sign in to comment.