Skip to content

Commit 34afeee

Browse files
committed
Use p* for vararg splices in expressions
1 parent 217e8dc commit 34afeee

File tree

6 files changed

+56
-65
lines changed

6 files changed

+56
-65
lines changed

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

Lines changed: 49 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -919,7 +919,20 @@ object Parsers {
919919
val next = in.lookahead.token
920920
next == LBRACKET || next == LPAREN
921921

922-
/* --------- OPERAND/OPERATOR STACK --------------------------------------- */
922+
/** Is current ident a `*`, and is it followed by a `)` or `, )`? */
923+
def followingIsVararg(): Boolean =
924+
in.isIdent(nme.raw.STAR) && {
925+
val lookahead = in.LookaheadScanner()
926+
lookahead.nextToken()
927+
lookahead.token == RPAREN
928+
|| lookahead.token == COMMA
929+
&& {
930+
lookahead.nextToken()
931+
lookahead.token == RPAREN
932+
}
933+
}
934+
935+
/* --------- OPERAND/OPERATOR STACK --------------------------------------- */
923936

924937
var opStack: List[OpInfo] = Nil
925938

@@ -956,8 +969,8 @@ object Parsers {
956969
*/
957970
def infixOps(
958971
first: Tree, canStartOperand: Token => Boolean, operand: () => Tree,
959-
isType: Boolean = false,
960-
isOperator: => Boolean = true,
972+
isType: Boolean,
973+
isOperator: => Boolean,
961974
maybePostfix: Boolean = false): Tree = {
962975
val base = opStack
963976

@@ -1522,15 +1535,9 @@ object Parsers {
15221535
*/
15231536
def infixType(): Tree = infixTypeRest(refinedType())
15241537

1525-
/** Is current ident a `*`, and is it followed by a `)` or `,`? */
1526-
def isPostfixStar: Boolean =
1527-
in.isIdent(nme.raw.STAR) && {
1528-
val nxt = in.lookahead.token
1529-
nxt == RPAREN || nxt == COMMA
1530-
}
1531-
15321538
def infixTypeRest(t: Tree): Tree =
1533-
infixOps(t, canStartTypeTokens, refinedType, isType = true, isOperator = !isPostfixStar)
1539+
infixOps(t, canStartTypeTokens, refinedType, isType = true,
1540+
isOperator = !followingIsVararg())
15341541

15351542
/** RefinedType ::= WithType {[nl] Refinement}
15361543
*/
@@ -2046,7 +2053,7 @@ object Parsers {
20462053
case t =>
20472054
syntaxError(em"`inline` must be followed by an `if` or a `match`", start)
20482055
t
2049-
else expr1Rest(postfixExpr(), location)
2056+
else expr1Rest(postfixExpr(location), location)
20502057
end expr1
20512058

20522059
def expr1Rest(t: Tree, location: Location): Tree = in.token match
@@ -2068,22 +2075,23 @@ object Parsers {
20682075

20692076
def ascription(t: Tree, location: Location): Tree = atSpan(startOffset(t)) {
20702077
in.token match {
2071-
case USCORE =>
2078+
case USCORE if in.lookahead.isIdent(nme.raw.STAR) =>
20722079
val uscoreStart = in.skipToken()
2073-
if isIdent(nme.raw.STAR) then
2074-
in.nextToken()
2075-
if !(location.inArgs && in.token == RPAREN) then
2076-
if opStack.nonEmpty then
2077-
report.errorOrMigrationWarning(
2078-
em"""`_*` can be used only for last argument of method application.
2079-
|It is no longer allowed in operands of infix operations.""",
2080-
in.sourcePos(uscoreStart))
2081-
else
2082-
syntaxError(SeqWildcardPatternPos(), uscoreStart)
2083-
Typed(t, atSpan(uscoreStart) { Ident(tpnme.WILDCARD_STAR) })
2080+
val isVarargSplice = location.inArgs && followingIsVararg()
2081+
in.nextToken()
2082+
if isVarargSplice then
2083+
if sourceVersion.isAtLeast(`3.1`) then
2084+
report.errorOrMigrationWarning(
2085+
em"The syntax `x: _*` is no longer supported for vararg splices; use `x*` instead",
2086+
in.sourcePos(uscoreStart))
2087+
else if opStack.nonEmpty then
2088+
report.errorOrMigrationWarning(
2089+
em"""`_*` can be used only for last argument of method application.
2090+
|It is no longer allowed in operands of infix operations.""",
2091+
in.sourcePos(uscoreStart))
20842092
else
2085-
syntaxErrorOrIncomplete(IncorrectRepeatedParameterSyntax())
2086-
t
2093+
syntaxError(SeqWildcardPatternPos(), uscoreStart)
2094+
Typed(t, atSpan(uscoreStart) { Ident(tpnme.WILDCARD_STAR) })
20872095
case AT if !location.inPattern =>
20882096
annotations().foldLeft(t)(Annotated)
20892097
case _ =>
@@ -2200,10 +2208,18 @@ object Parsers {
22002208
* | InfixExpr id [nl] InfixExpr
22012209
* | InfixExpr MatchClause
22022210
*/
2203-
def postfixExpr(): Tree = postfixExprRest(prefixExpr())
2211+
def postfixExpr(location: Location = Location.ElseWhere): Tree =
2212+
val t = postfixExprRest(prefixExpr(), location)
2213+
if location.inArgs && followingIsVararg() then
2214+
Typed(t, atSpan(in.skipToken()) { Ident(tpnme.WILDCARD_STAR) })
2215+
else
2216+
t
22042217

2205-
def postfixExprRest(t: Tree): Tree =
2206-
infixOps(t, in.canStartExprTokens, prefixExpr, maybePostfix = true)
2218+
def postfixExprRest(t: Tree, location: Location = Location.ElseWhere): Tree =
2219+
infixOps(t, in.canStartExprTokens, prefixExpr,
2220+
isType = false,
2221+
isOperator = !(location.inArgs && followingIsVararg()),
2222+
maybePostfix = true)
22072223

22082224
/** PrefixExpr ::= [`-' | `+' | `~' | `!'] SimpleExpr
22092225
*/
@@ -2331,7 +2347,7 @@ object Parsers {
23312347
if (in.token == RPAREN) Nil else commaSeparated(exprInParens)
23322348

23332349
/** ParArgumentExprs ::= `(' [‘using’] [ExprsInParens] `)'
2334-
* | `(' [ExprsInParens `,'] PostfixExpr `:' `_' `*' ')'
2350+
* | `(' [ExprsInParens `,'] PostfixExpr `*' ')'
23352351
*/
23362352
def parArgumentExprs(): (List[Tree], Boolean) = inParens {
23372353
if in.token == RPAREN then
@@ -2614,7 +2630,7 @@ object Parsers {
26142630
*/
26152631
def pattern3(): Tree =
26162632
val p = infixPattern()
2617-
if isPostfixStar then
2633+
if followingIsVararg() then
26182634
atSpan(in.skipToken()) {
26192635
Typed(p, Ident(tpnme.WILDCARD_STAR))
26202636
}
@@ -2646,7 +2662,8 @@ object Parsers {
26462662
*/
26472663
def infixPattern(): Tree =
26482664
infixOps(simplePattern(), in.canStartExprTokens, simplePattern,
2649-
isOperator = in.name != nme.raw.BAR && !isPostfixStar)
2665+
isType = false,
2666+
isOperator = in.name != nme.raw.BAR && !followingIsVararg())
26502667

26512668
/** SimplePattern ::= PatVar
26522669
* | Literal

compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ enum ErrorMessageID extends java.lang.Enum[ErrorMessageID] {
3535
IllegalVariableInPatternAlternativeID,
3636
IdentifierExpectedID,
3737
AuxConstructorNeedsNonImplicitParameterID,
38-
IncorrectRepeatedParameterSyntaxID,
38+
VarArgsParamMustComeLastID,
3939
IllegalLiteralID,
4040
PatternMatchExhaustivityID,
4141
MatchCaseUnreachableID,

compiler/src/dotty/tools/dotc/reporting/messages.scala

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -779,32 +779,6 @@ import transform.SymUtils._
779779
|"""
780780
}
781781

782-
class IncorrectRepeatedParameterSyntax()(using Context)
783-
extends SyntaxMsg(IncorrectRepeatedParameterSyntaxID) {
784-
def msg = "'*' expected"
785-
def explain =
786-
em"""|Expected * in ${hl("_*")} operator.
787-
|
788-
|The ${hl("_*")} operator can be used to supply a sequence-based argument
789-
|to a method with a variable-length or repeated parameter. It is used
790-
|to expand the sequence to a variable number of arguments, such that:
791-
|${hl("func(args: _*)")} would expand to ${hl("func(arg1, arg2 ... argN)")}.
792-
|
793-
|Below is an example of how a method with a variable-length
794-
|parameter can be declared and used.
795-
|
796-
|Squares the arguments of a variable-length parameter:
797-
|${hl("def square(args: Int*) = args.map(a => a * a)")}
798-
|
799-
|Usage:
800-
|${hl("square(1, 2, 3) // res0: List[Int] = List(1, 4, 9)")}
801-
|
802-
|Secondary Usage with ${hl("_*")}:
803-
|${hl("val ints = List(2, 3, 4) // ints: List[Int] = List(2, 3, 4)")}
804-
|${hl("square(ints: _*) // res1: List[Int] = List(4, 9, 16)")}
805-
|""".stripMargin
806-
}
807-
808782
class IllegalLiteral()(using Context)
809783
extends SyntaxMsg(IllegalLiteralID) {
810784
def msg = "Illegal literal"
@@ -865,11 +839,11 @@ import transform.SymUtils._
865839

866840
class SeqWildcardPatternPos()(using Context)
867841
extends SyntaxMsg(SeqWildcardPatternPosID) {
868-
def msg = em"""${hl("_*")} can be used only for last argument"""
842+
def msg = em"""${hl("*")} can be used only for last argument"""
869843
def explain = {
870844
val code =
871845
"""def sumOfTheFirstTwo(list: List[Int]): Int = list match {
872-
| case List(first, second, x:_*) => first + second
846+
| case List(first, second, x*) => first + second
873847
| case _ => 0
874848
|}"""
875849
em"""|Sequence wildcard pattern is expected at the end of an argument list.
@@ -1274,7 +1248,7 @@ import transform.SymUtils._
12741248
}
12751249

12761250
class VarArgsParamMustComeLast()(using Context)
1277-
extends SyntaxMsg(IncorrectRepeatedParameterSyntaxID) {
1251+
extends SyntaxMsg(VarArgsParamMustComeLastID) {
12781252
def msg = em"""${hl("varargs")} parameter must come last"""
12791253
def explain =
12801254
em"""|The ${hl("varargs")} field must be the last field in the method signature.

tests/neg/t1625.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
trait T3 {
2-
def foo(x: String*, y: String*, c: String*): Int // error // error: varargs parameter must come last
2+
def foo(x: String*, y: String*, c: String*): Int // error: an identifier expected, but ',' found
33
}

tests/neg/t1625b.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
object T5 {
2-
case class Abc(x: String*, c: String*) // error // error: varargs parameter must come last AND found: String* required: String
2+
case class Abc(x: String*, c: String*) // error: identifier expected but `,` found
33
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ object Test {
1212
case List(1, _*3,) => // error // error // error: illegal start of simple pattern
1313
//case List(1, _*3:) => // poor recovery by parens
1414
case List(1, x*) => // ok
15-
case List(x*, 1) => //ok
15+
case List(x*, 1) => // error: pattern expected
1616
case (1, x*) => //ok
1717
case (1, x: _*) => // error: bad use of _* (sequence pattern not allowed)
1818
}

0 commit comments

Comments
 (0)