1

I am playing with scalameta and I want to have a generic measurement annotation which sends measurements about how long the method execution took.

I used Qing Wei's cache annotation demo. https://www.cakesolutions.net/teamblogs/scalameta-tut-cache

It works for non async methods but my attribute doesn't match on methods which return Future due to the ExecutionContext argument list.

My annotation looks like this:

package measurements 

import scala.concurrent.Future
import scala.meta._

class measure(name: String) extends scala.annotation.StaticAnnotation {
  inline def apply(defn: Any): Any = meta {
    defn match {
      case defn: Defn.Def => {
        this match {
          case q"new $_($backendParam)" =>
            val body: Term = MeasureMacroImpl.expand(backendParam, defn)
            defn.copy(body = body)
          case x =>
            abort(s"Unrecognized pattern $x")
        }
      }
      case _ =>
        abort("This annotation only works on `def`")
    }
  }
}

object MeasureMacroImpl {

  def expand(nameExpr: Term.Arg, annotatedDef: Defn.Def): Term = {
    val name: Term.Name = Term.Name(nameExpr.syntax)
    annotatedDef match {
      case q"..$_ def $methodName[..$tps](..$nonCurriedParams): $rtType = $expr" => {
        rtType match {
          case f: Future[Any] => q"""
            val name = $name
            println("before " + name)
            val future: ${rtType} = ${expr}
            future.map(result => {
              println("after " + name)
              result
            })
           """
          case _ => q"""
            val name = $name
            println("before " + name)
            val result: ${rtType} = ${expr}
            println("after " + name)
            result
           """
        }
      }
      case _ => abort("This annotation only works on `def`")
    }
  }
}

I use the annotation like this:

@measure("A")
def test(x: String): String = x

@measure("B")
def testMultipleArg(x: Int, y: Int): Int = x + y

I would like to use it with async methods like this:

@measure("C")
def testAsync(x: String)(implicit ec: ExecutionContext) : Future[String] = {
 Future(test(x))
}

but I get the following error:

exception during macro expansion: 
scala.meta.internal.inline.AbortException: This annotation only works on `def`

I assume the issue is MeasureMacroImpl matching but I am not sure how to match on multiple argument groups. Could you guys help me? Any ideas or sample code would be greatly appreciated. I am pretty new to scala and scala meta so apologies if I asked a trivial question.

4

1 回答 1

2

You are getting error because MeasureMacroImpl does not match curried parameters.

It's fairly trivial to match curried params, simply use

scala case q"..$_ def $methodName[..$tps](...$nonCurriedParams): $rtType = $expr"

Notice the ...$nonCurriedParams instead of ..$nonCurriedParams

于 2017-11-03T12:07:37.110 回答