aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFelix Mulder <felix.mulder@gmail.com>2016-09-14 17:52:11 +0200
committerFelix Mulder <felix.mulder@gmail.com>2016-10-10 13:25:32 +0200
commit281dbb553b20ca16dd9e749acdfe39129e20fa28 (patch)
treea21e8339e443fddd3ff3b9f80435d59f92708d6e /src
parent3bb27d468cb06894c72b9f2cfba854ad49d68a74 (diff)
downloaddotty-281dbb553b20ca16dd9e749acdfe39129e20fa28.tar.gz
dotty-281dbb553b20ca16dd9e749acdfe39129e20fa28.tar.bz2
dotty-281dbb553b20ca16dd9e749acdfe39129e20fa28.zip
Add initial structure for improved explanations of error messages
Diffstat (limited to 'src')
-rw-r--r--src/dotty/tools/dotc/config/ScalaSettings.scala1
-rw-r--r--src/dotty/tools/dotc/parsing/Parsers.scala23
-rw-r--r--src/dotty/tools/dotc/reporting/Examples.scala67
-rw-r--r--src/dotty/tools/dotc/reporting/Reporter.scala13
4 files changed, 91 insertions, 13 deletions
diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala
index ff17a9939..a4daefcba 100644
--- a/src/dotty/tools/dotc/config/ScalaSettings.scala
+++ b/src/dotty/tools/dotc/config/ScalaSettings.scala
@@ -23,6 +23,7 @@ class ScalaSettings extends Settings.SettingGroup {
val migration = BooleanSetting("-migration", "Emit warning and location for migration issues from Scala 2.")
val encoding = StringSetting("-encoding", "encoding", "Specify character encoding used by source files.", Properties.sourceEncoding)
val explaintypes = BooleanSetting("-explaintypes", "Explain type errors in more detail.")
+ val explainerrors = BooleanSetting("-explain", "Explain errors in more detail.")
val feature = BooleanSetting("-feature", "Emit warning and location for usages of features that should be imported explicitly.")
val g = ChoiceSetting("-g", "level", "Set level of generated debugging info.", List("none", "source", "line", "vars", "notailcalls"), "vars")
val help = BooleanSetting("-help", "Print a synopsis of standard options")
diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala
index 9aadf0c61..e4378d82b 100644
--- a/src/dotty/tools/dotc/parsing/Parsers.scala
+++ b/src/dotty/tools/dotc/parsing/Parsers.scala
@@ -17,6 +17,7 @@ import ast.Trees._
import Decorators._
import StdNames._
import util.Positions._
+import reporting.ErrorExplanations._
import Constants._
import ScriptParsers._
import Comments._
@@ -97,17 +98,17 @@ object Parsers {
/** Issue an error at given offset if beyond last error offset
* and update lastErrorOffset.
*/
- def syntaxError(msg: String, offset: Int = in.offset): Unit =
+ def syntaxError(expl: Explanation, offset: Int = in.offset): Unit =
if (offset > lastErrorOffset) {
- syntaxError(msg, Position(offset))
+ syntaxError(expl, Position(offset))
lastErrorOffset = in.offset
}
/** Unconditionally issue an error at given position, without
* updating lastErrorOffset.
*/
- def syntaxError(msg: String, pos: Position): Unit =
- ctx.error(msg, source atPos pos)
+ def syntaxError(expl: Explanation, pos: Position): Unit =
+ ctx.explainError(expl, source atPos pos)
}
@@ -213,8 +214,8 @@ object Parsers {
}
}
- def warning(msg: String, offset: Int = in.offset) =
- ctx.warning(msg, source atPos Position(offset))
+ def warning(msg: Explanation, offset: Int = in.offset) =
+ ctx.explainWarning(msg, source atPos Position(offset))
def deprecationWarning(msg: String, offset: Int = in.offset) =
ctx.deprecationWarning(msg, source atPos Position(offset))
@@ -1013,19 +1014,15 @@ object Parsers {
} else EmptyTree
handler match {
- case Block(Nil, EmptyTree) => syntaxError(
- "`catch` block does not contain a valid expression, try adding a case like - `case e: Exception =>` to the block",
- handler.pos
- )
+ case Block(Nil, EmptyTree) =>
+ syntaxError(EmptyCatchBlock(body), handler.pos)
case _ =>
}
val finalizer =
if (in.token == FINALLY) { accept(FINALLY); expr() }
else {
- if (handler.isEmpty)
- warning("A try without `catch` or `finally` is equivalent to putting its body in a block; no exceptions are handled.")
-
+ if (handler.isEmpty) warning(EmptyCatchAndFinallyBlock(body))
EmptyTree
}
ParsedTry(body, handler, finalizer)
diff --git a/src/dotty/tools/dotc/reporting/Examples.scala b/src/dotty/tools/dotc/reporting/Examples.scala
new file mode 100644
index 000000000..d2dbd70eb
--- /dev/null
+++ b/src/dotty/tools/dotc/reporting/Examples.scala
@@ -0,0 +1,67 @@
+package dotty.tools
+package dotc
+package reporting
+
+import dotc.core.Contexts.Context
+
+object ErrorExplanations {
+ import dotc.ast.Trees._
+ import dotc.ast.untpd
+
+ implicit def stringToExplanation(s: String) = NoExplanation(s)
+
+ implicit class ShouldExplainCtx(val c: Context) extends AnyVal {
+ def shouldExplain(expl: Explanation): Boolean = {
+ implicit val ctx = c
+ expl match {
+ case _: NoExplanation => false
+ case expl if ctx.settings.explainerrors.value => true
+ case _ => false
+ }
+ }
+ }
+
+ trait Explanation {
+ def msg: String
+ def explanation: String
+ }
+
+ case class NoExplanation(msg: String) extends Explanation {
+ val explanation = ""
+ }
+
+ abstract class EmptyCatchOrFinallyBlock(tryBody: untpd.Tree)(implicit ctx: Context) extends Explanation {
+ val explanation = {
+ val tryString = tryBody match {
+ case Block(Nil, untpd.EmptyTree) => "{}"
+ case _ => tryBody.show
+ }
+ s"""|Explanation:
+ |============
+ |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:
+ |
+ |try $tryString catch {
+ | case t: Throwable => ???
+ |}
+ |
+ |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`:
+ |
+ |try $tryString finally {
+ | // perform your cleanup here!
+ |}""".stripMargin
+ }
+ }
+
+ case class EmptyCatchBlock(tryBody: untpd.Tree)(implicit ctx: Context) extends EmptyCatchOrFinallyBlock(tryBody) {
+ val msg =
+ "`catch` block does not contain a valid expression, try adding a case like - `case e: Exception =>` to the block"
+ }
+
+ case class EmptyCatchAndFinallyBlock(tryBody: untpd.Tree)(implicit ctx: Context) extends EmptyCatchOrFinallyBlock(tryBody) {
+ val msg =
+ "A try without `catch` or `finally` is equivalent to putting its body in a block; no exceptions are handled."
+ }
+}
diff --git a/src/dotty/tools/dotc/reporting/Reporter.scala b/src/dotty/tools/dotc/reporting/Reporter.scala
index 75113d823..eb9a10a59 100644
--- a/src/dotty/tools/dotc/reporting/Reporter.scala
+++ b/src/dotty/tools/dotc/reporting/Reporter.scala
@@ -13,6 +13,7 @@ import java.lang.System.currentTimeMillis
import core.Mode
import interfaces.Diagnostic.{ERROR, WARNING, INFO}
import dotty.tools.dotc.core.Symbols.Symbol
+import ErrorExplanations._
object Reporter {
class Error(msgFn: => String, pos: SourcePosition) extends Diagnostic(msgFn, pos, ERROR)
@@ -95,6 +96,12 @@ trait Reporting { this: Context =>
def warning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
reporter.report(new Warning(msg, pos))
+ def explainWarning(expl: => Explanation, pos: SourcePosition = NoSourcePosition): Unit = {
+ warning(expl.msg, pos)
+ if (this.shouldExplain(expl))
+ reporter.report(new Info(expl.explanation, NoSourcePosition))
+ }
+
def strictWarning(msg: => String, 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)
@@ -104,6 +111,12 @@ trait Reporting { this: Context =>
reporter.report(new Error(msg, pos))
}
+ def explainError(expl: => Explanation, pos: SourcePosition = NoSourcePosition): Unit = {
+ error(expl.msg, pos)
+ if (this.shouldExplain(expl))
+ reporter.report(new Info(expl.explanation, NoSourcePosition))
+ }
+
def errorOrMigrationWarning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
if (ctx.scala2Mode) migrationWarning(msg, pos) else error(msg, pos)