aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala
diff options
context:
space:
mode:
authorFelix Mulder <felix.mulder@gmail.com>2016-11-02 11:08:28 +0100
committerGuillaume Martres <smarter@ubuntu.com>2016-11-22 01:35:07 +0100
commit8a61ff432543a29234193cd1f7c14abd3f3d31a0 (patch)
treea8147561d307af862c295cfc8100d271063bb0dd /compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala
parent6a455fe6da5ff9c741d91279a2dc6fe2fb1b472f (diff)
downloaddotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.gz
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.bz2
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.zip
Move compiler and compiler tests to compiler dir
Diffstat (limited to 'compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala')
-rw-r--r--compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala145
1 files changed, 145 insertions, 0 deletions
diff --git a/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala b/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala
new file mode 100644
index 000000000..24d583b19
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala
@@ -0,0 +1,145 @@
+package dotty.tools
+package dotc
+package reporting
+
+import core.Contexts.Context
+import core.Decorators._
+import printing.Highlighting.{Blue, Red}
+import diagnostic.{Message, MessageContainer, NoExplanation}
+import diagnostic.messages._
+import util.SourcePosition
+
+import scala.collection.mutable
+
+trait MessageRendering {
+ /** Remove ANSI coloring from `str`, useful for getting real length of
+ * strings
+ *
+ * @return string stripped of ANSI escape codes
+ */
+ def stripColor(str: String): String =
+ str.replaceAll("\u001B\\[[;\\d]*m", "")
+
+ /** When inlining a method call, if there's an error we'd like to get the
+ * outer context and the `pos` at which the call was inlined.
+ *
+ * @return a list of strings with inline locations
+ */
+ def outer(pos: SourcePosition, prefix: String)(implicit ctx: Context): List[String] =
+ if (pos.outer.exists) {
+ s"$prefix| This location is in code that was inlined at ${pos.outer}" ::
+ outer(pos.outer, prefix)
+ } else Nil
+
+ /** Get the sourcelines before and after the position, as well as the offset
+ * for rendering line numbers
+ *
+ * @return (lines before error, lines after error, line numbers offset)
+ */
+ def sourceLines(pos: SourcePosition)(implicit ctx: Context): (List[String], List[String], Int) = {
+ var maxLen = Int.MinValue
+ def render(xs: List[Int]) =
+ xs.map(pos.source.offsetToLine(_))
+ .map { lineNbr =>
+ val prefix = s"${lineNbr + 1} |"
+ maxLen = math.max(maxLen, prefix.length)
+ (prefix, pos.lineContent(lineNbr).stripLineEnd)
+ }
+ .map { case (prefix, line) =>
+ val lnum = Red(" " * math.max(0, maxLen - prefix.length) + prefix)
+ hl"$lnum$line"
+ }
+
+ val (before, after) = pos.beforeAndAfterPoint
+ (render(before), render(after), maxLen)
+ }
+
+ /** The column markers aligned under the error */
+ def columnMarker(pos: SourcePosition, offset: Int)(implicit ctx: Context): String = {
+ val prefix = " " * (offset - 1)
+ val whitespace = " " * pos.startColumn
+ val carets = Red {
+ if (pos.startLine == pos.endLine)
+ "^" * math.max(1, pos.endColumn - pos.startColumn)
+ else "^"
+ }
+
+ s"$prefix|$whitespace${carets.show}"
+ }
+
+ /** The error message (`msg`) aligned under `pos`
+ *
+ * @return aligned error message
+ */
+ def errorMsg(pos: SourcePosition, msg: String, offset: Int)(implicit ctx: Context): String = {
+ val leastWhitespace = msg.lines.foldLeft(Int.MaxValue) { (minPad, line) =>
+ val lineLength = stripColor(line).length
+ val currPad = math.min(
+ math.max(0, ctx.settings.pageWidth.value - offset - lineLength),
+ offset + pos.startColumn
+ )
+
+ math.min(currPad, minPad)
+ }
+
+ msg.lines
+ .map { line => " " * (offset - 1) + "|" + (" " * (leastWhitespace - offset)) + line}
+ .mkString(sys.props("line.separator"))
+ }
+
+ /** The separator between errors containing the source file and error type
+ *
+ * @return separator containing error location and kind
+ */
+ def posStr(pos: SourcePosition, diagnosticLevel: String, message: Message)(implicit ctx: Context): String =
+ if (pos.exists) Blue({
+ val file = pos.source.file.toString
+ val errId =
+ if (message.errorId != NoExplanation.ID)
+ s"[E${"0" * (3 - message.errorId.toString.length) + message.errorId}] "
+ else ""
+ val kind =
+ if (message.kind == "") diagnosticLevel
+ else s"${message.kind} $diagnosticLevel"
+ val prefix = s"-- ${errId}${kind}: $file "
+
+ prefix +
+ ("-" * math.max(ctx.settings.pageWidth.value - stripColor(prefix).length, 0))
+ }).show else ""
+
+ /** Explanation rendered under "Explanation" header */
+ def explanation(m: Message)(implicit ctx: Context): String = {
+ val sb = new StringBuilder(
+ hl"""|
+ |${Blue("Explanation")}
+ |${Blue("===========")}"""
+ )
+ sb.append('\n').append(m.explanation)
+ if (m.explanation.lastOption != Some('\n')) sb.append('\n')
+ sb.toString
+ }
+
+ /** The whole message rendered from `msg` */
+ def messageAndPos(msg: Message, pos: SourcePosition, diagnosticLevel: String)(implicit ctx: Context): String = {
+ val sb = mutable.StringBuilder.newBuilder
+ sb.append(posStr(pos, diagnosticLevel, msg)).append('\n')
+ if (pos.exists) {
+ val (srcBefore, srcAfter, offset) = sourceLines(pos)
+ val marker = columnMarker(pos, offset)
+ val err = errorMsg(pos, msg.msg, offset)
+ sb.append((srcBefore ::: marker :: err :: outer(pos, " " * (offset - 1)) ::: srcAfter).mkString("\n"))
+ } else sb.append(msg.msg)
+ sb.toString
+ }
+
+ def diagnosticLevel(cont: MessageContainer): String =
+ cont match {
+ case m: Error => "Error"
+ case m: FeatureWarning => "Feature Warning"
+ case m: DeprecationWarning => "Deprecation Warning"
+ case m: UncheckedWarning => "Unchecked Warning"
+ case m: MigrationWarning => "Migration Warning"
+ case m: Warning => "Warning"
+ case m: Info => "Info"
+ }
+}