Skip to content

Commit 8de9ed5

Browse files
committed
chore: Show errors from crashes
Currently AssertionError, FatalError and IllegalArgument exceptions are sometimes enriched with additional information, which we can try to parse.
1 parent 65b0b29 commit 8de9ed5

File tree

3 files changed

+137
-2
lines changed

3 files changed

+137
-2
lines changed

backend/src/main/scala/bloop/Compiler.scala

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import java.util.concurrent.Executor
1010

1111
import scala.collection.mutable
1212
import scala.concurrent.Promise
13+
import scala.tools.nsc.FatalError
1314
import scala.util.Try
1415

1516
import bloop.Compiler.Result.Failed
@@ -21,6 +22,7 @@ import bloop.io.{Paths => BloopPaths}
2122
import bloop.logging.DebugFilter
2223
import bloop.logging.Logger
2324
import bloop.logging.ObservedLogger
25+
import bloop.reporter.ObservedReporter
2426
import bloop.reporter.ProblemPerPhase
2527
import bloop.reporter.Reporter
2628
import bloop.reporter.ZincReporter
@@ -31,6 +33,7 @@ import bloop.util.AnalysisUtils
3133
import bloop.util.BestEffortUtils
3234
import bloop.util.BestEffortUtils.BestEffortProducts
3335
import bloop.util.CacheHashCode
36+
import bloop.util.HashedSource
3437
import bloop.util.JavaRuntime
3538
import bloop.util.UUIDUtil
3639

@@ -47,7 +50,6 @@ import sbt.util.InterfaceUtil
4750
import xsbti.T2
4851
import xsbti.VirtualFileRef
4952
import xsbti.compile._
50-
import bloop.util.HashedSource
5153

5254
case class CompileInputs(
5355
scalaInstance: ScalaInstance,
@@ -783,7 +785,33 @@ object Compiler {
783785
val backgroundTasks =
784786
toBackgroundTasks(backgroundTasksForFailedCompilation.toList)
785787
val failedProblems = findFailedProblems(reporter, None)
786-
Result.Failed(failedProblems, Some(t), elapsed, backgroundTasks, None)
788+
val errorFromThrowable = t match {
789+
case _: AssertionError | _: FatalError | _: IllegalArgumentException =>
790+
bloop.reporter.Problem.fromError(t) match {
791+
case Some(problem) =>
792+
reporter match {
793+
case observedReporter: ObservedReporter =>
794+
observedReporter.log(problem)
795+
case _ =>
796+
}
797+
List(
798+
ProblemPerPhase(
799+
problem,
800+
phase = None
801+
)
802+
)
803+
804+
case None => Nil
805+
}
806+
case _ => Nil
807+
}
808+
Result.Failed(
809+
failedProblems ++ errorFromThrowable,
810+
Some(t),
811+
elapsed,
812+
backgroundTasks,
813+
None
814+
)
787815
}
788816
}
789817
}

frontend/src/test/scala/bloop/bsp/BspCompileSpec.scala

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import java.util.concurrent.TimeUnit
88
import scala.collection.JavaConverters._
99
import scala.concurrent.duration.FiniteDuration
1010

