Skip to content

Commit 7b0b6f8

Browse files
committed
REPL force module load as last resort
If there are no members, and it's not a template, forcibly initialize the enclosing class.
1 parent f334e3f commit 7b0b6f8

File tree

3 files changed

+34
-9
lines changed

3 files changed

+34
-9
lines changed

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

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -113,18 +113,25 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None) {
113113
infoDiagnostic(d.symbol.showUser, d)
114114

115115
/** Render value definition result */
116-
def renderVal(d: Denotation)(using Context): Option[Diagnostic] = {
116+
def renderVal(d: Denotation)(using Context): Option[Diagnostic] =
117117
val dcl = d.symbol.showUser
118118

119-
try {
119+
try
120120
if (d.symbol.is(Flags.Lazy)) Some(infoDiagnostic(dcl, d))
121121
else valueOf(d.symbol).map(value => infoDiagnostic(s"$dcl = $value", d))
122-
}
123-
catch { case ex: InvocationTargetException => Some(infoDiagnostic(renderError(ex), d)) }
124-
}
125-
126-
/** Render the stack trace of the underlying exception */
127-
private def renderError(ex: InvocationTargetException): String = {
122+
catch case ex: InvocationTargetException => Some(infoDiagnostic(renderError(ex), d))
123+
end renderVal
124+
125+
/** Force module initialization in the absence of members. */
126+
def forceModule(sym: Symbol)(using Context): Seq[Diagnostic] =
127+
def load() =
128+
val objectName = sym.fullName.encode.toString
129+
val resObj: Class[?] = Class.forName(objectName, true, classLoader())
130+
Nil
131+
try load() catch case e: ExceptionInInitializerError => List(infoDiagnostic(renderError(e), sym.denot))
132+
133+
/** Render the stack trace of the underlying exception. */
134+
private def renderError(ex: InvocationTargetException | ExceptionInInitializerError): String = {
128135
val cause = ex.getCause match {
129136
case ex: ExceptionInInitializerError => ex.getCause
130137
case ex => ex

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,9 @@ class ReplDriver(settings: Array[String],
309309
defs.map(rendering.renderMethod) ++
310310
vals.flatMap(rendering.renderVal)
311311

312-
(state.copy(valIndex = state.valIndex - vals.count(resAndUnit)), formattedMembers)
312+
val diagnostics = if formattedMembers.isEmpty then rendering.forceModule(symbol) else formattedMembers
313+
314+
(state.copy(valIndex = state.valIndex - vals.count(resAndUnit)), diagnostics)
313315
}
314316
else (state, Seq.empty)
315317

compiler/test/dotty/tools/repl/ReplCompilerTests.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,22 @@ class ReplCompilerTests extends ReplTest {
203203
run("val a: 1 | 0 = 1")
204204
assertEquals("val a: 1 | 0 = 1", storedOutput().trim)
205205
}
206+
207+
@Test def `i10214 must show classic MatchError` = fromInitialState { implicit state =>
208+
run("val 1 = 2")
209+
assertEquals("scala.MatchError: 2 (of class java.lang.Integer)", storedOutput().linesIterator.next())
210+
}
211+
@Test def `i10214 must show useful regex MatchError` =
212+
fromInitialState { implicit state =>
213+
run("""val r = raw"\d+".r""")
214+
} andThen { implicit state =>
215+
run("""val r() = "abc"""")
216+
assertEquals("scala.MatchError: abc (of class java.lang.String)", storedOutput().linesIterator.drop(2).next())
217+
}
218+
@Test def `i10214 must show MatchError on literal type` = fromInitialState { implicit state =>
219+
run("val (x: 1) = 2")
220+
assertEquals("scala.MatchError: 2 (of class java.lang.Integer)", storedOutput().linesIterator.next())
221+
}
206222
}
207223

208224
object ReplCompilerTests {

0 commit comments

Comments
 (0)