Skip to content

Commit bb51905

Browse files
authored
Merge pull request #11240 from dotty-staging/fix-11233
Use p* for vararg splices
2 parents 44931da + ac51c11 commit bb51905

File tree

164 files changed

+568
-583
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

164 files changed

+568
-583
lines changed

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

Lines changed: 74 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -376,13 +376,13 @@ object Parsers {
376376
in.nextToken() // needed to ensure progress; otherwise we might cycle forever
377377
accept(SEMI)
378378

379-
def rewriteNotice(additionalOption: String = "") = {
379+
def rewriteNotice(version: String = "3.0", additionalOption: String = "") = {
380380
val optionStr = if (additionalOption.isEmpty) "" else " " ++ additionalOption
381-
i"\nThis construct can be rewritten automatically under$optionStr -rewrite -source 3.0-migration."
381+
i"\nThis construct can be rewritten automatically under$optionStr -rewrite -source $version-migration."
382382
}
383383

384384
def syntaxVersionError(option: String, span: Span) =
385-
syntaxError(em"""This construct is not allowed under $option.${rewriteNotice(option)}""", span)
385+
syntaxError(em"""This construct is not allowed under $option.${rewriteNotice("3.0", option)}""", span)
386386

387387
def rewriteToNewSyntax(span: Span = Span(in.offset)): Boolean = {
388388
if (in.newSyntax) {
@@ -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.name == 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,25 @@ 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${rewriteNotice("3.1")}",
2086+
in.sourcePos(uscoreStart))
2087+
if sourceVersion == `3.1-migration` then
2088+
patch(source, Span(t.span.end, in.lastOffset), " *")
2089+
else if opStack.nonEmpty then
2090+
report.errorOrMigrationWarning(
2091+
em"""`_*` can be used only for last argument of method application.
2092+
|It is no longer allowed in operands of infix operations.""",
2093+
in.sourcePos(uscoreStart))
20842094
else
2085-
syntaxErrorOrIncomplete(IncorrectRepeatedParameterSyntax())
2086-
t
2095+
syntaxError(SeqWildcardPatternPos(), uscoreStart)
2096+
Typed(t, atSpan(uscoreStart) { Ident(tpnme.WILDCARD_STAR) })
20872097
case AT if !location.inPattern =>
20882098
annotations().foldLeft(t)(Annotated)
20892099
case _ =>
@@ -2152,7 +2162,7 @@ object Parsers {
21522162
// Don't error in non-strict mode, as the alternative syntax "implicit (x: T) => ... "
21532163
// is not supported by Scala2.x
21542164
report.errorOrMigrationWarning(
2155-
s"This syntax is no longer supported; parameter needs to be enclosed in (...)${rewriteNotice()}",
2165+
s"This syntax is no longer supported; parameter needs to be enclosed in (...)${rewriteNotice("3.1")}",
21562166
source.atSpan(Span(start, in.lastOffset)))
21572167
in.nextToken()
21582168
val t = infixType()
@@ -2200,10 +2210,18 @@ object Parsers {
22002210
* | InfixExpr id [nl] InfixExpr
22012211
* | InfixExpr MatchClause
22022212
*/
2203-
def postfixExpr(): Tree = postfixExprRest(prefixExpr())
2213+
def postfixExpr(location: Location = Location.ElseWhere): Tree =
2214+
val t = postfixExprRest(prefixExpr(), location)
2215+
if location.inArgs && followingIsVararg() then
2216+
Typed(t, atSpan(in.skipToken()) { Ident(tpnme.WILDCARD_STAR) })
2217+
else
2218+
t
22042219

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

22082226
/** PrefixExpr ::= [`-' | `+' | `~' | `!'] SimpleExpr
22092227
*/
@@ -2331,7 +2349,7 @@ object Parsers {
23312349
if (in.token == RPAREN) Nil else commaSeparated(exprInParens)
23322350

23332351
/** ParArgumentExprs ::= `(' [‘using’] [ExprsInParens] `)'
2334-
* | `(' [ExprsInParens `,'] PostfixExpr `:' `_' `*' ')'
2352+
* | `(' [ExprsInParens `,'] PostfixExpr `*' ')'
23352353
*/
23362354
def parArgumentExprs(): (List[Tree], Boolean) = inParens {
23372355
if in.token == RPAREN then
@@ -2610,37 +2628,44 @@ object Parsers {
26102628
ascription(p, location)
26112629
else p
26122630

2613-
/** Pattern2 ::= [id `as'] InfixPattern
2631+
/** Pattern3 ::= InfixPattern [‘*’]
2632+
*/
2633+
def pattern3(): Tree =
2634+
val p = infixPattern()
2635+
if followingIsVararg() then
2636+
atSpan(in.skipToken()) {
2637+
Typed(p, Ident(tpnme.WILDCARD_STAR))
2638+
}
2639+
else p
2640+
2641+
/** Pattern2 ::= [id `@'] Pattern3
26142642
*/
2615-
val pattern2: () => Tree = () => infixPattern() match {
2643+
val pattern2: () => Tree = () => pattern3() match
26162644
case p @ Ident(name) if in.token == AT =>
26172645
val offset = in.skipToken()
2618-
infixPattern() match {
2619-
case pt @ Ident(tpnme.WILDCARD_STAR) => // compatibility for Scala2 `x @ _*` syntax
2620-
warnMigration(p)
2621-
atSpan(startOffset(p), offset) { Typed(p, pt) }
2646+
pattern3() match {
26222647
case pt @ Bind(nme.WILDCARD, pt1: Typed) if pt.mods.is(Given) =>
26232648
atSpan(startOffset(p), 0) { Bind(name, pt1).withMods(pt.mods) }
2649+
case Typed(Ident(nme.WILDCARD), pt @ Ident(tpnme.WILDCARD_STAR)) =>
2650+
atSpan(startOffset(p), 0) { Typed(p, pt) }
26242651
case pt =>
26252652
atSpan(startOffset(p), 0) { Bind(name, pt) }
26262653
}
2627-
case p @ Ident(tpnme.WILDCARD_STAR) =>
2628-
warnMigration(p)
2629-
atSpan(startOffset(p)) { Typed(Ident(nme.WILDCARD), p) }
26302654
case p =>
26312655
p
2632-
}
26332656

2634-
private def warnMigration(p: Tree) =
2657+
private def warnStarMigration(p: Tree) =
26352658
if sourceVersion.isAtLeast(`3.1`) then
26362659
report.errorOrMigrationWarning(
2637-
"The syntax `x @ _*` is no longer supported; use `x : _*` instead",
2660+
em"The syntax `x: _*` is no longer supported for vararg splices; use `x*` instead",
26382661
in.sourcePos(startOffset(p)))
26392662

26402663
/** InfixPattern ::= SimplePattern {id [nl] SimplePattern}
26412664
*/
26422665
def infixPattern(): Tree =
2643-
infixOps(simplePattern(), in.canStartExprTokens, simplePattern, isOperator = in.name != nme.raw.BAR)
2666+
infixOps(simplePattern(), in.canStartExprTokens, simplePattern,
2667+
isType = false,
2668+
isOperator = in.name != nme.raw.BAR && !followingIsVararg())
26442669

26452670
/** SimplePattern ::= PatVar
26462671
* | Literal
@@ -2659,16 +2684,7 @@ object Parsers {
26592684
case id @ Ident(nme.raw.MINUS) if isNumericLit => literal(startOffset(id))
26602685
case t => simplePatternRest(t)
26612686
case USCORE =>
2662-
val wildIdent = wildcardIdent()
2663-
2664-
// compatibility for Scala2 `x @ _*` and `_*` syntax
2665-
// `x: _*' is parsed in `ascription'
2666-
if (isIdent(nme.raw.STAR)) {
2667-
in.nextToken()
2668-
if (in.token != RPAREN) syntaxError(SeqWildcardPatternPos(), wildIdent.span)
2669-
atSpan(wildIdent.span) { Ident(tpnme.WILDCARD_STAR) }
2670-
}
2671-
else wildIdent
2687+
wildcardIdent()
26722688
case LPAREN =>
26732689
atSpan(in.offset) { makeTupleOrParens(inParens(patternsOpt())) }
26742690
case QUOTE =>
@@ -2710,7 +2726,7 @@ object Parsers {
27102726
if (in.token == RPAREN) Nil else patterns(location)
27112727

27122728
/** ArgumentPatterns ::= ‘(’ [Patterns] ‘)’
2713-
* | ‘(’ [Patterns ‘,’] Pattern2 ‘:’ ‘_’ ‘*’ ‘)’
2729+
* | ‘(’ [Patterns ‘,’] Pattern2 ‘*’ ‘)’
27142730
*/
27152731
def argumentPatterns(): List[Tree] =
27162732
inParens(patternsOpt(Location.InPatternArgs))

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -415,10 +415,20 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
415415
}
416416
case Typed(expr, tpt) =>
417417
changePrec(InfixPrec) {
418-
val exprText = toText(expr)
419-
val line = exprText.lastLine
420-
val colon = if (!line.isEmpty && isOperatorPart(line.last)) " :" else ":"
421-
exprText ~ colon ~ toText(tpt) }
418+
if isWildcardStarArg(tree) then
419+
expr match
420+
case Ident(nme.WILDCARD_STAR) =>
421+
// `_*` is used as a wildcard name to indicate a vararg splice pattern;
422+
// avoid the double `*` in this case.
423+
toText(expr)
424+
case _ =>
425+
toText(expr) ~ "*"
426+
else
427+
val exprText = toText(expr)
428+
val line = exprText.lastLine
429+
val colon = if !line.isEmpty && isOperatorPart(line.last) then " :" else ":"
430+
exprText ~ colon ~ toText(tpt)
431+
}
422432
case NamedArg(name, arg) =>
423433
toText(name) ~ " = " ~ toText(arg)
424434
case Assign(lhs, rhs) =>

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.

docs/docs/internals/syntax.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ ExprsInParens ::= ExprInParens {‘,’ ExprInParens}
252252
ExprInParens ::= PostfixExpr ‘:’ Type -- normal Expr allows only RefinedType here
253253
| Expr
254254
ParArgumentExprs ::= ‘(’ [‘using’] ExprsInParens ‘)’ exprs
255-
| ‘(’ [ExprsInParens ‘,’] PostfixExpr ‘:’ ‘_’ ‘*’ ‘)’ exprs :+ Typed(expr, Ident(wildcardStar))
255+
| ‘(’ [ExprsInParens ‘,’] PostfixExpr ‘*’ ‘)’ exprs :+ Typed(expr, Ident(wildcardStar))
256256
ArgumentExprs ::= ParArgumentExprs
257257
| BlockExpr
258258
BlockExpr ::= <<< (CaseClauses | Block) >>>
@@ -297,7 +297,7 @@ PatVar ::= varid
297297
| ‘_’
298298
Patterns ::= Pattern {‘,’ Pattern}
299299
ArgumentPatterns ::= ‘(’ [Patterns] ‘)’ Apply(fn, pats)
300-
| ‘(’ [Patterns ‘,’] Pattern2 ‘:’ ‘_’ ‘*’ ‘)’
300+
| ‘(’ [Patterns ‘,’] Pattern2 ‘*’ ‘)’
301301
```
302302

303303
### Type and Value Parameters

docs/docs/reference/changed-features/vararg-patterns.md

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

0 commit comments

Comments
 (0)