aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala')
-rw-r--r--compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala902
1 files changed, 902 insertions, 0 deletions
diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
new file mode 100644
index 000000000..489165e56
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
@@ -0,0 +1,902 @@
+package dotty.tools
+package dotc
+package reporting
+package diagnostic
+
+import dotc.core._
+import Contexts.Context, Decorators._, Symbols._, Names._, NameOps._, Types._
+import util.{SourceFile, NoSource}
+import util.{SourcePosition, NoSourcePosition}
+import config.Settings.Setting
+import interfaces.Diagnostic.{ERROR, WARNING, INFO}
+import printing.Highlighting._
+import printing.Formatting
+
+object messages {
+
+ // `MessageContainer`s to be consumed by `Reporter` ---------------------- //
+ class Error(
+ msgFn: => Message,
+ pos: SourcePosition
+ ) extends MessageContainer(msgFn, pos, ERROR)
+
+ class Warning(
+ msgFn: => Message,
+ pos: SourcePosition
+ ) extends MessageContainer(msgFn, pos, WARNING)
+
+ class Info(
+ msgFn: => Message,
+ pos: SourcePosition
+ ) extends MessageContainer(msgFn, pos, INFO)
+
+ abstract class ConditionalWarning(
+ msgFn: => Message,
+ pos: SourcePosition
+ ) extends Warning(msgFn, pos) {
+ def enablingOption(implicit ctx: Context): Setting[Boolean]
+ }
+
+ class FeatureWarning(
+ msgFn: => Message,
+ pos: SourcePosition
+ ) extends ConditionalWarning(msgFn, pos) {
+ def enablingOption(implicit ctx: Context) = ctx.settings.feature
+ }
+
+ class UncheckedWarning(
+ msgFn: => Message,
+ pos: SourcePosition
+ ) extends ConditionalWarning(msgFn, pos) {
+ def enablingOption(implicit ctx: Context) = ctx.settings.unchecked
+ }
+
+ class DeprecationWarning(
+ msgFn: => Message,
+ pos: SourcePosition
+ ) extends ConditionalWarning(msgFn, pos) {
+ def enablingOption(implicit ctx: Context) = ctx.settings.deprecation
+ }
+
+ class MigrationWarning(
+ msgFn: => Message,
+ pos: SourcePosition
+ ) extends ConditionalWarning(msgFn, pos) {
+ 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(pos) // res: Error
+ * EmptyCatchBlock(tree).warning(pos) // res: Warning
+ * ```
+ */
+ import ast.Trees._
+ import ast.untpd
+ import ast.tpd
+
+ /** Helper methods for messages */
+ def implicitClassRestrictionsText(implicit ctx: Context) =
+ hl"""|${NoColor("For a full list of restrictions on implicit classes visit")}
+ |${Blue("http://docs.scala-lang.org/overviews/core/implicit-classes.html")}"""
+
+
+ // Syntax Errors ---------------------------------------------------------- //
+ abstract class EmptyCatchOrFinallyBlock(tryBody: untpd.Tree, errNo: Int)(implicit ctx: Context)
+ extends Message(errNo) {
+ val explanation = {
+ val tryString = tryBody match {
+ case Block(Nil, untpd.EmptyTree) => "{}"
+ case _ => tryBody.show
+ }
+
+ val code1 =
+ s"""|import scala.util.control.NonFatal
+ |
+ |try $tryString catch {
+ | case NonFatal(e) => ???
+ |}""".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
+ |
+ |It is recommended to use the ${"NonFatal"} extractor to catch all exceptions as it
+ |correctly handles transfer functions like ${"return"}."""
+ }
+ }
+
+ case class EmptyCatchBlock(tryBody: untpd.Tree)(implicit ctx: Context)
+ extends EmptyCatchOrFinallyBlock(tryBody, 1) {
+ 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"""
+ }
+
+ case class EmptyCatchAndFinallyBlock(tryBody: untpd.Tree)(implicit ctx: Context)
+ extends EmptyCatchOrFinallyBlock(tryBody, 2) {
+ 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."""
+ }
+
+ case class DeprecatedWithOperator()(implicit ctx: Context)
+ extends Message(3) {
+ val kind = "Syntax"
+ val msg =
+ hl"""${"with"} as a type operator has been deprecated; use `&' instead"""
+ val explanation =
+ 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"}'."""
+ }
+
+ case class CaseClassMissingParamList(cdef: untpd.TypeDef)(implicit ctx: Context)
+ extends Message(4) {
+ val kind = "Syntax"
+ val msg =
+ hl"""|A ${"case class"} must have at least one parameter list"""
+
+ val explanation =
+ hl"""|${cdef.name} must have at least one parameter list, if you would rather
+ |have a singleton representation of ${cdef.name}, use a "${"case object"}".
+ |Or, add an explicit `()' as a parameter list to ${cdef.name}."""
+ }
+
+
+ // Type Errors ------------------------------------------------------------ //
+ case class DuplicateBind(bind: untpd.Bind, tree: untpd.CaseDef)(implicit ctx: Context)
+ extends Message(5) {
+ 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!"""
+ }
+ }
+
+ case class MissingIdent(tree: untpd.Ident, treeKind: String, name: String)(implicit ctx: Context)
+ extends Message(6) {
+ val kind = "Unbound Identifier"
+ val msg = em"not found: $treeKind$name"
+
+ val explanation = {
+ hl"""|The identifier for `$treeKind$name` is not bound, that is,
+ |no declaration for this identifier can be found.
+ |That can happen for instance if $name or its declaration has either been
+ |misspelt, or if you're forgetting an import"""
+ }
+ }
+
+ case class TypeMismatch(found: Type, expected: Type, whyNoMatch: String = "", implicitFailure: String = "")(implicit ctx: Context)
+ extends Message(7) {
+ val kind = "Type Mismatch"
+ val msg = {
+ val (where, printCtx) = Formatting.disambiguateTypes(found, expected)
+ val (fnd, exp) = Formatting.typeDiff(found, expected)(printCtx)
+ s"""|found: $fnd
+ |required: $exp
+ |
+ |$where""".stripMargin + whyNoMatch + implicitFailure
+ }
+
+ val explanation = ""
+ }
+
+ case class NotAMember(site: Type, name: Name, selected: String)(implicit ctx: Context)
+ extends Message(8) {
+ val kind = "Member Not Found"
+
+ val msg = {
+ import core.Flags._
+ val maxDist = 3
+ val decls = site.decls.flatMap { sym =>
+ if (sym.is(Synthetic | PrivateOrLocal) || sym.isConstructor) Nil
+ else List((sym.name.show, sym))
+ }
+
+ // Calculate Levenshtein distance
+ def distance(n1: Iterable[_], n2: Iterable[_]) =
+ n1.foldLeft(List.range(0, n2.size)) { (prev, x) =>
+ (prev zip prev.tail zip n2).scanLeft(prev.head + 1) {
+ case (h, ((d, v), y)) => math.min(
+ math.min(h + 1, v + 1),
+ if (x == y) d else d + 1
+ )
+ }
+ }.last
+
+ // Count number of wrong characters
+ def incorrectChars(x: (String, Int, Symbol)): (String, Symbol, Int) = {
+ val (currName, _, sym) = x
+ val matching = name.show.zip(currName).foldLeft(0) {
+ case (acc, (x,y)) => if (x != y) acc + 1 else acc
+ }
+ (currName, sym, matching)
+ }
+
+ // Get closest match in `site`
+ val closest =
+ decls
+ .map { case (n, sym) => (n, distance(n, name.show), sym) }
+ .collect { case (n, dist, sym) if dist <= maxDist => (n, dist, sym) }
+ .groupBy(_._2).toList
+ .sortBy(_._1)
+ .headOption.map(_._2).getOrElse(Nil)
+ .map(incorrectChars).toList
+ .sortBy(_._3)
+ .take(1).map { case (n, sym, _) => (n, sym) }
+
+ val siteName = site match {
+ case site: NamedType => site.name.show
+ case site => i"$site"
+ }
+
+ val closeMember = closest match {
+ case (n, sym) :: Nil => hl""" - did you mean `${s"$siteName.$n"}`?"""
+ case Nil => ""
+ case _ => assert(
+ false,
+ "Could not single out one distinct member to match on input with"
+ )
+ }
+
+ ex"$selected `$name` is not a member of $site$closeMember"
+ }
+
+ val explanation = ""
+ }
+
+ case class EarlyDefinitionsNotSupported()(implicit ctx: Context)
+ extends Message(9) {
+ val kind = "Syntax"
+ val msg = "early definitions are not supported; use trait parameters instead"
+
+ val explanation = {
+ val code1 =
+ """|trait Logging {
+ | val f: File
+ | f.open()
+ | onExit(f.close())
+ | def log(msg: String) = f.write(msg)
+ |}
+ |
+ |class B extends Logging {
+ | val f = new File("log.data") // triggers a NullPointerException
+ |}
+ |
+ |// early definition gets around the NullPointerException
+ |class C extends {
+ | val f = new File("log.data")
+ |} with Logging""".stripMargin
+
+ val code2 =
+ """|trait Logging(f: File) {
+ | f.open()
+ | onExit(f.close())
+ | def log(msg: String) = f.write(msg)
+ |}
+ |
+ |class C extends Logging(new File("log.data"))""".stripMargin
+
+ hl"""|Earlier versions of Scala did not support trait parameters and "early
+ |definitions" (also known as "early initializers") were used as an alternative.
+ |
+ |Example of old syntax:
+ |
+ |$code1
+ |
+ |The above code can now be written as:
+ |
+ |$code2
+ |"""
+ }
+ }
+
+ case class TopLevelImplicitClass(cdef: untpd.TypeDef)(implicit ctx: Context)
+ extends Message(10) {
+ val kind = "Syntax"
+ val msg = hl"""An ${"implicit class"} may not be top-level"""
+
+ val explanation = {
+ val TypeDef(name, impl @ Template(constr0, parents, self, _)) = cdef
+ val exampleArgs =
+ constr0.vparamss(0).map(_.withMods(untpd.Modifiers()).show).mkString(", ")
+ def defHasBody[T] = impl.body.exists(!_.isEmpty)
+ val exampleBody = if (defHasBody) "{\n ...\n }" else ""
+ hl"""|There may not be any method, member or object in scope with the same name as
+ |the implicit class and a case class automatically gets a companion object with
+ |the same name created by the compiler which would cause a naming conflict if it
+ |were allowed.
+ |
+ |""" + implicitClassRestrictionsText + hl"""|
+ |
+ |To resolve the conflict declare ${cdef.name} inside of an ${"object"} then import the class
+ |from the object at the use site if needed, for example:
+ |
+ |object Implicits {
+ | implicit class ${cdef.name}($exampleArgs)$exampleBody
+ |}
+ |
+ |// At the use site:
+ |import Implicits.${cdef.name}"""
+ }
+ }
+
+ case class ImplicitCaseClass(cdef: untpd.TypeDef)(implicit ctx: Context)
+ extends Message(11) {
+ val kind = "Syntax"
+ val msg = hl"""A ${"case class"} may not be defined as ${"implicit"}"""
+
+ val explanation =
+ hl"""|implicit classes may not be case classes. Instead use a plain class:
+ |
+ |implicit class ${cdef.name}...
+ |
+ |""" + implicitClassRestrictionsText
+ }
+
+ case class ObjectMayNotHaveSelfType(mdef: untpd.ModuleDef)(implicit ctx: Context)
+ extends Message(12) {
+ val kind = "Syntax"
+ val msg = hl"""${"object"}s must not have a self ${"type"}"""
+
+ val explanation = {
+ val untpd.ModuleDef(name, tmpl) = mdef
+ val ValDef(_, selfTpt, _) = tmpl.self
+ hl"""|${"object"}s must not have a self ${"type"}:
+ |
+ |Consider these alternative solutions:
+ | - Create a trait or a class instead of an object
+ | - Let the object extend a trait containing the self type:
+ |
+ | object $name extends ${selfTpt.show}"""
+ }
+ }
+
+ case class TupleTooLong(ts: List[untpd.Tree])(implicit ctx: Context)
+ extends Message(13) {
+ import Definitions.MaxTupleArity
+ val kind = "Syntax"
+ val msg = hl"""A ${"tuple"} cannot have more than ${MaxTupleArity} members"""
+
+ val explanation = {
+ val members = ts.map(_.showSummary).grouped(MaxTupleArity)
+ val nestedRepresentation = members.map(_.mkString(", ")).mkString(")(")
+ hl"""|This restriction will be removed in the future.
+ |Currently it is possible to use nested tuples when more than $MaxTupleArity are needed, for example:
+ |
+ |((${nestedRepresentation}))"""
+ }
+ }
+
+ case class RepeatedModifier(modifier: String)(implicit ctx:Context)
+ extends Message(14) {
+ val kind = "Syntax"
+ val msg = hl"""repeated modifier $modifier"""
+
+ val explanation = {
+ val code1 = hl"""private private val Origin = Point(0, 0)"""
+ val code2 = hl"""private final val Origin = Point(0, 0)"""
+ hl"""This happens when you accidentally specify the same modifier twice.
+ |
+ |Example:
+ |
+ |$code1
+ |
+ |instead of
+ |
+ |$code2
+ |
+ |"""
+ }
+ }
+
+ case class InterpolatedStringError()(implicit ctx:Context)
+ extends Message(15) {
+ val kind = "Syntax"
+ val msg = "error in interpolated string: identifier or block expected"
+ val explanation = {
+ val code1 = "s\"$new Point(0, 0)\""
+ val code2 = "s\"${new Point(0, 0)}\""
+ hl"""|This usually happens when you forget to place your expressions inside curly braces.
+ |
+ |$code1
+ |
+ |should be written as
+ |
+ |$code2
+ |"""
+ }
+ }
+
+ case class UnboundPlaceholderParameter()(implicit ctx:Context)
+ extends Message(16) {
+ val kind = "Syntax"
+ val msg = "unbound placeholder parameter; incorrect use of `_`"
+ val explanation =
+ hl"""|The `_` placeholder syntax was used where it could not be bound.
+ |Consider explicitly writing the variable binding.
+ |
+ |This can be done by replacing `_` with a variable (eg. `x`)
+ |and adding ${"x =>"} where applicable.
+ |
+ |Example before:
+ |
+ |${"{ _ }"}
+ |
+ |Example after:
+ |
+ |${"x => { x }"}
+ |
+ |Another common occurrence for this error is defining a val with `_`:
+ |
+ |${"val a = _"}
+ |
+ |But this val definition isn't very useful, it can never be assigned
+ |another value. And thus will always remain uninitialized.
+ |Consider replacing the ${"val"} with ${"var"}:
+ |
+ |${"var a = _"}
+ |
+ |Note that this use of `_` is not placeholder syntax,
+ |but an uninitialized var definition"""
+ }
+
+ case class IllegalStartSimpleExpr(illegalToken: String)(implicit ctx: Context)
+ extends Message(17) {
+ val kind = "Syntax"
+ val msg = "illegal start of simple expression"
+ val explanation = {
+ hl"""|An expression yields a value. In the case of the simple expression, this error
+ |commonly occurs when there's a missing parenthesis or brace. The reason being
+ |that a simple expression is one of the following:
+ |
+ |- Block
+ |- Expression in parenthesis
+ |- Identifier
+ |- Object creation
+ |- Literal
+ |
+ |which cannot start with ${Red(illegalToken)}."""
+ }
+ }
+
+ case class MissingReturnType()(implicit ctx:Context) extends Message(18) {
+ val kind = "Syntax"
+ val msg = "missing return type"
+ val explanation =
+ hl"""|An abstract declaration must have a return type. For example:
+ |
+ |trait Shape {
+ | def area: Double // abstract declaration returning a ${"Double"}
+ |}"""
+ }
+
+ case class YieldOrDoExpectedInForComprehension()(implicit ctx: Context)
+ extends Message(19) {
+ val kind = "Syntax"
+ val msg = hl"${"yield"} or ${"do"} expected"
+
+ val explanation =
+ hl"""|When the enumerators in a for comprehension are not placed in parentheses or
+ |braces, a ${"do"} or ${"yield"} statement is required after the enumerators
+ |section of the comprehension.
+ |
+ |You can save some keystrokes by omitting the parentheses and writing
+ |
+ |${"val numbers = for i <- 1 to 3 yield i"}
+ |
+ | instead of
+ |
+ |${"val numbers = for (i <- 1 to 3) yield i"}
+ |
+ |but the ${"yield"} keyword is still required.
+ |
+ |For comprehensions that simply perform a side effect without yielding anything
+ |can also be written without parentheses but a ${"do"} keyword has to be
+ |included. For example,
+ |
+ |${"for (i <- 1 to 3) println(i)"}
+ |
+ |can be written as
+ |
+ |${"for i <- 1 to 3 do println(i) // notice the 'do' keyword"}
+ |
+ |"""
+ }
+
+ case class ProperDefinitionNotFound()(implicit ctx: Context)
+ extends Message(20) {
+ val kind = "Definition Not Found"
+ val msg = hl"""Proper definition was not found in ${"@usecase"}"""
+
+ val explanation = {
+ val noUsecase =
+ "def map[B, That](f: A => B)(implicit bf: CanBuildFrom[List[A], B, That]): That"
+
+ val usecase =
+ """|/** Map from List[A] => List[B]
+ | *
+ | * @usecase def map[B](f: A => B): List[B]
+ | */
+ |def map[B, That](f: A => B)(implicit bf: CanBuildFrom[List[A], B, That]): That
+ |""".stripMargin
+
+ hl"""|Usecases are only supported for ${"def"}s. They exist because with Scala's
+ |advanced type-system, we sometimes end up with seemingly scary signatures.
+ |The usage of these methods, however, needs not be - for instance the `map`
+ |function
+ |
+ |${"List(1, 2, 3).map(2 * _) // res: List(2, 4, 6)"}
+ |
+ |is easy to understand and use - but has a rather bulky signature:
+ |
+ |$noUsecase
+ |
+ |to mitigate this and ease the usage of such functions we have the ${"@usecase"}
+ |annotation for docstrings. Which can be used like this:
+ |
+ |$usecase
+ |
+ |When creating the docs, the signature of the method is substituted by the
+ |usecase and the compiler makes sure that it is valid. Because of this, you're
+ |only allowed to use ${"def"}s when defining usecases."""
+ }
+ }
+
+ case class ByNameParameterNotSupported()(implicit ctx: Context)
+ extends Message(21) {
+ val kind = "Syntax"
+ val msg = "By-name parameter type not allowed here."
+
+ val explanation =
+ hl"""|By-name parameters act like functions that are only evaluated when referenced,
+ |allowing for lazy evaluation of a parameter.
+ |
+ |An example of using a by-name parameter would look like:
+ |${"def func(f: => Boolean) = f // 'f' is evaluated when referenced within the function"}
+ |
+ |An example of the syntax of passing an actual function as a parameter:
+ |${"def func(f: (Boolean => Boolean)) = f(true)"}
+ |
+ |or:
+ |
+ |${"def func(f: Boolean => Boolean) = f(true)"}
+ |
+ |And the usage could be as such:
+ |${"func(bool => // do something...)"}
+ |"""
+ }
+
+ case class WrongNumberOfArgs(fntpe: Type, argKind: String, expectedArgs: List[TypeParamInfo], actual: List[untpd.Tree])(implicit ctx: Context)
+ extends Message(22) {
+ val kind = "Syntax"
+
+ private val expectedCount = expectedArgs.length
+ private val actualCount = actual.length
+ private val msgPrefix = if (actualCount > expectedCount) "Too many" else "Not enough"
+
+ //TODO add def simpleParamName to TypeParamInfo
+ private val expectedArgString = fntpe
+ .widen.typeParams
+ .map(_.paramName.unexpandedName.show)
+ .mkString("[", ", ", "]")
+
+ private val actualArgString = actual.map(_.show).mkString("[", ", ", "]")
+
+ private val prettyName = fntpe.termSymbol match {
+ case NoSymbol => fntpe.show
+ case symbol => symbol.showFullName
+ }
+
+ val msg =
+ hl"""|${NoColor(msgPrefix)} ${argKind} arguments for $prettyName$expectedArgString
+ |expected: $expectedArgString
+ |actual: $actualArgString""".stripMargin
+
+ val explanation = {
+ val tooManyTypeParams =
+ """|val tuple2: (Int, String) = (1, "one")
+ |val list: List[(Int, String)] = List(tuple2)""".stripMargin
+
+ if (actualCount > expectedCount)
+ hl"""|You have supplied too many type parameters
+ |
+ |For example List takes a single type parameter (List[A])
+ |If you need to hold more types in a list then you need to combine them
+ |into another data type that can contain the number of types you need,
+ |In this example one solution would be to use a Tuple:
+ |
+ |${tooManyTypeParams}"""
+ else
+ hl"""|You have not supplied enough type parameters
+ |If you specify one type parameter then you need to specify every type parameter."""
+ }
+ }
+
+ case class IllegalVariableInPatternAlternative()(implicit ctx: Context)
+ extends Message(23) {
+ val kind = "Syntax"
+ val msg = "Variables are not allowed in alternative patterns"
+ val explanation = {
+ val varInAlternative =
+ """|def g(pair: (Int,Int)): Int = pair match {
+ | case (1, n) | (n, 1) => n
+ | case _ => 0
+ |}""".stripMargin
+
+ val fixedVarInAlternative =
+ """|def g(pair: (Int,Int)): Int = pair match {
+ | case (1, n) => n
+ | case (n, 1) => n
+ | case _ => 0
+ |}""".stripMargin
+
+ hl"""|Variables are not allowed within alternate pattern matches. You can workaround
+ |this issue by adding additional cases for each alternative. For example, the
+ |illegal function:
+ |
+ |$varInAlternative
+ |could be implemented by moving each alternative into a separate case:
+ |
+ |$fixedVarInAlternative"""
+ }
+ }
+
+ case class TypeParamsTypeExpected(mods: untpd.Modifiers, identifier: TermName)(implicit ctx: Context)
+ extends Message(24) {
+ val kind = "Syntax"
+ val msg = hl"""Expected ${"type"} keyword for type parameter $identifier"""
+ val explanation =
+ hl"""|This happens when you add modifiers like ${"private"} or ${"protected"}
+ |to your type parameter definition without adding the ${"type"} keyword.
+ |
+ |Add ${"type"} to your code, e.g.:
+ |${s"trait A[${mods.flags} type $identifier]"}
+ |"""
+ }
+
+ case class IdentifierExpected(identifier: String)(implicit ctx: Context)
+ extends Message(25) {
+ val kind = "Syntax"
+ val msg = "identifier expected"
+ val explanation = {
+ val wrongIdentifier = s"def foo: $identifier = {...}"
+ val validIdentifier = s"def foo = {...}"
+ hl"""|An identifier expected, but `$identifier` found. This could be because
+ |`$identifier` is not a valid identifier. As a workaround, the compiler could
+ |infer the type for you. For example, instead of:
+ |
+ |$wrongIdentifier
+ |
+ |Write your code like:
+ |
+ |$validIdentifier
+ |
+ |"""
+ }
+ }
+
+ case class AuxConstructorNeedsNonImplicitParameter()(implicit ctx:Context)
+ extends Message(26) {
+ val kind = "Syntax"
+ val msg = "auxiliary constructor needs non-implicit parameter list"
+ val explanation =
+ hl"""|Only the primary constructor is allowed an ${"implicit"} parameter list;
+ |auxiliary constructors need non-implicit parameter lists. When a primary
+ |constructor has an implicit argslist, auxiliary constructors that call the
+ |primary constructor must specify the implicit value.
+ |
+ |To resolve this issue check for:
+ | - forgotten parenthesis on ${"this"} (${"def this() = { ... }"})
+ | - auxiliary constructors specify the implicit value
+ |"""
+ }
+
+ case class IncorrectRepeatedParameterSyntax()(implicit ctx: Context) extends Message(27) {
+ val kind = "Syntax"
+ val msg = "'*' expected"
+ val explanation =
+ hl"""|Expected * in '_*' operator.
+ |
+ |The '_*' operator can be used to supply a sequence-based argument
+ |to a method with a variable-length or repeated parameter. It is used
+ |to expand the sequence to a variable number of arguments, such that:
+ |func(args: _*) would expand to func(arg1, arg2 ... argN).
+ |
+ |Below is an example of how a method with a variable-length
+ |parameter can be declared and used.
+ |
+ |Squares the arguments of a variable-length parameter:
+ |${"def square(args: Int*) = args.map(a => a * a)"}
+ |
+ |Usage:
+ |${"square(1, 2, 3) // res0: List[Int] = List(1, 4, 9)"}
+ |
+ |Secondary Usage with '_*':
+ |${"val ints = List(2, 3, 4) // ints: List[Int] = List(2, 3, 4)"}
+ |${"square(ints: _*) // res1: List[Int] = List(4, 9, 16)"}
+ |""".stripMargin
+ }
+
+ case class IllegalLiteral()(implicit ctx: Context) extends Message(28) {
+ val kind = "Syntax"
+ val msg = "illegal literal"
+ val explanation =
+ hl"""|Available literals can be divided into several groups:
+ | - Integer literals: 0, 21, 0xFFFFFFFF, -42L
+ | - Floating Point Literals: 0.0, 1e30f, 3.14159f, 1.0e-100, .1
+ | - Boolean Literals: true, false
+ | - Character Literals: 'a', '\u0041', '\n'
+ | - String Literals: "Hello, World!"
+ | - null
+ |"""
+ }
+
+ case class PatternMatchExhaustivity(uncovered: String)(implicit ctx: Context)
+ extends Message(29) {
+ val kind = "Pattern Match Exhaustivity"
+ val msg =
+ hl"""|match may not be exhaustive.
+ |
+ |It would fail on: $uncovered"""
+
+
+ val explanation = ""
+ }
+
+ case class MatchCaseUnreachable()(implicit ctx: Context)
+ extends Message(30) {
+ val kind = s"""Match ${hl"case"} Unreachable"""
+ val msg = "unreachable code"
+ val explanation = ""
+ }
+
+ case class SeqWildcardPatternPos()(implicit ctx: Context)
+ extends Message(31) {
+ val kind = "Syntax"
+ val msg = "`_*' can be used only for last argument"
+ val explanation = {
+ val code =
+ """def sumOfTheFirstTwo(list: List[Int]): Int = list match {
+ | case List(first, second, x:_*) => first + second
+ | case _ => 0
+ |}"""
+ hl"""|Sequence wildcard pattern is expected at the end of an argument list.
+ |This pattern matches any remaining elements in a sequence.
+ |Consider the following example:
+ |
+ |$code
+ |
+ |Calling:
+ |
+ |${"sumOfTheFirstTwo(List(1, 2, 10))"}
+ |
+ |would give 3 as a result"""
+ }
+ }
+
+ case class IllegalStartOfSimplePattern()(implicit ctx: Context) extends Message(32) {
+ val kind = "Syntax"
+ val msg = "illegal start of simple pattern"
+ val explanation = {
+ val sipCode =
+ """def f(x: Int, y: Int) = x match {
+ | case `y` => ...
+ |}
+ """
+ val constructorPatternsCode =
+ """case class Person(name: String, age: Int)
+ |
+ |def test(p: Person) = p match {
+ | case Person(name, age) => ...
+ |}
+ """
+ val tupplePatternsCode =
+ """def swap(tuple: (String, Int)): (Int, String) = tuple match {
+ | case (text, number) => (number, text)
+ |}
+ """
+ val patternSequencesCode =
+ """def getSecondValue(list: List[Int]): Int = list match {
+ | case List(_, second, x:_*) => second
+ | case _ => 0
+ |}"""
+ hl"""|Simple patterns can be divided into several groups:
+ |- Variable Patterns: ${"case x => ..."}.
+ | It matches any value, and binds the variable name to that value.
+ | A special case is the wild-card pattern _ which is treated as if it was a fresh
+ | variable on each occurrence.
+ |
+ |- Typed Patterns: ${"case x: Int => ..."} or ${"case _: Int => ..."}.
+ | This pattern matches any value matched by the specified type; it binds the variable
+ | name to that value.
+ |
+ |- Literal Patterns: ${"case 123 => ..."} or ${"case 'A' => ..."}.
+ | This type of pattern matches any value that is equal to the specified literal.
+ |
+ |- Stable Identifier Patterns:
+ |
+ | $sipCode
+ |
+ | the match succeeds only if the x argument and the y argument of f are equal.
+ |
+ |- Constructor Patterns:
+ |
+ | $constructorPatternsCode
+ |
+ | The pattern binds all object's fields to the variable names (name and age, in this
+ | case).
+ |
+ |- Tuple Patterns:
+ |
+ | $tupplePatternsCode
+ |
+ | Calling:
+ |
+ | ${"""swap(("Luftballons", 99)"""}
+ |
+ | would give ${"""(99, "Luftballons")"""} as a result.
+ |
+ |- Pattern Sequences:
+ |
+ | $patternSequencesCode
+ |
+ | Calling:
+ |
+ | ${"getSecondValue(List(1, 10, 2))"}
+ |
+ | would give 10 as a result.
+ | This pattern is possible because a companion object for the List class has a method
+ | with the following signature:
+ |
+ | ${"def unapplySeq[A](x: List[A]): Some[List[A]]"}
+ |"""
+ }
+ }
+
+ case class PkgDuplicateSymbol(existing: Symbol)(implicit ctx: Context)
+ extends Message(33) {
+ val kind = "Duplicate Symbol"
+ val msg = hl"trying to define package with same name as `$existing`"
+ val explanation = ""
+ }
+}