Skip to content

Commit 9410003

Browse files
committed
Add links to Awsome Error Messages
Infrastructure to check URL exisitence in tests to keep links alive. Link checking is switched off automatically in case github.com can't be reached. It can be switched off via a system property as well.
1 parent 20239f0 commit 9410003

File tree

5 files changed

+232
-107
lines changed

5 files changed

+232
-107
lines changed

compiler/src/dotty/tools/dotc/reporting/diagnostic/Message.scala

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,17 @@ sealed trait DocumentationLink {
2222
def url: String
2323
}
2424

25+
/** A documentation link can be rendered by tooling to direct the programmer to
26+
* good resources for more details related to the compiler error or warning.
27+
*
28+
* To keep base links at a single place use the cases classes to share a common
29+
* prefix.
30+
*
31+
* Links are checked for existence in tests in `dotty.tools.dotc.reporting.ErrorMessagesTests`
32+
* if the error class is tested there.
33+
*/
2534
object DocumentationLink {
26-
case class LanguageSpec(text: String = "Language Specification", suffix: String) extends DocumentationLink {
35+
case class LanguageSpec(text: String = "Scala Language Specification", suffix: String) extends DocumentationLink {
2736
val url = s"https://www.scala-lang.org/files/archive/spec/2.13/$suffix"
2837
}
2938
case class TourUrl(text: String, suffix: String) extends DocumentationLink {

compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import dotty.tools.dotc.ast.Trees
2323
import dotty.tools.dotc.config.ScalaVersion
2424
import dotty.tools.dotc.core.Flags.{FlagSet, Mutable}
2525
import dotty.tools.dotc.core.SymDenotations.SymDenotation
26-
import dotty.tools.dotc.reporting.diagnostic.DocumentationLink.{DottyDocs, LanguageSpec}
26+
import diagnostic.DocumentationLink.{DottyDocs, LanguageSpec, TourUrl}
2727

2828
import scala.util.control.NonFatal
2929

@@ -99,10 +99,13 @@ object messages {
9999
import ast.tpd
100100

101101
/** Helper methods for messages */
102-
def implicitClassRestrictionsText(implicit ctx: Context) =
103-
hl"""|${NoColor("For a full list of restrictions on implicit classes visit")}
104-
|${Blue("http://docs.scala-lang.org/overviews/core/implicit-classes.html")}"""
102+
private def tryExpressionLinks = List(
103+
LanguageSpec(suffix = "06-expressions.html#try-expressions")
104+
)
105105

106+
private def implicitClassLinks = List(
107+
TourUrl("More information on implicit classes", "core/implicit-classes.html")
108+
)
106109

107110
// Syntax Errors ---------------------------------------------------------- //
108111
abstract class EmptyCatchOrFinallyBlock(tryBody: untpd.Tree, errNo: ErrorMessageID)(implicit ctx: Context)
@@ -140,9 +143,7 @@ object messages {
140143
|correctly handles transfer functions like ${"return"}."""
141144
}
142145

143-
override def links: List[DocumentationLink] = List(
144-
LanguageSpec(suffix = "06-expressions.html#try-expressions")
145-
)
146+
override def links = tryExpressionLinks
146147
}
147148

148149
case class EmptyCatchBlock(tryBody: untpd.Tree)(implicit ctx: Context)
@@ -151,6 +152,8 @@ object messages {
151152
val msg =
152153
hl"""|The ${"catch"} block does not contain a valid expression, try
153154
|adding a case like - `${"case e: Exception =>"}` to the block"""
155+
156+
override def links = tryExpressionLinks
154157
}
155158

156159
case class EmptyCatchAndFinallyBlock(tryBody: untpd.Tree)(implicit ctx: Context)
@@ -159,6 +162,8 @@ object messages {
159162
val msg =
160163
hl"""|A ${"try"} without ${"catch"} or ${"finally"} is equivalent to putting
161164
|its body in a block; no exceptions are handled."""
165+
166+
override def links = tryExpressionLinks
162167
}
163168

164169
case class DeprecatedWithOperator()(implicit ctx: Context)
@@ -452,8 +457,6 @@ object messages {
452457
|the same name created by the compiler which would cause a naming conflict if it
453458
|were allowed.
454459
|
455-
|""" + implicitClassRestrictionsText + hl"""|
456-
|
457460
|To resolve the conflict declare ${cdef.name} inside of an ${"object"} then import the class
458461
|from the object at the use site if needed, for example:
459462
|
@@ -462,8 +465,10 @@ object messages {
462465
|}
463466
|
464467
|// At the use site:
465-
|import Implicits.${cdef.name}"""
468+
|${"import Implicits"}.${cdef.name}"""
466469
}
470+
471+
override def links = implicitClassLinks
467472
}
468473

469474
case class ImplicitCaseClass(cdef: untpd.TypeDef)(implicit ctx: Context)
@@ -474,9 +479,10 @@ object messages {
474479
val explanation =
475480
hl"""|implicit classes may not be case classes. Instead use a plain class:
476481
|
477-
|implicit class ${cdef.name}...
478-
|
479-
|""" + implicitClassRestrictionsText
482+
|${"implicit class"} ${cdef.name}...
483+
|"""
484+
485+
override def links = implicitClassLinks
480486
}
481487

482488
case class ImplicitClassPrimaryConstructorArity()(implicit ctx: Context)
@@ -491,7 +497,7 @@ object messages {
491497
|
492498
|While it’s possible to create an implicit class with more than one non-implicit argument,
493499
|such classes aren’t used during implicit lookup.
494-
|""" + implicitClassRestrictionsText
500+
|"""
495501
}
496502
}
497503

@@ -556,17 +562,19 @@ object messages {
556562
val kind = "Syntax"
557563
val msg = "error in interpolated string: identifier or block expected"
558564
val explanation = {
559-
val code1 = "s\"$new Point(0, 0)\""
560-
val code2 = "s\"${new Point(0, 0)}\""
561565
hl"""|This usually happens when you forget to place your expressions inside curly braces.
562566
|
563-
|$code1
567+
|${"s\"$new Point(0, 0)\""}
564568
|
565569
|should be written as
566570
|
567-
|$code2
571+
|${"s\"${new Point(0, 0)}\""}
568572
|"""
569573
}
574+
575+
override def links = List(
576+
TourUrl("More on String Interpolation", "core/string-interpolation.html")
577+
)
570578
}
571579

572580
case class UnboundPlaceholderParameter()(implicit ctx:Context)
@@ -1720,19 +1728,13 @@ object messages {
17201728
val kind = "Syntax"
17211729
val msg = s"modifier(s) `$flags' not allowed for ${printableType.getOrElse("combination")}"
17221730
val explanation = {
1723-
val first = "sealed def y: Int = 1"
1724-
val second = "sealed lazy class z"
17251731
hl"""You tried to use a modifier that is inapplicable for the type of item under modification
1726-
|
1727-
| Please see the official Scala Language Specification section on modifiers:
1728-
| https://www.scala-lang.org/files/archive/spec/2.11/05-classes-and-objects.html#modifiers
17291732
|
17301733
|Consider the following example:
1731-
|$first
1734+
|${"sealed def y: Int = 1"}
17321735
|In this instance, the modifier 'sealed' is not applicable to the item type 'def' (method)
1733-
|$second
1734-
|In this instance, the modifier combination is not supported
1735-
"""
1736+
|${"sealed lazy class z"}
1737+
|In this instance, the modifier combination is not supported"""
17361738
}
17371739

17381740
override def links: List[DocumentationLink] = List(

compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTest.scala

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,4 @@ trait ErrorMessagesTest extends DottyTest {
4444
}
4545
}
4646

47-
def assertMessageCount(expected: Int, messages: List[Message]): Unit =
48-
assertEquals(messages.mkString,
49-
expected,
50-
messages.length
51-
)
5247
}

0 commit comments

Comments
 (0)