diff options
author | Felix Mulder <felix.mulder@gmail.com> | 2016-09-19 19:26:16 +0200 |
---|---|---|
committer | Felix Mulder <felix.mulder@gmail.com> | 2016-10-10 13:25:34 +0200 |
commit | e24289ac19a21c6b3794d02e8fe42766224f173c (patch) | |
tree | 94dae1ffb70995c2851550952f0f17a953936711 /src/dotty/tools/dotc/reporting | |
parent | 628b7f317756ce2c366359a7399b8dda9d0190b7 (diff) | |
download | dotty-e24289ac19a21c6b3794d02e8fe42766224f173c.tar.gz dotty-e24289ac19a21c6b3794d02e8fe42766224f173c.tar.bz2 dotty-e24289ac19a21c6b3794d02e8fe42766224f173c.zip |
Make relevant parts of compiler conform to new error handling
Diffstat (limited to 'src/dotty/tools/dotc/reporting')
14 files changed, 291 insertions, 313 deletions
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 - } - } -} |