From 0cecad3598b63e83bf41d3393e37013d6640ecf8 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Mon, 9 Aug 2021 14:17:42 +0200 Subject: [PATCH] Allow `case` in pattern bindings even without -Xsource:3 In #9558 (which shipped with 2.13.6) we added support for `case` bindings under -Xsource:3. Since this parser change does not break any existing code and since IntelliJ and scalameta/metals now understand this syntax in Scala 2 code, it should be safe to enable it by default to further ease cross-compilation between Scala 2 and 3. --- spec/06-expressions.md | 14 +++++++---- spec/13-syntax-summary.md | 2 +- .../scala/tools/nsc/ast/parser/Parsers.scala | 6 ++--- .../neg/for-comprehension-case-future.check | 7 ------ .../neg/for-comprehension-case-future.scala | 24 ------------------- test/files/neg/for-comprehension-case.check | 14 ++++------- test/files/neg/for-comprehension-case.scala | 16 +++++++++---- 7 files changed, 29 insertions(+), 54 deletions(-) delete mode 100644 test/files/neg/for-comprehension-case-future.check delete mode 100644 test/files/neg/for-comprehension-case-future.scala diff --git a/spec/06-expressions.md b/spec/06-expressions.md index 0387ce17e7f8..49687a2bf97e 100644 --- a/spec/06-expressions.md +++ b/spec/06-expressions.md @@ -919,7 +919,7 @@ A semicolon preceding the `while` symbol of a do loop expression is ignored. Expr1 ::= ‘for’ (‘(’ Enumerators ‘)’ | ‘{’ Enumerators ‘}’) {nl} [‘yield’] Expr Enumerators ::= Generator {semi Generator} -Generator ::= Pattern1 ‘<-’ Expr {[semi] Guard | semi Pattern1 ‘=’ Expr} +Generator ::= [‘case’] Pattern1 ‘<-’ Expr {[semi] Guard | semi Pattern1 ‘=’ Expr} Guard ::= ‘if’ PostfixExpr ``` @@ -929,9 +929,15 @@ A _for comprehension_ `for (´\mathit{enums}\,´) yield ´e´` evaluates expression ´e´ for each binding generated by the enumerators ´\mathit{enums}´ and collects the results. An enumerator sequence always starts with a generator; this can be followed by further generators, value -definitions, or guards. A _generator_ `´p´ <- ´e´` -produces bindings from an expression ´e´ which is matched in some way -against pattern ´p´. A _value definition_ `´p´ = ´e´` +definitions, or guards. + +A _generator_ `´p´ <- ´e´` produces bindings from an expression ´e´ which is +matched in some way against pattern ´p´. Optionally, `case` can appear in front +of a generator pattern, this has no meaning in Scala 2 but will be [required in +Scala 3 if `p` is not +irrefutable](https://docs.scala-lang.org/scala3/reference/changed-features/pattern-bindings.html). + +A _value definition_ `´p´ = ´e´` binds the value name ´p´ (or several names in a pattern ´p´) to the result of evaluating the expression ´e´. A _guard_ `if ´e´` contains a boolean expression which restricts diff --git a/spec/13-syntax-summary.md b/spec/13-syntax-summary.md index 1f54d346a3b2..cda92a3b3e5f 100644 --- a/spec/13-syntax-summary.md +++ b/spec/13-syntax-summary.md @@ -184,7 +184,7 @@ grammar: | (Bindings | ([‘implicit’] id | ‘_’) ‘:’ CompoundType) ‘=>’ Block Enumerators ::= Generator {semi Generator} - Generator ::= Pattern1 ‘<-’ Expr {[semi] Guard | semi Pattern1 ‘=’ Expr} + Generator ::= [‘case’] Pattern1 ‘<-’ Expr {[semi] Guard | semi Pattern1 ‘=’ Expr} CaseClauses ::= CaseClause { CaseClause } CaseClause ::= ‘case’ Pattern [Guard] ‘=>’ Block diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index adc577f54c86..5f7456044802 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -1993,16 +1993,14 @@ self => } /** {{{ - * Generator ::= Pattern1 (`<-` | `=`) Expr [Guard] + * Generator ::= [`case`] Pattern1 (`<-` | `=`) Expr [Guard] * }}} */ 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.") + if (hasCase) in.skipCASE() - } val hasVal = in.token == VAL if (hasVal) diff --git a/test/files/neg/for-comprehension-case-future.check b/test/files/neg/for-comprehension-case-future.check deleted file mode 100644 index 9ce9a9456882..000000000000 --- a/test/files/neg/for-comprehension-case-future.check +++ /dev/null @@ -1,7 +0,0 @@ -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 deleted file mode 100644 index 05602e537759..000000000000 --- a/test/files/neg/for-comprehension-case-future.scala +++ /dev/null @@ -1,24 +0,0 @@ -// 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 index 2e86e5d367b0..549e4943e344 100644 --- a/test/files/neg/for-comprehension-case.check +++ b/test/files/neg/for-comprehension-case.check @@ -1,13 +1,7 @@ -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. +for-comprehension-case.scala:20: error: '<-' expected but '=' found. case y = x + 1 ^ -for-comprehension-case.scala:13: error: illegal start of simple expression - } yield x+y +for-comprehension-case.scala:21: error: illegal start of simple expression + } yield x + y ^ -4 errors +2 errors diff --git a/test/files/neg/for-comprehension-case.scala b/test/files/neg/for-comprehension-case.scala index 55e8d44a40e3..d6b14eb91a90 100644 --- a/test/files/neg/for-comprehension-case.scala +++ b/test/files/neg/for-comprehension-case.scala @@ -1,14 +1,22 @@ class A { - // fail + // ok val a = for { case Some(x) <- List(Some(1), None) - } yield x + y = x + 1 + } yield x + y - // fail + // ok val b = for { Some(x) <- List(Some(1), None) - case y = x + 1 + 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 }