Skip to content

Commit 39af2f5

Browse files
authored
Merge pull request #1983 from ennru/ennru_ExpectedTokenError
Change '... expected but found ...' to Message
2 parents 606e36b + 81cda70 commit 39af2f5

File tree

6 files changed

+75
-13
lines changed

6 files changed

+75
-13
lines changed

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

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ object Parsers {
110110
*/
111111
def syntaxError(msg: => Message, offset: Int = in.offset): Unit =
112112
if (offset > lastErrorOffset) {
113-
syntaxError(msg, Position(offset))
113+
val length = if (in.name != null) in.name.show.length else 0
114+
syntaxError(msg, Position(offset, offset + length))
114115
lastErrorOffset = in.offset
115116
}
116117

@@ -249,11 +250,6 @@ object Parsers {
249250
lastErrorOffset = in.offset
250251
} // DEBUG
251252

252-
private def expectedMsg(token: Int): String =
253-
expectedMessage(showToken(token))
254-
private def expectedMessage(what: String): String =
255-
s"$what expected but ${showToken(in.token)} found"
256-
257253
/** Consume one token of the specified type, or
258254
* signal an error if it is not there.
259255
*
@@ -262,7 +258,7 @@ object Parsers {
262258
def accept(token: Int): Int = {
263259
val offset = in.offset
264260
if (in.token != token) {
265-
syntaxErrorOrIncomplete(expectedMsg(token))
261+
syntaxErrorOrIncomplete(ExpectedTokenButFound(token, in.token, in.name))
266262
}
267263
if (in.token == token) in.nextToken()
268264
offset
@@ -474,7 +470,7 @@ object Parsers {
474470
in.nextToken()
475471
name
476472
} else {
477-
syntaxErrorOrIncomplete(expectedMsg(IDENTIFIER))
473+
syntaxErrorOrIncomplete(ExpectedTokenButFound(IDENTIFIER, in.token, in.name))
478474
nme.ERROR
479475
}
480476

@@ -1055,7 +1051,7 @@ object Parsers {
10551051
case Block(Nil, EmptyTree) =>
10561052
assert(handlerStart != -1)
10571053
syntaxError(
1058-
new EmptyCatchBlock(body),
1054+
EmptyCatchBlock(body),
10591055
Position(handlerStart, endOffset(handler))
10601056
)
10611057
case _ =>

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ abstract class TokensCommon {
1717

1818
def showToken(token: Int) = {
1919
val str = tokenString(token)
20-
if (keywords contains token) s"'$str'" else str
20+
if (isKeyword(token)) s"'$str'" else str
2121
}
2222

2323
val tokenString, debugString = new Array[String](maxToken + 1)
@@ -114,6 +114,8 @@ abstract class TokensCommon {
114114

115115
val keywords: TokenSet
116116

117+
def isKeyword(token: Token): Boolean = keywords contains token
118+
117119
/** parentheses */
118120
final val LPAREN = 90; enter(LPAREN, "'('")
119121
final val RPAREN = 91; enter(RPAREN, "')'")

compiler/src/dotty/tools/dotc/reporting/diagnostic/Message.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ class ExtendMessage(_msg: () => Message)(f: String => String) { self =>
119119
class NoExplanation(val msg: String) extends Message(NoExplanation.ID) {
120120
val explanation = ""
121121
val kind = ""
122+
123+
override def toString(): String = s"NoExplanation($msg)"
122124
}
123125

124126
/** The extractor for `NoExplanation` can be used to check whether any error

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

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,17 @@ package reporting
44
package diagnostic
55

66
import dotc.core._
7-
import Contexts.Context, Decorators._, Symbols._, Names._, NameOps._, Types._
7+
import Contexts.Context
8+
import Decorators._
9+
import Symbols._
10+
import Names._
11+
import NameOps._
12+
import Types._
813
import util.SourcePosition
914
import config.Settings.Setting
10-
import interfaces.Diagnostic.{ERROR, WARNING, INFO}
15+
import interfaces.Diagnostic.{ERROR, INFO, WARNING}
16+
import dotc.parsing.Scanners.Token
17+
import dotc.parsing.Tokens
1118
import printing.Highlighting._
1219
import printing.Formatting
1320

@@ -1053,4 +1060,30 @@ object messages {
10531060
|""".stripMargin
10541061
}
10551062

1063+
case class ExpectedTokenButFound(expected: Token, found: Token, foundName: TermName)(implicit ctx: Context)
1064+
extends Message(40) {
1065+
val kind = "Syntax"
1066+
1067+
private val expectedText =
1068+
if (Tokens.isIdentifier(expected)) "an identifier"
1069+
else Tokens.showToken(expected)
1070+
1071+
private val foundText =
1072+
if (foundName != null) hl"`${foundName.show}`"
1073+
else Tokens.showToken(found)
1074+
1075+
val msg = hl"""${expectedText} expected, but ${foundText} found"""
1076+
1077+
private val ifKeyword =
1078+
if (Tokens.isIdentifier(expected) && Tokens.isKeyword(found))
1079+
s"""
1080+
|If you necessarily want to use $foundText as identifier, you may put it in backticks.""".stripMargin
1081+
else
1082+
""
1083+
1084+
val explanation =
1085+
s"""|The text ${foundText} may not occur at that position, the compiler expected ${expectedText}.$ifKeyword
1086+
|""".stripMargin
1087+
}
1088+
10561089
}

compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ trait ErrorMessagesTest extends DottyTest {
6565
}
6666

6767
def assertMessageCount(expected: Int, messages: List[Message]): Unit =
68-
assertEquals(
68+
assertEquals(messages.mkString,
6969
expected,
7070
messages.length
7171
)

compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package reporting
44

55
import core.Contexts.Context
66
import diagnostic.messages._
7+
import dotty.tools.dotc.parsing.Tokens
78
import org.junit.Assert._
89
import org.junit.Test
910

@@ -107,4 +108,32 @@ class ErrorMessagesTests extends ErrorMessagesTest {
107108
assertEquals("value a", definition.show)
108109
}
109110

111+
@Test def unexpectedToken =
112+
checkMessagesAfter("frontend") {
113+
"""
114+
|object Forward {
115+
| def val = "ds"
116+
|}
117+
""".stripMargin
118+
}
119+
.expect { (ictx, messages) =>
120+
implicit val ctx: Context = ictx
121+
val defn = ictx.definitions
122+
123+
assertMessageCount(1, messages)
124+
val ExpectedTokenButFound(expected, found, foundName) :: Nil = messages
125+
assertEquals(Tokens.IDENTIFIER, expected)
126+
assertEquals(Tokens.VAL, found)
127+
assertEquals("val", foundName.show)
128+
}
129+
130+
@Test def expectedToken =
131+
checkMessagesAfter("frontend") {
132+
"""
133+
|object Forward {
134+
| def `val` = "ds"
135+
|}
136+
""".stripMargin
137+
}
138+
.expectNoErrors
110139
}

0 commit comments

Comments
 (0)