diff --git a/compiler/src/dotty/tools/repl/Rendering.scala b/compiler/src/dotty/tools/repl/Rendering.scala index 00d489d08391..4194b884b97d 100644 --- a/compiler/src/dotty/tools/repl/Rendering.scala +++ b/compiler/src/dotty/tools/repl/Rendering.scala @@ -113,18 +113,25 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None) { infoDiagnostic(d.symbol.showUser, d) /** Render value definition result */ - def renderVal(d: Denotation)(using Context): Option[Diagnostic] = { + def renderVal(d: Denotation)(using Context): Option[Diagnostic] = val dcl = d.symbol.showUser - try { + try if (d.symbol.is(Flags.Lazy)) Some(infoDiagnostic(dcl, d)) else valueOf(d.symbol).map(value => infoDiagnostic(s"$dcl = $value", d)) - } - catch { case ex: InvocationTargetException => Some(infoDiagnostic(renderError(ex), d)) } - } - - /** Render the stack trace of the underlying exception */ - private def renderError(ex: InvocationTargetException): String = { + catch case ex: InvocationTargetException => Some(infoDiagnostic(renderError(ex), d)) + end renderVal + + /** Force module initialization in the absence of members. */ + def forceModule(sym: Symbol)(using Context): Seq[Diagnostic] = + def load() = + val objectName = sym.fullName.encode.toString + val resObj: Class[?] = Class.forName(objectName, true, classLoader()) + Nil + try load() catch case e: ExceptionInInitializerError => List(infoDiagnostic(renderError(e), sym.denot)) + + /** Render the stack trace of the underlying exception. */ + private def renderError(ex: InvocationTargetException | ExceptionInInitializerError): String = { val cause = ex.getCause match { case ex: ExceptionInInitializerError => ex.getCause case ex => ex diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index c1a381d6c194..dc570d3c0c4e 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -309,7 +309,9 @@ class ReplDriver(settings: Array[String], defs.map(rendering.renderMethod) ++ vals.flatMap(rendering.renderVal) - (state.copy(valIndex = state.valIndex - vals.count(resAndUnit)), formattedMembers) + val diagnostics = if formattedMembers.isEmpty then rendering.forceModule(symbol) else formattedMembers + + (state.copy(valIndex = state.valIndex - vals.count(resAndUnit)), diagnostics) } else (state, Seq.empty) diff --git a/compiler/test/dotty/tools/repl/ReplCompilerTests.scala b/compiler/test/dotty/tools/repl/ReplCompilerTests.scala index f78c6331b0e0..7192f8de7e4f 100644 --- a/compiler/test/dotty/tools/repl/ReplCompilerTests.scala +++ b/compiler/test/dotty/tools/repl/ReplCompilerTests.scala @@ -203,6 +203,22 @@ class ReplCompilerTests extends ReplTest { run("val a: 1 | 0 = 1") assertEquals("val a: 1 | 0 = 1", storedOutput().trim) } + + @Test def `i10214 must show classic MatchError` = fromInitialState { implicit state => + run("val 1 = 2") + assertEquals("scala.MatchError: 2 (of class java.lang.Integer)", storedOutput().linesIterator.next()) + } + @Test def `i10214 must show useful regex MatchError` = + fromInitialState { implicit state => + run("""val r = raw"\d+".r""") + } andThen { implicit state => + run("""val r() = "abc"""") + assertEquals("scala.MatchError: abc (of class java.lang.String)", storedOutput().linesIterator.drop(2).next()) + } + @Test def `i10214 must show MatchError on literal type` = fromInitialState { implicit state => + run("val (x: 1) = 2") + assertEquals("scala.MatchError: 2 (of class java.lang.Integer)", storedOutput().linesIterator.next()) + } } object ReplCompilerTests {