Skip to content

Commit b70f63c

Browse files
committed
Support pinpointing in stacktrace
1 parent 9f9f944 commit b70f63c

File tree

2 files changed

+41
-6
lines changed

2 files changed

+41
-6
lines changed

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

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ class CycleChecker(cache: Cache) {
147147
val ctor = obj.moduleClass.primaryConstructor
148148
var trace = state.trace.dropWhile(_.symbol != ctor) :+ dep
149149

150+
val pinpointOpt = trace.find(_.isInstanceOf[InstanceUsage])
150151
val traceSuppress = trace.size > traceNumberLimit
151152
if traceSuppress then
152153
// truncate trace up to the first escape of object
@@ -159,10 +160,16 @@ class CycleChecker(cache: Cache) {
159160
trace = trace :+ elem
160161

161162
val locations = trace.flatMap(_.source)
162-
if cycle.size > 1 then
163-
CyclicObjectInit(cycle, locations, traceSuppress) :: Nil
164-
else
165-
ObjectLeakDuringInit(obj, locations, traceSuppress) :: Nil
163+
val warning =
164+
if cycle.size > 1 then
165+
CyclicObjectInit(cycle, locations, traceSuppress)
166+
else
167+
ObjectLeakDuringInit(obj, locations, traceSuppress)
168+
169+
if pinpointOpt.nonEmpty then
170+
warning.pinpoint(pinpointOpt.get.source.last, "Leaking the object may cause initialization problems")
171+
172+
warning :: Nil
166173
else
167174
val constr = obj.moduleClass.primaryConstructor
168175
state.visitObject(dep) {

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

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import ast.tpd._
88
import core._
99
import Decorators._, printing.SyntaxHighlighting
1010
import Types._, Symbols._, Contexts._
11+
import util.{ SimpleIdentityMap, SourcePosition }
12+
13+
import reporting.MessageRendering
1114

1215
import Effects._, Potentials._
1316

@@ -30,10 +33,31 @@ object Errors {
3033

3134
def toErrors: Errors = this :: Nil
3235

36+
/** pinpoints in stacktrace */
37+
private var pinpoints: SimpleIdentityMap[Tree, String] = SimpleIdentityMap.empty
38+
39+
def pinpoint(tree: Tree, msg: String): this.type =
40+
this.pinpoints = this.pinpoints.updated(tree, msg)
41+
this
42+
3343
private def stacktracePrefix: String =
3444
val str = if traceSuppressed then "suppressed" else "full"
3545
" Calling trace (" + str + "):\n"
3646

47+
private val render = new MessageRendering {}
48+
49+
private def pinpointText(pos: SourcePosition, msg: String, offset: Int)(using Context): String =
50+
val carets = render.hl("Warning") {
51+
if (pos.startLine == pos.endLine)
52+
"^" * math.max(1, pos.endColumn - pos.startColumn)
53+
else "^"
54+
}
55+
56+
val padding = pos.startColumnPadding + (" " * offset)
57+
val marker = padding + carets
58+
val textline = padding + msg
59+
"\n" + marker + "\n" + textline
60+
3761
def stacktrace(using Context): String = if (trace.isEmpty) "" else stacktracePrefix + {
3862
var indentCount = 0
3963
var last: String = ""
@@ -45,8 +69,12 @@ object Errors {
4569
val line =
4670
if pos.source.exists then
4771
val loc = "[ " + pos.source.file.name + ":" + (pos.line + 1) + " ]"
48-
val code = SyntaxHighlighting.highlight(pos.lineContent.trim)
49-
i"$code\t$loc"
72+
val code = SyntaxHighlighting.highlight(pos.lineContent.stripLineEnd)
73+
val pinpoint =
74+
if !pinpoints.contains(tree) then ""
75+
else pinpointText(pos, pinpoints(tree), indentCount + 3)
76+
77+
i"$code\t$loc" + pinpoint
5078
else
5179
tree.show
5280

0 commit comments

Comments
 (0)