Skip to content

Commit d8605c3

Browse files
authored
Skip caret when source is missing in initialization checker (#23926)
Skip caret when source is missing in initialization checker
1 parent c068c25 commit d8605c3

File tree

5 files changed

+61
-6
lines changed

5 files changed

+61
-6
lines changed

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

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import core.*
66
import Contexts.*
77
import ast.tpd.*
88
import util.SourcePosition
9+
import util.SourceFile
910

1011
import Decorators.*, printing.SyntaxHighlighting
1112

@@ -42,17 +43,33 @@ object Trace:
4243

4344
inline def extendTrace[T](node: Tree)(using t: Trace)(op: Trace ?=> T): T = op(using t.add(node))
4445

46+
/**
47+
* Returns whether the source file exists
48+
*
49+
* The method SourceFile#exists always return true thus cannot be used.
50+
*/
51+
def fileExists(source: SourceFile): Boolean =
52+
source.content().nonEmpty
53+
4554
def buildStacktrace(trace: Trace, preamble: String)(using Context): String = if trace.isEmpty then "" else preamble + {
4655
var lastLineNum = -1
4756
var lines: mutable.ArrayBuffer[String] = new mutable.ArrayBuffer
4857
trace.foreach { tree =>
4958
val isLastTraceItem = tree `eq` trace.last
5059
val pos = tree.sourcePos
60+
val hasSource = fileExists(pos.source)
5161
val line =
52-
if pos.source.exists then
53-
val loc = "[ " + pos.source.file.name + ":" + (pos.line + 1) + " ]"
54-
val code = SyntaxHighlighting.highlight(pos.lineContent.trim)
55-
i"$code\t$loc"
62+
if pos.exists then
63+
// Show more information for external code without source
64+
val file = if hasSource then pos.source.file.name else pos.source.file.path
65+
val loc = file + ":" + (pos.line + 1)
66+
val code =
67+
if hasSource then
68+
SyntaxHighlighting.highlight(pos.lineContent.trim)
69+
else
70+
"(no source)"
71+
72+
i"$code\t[ $loc ]"
5673
else
5774
tree match
5875
case defDef: DefTree =>
@@ -62,7 +79,7 @@ object Trace:
6279
tree.show.split(System.lineSeparator(), 2).head
6380

6481
val positionMarkerLine =
65-
if pos.exists && pos.source.exists then
82+
if pos.exists && hasSource then
6683
(if isLastTraceItem then EMPTY_PADDING else CONNECTING_INDENT)+ positionMarker(pos)
6784
else
6885
""

compiler/test/dotty/tools/dotc/CompilationTests.scala

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ class CompilationTests {
257257
compileFilesInDir("tests/explicit-nulls/run", explicitNullsOptions)
258258
}.checkRuns()
259259

260-
// initialization tests
260+
// initialization tests for global objects
261261
@Test def checkInitGlobal: Unit = {
262262
implicit val testGroup: TestGroup = TestGroup("checkInitGlobal")
263263
compileFilesInDir("tests/init-global/warn", defaultOptions.and("-Ysafe-init-global"), FileFilter.exclude(TestSources.negInitGlobalScala2LibraryTastyExcludelisted)).checkWarnings()
@@ -266,6 +266,20 @@ class CompilationTests {
266266
compileFilesInDir("tests/init-global/warn-tasty", defaultOptions.and("-Ysafe-init-global"), FileFilter.exclude(TestSources.negInitGlobalScala2LibraryTastyExcludelisted)).checkWarnings()
267267
compileFilesInDir("tests/init-global/pos-tasty", defaultOptions.and("-Ysafe-init-global", "-Werror"), FileFilter.exclude(TestSources.posInitGlobalScala2LibraryTastyExcludelisted)).checkCompile()
268268
end if
269+
270+
locally {
271+
val group = TestGroup("checkInitGlobal/tastySource")
272+
val tastSourceOptions = defaultOptions.and("-Ysafe-init-global")
273+
val outDirLib = defaultOutputDir + group + "/A/tastySource/A"
274+
275+
// Set -sourceroot such that the source code cannot be found by the compiler
276+
val libOptions = tastSourceOptions.and("-sourceroot", "tests/init-global/special")
277+
val lib = compileFile("tests/init-global/special/tastySource/A.scala", libOptions)(group).keepOutput.checkCompile()
278+
279+
compileFile("tests/init-global/special/tastySource/B.scala", tastSourceOptions.withClasspath(outDirLib))(group).checkWarnings()
280+
281+
lib.delete()
282+
}
269283
}
270284

271285
// initialization tests
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
object A:
2+
def foo(fn: => Int) = bar(fn)
3+
4+
def bar(fn: => Int) = fn
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
-- Warning: tests/init-global/special/tastySource/B.scala:4:14 ---------------------------------------------------------
2+
4 | def bar = C.n * 3 // warn
3+
| ^^^
4+
|Reading mutable state of object C during initialization of object B.
5+
|Reading mutable state of other static objects is forbidden as it breaks initialization-time irrelevance. Calling trace:
6+
|├── object B: [ B.scala:1 ]
7+
|│ ^
8+
|├── (no source) [ tastySource/A.scala:2 ]
9+
|├── (no source) [ tastySource/A.scala:4 ]
10+
|├── var y = A.foo(bar) * 2 [ B.scala:2 ]
11+
|│ ^^^
12+
|└── def bar = C.n * 3 // warn [ B.scala:4 ]
13+
| ^^^
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
object B:
2+
var y = A.foo(bar) * 2
3+
4+
def bar = C.n * 3 // warn
5+
6+
object C:
7+
var n = 10

0 commit comments

Comments
 (0)