Skip to content

Commit 4639991

Browse files
committed
Fix #8715: enforce syntax for _* (alternate version)
Previously we didn't check that _* is indeed the last argument, checking for ")" is not enough, as ")" may be the closing parenthesis of a nested pattern.
1 parent b3f2aee commit 4639991

File tree

8 files changed

+88
-73
lines changed

8 files changed

+88
-73
lines changed

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

Lines changed: 46 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,13 @@ 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+
Assign(t, subPart(() => expr(location)))
2002+
}
19972003
case _ =>
19982004
t
19992005
case COLON =>
@@ -2003,24 +2009,29 @@ object Parsers {
20032009
t
20042010
end expr1Rest
20052011

2006-
def ascription(t: Tree, location: Location.Value): Tree = atSpan(startOffset(t)) {
2012+
def ascription(t: Tree, location: Location): Tree = atSpan(startOffset(t)) {
20072013
in.token match {
20082014
case USCORE =>
20092015
val uscoreStart = in.skipToken()
2010-
if (isIdent(nme.raw.STAR)) {
2016+
if isIdent(nme.raw.STAR) then
20112017
in.nextToken()
2012-
if (in.token != RPAREN) syntaxError(SeqWildcardPatternPos(), uscoreStart)
2018+
if !(location.inArgs && in.token == RPAREN) then
2019+
if opStack.nonEmpty
2020+
ctx.errorOrMigrationWarning(
2021+
em"""`_*` can be used only for last argument of method application.
2022+
|It is no longer allowed in operands of infix operations.""",
2023+
in.sourcePos(uscoreStart))
2024+
else
2025+
syntaxError(SeqWildcardPatternPos(), uscoreStart)
20132026
Typed(t, atSpan(uscoreStart) { Ident(tpnme.WILDCARD_STAR) })
2014-
}
2015-
else {
2027+
else
20162028
syntaxErrorOrIncomplete(IncorrectRepeatedParameterSyntax())
20172029
t
2018-
}
2019-
case AT if location != Location.InPattern =>
2030+
case AT if !location.inPattern =>
20202031
annotations().foldLeft(t)(Annotated)
20212032
case _ =>
20222033
val tpt = typeDependingOn(location)
2023-
if (isWildcard(t) && location != Location.InPattern) {
2034+
if (isWildcard(t) && !location.inPattern) {
20242035
val vd :: rest = placeholderParams
20252036
placeholderParams =
20262037
cpy.ValDef(vd)(tpt = tpt).withSpan(vd.span.union(tpt.span)) :: rest
@@ -2063,7 +2074,7 @@ object Parsers {
20632074
* | `_'
20642075
* Bindings ::= `(' [[‘using’] [‘erased’] Binding {`,' Binding}] `)'
20652076
*/
2066-
def funParams(mods: Modifiers, location: Location.Value): List[Tree] =
2077+
def funParams(mods: Modifiers, location: Location): List[Tree] =
20672078
if in.token == LPAREN then
20682079
in.nextToken()
20692080
if in.token == RPAREN then
@@ -2117,10 +2128,10 @@ object Parsers {
21172128
/** Expr ::= [‘implicit’] FunParams `=>' Expr
21182129
* BlockResult ::= implicit id [`:' InfixType] `=>' Block // Scala2 only
21192130
*/
2120-
def closure(start: Int, location: Location.Value, implicitMods: Modifiers): Tree =
2131+
def closure(start: Int, location: Location, implicitMods: Modifiers): Tree =
21212132
closureRest(start, location, funParams(implicitMods, location))
21222133

2123-
def closureRest(start: Int, location: Location.Value, params: List[Tree]): Tree =
2134+
def closureRest(start: Int, location: Location, params: List[Tree]): Tree =
21242135
atSpan(start, in.offset) {
21252136
if in.token == CTXARROW then in.nextToken() else accept(ARROW)
21262137
Function(params, if (location == Location.InBlock) block() else expr())
@@ -2295,10 +2306,9 @@ object Parsers {
22952306
if args._2 then res.setUsingApply()
22962307
res
22972308

2298-
val argumentExpr: () => Tree = () => exprInParens() match {
2309+
val argumentExpr: () => Tree = () => expr(Location.InArgs) match
22992310
case arg @ Assign(Ident(id), rhs) => cpy.NamedArg(arg)(id, rhs)
23002311
case arg => arg
2301-
}
23022312

23032313
/** ArgumentExprss ::= {ArgumentExprs}
23042314
*/
@@ -2535,21 +2545,20 @@ object Parsers {
25352545

25362546
/** Pattern ::= Pattern1 { `|' Pattern1 }
25372547
*/
2538-
val pattern: () => Tree = () => {
2539-
val pat = pattern1()
2548+
def pattern(location: Location = Location.InPattern): Tree =
2549+
val pat = pattern1(location)
25402550
if (isIdent(nme.raw.BAR))
2541-
atSpan(startOffset(pat)) { Alternative(pat :: patternAlts()) }
2551+
atSpan(startOffset(pat)) { Alternative(pat :: patternAlts(location)) }
25422552
else pat
2543-
}
25442553

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

25492558
/** Pattern1 ::= Pattern2 [Ascription]
25502559
* | ‘given’ PatVar ‘:’ RefinedType
25512560
*/
2552-
def pattern1(): Tree =
2561+
def pattern1(location: Location = Location.InPattern): Tree =
25532562
if (in.token == GIVEN) {
25542563
val givenMod = atSpan(in.skipToken())(Mod.Given())
25552564
atSpan(in.offset) {
@@ -2558,7 +2567,7 @@ object Parsers {
25582567
val name = in.name
25592568
in.nextToken()
25602569
accept(COLON)
2561-
val typed = ascription(Ident(nme.WILDCARD), Location.InPattern)
2570+
val typed = ascription(Ident(nme.WILDCARD), location)
25622571
Bind(name, typed).withMods(addMod(Modifiers(), givenMod))
25632572
case _ =>
25642573
syntaxErrorOrIncomplete("pattern variable expected")
@@ -2570,7 +2579,7 @@ object Parsers {
25702579
val p = pattern2()
25712580
if (in.token == COLON) {
25722581
in.nextToken()
2573-
ascription(p, Location.InPattern)
2582+
ascription(p, location)
25742583
}
25752584
else p
25762585
}
@@ -2661,17 +2670,17 @@ object Parsers {
26612670

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

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

26702679
/** ArgumentPatterns ::= ‘(’ [Patterns] ‘)’
26712680
* | ‘(’ [Patterns ‘,’] Pattern2 ‘:’ ‘_’ ‘*’ ‘)’
26722681
*/
26732682
def argumentPatterns(): List[Tree] =
2674-
inParens(patternsOpt())
2683+
inParens(patternsOpt(Location.InPatternArgs))
26752684

26762685
/* -------- MODIFIERS and ANNOTATIONS ------------------------------------------- */
26772686

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)