Skip to content

Commit d151881

Browse files
committed
Refinement to error messages and tests
1 parent 21cc1b6 commit d151881

File tree

9 files changed

+97
-18
lines changed

9 files changed

+97
-18
lines changed

compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,19 @@ class ConsoleReporter(
2424
def doReport(m: MessageContainer)(implicit ctx: Context): Unit = {
2525
val didPrint = m match {
2626
case m: Error =>
27-
printMessage(messageAndPos(m.contained(), m.pos, diagnosticLevel(m)))
27+
printMessage(messageAndPos(m.contained, m.pos, diagnosticLevel(m)))
2828
if (ctx.settings.Xprompt.value) Reporter.displayPrompt(reader, writer)
2929
true
3030
case m: ConditionalWarning if !m.enablingOption.value =>
3131
false
3232
case m =>
33-
printMessage(messageAndPos(m.contained(), m.pos, diagnosticLevel(m)))
33+
printMessage(messageAndPos(m.contained, m.pos, diagnosticLevel(m)))
3434
true
3535
}
3636

3737
if (didPrint && ctx.shouldExplain(m))
38-
printMessage(explanation(m.contained()))
39-
else if (didPrint && m.contained().explanation.nonEmpty)
38+
printMessage(explanation(m.contained))
39+
else if (didPrint && m.contained.explanation.nonEmpty)
4040
printMessage("\nlonger explanation available when compiling with `-explain`")
4141
}
4242

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ object MessageContainer {
1515
implicit class MessageContext(val c: Context) extends AnyVal {
1616
def shouldExplain(cont: MessageContainer): Boolean = {
1717
implicit val ctx = c
18-
cont.contained().explanation match {
18+
cont.contained.explanation match {
1919
case "" => false
2020
case _ => ctx.settings.explain.value
2121
}
@@ -39,7 +39,7 @@ class MessageContainer(
3939
/** The message to report */
4040
def message: String = {
4141
if (myMsg == null) {
42-
myMsg = contained().msg.replaceAll("\u001B\\[[;\\d]*m", "")
42+
myMsg = contained.msg.replaceAll("\u001B\\[[;\\d]*m", "")
4343
if (myMsg.contains(nonSensicalStartTag)) {
4444
myIsNonSensical = true
4545
// myMsg might be composed of several d"..." invocations -> nested
@@ -54,7 +54,7 @@ class MessageContainer(
5454
}
5555

5656
/** This function forces the contained message and returns it */
57-
def contained(): Message = {
57+
def contained: Message = {
5858
if (myContained == null)
5959
myContained = msgFn
6060

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import collection.mutable
2828
import config.Printers.{overload, typr, unapp}
2929
import TypeApplications._
3030

31-
import reporting.diagnostic.Message
31+
import reporting.diagnostic.{Message, messages}
3232
import reporting.trace
3333
import Constants.{Constant, IntTag, LongTag}
3434
import dotty.tools.dotc.reporting.diagnostic.messages.{UnapplyInvalidReturnType, NotAnExtractor, UnapplyInvalidNumberOfArguments}
@@ -1058,15 +1058,27 @@ trait Applications extends Compatibility {
10581058
record("typedUnApply")
10591059
val Apply(qual, args) = tree
10601060

1061-
def notAnExtractor(tree: Tree) =
1061+
def notAnExtractor(tree: Tree): Tree =
10621062
// prefer inner errors
10631063
// e.g. report not found ident instead of not an extractor in tests/neg/i2950.scala
10641064
if (!tree.tpe.isError && tree.tpe.isErroneous) tree
10651065
else errorTree(tree, NotAnExtractor(qual))
10661066

1067-
def reportErrors(tree: Tree, state: TyperState) =
1067+
/** Report errors buffered in state.
1068+
* @pre state has errors to report
1069+
* If there is a single error stating that "unapply" is not a member, print
1070+
* the more informative "notAnExtractor" message instead.
1071+
*/
1072+
def reportErrors(tree: Tree, state: TyperState): Tree =
10681073
assert(state.reporter.hasErrors)
1069-
state.reporter.flush()
1074+
val msgs = state.reporter.removeBufferedMessages
1075+
msgs match
1076+
case msg :: Nil =>
1077+
msg.contained match
1078+
case messages.NotAMember(_, nme.unapply, _, _) => return notAnExtractor(tree)
1079+
case _ =>
1080+
case _ =>
1081+
msgs.foreach(ctx.reporter.report)
10701082
tree
10711083

10721084
/** If this is a term ref tree, try to typecheck with its type name.

compiler/src/dotty/tools/repl/ReplDriver.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ class ReplDriver(settings: Array[String],
381381

382382
/** Render messages using the `MessageRendering` trait */
383383
private def renderMessage(cont: MessageContainer): Context => String =
384-
messageRenderer.messageAndPos(cont.contained(), cont.pos, messageRenderer.diagnosticLevel(cont))(_)
384+
messageRenderer.messageAndPos(cont.contained, cont.pos, messageRenderer.diagnosticLevel(cont))(_)
385385

386386
/** Output errors to `out` */
387387
private def displayErrors(errs: Seq[MessageContainer])(implicit state: State): State = {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ trait ErrorMessagesTest extends DottyTest {
3939
if (!runCtx.reporter.hasErrors) new EmptyReport
4040
else {
4141
val rep = runCtx.reporter.asInstanceOf[StoreReporter]
42-
val msgs = rep.removeBufferedMessages(runCtx).map(_.contained()).reverse
42+
val msgs = rep.removeBufferedMessages(runCtx).map(_.contained).reverse
4343
new Report(msgs, runCtx)
4444
}
4545
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ extends Reporter with UniqueMessagePositions with HideNonSensicalMessages with M
5454

5555
/** Prints the message with the given position indication. */
5656
def printMessageAndPos(m: MessageContainer, extra: String)(implicit ctx: Context): Unit = {
57-
val msg = messageAndPos(m.contained(), m.pos, diagnosticLevel(m))
57+
val msg = messageAndPos(m.contained, m.pos, diagnosticLevel(m))
5858
val extraInfo = inlineInfo(m.pos)
5959

6060
if (m.level >= logLevel) {
@@ -69,7 +69,7 @@ extends Reporter with UniqueMessagePositions with HideNonSensicalMessages with M
6969
override def doReport(m: MessageContainer)(implicit ctx: Context): Unit = {
7070

7171
// Here we add extra information that we should know about the error message
72-
val extra = m.contained() match {
72+
val extra = m.contained match {
7373
case pm: PatternMatchExhaustivity => s": ${pm.uncovered}"
7474
case _ => ""
7575
}
@@ -127,7 +127,7 @@ object TestReporter {
127127
/** Prints the message with the given position indication in a simplified manner */
128128
override def printMessageAndPos(m: MessageContainer, extra: String)(implicit ctx: Context): Unit = {
129129
def report() = {
130-
val msg = s"${m.pos.line + 1}: " + m.contained().kind + extra
130+
val msg = s"${m.pos.line + 1}: " + m.contained.kind + extra
131131
val extraInfo = inlineInfo(m.pos)
132132

133133
writer.println(msg)

tests/neg/bad-unapplies.check

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
-- [E051] Reference Error: tests/neg/bad-unapplies.scala:22:9 ----------------------------------------------------------
2+
22 | case A("2") => // error (cannot resolve overloading)
3+
| ^
4+
| Ambiguous overload. The overloaded alternatives of method unapply in object A with types
5+
| (x: B): Option[String]
6+
| (x: A): Option[String]
7+
| both match arguments (C)
8+
9+
longer explanation available when compiling with `-explain`
10+
-- [E127] Syntax Error: tests/neg/bad-unapplies.scala:23:9 -------------------------------------------------------------
11+
23 | case B("2") => // error (cannot be used as an extractor)
12+
| ^
13+
| B cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method
14+
15+
longer explanation available when compiling with `-explain`
16+
-- [E127] Syntax Error: tests/neg/bad-unapplies.scala:24:9 -------------------------------------------------------------
17+
24 | case D("2") => // error (cannot be used as an extractor)
18+
| ^
19+
| D cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method
20+
21+
longer explanation available when compiling with `-explain`
22+
-- [E050] Reference Error: tests/neg/bad-unapplies.scala:25:9 ----------------------------------------------------------
23+
25 | case E("2") => // error (value unapply in object E does not take parameters)
24+
| ^
25+
| value unapply in object E does not take parameters
26+
27+
longer explanation available when compiling with `-explain`
28+
-- [E107] Syntax Error: tests/neg/bad-unapplies.scala:26:10 ------------------------------------------------------------
29+
26 | case F("2") => // error (Wrong number of argument patterns for F; expected: ())
30+
| ^^^^^^
31+
| Wrong number of argument patterns for F; expected: ()
32+
33+
longer explanation available when compiling with `-explain`
34+
-- [E006] Unbound Identifier Error: tests/neg/bad-unapplies.scala:27:9 -------------------------------------------------
35+
27 | case G("2") => // error (Not found: G)
36+
| ^
37+
| Not found: G
38+
39+
longer explanation available when compiling with `-explain`

tests/neg/bad-unapplies.scala

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
trait A
2+
trait B
3+
class C extends A, B
4+
object A:
5+
def unapply(x: A): Option[String] = Some(x.toString)
6+
def unapply(x: B): Option[String] = Some(x.toString)
7+
8+
object B
9+
10+
object D:
11+
def unapply(x: A, y: B): Option[String] = Some(x.toString)
12+
13+
object E:
14+
val unapply: Option[String] = Some("")
15+
16+
object F:
17+
def unapply(x: Int): Boolean = true
18+
19+
20+
@main def Test =
21+
C() match
22+
case A("2") => // error (cannot resolve overloading)
23+
case B("2") => // error (cannot be used as an extractor)
24+
case D("2") => // error (cannot be used as an extractor)
25+
case E("2") => // error (value unapply in object E does not take parameters)
26+
case F("2") => // error (Wrong number of argument patterns for F; expected: ())
27+
case G("2") => // error (Not found: G)
28+
end Test

tests/neg/i5101.check

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
-- [E006] Unbound Identifier Error: tests/neg/i5101.scala:11:13 --------------------------------------------------------
1+
-- [E006] Unbound Identifier Error: tests/neg/i5101.scala:11:11 --------------------------------------------------------
22
11 | case A0(_) => // error
3-
| ^^^^^
3+
| ^^
44
| Not found: A0
55

66
longer explanation available when compiling with `-explain`

0 commit comments

Comments
 (0)