Skip to content

Commit c3ec6df

Browse files
committed
More detail in error messages
Split error messages for recursive method and overloaded method needs type into two (but did not solve the analysis which to show). Make CyclicReference type error construct corresponding error message.
1 parent 34e3508 commit c3ec6df

File tree

5 files changed

+71
-10
lines changed

5 files changed

+71
-10
lines changed

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import util.Positions.{Position, NoPosition}
1919
import util.Stats._
2020
import util.{DotClass, SimpleMap}
2121
import reporting.diagnostic.Message
22+
import reporting.diagnostic.messages.CyclicReferenceInvolving
2223
import ast.tpd._
2324
import ast.TreeTypeMap
2425
import printing.Texts._
@@ -3856,7 +3857,7 @@ object Types {
38563857

38573858
class CyclicReference private (val denot: SymDenotation)
38583859
extends TypeError(s"cyclic reference involving $denot") {
3859-
def show(implicit ctx: Context) = s"cyclic reference involving ${denot.show}"
3860+
def toMessage(implicit ctx: Context) = CyclicReferenceInvolving(denot)
38603861
}
38613862

38623863
object CyclicReference {

compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,10 @@ public enum ErrorMessageID {
5252
MixedLeftAndRightAssociativeOpsID,
5353
CantInstantiateAbstractClassOrTraitID,
5454
AnnotatedPrimaryConstructorRequiresModifierOrThisID,
55-
OverloadedOrRecursiveMethodNeedsResultTypeID,
55+
OverloadedMethodNeedsResultTypeID,
56+
RecursiveMethodNeedsResultTypeID,
5657
RecursiveValueNeedsResultTypeID,
58+
CyclicReferenceInvolvingID,
5759
CyclicReferenceInvolvingImplicitID,
5860
;
5961

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

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import dotc.parsing.Tokens
1818
import printing.Highlighting._
1919
import printing.Formatting
2020
import ErrorMessageID._
21+
import dotty.tools.dotc.core.SymDenotations.SymDenotation
2122

2223
object messages {
2324

@@ -1161,16 +1162,25 @@ object messages {
11611162
|""".stripMargin
11621163
}
11631164

1164-
case class OverloadedOrRecursiveMethodNeedsResultType(tree: Names.TermName)(implicit ctx: Context)
1165-
extends Message(OverloadedOrRecursiveMethodNeedsResultTypeID) {
1165+
case class OverloadedMethodNeedsResultType(tree: Names.TermName)(implicit ctx: Context)
1166+
extends Message(OverloadedMethodNeedsResultTypeID) {
11661167
val kind = "Syntax"
1167-
val msg = hl"""overloaded or recursive method ${tree} needs result type"""
1168+
val msg = hl"""overloaded method ${tree} needs result type"""
11681169
val explanation =
11691170
hl"""|${tree} is overloaded and at least one definition of it calls another.
11701171
|You need to specify the calling method's return type.
11711172
""".stripMargin
11721173
}
11731174

1175+
case class RecursiveMethodNeedsResultType(tree: Names.TermName)(implicit ctx: Context)
1176+
extends Message(RecursiveMethodNeedsResultTypeID) {
1177+
val kind = "Syntax"
1178+
val msg = hl"""recursive method ${tree} needs result type"""
1179+
val explanation =
1180+
hl"""|The definition of `${tree.name}` is recursive and you need to specify its type.
1181+
""".stripMargin
1182+
}
1183+
11741184
case class RecursiveValueNeedsResultType(tree: Names.TermName)(implicit ctx: Context)
11751185
extends Message(RecursiveValueNeedsResultTypeID) {
11761186
val kind = "Syntax"
@@ -1180,6 +1190,16 @@ object messages {
11801190
""".stripMargin
11811191
}
11821192

1193+
case class CyclicReferenceInvolving(denot: SymDenotation)(implicit ctx: Context)
1194+
extends Message(CyclicReferenceInvolvingID) {
1195+
val kind = "Syntax"
1196+
val msg = hl"""cyclic reference involving $denot"""
1197+
val explanation =
1198+
hl"""|$denot is declared as part of a cycle which makes it impossible for the
1199+
|compiler to decide upon ${denot.name}'s type.
1200+
|""".stripMargin
1201+
}
1202+
11831203
case class CyclicReferenceInvolvingImplicit(cycleSym: Symbol)(implicit ctx: Context)
11841204
extends Message(CyclicReferenceInvolvingImplicitID) {
11851205
val kind = "Syntax"

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,14 @@ object ErrorReporting {
2828

2929
def cyclicErrorMsg(ex: CyclicReference)(implicit ctx: Context) = {
3030
val cycleSym = ex.denot.symbol
31-
def errorMsg(msg: String, cx: Context): Message =
31+
def errorMsg(msg: Message, cx: Context): Message =
3232
if (cx.mode is Mode.InferringReturnType) {
3333
cx.tree match {
3434
case tree: untpd.DefDef if !tree.tpt.typeOpt.exists =>
35-
OverloadedOrRecursiveMethodNeedsResultType(tree.name)
35+
// TODO: analysis if tree is an overloaded method (or directly recursive)
36+
val overloaded = true
37+
if (overloaded) OverloadedMethodNeedsResultType(tree.name)
38+
else RecursiveMethodNeedsResultType(tree.name)
3639
case tree: untpd.ValDef if !tree.tpt.typeOpt.exists =>
3740
RecursiveValueNeedsResultType(tree.name)
3841
case _ =>
@@ -43,7 +46,7 @@ object ErrorReporting {
4346
if (cycleSym.is(Implicit, butNot = Method) && cycleSym.owner.isTerm)
4447
CyclicReferenceInvolvingImplicit(cycleSym)
4548
else
46-
errorMsg(ex.show, ctx)
49+
errorMsg(ex.toMessage, ctx)
4750
}
4851

4952
def wrongNumberOfTypeArgs(fntpe: Type, expectedArgs: List[TypeParamInfo], actual: List[untpd.Tree], pos: Position)(implicit ctx: Context) =

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

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import core.Contexts.Context
66
import diagnostic.messages._
77
import dotty.tools.dotc.parsing.Tokens
88
import org.junit.Assert._
9-
import org.junit.Test
9+
import org.junit.{Ignore, Test}
1010

1111
class ErrorMessagesTests extends ErrorMessagesTest {
1212
// In the case where there are no errors, we can do "expectNoErrors" in the
@@ -227,10 +227,27 @@ class ErrorMessagesTests extends ErrorMessagesTest {
227227
val defn = ictx.definitions
228228

229229
assertMessageCount(1, messages)
230-
val OverloadedOrRecursiveMethodNeedsResultType(tree) :: Nil = messages
230+
val OverloadedMethodNeedsResultType(tree) :: Nil = messages
231231
assertEquals("foo", tree.show)
232232
}
233233

234+
@Test @Ignore def recursiveMethodNeedsReturnType =
235+
checkMessagesAfter("frontend") {
236+
"""
237+
|class Scope() {
238+
| def i = i + 5
239+
|}
240+
""".stripMargin
241+
}
242+
.expect { (ictx, messages) =>
243+
implicit val ctx: Context = ictx
244+
val defn = ictx.definitions
245+
246+
assertMessageCount(1, messages)
247+
val RecursiveMethodNeedsResultType(tree) :: Nil = messages
248+
assertEquals("i", tree.show)
249+
}
250+
234251
@Test def recursiveValueNeedsReturnType =
235252
checkMessagesAfter("frontend") {
236253
"""
@@ -248,6 +265,24 @@ class ErrorMessagesTests extends ErrorMessagesTest {
248265
assertEquals("i", tree.show)
249266
}
250267

268+
@Test def cyclicReferenceInvolving =
269+
checkMessagesAfter("frontend") {
270+
"""
271+
|class A {
272+
| val x: T = ???
273+
| type T <: x.type // error: cyclic reference involving value x
274+
|}
275+
""".stripMargin
276+
}
277+
.expect { (ictx, messages) =>
278+
implicit val ctx: Context = ictx
279+
val defn = ictx.definitions
280+
281+
assertMessageCount(1, messages)
282+
val CyclicReferenceInvolving(denot) :: Nil = messages
283+
assertEquals("value x", denot.show)
284+
}
285+
251286
@Test def cyclicReferenceInvolvingImplicit =
252287
checkMessagesAfter("frontend") {
253288
"""

0 commit comments

Comments
 (0)