Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ class ConsoleReporter(
true
}

if (didPrint && ctx.shouldExplain(m))
if (didPrint && ctx.shouldExplain(m)) {
printMessage(explanation(m.contained()))
if (m.contained().links.nonEmpty) printMessage(documentationLinks(m.contained()))
}
else if (didPrint && m.contained().explanation.nonEmpty)
printMessage("\nlonger explanation available when compiling with `-explain`")
}
Expand Down
28 changes: 22 additions & 6 deletions compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ package reporting

import core.Contexts.Context
import core.Decorators._
import printing.Highlighting.{Blue, Red}
import printing.Highlighting.{Blue, Red, Yellow}
import printing.SyntaxHighlighting
import diagnostic.{ErrorMessageID, Message, MessageContainer, NoExplanation}
import diagnostic.messages._
import util.SourcePosition
import util.Chars.{ LF, CR, FF, SU }
import scala.annotation.switch
import util.Chars.{CR, FF, LF, SU}

import scala.annotation.switch
import scala.collection.mutable

trait MessageRendering {
Expand Down Expand Up @@ -130,13 +130,29 @@ trait MessageRendering {
val sb = new StringBuilder(
hl"""|
|${Blue("Explanation")}
|${Blue("===========")}"""
|${Blue("===========")}
|"""
)
sb.append('\n').append(m.explanation)
if (m.explanation.lastOption != Some('\n')) sb.append('\n')
sb.append(m.explanation)
if (!m.explanation.endsWith("\n")) sb.append('\n')
sb.toString
}

/** Documentation links rendered under "Further reading" header */
def documentationLinks(m: Message)(implicit ctx: Context): String = {
val sb = new StringBuilder(
hl"""|
|${Blue("Further reading")}
|${Blue("===============")}
|"""
)
for (link <- m.links) {
sb.append(link.text).append(hl" ${Yellow(link.url)}\n")
}
sb.append('\n').toString
}


/** The whole message rendered from `msg` */
def messageAndPos(msg: Message, pos: SourcePosition, diagnosticLevel: String)(implicit ctx: Context): String = {
val sb = mutable.StringBuilder.newBuilder
Expand Down
38 changes: 38 additions & 0 deletions compiler/src/dotty/tools/dotc/reporting/diagnostic/Message.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,36 @@ object Message {
new NoExplanation(str)
}

sealed trait DocumentationLink {
def url: String
def text: String
}

/** A documentation link can be rendered by tooling to direct the programmer to
* good resources for more details related to the compiler error or warning.
*
* To keep base links at a single place use the cases classes to share a common
* prefix.
*
* Links are checked for existence in tests in `dotty.tools.dotc.reporting.ErrorMessagesTests`
* if the error class is tested there.
*/
object DocumentationLink {
case class LanguageSpec(suffix: String, text: String = "Scala Language Specification") extends DocumentationLink {
val url = s"https://www.scala-lang.org/files/archive/spec/2.13/$suffix"
}
case class TourUrl(suffix: String, text: String) extends DocumentationLink {
val url = s"http://docs.scala-lang.org/overviews/$suffix"
}
case class Sip(suffix: String, text: String) extends DocumentationLink {
val url = s"http://docs.scala-lang.org/sips/$suffix"
}
case class DottyDocs(suffix: String, text: String = "Dotty documentation") extends DocumentationLink {
val url = s"http://dotty.epfl.ch/docs/$suffix"
}
case class FullUrl(url: String, text: String) extends DocumentationLink
}

/** 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
Expand Down Expand Up @@ -56,6 +86,11 @@ abstract class Message(val errorId: ErrorMessageID) { self =>
*/
def explanation: String

/** Links may list URLs to Internet resources related to the error
* e.g. the Scala Language Specification.
*/
val links: List[DocumentationLink]

/** 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.
Expand All @@ -64,6 +99,7 @@ abstract class Message(val errorId: ErrorMessageID) { self =>
val msg = self.msg
val kind = self.kind
val explanation = self.explanation
override val links = self.links
}
}

Expand All @@ -83,6 +119,7 @@ class ExtendMessage(_msg: () => Message)(f: String => String) { self =>
val msg = self.msg
val kind = self.kind
val explanation = self.explanation
val links = Nil
}

/** Enclose this message in an `Error` container */
Expand Down Expand Up @@ -118,6 +155,7 @@ class ExtendMessage(_msg: () => Message)(f: String => String) { self =>
class NoExplanation(val msg: String) extends Message(ErrorMessageID.NoExplanationID) {
val explanation = ""
val kind = ""
val links = Nil

override def toString(): String = s"NoExplanation($msg)"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@ object MessageContainer {
implicit class MessageContext(val c: Context) extends AnyVal {
def shouldExplain(cont: MessageContainer): Boolean = {
implicit val ctx = c
cont.contained().explanation match {
case "" => false
case _ => ctx.settings.explain.value
}
cont.contained().explanation.nonEmpty && c.settings.explain.value
}
}
}
Expand Down
Loading