Skip to content

Commit d1c4ff9

Browse files
oderskyWojciechMazur
authored andcommitted
Mention , in addition to ) in error messages
[Cherry-picked 45fc82a]
1 parent 95c110c commit d1c4ff9

File tree

6 files changed

+96
-23
lines changed

6 files changed

+96
-23
lines changed

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

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -555,10 +555,29 @@ object Parsers {
555555
accept(tok)
556556
try body finally accept(tok + 1)
557557

558+
/** Same as enclosed, but if closing token is missing, add `,` to the expected tokens
559+
* in the error message provided the next token could have followed a `,`.
560+
*/
561+
def enclosedWithCommas[T](tok: Token, body: => T): T =
562+
accept(tok)
563+
val closing = tok + 1
564+
val isEmpty = in.token == closing
565+
val ts = body
566+
if in.token != closing then
567+
val followComma =
568+
if tok == LPAREN then canStartExprTokens3 else canStartTypeTokens
569+
val prefix = if !isEmpty && followComma.contains(in.token) then "',' or " else ""
570+
syntaxErrorOrIncomplete(ExpectedTokenButFound(closing, in.token, prefix))
571+
if in.token == closing then in.nextToken()
572+
ts
573+
558574
def inParens[T](body: => T): T = enclosed(LPAREN, body)
559575
def inBraces[T](body: => T): T = enclosed(LBRACE, body)
560576
def inBrackets[T](body: => T): T = enclosed(LBRACKET, body)
561577

578+
def inParensWithCommas[T](body: => T): T = enclosedWithCommas(LPAREN, body)
579+
def inBracketsWithCommas[T](body: => T): T = enclosedWithCommas(LBRACKET, body)
580+
562581
def inBracesOrIndented[T](body: => T, rewriteWithColon: Boolean = false): T =
563582
if in.token == INDENT then
564583
val rewriteToBraces = in.rewriteNoIndent
@@ -1672,7 +1691,7 @@ object Parsers {
16721691
/** FunParamClause ::= ‘(’ TypedFunParam {‘,’ TypedFunParam } ‘)’
16731692
*/
16741693
def funParamClause(): List[ValDef] =
1675-
inParens(commaSeparated(() => typedFunParam(in.offset, ident())))
1694+
inParensWithCommas(commaSeparated(() => typedFunParam(in.offset, ident())))
16761695

16771696
def funParamClauses(): List[List[ValDef]] =
16781697
if in.token == LPAREN then funParamClause() :: funParamClauses() else Nil
@@ -1821,7 +1840,7 @@ object Parsers {
18211840
else
18221841
def singletonArgs(t: Tree): Tree =
18231842
if in.token == LPAREN && in.featureEnabled(Feature.dependent)
1824-
then singletonArgs(AppliedTypeTree(t, inParens(commaSeparated(singleton))))
1843+
then singletonArgs(AppliedTypeTree(t, inParensWithCommas(commaSeparated(singleton))))
18251844
else t
18261845
singletonArgs(simpleType1())
18271846

@@ -1837,7 +1856,7 @@ object Parsers {
18371856
def simpleType1() = simpleTypeRest {
18381857
if in.token == LPAREN then
18391858
atSpan(in.offset) {
1840-
makeTupleOrParens(inParens(argTypes(namedOK = false, wildOK = true)))
1859+
makeTupleOrParens(inParensWithCommas(argTypes(namedOK = false, wildOK = true)))
18411860
}
18421861
else if in.token == LBRACE then
18431862
atSpan(in.offset) { RefinedTypeTree(EmptyTree, refinement(indentOK = false)) }
@@ -1990,7 +2009,8 @@ object Parsers {
19902009
/** TypeArgs ::= `[' Type {`,' Type} `]'
19912010
* NamedTypeArgs ::= `[' NamedTypeArg {`,' NamedTypeArg} `]'
19922011
*/
1993-
def typeArgs(namedOK: Boolean, wildOK: Boolean): List[Tree] = inBrackets(argTypes(namedOK, wildOK))
2012+
def typeArgs(namedOK: Boolean, wildOK: Boolean): List[Tree] =
2013+
inBracketsWithCommas(argTypes(namedOK, wildOK))
19942014

19952015
/** Refinement ::= `{' RefineStatSeq `}'
19962016
*/
@@ -2487,7 +2507,7 @@ object Parsers {
24872507
placeholderParams = param :: placeholderParams
24882508
atSpan(start) { Ident(pname) }
24892509
case LPAREN =>
2490-
atSpan(in.offset) { makeTupleOrParens(inParens(exprsInParensOrBindings())) }
2510+
atSpan(in.offset) { makeTupleOrParens(inParensWithCommas(exprsInParensOrBindings())) }
24912511
case LBRACE | INDENT =>
24922512
canApply = false
24932513
blockExpr()
@@ -2592,15 +2612,15 @@ object Parsers {
25922612
/** ParArgumentExprs ::= `(' [‘using’] [ExprsInParens] `)'
25932613
* | `(' [ExprsInParens `,'] PostfixExpr `*' ')'
25942614
*/
2595-
def parArgumentExprs(): (List[Tree], Boolean) = inParens {
2596-
if in.token == RPAREN then
2597-
(Nil, false)
2598-
else if isIdent(nme.using) then
2599-
in.nextToken()
2600-
(commaSeparated(argumentExpr), true)
2601-
else
2602-
(commaSeparated(argumentExpr), false)
2603-
}
2615+
def parArgumentExprs(): (List[Tree], Boolean) =
2616+
inParensWithCommas:
2617+
if in.token == RPAREN then
2618+
(Nil, false)
2619+
else if isIdent(nme.using) then
2620+
in.nextToken()
2621+
(commaSeparated(argumentExpr), true)
2622+
else
2623+
(commaSeparated(argumentExpr), false)
26042624

26052625
/** ArgumentExprs ::= ParArgumentExprs
26062626
* | [nl] BlockExpr
@@ -2957,7 +2977,7 @@ object Parsers {
29572977
case USCORE =>
29582978
wildcardIdent()
29592979
case LPAREN =>
2960-
atSpan(in.offset) { makeTupleOrParens(inParens(patternsOpt())) }
2980+
atSpan(in.offset) { makeTupleOrParens(inParensWithCommas(patternsOpt())) }
29612981
case QUOTE =>
29622982
simpleExpr(Location.InPattern)
29632983
case XMLSTART =>
@@ -3003,7 +3023,7 @@ object Parsers {
30033023
* | ‘(’ [Patterns ‘,’] PatVar ‘*’ ‘)’
30043024
*/
30053025
def argumentPatterns(): List[Tree] =
3006-
inParens(patternsOpt(Location.InPatternArgs))
3026+
inParensWithCommas(patternsOpt(Location.InPatternArgs))
30073027

30083028
/* -------- MODIFIERS and ANNOTATIONS ------------------------------------------- */
30093029

@@ -3192,7 +3212,7 @@ object Parsers {
31923212
* HkTypeParamClause ::= ‘[’ HkTypeParam {‘,’ HkTypeParam} ‘]’
31933213
* HkTypeParam ::= {Annotation} [‘+’ | ‘-’] (id [HkTypePamClause] | ‘_’) TypeBounds
31943214
*/
3195-
def typeParamClause(ownerKind: ParamOwner): List[TypeDef] = inBrackets {
3215+
def typeParamClause(ownerKind: ParamOwner): List[TypeDef] = inBracketsWithCommas {
31963216

31973217
def checkVarianceOK(): Boolean =
31983218
val ok = ownerKind != ParamOwner.Def && ownerKind != ParamOwner.TypeParam
@@ -3331,7 +3351,7 @@ object Parsers {
33313351
}
33323352

33333353
// begin termParamClause
3334-
inParens {
3354+
inParensWithCommas {
33353355
if in.token == RPAREN && !prefix && !impliedMods.is(Given) then Nil
33363356
else
33373357
val clause =

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,7 +1169,7 @@ extends ReferenceMsg(ForwardReferenceExtendsOverDefinitionID) {
11691169
|"""
11701170
}
11711171

1172-
class ExpectedTokenButFound(expected: Token, found: Token)(using Context)
1172+
class ExpectedTokenButFound(expected: Token, found: Token, prefix: String = "")(using Context)
11731173
extends SyntaxMsg(ExpectedTokenButFoundID) {
11741174

11751175
private def foundText = Tokens.showToken(found)
@@ -1178,7 +1178,7 @@ extends SyntaxMsg(ExpectedTokenButFoundID) {
11781178
val expectedText =
11791179
if (Tokens.isIdentifier(expected)) "an identifier"
11801180
else Tokens.showToken(expected)
1181-
i"""${expectedText} expected, but ${foundText} found"""
1181+
i"""$prefix$expectedText expected, but $foundText found"""
11821182

11831183
def explain(using Context) =
11841184
if (Tokens.isIdentifier(expected) && Tokens.isKeyword(found))

tests/neg/i18734.check

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-- [E040] Syntax Error: tests/neg/i18734.scala:7:8 ---------------------------------------------------------------------
2+
7 | Foo(1 2) // error
3+
| ^
4+
| ',' or ')' expected, but integer literal found
5+
-- [E040] Syntax Error: tests/neg/i18734.scala:9:8 ---------------------------------------------------------------------
6+
9 | Foo(x y) // error
7+
| ^
8+
| ',' or ')' expected, but identifier found
9+
-- [E040] Syntax Error: tests/neg/i18734.scala:11:8 --------------------------------------------------------------------
10+
11 | Foo(1 b = 2) // error
11+
| ^
12+
| ',' or ')' expected, but identifier found
13+
-- [E040] Syntax Error: tests/neg/i18734.scala:16:4 --------------------------------------------------------------------
14+
16 | b = 2 // error
15+
| ^
16+
| ',' or ')' expected, but identifier found
17+
-- [E040] Syntax Error: tests/neg/i18734.scala:19:32 -------------------------------------------------------------------
18+
19 | val f: (Int, Int) => Int = (x y) => x + y // error
19+
| ^
20+
| ',' or ')' expected, but identifier found
21+
-- [E040] Syntax Error: tests/neg/i18734.scala:23:10 -------------------------------------------------------------------
22+
23 | bar[Int String](1 2) // error // error
23+
| ^^^^^^
24+
| ',' or ']' expected, but identifier found
25+
-- [E040] Syntax Error: tests/neg/i18734.scala:23:20 -------------------------------------------------------------------
26+
23 | bar[Int String](1 2) // error // error
27+
| ^
28+
| ',' or ')' expected, but integer literal found

tests/neg/i18734.scala

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
case class Foo(a: Int, b: Int)
2+
3+
object Bar:
4+
val x = 1
5+
val y = 2
6+
7+
Foo(1 2) // error
8+
9+
Foo(x y) // error
10+
11+
Foo(1 b = 2) // error
12+
13+
// Or
14+
Foo(
15+
a = 1
16+
b = 2 // error
17+
)
18+
19+
val f: (Int, Int) => Int = (x y) => x + y // error
20+
21+
def bar[X, Y](x: X, y: Y) = ???
22+
23+
bar[Int String](1 2) // error // error
24+
25+

tests/neg/i5498-postfixOps.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
-- [E040] Syntax Error: tests/neg/i5498-postfixOps.scala:6:29 ----------------------------------------------------------
88
6 | Seq(1, 2).filter(List(1,2) contains) // error: usage of postfix operator // error // error (type error) // error (type error)
99
| ^^^^^^^^
10-
| ')' expected, but identifier found
10+
| ',' or ')' expected, but identifier found
1111
-- [E172] Type Error: tests/neg/i5498-postfixOps.scala:6:0 -------------------------------------------------------------
1212
6 | Seq(1, 2).filter(List(1,2) contains) // error: usage of postfix operator // error // error (type error) // error (type error)
1313
|^

tests/neg/syntax-error-recovery.check

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
-- [E040] Syntax Error: tests/neg/syntax-error-recovery.scala:19:4 -----------------------------------------------------
1010
19 | if x == 0 then println(bar) // error
1111
| ^^
12-
| ')' expected, but 'if' found
12+
| ',' or ')' expected, but 'if' found
1313
-- [E040] Syntax Error: tests/neg/syntax-error-recovery.scala:23:12 ----------------------------------------------------
1414
23 | if x < 0) then // error
1515
| ^
@@ -25,7 +25,7 @@
2525
-- [E040] Syntax Error: tests/neg/syntax-error-recovery.scala:48:4 -----------------------------------------------------
2626
48 | if x == 0 then println(bar) // error
2727
| ^^
28-
| ')' expected, but 'if' found
28+
| ',' or ')' expected, but 'if' found
2929
-- [E040] Syntax Error: tests/neg/syntax-error-recovery.scala:52:12 ----------------------------------------------------
3030
52 | if x < 0) then // error
3131
| ^

0 commit comments

Comments
 (0)