aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/reporting/diagnostic/Message.scala
blob: ab1222dab6b71dcf2b4aa7520e9cf9d71d31e4e4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package dotty.tools
package dotc
package reporting
package diagnostic

import util.SourcePosition
import core.Contexts.Context

import messages._

object Message {
  /** This implicit conversion provides a fallback for error messages that have
    * not yet been ported to the new scheme. Comment out this `implicit def` to
    * see where old errors still exist
    */
  implicit def toNoExplanation(str: String): Message =
    new NoExplanation(str)
}

/** A `Message` contains all semantic information necessary to easily
  * comprehend what caused the message to be logged. Each message can be turned
  * into a `MessageContainer` which contains the log level and can later be
  * consumed by a subclass of `Reporter`. However, the error position is only
  * part of `MessageContainer`, not `Message`.
  *
  * NOTE: you should not be persisting messages. Most messages take an implicit
  * `Context` and these contexts weigh in at about 4mb per instance, as such
  * persisting these will result in a memory leak.
  *
  * Instead use the `persist` method to create an instance that does not keep a
  * reference to these contexts.
  *
  * @param errorId a unique number identifying the message, this will later be
  *                used to reference documentation online
  */
abstract class Message(val errorId: Int) { self =>
  import messages._

  /** The `msg` contains the diagnostic message e.g:
    *
    * > expected: String
    * > found:    Int
    *
    * This message will be placed underneath the position given by the enclosing
    * `MessageContainer`
    */
  def msg: String

  /** The kind of the error message is something like "Syntax" or "Type
    * Mismatch"
    */
  def kind: String

  /** The explanation should provide a detailed description of why the error
    * occurred and use examples from the user's own code to illustrate how to
    * avoid these errors.
    */
  def explanation: String

  /** The implicit `Context` in messages is a large thing that we don't want
    * persisted. This method gets around that by duplicating the message
    * without the implicit context being passed along.
    */
  def persist: Message = new Message (errorId) {
    val msg         = self.msg
    val kind        = self.kind
    val explanation = self.explanation
  }
}

/** An extended message keeps the contained message from being evaluated, while
  * allowing for extension for the `msg` string
  *
  * This is useful when we need to add additional information to an existing
  * message.
  */
class ExtendMessage(_msg: () => Message)(f: String => String) { self =>
  lazy val msg = f(_msg().msg)
  lazy val kind = _msg().kind
  lazy val explanation = _msg().explanation
  lazy val errorId = _msg().errorId

  private def toMessage = new Message(errorId) {
    val msg = self.msg
    val kind = self.kind
    val explanation = self.explanation
  }

  /** Enclose this message in an `Error` container */
  def error(pos: SourcePosition) =
    new Error(toMessage, pos)

  /** Enclose this message in an `Warning` container */
  def warning(pos: SourcePosition) =
    new Warning(toMessage, pos)

  /** Enclose this message in an `Info` container */
  def info(pos: SourcePosition) =
    new Info(toMessage, pos)

  /** Enclose this message in an `FeatureWarning` container */
  def featureWarning(pos: SourcePosition) =
    new FeatureWarning(toMessage, pos)

  /** Enclose this message in an `UncheckedWarning` container */
  def uncheckedWarning(pos: SourcePosition) =
    new UncheckedWarning(toMessage, pos)

  /** Enclose this message in an `DeprecationWarning` container */
  def deprecationWarning(pos: SourcePosition) =
    new DeprecationWarning(toMessage, pos)

  /** Enclose this message in an `MigrationWarning` container */
  def migrationWarning(pos: SourcePosition) =
    new MigrationWarning(toMessage, pos)
}

/** The fallback `Message` containing no explanation and having no `kind` */
class NoExplanation(val msg: String) extends Message(NoExplanation.ID) {
  val explanation = ""
  val kind = ""

  override def toString(): String = s"NoExplanation($msg)"
}

/** The extractor for `NoExplanation` can be used to check whether any error
  * lacks an explanation
  */
object NoExplanation {
  final val ID = -1

  def unapply(m: Message): Option[Message] =
    if (m.explanation == "") Some(m)
    else None
}