11+
import bloop.Compiler.Result.Failed
1112
import bloop.cli.BspProtocol
1213
import bloop.cli.ExitStatus
1314
import bloop.internal.build.BuildInfo
@@ -1422,5 +1423,69 @@ class BspCompileSpec(
14221423
runTest(2000)
14231424
}
14241425
}
1426+
test("crash-compiler-diagnostics") {
1427+
TestUtil.withinWorkspace { workspace =>
1428+
// Macro project - must be compiled first
1429+
val macroProject = TestProject(
1430+
workspace,
1431+
"macros",
1432+
List(
1433+
"""/main/scala/FooMacro.scala
1434+
|package macros
1435+
|
1436+
|import scala.language.experimental.macros
1437+
|import scala.reflect.macros.blackbox
1438+
|
1439+
|object FooMacro {
1440+
| def crashNow: Int = macro crashNowImpl
1441+
|
1442+
| def crashNowImpl(c: blackbox.Context): c.Expr[Int] = {
1443+
| import c.universe._
1444+
| val badSymbol = c.internal.newTermSymbol(NoSymbol, TermName("badSymbol"))
1445+
| val badTree = Ident(badSymbol)
1446+
| c.Expr[Int](badTree)
1447+
| }
1448+
|}
1449+
|
1450+
|
1451+
""".stripMargin
1452+
)
1453+
)
1454+
// Fast file that will be modified to break compilation if stale bytecode is used
1455+
val downstreamFile =
1456+
"""/main/scala/a/FastCompilation.scala
1457+
|package a
1458+
|import macros.FooMacro
1459+
|
1460+
|case class User(name: Int, age: Int)
1461+
|
1462+
|object Bar{
1463+
| def baz2 = FooMacro.crashNow
1464+
|
1465+
|}
1466+
|
1467+
""".stripMargin
1468+
1469+
val logger = new RecordingLogger(ansiCodesSupported = false)
1470+
val testProject = TestProject(
1471+
workspace,
1472+
"a",
1473+
List(downstreamFile),
1474+
List(macroProject),
1475+
scalaVersion = Some("2.13.16")
1476+
)
1477+
val projects = List(macroProject, testProject)
1478+
loadBspState(workspace, projects, logger) { state =>
1479+
val macroCompiled = state.compile(macroProject)
1480+
assert(macroCompiled.status == ExitStatus.Ok)
1481+
val downstreamCompiled = state.compile(testProject)
1482+
assert(downstreamCompiled.status == ExitStatus.CompilationError)
1483+
val lastDiagnostics = downstreamCompiled.lastDiagnostics(testProject)
1484+
assertContains(lastDiagnostics, "badSymbol")
1485+
assertContains(lastDiagnostics, "tree position: line 7 of ")
1486+
assertContains(lastDiagnostics, "a/src/main/scala/a/FastCompilation.scala")
1487+
}
1488+
}
1489+
}
14251490

14261491
}

shared/src/main/scala/bloop/reporter/Problem.scala

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ package bloop.reporter
33
import java.util.Optional
44

55
import xsbti.Severity
6+
import java.util.ArrayList
7+
import java.io.File
8+
import java.nio.file.Paths
69

710
/** Describes a problem (error, warning, message, etc.) given to the reporter. */
811
final case class Problem private (
@@ -36,6 +39,45 @@ object Problem {
3639
)
3740
}
3841

42+
def fromError(error: Throwable): Option[Problem] = {
43+
val assertionErrorRegex = """tree position: line (\d+) of (.+\.scala)""".r
44+
assertionErrorRegex.findFirstMatchIn(error.getMessage()) match {
45+
case Some(m) =>
46+
val line = m.group(1).toInt
47+
val file = Paths.get(m.group(2)).toFile()
48+
Some(
49+
Problem(
50+
-1,
51+
xsbti.Severity.Error,
52+
error.getMessage(),
53+
BloopPosition(file, Optional.of(line)),
54+
"assertion error",
55+
Optional.empty(),
56+
new ArrayList[xsbti.DiagnosticRelatedInformation](),
57+
new ArrayList[xsbti.Action]()
58+
)
59+
)
60+
case None => // No match
61+
None
62+
}
63+
}
64+
65+
case class BloopPosition(file: File, line: Optional[Integer]) extends xsbti.Position {
66+
67+
override def lineContent(): String = ""
68+
69+
override def offset(): Optional[Integer] = Optional.empty()
70+
71+
override def pointer(): Optional[Integer] = Optional.empty()
72+
73+
override def pointerSpace(): Optional[String] = Optional.empty()
74+
75+
override def sourcePath(): Optional[String] = Optional.empty()
76+
77+
override def sourceFile(): Optional[File] = Optional.of(file)
78+
79+
}
80+
3981
case class DiagnosticsCount(errors: Long, warnings: Long) {
4082
override def toString: String = s"$errors errors, $warnings warnings"
4183
}

0 commit comments

Comments
 (0)