Skip to content

Commit

Permalink
Fix syntax and parsing of vararg patterns (#18055)
Browse files Browse the repository at this point in the history
Syntax: Remove outdated `*` after InfixPattern
Parsing: Only allow vararg `*` in ArgumentPatterns

Fixes #17443
  • Loading branch information
prolativ committed Jun 27, 2023
2 parents d43c2cf + d7ec913 commit 8205a5b
Show file tree
Hide file tree
Showing 9 changed files with 40 additions and 30 deletions.
36 changes: 20 additions & 16 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1666,7 +1666,7 @@ object Parsers {
if in.token == LPAREN then funParamClause() :: funParamClauses() else Nil

/** InfixType ::= RefinedType {id [nl] RefinedType}
* | RefinedType `^`
* | RefinedType `^` // under capture checking
*/
def infixType(): Tree = infixTypeRest(refinedType())

Expand Down Expand Up @@ -2881,13 +2881,13 @@ object Parsers {
if (isIdent(nme.raw.BAR)) { in.nextToken(); pattern1(location) :: patternAlts(location) }
else Nil

/** Pattern1 ::= PatVar Ascription
* | [‘-’] integerLiteral Ascription
* | [‘-’] floatingPointLiteral Ascription
/** Pattern1 ::= PatVar `:` RefinedType
* | [‘-’] integerLiteral `:` RefinedType
* | [‘-’] floatingPointLiteral `:` RefinedType
* | Pattern2
*/
def pattern1(location: Location = Location.InPattern): Tree =
val p = pattern2()
val p = pattern2(location)
if in.isColon then
val isVariableOrNumber = isVarPattern(p) || p.isInstanceOf[Number]
if !isVariableOrNumber then
Expand All @@ -2905,26 +2905,29 @@ object Parsers {
else p

/** Pattern3 ::= InfixPattern
* | PatVar ‘*’
*/
def pattern3(): Tree =
def pattern3(location: Location): Tree =
val p = infixPattern()
if followingIsVararg() then
val start = in.skipToken()
p match
case p @ Ident(name) if name.isVarPattern =>
Typed(p, atSpan(start) { Ident(tpnme.WILDCARD_STAR) })
case _ =>
syntaxError(em"`*` must follow pattern variable", start)
p
if location.inArgs then
p match
case p @ Ident(name) if name.isVarPattern =>
Typed(p, atSpan(start) { Ident(tpnme.WILDCARD_STAR) })
case _ =>
syntaxError(em"`*` must follow pattern variable", start)
p
else
syntaxError(em"bad use of `*` - sequence pattern not allowed here", start)
p
else p

/** Pattern2 ::= [id `@'] Pattern3
*/
val pattern2: () => Tree = () => pattern3() match
val pattern2: Location => Tree = location => pattern3(location) match
case p @ Ident(name) if in.token == AT =>
val offset = in.skipToken()
pattern3() match {
pattern3(location) match {
case pt @ Bind(nme.WILDCARD, pt1: Typed) if pt.mods.is(Given) =>
atSpan(startOffset(p), 0) { Bind(name, pt1).withMods(pt.mods) }
case Typed(Ident(nme.WILDCARD), pt @ Ident(tpnme.WILDCARD_STAR)) =>
Expand Down Expand Up @@ -2954,6 +2957,7 @@ object Parsers {
* | XmlPattern
* | `(' [Patterns] `)'
* | SimplePattern1 [TypeArgs] [ArgumentPatterns]
* | ‘given’ RefinedType
* SimplePattern1 ::= SimpleRef
* | SimplePattern1 `.' id
* PatVar ::= id
Expand Down Expand Up @@ -3597,7 +3601,7 @@ object Parsers {
* VarDcl ::= id {`,' id} `:' Type
*/
def patDefOrDcl(start: Offset, mods: Modifiers): Tree = atSpan(start, nameStart) {
val first = pattern2()
val first = pattern2(Location.InPattern)
var lhs = first match {
case id: Ident if in.token == COMMA =>
in.nextToken()
Expand Down
2 changes: 1 addition & 1 deletion docs/_docs/internals/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ Pattern1 ::= PatVar ‘:’ RefinedType
| [‘-’] integerLiteral ‘:’ RefinedType Typed(pat, tpe)
| [‘-’] floatingPointLiteral ‘:’ RefinedType Typed(pat, tpe)
| Pattern2
Pattern2 ::= [id ‘@’] InfixPattern [‘*’] Bind(name, pat)
Pattern2 ::= [id ‘@’] InfixPattern Bind(name, pat)
InfixPattern ::= SimplePattern { id [nl] SimplePattern } InfixOp(pat, op, pat)
SimplePattern ::= PatVar Ident(wildcard)
| Literal Bind(name, Ident(wildcard))
Expand Down
2 changes: 1 addition & 1 deletion docs/_docs/reference/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ Pattern1 ::= PatVar ‘:’ RefinedType
| [‘-’] integerLiteral ‘:’ RefinedType
| [‘-’] floatingPointLiteral ‘:’ RefinedType
| Pattern2
Pattern2 ::= [id ‘@’] InfixPattern [‘*’]
Pattern2 ::= [id ‘@’] InfixPattern
InfixPattern ::= SimplePattern { id [nl] SimplePattern }
SimplePattern ::= PatVar
| Literal
Expand Down
2 changes: 2 additions & 0 deletions tests/neg/i17443.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def run() =
val x = List(1) match { case (xs*) => xs } // error
4 changes: 4 additions & 0 deletions tests/neg/i8715.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- Error: tests/neg/i8715.scala:2:46 -----------------------------------------------------------------------------------
2 |def Test = List(42) match { case List(xs @ (ys*)) => xs } // error
| ^
| bad use of `*` - sequence pattern not allowed here
2 changes: 2 additions & 0 deletions tests/neg/i8715.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@main
def Test = List(42) match { case List(xs @ (ys*)) => xs } // error
16 changes: 8 additions & 8 deletions tests/neg/t5702-neg-bad-and-wild.check
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
| pattern expected
|
| longer explanation available when compiling with `-explain`
-- Error: tests/neg/t5702-neg-bad-and-wild.scala:16:16 -----------------------------------------------------------------
16 | case (1, x*) => // error: bad use of *
| ^
| bad use of `*` - sequence pattern not allowed here
-- [E031] Syntax Error: tests/neg/t5702-neg-bad-and-wild.scala:17:18 ---------------------------------------------------
17 | case (1, x: _*) => // error: bad use of _* (sequence pattern not allowed)
| ^
Expand All @@ -32,6 +36,10 @@
| pattern expected
|
| longer explanation available when compiling with `-explain`
-- Error: tests/neg/t5702-neg-bad-and-wild.scala:25:14 -----------------------------------------------------------------
25 | val (b, _ * ) = (5,6) // error: bad use of `*`
| ^
| bad use of `*` - sequence pattern not allowed here
-- [E161] Naming Error: tests/neg/t5702-neg-bad-and-wild.scala:24:10 ---------------------------------------------------
24 | val K(x) = k // error: x is already defined as value x
| ^^^^^^^^^^^^
Expand Down Expand Up @@ -71,11 +79,3 @@
| If the narrowing is intentional, this can be communicated by adding `: @unchecked` after the expression,
| which may result in a MatchError at runtime.
| This patch can be rewritten automatically under -rewrite -source 3.2-migration.
-- Warning: tests/neg/t5702-neg-bad-and-wild.scala:25:20 ---------------------------------------------------------------
25 | val (b, _ * ) = (5,6) // ok
| ^^^^^
| pattern's type Int* does not match the right hand side expression's type Int
|
| If the narrowing is intentional, this can be communicated by adding `: @unchecked` after the expression,
| which may result in a MatchError at runtime.
| This patch can be rewritten automatically under -rewrite -source 3.2-migration.
4 changes: 2 additions & 2 deletions tests/neg/t5702-neg-bad-and-wild.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ object Test {
case List(1, _*3:) => // error // error
case List(1, x*) => // ok
case List(x*, 1) => // error: pattern expected
case (1, x*) => //ok
case (1, x*) => // error: bad use of *
case (1, x: _*) => // error: bad use of _* (sequence pattern not allowed)
}

Expand All @@ -22,7 +22,7 @@ object Test {
val K(x @ _*) = k
val K(ns @ _*, xx) = k // error: pattern expected // error
val K(x) = k // error: x is already defined as value x
val (b, _ * ) = (5,6) // ok
val (b, _ * ) = (5,6) // error: bad use of `*`
// no longer complains
//bad-and-wild.scala:15: error: ')' expected but '}' found.
}
Expand Down
2 changes: 0 additions & 2 deletions tests/pos/i8715.scala

This file was deleted.

0 comments on commit 8205a5b

Please sign in to comment.