从 Scala 宏注解获取参数
Getting Parameters from Scala Macro Annotation
所以我在函数 (DefDef) 上有一个注释。这个注解有参数。
但是,我对如何从构造函数获取参数感到困惑。
用法示例:
class TestMacro {
@Foo(true)
def foo(): String = ""
foo
}
注释代码如下:
class Foo(b: Boolean) extends StaticAnnotation {
def macroTransform(annottees: Any*) = macro Foo.impl
}
object Foo {
def impl(c: whitebox.Context)(annottees: c.Tree*): c.Expr[Any] = {
import c.universe._
//how do I get value of `b` here???
c.abort(c.enclosingPosition, "message")
}
}
这个怎么样:
val b: Boolean = c.prefix.tree match {
case q"new Foo($b)" => c.eval[Boolean](c.Expr(b))
}
为了完整起见,这是完整的来源:
import scala.reflect.macros.Context
import scala.language.experimental.macros
import scala.annotation.StaticAnnotation
import scala.annotation.compileTimeOnly
import scala.reflect.api.Trees
import scala.reflect.runtime.universe._
class Foo(b: Boolean) extends StaticAnnotation {
def macroTransform(annottees: Any*) :Any = macro FooMacro.impl
}
object FooMacro {
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
val b: Boolean = c.prefix.tree match {
case q"new Foo($b)" => c.eval[Boolean](c.Expr(b))
}
c.abort(c.enclosingPosition, "message")
}
}
如果您想使用具有可选命名参数的静态注释,这是一个显示 Federico 技术变体的答案。在这种情况下,您需要考虑 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
}
另请注意,由于这些模式的工作方式,您必须在宏代码中明确提供默认参数值,不幸的是,这有点 hacky,但最终评估的 class 不可用于你.
作为实验,我尝试通过调用 evalTree[scope.of.class.noop](c.prefix.tree)
实际创建 class,但 Scala 编译器抛出错误,因为它认为在注释宏代码中引用了注释,这是非法的。
所以我在函数 (DefDef) 上有一个注释。这个注解有参数。 但是,我对如何从构造函数获取参数感到困惑。
用法示例:
class TestMacro {
@Foo(true)
def foo(): String = ""
foo
}
注释代码如下:
class Foo(b: Boolean) extends StaticAnnotation {
def macroTransform(annottees: Any*) = macro Foo.impl
}
object Foo {
def impl(c: whitebox.Context)(annottees: c.Tree*): c.Expr[Any] = {
import c.universe._
//how do I get value of `b` here???
c.abort(c.enclosingPosition, "message")
}
}
这个怎么样:
val b: Boolean = c.prefix.tree match {
case q"new Foo($b)" => c.eval[Boolean](c.Expr(b))
}
为了完整起见,这是完整的来源:
import scala.reflect.macros.Context
import scala.language.experimental.macros
import scala.annotation.StaticAnnotation
import scala.annotation.compileTimeOnly
import scala.reflect.api.Trees
import scala.reflect.runtime.universe._
class Foo(b: Boolean) extends StaticAnnotation {
def macroTransform(annottees: Any*) :Any = macro FooMacro.impl
}
object FooMacro {
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
val b: Boolean = c.prefix.tree match {
case q"new Foo($b)" => c.eval[Boolean](c.Expr(b))
}
c.abort(c.enclosingPosition, "message")
}
}
如果您想使用具有可选命名参数的静态注释,这是一个显示 Federico 技术变体的答案。在这种情况下,您需要考虑 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
}
另请注意,由于这些模式的工作方式,您必须在宏代码中明确提供默认参数值,不幸的是,这有点 hacky,但最终评估的 class 不可用于你.
作为实验,我尝试通过调用 evalTree[scope.of.class.noop](c.prefix.tree)
实际创建 class,但 Scala 编译器抛出错误,因为它认为在注释宏代码中引用了注释,这是非法的。