From 5209c752cf0a8d94892ef29780802eb80057bc71 Mon Sep 17 00:00:00 2001 From: Enno Runne Date: Wed, 27 Sep 2017 21:21:20 +0200 Subject: [PATCH 1/6] Add links to Awsome Error Messages Error messages should support including links for further reading that can be picked up by tools like IDEs. Examples are links to the language specification, or the "changes in Dotty" pages. --- .../dotc/reporting/ConsoleReporter.scala | 4 ++- .../dotc/reporting/MessageRendering.scala | 28 +++++++++++++++---- .../dotc/reporting/diagnostic/Message.scala | 22 +++++++++++++++ .../diagnostic/MessageContainer.scala | 5 +--- .../dotc/reporting/diagnostic/messages.scala | 27 ++++++++++++++++++ sbt-bridge/src/xsbt/DelegatingReporter.scala | 3 +- 6 files changed, 77 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala b/compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala index eb40cab959a2..b763f3b6933e 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala @@ -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`") } diff --git a/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala b/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala index 65443a2377e3..43466fb76bf9 100644 --- a/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala +++ b/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala @@ -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 { @@ -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 diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/Message.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/Message.scala index 09d7ae9751be..5ba31a23549f 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/Message.scala @@ -17,6 +17,24 @@ object Message { new NoExplanation(str) } +sealed trait DocumentationLink { + def text: String + def url: String +} + +object DocumentationLink { + case class LanguageSpec(text: String = "Language Specification", suffix: String) extends DocumentationLink { + val url = s"https://www.scala-lang.org/files/archive/spec/2.13/$suffix" + } + case class TourUrl(text: String, suffix: String) extends DocumentationLink { + val url = s"http://docs.scala-lang.org/overviews/$suffix" + } + case class DottyDocs(text: String = "Dotty documentation", suffix: String) extends DocumentationLink { + val url = s"http://dotty.epfl.ch/docs/$suffix" + } + case class FullUrl(text: String, url: 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 @@ -56,6 +74,8 @@ abstract class Message(val errorId: ErrorMessageID) { self => */ def explanation: String + def links: List[DocumentationLink] = Nil + /** 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. @@ -64,6 +84,8 @@ abstract class Message(val errorId: ErrorMessageID) { self => val msg = self.msg val kind = self.kind val explanation = self.explanation + private val persistedLinks = self.links + override def links = persistedLinks } } diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/MessageContainer.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/MessageContainer.scala index c2d94030ca0b..a4fda666d822 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/MessageContainer.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/MessageContainer.scala @@ -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 } } } diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index c98e219a027a..c9435467b12a 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -23,6 +23,8 @@ import dotty.tools.dotc.ast.Trees import dotty.tools.dotc.config.ScalaVersion import dotty.tools.dotc.core.Flags._ import dotty.tools.dotc.core.SymDenotations.SymDenotation +import dotty.tools.dotc.reporting.diagnostic.DocumentationLink.{DottyDocs, LanguageSpec} + import scala.util.control.NonFatal object messages { @@ -137,6 +139,10 @@ object messages { |It is recommended to use the ${"NonFatal"} extractor to catch all exceptions as it |correctly handles transfer functions like ${"return"}.""" } + + override def links: List[DocumentationLink] = List( + LanguageSpec(suffix = "06-expressions.html#try-expressions") + ) } case class EmptyCatchBlock(tryBody: untpd.Tree)(implicit ctx: Context) @@ -164,6 +170,10 @@ object messages { 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"}'.""" + + override def links = List( + DottyDocs(suffix = "reference/intersection-types.html") + ) } case class CaseClassMissingParamList(cdef: untpd.TypeDef)(implicit ctx: Context) @@ -176,6 +186,10 @@ object messages { 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}.""" + + override def links = List( + LanguageSpec(suffix = "05-classes-and-objects.html#case-classes") + ) } case class AnonymousFunctionMissingParamType(param: untpd.ValDef, @@ -208,6 +222,10 @@ object messages { |Make sure you give it a type of what you expect to match and help the type inference system: | |${"val f: Any => Int = { case x: Int => x + 1 }"} """ + + override def links = List( + LanguageSpec(suffix = "06-expressions.html#anonymous-functions") + ) } case class WildcardOnTypeArgumentNotAllowedOnNew()(implicit ctx: Context) @@ -409,7 +427,12 @@ object messages { | |$code2 |""" + } + + override def links = List( + DottyDocs(suffix = "reference/trait-parameters.html") + ) } case class TopLevelImplicitClass(cdef: untpd.TypeDef)(implicit ctx: Context) @@ -1711,6 +1734,10 @@ object messages { |In this instance, the modifier combination is not supported """ } + + override def links: List[DocumentationLink] = List( + LanguageSpec("Please see the official Scala Language Specification section on modifiers", + "05-classes-and-objects.html#modifiers")) } case class FunctionTypeNeedsNonEmptyParameterList(isImplicit: Boolean = true, isErased: Boolean = true)(implicit ctx: Context) diff --git a/sbt-bridge/src/xsbt/DelegatingReporter.scala b/sbt-bridge/src/xsbt/DelegatingReporter.scala index ffc4792ecef0..bac19b6bec99 100644 --- a/sbt-bridge/src/xsbt/DelegatingReporter.scala +++ b/sbt-bridge/src/xsbt/DelegatingReporter.scala @@ -47,8 +47,9 @@ final class DelegatingReporter(delegate: xsbti.Reporter) extends Reporter val sb = new StringBuilder() sb.append(messageAndPos(cont.contained(), cont.pos, diagnosticLevel(cont))) - if (ctx.shouldExplain(cont) && cont.contained().explanation.nonEmpty) { + if (ctx.shouldExplain(cont)) { sb.append(explanation(cont.contained())) + if (cont.contained().links.nonEmpty) sb.append(documentationLinks(cont.contained())) } delegate.log(position, sb.toString(), severity) From 23f02c3f5f9fff340bcc3741cc5890241b22bab4 Mon Sep 17 00:00:00 2001 From: Enno Runne Date: Thu, 5 Oct 2017 21:03:55 +0200 Subject: [PATCH 2/6] 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. --- .../dotc/reporting/diagnostic/Message.scala | 11 +- .../dotc/reporting/diagnostic/messages.scala | 56 +++--- .../dotc/reporting/ErrorMessagesTest.scala | 5 - .../dotc/reporting/ErrorMessagesTests.scala | 174 ++++++++++-------- .../dotc/reporting/UrlExistenceChecker.scala | 93 ++++++++++ 5 files changed, 232 insertions(+), 107 deletions(-) create mode 100644 compiler/test/dotty/tools/dotc/reporting/UrlExistenceChecker.scala diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/Message.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/Message.scala index 5ba31a23549f..c4eb48099414 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/Message.scala @@ -22,8 +22,17 @@ sealed trait DocumentationLink { def url: 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(text: String = "Language Specification", suffix: String) extends DocumentationLink { + case class LanguageSpec(text: String = "Scala Language Specification", suffix: String) extends DocumentationLink { val url = s"https://www.scala-lang.org/files/archive/spec/2.13/$suffix" } case class TourUrl(text: String, suffix: String) extends DocumentationLink { diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index c9435467b12a..f1096579ede1 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -23,7 +23,7 @@ import dotty.tools.dotc.ast.Trees import dotty.tools.dotc.config.ScalaVersion import dotty.tools.dotc.core.Flags._ import dotty.tools.dotc.core.SymDenotations.SymDenotation -import dotty.tools.dotc.reporting.diagnostic.DocumentationLink.{DottyDocs, LanguageSpec} +import diagnostic.DocumentationLink.{DottyDocs, LanguageSpec, TourUrl} import scala.util.control.NonFatal @@ -99,10 +99,13 @@ object messages { 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")}""" + private def tryExpressionLinks = List( + LanguageSpec(suffix = "06-expressions.html#try-expressions") + ) + private def implicitClassLinks = List( + TourUrl("More information on implicit classes", "core/implicit-classes.html") + ) // Syntax Errors ---------------------------------------------------------- // abstract class EmptyCatchOrFinallyBlock(tryBody: untpd.Tree, errNo: ErrorMessageID)(implicit ctx: Context) @@ -140,9 +143,7 @@ object messages { |correctly handles transfer functions like ${"return"}.""" } - override def links: List[DocumentationLink] = List( - LanguageSpec(suffix = "06-expressions.html#try-expressions") - ) + override def links = tryExpressionLinks } case class EmptyCatchBlock(tryBody: untpd.Tree)(implicit ctx: Context) @@ -151,6 +152,8 @@ object messages { val msg = hl"""|The ${"catch"} block does not contain a valid expression, try |adding a case like - `${"case e: Exception =>"}` to the block""" + + override def links = tryExpressionLinks } case class EmptyCatchAndFinallyBlock(tryBody: untpd.Tree)(implicit ctx: Context) @@ -159,6 +162,8 @@ object messages { val msg = hl"""|A ${"try"} without ${"catch"} or ${"finally"} is equivalent to putting |its body in a block; no exceptions are handled.""" + + override def links = tryExpressionLinks } case class DeprecatedWithOperator()(implicit ctx: Context) @@ -452,8 +457,6 @@ object messages { |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: | @@ -462,8 +465,10 @@ object messages { |} | |// At the use site: - |import Implicits.${cdef.name}""" + |${"import Implicits"}.${cdef.name}""" } + + override def links = implicitClassLinks } case class ImplicitCaseClass(cdef: untpd.TypeDef)(implicit ctx: Context) @@ -474,9 +479,10 @@ object messages { val explanation = hl"""|implicit classes may not be case classes. Instead use a plain class: | - |implicit class ${cdef.name}... - | - |""" + implicitClassRestrictionsText + |${"implicit class"} ${cdef.name}... + |""" + + override def links = implicitClassLinks } case class ImplicitClassPrimaryConstructorArity()(implicit ctx: Context) @@ -491,7 +497,7 @@ object messages { | |While it’s possible to create an implicit class with more than one non-implicit argument, |such classes aren’t used during implicit lookup. - |""" + implicitClassRestrictionsText + |""" } } @@ -556,17 +562,19 @@ object messages { 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 + |${"s\"$new Point(0, 0)\""} | |should be written as | - |$code2 + |${"s\"${new Point(0, 0)}\""} |""" } + + override def links = List( + TourUrl("More on String Interpolation", "core/string-interpolation.html") + ) } case class UnboundPlaceholderParameter()(implicit ctx:Context) @@ -1720,19 +1728,13 @@ object messages { val kind = "Syntax" val msg = s"modifier(s) `$flags' not allowed for ${printableType.getOrElse("combination")}" val explanation = { - val first = "sealed def y: Int = 1" - val second = "sealed lazy class z" hl"""You tried to use a modifier that is inapplicable for the type of item under modification - | - | Please see the official Scala Language Specification section on modifiers: - | https://www.scala-lang.org/files/archive/spec/2.11/05-classes-and-objects.html#modifiers | |Consider the following example: - |$first + |${"sealed def y: Int = 1"} |In this instance, the modifier 'sealed' is not applicable to the item type 'def' (method) - |$second - |In this instance, the modifier combination is not supported - """ + |${"sealed lazy class z"} + |In this instance, the modifier combination is not supported""" } override def links: List[DocumentationLink] = List( diff --git a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTest.scala b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTest.scala index 05280e9f7546..5eb7087aee06 100644 --- a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTest.scala +++ b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTest.scala @@ -44,9 +44,4 @@ trait ErrorMessagesTest extends DottyTest { } } - def assertMessageCount(expected: Int, messages: List[Message]): Unit = - assertEquals(messages.mkString, - expected, - messages.length - ) } diff --git a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala index 25f1e8868c93..c363128af9dd 100644 --- a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala +++ b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala @@ -3,15 +3,39 @@ package dotc package reporting import core.Contexts.Context +import diagnostic.Message import diagnostic.messages._ import dotty.tools.dotc.core.Flags import dotty.tools.dotc.core.Flags.FlagSet import dotty.tools.dotc.core.Types.WildcardType import dotty.tools.dotc.parsing.Tokens import org.junit.Assert._ -import org.junit.Test +import org.junit.{AfterClass, BeforeClass, Test} + +object ErrorMessagesTests extends UrlExistenceChecker { + @BeforeClass + def initConnectivityCheck(): Unit = checkForConnectivity() + + @AfterClass + def evaluateConnectivityCheck(): Unit = checkUrlResponses() +} class ErrorMessagesTests extends ErrorMessagesTest { + import ErrorMessagesTests.checkLinks + + private def checkDocumentationLinks(messages: List[Message]): Unit = + messages.foreach { message => + checkLinks(message.getClass.getSimpleName, message.links.map(_.url)) + } + + def assertMessageCountAndCheckLinks(expected: Int, messages: List[Message]): Unit = { + assertEquals(messages.mkString, + expected, + messages.length + ) + checkDocumentationLinks(messages) + } + // In the case where there are no errors, we can do "expectNoErrors" in the // `Report` @Test def noErrors = @@ -31,7 +55,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { val defn = ictx.definitions // Assert that we only got one error message - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) // Pattern match out the expected error val TypeMismatch(found, expected, _, _) :: Nil = messages @@ -57,7 +81,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val OverridesNothing(member) :: Nil = messages assertEquals("bar", member.name.show) } @@ -78,7 +102,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val OverridesNothingButNameExists(member, sameName) :: Nil = messages // check expected context data assertEquals("bar", member.name.show) @@ -102,7 +126,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val ForwardReferenceExtendsOverDefinition(value, definition) :: Nil = messages assertEquals("value b", value.show) assertEquals("value a", definition.show) @@ -119,7 +143,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val ExpectedTokenButFound(expected, found) :: Nil = messages assertEquals(Tokens.IDENTIFIER, expected) assertEquals(Tokens.VAL, found) @@ -150,7 +174,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val MixedLeftAndRightAssociativeOps(op1, op2, op2LeftAssoc) :: Nil = messages assertEquals("+-", op1.show) assertEquals("+:", op2.show) @@ -169,7 +193,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val CantInstantiateAbstractClassOrTrait(cls, isTrait) :: Nil = messages assertEquals("Concept", cls.name.show) assertFalse("expected class", isTrait) @@ -187,7 +211,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val CantInstantiateAbstractClassOrTrait(cls, isTrait) :: Nil = messages assertEquals("Concept", cls.name.show) assertTrue("expected trait", isTrait) @@ -205,7 +229,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val OverloadedOrRecursiveMethodNeedsResultType(tree) :: Nil = messages assertEquals("foo", tree.show) } @@ -221,7 +245,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val OverloadedOrRecursiveMethodNeedsResultType(tree) :: Nil = messages assertEquals("i", tree.show) } @@ -237,7 +261,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val RecursiveValueNeedsResultType(tree) :: Nil = messages assertEquals("i", tree.show) } @@ -254,7 +278,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val CyclicReferenceInvolving(denot) :: Nil = messages assertEquals("value x", denot.show) } @@ -274,7 +298,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val CyclicReferenceInvolvingImplicit(tree) :: Nil = messages assertEquals("x", tree.name.show) } @@ -297,7 +321,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val SuperQualMustBeParent(qual, cls) :: Nil = messages assertEquals("B", qual.show) @@ -326,7 +350,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { import typer.Typer.BindingPrec._ - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val AmbiguousImport(name, newPrec, prevPrec, prevCtx) :: Nil = messages assertEquals("ToBeImported", name.show) assertEquals(namedImport, newPrec) @@ -345,7 +369,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val MethodDoesNotTakeParameters(tree, methodPart) :: Nil = messages assertEquals("Scope.foo", tree.show) @@ -364,7 +388,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val MethodDoesNotTakeParameters(tree, methodPart) :: Nil = messages assertEquals("Scope.foo(1)", tree.show) @@ -388,7 +412,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val AmbiguousOverload(tree, List(alt1, alt2), pt: WildcardType) :: Nil = messages assertEquals("method foo", alt1.show) assertEquals("(s: String): String", alt1.info.show) @@ -406,7 +430,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val ReassignmentToVal(name) :: Nil = messages assertEquals("value", name.show) } @@ -421,7 +445,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val TypeDoesNotTakeParameters(tpe, params) :: Nil = messages assertEquals("WithOutParams", tpe.show) } @@ -436,7 +460,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val ParameterizedTypeLacksArguments(symbol) :: Nil = messages assertEquals("trait WithParams", symbol.show) } @@ -447,7 +471,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val VarValParametersMayNotBeCallByName(name, false) :: Nil = messages assertEquals("noNoNo", name.show) } @@ -460,7 +484,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val MissingTypeParameterFor(tpe) :: Nil = messages assertEquals("List", tpe.show) } @@ -474,7 +498,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val DoesNotConformToBound(tpe, which, bound) :: Nil = messages assertEquals("Int", tpe.show) assertEquals("upper", which) @@ -492,7 +516,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val DoesNotConformToSelfType(category, selfType, cls, otherSelf, relation, other) :: Nil = messages assertEquals("illegal inheritance", category) assertEquals("Blended", selfType.show) @@ -513,7 +537,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val DoesNotConformToSelfTypeCantBeInstantiated(tpe, selfType) :: Nil = messages assertEquals("RequiresBase", tpe.show) assertEquals("Base", selfType.show) @@ -528,7 +552,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val AbstractMemberMayNotHaveModifier(symbol, flags) :: Nil = messages assertEquals("value s", symbol.show) assertEquals("final", flags.toString) @@ -543,7 +567,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val TopLevelCantBeImplicit(symbol) :: Nil = messages assertEquals("object S", symbol.show) } @@ -557,7 +581,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val TypesAndTraitsCantBeImplicit(symbol) :: Nil = messages assertEquals("trait S", symbol.show) } @@ -571,7 +595,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val OnlyClassesCanBeAbstract(symbol) :: Nil = messages assertEquals("value s", symbol.show) } @@ -585,7 +609,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val AbstractOverrideOnlyInTraits(symbol) :: Nil = messages assertEquals("value s", symbol.show) } @@ -596,7 +620,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val TraitsMayNotBeFinal(symbol) :: Nil = messages assertEquals("trait Foo", symbol.show) } @@ -610,7 +634,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val NativeMembersMayNotHaveImplementation(symbol) :: Nil = messages assertEquals("method foo", symbol.show) } @@ -624,7 +648,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val OnlyClassesCanHaveDeclaredButUndefinedMembers(symbol) :: Nil = messages assertEquals("method foo", symbol.show) } @@ -635,7 +659,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val CannotExtendAnyVal(symbol) :: Nil = messages assertEquals("trait Foo", symbol.show) } @@ -651,7 +675,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val CannotHaveSameNameAs(symbol, cls, _) :: Nil = messages assertEquals("class A", symbol.show) assertEquals("class A", cls.show) @@ -666,7 +690,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val ValueClassesMayNotDefineInner(valueClass, inner) :: Nil = messages assertEquals("class MyValue", valueClass.show) assertEquals("class Inner", inner.show) @@ -681,7 +705,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val ValueClassesMayNotDefineNonParameterField(valueClass, field) :: Nil = messages assertEquals("class MyValue", valueClass.show) assertEquals("value illegal", field.show) @@ -696,7 +720,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val ValueClassesMayNotDefineASecondaryConstructor(valueClass, constuctor) :: Nil = messages assertEquals("class MyValue", valueClass.show) assertEquals("constructor MyValue", constuctor.show) @@ -711,7 +735,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val ValueClassesMayNotContainInitalization(valueClass) :: Nil = messages assertEquals("class MyValue", valueClass.show) } @@ -725,7 +749,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val ValueClassesMayNotBeContainted(valueClass) :: Nil = messages assertEquals("class MyValue", valueClass.show) } @@ -736,7 +760,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val ValueClassesMayNotWrapItself(valueClass) :: Nil = messages assertEquals("class MyValue", valueClass.show) } @@ -747,7 +771,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val ValueClassParameterMayNotBeAVar(valueClass, param) :: Nil = messages assertEquals("class MyValue", valueClass.show) assertEquals("variable i", param.show) @@ -759,7 +783,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val ValueClassNeedsOneValParam(valueClass) :: Nil = messages assertEquals("class MyValue", valueClass.show) } @@ -770,7 +794,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val err :: Nil = messages assertEquals(err, OnlyCaseClassOrCaseObjectAllowed()) } @@ -781,7 +805,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val err :: Nil = messages assertEquals(err, ExpectedClassOrObjectDef()) } @@ -796,7 +820,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (itcx, messages) => implicit val ctx: Context = itcx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val err :: Nil = messages assertEquals(err, ImplicitClassPrimaryConstructorArity()) } @@ -811,7 +835,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val AnonymousFunctionMissingParamType(param, args, _, pt) = messages.head assertEquals("x$1", param.show) assertEquals("?", pt.show) @@ -831,7 +855,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val err :: Nil = messages val SuperCallsNotAllowedInline(symbol) = err assertEquals("method bar", symbol.show) @@ -848,7 +872,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { checkMessagesAfter("refchecks")(code) .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val ModifiersNotAllowed(flags, sort) :: Nil = messages assertEquals(modifierAssertion, flags.toString) assertEquals(typeAssertion, sort) @@ -866,7 +890,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val err :: Nil = messages assertEquals(err, WildcardOnTypeArgumentNotAllowedOnNew()) @@ -882,7 +906,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(2, messages) + assertMessageCountAndCheckLinks(2, messages) messages.foreach(assertEquals(_, FunctionTypeNeedsNonEmptyParameterList())) } @@ -896,7 +920,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val err :: Nil = messages assertEquals(err, WrongNumberOfParameters(1)) @@ -911,7 +935,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val err :: Nil = messages assertEquals(DuplicatePrivateProtectedQualifier(), err) @@ -924,7 +948,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val err :: Nil = messages assertEquals(ExpectedStartOfTopLevelDefinition(), err) @@ -939,7 +963,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { }.expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val MissingReturnTypeWithReturnStatement(method) :: Nil = messages assertEquals(method.name.show, "bad") @@ -954,7 +978,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { }.expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val NoReturnFromInline(method) :: Nil = messages assertEquals("method usesReturn", method.show) @@ -968,7 +992,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { """.stripMargin }.expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val ReturnOutsideMethodDefinition(owner) :: Nil = messages assertEquals("object A", owner.show) } @@ -980,7 +1004,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { """.stripMargin }.expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val ExtendFinalClass(extender, parent) :: Nil = messages assertEquals(extender.show, "class B") assertEquals(parent.show, "class A") @@ -996,7 +1020,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { """.stripMargin }.expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val TailrecNotApplicable(method) :: Nil = messages assertEquals(method.show, "method foo") } @@ -1010,7 +1034,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { }.expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val ExpectedTypeBoundOrEquals(found) :: Nil = messages assertEquals(Tokens.IDENTIFIER, found) } @@ -1028,7 +1052,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { }.expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val ClassAndCompanionNameClash(cls, other) :: Nil = messages assertEquals("class T", cls.owner.show) @@ -1051,7 +1075,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { }.expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val OnlyFunctionsCanBeFollowedByUnderscore(pt) :: Nil = messages assertEquals("String(n)", pt.show) } @@ -1070,7 +1094,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val MissingEmptyArgumentList(method) :: Nil = messages assertEquals("method greet", method.show) } @@ -1089,7 +1113,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(2, messages) + assertMessageCountAndCheckLinks(2, messages) val DuplicateNamedTypeParameter(n2) :: DuplicateNamedTypeParameter(n1) :: Nil = messages assertEquals("A", n1.show) assertEquals("B", n2.show) @@ -1109,7 +1133,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(2, messages) + assertMessageCountAndCheckLinks(2, messages) val UndefinedNamedTypeParameter(n2, l2) :: UndefinedNamedTypeParameter(n1, l1) :: Nil = messages val tpParams = List("A", "B") assertEquals("C", n1.show) @@ -1131,7 +1155,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(2, messages) + assertMessageCountAndCheckLinks(2, messages) val errWithModifier :: err :: Nil = messages assertEquals(IllegalStartOfStatement(isModifier = false), err) @@ -1154,7 +1178,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val TraitIsExpected(symbol) :: Nil = messages assertEquals("class B", symbol.show) } @@ -1170,7 +1194,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val TraitRedefinedFinalMethodFromAnyRef(method) = messages.head assertEquals("method wait", method.show) } @@ -1205,7 +1229,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val UnapplyInvalidNumberOfArguments(qual, argTypes) :: Nil = messages assertEquals("Boo", qual.show) assertEquals("(class Int, class String)", argTypes.map(_.typeSymbol).mkString("(", ", ", ")")) @@ -1220,6 +1244,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { """.stripMargin }.expect { (ictx, messages) => implicit val ctx: Context = ictx + assertMessageCountAndCheckLinks(1, messages) val StaticFieldsOnlyAllowedInObjects(field) = messages.head assertEquals(field.show, "method bar") } @@ -1231,7 +1256,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val CyclicInheritance(symbol, _) :: Nil = messages assertEquals("class A", symbol.show) } @@ -1245,6 +1270,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { """.stripMargin }.expect { (itcx, messages) => implicit val ctx: Context = itcx + assertMessageCountAndCheckLinks(1, messages) val MissingCompanionForStatic(member) = messages.head assertEquals(member.show, "method bar") } @@ -1260,7 +1286,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { }.expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val PolymorphicMethodMissingTypeInParent(rsym, parentSym) = messages.head assertEquals("method get", rsym.show) assertEquals("class Object", parentSym.show) @@ -1277,7 +1303,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { }.expect { (itcx, messages) => implicit val ctx: Context = itcx - assertMessageCount(1, messages) + assertMessageCountAndCheckLinks(1, messages) val JavaSymbolIsNotAValue(symbol) = messages.head assertEquals(symbol.show, "package p") } diff --git a/compiler/test/dotty/tools/dotc/reporting/UrlExistenceChecker.scala b/compiler/test/dotty/tools/dotc/reporting/UrlExistenceChecker.scala new file mode 100644 index 000000000000..20d5ba8f69bb --- /dev/null +++ b/compiler/test/dotty/tools/dotc/reporting/UrlExistenceChecker.scala @@ -0,0 +1,93 @@ +package dotty.tools.dotc.reporting + +import java.net.{HttpURLConnection, SocketTimeoutException, URL, UnknownHostException} +import java.util.concurrent.atomic.AtomicBoolean + +import org.junit.Assert.fail + +import scala.concurrent.{Await, Future} +import scala.concurrent.ExecutionContext.Implicits.global +import scala.util.control.NonFatal + +/** Checks URL existence by HTTP requests expecting OK responses (200). + * + * To not fail if no network is available all URL tests are cancelled if in case + * "https://www.github.com/" is not available. + * + * URL testing can be switched off via system property `linkChecking=false`. + */ +trait UrlExistenceChecker { + + private[this] val runChecks = new AtomicBoolean(true) + private[this] val httpOk = 200 + + private[this] var checks: List[Future[String]] = Nil + private[this] val urlTimeout = 2000 //ms + private[this] val systemProperty = "linkChecking" //ms + + /** Tries to connect to "https://www.github.com/" and cancels URL testing if + * unavailable. + * Checks system property `linkChecking` to contain `true`. + */ + def checkForConnectivity(): Unit = + if (sys.props.getOrElse(systemProperty, "true") == "true") + Future { + try { + if (connectTo("https://www.github.com/") != httpOk) noChecking() + } catch { + case NonFatal(_) => + noChecking() + } + } + else { + runChecks.set(false) + println(s"Warning: URL checking has been switched off via system property `$systemProperty`.") + } + + def checkLinks(message: String, links: List[String]): Unit = + if (runChecks.get()) + links.foreach(url => + checks = checkUrl(message, url) :: checks) + + def checkUrlResponses(): Unit = + if (runChecks.get()) { + import scala.concurrent.duration.DurationInt + val futureSeq = Future.sequence(checks) + val res = Await.result(futureSeq, 5.seconds) + val resStr = res.filter(_.nonEmpty) + if (resStr.nonEmpty) { + fail( + s"""Some URLs did not get response OK (200): + | ${resStr.mkString("\n ")} + |(Switch URL checking off in sbt via `set javaOptions in Test += "-DlinkChecking=false"`)""".stripMargin) + } + } + + private def checkUrl(message: String, url: String) = Future { + try { + if (connectTo(url) != httpOk) + s"$message: No OK response from $url" + else "" + } catch { + case _: SocketTimeoutException => + s"$message: Connect timeout for $url" + case _: UnknownHostException => + s"$message: Unknown host for $url" + case NonFatal(e) => + s"$message: $e for $url" + } + } + + private def connectTo(url: String): Int = { + val conn = new URL(url).openConnection().asInstanceOf[HttpURLConnection] + conn.setRequestMethod("HEAD") + conn.setConnectTimeout(urlTimeout) + conn.setReadTimeout(urlTimeout) + conn.getResponseCode + } + + private def noChecking(): Unit = { + runChecks.set(false) + println("Warning: URL checking has been switched off as github.com can't be reached.") + } +} From 41878e177b821be671600537af16b7f913bfc094 Mon Sep 17 00:00:00 2001 From: Enno Runne Date: Thu, 5 Oct 2017 22:50:08 +0200 Subject: [PATCH 3/6] Add links to Awsome Error Messages * Declare `links` as `val` instead. * Added a couple of links to specs and docs --- .../dotc/reporting/diagnostic/Message.scala | 11 +++-- .../dotc/reporting/diagnostic/messages.scala | 49 ++++++++++++++----- 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/Message.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/Message.scala index c4eb48099414..25dbb8af12a6 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/Message.scala @@ -38,6 +38,9 @@ object DocumentationLink { case class TourUrl(text: String, suffix: String) extends DocumentationLink { val url = s"http://docs.scala-lang.org/overviews/$suffix" } + case class Sip(text: String, suffix: String) extends DocumentationLink { + val url = s"http://docs.scala-lang.org/sips/$suffix" + } case class DottyDocs(text: String = "Dotty documentation", suffix: String) extends DocumentationLink { val url = s"http://dotty.epfl.ch/docs/$suffix" } @@ -83,7 +86,10 @@ abstract class Message(val errorId: ErrorMessageID) { self => */ def explanation: String - def links: List[DocumentationLink] = Nil + /** Links may list URLs to Internet resources related to the error + * e.g. the Scala Language Specification. + */ + val links: List[DocumentationLink] = Nil /** The implicit `Context` in messages is a large thing that we don't want * persisted. This method gets around that by duplicating the message @@ -93,8 +99,7 @@ abstract class Message(val errorId: ErrorMessageID) { self => val msg = self.msg val kind = self.kind val explanation = self.explanation - private val persistedLinks = self.links - override def links = persistedLinks + override val links = self.links } } diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index f1096579ede1..a7150c291e9c 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -107,6 +107,12 @@ object messages { TourUrl("More information on implicit classes", "core/implicit-classes.html") ) + private def patternMatchingLinks = List( + LanguageSpec(suffix = "08-pattern-matching.html"), + DottyDocs(suffix = "reference/changed/pattern-matching.html") + ) + + // Syntax Errors ---------------------------------------------------------- // abstract class EmptyCatchOrFinallyBlock(tryBody: untpd.Tree, errNo: ErrorMessageID)(implicit ctx: Context) extends Message(EmptyCatchOrFinallyBlockID) { @@ -143,7 +149,7 @@ object messages { |correctly handles transfer functions like ${"return"}.""" } - override def links = tryExpressionLinks + override val links = tryExpressionLinks } case class EmptyCatchBlock(tryBody: untpd.Tree)(implicit ctx: Context) @@ -153,7 +159,7 @@ object messages { hl"""|The ${"catch"} block does not contain a valid expression, try |adding a case like - `${"case e: Exception =>"}` to the block""" - override def links = tryExpressionLinks + override val links = tryExpressionLinks } case class EmptyCatchAndFinallyBlock(tryBody: untpd.Tree)(implicit ctx: Context) @@ -163,7 +169,7 @@ object messages { hl"""|A ${"try"} without ${"catch"} or ${"finally"} is equivalent to putting |its body in a block; no exceptions are handled.""" - override def links = tryExpressionLinks + override val links = tryExpressionLinks } case class DeprecatedWithOperator()(implicit ctx: Context) @@ -176,7 +182,7 @@ object messages { |use of the ${"with"} keyword. There are a few differences in |semantics between intersection types and using `${"with"}'.""" - override def links = List( + override val links = List( DottyDocs(suffix = "reference/intersection-types.html") ) } @@ -192,7 +198,7 @@ object messages { |have a singleton representation of ${cdef.name}, use a "${"case object"}". |Or, add an explicit `()' as a parameter list to ${cdef.name}.""" - override def links = List( + override val links = List( LanguageSpec(suffix = "05-classes-and-objects.html#case-classes") ) } @@ -228,7 +234,7 @@ object messages { | |${"val f: Any => Int = { case x: Int => x + 1 }"} """ - override def links = List( + override val links = List( LanguageSpec(suffix = "06-expressions.html#anonymous-functions") ) } @@ -435,7 +441,7 @@ object messages { } - override def links = List( + override val links = List( DottyDocs(suffix = "reference/trait-parameters.html") ) } @@ -468,7 +474,7 @@ object messages { |${"import Implicits"}.${cdef.name}""" } - override def links = implicitClassLinks + override val links = implicitClassLinks } case class ImplicitCaseClass(cdef: untpd.TypeDef)(implicit ctx: Context) @@ -482,7 +488,7 @@ object messages { |${"implicit class"} ${cdef.name}... |""" - override def links = implicitClassLinks + override val links = implicitClassLinks } case class ImplicitClassPrimaryConstructorArity()(implicit ctx: Context) @@ -572,7 +578,7 @@ object messages { |""" } - override def links = List( + override val links = List( TourUrl("More on String Interpolation", "core/string-interpolation.html") ) } @@ -828,6 +834,7 @@ object messages { | |$fixedVarInAlternative""" } + override val links = patternMatchingLinks } case class IdentifierExpected(identifier: String)(implicit ctx: Context) @@ -907,6 +914,10 @@ object messages { | - String Literals: "Hello, World!" | - null |""" + + override val links = List( + LanguageSpec(suffix = "01-lexical-syntax.html#literals") + ) } case class PatternMatchExhaustivity(uncovered: String)(implicit ctx: Context) @@ -924,6 +935,8 @@ object messages { | - If an extractor always return 'Some(...)', write 'Some[X]' for its return type | - Add a 'case _ => ...' at the end to match all remaining cases |""" + + override val links = patternMatchingLinks } case class UncheckedTypePattern(msg: String)(implicit ctx: Context) @@ -943,6 +956,7 @@ object messages { val kind = s"""Match ${hl"case"} Unreachable""" val msg = "unreachable code" val explanation = "" + override val links = patternMatchingLinks } case class SeqWildcardPatternPos()(implicit ctx: Context) @@ -967,6 +981,7 @@ object messages { | |would give 3 as a result""" } + override val links = patternMatchingLinks } case class IllegalStartOfSimplePattern()(implicit ctx: Context) @@ -1077,6 +1092,9 @@ object messages { | |${"List[_]"} |""" + override val links = List( + DottyDocs(suffix = "reference/dropped/existential-types.html") + ) } case class UnboundWildcardType()(implicit ctx: Context) @@ -1263,6 +1281,9 @@ object messages { | (all other special characters) |Operators starting with a letter have lowest precedence, followed by operators starting with `|`, etc. |""".stripMargin + override val links = List( + LanguageSpec(suffix = "06-expressions.html#infix-operations") + ) } case class CantInstantiateAbstractClassOrTrait(cls: Symbol, isTrait: Boolean)(implicit ctx: Context) @@ -1737,7 +1758,7 @@ object messages { |In this instance, the modifier combination is not supported""" } - override def links: List[DocumentationLink] = List( + override val links = List( LanguageSpec("Please see the official Scala Language Specification section on modifiers", "05-classes-and-objects.html#modifiers")) } @@ -1759,6 +1780,9 @@ object messages { | |$code2""".stripMargin } + override val links = List( + DottyDocs(suffix = "reference/implicit-function-types.html") + ) } case class WrongNumberOfParameters(expected: Int)(implicit ctx: Context) @@ -1782,6 +1806,9 @@ object messages { val msg = "expected start of definition" val explanation = hl"you have to provide either ${"class"}, ${"trait"}, ${"object"}, or ${"enum"} definitions after qualifiers" + override val links = List( + LanguageSpec(suffix = "09-top-level-definitions.html") + ) } case class NoReturnFromInline(owner: Symbol)(implicit ctx: Context) From f57b041210843c84123c5c6a323a1be6a66fef64 Mon Sep 17 00:00:00 2001 From: Enno Runne Date: Sat, 7 Oct 2017 17:31:11 +0200 Subject: [PATCH 4/6] Add links to Awsome Error Messages * Improved timeout handling for URL checking * Check unique URLs only once * Changed notation for URLs --- .../dotc/reporting/diagnostic/Message.scala | 12 ++-- .../dotc/reporting/diagnostic/messages.scala | 61 +++++----------- .../dotc/reporting/UrlExistenceChecker.scala | 70 +++++++++++-------- 3 files changed, 63 insertions(+), 80 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/Message.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/Message.scala index 25dbb8af12a6..94287e52b693 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/Message.scala @@ -18,8 +18,8 @@ object Message { } sealed trait DocumentationLink { - def text: String def url: String + def text: String } /** A documentation link can be rendered by tooling to direct the programmer to @@ -32,19 +32,19 @@ sealed trait DocumentationLink { * if the error class is tested there. */ object DocumentationLink { - case class LanguageSpec(text: String = "Scala Language Specification", suffix: String) extends 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(text: String, suffix: String) extends DocumentationLink { + case class TourUrl(suffix: String, text: String) extends DocumentationLink { val url = s"http://docs.scala-lang.org/overviews/$suffix" } - case class Sip(text: String, suffix: String) extends DocumentationLink { + case class Sip(suffix: String, text: String) extends DocumentationLink { val url = s"http://docs.scala-lang.org/sips/$suffix" } - case class DottyDocs(text: String = "Dotty documentation", suffix: String) extends DocumentationLink { + case class DottyDocs(suffix: String, text: String = "Dotty documentation") extends DocumentationLink { val url = s"http://dotty.epfl.ch/docs/$suffix" } - case class FullUrl(text: String, url: String) extends DocumentationLink + case class FullUrl(url: String, text: String) extends DocumentationLink } /** A `Message` contains all semantic information necessary to easily diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index a7150c291e9c..19facb658a1b 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -23,7 +23,7 @@ import dotty.tools.dotc.ast.Trees import dotty.tools.dotc.config.ScalaVersion import dotty.tools.dotc.core.Flags._ import dotty.tools.dotc.core.SymDenotations.SymDenotation -import diagnostic.DocumentationLink.{DottyDocs, LanguageSpec, TourUrl} +import diagnostic.DocumentationLink.{DottyDocs, FullUrl, LanguageSpec, TourUrl} import scala.util.control.NonFatal @@ -99,18 +99,13 @@ object messages { import ast.tpd /** Helper methods for messages */ - private def tryExpressionLinks = List( - LanguageSpec(suffix = "06-expressions.html#try-expressions") - ) + private def tryExpressionLinks = LanguageSpec("06-expressions.html#try-expressions") :: Nil - private def implicitClassLinks = List( - TourUrl("More information on implicit classes", "core/implicit-classes.html") - ) + private def implicitClassLinks = TourUrl("core/implicit-classes.html", "More information on implicit classes") :: Nil - private def patternMatchingLinks = List( - LanguageSpec(suffix = "08-pattern-matching.html"), - DottyDocs(suffix = "reference/changed/pattern-matching.html") - ) + private def patternMatchingLinks = + LanguageSpec("08-pattern-matching.html") :: + DottyDocs("reference/changed/pattern-matching.html") :: Nil // Syntax Errors ---------------------------------------------------------- // @@ -182,9 +177,7 @@ object messages { |use of the ${"with"} keyword. There are a few differences in |semantics between intersection types and using `${"with"}'.""" - override val links = List( - DottyDocs(suffix = "reference/intersection-types.html") - ) + override val links = DottyDocs("reference/intersection-types.html") :: Nil } case class CaseClassMissingParamList(cdef: untpd.TypeDef)(implicit ctx: Context) @@ -198,9 +191,7 @@ object messages { |have a singleton representation of ${cdef.name}, use a "${"case object"}". |Or, add an explicit `()' as a parameter list to ${cdef.name}.""" - override val links = List( - LanguageSpec(suffix = "05-classes-and-objects.html#case-classes") - ) + override val links = LanguageSpec("05-classes-and-objects.html#case-classes") :: Nil } case class AnonymousFunctionMissingParamType(param: untpd.ValDef, @@ -234,9 +225,7 @@ object messages { | |${"val f: Any => Int = { case x: Int => x + 1 }"} """ - override val links = List( - LanguageSpec(suffix = "06-expressions.html#anonymous-functions") - ) + override val links = LanguageSpec("06-expressions.html#anonymous-functions") :: Nil } case class WildcardOnTypeArgumentNotAllowedOnNew()(implicit ctx: Context) @@ -441,9 +430,7 @@ object messages { } - override val links = List( - DottyDocs(suffix = "reference/trait-parameters.html") - ) + override val links = DottyDocs("reference/trait-parameters.html") :: Nil } case class TopLevelImplicitClass(cdef: untpd.TypeDef)(implicit ctx: Context) @@ -578,9 +565,7 @@ object messages { |""" } - override val links = List( - TourUrl("More on String Interpolation", "core/string-interpolation.html") - ) + override val links = TourUrl("core/string-interpolation.html", "More on String Interpolation") :: Nil } case class UnboundPlaceholderParameter()(implicit ctx:Context) @@ -915,9 +900,7 @@ object messages { | - null |""" - override val links = List( - LanguageSpec(suffix = "01-lexical-syntax.html#literals") - ) + override val links = LanguageSpec(suffix = "01-lexical-syntax.html#literals") :: Nil } case class PatternMatchExhaustivity(uncovered: String)(implicit ctx: Context) @@ -1092,9 +1075,7 @@ object messages { | |${"List[_]"} |""" - override val links = List( - DottyDocs(suffix = "reference/dropped/existential-types.html") - ) + override val links = DottyDocs("reference/dropped/existential-types.html") :: Nil } case class UnboundWildcardType()(implicit ctx: Context) @@ -1281,9 +1262,7 @@ object messages { | (all other special characters) |Operators starting with a letter have lowest precedence, followed by operators starting with `|`, etc. |""".stripMargin - override val links = List( - LanguageSpec(suffix = "06-expressions.html#infix-operations") - ) + override val links = LanguageSpec("06-expressions.html#infix-operations") :: Nil } case class CantInstantiateAbstractClassOrTrait(cls: Symbol, isTrait: Boolean)(implicit ctx: Context) @@ -1758,9 +1737,7 @@ object messages { |In this instance, the modifier combination is not supported""" } - override val links = List( - LanguageSpec("Please see the official Scala Language Specification section on modifiers", - "05-classes-and-objects.html#modifiers")) + override val links = LanguageSpec("05-classes-and-objects.html#modifiers") :: Nil } case class FunctionTypeNeedsNonEmptyParameterList(isImplicit: Boolean = true, isErased: Boolean = true)(implicit ctx: Context) @@ -1780,9 +1757,7 @@ object messages { | |$code2""".stripMargin } - override val links = List( - DottyDocs(suffix = "reference/implicit-function-types.html") - ) + override val links = DottyDocs("reference/implicit-function-types.html") :: Nil } case class WrongNumberOfParameters(expected: Int)(implicit ctx: Context) @@ -1806,9 +1781,7 @@ object messages { val msg = "expected start of definition" val explanation = hl"you have to provide either ${"class"}, ${"trait"}, ${"object"}, or ${"enum"} definitions after qualifiers" - override val links = List( - LanguageSpec(suffix = "09-top-level-definitions.html") - ) + override val links = LanguageSpec(suffix = "09-top-level-definitions.html") :: Nil } case class NoReturnFromInline(owner: Symbol)(implicit ctx: Context) diff --git a/compiler/test/dotty/tools/dotc/reporting/UrlExistenceChecker.scala b/compiler/test/dotty/tools/dotc/reporting/UrlExistenceChecker.scala index 20d5ba8f69bb..88ddc8582b66 100644 --- a/compiler/test/dotty/tools/dotc/reporting/UrlExistenceChecker.scala +++ b/compiler/test/dotty/tools/dotc/reporting/UrlExistenceChecker.scala @@ -6,6 +6,7 @@ import java.util.concurrent.atomic.AtomicBoolean import org.junit.Assert.fail import scala.concurrent.{Await, Future} +import scala.concurrent.duration.DurationInt import scala.concurrent.ExecutionContext.Implicits.global import scala.util.control.NonFatal @@ -21,52 +22,65 @@ trait UrlExistenceChecker { private[this] val runChecks = new AtomicBoolean(true) private[this] val httpOk = 200 + private[this] var checkingUrls: Set[String] = Set.empty private[this] var checks: List[Future[String]] = Nil - private[this] val urlTimeout = 2000 //ms - private[this] val systemProperty = "linkChecking" //ms + private[this] val urlTimeout = 10.seconds + private[this] val urlTimeoutInt: Int = urlTimeout.toMillis.toInt + private[this] val systemProperty = "linkChecking" /** Tries to connect to "https://www.github.com/" and cancels URL testing if * unavailable. - * Checks system property `linkChecking` to contain `true`. + * Checks system property `linkChecking` be undefined or to contain `true`. */ - def checkForConnectivity(): Unit = + def checkForConnectivity(): Unit = { + def noChecking(text: String = + "Warning: URL checking has been switched off as github.com can't be reached."): Unit = { + runChecks.set(false) + println(text) + } + if (sys.props.getOrElse(systemProperty, "true") == "true") Future { try { - if (connectTo("https://www.github.com/") != httpOk) noChecking() + if (connectTo("https://www.github.com/", timeout = 2000) != httpOk) + noChecking() } catch { case NonFatal(_) => noChecking() } } - else { - runChecks.set(false) - println(s"Warning: URL checking has been switched off via system property `$systemProperty`.") - } + else + noChecking( + s"Warning: URL checking has been switched off via system property `$systemProperty`.") + } def checkLinks(message: String, links: List[String]): Unit = if (runChecks.get()) - links.foreach(url => - checks = checkUrl(message, url) :: checks) + links.foreach { url => + if (!checkingUrls.contains(url)) { + checkingUrls += url + checks = checkUrl(message, url) :: checks + } + } + /** Awaits completion of all URL checks and fails the test if any has a non empty + * String as a result. + */ def checkUrlResponses(): Unit = if (runChecks.get()) { - import scala.concurrent.duration.DurationInt - val futureSeq = Future.sequence(checks) - val res = Await.result(futureSeq, 5.seconds) - val resStr = res.filter(_.nonEmpty) - if (resStr.nonEmpty) { - fail( - s"""Some URLs did not get response OK (200): - | ${resStr.mkString("\n ")} - |(Switch URL checking off in sbt via `set javaOptions in Test += "-DlinkChecking=false"`)""".stripMargin) - } + val allFutures = Future.sequence(checks) + val results = Await.result(allFutures, urlTimeout + 2.seconds).filter(_.nonEmpty) + if (results.nonEmpty) + fail(s"""Some URLs did not get response OK (200): + | ${results.mkString("\n ")} + |(Switch URL checking off in sbt via `set javaOptions in Test += "-DlinkChecking=false"`)""".stripMargin) } private def checkUrl(message: String, url: String) = Future { try { - if (connectTo(url) != httpOk) - s"$message: No OK response from $url" + val response = connectTo(url) + if (response != httpOk) + s"$message: Got HTTP $response response from $url" else "" } catch { case _: SocketTimeoutException => @@ -78,16 +92,12 @@ trait UrlExistenceChecker { } } - private def connectTo(url: String): Int = { + private def connectTo(url: String, timeout: Int = urlTimeoutInt): Int = { val conn = new URL(url).openConnection().asInstanceOf[HttpURLConnection] conn.setRequestMethod("HEAD") - conn.setConnectTimeout(urlTimeout) - conn.setReadTimeout(urlTimeout) + conn.setConnectTimeout(timeout) + conn.setReadTimeout(timeout) conn.getResponseCode } - private def noChecking(): Unit = { - runChecks.set(false) - println("Warning: URL checking has been switched off as github.com can't be reached.") - } } From 5f9957214405113b288a9e4ec36d838cc1e18475 Mon Sep 17 00:00:00 2001 From: Enno Runne <458526+ennru@users.noreply.github.com> Date: Tue, 23 Jan 2018 09:31:56 +0100 Subject: [PATCH 5/6] rename back to assertMessageCount --- .../dotc/reporting/ErrorMessagesTests.scala | 152 +++++++++--------- 1 file changed, 76 insertions(+), 76 deletions(-) diff --git a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala index c363128af9dd..9e31b6ffaaca 100644 --- a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala +++ b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala @@ -28,7 +28,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { checkLinks(message.getClass.getSimpleName, message.links.map(_.url)) } - def assertMessageCountAndCheckLinks(expected: Int, messages: List[Message]): Unit = { + def assertMessageCount(expected: Int, messages: List[Message]): Unit = { assertEquals(messages.mkString, expected, messages.length @@ -55,7 +55,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { val defn = ictx.definitions // Assert that we only got one error message - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) // Pattern match out the expected error val TypeMismatch(found, expected, _, _) :: Nil = messages @@ -81,7 +81,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val OverridesNothing(member) :: Nil = messages assertEquals("bar", member.name.show) } @@ -102,7 +102,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val OverridesNothingButNameExists(member, sameName) :: Nil = messages // check expected context data assertEquals("bar", member.name.show) @@ -126,7 +126,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val ForwardReferenceExtendsOverDefinition(value, definition) :: Nil = messages assertEquals("value b", value.show) assertEquals("value a", definition.show) @@ -143,7 +143,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val ExpectedTokenButFound(expected, found) :: Nil = messages assertEquals(Tokens.IDENTIFIER, expected) assertEquals(Tokens.VAL, found) @@ -174,7 +174,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val MixedLeftAndRightAssociativeOps(op1, op2, op2LeftAssoc) :: Nil = messages assertEquals("+-", op1.show) assertEquals("+:", op2.show) @@ -193,7 +193,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val CantInstantiateAbstractClassOrTrait(cls, isTrait) :: Nil = messages assertEquals("Concept", cls.name.show) assertFalse("expected class", isTrait) @@ -211,7 +211,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val CantInstantiateAbstractClassOrTrait(cls, isTrait) :: Nil = messages assertEquals("Concept", cls.name.show) assertTrue("expected trait", isTrait) @@ -229,7 +229,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val OverloadedOrRecursiveMethodNeedsResultType(tree) :: Nil = messages assertEquals("foo", tree.show) } @@ -245,7 +245,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val OverloadedOrRecursiveMethodNeedsResultType(tree) :: Nil = messages assertEquals("i", tree.show) } @@ -261,7 +261,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val RecursiveValueNeedsResultType(tree) :: Nil = messages assertEquals("i", tree.show) } @@ -278,7 +278,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val CyclicReferenceInvolving(denot) :: Nil = messages assertEquals("value x", denot.show) } @@ -298,7 +298,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val CyclicReferenceInvolvingImplicit(tree) :: Nil = messages assertEquals("x", tree.name.show) } @@ -321,7 +321,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val SuperQualMustBeParent(qual, cls) :: Nil = messages assertEquals("B", qual.show) @@ -350,7 +350,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { import typer.Typer.BindingPrec._ - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val AmbiguousImport(name, newPrec, prevPrec, prevCtx) :: Nil = messages assertEquals("ToBeImported", name.show) assertEquals(namedImport, newPrec) @@ -369,7 +369,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val MethodDoesNotTakeParameters(tree, methodPart) :: Nil = messages assertEquals("Scope.foo", tree.show) @@ -388,7 +388,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val MethodDoesNotTakeParameters(tree, methodPart) :: Nil = messages assertEquals("Scope.foo(1)", tree.show) @@ -412,7 +412,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val AmbiguousOverload(tree, List(alt1, alt2), pt: WildcardType) :: Nil = messages assertEquals("method foo", alt1.show) assertEquals("(s: String): String", alt1.info.show) @@ -430,7 +430,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val ReassignmentToVal(name) :: Nil = messages assertEquals("value", name.show) } @@ -445,7 +445,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val TypeDoesNotTakeParameters(tpe, params) :: Nil = messages assertEquals("WithOutParams", tpe.show) } @@ -460,7 +460,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val ParameterizedTypeLacksArguments(symbol) :: Nil = messages assertEquals("trait WithParams", symbol.show) } @@ -471,7 +471,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val VarValParametersMayNotBeCallByName(name, false) :: Nil = messages assertEquals("noNoNo", name.show) } @@ -484,7 +484,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val MissingTypeParameterFor(tpe) :: Nil = messages assertEquals("List", tpe.show) } @@ -498,7 +498,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val DoesNotConformToBound(tpe, which, bound) :: Nil = messages assertEquals("Int", tpe.show) assertEquals("upper", which) @@ -516,7 +516,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val DoesNotConformToSelfType(category, selfType, cls, otherSelf, relation, other) :: Nil = messages assertEquals("illegal inheritance", category) assertEquals("Blended", selfType.show) @@ -537,7 +537,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val DoesNotConformToSelfTypeCantBeInstantiated(tpe, selfType) :: Nil = messages assertEquals("RequiresBase", tpe.show) assertEquals("Base", selfType.show) @@ -552,7 +552,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val AbstractMemberMayNotHaveModifier(symbol, flags) :: Nil = messages assertEquals("value s", symbol.show) assertEquals("final", flags.toString) @@ -567,7 +567,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val TopLevelCantBeImplicit(symbol) :: Nil = messages assertEquals("object S", symbol.show) } @@ -581,7 +581,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val TypesAndTraitsCantBeImplicit(symbol) :: Nil = messages assertEquals("trait S", symbol.show) } @@ -595,7 +595,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val OnlyClassesCanBeAbstract(symbol) :: Nil = messages assertEquals("value s", symbol.show) } @@ -609,7 +609,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val AbstractOverrideOnlyInTraits(symbol) :: Nil = messages assertEquals("value s", symbol.show) } @@ -620,7 +620,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val TraitsMayNotBeFinal(symbol) :: Nil = messages assertEquals("trait Foo", symbol.show) } @@ -634,7 +634,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val NativeMembersMayNotHaveImplementation(symbol) :: Nil = messages assertEquals("method foo", symbol.show) } @@ -648,7 +648,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val OnlyClassesCanHaveDeclaredButUndefinedMembers(symbol) :: Nil = messages assertEquals("method foo", symbol.show) } @@ -659,7 +659,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val CannotExtendAnyVal(symbol) :: Nil = messages assertEquals("trait Foo", symbol.show) } @@ -675,7 +675,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val CannotHaveSameNameAs(symbol, cls, _) :: Nil = messages assertEquals("class A", symbol.show) assertEquals("class A", cls.show) @@ -690,7 +690,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val ValueClassesMayNotDefineInner(valueClass, inner) :: Nil = messages assertEquals("class MyValue", valueClass.show) assertEquals("class Inner", inner.show) @@ -705,7 +705,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val ValueClassesMayNotDefineNonParameterField(valueClass, field) :: Nil = messages assertEquals("class MyValue", valueClass.show) assertEquals("value illegal", field.show) @@ -720,7 +720,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val ValueClassesMayNotDefineASecondaryConstructor(valueClass, constuctor) :: Nil = messages assertEquals("class MyValue", valueClass.show) assertEquals("constructor MyValue", constuctor.show) @@ -735,7 +735,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val ValueClassesMayNotContainInitalization(valueClass) :: Nil = messages assertEquals("class MyValue", valueClass.show) } @@ -749,7 +749,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val ValueClassesMayNotBeContainted(valueClass) :: Nil = messages assertEquals("class MyValue", valueClass.show) } @@ -760,7 +760,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val ValueClassesMayNotWrapItself(valueClass) :: Nil = messages assertEquals("class MyValue", valueClass.show) } @@ -771,7 +771,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val ValueClassParameterMayNotBeAVar(valueClass, param) :: Nil = messages assertEquals("class MyValue", valueClass.show) assertEquals("variable i", param.show) @@ -783,7 +783,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val ValueClassNeedsOneValParam(valueClass) :: Nil = messages assertEquals("class MyValue", valueClass.show) } @@ -794,7 +794,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val err :: Nil = messages assertEquals(err, OnlyCaseClassOrCaseObjectAllowed()) } @@ -805,7 +805,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val err :: Nil = messages assertEquals(err, ExpectedClassOrObjectDef()) } @@ -820,7 +820,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (itcx, messages) => implicit val ctx: Context = itcx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val err :: Nil = messages assertEquals(err, ImplicitClassPrimaryConstructorArity()) } @@ -835,7 +835,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val AnonymousFunctionMissingParamType(param, args, _, pt) = messages.head assertEquals("x$1", param.show) assertEquals("?", pt.show) @@ -855,7 +855,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val err :: Nil = messages val SuperCallsNotAllowedInline(symbol) = err assertEquals("method bar", symbol.show) @@ -872,7 +872,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { checkMessagesAfter("refchecks")(code) .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val ModifiersNotAllowed(flags, sort) :: Nil = messages assertEquals(modifierAssertion, flags.toString) assertEquals(typeAssertion, sort) @@ -890,7 +890,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val err :: Nil = messages assertEquals(err, WildcardOnTypeArgumentNotAllowedOnNew()) @@ -906,7 +906,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(2, messages) + assertMessageCount(2, messages) messages.foreach(assertEquals(_, FunctionTypeNeedsNonEmptyParameterList())) } @@ -920,7 +920,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val err :: Nil = messages assertEquals(err, WrongNumberOfParameters(1)) @@ -935,7 +935,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val err :: Nil = messages assertEquals(DuplicatePrivateProtectedQualifier(), err) @@ -948,7 +948,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val err :: Nil = messages assertEquals(ExpectedStartOfTopLevelDefinition(), err) @@ -963,7 +963,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { }.expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val MissingReturnTypeWithReturnStatement(method) :: Nil = messages assertEquals(method.name.show, "bad") @@ -978,7 +978,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { }.expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val NoReturnFromInline(method) :: Nil = messages assertEquals("method usesReturn", method.show) @@ -992,7 +992,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { """.stripMargin }.expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val ReturnOutsideMethodDefinition(owner) :: Nil = messages assertEquals("object A", owner.show) } @@ -1004,7 +1004,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { """.stripMargin }.expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val ExtendFinalClass(extender, parent) :: Nil = messages assertEquals(extender.show, "class B") assertEquals(parent.show, "class A") @@ -1020,7 +1020,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { """.stripMargin }.expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val TailrecNotApplicable(method) :: Nil = messages assertEquals(method.show, "method foo") } @@ -1034,7 +1034,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { }.expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val ExpectedTypeBoundOrEquals(found) :: Nil = messages assertEquals(Tokens.IDENTIFIER, found) } @@ -1052,7 +1052,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { }.expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val ClassAndCompanionNameClash(cls, other) :: Nil = messages assertEquals("class T", cls.owner.show) @@ -1075,7 +1075,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { }.expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val OnlyFunctionsCanBeFollowedByUnderscore(pt) :: Nil = messages assertEquals("String(n)", pt.show) } @@ -1094,7 +1094,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val MissingEmptyArgumentList(method) :: Nil = messages assertEquals("method greet", method.show) } @@ -1113,7 +1113,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(2, messages) + assertMessageCount(2, messages) val DuplicateNamedTypeParameter(n2) :: DuplicateNamedTypeParameter(n1) :: Nil = messages assertEquals("A", n1.show) assertEquals("B", n2.show) @@ -1133,7 +1133,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(2, messages) + assertMessageCount(2, messages) val UndefinedNamedTypeParameter(n2, l2) :: UndefinedNamedTypeParameter(n1, l1) :: Nil = messages val tpParams = List("A", "B") assertEquals("C", n1.show) @@ -1155,7 +1155,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(2, messages) + assertMessageCount(2, messages) val errWithModifier :: err :: Nil = messages assertEquals(IllegalStartOfStatement(isModifier = false), err) @@ -1178,7 +1178,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val TraitIsExpected(symbol) :: Nil = messages assertEquals("class B", symbol.show) } @@ -1194,7 +1194,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val TraitRedefinedFinalMethodFromAnyRef(method) = messages.head assertEquals("method wait", method.show) } @@ -1229,7 +1229,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { } .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val UnapplyInvalidNumberOfArguments(qual, argTypes) :: Nil = messages assertEquals("Boo", qual.show) assertEquals("(class Int, class String)", argTypes.map(_.typeSymbol).mkString("(", ", ", ")")) @@ -1244,7 +1244,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { """.stripMargin }.expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val StaticFieldsOnlyAllowedInObjects(field) = messages.head assertEquals(field.show, "method bar") } @@ -1256,7 +1256,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { .expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val CyclicInheritance(symbol, _) :: Nil = messages assertEquals("class A", symbol.show) } @@ -1270,7 +1270,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { """.stripMargin }.expect { (itcx, messages) => implicit val ctx: Context = itcx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val MissingCompanionForStatic(member) = messages.head assertEquals(member.show, "method bar") } @@ -1286,7 +1286,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { }.expect { (ictx, messages) => implicit val ctx: Context = ictx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val PolymorphicMethodMissingTypeInParent(rsym, parentSym) = messages.head assertEquals("method get", rsym.show) assertEquals("class Object", parentSym.show) @@ -1303,7 +1303,7 @@ class ErrorMessagesTests extends ErrorMessagesTest { }.expect { (itcx, messages) => implicit val ctx: Context = itcx - assertMessageCountAndCheckLinks(1, messages) + assertMessageCount(1, messages) val JavaSymbolIsNotAValue(symbol) = messages.head assertEquals(symbol.show, "package p") } From a73eb368b84eab3ac62adc68bc8527f62d39ffff Mon Sep 17 00:00:00 2001 From: Enno Runne <458526+ennru@users.noreply.github.com> Date: Sat, 3 Feb 2018 22:12:26 +0100 Subject: [PATCH 6/6] Make links abstract and add the val in every error message class --- .../dotc/reporting/diagnostic/Message.scala | 4 +- .../dotc/reporting/diagnostic/messages.scala | 171 ++++++++++++++++-- .../dotc/reporting/TestMessageLaziness.scala | 1 + 3 files changed, 155 insertions(+), 21 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/Message.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/Message.scala index 94287e52b693..8d6554fd3d78 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/Message.scala @@ -89,7 +89,7 @@ abstract class Message(val errorId: ErrorMessageID) { self => /** Links may list URLs to Internet resources related to the error * e.g. the Scala Language Specification. */ - val links: List[DocumentationLink] = Nil + 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 @@ -119,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 */ @@ -154,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)" } diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index 19facb658a1b..a81b4c85097b 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -144,7 +144,6 @@ object messages { |correctly handles transfer functions like ${"return"}.""" } - override val links = tryExpressionLinks } case class EmptyCatchBlock(tryBody: untpd.Tree)(implicit ctx: Context) @@ -154,7 +153,7 @@ object messages { hl"""|The ${"catch"} block does not contain a valid expression, try |adding a case like - `${"case e: Exception =>"}` to the block""" - override val links = tryExpressionLinks + val links = tryExpressionLinks } case class EmptyCatchAndFinallyBlock(tryBody: untpd.Tree)(implicit ctx: Context) @@ -164,7 +163,7 @@ object messages { hl"""|A ${"try"} without ${"catch"} or ${"finally"} is equivalent to putting |its body in a block; no exceptions are handled.""" - override val links = tryExpressionLinks + val links = tryExpressionLinks } case class DeprecatedWithOperator()(implicit ctx: Context) @@ -177,7 +176,7 @@ object messages { |use of the ${"with"} keyword. There are a few differences in |semantics between intersection types and using `${"with"}'.""" - override val links = DottyDocs("reference/intersection-types.html") :: Nil + val links = DottyDocs("reference/intersection-types.html") :: Nil } case class CaseClassMissingParamList(cdef: untpd.TypeDef)(implicit ctx: Context) @@ -191,7 +190,7 @@ object messages { |have a singleton representation of ${cdef.name}, use a "${"case object"}". |Or, add an explicit `()' as a parameter list to ${cdef.name}.""" - override val links = LanguageSpec("05-classes-and-objects.html#case-classes") :: Nil + val links = LanguageSpec("05-classes-and-objects.html#case-classes") :: Nil } case class AnonymousFunctionMissingParamType(param: untpd.ValDef, @@ -225,7 +224,7 @@ object messages { | |${"val f: Any => Int = { case x: Int => x + 1 }"} """ - override val links = LanguageSpec("06-expressions.html#anonymous-functions") :: Nil + val links = LanguageSpec("06-expressions.html#anonymous-functions") :: Nil } case class WildcardOnTypeArgumentNotAllowedOnNew()(implicit ctx: Context) @@ -259,6 +258,8 @@ object messages { |You must complete all the type parameters, for instance: | |$code2 """ + + val links = Nil } @@ -288,6 +289,8 @@ object messages { | |`${bind.name}` is not unique. Rename one of the bound variables!""" } + + val links = Nil } case class MissingIdent(tree: untpd.Ident, treeKind: String, name: String)(implicit ctx: Context) @@ -301,6 +304,8 @@ object messages { |That can happen for instance if $name or its declaration has either been |misspelt, or if you're forgetting an import""" } + + val links = Nil } case class TypeMismatch(found: Type, expected: Type, whyNoMatch: String = "", implicitFailure: String = "")(implicit ctx: Context) @@ -316,6 +321,8 @@ object messages { } val explanation = "" + + val links = Nil } case class NotAMember(site: Type, name: Name, selected: String)(implicit ctx: Context) @@ -382,6 +389,8 @@ object messages { } val explanation = "" + + val links = Nil } case class EarlyDefinitionsNotSupported()(implicit ctx: Context) @@ -430,7 +439,7 @@ object messages { } - override val links = DottyDocs("reference/trait-parameters.html") :: Nil + val links = DottyDocs("reference/trait-parameters.html") :: Nil } case class TopLevelImplicitClass(cdef: untpd.TypeDef)(implicit ctx: Context) @@ -461,7 +470,7 @@ object messages { |${"import Implicits"}.${cdef.name}""" } - override val links = implicitClassLinks + val links = implicitClassLinks } case class ImplicitCaseClass(cdef: untpd.TypeDef)(implicit ctx: Context) @@ -475,7 +484,7 @@ object messages { |${"implicit class"} ${cdef.name}... |""" - override val links = implicitClassLinks + val links = implicitClassLinks } case class ImplicitClassPrimaryConstructorArity()(implicit ctx: Context) @@ -492,6 +501,8 @@ object messages { |such classes aren’t used during implicit lookup. |""" } + + val links = Nil } case class ObjectMayNotHaveSelfType(mdef: untpd.ModuleDef)(implicit ctx: Context) @@ -510,6 +521,8 @@ object messages { | | object $name extends ${selfTpt.show}""" } + + val links = Nil } case class TupleTooLong(ts: List[untpd.Tree])(implicit ctx: Context) @@ -526,6 +539,8 @@ object messages { | |((${nestedRepresentation}))""" } + + val links = Nil } case class RepeatedModifier(modifier: String)(implicit ctx:Context) @@ -548,6 +563,8 @@ object messages { | |""" } + + val links = Nil } case class InterpolatedStringError()(implicit ctx:Context) @@ -565,7 +582,7 @@ object messages { |""" } - override val links = TourUrl("core/string-interpolation.html", "More on String Interpolation") :: Nil + val links = TourUrl("core/string-interpolation.html", "More on String Interpolation") :: Nil } case class UnboundPlaceholderParameter()(implicit ctx:Context) @@ -602,6 +619,8 @@ object messages { |Only fields can be left uninitialized in this manner; local variables |must be initialized. |""" + + val links = Nil } case class IllegalStartSimpleExpr(illegalToken: String)(implicit ctx: Context) @@ -621,6 +640,7 @@ object messages { | |which cannot start with ${Red(illegalToken)}.""" } + val links = Nil } case class MissingReturnType()(implicit ctx:Context) @@ -633,6 +653,7 @@ object messages { |trait Shape { | def area: Double // abstract declaration returning a ${"Double"} |}""" + val links = Nil } case class MissingReturnTypeWithReturnStatement(method: Symbol)(implicit ctx: Context) @@ -644,6 +665,7 @@ object messages { |explicit return type. For example: | |${"def good: Int /* explicit return type */ = return 1"}""" + val links = Nil } case class YieldOrDoExpectedInForComprehension()(implicit ctx: Context) @@ -677,6 +699,8 @@ object messages { |${"for i <- 1 to 3 do println(i) // notice the 'do' keyword"} | |""" + + val links = Nil } case class ProperDefinitionNotFound()(implicit ctx: Context) @@ -716,6 +740,8 @@ object messages { |usecase and the compiler makes sure that it is valid. Because of this, you're |only allowed to use ${"def"}s when defining usecases.""" } + + val links = Nil } case class ByNameParameterNotSupported()(implicit ctx: Context) @@ -740,6 +766,8 @@ object messages { |And the usage could be as such: |${"func(bool => // do something...)"} |""" + + val links = Nil } case class WrongNumberOfTypeArgs(fntpe: Type, expectedArgs: List[ParamInfo], actual: List[untpd.Tree])(implicit ctx: Context) @@ -790,6 +818,8 @@ object messages { hl"""|You have not supplied enough type parameters |If you specify one type parameter then you need to specify every type parameter.""" } + + val links = Nil } case class IllegalVariableInPatternAlternative()(implicit ctx: Context) @@ -819,7 +849,7 @@ object messages { | |$fixedVarInAlternative""" } - override val links = patternMatchingLinks + val links = patternMatchingLinks } case class IdentifierExpected(identifier: String)(implicit ctx: Context) @@ -841,6 +871,8 @@ object messages { | |""" } + + val links = Nil } case class AuxConstructorNeedsNonImplicitParameter()(implicit ctx:Context) @@ -857,6 +889,8 @@ object messages { | - forgotten parenthesis on ${"this"} (${"def this() = { ... }"}) | - auxiliary constructors specify the implicit value |""" + + val links = Nil } case class IncorrectRepeatedParameterSyntax()(implicit ctx: Context) @@ -884,6 +918,8 @@ object messages { |${"val ints = List(2, 3, 4) // ints: List[Int] = List(2, 3, 4)"} |${"square(ints: _*) // res1: List[Int] = List(4, 9, 16)"} |""".stripMargin + + val links = Nil } case class IllegalLiteral()(implicit ctx: Context) @@ -900,7 +936,7 @@ object messages { | - null |""" - override val links = LanguageSpec(suffix = "01-lexical-syntax.html#literals") :: Nil + val links = LanguageSpec(suffix = "01-lexical-syntax.html#literals") :: Nil } case class PatternMatchExhaustivity(uncovered: String)(implicit ctx: Context) @@ -919,7 +955,7 @@ object messages { | - Add a 'case _ => ...' at the end to match all remaining cases |""" - override val links = patternMatchingLinks + val links = patternMatchingLinks } case class UncheckedTypePattern(msg: String)(implicit ctx: Context) @@ -932,6 +968,8 @@ object messages { | |You can either replace the type arguments by `_` or use `@unchecked`. |""" + + val links = Nil } case class MatchCaseUnreachable()(implicit ctx: Context) @@ -939,7 +977,7 @@ object messages { val kind = s"""Match ${hl"case"} Unreachable""" val msg = "unreachable code" val explanation = "" - override val links = patternMatchingLinks + val links = patternMatchingLinks } case class SeqWildcardPatternPos()(implicit ctx: Context) @@ -964,7 +1002,7 @@ object messages { | |would give 3 as a result""" } - override val links = patternMatchingLinks + val links = patternMatchingLinks } case class IllegalStartOfSimplePattern()(implicit ctx: Context) @@ -1045,6 +1083,8 @@ object messages { | ${"def unapplySeq[A](x: List[A]): Some[List[A]]"} |""" } + + val links = Nil } case class PkgDuplicateSymbol(existing: Symbol)(implicit ctx: Context) @@ -1052,6 +1092,7 @@ object messages { val kind = "Duplicate Symbol" val msg = hl"trying to define package with same name as `$existing`" val explanation = "" + val links = Nil } case class ExistentialTypesNoLongerSupported()(implicit ctx: Context) @@ -1075,7 +1116,7 @@ object messages { | |${"List[_]"} |""" - override val links = DottyDocs("reference/dropped/existential-types.html") :: Nil + val links = DottyDocs("reference/dropped/existential-types.html") :: Nil } case class UnboundWildcardType()(implicit ctx: Context) @@ -1121,6 +1162,8 @@ object messages { | Use: | ${"val foo: Int = 3"} |""" + + val links = Nil } case class DanglingThisInPath()(implicit ctx: Context) extends Message(DanglingThisInPathID) { @@ -1157,6 +1200,8 @@ object messages { |- this is a valid type using a path |${typeCode} |""" + + val links = Nil } case class OverridesNothing(member: Symbol)(implicit ctx: Context) @@ -1169,6 +1214,8 @@ object messages { |class of `${member.owner}` to override it. Did you misspell it? |Are you extending the right classes? |""" + + val links = Nil } case class OverridesNothingButNameExists(member: Symbol, existing: List[Denotations.SingleDenotation])(implicit ctx: Context) @@ -1188,6 +1235,8 @@ object messages { |named `${member.name}`: | ${existingDecl} |""" + + val links = Nil } case class ForwardReferenceExtendsOverDefinition(value: Symbol, definition: Symbol)(implicit ctx: Context) @@ -1207,6 +1256,8 @@ object messages { |the declaration of `${definition.name}` and its use, |or define `${value.name}` as lazy. |""".stripMargin + + val links = Nil } case class ExpectedTokenButFound(expected: Token, found: Token)(implicit ctx: Context) @@ -1228,6 +1279,8 @@ object messages { else "" val explanation = s"$ifKeyword" + + val links = Nil } case class MixedLeftAndRightAssociativeOps(op1: Name, op2: Name, op2LeftAssoc: Boolean)(implicit ctx: Context) @@ -1262,7 +1315,8 @@ object messages { | (all other special characters) |Operators starting with a letter have lowest precedence, followed by operators starting with `|`, etc. |""".stripMargin - override val links = LanguageSpec("06-expressions.html#infix-operations") :: Nil + + val links = LanguageSpec("06-expressions.html#infix-operations") :: Nil } case class CantInstantiateAbstractClassOrTrait(cls: Symbol, isTrait: Boolean)(implicit ctx: Context) @@ -1282,6 +1336,8 @@ object messages { | |You need to implement any abstract members in both cases. |""".stripMargin + + val links = Nil } case class OverloadedOrRecursiveMethodNeedsResultType(tree: Names.TermName)(implicit ctx: Context) @@ -1296,6 +1352,8 @@ object messages { |Case 2: ${tree} is recursive |If `${tree.name}` calls itself on any path, you need to specify its return type. |""".stripMargin + + val links = Nil } case class RecursiveValueNeedsResultType(tree: Names.TermName)(implicit ctx: Context) @@ -1305,6 +1363,7 @@ object messages { val explanation = hl"""The definition of `${tree.name}` is recursive and you need to specify its type. |""".stripMargin + val links = Nil } case class CyclicReferenceInvolving(denot: SymDenotation)(implicit ctx: Context) @@ -1315,6 +1374,7 @@ object messages { hl"""|$denot is declared as part of a cycle which makes it impossible for the |compiler to decide upon ${denot.name}'s type. |""".stripMargin + val links = Nil } case class CyclicReferenceInvolvingImplicit(cycleSym: Symbol)(implicit ctx: Context) @@ -1325,6 +1385,7 @@ object messages { hl"""|This happens when the right hand-side of $cycleSym's definition involves an implicit search. |To avoid this error, give `${cycleSym.name}` an explicit type. |""".stripMargin + val links = Nil } case class SuperQualMustBeParent(qual: untpd.Ident, cls: Symbols.ClassSymbol)(implicit ctx: Context) @@ -1342,6 +1403,8 @@ object messages { |In this case, the parents of $cls are: |${parents.mkString(" - ", "\n - ", "")} |""".stripMargin + + val links = Nil } case class VarArgsParamMustComeLast()(implicit ctx: Context) @@ -1352,6 +1415,7 @@ object messages { hl"""|The varargs field must be the last field in the method signature. |Attempting to define a field in a method signature after a varargs field is an error. |""" + val links = Nil } case class AmbiguousImport(name: Names.Name, newPrec: Int, prevPrec: Int, prevCtx: Context)(implicit ctx: Context) @@ -1393,6 +1457,8 @@ object messages { |- You may replace a name when imported using | ${"import"} scala.{ $name => ${name.show + "Tick"} } |""" + + val links = Nil } case class MethodDoesNotTakeParameters(tree: tpd.Tree, methPartType: Types.Type)(err: typer.ErrorReporting.Errors)(implicit ctx: Context) @@ -1417,6 +1483,7 @@ object messages { s"""|You have specified more parameter lists as defined in the method definition(s). |$noParameters""".stripMargin + val links = Nil } case class AmbiguousOverload(tree: tpd.Tree, alts: List[SingleDenotation], pt: Type)( @@ -1436,6 +1503,8 @@ object messages { |- assigning it to a value with a specified type, or |- adding a type ascription as in `${"instance.myMethod: String => Int"}` |""" + + val links = Nil } case class ReassignmentToVal(name: Names.Name)(implicit ctx: Context) @@ -1450,6 +1519,7 @@ object messages { |variable | ${"var"} $name ${"="} ... |""".stripMargin + val links = Nil } case class TypeDoesNotTakeParameters(tpe: Types.Type, params: List[Trees.Tree[Trees.Untyped]])(implicit ctx: Context) @@ -1465,6 +1535,8 @@ object messages { i"""You specified ${NoColor(ps)} for ${hl"$tpe"}, which is not |declared to take any. |""" + + val links = Nil } case class ParameterizedTypeLacksArguments(psym: Symbol)(implicit ctx: Context) @@ -1475,6 +1547,7 @@ object messages { hl"""The $psym is declared with non-implicit parameters, you may not leave |out the parameter list when extending it. |""" + val links = Nil } case class VarValParametersMayNotBeCallByName(name: Names.TermName, mutable: Boolean)(implicit ctx: Context) @@ -1489,6 +1562,7 @@ object messages { | ${s" def $name() = ${name}Tick"} | ${"}"} |""" + val links = Nil } case class MissingTypeParameterFor(tpe: Type)(implicit ctx: Context) @@ -1496,6 +1570,7 @@ object messages { val msg = hl"missing type parameter for ${tpe}" val kind = "Syntax" val explanation = "" + val links = Nil } case class DoesNotConformToBound(tpe: Type, which: String, bound: Type)( @@ -1504,6 +1579,7 @@ object messages { val msg = hl"Type argument ${tpe} does not conform to $which bound $bound ${err.whyNoMatchStr(tpe, bound)}" val kind = "Type Mismatch" val explanation = "" + val links = Nil } case class DoesNotConformToSelfType(category: String, selfType: Type, cls: Symbol, @@ -1520,6 +1596,7 @@ object messages { |Note: Self types are indicated with the notation | ${s"class "}$other ${"{ this: "}$otherSelf${" => "} """ + val links = Nil } case class DoesNotConformToSelfTypeCantBeInstantiated(tp: Type, selfType: Type)( @@ -1533,6 +1610,7 @@ object messages { |Note: Self types are indicated with the notation | ${s"class "}$tp ${"{ this: "}$selfType${" => "} |""" + val links = Nil } case class AbstractMemberMayNotHaveModifier(sym: Symbol, flag: FlagSet)( @@ -1541,6 +1619,7 @@ object messages { val msg = hl"""${"abstract"} $sym may not have `$flag' modifier""" val kind = "Syntax" val explanation = "" + val links = Nil } case class TopLevelCantBeImplicit(sym: Symbol)( @@ -1549,6 +1628,7 @@ object messages { val msg = hl"""${"implicit"} modifier cannot be used for top-level definitions""" val kind = "Syntax" val explanation = "" + val links = Nil } case class TypesAndTraitsCantBeImplicit(sym: Symbol)( @@ -1557,6 +1637,7 @@ object messages { val msg = hl"""${"implicit"} modifier cannot be used for types or traits""" val kind = "Syntax" val explanation = "" + val links = Nil } case class OnlyClassesCanBeAbstract(sym: Symbol)( @@ -1565,6 +1646,7 @@ object messages { val msg = hl"""${"abstract"} modifier can be used only for classes; it should be omitted for abstract members""" val kind = "Syntax" val explanation = "" + val links = Nil } case class AbstractOverrideOnlyInTraits(sym: Symbol)( @@ -1573,6 +1655,7 @@ object messages { val msg = hl"""${"abstract override"} modifier only allowed for members of traits""" val kind = "Syntax" val explanation = "" + val links = Nil } case class TraitsMayNotBeFinal(sym: Symbol)( @@ -1582,6 +1665,7 @@ object messages { val kind = "Syntax" val explanation = "A trait can never be final since it is abstract and must be extended to be useful." + val links = Nil } case class NativeMembersMayNotHaveImplementation(sym: Symbol)( @@ -1590,6 +1674,7 @@ object messages { val msg = hl"""${"@native"} members may not have an implementation""" val kind = "Syntax" val explanation = "" + val links = Nil } case class OnlyClassesCanHaveDeclaredButUndefinedMembers(sym: Symbol)( @@ -1602,6 +1687,7 @@ object messages { val msg = hl"""only classes can have declared but undefined members""" val kind = "Syntax" val explanation = s"$varNote" + val links = Nil } case class CannotExtendAnyVal(sym: Symbol)(implicit ctx: Context) @@ -1613,6 +1699,7 @@ object messages { |${"Any"} to become ${Green("\"universal traits\"")} which may only have ${"def"} members. |Universal traits can be mixed into classes that extend ${"AnyVal"}. |""" + val links = Nil } case class CannotHaveSameNameAs(sym: Symbol, cls: Symbol, reason: CannotHaveSameNameAs.Reason)(implicit ctx: Context) @@ -1629,6 +1716,7 @@ object messages { val msg = hl"""$sym cannot have the same name as ${cls.showLocated} -- """ + reasonMessage val kind = "Syntax" val explanation = "" + val links = Nil } object CannotHaveSameNameAs { sealed trait Reason @@ -1641,6 +1729,7 @@ object messages { val msg = hl"""value classes may not define an inner class""" val kind = "Syntax" val explanation = "" + val links = Nil } case class ValueClassesMayNotDefineNonParameterField(valueClass: Symbol, field: Symbol)(implicit ctx: Context) @@ -1648,6 +1737,7 @@ object messages { val msg = hl"""value classes may not define non-parameter field""" val kind = "Syntax" val explanation = "" + val links = Nil } case class ValueClassesMayNotDefineASecondaryConstructor(valueClass: Symbol, constructor: Symbol)(implicit ctx: Context) @@ -1655,6 +1745,7 @@ object messages { val msg = hl"""value classes may not define a secondary constructor""" val kind = "Syntax" val explanation = "" + val links = Nil } case class ValueClassesMayNotContainInitalization(valueClass: Symbol)(implicit ctx: Context) @@ -1662,6 +1753,7 @@ object messages { val msg = hl"""value classes may not contain initialization statements""" val kind = "Syntax" val explanation = "" + val links = Nil } case class ValueClassesMayNotBeAbstract(valueClass: Symbol)(implicit ctx: Context) @@ -1669,6 +1761,7 @@ object messages { val msg = hl"""value classes may not be ${"abstract"}""" val kind = "Syntax" val explanation = "" + val links = Nil } case class ValueClassesMayNotBeContainted(valueClass: Symbol)(implicit ctx: Context) @@ -1677,6 +1770,7 @@ object messages { val msg = s"""value classes may not be a $localOrMember""" val kind = "Syntax" val explanation = "" + val links = Nil } case class ValueClassesMayNotWrapItself(valueClass: Symbol)(implicit ctx: Context) @@ -1684,6 +1778,7 @@ object messages { val msg = """a value class may not wrap itself""" val kind = "Syntax" val explanation = "" + val links = Nil } case class ValueClassParameterMayNotBeAVar(valueClass: Symbol, param: Symbol)(implicit ctx: Context) @@ -1693,6 +1788,7 @@ object messages { val explanation = hl"""A value class must have exactly one ${"val"} parameter. |""" + val links = Nil } case class ValueClassNeedsOneValParam(valueClass: Symbol)(implicit ctx: Context) @@ -1700,6 +1796,7 @@ object messages { val msg = hl"""value class needs one ${"val"} parameter""" val kind = "Syntax" val explanation = "" + val links = Nil } case class OnlyCaseClassOrCaseObjectAllowed()(implicit ctx: Context) @@ -1707,6 +1804,7 @@ object messages { val msg = "only `case class` or `case object` allowed" val kind = "Syntax" val explanation = "" + val links = Nil } case class ExpectedClassOrObjectDef()(implicit ctx: Context) @@ -1714,6 +1812,7 @@ object messages { val kind = "Syntax" val msg = "expected class or object definition" val explanation = "" + val links = Nil } case class SuperCallsNotAllowedInline(symbol: Symbol)(implicit ctx: Context) @@ -1721,6 +1820,7 @@ object messages { val kind = "Syntax" val msg = s"super call not allowed in inline $symbol" val explanation = "Method inlining prohibits calling superclass methods, as it may lead to confusion about which super is being called." + val links = Nil } case class ModifiersNotAllowed(flags: FlagSet, printableType: Option[String])(implicit ctx: Context) @@ -1737,7 +1837,7 @@ object messages { |In this instance, the modifier combination is not supported""" } - override val links = LanguageSpec("05-classes-and-objects.html#modifiers") :: Nil + val links = LanguageSpec("05-classes-and-objects.html#modifiers") :: Nil } case class FunctionTypeNeedsNonEmptyParameterList(isImplicit: Boolean = true, isErased: Boolean = true)(implicit ctx: Context) @@ -1757,7 +1857,7 @@ object messages { | |$code2""".stripMargin } - override val links = DottyDocs("reference/implicit-function-types.html") :: Nil + val links = DottyDocs("reference/implicit-function-types.html") :: Nil } case class WrongNumberOfParameters(expected: Int)(implicit ctx: Context) @@ -1765,6 +1865,7 @@ object messages { val kind = "Syntax" val msg = s"wrong number of parameters, expected: $expected" val explanation = "" + val links = Nil } case class DuplicatePrivateProtectedQualifier()(implicit ctx: Context) @@ -1773,6 +1874,7 @@ object messages { val msg = "duplicate private/protected qualifier" val explanation = hl"It is not allowed to combine `private` and `protected` modifiers even if they are qualified to different scopes" + val links = Nil } case class ExpectedStartOfTopLevelDefinition()(implicit ctx: Context) @@ -1781,7 +1883,7 @@ object messages { val msg = "expected start of definition" val explanation = hl"you have to provide either ${"class"}, ${"trait"}, ${"object"}, or ${"enum"} definitions after qualifiers" - override val links = LanguageSpec(suffix = "09-top-level-definitions.html") :: Nil + val links = LanguageSpec(suffix = "09-top-level-definitions.html") :: Nil } case class NoReturnFromInline(owner: Symbol)(implicit ctx: Context) @@ -1793,6 +1895,7 @@ object messages { |Instead, you should rely on the last expression's value being |returned from a method. |""" + val links = Nil } case class ReturnOutsideMethodDefinition(owner: Symbol)(implicit ctx: Context) @@ -1803,6 +1906,7 @@ object messages { hl"""You used ${"return"} in ${owner}. |${"return"} is a keyword and may only be used within method declarations. |""" + val links = Nil } case class ExtendFinalClass(clazz:Symbol, finalClazz: Symbol)(implicit ctx: Context) @@ -1811,6 +1915,7 @@ object messages { val msg = hl"$clazz cannot extend ${"final"} $finalClazz" val explanation = hl"""A class marked with the ${"final"} keyword cannot be extended""" + val links = Nil } case class ExpectedTypeBoundOrEquals(found: Token)(implicit ctx: Context) @@ -1829,6 +1934,8 @@ object messages { |An upper type bound ${"T <: A"} declares that type variable ${"T"} |refers to a subtype of type ${"A"}. |""" + + val links = Nil } case class ClassAndCompanionNameClash(cls: Symbol, other: Symbol)(implicit ctx: Context) @@ -1842,6 +1949,7 @@ object messages { | - ${cls.owner} defines ${cls} | - ${other.owner} defines ${other}""" } + val links = Nil } case class TailrecNotApplicable(method: Symbol)(implicit ctx: Context) @@ -1850,6 +1958,7 @@ object messages { val msg = hl"TailRec optimisation not applicable, $method is neither ${"private"} nor ${"final"}." val explanation = hl"A method annotated ${"@tailrec"} must be declared ${"private"} or ${"final"} so it can't be overridden." + val links = Nil } case class FailureToEliminateExistential(tp: Type, tp1: Type, tp2: Type, boundSyms: List[Symbol])(implicit ctx: Context) @@ -1862,6 +1971,7 @@ object messages { |reduces to : $tp1 |type used instead: $tp2""" } + val links = Nil } case class OnlyFunctionsCanBeFollowedByUnderscore(pt: Type)(implicit ctx: Context) @@ -1871,6 +1981,7 @@ object messages { val explanation = hl"""The syntax ${"x _"} is no longer supported if ${"x"} is not a function. |To convert to a function value, you need to explicitly write ${"() => x"}""" + val links = Nil } case class MissingEmptyArgumentList(method: Symbol)(implicit ctx: Context) @@ -1889,6 +2000,7 @@ object messages { |In Dotty, this idiom is an error. The application syntax has to follow exactly the parameter syntax. |Excluded from this rule are methods that are defined in Java or that override methods defined in Java.""" } + val links = Nil } case class DuplicateNamedTypeParameter(name: Name)(implicit ctx: Context) @@ -1896,6 +2008,7 @@ object messages { val kind = "Syntax" val msg = hl"Type parameter $name was defined multiple times." val explanation = "" + val links = Nil } case class UndefinedNamedTypeParameter(undefinedName: Name, definedNames: List[Name])(implicit ctx: Context) @@ -1903,6 +2016,7 @@ object messages { val kind = "Syntax" val msg = hl"Type parameter $undefinedName is undefined. Expected one of ${definedNames.map(_.show).mkString(", ")}." val explanation = "" + val links = Nil } case class IllegalStartOfStatement(isModifier: Boolean)(implicit ctx: Context) extends Message(IllegalStartOfStatementID) { @@ -1912,6 +2026,7 @@ object messages { "Illegal start of statement" + addendum } val explanation = "A statement is either an import, a definition or an expression." + val links = Nil } case class TraitIsExpected(symbol: Symbol)(implicit ctx: Context) extends Message(TraitIsExpectedID) { @@ -1940,12 +2055,14 @@ object messages { |$codeExample |""" } + val links = Nil } case class TraitRedefinedFinalMethodFromAnyRef(method: Symbol)(implicit ctx: Context) extends Message(TraitRedefinedFinalMethodFromAnyRefID) { val kind = "Syntax" val msg = hl"Traits cannot redefine final $method from ${"class AnyRef"}." val explanation = "" + val links = Nil } case class PackageNameAlreadyDefined(pkg: Symbol)(implicit ctx: Context) extends Message(PackageNameAlreadyDefinedID) { @@ -1953,6 +2070,7 @@ object messages { val kind = "Naming" val explanation = hl"An ${"object"} cannot have the same name as an existing ${"package"}. Rename either one of them." + val links = Nil } case class UnapplyInvalidNumberOfArguments(qual: untpd.Tree, argTypes: List[Type])(implicit ctx: Context) @@ -1966,6 +2084,7 @@ object messages { | |where subsequent arguments would have following types: ($argTypes%, %). |""".stripMargin + val links = Nil } case class StaticFieldsOnlyAllowedInObjects(member: Symbol)(implicit ctx: Context) extends Message(StaticFieldsOnlyAllowedInObjectsID) { @@ -1973,6 +2092,7 @@ object messages { val kind = "Syntax" val explanation = hl"${"@static"} members are only allowed inside objects." + val links = Nil } case class CyclicInheritance(symbol: Symbol, addendum: String)(implicit ctx: Context) extends Message(CyclicInheritanceID) { @@ -1990,6 +2110,7 @@ object messages { |creates a "cycle" where a not yet defined class A extends itself which makes |impossible to instantiate an object of this class""" } + val links = Nil } case class BadSymbolicReference(denot: SymDenotation)(implicit ctx: Context) extends Message(BadSymbolicReferenceID) { @@ -2010,12 +2131,15 @@ object messages { } val explanation = "" + + val links = Nil } case class UnableToExtendSealedClass(pclazz: Symbol)(implicit ctx: Context) extends Message(UnableToExtendSealedClassID) { val kind = "Syntax" val msg = hl"Cannot extend ${"sealed"} $pclazz in a different source file" val explanation = "A sealed class or trait can only be extended in the same file as its declaration" + val links = Nil } case class SymbolHasUnparsableVersionNumber(symbol: Symbol, migrationMessage: String)(implicit ctx: Context) @@ -2028,6 +2152,7 @@ object messages { |The ${symbol.showLocated} is marked with ${"@migration"} indicating it has changed semantics |between versions and the ${"-Xmigration"} settings is used to warn about constructs |whose behavior may have changed since version change.""" + val links = Nil } case class SymbolChangedSemanticsInVersion( @@ -2041,6 +2166,7 @@ object messages { |between versions and the ${"-Xmigration"} settings is used to warn about constructs |whose behavior may have changed since version change.""" } + val links = Nil } case class UnableToEmitSwitch(tooFewCases: Boolean)(implicit ctx: Context) @@ -2071,6 +2197,7 @@ object messages { |- the matched value is not a constant literal |- there are less than three cases""" } + val links = Nil } case class MissingCompanionForStatic(member: Symbol)(implicit ctx: Context) extends Message(MissingCompanionForStaticID) { @@ -2078,6 +2205,7 @@ object messages { val kind = "Syntax" val explanation = hl"An object that contains ${"@static"} members must have a companion class." + val links = Nil } case class PolymorphicMethodMissingTypeInParent(rsym: Symbol, parentSym: Symbol)(implicit ctx: Context) @@ -2088,6 +2216,7 @@ object messages { hl"""Polymorphic $rsym is not allowed in the structural refinement of $parentSym because |$rsym does not override any method in $parentSym. Structural refinement does not allow for |polymorphic methods.""" + val links = Nil } case class ParamsNoInline(owner: Symbol)(implicit ctx: Context) @@ -2095,6 +2224,7 @@ object messages { val kind = "Syntax" val msg = hl"""${"inline"} modifier cannot be used for a ${owner.showKind} parameter""" val explanation = "" + val links = Nil } case class JavaSymbolIsNotAValue(symbol: Symbol)(implicit ctx: Context) extends Message(JavaSymbolIsNotAValueID) { @@ -2107,5 +2237,6 @@ object messages { s"$kind is not a value" } val explanation = "" + val links = Nil } } diff --git a/compiler/test/dotty/tools/dotc/reporting/TestMessageLaziness.scala b/compiler/test/dotty/tools/dotc/reporting/TestMessageLaziness.scala index 858660075bb2..fd490a309969 100644 --- a/compiler/test/dotty/tools/dotc/reporting/TestMessageLaziness.scala +++ b/compiler/test/dotty/tools/dotc/reporting/TestMessageLaziness.scala @@ -23,6 +23,7 @@ class TestMessageLaziness extends DottyTest { val kind = "Test" val msg = "Please don't blow up" val explanation = "" + val links = Nil } @Test def assureLazy =