aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Mulder <felix.mulder@gmail.com>2016-09-19 19:26:16 +0200
committerFelix Mulder <felix.mulder@gmail.com>2016-10-10 13:25:34 +0200
commite24289ac19a21c6b3794d02e8fe42766224f173c (patch)
tree94dae1ffb70995c2851550952f0f17a953936711
parent628b7f317756ce2c366359a7399b8dda9d0190b7 (diff)
downloaddotty-e24289ac19a21c6b3794d02e8fe42766224f173c.tar.gz
dotty-e24289ac19a21c6b3794d02e8fe42766224f173c.tar.bz2
dotty-e24289ac19a21c6b3794d02e8fe42766224f173c.zip
Make relevant parts of compiler conform to new error handling
-rw-r--r--src/dotty/tools/dotc/parsing/Parsers.scala23
-rw-r--r--src/dotty/tools/dotc/printing/Formatting.scala5
-rw-r--r--src/dotty/tools/dotc/reporting/ConsoleReporter.scala25
-rw-r--r--src/dotty/tools/dotc/reporting/FancyConsoleReporter.scala18
-rw-r--r--src/dotty/tools/dotc/reporting/HideNonSensicalMessages.scala4
-rw-r--r--src/dotty/tools/dotc/reporting/Reporter.scala59
-rw-r--r--src/dotty/tools/dotc/reporting/StoreReporter.scala6
-rw-r--r--src/dotty/tools/dotc/reporting/ThrowingReporter.scala4
-rw-r--r--src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala4
-rw-r--r--src/dotty/tools/dotc/reporting/diagnostic/Message.scala98
-rw-r--r--src/dotty/tools/dotc/reporting/diagnostic/MessageContainer.scala76
-rw-r--r--src/dotty/tools/dotc/reporting/diagnostic/MessageCreator.scala52
-rw-r--r--src/dotty/tools/dotc/reporting/diagnostic/messages.scala134
-rw-r--r--src/dotty/tools/dotc/reporting/diagnostic/parser.scala8
-rw-r--r--src/dotty/tools/dotc/reporting/diagnostic/syntax.scala61
-rw-r--r--src/dotty/tools/dotc/reporting/diagnostic/tpe.scala55
-rw-r--r--src/dotty/tools/dotc/typer/ErrorReporting.scala3
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala12
-rw-r--r--test/test/CompilerTest.scala4
-rw-r--r--test/test/OtherEntryPointsTest.scala4
20 files changed, 317 insertions, 338 deletions
diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala
index 1b451ced7..dcd596298 100644
--- a/src/dotty/tools/dotc/parsing/Parsers.scala
+++ b/src/dotty/tools/dotc/parsing/Parsers.scala
@@ -27,9 +27,8 @@ import rewrite.Rewrites.patch
object Parsers {
import ast.untpd._
- import reporting.diagnostic.MessageCreator
- import MessageCreator._
- import reporting.diagnostic.syntax._
+ import reporting.diagnostic.Message
+ import reporting.diagnostic.messages._
case class OpInfo(operand: Tree, operator: Name, offset: Offset)
@@ -100,17 +99,17 @@ object Parsers {
/** Issue an error at given offset if beyond last error offset
* and update lastErrorOffset.
*/
- def syntaxError(expl: MessageCreator, offset: Int = in.offset): Unit =
+ def syntaxError(msg: Message, offset: Int = in.offset): Unit =
if (offset > lastErrorOffset) {
- syntaxError(expl, Position(offset))
+ syntaxError(msg, Position(offset))
lastErrorOffset = in.offset
}
/** Unconditionally issue an error at given position, without
* updating lastErrorOffset.
*/
- def syntaxError(expl: MessageCreator, pos: Position): Unit =
- ctx.explainError(expl, source atPos pos)
+ def syntaxError(msg: Message, pos: Position): Unit =
+ ctx.error(msg, source atPos pos)
}
@@ -216,20 +215,20 @@ object Parsers {
}
}
- def warning(msg: MessageCreator, offset: Int = in.offset) =
- ctx.explainWarning(msg, source atPos Position(offset))
+ def warning(msg: Message, offset: Int = in.offset) =
+ ctx.warning(msg, source atPos Position(offset))
- def deprecationWarning(msg: String, offset: Int = in.offset) =
+ def deprecationWarning(msg: Message, offset: Int = in.offset) =
ctx.deprecationWarning(msg, source atPos Position(offset))
/** Issue an error at current offset taht input is incomplete */
- def incompleteInputError(msg: String) =
+ def incompleteInputError(msg: Message) =
ctx.incompleteInputError(msg, source atPos Position(in.offset))
/** If at end of file, issue an incompleteInputError.
* Otherwise issue a syntax error and skip to next safe point.
*/
- def syntaxErrorOrIncomplete(msg: String) =
+ def syntaxErrorOrIncomplete(msg: Message) =
if (in.token == EOF) incompleteInputError(msg)
else {
syntaxError(msg)
diff --git a/src/dotty/tools/dotc/printing/Formatting.scala b/src/dotty/tools/dotc/printing/Formatting.scala
index f1bb57bd5..b39d5683e 100644
--- a/src/dotty/tools/dotc/printing/Formatting.scala
+++ b/src/dotty/tools/dotc/printing/Formatting.scala
@@ -8,7 +8,7 @@ import collection.Map
import Decorators._
import scala.annotation.switch
import scala.util.control.NonFatal
-import reporting.diagnostic.Message
+import reporting.diagnostic.MessageContainer
object Formatting {
@@ -67,6 +67,7 @@ object Formatting {
*/
class ErrorMessageFormatter(sc: StringContext) extends StringFormatter(sc) {
override protected def showArg(arg: Any)(implicit ctx: Context): String = {
+ import MessageContainer._
def isSensical(arg: Any): Boolean = arg match {
case tpe: Type =>
tpe.exists && !tpe.isErroneous
@@ -76,7 +77,7 @@ object Formatting {
}
val str = super.showArg(arg)
if (isSensical(arg)) str
- else Message.nonSensicalStartTag + str + Message.nonSensicalEndTag
+ else nonSensicalStartTag + str + nonSensicalEndTag
}
}
diff --git a/src/dotty/tools/dotc/reporting/ConsoleReporter.scala b/src/dotty/tools/dotc/reporting/ConsoleReporter.scala
index 1ae6a1135..92eea9fa9 100644
--- a/src/dotty/tools/dotc/reporting/ConsoleReporter.scala
+++ b/src/dotty/tools/dotc/reporting/ConsoleReporter.scala
@@ -9,6 +9,7 @@ import Reporter._
import java.io.{ BufferedReader, IOException, PrintWriter }
import scala.reflect.internal.util._
import diagnostic.Message
+import diagnostic.MessageContainer
import diagnostic.messages._
/**
@@ -16,11 +17,11 @@ import diagnostic.messages._
* console.
*/
class ConsoleReporter(
- reader: BufferedReader = Console.in,
- writer: PrintWriter = new PrintWriter(Console.err, true))
- extends Reporter with UniqueMessagePositions with HideNonSensicalMessages {
+ reader: BufferedReader = Console.in,
+ writer: PrintWriter = new PrintWriter(Console.err, true)
+) extends Reporter with UniqueMessagePositions with HideNonSensicalMessages {
- import Message._
+ import MessageContainer._
/** maximal number of error messages to be printed */
protected def ErrorLimit = 100
@@ -35,9 +36,9 @@ class ConsoleReporter(
def printMessage(msg: String): Unit = { writer.print(msg + "\n"); writer.flush() }
/** Prints the message with the given position indication. */
- def printMessageAndPos(msg: String, pos: SourcePosition, kind: String = "")(implicit ctx: Context): Unit = {
+ def printMessageAndPos(msg: Message, pos: SourcePosition, kind: String)(implicit ctx: Context): Unit = {
val posStr = if (pos.exists) s"$pos: " else ""
- printMessage(s"${posStr}$kind: $msg")
+ printMessage(s"${posStr}${kind}: ${msg.msg}")
if (pos.exists) {
printSourceLine(pos)
printColumnMarker(pos)
@@ -52,21 +53,21 @@ class ConsoleReporter(
|${m.explanation}""".stripMargin
)
- override def doReport(m: Message)(implicit ctx: Context): Unit = {
+ override def doReport(m: MessageContainer)(implicit ctx: Context): Unit = {
m match {
case m: Error =>
- printMessageAndPos(m.message, m.pos, m.kind)
+ printMessageAndPos(m.contained, m.pos, m.kind)
if (ctx.settings.prompt.value) displayPrompt()
case m: ConditionalWarning if !m.enablingOption.value =>
case m: MigrationWarning =>
- printMessageAndPos(m.message, m.pos, m.kind)
+ printMessageAndPos(m.contained, m.pos, m.kind)
case m: Warning =>
- printMessageAndPos(m.message, m.pos, m.kind)
+ printMessageAndPos(m.contained, m.pos, m.kind)
case _ =>
- printMessageAndPos(m.message, m.pos, m.kind)
+ printMessageAndPos(m.contained, m.pos, m.kind)
}
- if (ctx.shouldExplain(m)) printExplanation(m)
+ if (ctx.shouldExplain(m)) printExplanation(m.contained)
}
def displayPrompt(): Unit = {
diff --git a/src/dotty/tools/dotc/reporting/FancyConsoleReporter.scala b/src/dotty/tools/dotc/reporting/FancyConsoleReporter.scala
index d69396f5f..725e69ff8 100644
--- a/src/dotty/tools/dotc/reporting/FancyConsoleReporter.scala
+++ b/src/dotty/tools/dotc/reporting/FancyConsoleReporter.scala
@@ -73,15 +73,15 @@ class FancyConsoleReporter(
}).show else ""
/** Prints the message with the given position indication. */
- override def printMessageAndPos(msg: String, pos: SourcePosition, kind: String = "")(implicit ctx: Context): Unit = {
+ override def printMessageAndPos(msg: Message, pos: SourcePosition, kind: String)(implicit ctx: Context): Unit = {
printMessage(posStr(pos, kind))
if (pos.exists) {
val (src, offset) = sourceLine(pos)
val marker = columnMarker(pos, offset)
- val err = errorMsg(pos, msg, offset)
+ val err = errorMsg(pos, msg.msg, offset)
printMessage(List(src, marker, err).mkString("\n"))
- } else printMessage(msg)
+ } else printMessage(msg.msg)
}
override def printExplanation(m: Message)(implicit ctx: Context): Unit = {
@@ -90,16 +90,4 @@ class FancyConsoleReporter(
|${Blue("===========")}""".stripMargin)
printMessage(m.explanation)
}
-
-
- //override def summary(implicit ctx: Context): String = {
- // val b = new mutable.ListBuffer[String]
- // if (warningCount > 0)
- // b += countString(warningCount, Yellow("warning").show) + " found"
- // if (errorCount > 0)
- // b += countString(errorCount, Red("error").show) + " found"
- // for ((settingName, count) <- unreportedWarnings)
- // b += s"there were $count ${settingName.tail} ${Yellow("warning(s)").show}; re-run with $settingName for details"
- // b.mkString("\n")
- //}
}
diff --git a/src/dotty/tools/dotc/reporting/HideNonSensicalMessages.scala b/src/dotty/tools/dotc/reporting/HideNonSensicalMessages.scala
index 140777275..ba1ab9b33 100644
--- a/src/dotty/tools/dotc/reporting/HideNonSensicalMessages.scala
+++ b/src/dotty/tools/dotc/reporting/HideNonSensicalMessages.scala
@@ -3,7 +3,7 @@ package dotc
package reporting
import core.Contexts.Context
-import diagnostic.Message
+import diagnostic.MessageContainer
/**
* This trait implements `isHidden` so that we avoid reporting non-sensical messages.
@@ -12,7 +12,7 @@ trait HideNonSensicalMessages extends Reporter {
/** Hides non-sensical messages, unless we haven't reported any error yet or
* `-Yshow-suppressed-errors` is set.
*/
- override def isHidden(m: Message)(implicit ctx: Context): Boolean =
+ override def isHidden(m: MessageContainer)(implicit ctx: Context): Boolean =
super.isHidden(m) || {
m.isNonSensical &&
hasErrors && // if there are no errors yet, report even if diagnostic is non-sensical
diff --git a/src/dotty/tools/dotc/reporting/Reporter.scala b/src/dotty/tools/dotc/reporting/Reporter.scala
index 538464daa..b969fa878 100644
--- a/src/dotty/tools/dotc/reporting/Reporter.scala
+++ b/src/dotty/tools/dotc/reporting/Reporter.scala
@@ -12,13 +12,13 @@ import core.Mode
import dotty.tools.dotc.core.Symbols.Symbol
import diagnostic.messages._
import diagnostic._
-import MessageCreator._
+import Message._
object Reporter {
/** Convert a SimpleReporter into a real Reporter */
def fromSimpleReporter(simple: interfaces.SimpleReporter): Reporter =
new Reporter with UniqueMessagePositions with HideNonSensicalMessages {
- override def doReport(m: Message)(implicit ctx: Context): Unit = m match {
+ override def doReport(m: MessageContainer)(implicit ctx: Context): Unit = m match {
case m: ConditionalWarning if !m.enablingOption.value =>
case _ =>
simple.report(m)
@@ -37,17 +37,17 @@ trait Reporting { this: Context =>
def echo(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
reporter.report(new Info(msg, pos, "Info"))
- def deprecationWarning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
- reporter.report(new DeprecationWarning(msg, pos, "Deprecation Warning"))
+ def deprecationWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
+ reporter.report(msg.deprecationWarning(pos))
- def migrationWarning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
- reporter.report(new MigrationWarning(msg, pos, "Migration Warning"))
+ def migrationWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
+ reporter.report(msg.migrationWarning(pos))
- def uncheckedWarning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
- reporter.report(new UncheckedWarning(msg, pos, "Unchecked Warning"))
+ def uncheckedWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
+ reporter.report(msg.uncheckedWarning(pos))
- def featureWarning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
- reporter.report(new FeatureWarning(msg, pos, "Feature Warning"))
+ def featureWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
+ reporter.report(msg.featureWarning(pos))
def featureWarning(feature: String, featureDescription: String, isScala2Feature: Boolean,
featureUseSite: Symbol, required: Boolean, pos: SourcePosition): Unit = {
@@ -72,32 +72,24 @@ trait Reporting { this: Context =>
else reporter.report(new FeatureWarning(msg, pos))
}
- def warning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
- reporter.report(new Warning(msg, pos))
-
- def explainWarning(msg: => MessageCreator, pos: SourcePosition = NoSourcePosition): Unit =
+ def warning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
reporter.report(msg.warning(pos))
- def strictWarning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
+ def strictWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
if (this.settings.strict.value) error(msg, pos)
- else warning(msg + "\n(This would be an error under strict mode)", pos)
-
- def error(msg: => String, pos: SourcePosition = NoSourcePosition): Unit = {
- // println("*** ERROR: " + msg) // !!! DEBUG
- reporter.report(new Error(msg, pos))
- }
+ else warning(msg.mapMsg(_ + "\n(This would be an error under strict mode)"), pos)
- def explainError(msg: => MessageCreator, pos: SourcePosition = NoSourcePosition): Unit =
+ def error(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
reporter.report(msg.error(pos))
- def errorOrMigrationWarning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
+ def errorOrMigrationWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
if (ctx.scala2Mode) migrationWarning(msg, pos) else error(msg, pos)
- def restrictionError(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
- error(s"Implementation restriction: $msg", pos)
+ def restrictionError(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit =
+ error(msg.mapMsg(m => s"Implementation restriction: $m"), pos)
- def incompleteInputError(msg: String, pos: SourcePosition = NoSourcePosition)(implicit ctx: Context): Unit =
- reporter.incomplete(new Error(msg, pos))(ctx)
+ def incompleteInputError(msg: Message, pos: SourcePosition = NoSourcePosition)(implicit ctx: Context): Unit =
+ reporter.incomplete(msg.error(pos))(ctx)
/** Log msg if settings.log contains the current phase.
* See [[config.CompilerCommand#explainAdvanced]] for the exact meaning of
@@ -184,7 +176,7 @@ trait Reporting { this: Context =>
abstract class Reporter extends interfaces.ReporterResult {
/** Report a diagnostic */
- def doReport(d: Message)(implicit ctx: Context): Unit
+ def doReport(d: MessageContainer)(implicit ctx: Context): Unit
/** Whether very long lines can be truncated. This exists so important
* debugging information (like printing the classpath) is not rendered
@@ -199,7 +191,7 @@ abstract class Reporter extends interfaces.ReporterResult {
finally _truncationOK = saved
}
- type ErrorHandler = Message => Context => Unit
+ type ErrorHandler = MessageContainer => Context => Unit
private var incompleteHandler: ErrorHandler = d => c => report(d)(c)
def withIncompleteHandler[T](handler: ErrorHandler)(op: => T): T = {
val saved = incompleteHandler
@@ -228,7 +220,7 @@ abstract class Reporter extends interfaces.ReporterResult {
override def default(key: String) = 0
}
- def report(d: Message)(implicit ctx: Context): Unit =
+ def report(d: MessageContainer)(implicit ctx: Context): Unit =
if (!isHidden(d)) {
doReport(d)(ctx.addMode(Mode.Printing))
d match {
@@ -242,12 +234,11 @@ abstract class Reporter extends interfaces.ReporterResult {
}
}
- def incomplete(d: Message)(implicit ctx: Context): Unit =
+ def incomplete(d: MessageContainer)(implicit ctx: Context): Unit =
incompleteHandler(d)(ctx)
-
/** Summary of warnings and errors */
- def summary/*(implicit ctx: Context)*/: String = {
+ def summary: String = {
val b = new mutable.ListBuffer[String]
if (warningCount > 0)
b += countString(warningCount, "warning") + " found"
@@ -275,7 +266,7 @@ abstract class Reporter extends interfaces.ReporterResult {
}
/** Should this diagnostic not be reported at all? */
- def isHidden(m: Message)(implicit ctx: Context): Boolean = ctx.mode.is(Mode.Printing)
+ def isHidden(m: MessageContainer)(implicit ctx: Context): Boolean = ctx.mode.is(Mode.Printing)
/** Does this reporter contain not yet reported errors or warnings? */
def hasPending: Boolean = false
diff --git a/src/dotty/tools/dotc/reporting/StoreReporter.scala b/src/dotty/tools/dotc/reporting/StoreReporter.scala
index fa4fd991f..3744a35dd 100644
--- a/src/dotty/tools/dotc/reporting/StoreReporter.scala
+++ b/src/dotty/tools/dotc/reporting/StoreReporter.scala
@@ -6,7 +6,7 @@ import core.Contexts.Context
import collection.mutable
import Reporter.{Error, Warning}
import config.Printers.typr
-import diagnostic.Message
+import diagnostic.MessageContainer
import diagnostic.messages._
/**
@@ -14,9 +14,9 @@ import diagnostic.messages._
*/
class StoreReporter(outer: Reporter) extends Reporter {
- private var infos: mutable.ListBuffer[Message] = null
+ private var infos: mutable.ListBuffer[MessageContainer] = null
- def doReport(m: Message)(implicit ctx: Context): Unit = {
+ def doReport(m: MessageContainer)(implicit ctx: Context): Unit = {
typr.println(s">>>> StoredError: ${m.message}") // !!! DEBUG
if (infos == null) infos = new mutable.ListBuffer
infos += m
diff --git a/src/dotty/tools/dotc/reporting/ThrowingReporter.scala b/src/dotty/tools/dotc/reporting/ThrowingReporter.scala
index f4e3b472d..d8e03ab66 100644
--- a/src/dotty/tools/dotc/reporting/ThrowingReporter.scala
+++ b/src/dotty/tools/dotc/reporting/ThrowingReporter.scala
@@ -4,7 +4,7 @@ package reporting
import core.Contexts.Context
import collection.mutable
-import diagnostic.Message
+import diagnostic.MessageContainer
import diagnostic.messages.Error
import Reporter._
@@ -13,7 +13,7 @@ import Reporter._
* info to the underlying reporter.
*/
class ThrowingReporter(reportInfo: Reporter) extends Reporter {
- def doReport(m: Message)(implicit ctx: Context): Unit = m match {
+ def doReport(m: MessageContainer)(implicit ctx: Context): Unit = m match {
case _: Error => throw m
case _ => reportInfo.doReport(m)
}
diff --git a/src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala b/src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala
index 1ef8b3447..c5ff8cb6b 100644
--- a/src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala
+++ b/src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala
@@ -5,7 +5,7 @@ package reporting
import scala.collection.mutable
import util.{SourcePosition, SourceFile}
import core.Contexts.Context
-import diagnostic.Message
+import diagnostic.MessageContainer
/**
* This trait implements `isHidden` so that multiple messages per position
@@ -18,7 +18,7 @@ trait UniqueMessagePositions extends Reporter {
/** Logs a position and returns true if it was already logged.
* @note Two positions are considered identical for logging if they have the same point.
*/
- override def isHidden(m: Message)(implicit ctx: Context): Boolean =
+ override def isHidden(m: MessageContainer)(implicit ctx: Context): Boolean =
super.isHidden(m) || {
m.pos.exists && {
positions get (ctx.source, m.pos.point) match {
diff --git a/src/dotty/tools/dotc/reporting/diagnostic/Message.scala b/src/dotty/tools/dotc/reporting/diagnostic/Message.scala
index b3c19820f..443dc4de8 100644
--- a/src/dotty/tools/dotc/reporting/diagnostic/Message.scala
+++ b/src/dotty/tools/dotc/reporting/diagnostic/Message.scala
@@ -3,66 +3,60 @@ package dotc
package reporting
package diagnostic
-import util.SourcePosition
+import util.{SourcePosition, NoSourcePosition}
import core.Contexts.Context
-import java.util.Optional
-
object Message {
- val nonSensicalStartTag = "<nonsensical>"
- val nonSensicalEndTag = "</nonsensical>"
-
- implicit class MessageContext(val c: Context) extends AnyVal {
- def shouldExplain(msg: Message): Boolean = {
- implicit val ctx: Context = c
- msg.explanation match {
- case "" => false
- case _ => ctx.settings.explain.value
- }
- }
- }
+ implicit def toNoExplanation(str: String): Message =
+ new NoExplanation(str)
}
-class Message(
- msgFn: => String,
- val pos: SourcePosition,
- val level: Int,
- val kind: String,
- val explanation: String
-) extends Exception with interfaces.Diagnostic {
- import Message._
- private var myMsg: String = null
- private var myIsNonSensical: Boolean = false
+abstract class Message(val errorId: String) { self =>
+ import messages._
+
+ def msg: String
+ def kind: String
+ def explanation: String
- override def position: Optional[interfaces.SourcePosition] =
- if (pos.exists && pos.source.exists) Optional.of(pos) else Optional.empty()
+ def container(c: String) =
+ if (kind == "") c
+ else s"$kind $c"
- /** The message to report */
- def message: String = {
- if (myMsg == null) {
- myMsg = msgFn
- if (myMsg.contains(nonSensicalStartTag)) {
- myIsNonSensical = true
- // myMsg might be composed of several d"..." invocations -> nested
- // nonsensical tags possible
- myMsg =
- myMsg
- .replaceAllLiterally(nonSensicalStartTag, "")
- .replaceAllLiterally(nonSensicalEndTag, "")
- }
- }
- myMsg
+ def mapMsg(f: String => String) = new Message(errorId) {
+ val msg = f(self.msg)
+ val kind = self.kind
+ val explanation = self.explanation
}
- /** A message is non-sensical if it contains references to <nonsensical>
- * tags. Such tags are inserted by the error diagnostic framework if a
- * message contains references to internally generated error types. Normally
- * we want to suppress error messages referring to types like this because
- * they look weird and are normally follow-up errors to something that was
- * diagnosed before.
- */
- def isNonSensical = { message; myIsNonSensical }
+ def error(pos: SourcePosition) =
+ new Error(self, pos, container("Error"), explanation)
+
+ def warning(pos: SourcePosition) =
+ new Warning(self, pos, container("Warning"), explanation)
+
+ def info(pos: SourcePosition) =
+ new Info(self, pos, container("Info"), explanation)
+
+ def featureWarning(pos: SourcePosition) =
+ new FeatureWarning(self, pos, container("Feature Warning"), explanation)
+
+ def uncheckedWarning(pos: SourcePosition) =
+ new UncheckedWarning(self, pos, container("Unchecked Warning"), explanation)
+
+ def deprecationWarning(pos: SourcePosition) =
+ new DeprecationWarning(self, pos, container("Deprecation Warning"), explanation)
+
+ def migrationWarning(pos: SourcePosition) =
+ new MigrationWarning(self, pos, container("Migration Warning"), explanation)
+}
+
+class NoExplanation(val msg: String) extends Message("") {
+ val explanation = ""
+ val kind = ""
+}
- override def toString = s"$getClass at $pos: $message"
- override def getMessage() = message
+object NoExplanation {
+ def unapply(m: Message): Option[Message] =
+ if (m.explanation == "") Some(m)
+ else None
}
diff --git a/src/dotty/tools/dotc/reporting/diagnostic/MessageContainer.scala b/src/dotty/tools/dotc/reporting/diagnostic/MessageContainer.scala
new file mode 100644
index 000000000..d15c1d2f1
--- /dev/null
+++ b/src/dotty/tools/dotc/reporting/diagnostic/MessageContainer.scala
@@ -0,0 +1,76 @@
+package dotty.tools
+package dotc
+package reporting
+package diagnostic
+
+import util.SourcePosition
+import core.Contexts.Context
+
+import java.util.Optional
+
+object MessageContainer {
+ val nonSensicalStartTag = "<nonsensical>"
+ val nonSensicalEndTag = "</nonsensical>"
+
+ implicit class MessageContext(val c: Context) extends AnyVal {
+ def shouldExplain(cont: MessageContainer): Boolean = {
+ implicit val ctx: Context = c
+ cont.explanation match {
+ case "" => false
+ case _ => ctx.settings.explain.value
+ }
+ }
+ }
+}
+
+class MessageContainer(
+ msgFn: => Message,
+ val pos: SourcePosition,
+ val level: Int,
+ val kind: String,
+ val explanation: String
+) extends Exception with interfaces.Diagnostic {
+ import MessageContainer._
+ private var myMsg: String = null
+ private var myIsNonSensical: Boolean = false
+ private var myContained: Message = null
+
+ override def position: Optional[interfaces.SourcePosition] =
+ if (pos.exists && pos.source.exists) Optional.of(pos) else Optional.empty()
+
+ /** The message to report */
+ def message: String = {
+ if (myMsg == null) {
+ myMsg = msgFn.msg
+ if (myMsg.contains(nonSensicalStartTag)) {
+ myIsNonSensical = true
+ // myMsg might be composed of several d"..." invocations -> nested
+ // nonsensical tags possible
+ myMsg =
+ myMsg
+ .replaceAllLiterally(nonSensicalStartTag, "")
+ .replaceAllLiterally(nonSensicalEndTag, "")
+ }
+ }
+ myMsg
+ }
+
+ def contained: Message = {
+ if (myContained == null)
+ myContained = msgFn
+
+ myContained
+ }
+
+ /** A message is non-sensical if it contains references to <nonsensical>
+ * tags. Such tags are inserted by the error diagnostic framework if a
+ * message contains references to internally generated error types. Normally
+ * we want to suppress error messages referring to types like this because
+ * they look weird and are normally follow-up errors to something that was
+ * diagnosed before.
+ */
+ def isNonSensical = { message; myIsNonSensical }
+
+ override def toString = s"$getClass at $pos: ${message}"
+ override def getMessage() = message
+}
diff --git a/src/dotty/tools/dotc/reporting/diagnostic/MessageCreator.scala b/src/dotty/tools/dotc/reporting/diagnostic/MessageCreator.scala
deleted file mode 100644
index 99ccca4cc..000000000
--- a/src/dotty/tools/dotc/reporting/diagnostic/MessageCreator.scala
+++ /dev/null
@@ -1,52 +0,0 @@
-package dotty.tools
-package dotc
-package reporting
-package diagnostic
-
-import util.{SourcePosition, NoSourcePosition}
-import core.Contexts.Context
-
-object MessageCreator {
- implicit def toNoExplanation(str: String): MessageCreator =
- new NoExplanation(str)
-}
-
-trait MessageCreator {
- import messages._
-
- def msg: String
- def kind: String
- def explanation: String
-
- def error(pos: SourcePosition) =
- new Error(msg, pos, kind, explanation)
-
- def warning(pos: SourcePosition) =
- new Warning(msg, pos, kind, explanation)
-
- def info(pos: SourcePosition) =
- new Info(msg, pos, kind, explanation)
-
- def featureWarnign(pos: SourcePosition) =
- new FeatureWarning(msg, pos, kind, explanation)
-
- def uncheckedWarning(pos: SourcePosition) =
- new UncheckedWarning(msg, pos, kind, explanation)
-
- def deprecationWarning(pos: SourcePosition) =
- new DeprecationWarning(msg, pos, kind, explanation)
-
- def migrationWarning(pos: SourcePosition) =
- new MigrationWarning(msg, pos, kind, explanation)
-}
-
-class NoExplanation(val msg: String) extends MessageCreator {
- val explanation = ""
- val kind = ""
-}
-
-object NoExplanation {
- def unapply(m: MessageCreator): Option[MessageCreator] =
- if (m.explanation == "") Some(m)
- else None
-}
diff --git a/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
index 382e9a295..4b7502287 100644
--- a/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
+++ b/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
@@ -3,37 +3,40 @@ package dotc
package reporting
package diagnostic
-import dotc.core.Contexts.Context
+import dotc.core._
+import Contexts.Context, Decorators._, Symbols._, Names._
import util.{SourceFile, NoSource}
import util.{SourcePosition, NoSourcePosition}
import config.Settings.Setting
import interfaces.Diagnostic.{ERROR, WARNING, INFO}
+import dotc.printing.SyntaxHighlighting._
object messages {
+ /** Concrete messages to be consumed by the reporter ---------------------- */
class Error(
- msgFn: => String,
+ msgFn: => Message,
pos: SourcePosition,
- kind: String = "Error",
+ kind: String,
explanation: String = ""
- ) extends Message(msgFn, pos, ERROR, kind, explanation)
+ ) extends MessageContainer(msgFn, pos, ERROR, kind, explanation)
class Warning(
- msgFn: => String,
+ msgFn: => Message,
pos: SourcePosition,
- kind: String = "Warning",
+ kind: String,
explanation: String = ""
- ) extends Message(msgFn, pos, WARNING, kind, explanation)
+ ) extends MessageContainer(msgFn, pos, WARNING, kind, explanation)
class Info(
- msgFn: => String,
+ msgFn: => Message,
pos: SourcePosition,
- kind: String = "Info",
+ kind: String,
explanation: String = ""
- ) extends Message(msgFn, pos, INFO, kind, explanation)
+ ) extends MessageContainer(msgFn, pos, INFO, kind, explanation)
abstract class ConditionalWarning(
- msgFn: => String,
+ msgFn: => Message,
pos: SourcePosition,
kind: String,
explanation: String = ""
@@ -42,7 +45,7 @@ object messages {
}
class FeatureWarning(
- msgFn: => String,
+ msgFn: => Message,
pos: SourcePosition,
kind: String = "Feature Warning",
explanation: String = ""
@@ -51,7 +54,7 @@ object messages {
}
class UncheckedWarning(
- msgFn: => String,
+ msgFn: => Message,
pos: SourcePosition,
kind: String = "Unchecked Warning",
explanation: String = ""
@@ -60,7 +63,7 @@ object messages {
}
class DeprecationWarning(
- msgFn: => String,
+ msgFn: => Message,
pos: SourcePosition,
kind: String = "Deprecation Warning",
explanation: String = ""
@@ -69,7 +72,7 @@ object messages {
}
class MigrationWarning(
- msgFn: => String,
+ msgFn: => Message,
pos: SourcePosition,
kind: String = "Migration Warning",
explanation: String = ""
@@ -77,4 +80,105 @@ object messages {
def enablingOption(implicit ctx: Context) = ctx.settings.migration
}
+ /** Messages ----------------------------------------------------------------
+ *
+ * The role of messages is to provide the necessary details for a simple to
+ * understand diagnostic event. Each message can be turned into a message
+ * container (one of the above) by calling the appropriate method on them.
+ * For instance:
+ *
+ * ```scala
+ * EmptyCatchBlock(tree).error // res: Error
+ * EmptyCatchBlock(tree).warning // res: Warning
+ * ```
+ */
+ import dotc.ast.Trees._
+ import dotc.ast.untpd
+
+ /** Syntax Errors --------------------------------------------------------- */
+ abstract class EmptyCatchOrFinallyBlock(tryBody: untpd.Tree, errNo: String)(implicit ctx: Context)
+ extends Message(errNo) {
+ val explanation = {
+ val tryString = tryBody match {
+ case Block(Nil, untpd.EmptyTree) => "{}"
+ case _ => tryBody.show
+ }
+
+ val code1 =
+ s"""|try $tryString catch {
+ | case t: Throwable => ???
+ |}""".stripMargin
+
+ val code2 =
+ s"""|try $tryString finally {
+ | // perform your cleanup here!
+ |}""".stripMargin
+
+ hl"""|A ${"try"} expression should be followed by some mechanism to handle any exceptions
+ |thrown. Typically a ${"catch"} expression follows the ${"try"} and pattern matches
+ |on any expected exceptions. For example:
+ |
+ |$code1
+ |
+ |It is also possible to follow a ${"try"} immediately by a ${"finally"} - letting the
+ |exception propagate - but still allowing for some clean up in ${"finally"}:
+ |
+ |$code2""".stripMargin
+ }
+ }
+
+ class EmptyCatchBlock(tryBody: untpd.Tree)(implicit ctx: Context)
+ extends EmptyCatchOrFinallyBlock(tryBody, "E001") {
+ val kind = "Syntax"
+ val msg =
+ hl"""|The ${"catch"} block does not contain a valid expression, try
+ |adding a case like - `${"case e: Exception =>"}` to the block""".stripMargin
+ }
+
+ case class EmptyCatchAndFinallyBlock(tryBody: untpd.Tree)(implicit ctx: Context)
+ extends EmptyCatchOrFinallyBlock(tryBody, "E002") {
+ val kind = "Syntax"
+ val msg =
+ hl"""|A ${"try"} without ${"catch"} or ${"finally"} is equivalent to putting
+ |its body in a block; no exceptions are handled.""".stripMargin
+ }
+
+ /** Type Errors ----------------------------------------------------------- */
+ class DuplicateBind(bind: untpd.Bind, tree: untpd.CaseDef)(implicit ctx: Context)
+ extends Message("E003") {
+ val kind = "Naming"
+ val msg = em"duplicate pattern variable: `${bind.name}`"
+
+ val explanation = {
+ val pat = tree.pat.show
+ val guard = tree.guard match {
+ case untpd.EmptyTree => ""
+ case guard => s"if ${guard.show}"
+ }
+
+ val body = tree.body match {
+ case Block(Nil, untpd.EmptyTree) => ""
+ case body => s" ${body.show}"
+ }
+
+ val caseDef = s"case $pat$guard => $body"
+
+ hl"""|For each ${"case"} bound variable names have to be unique. In:
+ |
+ |$caseDef
+ |
+ |`${bind.name}` is not unique. Rename one of the bound variables!""".stripMargin
+ }
+ }
+
+ class MissingIdent(tree: untpd.Ident, treeKind: String, name: Name)(implicit ctx: Context)
+ extends Message("E004") {
+ val kind = "Missing identifier"
+ val msg = em"not found: $treeKind$name"
+
+ val explanation = {
+ hl"""|An identifier for `${name.show}` is missing. This means that something
+ |has either been misspelt or you're forgetting an import""".stripMargin
+ }
+ }
}
diff --git a/src/dotty/tools/dotc/reporting/diagnostic/parser.scala b/src/dotty/tools/dotc/reporting/diagnostic/parser.scala
deleted file mode 100644
index b73e353eb..000000000
--- a/src/dotty/tools/dotc/reporting/diagnostic/parser.scala
+++ /dev/null
@@ -1,8 +0,0 @@
-package dotty.tools
-package dotc
-package reporting
-package diagnostic
-
-object parser {
-
-}
diff --git a/src/dotty/tools/dotc/reporting/diagnostic/syntax.scala b/src/dotty/tools/dotc/reporting/diagnostic/syntax.scala
deleted file mode 100644
index b9a662c7d..000000000
--- a/src/dotty/tools/dotc/reporting/diagnostic/syntax.scala
+++ /dev/null
@@ -1,61 +0,0 @@
-package dotty.tools
-package dotc
-package reporting
-package diagnostic
-
-import dotc.core._
-import Contexts.Context, Decorators._, Symbols._
-import dotc.printing.SyntaxHighlighting._
-import util.{SourcePosition, NoSourcePosition}
-
-object syntax {
- import dotc.ast.Trees._
- import dotc.ast.untpd
-
- abstract class EmptyCatchOrFinallyBlock(tryBody: untpd.Tree)(implicit ctx: Context) extends MessageCreator {
- val explanation = {
- val tryString = tryBody match {
- case Block(Nil, untpd.EmptyTree) => "{}"
- case _ => tryBody.show
- }
-
- val code1 =
- s"""|try $tryString catch {
- | case t: Throwable => ???
- |}""".stripMargin
-
- val code2 =
- s"""|try $tryString finally {
- | // perform your cleanup here!
- |}""".stripMargin
-
- hl"""|A ${"try"} expression should be followed by some mechanism to handle any exceptions
- |thrown. Typically a ${"catch"} expression follows the ${"try"} and pattern matches
- |on any expected exceptions. For example:
- |
- |$code1
- |
- |It is also possible to follow a ${"try"} immediately by a ${"finally"} - letting the
- |exception propagate - but still allowing for some clean up in ${"finally"}:
- |
- |$code2
- """.stripMargin
- }
- }
-
- class EmptyCatchBlock(tryBody: untpd.Tree)(implicit ctx: Context)
- extends EmptyCatchOrFinallyBlock(tryBody) {
- val kind = "Syntax"
- val msg =
- hl"""|The ${"catch"} block does not contain a valid expression, try
- |adding a case like - `${"case e: Exception =>"}` to the block""".stripMargin
- }
-
- case class EmptyCatchAndFinallyBlock(tryBody: untpd.Tree)(implicit ctx: Context)
- extends EmptyCatchOrFinallyBlock(tryBody) {
- val kind = "Syntax"
- val msg =
- hl"""|A ${"try"} without ${"catch"} or ${"finally"} is equivalent to putting
- |its body in a block; no exceptions are handled.""".stripMargin
- }
-}
diff --git a/src/dotty/tools/dotc/reporting/diagnostic/tpe.scala b/src/dotty/tools/dotc/reporting/diagnostic/tpe.scala
deleted file mode 100644
index ee221f80d..000000000
--- a/src/dotty/tools/dotc/reporting/diagnostic/tpe.scala
+++ /dev/null
@@ -1,55 +0,0 @@
-package dotty.tools
-package dotc
-package reporting
-package diagnostic
-
-import dotc.core._
-import Contexts.Context, Decorators._, Symbols._, Names._
-import dotc.printing.SyntaxHighlighting._
-import util.{SourcePosition, NoSourcePosition}
-
-object tpe {
- import dotc.ast.Trees._
- import dotc.ast.untpd
-
- class DuplicateBind(
- bind: untpd.Bind,
- tree: untpd.CaseDef
- )(implicit ctx: Context) extends MessageCreator {
- val kind = "Naming"
-
- val msg =
- em"duplicate pattern variable: `${bind.name}`"
-
- val explanation = {
- val pat = tree.pat.show
- val guard = tree.guard match {
- case untpd.EmptyTree => ""
- case guard => s"if ${guard.show}"
- }
-
- val body = tree.body match {
- case Block(Nil, untpd.EmptyTree) => ""
- case body => s" ${body.show}"
- }
-
- val caseDef = s"case $pat$guard => $body"
-
- hl"""|For each ${"case"} bound variable names have to be unique. In:
- |
- |$caseDef
- |
- |`${bind.name}` is not unique. Rename one of the bound variables!""".stripMargin
- }
- }
-
- class MissingIdent(tree: untpd.Ident, treeKind: String, name: Name)(implicit ctx: Context) extends MessageCreator {
- val kind = "Missing identifier"
- val msg = em"not found: $treeKind$name"
-
- val explanation = {
- hl"""|An identifier for `${name.show}` is missing. This means that something
- |has either been misspelt or you're forgetting an import""".stripMargin
- }
- }
-}
diff --git a/src/dotty/tools/dotc/typer/ErrorReporting.scala b/src/dotty/tools/dotc/typer/ErrorReporting.scala
index 43c093510..8b740c3dc 100644
--- a/src/dotty/tools/dotc/typer/ErrorReporting.scala
+++ b/src/dotty/tools/dotc/typer/ErrorReporting.scala
@@ -11,6 +11,7 @@ import util.Positions._
import printing.{Showable, RefinedPrinter}
import scala.collection.mutable
import java.util.regex.Matcher.quoteReplacement
+import reporting.diagnostic.Message
object ErrorReporting {
@@ -19,7 +20,7 @@ object ErrorReporting {
def errorTree(tree: untpd.Tree, msg: => String)(implicit ctx: Context): tpd.Tree =
tree withType errorType(msg, tree.pos)
- def errorType(msg: => String, pos: Position)(implicit ctx: Context): ErrorType = {
+ def errorType(msg: => Message, pos: Position)(implicit ctx: Context): ErrorType = {
ctx.error(msg, pos)
ErrorType
}
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index b29254b89..0cc04613a 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -65,8 +65,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
import tpd.{cpy => _, _}
import untpd.cpy
import Dynamic.isDynamicMethod
- import reporting.diagnostic.MessageCreator
- import reporting.diagnostic.tpe._
+ import reporting.diagnostic.Message
+ import reporting.diagnostic.messages._
/** A temporary data item valid for a single typed ident:
* The set of all root import symbols that have been
@@ -99,7 +99,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
/** Method is necessary because error messages need to bind to
* to typedIdent's context which is lost in nested calls to findRef
*/
- def error(msg: => MessageCreator, pos: Position) = ctx.explainError(msg, pos)
+ def error(msg: => Message, pos: Position) = ctx.error(msg, pos)
/** Is this import a root import that has been shadowed by an explicit
* import in the same program?
@@ -772,10 +772,10 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
TypeTree(pt)
case _ =>
if (!mt.isDependent) EmptyTree
- else throw new Error(i"internal error: cannot turn dependent method type $mt into closure, position = ${tree.pos}, raw type = ${mt.toString}") // !!! DEBUG. Eventually, convert to an error?
+ else throw new java.lang.Error(i"internal error: cannot turn dependent method type $mt into closure, position = ${tree.pos}, raw type = ${mt.toString}") // !!! DEBUG. Eventually, convert to an error?
}
case tp =>
- throw new Error(i"internal error: closing over non-method $tp, pos = ${tree.pos}")
+ throw new java.lang.Error(i"internal error: closing over non-method $tp, pos = ${tree.pos}")
}
else typed(tree.tpt)
//println(i"typing closure $tree : ${meth1.tpe.widen}")
@@ -848,7 +848,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
super.transform(trt.withType(elimWildcardSym(trt.tpe))) match {
case b: Bind =>
if (ctx.scope.lookup(b.name) == NoSymbol) ctx.enter(b.symbol)
- else ctx.explainError(new DuplicateBind(b, tree), b.pos)
+ else ctx.error(new DuplicateBind(b, tree), b.pos)
b.symbol.info = elimWildcardSym(b.symbol.info)
b
case t => t
diff --git a/test/test/CompilerTest.scala b/test/test/CompilerTest.scala
index 942948f08..8cf6b2feb 100644
--- a/test/test/CompilerTest.scala
+++ b/test/test/CompilerTest.scala
@@ -5,7 +5,7 @@ import dotty.partest.DPConfig
import dotty.tools.dotc.{Main, Bench, Driver}
import dotty.tools.dotc.interfaces.Diagnostic.ERROR
import dotty.tools.dotc.reporting._
-import diagnostic.Message
+import diagnostic.MessageContainer
import dotty.tools.dotc.util.SourcePosition
import dotty.tools.dotc.config.CompilerCommand
import dotty.tools.io.PlainFile
@@ -238,7 +238,7 @@ abstract class CompilerTest {
val storeReporter = new Reporter with UniqueMessagePositions with HideNonSensicalMessages {
private val consoleReporter = new ConsoleReporter()
private val innerStoreReporter = new StoreReporter(consoleReporter)
- def doReport(m: Message)(implicit ctx: Context): Unit = {
+ def doReport(m: MessageContainer)(implicit ctx: Context): Unit = {
if (m.level == ERROR) {
innerStoreReporter.flush()
consoleReporter.doReport(m)
diff --git a/test/test/OtherEntryPointsTest.scala b/test/test/OtherEntryPointsTest.scala
index 824034055..abaa043c0 100644
--- a/test/test/OtherEntryPointsTest.scala
+++ b/test/test/OtherEntryPointsTest.scala
@@ -5,7 +5,7 @@ import org.junit.Assert._
import dotty.tools.dotc.Main
import dotty.tools.dotc.interfaces.{CompilerCallback, SourceFile}
import dotty.tools.dotc.reporting._
-import dotty.tools.dotc.reporting.diagnostic.Message
+import dotty.tools.dotc.reporting.diagnostic.MessageContainer
import dotty.tools.dotc.core.Contexts._
import java.io.File
import scala.collection.mutable.ListBuffer
@@ -51,7 +51,7 @@ class OtherEntryPointsTest {
private class CustomReporter extends Reporter
with UniqueMessagePositions
with HideNonSensicalMessages {
- def doReport(m: Message)(implicit ctx: Context): Unit = {
+ def doReport(m: MessageContainer)(implicit ctx: Context): Unit = {
}
}