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?
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.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With