Skip to content

Commit c560211

Browse files
authored
Merge pull request #8753 from dotty-staging/fix-8715-v2
Fix #8715: enforce syntax for _* (alternate version)
2 parents b3f2aee + 7a09b82 commit c560211

File tree

8 files changed

+89
-73
lines changed

8 files changed

+89
-73
lines changed

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

Lines changed: 47 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,13 @@ object Parsers {
4848
def nonePositive: Boolean = parCounts forall (_ <= 0)
4949
}
5050

51-
@sharable object Location extends Enumeration {
52-
val InParens, InBlock, InPattern, ElseWhere: Value = Value
53-
}
51+
enum Location(val inParens: Boolean, val inPattern: Boolean, val inArgs: Boolean):
52+
case InParens extends Location(true, false, false)
53+
case InArgs extends Location(true, false, true)
54+
case InPattern extends Location(false, true, false)
55+
case InPatternArgs extends Location(false, true, true) // InParens not true, since it might be an alternative
56+
case InBlock extends Location(false, false, false)
57+
case ElseWhere extends Location(false, false, false)
5458

5559
@sharable object ParamOwner extends Enumeration {
5660
val Class, Type, TypeParam, Def: Value = Value
@@ -1754,9 +1758,9 @@ object Parsers {
17541758
else TypeTree().withSpan(Span(in.lastOffset))
17551759
}
17561760

1757-
def typeDependingOn(location: Location.Value): Tree =
1758-
if (location == Location.InParens) typ()
1759-
else if (location == Location.InPattern) refinedType()
1761+
def typeDependingOn(location: Location): Tree =
1762+
if location.inParens then typ()
1763+
else if location.inPattern then refinedType()
17601764
else infixType()
17611765

17621766
/* ----------- EXPRESSIONS ------------------------------------------------ */
@@ -1843,7 +1847,7 @@ object Parsers {
18431847

18441848
def subExpr() = subPart(expr)
18451849

1846-
def expr(location: Location.Value): Tree = {
1850+
def expr(location: Location): Tree = {
18471851
val start = in.offset
18481852
def isSpecialClosureStart =
18491853
val lookahead = in.LookaheadScanner()
@@ -1876,7 +1880,7 @@ object Parsers {
18761880
}
18771881
}
18781882

1879-
def expr1(location: Location.Value = Location.ElseWhere): Tree = in.token match
1883+
def expr1(location: Location = Location.ElseWhere): Tree = in.token match
18801884
case IF =>
18811885
in.endMarkerScope(IF) { ifExpr(in.offset, If) }
18821886
case WHILE =>
@@ -1989,11 +1993,14 @@ object Parsers {
19891993
else expr1Rest(postfixExpr(), location)
19901994
end expr1
19911995

1992-
def expr1Rest(t: Tree, location: Location.Value): Tree = in.token match
1996+
def expr1Rest(t: Tree, location: Location): Tree = in.token match
19931997
case EQUALS =>
19941998
t match
19951999
case Ident(_) | Select(_, _) | Apply(_, _) =>
1996-
atSpan(startOffset(t), in.skipToken()) { Assign(t, subExpr()) }
2000+
atSpan(startOffset(t), in.skipToken()) {
2001+
val loc = if location.inArgs then location else Location.ElseWhere
2002+
Assign(t, subPart(() => expr(loc)))
2003+
}
19972004
case _ =>
19982005
t
19992006
case COLON =>
@@ -2003,24 +2010,29 @@ object Parsers {
20032010
t
20042011
end expr1Rest
20052012

2006-
def ascription(t: Tree, location: Location.Value): Tree = atSpan(startOffset(t)) {
2013+
def ascription(t: Tree, location: Location): Tree = atSpan(startOffset(t)) {
20072014
in.token match {
20082015
case USCORE =>
20092016
val uscoreStart = in.skipToken()
2010-
if (isIdent(nme.raw.STAR)) {
2017+
if isIdent(nme.raw.STAR) then
20112018
in.nextToken()
2012-
if (in.token != RPAREN) syntaxError(SeqWildcardPatternPos(), uscoreStart)
2019+
if !(location.inArgs && in.token == RPAREN) then
2020+
if opStack.nonEmpty
2021+
ctx.errorOrMigrationWarning(
2022+
em"""`_*` can be used only for last argument of method application.
2023+
|It is no longer allowed in operands of infix operations.""",
2024+
in.sourcePos(uscoreStart))
2025+
else
2026+
syntaxError(SeqWildcardPatternPos(), uscoreStart)
20132027
Typed(t, atSpan(uscoreStart) { Ident(tpnme.WILDCARD_STAR) })
2014-
}
2015-
else {
2028+
else
20162029
syntaxErrorOrIncomplete(IncorrectRepeatedParameterSyntax())
20172030
t
2018-
}
2019-
case AT if location != Location.InPattern =>
2031+
case AT if !location.inPattern =>
20202032
annotations().foldLeft(t)(Annotated)
20212033
case _ =>
20222034
val tpt = typeDependingOn(location)
2023-
if (isWildcard(t) && location != Location.InPattern) {
2035+
if (isWildcard(t) && !location.inPattern) {
20242036
val vd :: rest = placeholderParams
20252037
placeholderParams =
20262038
cpy.ValDef(vd)(tpt = tpt).withSpan(vd.span.union(tpt.span)) :: rest
@@ -2063,7 +2075,7 @@ object Parsers {
20632075
* | `_'
20642076
* Bindings ::= `(' [[‘using’] [‘erased’] Binding {`,' Binding}] `)'
20652077
*/
2066-
def funParams(mods: Modifiers, location: Location.Value): List[Tree] =
2078+
def funParams(mods: Modifiers, location: Location): List[Tree] =
20672079
if in.token == LPAREN then
20682080
in.nextToken()
20692081
if in.token == RPAREN then
@@ -2117,10 +2129,10 @@ object Parsers {
21172129
/** Expr ::= [‘implicit’] FunParams `=>' Expr
21182130
* BlockResult ::= implicit id [`:' InfixType] `=>' Block // Scala2 only
21192131
*/
2120-
def closure(start: Int, location: Location.Value, implicitMods: Modifiers): Tree =
2132+
def closure(start: Int, location: Location, implicitMods: Modifiers): Tree =
21212133
closureRest(start, location, funParams(implicitMods, location))
21222134

2123-
def closureRest(start: Int, location: Location.Value, params: List[Tree]): Tree =
2135+
def closureRest(start: Int, location: Location, params: List[Tree]): Tree =
21242136
atSpan(start, in.offset) {
21252137
if in.token == CTXARROW then in.nextToken() else accept(ARROW)
21262138
Function(params, if (location == Location.InBlock) block() else expr())
@@ -2295,10 +2307,9 @@ object Parsers {
22952307
if args._2 then res.setUsingApply()
22962308
res
22972309

2298-
val argumentExpr: () => Tree = () => exprInParens() match {
2310+
val argumentExpr: () => Tree = () => expr(Location.InArgs) match
22992311
case arg @ Assign(Ident(id), rhs) => cpy.NamedArg(arg)(id, rhs)
23002312
case arg => arg
2301-
}
23022313

23032314
/** ArgumentExprss ::= {ArgumentExprs}
23042315
*/
@@ -2535,21 +2546,20 @@ object Parsers {
25352546

25362547
/** Pattern ::= Pattern1 { `|' Pattern1 }
25372548
*/
2538-
val pattern: () => Tree = () => {
2539-
val pat = pattern1()
2549+
def pattern(location: Location = Location.InPattern): Tree =
2550+
val pat = pattern1(location)
25402551
if (isIdent(nme.raw.BAR))
2541-
atSpan(startOffset(pat)) { Alternative(pat :: patternAlts()) }
2552+
atSpan(startOffset(pat)) { Alternative(pat :: patternAlts(location)) }
25422553
else pat
2543-
}
25442554

2545-
def patternAlts(): List[Tree] =
2546-
if (isIdent(nme.raw.BAR)) { in.nextToken(); pattern1() :: patternAlts() }
2555+
def patternAlts(location: Location): List[Tree] =
2556+
if (isIdent(nme.raw.BAR)) { in.nextToken(); pattern1(location) :: patternAlts(location) }
25472557
else Nil
25482558

25492559
/** Pattern1 ::= Pattern2 [Ascription]
25502560
* | ‘given’ PatVar ‘:’ RefinedType
25512561
*/
2552-
def pattern1(): Tree =
2562+
def pattern1(location: Location = Location.InPattern): Tree =
25532563
if (in.token == GIVEN) {
25542564
val givenMod = atSpan(in.skipToken())(Mod.Given())
25552565
atSpan(in.offset) {
@@ -2558,7 +2568,7 @@ object Parsers {
25582568
val name = in.name
25592569
in.nextToken()
25602570
accept(COLON)
2561-
val typed = ascription(Ident(nme.WILDCARD), Location.InPattern)
2571+
val typed = ascription(Ident(nme.WILDCARD), location)
25622572
Bind(name, typed).withMods(addMod(Modifiers(), givenMod))
25632573
case _ =>
25642574
syntaxErrorOrIncomplete("pattern variable expected")
@@ -2570,7 +2580,7 @@ object Parsers {
25702580
val p = pattern2()
25712581
if (in.token == COLON) {
25722582
in.nextToken()
2573-
ascription(p, Location.InPattern)
2583+
ascription(p, location)
25742584
}
25752585
else p
25762586
}
@@ -2661,17 +2671,17 @@ object Parsers {
26612671

26622672
/** Patterns ::= Pattern [`,' Pattern]
26632673
*/
2664-
def patterns(): List[Tree] = commaSeparated(pattern)
2665-
2666-
def patternsOpt(): List[Tree] =
2667-
if (in.token == RPAREN) Nil else patterns()
2674+
def patterns(location: Location = Location.InPattern): List[Tree] =
2675+
commaSeparated(() => pattern(location))
26682676

2677+
def patternsOpt(location: Location = Location.InPattern): List[Tree] =
2678+
if (in.token == RPAREN) Nil else patterns(location)
26692679

26702680
/** ArgumentPatterns ::= ‘(’ [Patterns] ‘)’
26712681
* | ‘(’ [Patterns ‘,’] Pattern2 ‘:’ ‘_’ ‘*’ ‘)’
26722682
*/
26732683
def argumentPatterns(): List[Tree] =
2674-
inParens(patternsOpt())
2684+
inParens(patternsOpt(Location.InPatternArgs))
26752685

26762686
/* -------- MODIFIERS and ANNOTATIONS ------------------------------------------- */
26772687

compiler/test/dotty/tools/vulpix/ParallelTesting.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
627627
lazy val actualErrors = reporters.foldLeft(0)(_ + _.errorCount)
628628
def hasMissingAnnotations = getMissingExpectedErrors(errorMap, reporters.iterator.flatMap(_.errors))
629629
def showErrors = "-> following the errors:\n" +
630-
reporters.flatMap(_.allErrors.map(e => e.pos.toString + ": " + e.message)).mkString(start = "at ", sep = "\n at ", end = "")
630+
reporters.flatMap(_.allErrors.map(e => e.pos.line.toString + ": " + e.message)).mkString(start = "at ", sep = "\n at ", end = "")
631631

632632
if (compilerCrashed) Some(s"Compiler crashed when compiling: ${testSource.title}")
633633
else if (actualErrors == 0) Some(s"\nNo errors found when compiling neg test $testSource")

tests/neg/i7972.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
object O {
2-
def m1(a: Int*) = (a: _*) // error: Cannot return repeated parameter type Int*
2+
def m1(a: Int*) = (a: _*) // error // error: Cannot return repeated parameter type Int*
33
def m2(a: Int*) = { // error: Cannot return repeated parameter type Int*
4-
val b = (a: _*) // error: Cannot return repeated parameter type Int*
4+
val b = (a: _*) // error // error: Cannot return repeated parameter type Int*
55
b
66
}
77
def m3(a: Int*): Any = {
8-
val b = (a: _*) // error: Cannot return repeated parameter type Int*
8+
val b = (a: _*) // error // error: Cannot return repeated parameter type Int*
99
b
1010
}
11-
def m4(a: 2*) = (a: _*) // error: Cannot return repeated parameter type Int*
11+
def m4(a: 2*) = (a: _*) // error // error: Cannot return repeated parameter type Int*
1212

1313
}
1414

1515
class O(a: Int*) {
16-
val m = (a: _*) // error: Cannot return repeated parameter type Int*
16+
val m = (a: _*) // error // error: Cannot return repeated parameter type Int*
1717
}
1818

tests/neg/i8715.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@main
2+
def Test = List(42) match { case List(xs @ (ys: _*)) => xs } // error
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
object Test {
3+
case class K(i: Int)
4+
5+
def main(args: Array[String]) = {
6+
val k = new K(9)
7+
val is = List(1,2,3)
8+
9+
is match {
10+
case List(1, _*,) => // error // error // error: bad use of _* (a sequence pattern must be the last pattern)
11+
// illegal start of simple pattern
12+
case List(1, _*3,) => // error // error: illegal start of simple pattern
13+
//case List(1, _*3:) => // poor recovery by parens
14+
case List(1, x*) => // error: use _* to match a sequence
15+
case List(x*, 1) => // error: trailing * is not a valid pattern
16+
case (1, x*) => // error: trailing * is not a valid pattern
17+
case (1, x: _*) => // error: bad use of _* (sequence pattern not allowed)
18+
}
19+
20+
// good syntax, bad semantics, detected by typer
21+
//gowild.scala:14: error: star patterns must correspond with varargs parameters
22+
val K(x @ _*) = k
23+
val K(ns @ _*, x) = k // error: bad use of _* (a sequence pattern must be the last pattern)
24+
val (b, _ : _* ) = (5,6) // error: bad use of _* (sequence pattern not allowed)
25+
// no longer complains
26+
//bad-and-wild.scala:15: error: ')' expected but '}' found.
27+
}
28+
}
29+

tests/pos-scala2/i8715b.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// from stdlib
2+
class Test {
3+
def printf(text: String, args: Any*): Unit = { System.out.print(text format (args : _*)) }
4+
}

tests/untried/neg/t5702-neg-bad-and-wild.scala

Lines changed: 0 additions & 29 deletions
This file was deleted.

0 commit comments

Comments
 (0)