代码之家  ›  专栏  ›  技术社区  ›  ilinum

从Scala宏注释获取参数

  •  12
  • ilinum  · 技术社区  · 10 年前

    所以我有一个关于函数(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")
      }
    }
    
    2 回复  |  直到 10 年前
        1
  •  11
  •   Federico Pellegatta    10 年前

    这个呢

    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")
      }
    }
    
        2
  •  7
  •   Ashesh Ajay Singh    5 年前

    如果您想使用具有可选命名参数的静态注释,这是一个显示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
    }
    

    还要注意,由于这些模式的工作方式,您必须在宏代码中显式地提供默认的参数值,这很不幸,但最终计算的类不可用。

    作为一个实验,我尝试通过调用 evalTree[scope.of.class.noop](c.prefix.tree) ,但Scala编译器抛出一个错误,因为它认为对注释宏代码中的注释的引用是非法的。