Skip to content

Commit b38aef5

Browse files
committed
More friendly stack trace
1 parent 662da71 commit b38aef5

File tree

2 files changed

+33
-8
lines changed

2 files changed

+33
-8
lines changed

compiler/src/dotty/tools/dotc/transform/init/CycleChecker.scala

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ class CycleChecker(cache: Cache) {
8686
val classesInCurrentRun = mutable.Set.empty[Symbol]
8787
val objectsInCurrentRun = new mutable.ListBuffer[Symbol]
8888

89+
/** The limit of stack trace shown to programmers
90+
*
91+
* TODO: make it configurable from command-line for debugging purposes
92+
*/
93+
val traceNumberLimit = 10
94+
8995
/** Checking state */
9096
case class State(visited: mutable.Set[Dependency], path: Vector[Symbol], trace: Vector[Dependency]) {
9197
def visit[T](dep: Dependency)(op: State ?=> T): T =
@@ -138,11 +144,25 @@ class CycleChecker(cache: Cache) {
138144
val obj = dep.symbol
139145
if state.path.contains(obj) then
140146
val cycle = state.path.dropWhile(_ != obj)
141-
val trace = state.trace.dropWhile(_.symbol != obj).flatMap(_.source) ++ dep.source
147+
val ctor = obj.moduleClass.primaryConstructor
148+
var trace = state.trace.dropWhile(_.symbol != ctor) :+ dep
149+
150+
val traceSuppress = trace.size > traceNumberLimit
151+
if traceSuppress then
152+
// truncate trace up to the first escape of object
153+
val iter = trace.iterator
154+
trace = Vector.empty
155+
var elem = iter.next()
156+
trace = trace :+ elem
157+
while iter.hasNext && !elem.isInstanceOf[InstanceUsage] do
158+
elem = iter.next()
159+
trace = trace :+ elem
160+
161+
val locations = trace.flatMap(_.source)
142162
if cycle.size > 1 then
143-
CyclicObjectInit(cycle, trace) :: Nil
163+
CyclicObjectInit(cycle, locations, traceSuppress) :: Nil
144164
else
145-
ObjectLeakDuringInit(obj, trace) :: Nil
165+
ObjectLeakDuringInit(obj, locations, traceSuppress) :: Nil
146166
else
147167
val constr = obj.moduleClass.primaryConstructor
148168
state.visitObject(dep) {

compiler/src/dotty/tools/dotc/transform/init/Errors.scala

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,18 @@ object Errors {
2323
def trace: Seq[Tree]
2424
def show(using Context): String
2525

26+
def traceSuppressed: Boolean = false
27+
2628
def issue(using Context): Unit =
2729
report.warning(show + stacktrace, source.srcPos)
2830

2931
def toErrors: Errors = this :: Nil
3032

31-
def stacktrace(using Context): String = if (trace.isEmpty) "" else " Calling trace:\n" + {
33+
private def stacktracePrefix: String =
34+
val str = if traceSuppressed then "suppressed" else "full"
35+
" Calling trace (" + str + "):\n"
36+
37+
def stacktrace(using Context): String = if (trace.isEmpty) "" else stacktracePrefix + {
3238
var indentCount = 0
3339
var last: String = ""
3440
val sb = new StringBuilder
@@ -69,7 +75,7 @@ object Errors {
6975
report.warning(show + stacktrace, field.srcPos)
7076
}
7177

72-
case class CyclicObjectInit(objs: Seq[Symbol], trace: Seq[Tree]) extends Error {
78+
case class CyclicObjectInit(objs: Seq[Symbol], trace: Seq[Tree], override val traceSuppressed: Boolean) extends Error {
7379
def source: Tree = trace.last
7480
def show(using Context): String =
7581
"Cyclic object initialization for " + objs.map(_.show).mkString(", ") + "."
@@ -78,10 +84,9 @@ object Errors {
7884
report.warning(show + stacktrace, objs.head.srcPos)
7985
}
8086

81-
case class ObjectLeakDuringInit(obj: Symbol, trace: Seq[Tree]) extends Error {
87+
case class ObjectLeakDuringInit(obj: Symbol, trace: Seq[Tree], override val traceSuppressed: Boolean) extends Error {
8288
def source: Tree = trace.last
83-
def show(using Context): String =
84-
obj.show + " leaked during its initialization " + "."
89+
def show(using Context): String = obj.show + " leaked during its initialization " + "."
8590

8691
override def issue(using Context): Unit =
8792
report.warning(show + stacktrace, obj.srcPos)

0 commit comments

Comments
 (0)