Skip to content

Commit 9749b66

Browse files
committed
Classify an id as infix operator only if following can start an operand
Also, detect and report spread operators in illegal positions [Cherry-picked ffb2ab7][modified]
1 parent e2a2803 commit 9749b66

14 files changed

+77
-65
lines changed

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

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,15 @@ object Parsers {
970970
isArrowIndent()
971971
else false
972972

973+
/** Can the next lookahead token start an operand as defined by
974+
* leadingOperandTokens, or is postfix ops enabled?
975+
* This is used to decide whether the current token can be an infix operator.
976+
*/
977+
def nextCanFollowOperator(leadingOperandTokens: BitSet): Boolean =
978+
leadingOperandTokens.contains(in.lookahead.token)
979+
|| in.postfixOpsEnabled
980+
|| in.lookahead.token == COLONop
981+
973982
/* --------- OPERAND/OPERATOR STACK --------------------------------------- */
974983

975984
var opStack: List[OpInfo] = Nil
@@ -1050,7 +1059,11 @@ object Parsers {
10501059
then recur(top)
10511060
else top
10521061

1053-
recur(first)
1062+
val res = recur(first)
1063+
if isIdent(nme.raw.STAR) && !followingIsVararg() then
1064+
syntaxError(em"spread operator `*` not allowed here; must come last in a parameter list")
1065+
in.nextToken()
1066+
res
10541067
end infixOps
10551068

10561069
/* -------- IDENTIFIERS AND LITERALS ------------------------------------------- */
@@ -1671,7 +1684,8 @@ object Parsers {
16711684

16721685
def infixTypeRest(t: Tree): Tree =
16731686
infixOps(t, canStartInfixTypeTokens, refinedTypeFn, Location.ElseWhere, ParseKind.Type,
1674-
isOperator = !followingIsVararg() && !isPureArrow)
1687+
isOperator = !followingIsVararg() && !isPureArrow
1688+
&& nextCanFollowOperator(canStartInfixTypeTokens))
16751689

16761690
/** RefinedType ::= WithType {[nl] Refinement} [`^` CaptureSet]
16771691
*/
@@ -2413,7 +2427,8 @@ object Parsers {
24132427

24142428
def postfixExprRest(t: Tree, location: Location): Tree =
24152429
infixOps(t, in.canStartExprTokens, prefixExpr, location, ParseKind.Expr,
2416-
isOperator = !(location.inArgs && followingIsVararg()))
2430+
isOperator = !(location.inArgs && followingIsVararg())
2431+
&& nextCanFollowOperator(canStartInfixExprTokens))
24172432

24182433
/** PrefixExpr ::= [PrefixOperator'] SimpleExpr
24192434
* PrefixOperator ::= ‘-’ | ‘+’ | ‘~’ | ‘!’ (if not backquoted)
@@ -2920,7 +2935,8 @@ object Parsers {
29202935
def infixPattern(): Tree =
29212936
infixOps(
29222937
simplePattern(), in.canStartExprTokens, simplePatternFn, Location.InPattern, ParseKind.Pattern,
2923-
isOperator = in.name != nme.raw.BAR && !followingIsVararg())
2938+
isOperator = in.name != nme.raw.BAR && !followingIsVararg()
2939+
&& nextCanFollowOperator(canStartPatternTokens))
29242940

29252941
/** SimplePattern ::= PatVar
29262942
* | Literal

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -221,10 +221,13 @@ object Tokens extends TokensCommon {
221221

222222
final val openParensTokens = BitSet(LBRACE, LPAREN, LBRACKET)
223223

224-
final val canStartExprTokens3: TokenSet =
225-
atomicExprTokens
224+
final val canStartInfixExprTokens =
225+
atomicExprTokens
226226
| openParensTokens
227-
| BitSet(INDENT, QUOTE, IF, WHILE, FOR, NEW, TRY, THROW)
227+
| BitSet(QUOTE, NEW)
228+
229+
final val canStartExprTokens3: TokenSet =
230+
canStartInfixExprTokens | BitSet(INDENT, IF, WHILE, FOR, TRY, THROW)
228231

229232
final val canStartExprTokens2: TokenSet = canStartExprTokens3 | BitSet(DO)
230233

@@ -233,6 +236,8 @@ object Tokens extends TokensCommon {
233236

234237
final val canStartTypeTokens: TokenSet = canStartInfixTypeTokens | BitSet(LBRACE)
235238

239+
final val canStartPatternTokens = atomicExprTokens | openParensTokens | BitSet(USCORE, QUOTE)
240+
236241
final val templateIntroTokens: TokenSet = BitSet(CLASS, TRAIT, OBJECT, ENUM, CASECLASS, CASEOBJECT)
237242

238243
final val dclIntroTokens: TokenSet = BitSet(DEF, VAL, VAR, TYPE, GIVEN)

tests/neg/cc-only-defs.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ trait Test {
77

88
val b: ImpureFunction1[Int, Int] // now OK
99

10-
val a: {z} String // error
11-
} // error
10+
val a: {z} String // error // error
11+
}

tests/neg/i14564.check

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,4 @@
1-
-- [E018] Syntax Error: tests/neg/i14564.scala:5:28 --------------------------------------------------------------------
2-
5 |def test = sum"${ List(42)* }" // error // error
3-
| ^
4-
| expression expected but '}' found
5-
|
6-
| longer explanation available when compiling with `-explain`
7-
-- [E008] Not Found Error: tests/neg/i14564.scala:5:26 -----------------------------------------------------------------
8-
5 |def test = sum"${ List(42)* }" // error // error
9-
| ^^^^^^^^^
10-
| value * is not a member of List[Int], but could be made available as an extension method.
11-
|
12-
| One of the following imports might make progress towards fixing the problem:
13-
|
14-
| import scala.math.Fractional.Implicits.infixFractionalOps
15-
| import scala.math.Integral.Implicits.infixIntegralOps
16-
| import scala.math.Numeric.Implicits.infixNumericOps
17-
|
1+
-- Error: tests/neg/i14564.scala:5:26 ----------------------------------------------------------------------------------
2+
5 |def test = sum"${ List(42)* }" // error
3+
| ^
4+
| spread operator `*` not allowed here; must come last in a parameter list

tests/neg/i14564.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ import language.postfixOps as _
22

33
extension (sc: StringContext) def sum(xs: Int*): String = xs.sum.toString
44

5-
def test = sum"${ List(42)* }" // error // error
5+
def test = sum"${ List(42)* }" // error
66

tests/neg/i4453.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
class x0 { var x0 == _ * // error: _* can be used only for last argument // error: == cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method
2-
// error '=' expected, but eof found
1+
class x0 { var x0 == _ * // error spread operator `*` not allowed here // error '=' expected
2+
// error: == cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method

tests/neg/i5498-postfixOps.check

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,25 @@
44
| expression expected but end of statement found
55
|
66
| longer explanation available when compiling with `-explain`
7-
-- [E018] Syntax Error: tests/neg/i5498-postfixOps.scala:6:37 ----------------------------------------------------------
8-
6 | Seq(1, 2).filter(List(1,2) contains) // error: usage of postfix operator // error
9-
| ^
10-
| expression expected but ')' found
11-
|
12-
| longer explanation available when compiling with `-explain`
7+
-- [E040] Syntax Error: tests/neg/i5498-postfixOps.scala:6:29 ----------------------------------------------------------
8+
6 | Seq(1, 2).filter(List(1,2) contains) // error: usage of postfix operator // error // error (type error) // error (type error)
9+
| ^^^^^^^^
10+
| ')' expected, but identifier found
1311
-- [E172] Type Error: tests/neg/i5498-postfixOps.scala:6:0 -------------------------------------------------------------
14-
6 | Seq(1, 2).filter(List(1,2) contains) // error: usage of postfix operator // error
12+
6 | Seq(1, 2).filter(List(1,2) contains) // error: usage of postfix operator // error // error (type error) // error (type error)
1513
|^
1614
|No given instance of type scala.concurrent.duration.DurationConversions.Classifier[Null] was found for parameter ev of method second in trait DurationConversions
15+
-- [E007] Type Mismatch Error: tests/neg/i5498-postfixOps.scala:6:24 ---------------------------------------------------
16+
6 | Seq(1, 2).filter(List(1,2) contains) // error: usage of postfix operator // error // error (type error) // error (type error)
17+
| ^
18+
| Found: (1 : Int)
19+
| Required: Boolean
20+
|
21+
| longer explanation available when compiling with `-explain`
22+
-- [E007] Type Mismatch Error: tests/neg/i5498-postfixOps.scala:6:26 ---------------------------------------------------
23+
6 | Seq(1, 2).filter(List(1,2) contains) // error: usage of postfix operator // error // error (type error) // error (type error)
24+
| ^
25+
| Found: (2 : Int)
26+
| Required: Boolean
27+
|
28+
| longer explanation available when compiling with `-explain`

tests/neg/i5498-postfixOps.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ import scala.concurrent.duration.*
33
def test() = {
44
1 second // error: usage of postfix operator
55

6-
Seq(1, 2).filter(List(1,2) contains) // error: usage of postfix operator // error
6+
Seq(1, 2).filter(List(1,2) contains) // error: usage of postfix operator // error // error (type error) // error (type error)
77
}

tests/neg/i6059.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
def I0(I1: Int ) = I1
2-
val I1 = I0(I0 i2) => // error
2+
val I1 = I0(I0 i2) => // error // error
33
true

tests/neg/t11900.check

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@
1010
52 | println("b"), // error: weird comma
1111
| ^
1212
| end of statement expected but ',' found
13-
-- [E032] Syntax Error: tests/neg/t11900.scala:64:8 --------------------------------------------------------------------
13+
-- Error: tests/neg/t11900.scala:64:7 ----------------------------------------------------------------------------------
1414
64 | _*, // error
15-
| ^
16-
| pattern expected
17-
|
18-
| longer explanation available when compiling with `-explain`
15+
| ^
16+
| spread operator `*` not allowed here; must come last in a parameter list

tests/neg/t1625.check

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
-- [E040] Syntax Error: tests/neg/t1625.scala:2:20 ---------------------------------------------------------------------
1+
-- Error: tests/neg/t1625.scala:2:19 -----------------------------------------------------------------------------------
22
2 | def foo(x: String*, y: String*, c: String*): Int // error: an identifier expected, but ',' found // error: an identifier expected, but ',' found
3-
| ^
4-
| an identifier expected, but ',' found
5-
-- [E040] Syntax Error: tests/neg/t1625.scala:2:32 ---------------------------------------------------------------------
3+
| ^
4+
| spread operator `*` not allowed here; must come last in a parameter list
5+
-- Error: tests/neg/t1625.scala:2:31 -----------------------------------------------------------------------------------
66
2 | def foo(x: String*, y: String*, c: String*): Int // error: an identifier expected, but ',' found // error: an identifier expected, but ',' found
7-
| ^
8-
| an identifier expected, but ',' found
7+
| ^
8+
| spread operator `*` not allowed here; must come last in a parameter list

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: identifier expected but `,` found
2+
case class Abc(x: String*, c: String*) // error: varargs parameter must come last
33
}

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

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,20 @@
1414
13 | case List(1, _*3:) => // error // error
1515
| ^
1616
| an identifier expected, but ')' found
17-
-- [E032] Syntax Error: tests/neg/t5702-neg-bad-and-wild.scala:15:18 ---------------------------------------------------
17+
-- Error: tests/neg/t5702-neg-bad-and-wild.scala:15:17 -----------------------------------------------------------------
1818
15 | case List(x*, 1) => // error: pattern expected
19-
| ^
20-
| pattern expected
21-
|
22-
| longer explanation available when compiling with `-explain`
19+
| ^
20+
| spread operator `*` not allowed here; must come last in a parameter list
2321
-- [E031] Syntax Error: tests/neg/t5702-neg-bad-and-wild.scala:17:18 ---------------------------------------------------
2422
17 | case (1, x: _*) => // error: bad use of _* (sequence pattern not allowed)
2523
| ^
2624
| * can be used only for last argument
2725
|
2826
| longer explanation available when compiling with `-explain`
29-
-- [E032] Syntax Error: tests/neg/t5702-neg-bad-and-wild.scala:23:17 ---------------------------------------------------
27+
-- Error: tests/neg/t5702-neg-bad-and-wild.scala:23:16 -----------------------------------------------------------------
3028
23 | val K(ns @ _*, xx) = k // error: pattern expected
31-
| ^
32-
| pattern expected
33-
|
34-
| longer explanation available when compiling with `-explain`
29+
| ^
30+
| spread operator `*` not allowed here; must come last in a parameter list
3531
-- [E161] Naming Error: tests/neg/t5702-neg-bad-and-wild.scala:24:10 ---------------------------------------------------
3632
24 | val K(x) = k // error: x is already defined as value x
3733
| ^^^^^^^^^^^^

tests/neg/t5702-neg-bad-brace.check

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
-- [E032] Syntax Error: tests/neg/t5702-neg-bad-brace.scala:8:21 -------------------------------------------------------
1+
-- Error: tests/neg/t5702-neg-bad-brace.scala:8:20 ---------------------------------------------------------------------
22
8 | case List(1, _*} => // error: pattern expected
3-
| ^
4-
| pattern expected
5-
|
6-
| longer explanation available when compiling with `-explain`
3+
| ^
4+
| spread operator `*` not allowed here; must come last in a parameter list
75
-- [E040] Syntax Error: tests/neg/t5702-neg-bad-brace.scala:11:0 -------------------------------------------------------
86
11 |} // error: eof expected, but '}' found
97
|^

0 commit comments

Comments
 (0)