From 09b5ded130587e5336a0686e1b24a6536580c7e3 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Tue, 30 Mar 2021 14:15:08 +0200 Subject: [PATCH] Support `case` in pattern bindings under -Xsource:3 Just like in Scala 3.0, adding this keyword doesn't change anything, but it will be required in future versions of Scala 3 for non-exhaustive patterns in a for comprehension. We would like to start issuing warnings by default in Scala 3 for code which does not use `case` in those situations, but to not hamper cross-compilation we need Scala 2 to also support that keyword. For details, see: https://dotty.epfl.ch/docs/reference/changed-features/pattern-bindings.html --- .../scala/tools/nsc/ast/parser/Parsers.scala | 8 ++++++- .../scala/tools/nsc/ast/parser/Scanners.scala | 10 ++++++++ .../neg/for-comprehension-case-future.check | 7 ++++++ .../neg/for-comprehension-case-future.scala | 24 +++++++++++++++++++ test/files/neg/for-comprehension-case.check | 13 ++++++++++ test/files/neg/for-comprehension-case.scala | 14 +++++++++++ 6 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 test/files/neg/for-comprehension-case-future.check create mode 100644 test/files/neg/for-comprehension-case-future.scala create mode 100644 test/files/neg/for-comprehension-case.check create mode 100644 test/files/neg/for-comprehension-case.scala diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 055ed8c8fb39..7866f6d40b5d 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -1926,6 +1926,12 @@ self => */ def generator(eqOK: Boolean, allowNestedIf: Boolean = true): List[Tree] = { val start = in.offset + val hasCase = in.token == CASE + if (hasCase) { + if (!currentRun.isScala3) syntaxError(in.offset, s"`case` keyword in for comprehension requires the -Xsource:3 flag.") + in.skipCASE() + } + val hasVal = in.token == VAL if (hasVal) in.nextToken() @@ -1944,7 +1950,7 @@ self => else syntaxError(in.offset, msg("unsupported", "just remove `val`")) } - if (hasEq && eqOK) in.nextToken() + if (hasEq && eqOK && !hasCase) in.nextToken() else accept(LARROW) val rhs = expr() diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index a4f8efc43eea..0f41d3903c16 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -393,6 +393,16 @@ trait Scanners extends ScannersCommon { case _ => } + /** Advance beyond a case token without marking the CASE in sepRegions. + * This method should be called to skip beyond CASE tokens that are + * not part of matches, i.e. no ARROW is expected after them. + */ + def skipCASE(): Unit = { + assert(token == CASE, s"Internal error: skipCASE() called on non-case token $token") + nextToken() + sepRegions = sepRegions.tail + } + /** Produce next token, filling TokenData fields of Scanner. */ def nextToken(): Unit = { diff --git a/test/files/neg/for-comprehension-case-future.check b/test/files/neg/for-comprehension-case-future.check new file mode 100644 index 000000000000..9ce9a9456882 --- /dev/null +++ b/test/files/neg/for-comprehension-case-future.check @@ -0,0 +1,7 @@ +for-comprehension-case-future.scala:22: error: '<-' expected but '=' found. + case y = x + 1 + ^ +for-comprehension-case-future.scala:23: error: illegal start of simple expression + } yield x + y + ^ +2 errors diff --git a/test/files/neg/for-comprehension-case-future.scala b/test/files/neg/for-comprehension-case-future.scala new file mode 100644 index 000000000000..05602e537759 --- /dev/null +++ b/test/files/neg/for-comprehension-case-future.scala @@ -0,0 +1,24 @@ +// scalac: -Xsource:3 +// +class A { + // ok + val a = + for { + case Some(x) <- List(Some(1), None) + y = x + 1 + } yield x + y + + // ok + val b = + for { + Some(x) <- List(Some(1), None) + Some(y) <- List(None, Some(2)) + } yield x+y + + // fail + val c = + for { + case Some(x) <- List(Some(1), None) + case y = x + 1 + } yield x + y +} diff --git a/test/files/neg/for-comprehension-case.check b/test/files/neg/for-comprehension-case.check new file mode 100644 index 000000000000..2e86e5d367b0 --- /dev/null +++ b/test/files/neg/for-comprehension-case.check @@ -0,0 +1,13 @@ +for-comprehension-case.scala:5: error: `case` keyword in for comprehension requires the -Xsource:3 flag. + case Some(x) <- List(Some(1), None) + ^ +for-comprehension-case.scala:12: error: `case` keyword in for comprehension requires the -Xsource:3 flag. + case y = x + 1 + ^ +for-comprehension-case.scala:12: error: '<-' expected but '=' found. + case y = x + 1 + ^ +for-comprehension-case.scala:13: error: illegal start of simple expression + } yield x+y + ^ +4 errors diff --git a/test/files/neg/for-comprehension-case.scala b/test/files/neg/for-comprehension-case.scala new file mode 100644 index 000000000000..55e8d44a40e3 --- /dev/null +++ b/test/files/neg/for-comprehension-case.scala @@ -0,0 +1,14 @@ +class A { + // fail + val a = + for { + case Some(x) <- List(Some(1), None) + } yield x + + // fail + val b = + for { + Some(x) <- List(Some(1), None) + case y = x + 1 + } yield x+y +}