I'm using macro annotation to instrument code. How can I get the range position of some expressions ?
@ScalaKata object SHA {
val foo = "foo"
val bar = "bar"
foo; bar
// ...
}
// result: Map((75, 78) -> "foo", (80, 83) -> "bar")
The instrumenting macro:
package com.scalakata.eval
import scala.reflect.macros.blackbox.Context
import scala.language.experimental.macros
import scala.annotation.StaticAnnotation
object ScalaKataMacro {
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
val result: Tree = {
val eval = newTermName("eval$")
annottees.map(_.tree).toList match {
case q"object $name { ..$body }" :: Nil => {
val instr = newTermName("instr$")
implicit def lift = Liftable[c.universe.Position] { p =>
q"(${p.start}, ${p.end})"
}
def instrument(rhs: Tree): Tree = {
q"""
{
val t = $rhs
${instr}(${rhs.pos}) = t
t
}
"""
}
val bodyI = body.map {
case ident: Ident => instrument(ident)
case otherwise => otherwise
}
q"""
object $name {
val $instr = scala.collection.mutable.Map.empty[(Int, Int), Any]
def $eval() = {
..$bodyI
$instr
}
}
"""
}
}
}
c.Expr[Any](result)
}
}
class ScalaKata extends StaticAnnotation {
def macroTransform(annottees: Any*) = macro ScalaKataMacro.impl
}
I have range option enabled scalacOptions += "-Yrangepos"
I'm currently getting only the starting position: result: Map((75, 75) -> "foo", (80, 80) -> "bar")
There is a bug in paradise that corrupts range positions of macro arguments. It's now fixed in freshly published 2.1.0-SNAPSHOT.
However, there's also a regression in Scala 2.11.0 and 2.11.1 that also corrupts range positions. Therefore you will only be able to access range positions in macro annotations in 2.10.x or in the upcoming 2.11.2 (scheduled for the end of July).