3

我尝试按照文档中的描述实现 Scala 宏注释示例。我设法在使用它们的实际项目之前编译宏注释,即@compileTimeOnly("enable macro paradise to expand macro annotations")没有被触发意味着宏注释是在使用之前编译的。到目前为止,一切都很好。

但是,当我在实际项目中注释某些值时,如下所示:

@identity val foo: Double = 1.1
@identity val bar: String = "bar"

然后我希望在运行主项目时会发生以下打印(通过之前链接的宏注释示例):

(<empty>,List(val foo: Double = 1.1))
(<empty>,List(val bar: String = "bar"))

这是我感到困惑的地方,当我运行主项目时不会发生打印。但是,在将主项目编译为警告时,它确实会出现一瞬间?

(我正在使用 IntelliJ IDEA 和 Scala 2.12.8)

4

1 回答 1

3

我设法在使用它们的实际项目之前编译宏注释,即 @compileTimeOnly("enable macro Paradise to expand macro annotations") 没有被触发,这意味着宏注释在使用之前被编译

不,@compileTimeOnly被触发意味着注释在使用它的代码被编译后存在。所以它没有被触发意味着宏已经在编译期间执行。因为println它在宏的主体中,而不是在转换后的代码中,所以你会看到输出。

如果要在运行项目时进行打印,则需要修改包含转换后代码的返回值,即示例中的最后两行:

val outputs = expandees
c.Expr[Any](Block(outputs, Literal(Constant(()))))

使用quasiquotes直接操作 ASTs

未经测试,但使用类似这样的准引号应该可以工作

object identityMacro {
  def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
    import c.universe._
    val inputs = annottees.map(_.tree).toList
    val (annottee, expandees) = inputs match {
      case (param: ValDef) :: (rest @ (_ :: _)) => (param, rest)
      case (param: TypeDef) :: (rest @ (_ :: _)) => (param, rest)
      case _ => (EmptyTree, inputs)
    }
    val stringToPrint = (annottee, expandees).toString
    c.Expr[Any](q"""
    println($stringToPrint)
    $expandees
    ()
    """)
  }
}
于 2019-04-03T10:48:09.883 回答