Skip to content

Range Position of var x: T = _ does not include = _ #12213

@retronym

Description

@retronym

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 = _

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions