package dotty.tools
package dotc
package reporting
package diagnostic
import dotc.core._
import Contexts.Context, Decorators._, Symbols._, Names._, Types._
import util.{SourceFile, NoSource}
import util.{SourcePosition, NoSourcePosition}
import config.Settings.Setting
import interfaces.Diagnostic.{ERROR, WARNING, INFO}
import printing.SyntaxHighlighting._
import printing.Formatting
object messages {
/** Message container to be consumed by the reporter ---------------------- */
class Error(
msgFn: => Message,
pos: SourcePosition,
kind: String,
explanation: String = ""
) extends MessageContainer(msgFn, pos, ERROR, kind, explanation)
class Warning(
msgFn: => Message,
pos: SourcePosition,
kind: String,
explanation: String = ""
) extends MessageContainer(msgFn, pos, WARNING, kind, explanation)
class Info(
msgFn: => Message,
pos: SourcePosition,
kind: String,
explanation: String = ""
) extends MessageContainer(msgFn, pos, INFO, kind, explanation)
abstract class ConditionalWarning(
msgFn: => Message,
pos: SourcePosition,
kind: String,
explanation: String = ""
) extends Warning(msgFn, pos, kind, explanation) {
def enablingOption(implicit ctx: Context): Setting[Boolean]
}
class FeatureWarning(
msgFn: => Message,
pos: SourcePosition,
kind: String = "Feature Warning",
explanation: String = ""
) extends ConditionalWarning(msgFn, pos, kind, explanation) {
def enablingOption(implicit ctx: Context) = ctx.settings.feature
}
class UncheckedWarning(
msgFn: => Message,
pos: SourcePosition,
kind: String = "Unchecked Warning",
explanation: String = ""
) extends ConditionalWarning(msgFn, pos, kind, explanation) {
def enablingOption(implicit ctx: Context) = ctx.settings.unchecked
}
class DeprecationWarning(
msgFn: => Message,
pos: SourcePosition,
kind: String = "Deprecation Warning",
explanation: String = ""
) extends ConditionalWarning(msgFn, pos, kind, explanation) {
def enablingOption(implicit ctx: Context) = ctx.settings.deprecation
}
class MigrationWarning(
msgFn: => Message,
pos: SourcePosition,
kind: String = "Migration Warning",
explanation: String = ""
) extends ConditionalWarning(msgFn, pos, kind, explanation) {
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
}
}
case 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
}
case class DeprecatedWithOperator()(implicit ctx: Context)
extends Message("E003") {
val kind = "Syntax"
val msg =
hl"""${"with"} as a type operator has been deprecated; use `&' instead"""
val explanation = {
val codeBlock1 =
"""|trait A {
| type T = Int
|}
|
|trait B {
| type T = Double
|}""".stripMargin
hl"""|Dotty introduces intersection types - `&' types. These replace the
|use of the ${"with"} keyword. There are a few differences in
|semantics between intersection types and using `${"with"}'.
|
|`${"A with B"}' is ordered, `${"A & B"}' is not.
|
|In:
|
|$codeBlock1
|
|The type of `${"T"}' in `${"A with B"}' is ${"Int"} whereas in `${"A & B"}'
|the type of `${"T"}' is ${"Int & Double"}.""".stripMargin
}
}
/** Type Errors ----------------------------------------------------------- */
case class DuplicateBind(bind: untpd.Bind, tree: untpd.CaseDef)(implicit ctx: Context)
extends Message("E004") {
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
}
}
case class MissingIdent(tree: untpd.Ident, treeKind: String, name: String)(implicit ctx: Context)
extends Message("E005") {
val kind = "Missing Identifier"
val msg = em"not found: $treeKind$name"
val explanation = {
hl"""|An identifier for `$treeKind$name` is missing. This means that something
|has either been misspelt or you're forgetting an import""".stripMargin
}
}
case class TypeMismatch(found: Type, expected: Type, whyNoMatch: String = "")(implicit ctx: Context)
extends Message("E006") {
val kind = "Type Mismatch"
private val (where, printCtx) = Formatting.disambiguateTypes(found, expected)
private val (fnd, exp) = Formatting.typeDiff(found, expected)(printCtx)
val msg =
s"""|found: $fnd
|required: $exp
|
|$where""".stripMargin + whyNoMatch
val explanation = ""
}
}