aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala19
-rw-r--r--compiler/src/dotty/tools/dotc/typer/RefChecks.scala4
-rw-r--r--compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala24
-rw-r--r--tests/repl/errmsgs.check7
4 files changed, 51 insertions, 3 deletions
diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
index c25c49597..4d61f21cf 100644
--- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
+++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
@@ -1034,4 +1034,23 @@ object messages {
|"""
}
+ case class ForwardReferenceExtendsOverDefinition(value: Symbol, definition: Symbol)(implicit ctx: Context)
+ extends Message(39) {
+ val kind = "Reference"
+ val msg = hl"`${definition.name}` is a forward reference extending over the definition of `${value.name}`"
+
+ val explanation =
+ hl"""|`${definition.name}` is used before you define it, and the definition of `${value.name}`
+ |appears between that use and the definition of `${definition.name}`.
+ |
+ |Forward references are allowed only, if there are no value definitions between
+ |the reference and the referred method definition.
+ |
+ |Define `${definition.name}` before it is used,
+ |or move the definition of `${value.name}` so it does not appear between
+ |the declartion of `${definition.name}` and its use,
+ |or define `${value.name}` as lazy.
+ |""".stripMargin
+ }
+
}
diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala
index ada53047a..eab91701b 100644
--- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala
+++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala
@@ -767,6 +767,7 @@ import RefChecks._
class RefChecks extends MiniPhase { thisTransformer =>
import tpd._
+ import reporting.diagnostic.messages.ForwardReferenceExtendsOverDefinition
override def phaseName: String = "refchecks"
@@ -789,8 +790,7 @@ class RefChecks extends MiniPhase { thisTransformer =>
if (sym.exists && sym.owner.isTerm && !sym.is(Lazy))
currentLevel.levelAndIndex.get(sym) match {
case Some((level, symIdx)) if symIdx < level.maxIndex =>
- ctx.debuglog("refsym = " + level.refSym)
- ctx.error(s"forward reference extends over definition of $sym", level.refPos)
+ ctx.error(ForwardReferenceExtendsOverDefinition(sym, level.refSym), level.refPos)
case _ =>
}
tree
diff --git a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala
index be641fe15..d9b7f7f67 100644
--- a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala
+++ b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala
@@ -4,7 +4,6 @@ package reporting
import core.Contexts.Context
import diagnostic.messages._
-
import org.junit.Assert._
import org.junit.Test
@@ -85,4 +84,27 @@ class ErrorMessagesTests extends ErrorMessagesTest {
assert(sameName.forall(_.symbol.name.show == "bar"),
"at least one method had an unexpected name")
}
+
+ @Test def forwardReference =
+ checkMessagesAfter("refchecks") {
+ """
+ |object Forward {
+ | def block = {
+ | a.toInt
+ | val b = 2
+ | val a = BigDecimal("4")
+ | }
+ |}
+ """.stripMargin
+ }
+ .expect { (ictx, messages) =>
+ implicit val ctx: Context = ictx
+ val defn = ictx.definitions
+
+ assertMessageCount(1, messages)
+ val ForwardReferenceExtendsOverDefinition(value, definition) :: Nil = messages
+ assertEquals("value b", value.show)
+ assertEquals("value a", definition.show)
+ }
+
}
diff --git a/tests/repl/errmsgs.check b/tests/repl/errmsgs.check
index 0dc8e8ae5..d7a230e61 100644
--- a/tests/repl/errmsgs.check
+++ b/tests/repl/errmsgs.check
@@ -85,4 +85,11 @@ scala> val x: List[Int] = "foo" :: List(1)
| found: String($1$)
| required: Int
|
+scala> { def f: Int = g; val x: Int = 1; def g: Int = 5; }
+-- [E039] Reference Error: <console> -------------------------------------------
+5 |{ def f: Int = g; val x: Int = 1; def g: Int = 5; }
+ | ^
+ | `g` is a forward reference extending over the definition of `x`
+
+longer explanation available when compiling with `-explain`
scala> :quit