Это ответ, который показывает вариант метода Федерико, если вы хотите использовать статическую аннотацию с необязательными именованными аргументами. В этом случае вам необходимо рассмотреть возможные выражения вызова в операторе сопоставления case. Необязательный аргумент может быть явно назван, он может быть задан без имени или может отсутствовать. Каждый из них отображается во время компиляции как отдельный шаблон в c.prefix.tree
, как показано ниже.
@compileTimeOnly("Must enable the Scala macro paradise compiler plugin to expand static annotations")
class noop(arg1: Int, arg2: Int = 0) extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro AnnotationMacros.noop
}
class AnnotationMacros(val c: whitebox.Context) {
import c.universe._
// an annotation that doesn't do anything:
def noop(annottees: c.Expr[Any]*): c.Expr[Any] = {
// cases for handling optional arguments
val (arg1q, arg2q) = c.prefix.tree match {
case q"new noop($arg1, arg2 = $arg2)" => (arg1, arg2) // user gave named arg2
case q"new noop($arg1, $arg2)" => (arg1, arg2) // arg2 without name
case q"new noop($arg1)" => (arg1, q"0") // arg2 defaulted
case _ => c.abort(c.enclosingPosition, "unexpected annotation pattern!")
}
// print out the values
println(s"arg1= ${evalTree[Int](arg1q)} arg2= ${evalTree[Int](arg2q)}")
// just return the original annotee:
annottees.length match {
case 1 => c.Expr(q"{ ${annottees(0)} }")
case _ => c.abort(c.enclosingPosition, "Only one annottee!")
}
}
def evalTree[T](tree: Tree) = c.eval(c.Expr[T](c.untypecheck(tree.duplicate)))
}
Вот пример вызова с именем arg2
, поэтому он будет соответствовать первому шаблону — case q"new noop($arg1, arg2 = $arg2)"
— выше:
object demo {
// I will match this pattern: case q"new noop($arg1, arg2 = $arg2)"
@noop(1, arg2 = 2)
trait someDeclarationToAnnotate
}
Также обратите внимание, что из-за того, как работают эти шаблоны, вы должны явно указать значение аргумента по умолчанию внутри кода макроса, что, к сожалению, немного хакерское, но окончательный оцененный класс вам недоступен.
В качестве эксперимента я попытался на самом деле создать класс, вызвав evalTree[scope.of.class.noop](c.prefix.tree)
, но компилятор Scala выдает ошибку, поскольку считает, что ссылка на аннотацию находится внутри кода макроса аннотации, что является незаконным.
person
eje
schedule
22.03.2017