Skip to content

Change '... expected but found ...' to Message #1983

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 15, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 5 additions & 9 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ object Parsers {
*/
def syntaxError(msg: => Message, offset: Int = in.offset): Unit =
if (offset > lastErrorOffset) {
syntaxError(msg, Position(offset))
val length = if (in.name != null) in.name.show.length else 0
syntaxError(msg, Position(offset, offset + length))
lastErrorOffset = in.offset
}

Expand Down Expand Up @@ -249,11 +250,6 @@ object Parsers {
lastErrorOffset = in.offset
} // DEBUG

private def expectedMsg(token: Int): String =
expectedMessage(showToken(token))
private def expectedMessage(what: String): String =
s"$what expected but ${showToken(in.token)} found"

/** Consume one token of the specified type, or
* signal an error if it is not there.
*
Expand All @@ -262,7 +258,7 @@ object Parsers {
def accept(token: Int): Int = {
val offset = in.offset
if (in.token != token) {
syntaxErrorOrIncomplete(expectedMsg(token))
syntaxErrorOrIncomplete(ExpectedTokenButFound(token, in.token, in.name))
}
if (in.token == token) in.nextToken()
offset
Expand Down Expand Up @@ -474,7 +470,7 @@ object Parsers {
in.nextToken()
name
} else {
syntaxErrorOrIncomplete(expectedMsg(IDENTIFIER))
syntaxErrorOrIncomplete(ExpectedTokenButFound(IDENTIFIER, in.token, in.name))
nme.ERROR
}

Expand Down Expand Up @@ -1055,7 +1051,7 @@ object Parsers {
case Block(Nil, EmptyTree) =>
assert(handlerStart != -1)
syntaxError(
new EmptyCatchBlock(body),
EmptyCatchBlock(body),
Position(handlerStart, endOffset(handler))
)
case _ =>
Expand Down
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/parsing/Tokens.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ abstract class TokensCommon {

def showToken(token: Int) = {
val str = tokenString(token)
if (keywords contains token) s"'$str'" else str
if (isKeyword(token)) s"'$str'" else str
}

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

val keywords: TokenSet

def isKeyword(token: Token): Boolean = keywords contains token

/** parentheses */
final val LPAREN = 90; enter(LPAREN, "'('")
final val RPAREN = 91; enter(RPAREN, "')'")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ class ExtendMessage(_msg: () => Message)(f: String => String) { self =>
class NoExplanation(val msg: String) extends Message(NoExplanation.ID) {
val explanation = ""
val kind = ""

override def toString(): String = s"NoExplanation($msg)"
}

/** The extractor for `NoExplanation` can be used to check whether any error
Expand Down
37 changes: 35 additions & 2 deletions compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,17 @@ package reporting
package diagnostic

import dotc.core._
import Contexts.Context, Decorators._, Symbols._, Names._, NameOps._, Types._
import Contexts.Context
import Decorators._
import Symbols._
import Names._
import NameOps._
import Types._
import util.SourcePosition
import config.Settings.Setting
import interfaces.Diagnostic.{ERROR, WARNING, INFO}
import interfaces.Diagnostic.{ERROR, INFO, WARNING}
import dotc.parsing.Scanners.Token
import dotc.parsing.Tokens
import printing.Highlighting._
import printing.Formatting

Expand Down Expand Up @@ -1053,4 +1060,30 @@ object messages {
|""".stripMargin
}

case class ExpectedTokenButFound(expected: Token, found: Token, foundName: TermName)(implicit ctx: Context)
extends Message(40) {
val kind = "Syntax"

private val expectedText =
if (Tokens.isIdentifier(expected)) "an identifier"
else Tokens.showToken(expected)

private val foundText =
if (foundName != null) hl"`${foundName.show}`"
else Tokens.showToken(found)

val msg = hl"""${expectedText} expected, but ${foundText} found"""

private val ifKeyword =
if (Tokens.isIdentifier(expected) && Tokens.isKeyword(found))
s"""
|If you necessarily want to use $foundText as identifier, you may put it in backticks.""".stripMargin
else
""

val explanation =
s"""|The text ${foundText} may not occur at that position, the compiler expected ${expectedText}.$ifKeyword
|""".stripMargin
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ trait ErrorMessagesTest extends DottyTest {
}

def assertMessageCount(expected: Int, messages: List[Message]): Unit =
assertEquals(
assertEquals(messages.mkString,
expected,
messages.length
)
Expand Down
29 changes: 29 additions & 0 deletions compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package reporting

import core.Contexts.Context
import diagnostic.messages._
import dotty.tools.dotc.parsing.Tokens
import org.junit.Assert._
import org.junit.Test

Expand Down Expand Up @@ -107,4 +108,32 @@ class ErrorMessagesTests extends ErrorMessagesTest {
assertEquals("value a", definition.show)
}

@Test def unexpectedToken =
checkMessagesAfter("frontend") {
"""
|object Forward {
| def val = "ds"
|}
""".stripMargin
}
.expect { (ictx, messages) =>
implicit val ctx: Context = ictx
val defn = ictx.definitions

assertMessageCount(1, messages)
val ExpectedTokenButFound(expected, found, foundName) :: Nil = messages
assertEquals(Tokens.IDENTIFIER, expected)
assertEquals(Tokens.VAL, found)
assertEquals("val", foundName.show)
}

@Test def expectedToken =
checkMessagesAfter("frontend") {
"""
|object Forward {
| def `val` = "ds"
|}
""".stripMargin
}
.expectNoErrors
}