Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's up with position on Scala macros?

Tags:

macros

scala

I'm trying to get the original input string of a parameter to a macro, but the returned position seems a bit off. Consider this macro, for example:

object M {
  import scala.reflect.macros.Context
  import language.experimental.macros
  def f[T](v: => T) = macro fImpl[T]
  def fImpl[T : c.WeakTypeTag](c: Context)(v: c.Expr[T]): c.Expr[Unit] = {
    import c.universe._
    val pos = v.tree.pos
    println(pos.lineContent)
    println(" " * pos.column + "^")
    println(" " * pos.point + "^")
    c.literalUnit 
  }
}

When I try it with this file:

object N extends App {
  val x = 1
  val y = 2
  println(M.f(x + y))
}

I get this output:

  println(M.f(x + y))
                 ^
                                                                  ^

which doesn't make sense to me. I'd expect it to point to x, or be off by one. What's up with that?

like image 554
Daniel C. Sobral Avatar asked Oct 21 '22 01:10

Daniel C. Sobral


1 Answers

It's an off-by-one bug in the sense that Position.column and Position.line are 1-based.

It's a documentation bug in the sense that they bothered to document the API but didn't bother to mention that.

You can compile with -Yrangepos and:

val n = pos.column - (pos.point - pos.startOrPoint) - 1
println(" " * n + "^")

or similar to indicate the earliest position in the tree.

println(M.f(x + y))
            ^

Update:

Letting the macro return the expression it's given, and compiling with -Xprint:typer -Yshow-trees, the tree is the inner Apply node, which is positioned at the +:

      Apply( // def println(x: Any): Unit in object Predef, tree.tpe=Unit
        scala.this."Predef"."println" // def println(x: Any): Unit in object Predef, tree.tpe=(x: Any)Unit
        Apply( // def +(x: Int): Int in class Int, tree.tpe=Int
          "x"."$plus" // def +(x: Int): Int in class Int, tree.tpe=(x: Int)Int
          "y" // val y: Int, tree.tpe=Int
        )
      )

With a "range" position, the position of the top of the tree includes everything beneath it. So while point is where the + is, the start of a range position is the earliest position of everything enclosed by the range position, i.e., everything lower in the tree. In this case, the left leaf is the x.

So the difference point - start tells you how far to back up.

(I haven't considered what if the offsets into the source file are different from column offsets because of differences in character encoding.)

like image 138
som-snytt Avatar answered Oct 23 '22 16:10

som-snytt