-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Loading status checks…
backend computes line number from source of position (#21763)
fixes #21762 This makes it possible to implement line number correction for Mill build files under Scala 3
Showing
10 changed files
with
138 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
package framework | ||
|
||
class entrypoint extends scala.annotation.Annotation |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package scriptWrapper | ||
|
||
import dotty.tools.dotc.* | ||
import core.* | ||
import Contexts.Context | ||
import Contexts.ctx | ||
import plugins.* | ||
import ast.tpd | ||
import util.SourceFile | ||
|
||
class LineNumberPlugin extends StandardPlugin { | ||
val name: String = "linenumbers" | ||
val description: String = "adjusts line numbers of script files" | ||
|
||
override def initialize(options: List[String])(using Context): List[PluginPhase] = FixLineNumbers() :: Nil | ||
} | ||
|
||
// Loosely follows Mill linenumbers plugin (scan for marker with "original" source, adjust line numbers to match) | ||
class FixLineNumbers extends PluginPhase { | ||
|
||
val codeMarker = "//USER_CODE_HERE" | ||
|
||
def phaseName: String = "fixLineNumbers" | ||
override def runsAfter: Set[String] = Set("posttyper") | ||
override def runsBefore: Set[String] = Set("pickler") | ||
|
||
override def transformUnit(tree: tpd.Tree)(using Context): tpd.Tree = { | ||
val sourceContent = ctx.source.content() | ||
val lines = new String(sourceContent).linesWithSeparators.toVector | ||
val codeMarkerLine = lines.indexWhere(_.startsWith(codeMarker)) | ||
|
||
if codeMarkerLine < 0 then | ||
tree | ||
else | ||
val adjustedFile = lines.collectFirst { | ||
case s"//USER_SRC_FILE:./$file" => file.trim | ||
}.getOrElse("<unknown>") | ||
|
||
val adjustedSrc = ctx.source.file.container.lookupName(adjustedFile, directory = false) match | ||
case null => | ||
report.error(s"could not find file $adjustedFile", tree.sourcePos) | ||
return tree | ||
case file => | ||
SourceFile(file, scala.io.Codec.UTF8) | ||
|
||
val userCodeOffset = ctx.source.lineToOffset(codeMarkerLine + 1) // lines.take(codeMarkerLine).map(_.length).sum | ||
val lineMapper = LineMapper(codeMarkerLine, userCodeOffset, adjustedSrc) | ||
lineMapper.transform(tree) | ||
} | ||
|
||
} | ||
|
||
class LineMapper(markerLine: Int, userCodeOffset: Int, adjustedSrc: SourceFile) extends tpd.TreeMapWithPreciseStatContexts() { | ||
|
||
override def transform(tree: tpd.Tree)(using Context): tpd.Tree = { | ||
val tree0 = super.transform(tree) | ||
val pos = tree0.sourcePos | ||
if pos.exists && pos.start >= userCodeOffset then | ||
val tree1 = tree0.cloneIn(adjustedSrc).withSpan(pos.span.shift(-userCodeOffset)) | ||
// if tree1.show.toString == "???" then | ||
// val pos1 = tree1.sourcePos | ||
// sys.error(s"rewrote ??? at ${pos1.source}:${pos1.line + 1}:${pos1.column + 1} (sourced from ${markerLine + 2})") | ||
tree1 | ||
else | ||
tree0 | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
@main def Test: Unit = { | ||
val mainCls = Class.forName("foo_sc") | ||
val mainMethod = mainCls.getMethod("main", classOf[Array[String]]) | ||
val stackTrace: Array[String] = { | ||
try | ||
mainMethod.invoke(null, Array.empty[String]) | ||
sys.error("Expected an exception") | ||
catch | ||
case e: java.lang.reflect.InvocationTargetException => | ||
val cause = e.getCause | ||
if cause != null then | ||
cause.getStackTrace.map(_.toString) | ||
else | ||
throw e | ||
} | ||
|
||
val expected = Set( | ||
"foo_sc$.getRandom(foo_2.scala:3)", // adjusted line number (11 -> 3) | ||
"foo_sc$.brokenRandom(foo_2.scala:5)", // adjusted line number (13 -> 5) | ||
"foo_sc$.run(foo_2.scala:8)", // adjusted line number (16 -> 8) | ||
) | ||
|
||
val missing = expected -- stackTrace | ||
assert(missing.isEmpty, s"Missing: $missing") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// generated code | ||
// script: foo.sc | ||
object foo_sc { | ||
def main(args: Array[String]): Unit = { | ||
run // assume some macro generates this by scanning for @entrypoint | ||
} | ||
//USER_SRC_FILE:./foo_original_2.scala | ||
//USER_CODE_HERE | ||
import framework.* | ||
|
||
def getRandom: Int = brokenRandom // LINE 3; | ||
|
||
def brokenRandom: Int = ??? // LINE 5; | ||
|
||
@entrypoint | ||
def run = println("Hello, here is a random number: " + getRandom) // LINE 8; | ||
//END_USER_CODE_HERE | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import framework.* | ||
|
||
def getRandom: Int = brokenRandom // LINE 3; | ||
|
||
def brokenRandom: Int = ??? // LINE 5; | ||
|
||
@entrypoint | ||
def run = println("Hello, here is a random number: " + getRandom) // LINE 8; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
pluginClass=scriptWrapper.LineNumberPlugin |