-
Notifications
You must be signed in to change notification settings - Fork 22
Closed
Description
reproduction steps
➜ ~ scala -Yrangepos
Welcome to Scala 2.13.3 (OpenJDK 64-Bit Server VM, Java 14.0.1).
Type in expressions for evaluation. Or try :help.
scala> :power
Power mode enabled. :phase is at typer.
import scala.tools.nsc._, intp.global._, definitions._
Try :help or completions for vals._ and power._
scala> object Other { var x: Int = _; var y: Int = 42 }
object Other
scala> val trees = lastRequest.trees
val trees: List[$r.intp.global.Tree] =
List(<stable> object Other extends scala.AnyRef {
def <init>(): Other.type = {
super.<init>();
()
};
private[this] var x: Int = _;
private[this] var y: Int = 42
})
scala> val List(x, y) = trees.flatMap(_.collect { case vd: ValDef => vd } ).takeRight(2)
val x: $r.intp.global.ValDef = private[this] var x: Int = _
val y: $r.intp.global.ValDef = private[this] var y: Int = 42
scala> y.pos.source.content(y.pos.end)
val res0: Char =
scala> y.pos.source.content(y.pos.end - 1)
val res1: Char = 2
scala> y.pos.source.content.slice(y.pos.start, y.pos.end)
val res2: Array[Char] = Array(v, a, r, , y, :, , I, n, t, , =, , 4, 2)
scala> new String(y.pos.source.content(y.pos.start, y.pos.end))
^
error: too many arguments (found 2, expected 1) for method apply: (i: Int): Char in class Array
scala> new String(y.pos.source.content.slice(y.pos.start, y.pos.end)
|
| )
|
val res4: String = var y: Int = 42
scala> new String(y.pos.source.content.slice(y.pos.start, y.pos.end))
val res5: String = var y: Int = 42
scala> new String(x.pos.source.content.slice(y.pos.start, y.pos.end))
val res6: String = var y: Int = 42
scala> new String(x.pos.source.content.slice(x.pos.start, x.pos.end))
val res7: String = var x: Int
scala> // BUG: Parser doesn't include `= _` in the range position of the ValDef!
Workaround
An tool with access to the compiler API can detect that a) ValDef.rhs is EmptyTree and know that the range position is incomplete and b) re-tokenize the source file starting at the = to find the EQUAL and USCORE tokens.
scala> val scanner = newUnitScanner(new CompilationUnit(x.pos.source))
val scanner: $r.intp.global.syntaxAnalyzer.UnitScanner = '<-3>'
scala> scanner.ch = ' '; scanner.lastOffset = x.pos.end; scanner.offset = scanner.lastOffset + 1; scanner.charOffset = scanner.offset
// mutated scanner.ch
// mutated scanner.lastOffset
// mutated scanner.offset
// mutated scanner.charOffset
scala> scanner.nextToken(); val token = scanner.token
val token: $r.intp.global.syntaxAnalyzer.Token = 124
scala> Predef.assert(token == scala.tools.nsc.ast.parser.Tokens.EQUALS)
scala> scanner.nextToken(); val token = scanner.token
val token: $r.intp.global.syntaxAnalyzer.Token = 131
scala> Predef.assert(token == scala.tools.nsc.ast.parser.Tokens.USCORE)
scala> val realEnd = scanner.offset
val realEnd: $r.intp.global.syntaxAnalyzer.Offset = 28
scala> new String(x.pos.source.content.slice(x.pos.start, realEnd + 1))
val res11: String = var x: Int = _
scala> scanner.lastOffset
val res13: $r.intp.global.syntaxAnalyzer.Offset = 27
scala> scanner.offset
val res14: $r.intp.global.syntaxAnalyzer.Offset = 28
scala> new String(x.pos.source.content.slice(scanner.lastOffset + 1, scanner.offset + 1))
val res18: String = _
Reactions are currently unavailable