How to get range position from macro annotation?

214 Views Asked by At

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")

1

There are 1 best solutions below

4
On BEST ANSWER

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).