Skip to content

Commit f25fa1f

Browse files
committed
Fix #3305: Handle exceptions in REPL
The new REPL did not catch exceptions. We now handle exceptions but there is a lot of room for improvement when reporting an exception.
1 parent 023303d commit f25fa1f

File tree

3 files changed

+47
-6
lines changed

3 files changed

+47
-6
lines changed

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

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package dotty.tools
22
package repl
33

4-
import java.lang.ClassLoader
4+
import java.io.{ StringWriter, PrintWriter }
5+
import java.lang.{ ClassLoader, ExceptionInInitializerError }
6+
import java.lang.reflect.InvocationTargetException
57

68
import scala.util.control.NonFatal
79

@@ -70,10 +72,26 @@ private[repl] class Rendering(compiler: ReplCompiler,
7072
/** Render value definition result */
7173
def renderVal(d: Denotation)(implicit ctx: Context): Option[String] = {
7274
val dcl = d.symbol.showUser
73-
val resultValue =
74-
if (d.symbol.is(Flags.Lazy)) Some("<lazy>")
75-
else valueOf(d.symbol)
7675

77-
resultValue.map(value => s"$dcl = $value")
76+
try {
77+
val resultValue =
78+
if (d.symbol.is(Flags.Lazy)) Some("<lazy>")
79+
else valueOf(d.symbol)
80+
81+
resultValue.map(value => s"$dcl = $value")
82+
}
83+
catch { case ex: InvocationTargetException => Some(renderError(ex)) }
84+
}
85+
86+
/** Render the stack trace of the underlying exception */
87+
private def renderError(ex: InvocationTargetException): String = {
88+
val cause = ex.getCause match {
89+
case ex: ExceptionInInitializerError => ex.getCause
90+
case ex => ex
91+
}
92+
val sw = new StringWriter()
93+
val pw = new PrintWriter(sw)
94+
cause.printStackTrace(pw)
95+
sw.toString
7896
}
7997
}

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,27 @@ class ReplCompilerTests extends ReplTest {
9292
storedOutput()
9393
)
9494
}
95+
96+
@Test def i3305: Unit = {
97+
fromInitialState { implicit s =>
98+
compile("null.toString")
99+
storedOutput().startsWith("java.lang.NullPointerException")
100+
}
101+
102+
fromInitialState { implicit s =>
103+
compile("def foo: Int = 1 + foo; foo")
104+
storedOutput().startsWith("def foo: Int\njava.lang.StackOverflowError")
105+
}
106+
107+
fromInitialState { implicit s =>
108+
compile("""throw new IllegalArgumentException("Hello")""")
109+
storedOutput().startsWith("java.lang.IllegalArgumentException: Hello")
110+
}
111+
112+
// FIXME
113+
// fromInitialState { implicit s =>
114+
// compile("val (x, y) = null")
115+
// storedOutput().startsWith("scala.MatchError: null")
116+
// }
117+
}
95118
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import scala.io.Source
1111

1212
import dotc.reporting.MessageRendering
1313

14-
/** Runs all tests contained in `/repl/test-resources/scripts` */
14+
/** Runs all tests contained in `compiler/test-resources/repl/` */
1515
class ScriptedTests extends ReplTest with MessageRendering {
1616

1717
private def scripts(path: String): Array[JFile] = {

0 commit comments

Comments
 (0)