在使用宏转换树之后重新建立类型一致性的最佳方法是什么

jed*_*sah 5 types scala metaprogramming scala-macros

我有以下宏:

def testMacro[T](x: T): Option[T] = macro testMacroImpl[T]

def testMacroImpl[T: c.WeakTypeTag](c: Context)(x: c.Expr[T]): c.Expr[Option[T]] = {
    import c.universe._
    val newTree = x.tree match {
      case Block(List(first), second) => {
        val newFirst = first match {
          case ValDef(mods, name, _, rhs) => ValDef(mods, name, EmptyTree, q"Some($rhs)")
        }
        Block(List(newFirst), second)
      }
    }
    c.Expr[Option[T]](newTree)
  }
Run Code Online (Sandbox Code Playgroud)

基本上,这应该改变这个:

testMacro {
    val aa = "hello"
    aa
}
Run Code Online (Sandbox Code Playgroud)

{
    val aa = Some("hello")
    aa
}
Run Code Online (Sandbox Code Playgroud)

但是,它失败并出现以下错误:

[error]  found   : String
[error]  required: Option[String]
[error]     val f = IdiomBracket.testMacro{
[error]   
                                          ^
Run Code Online (Sandbox Code Playgroud)

我的调查显示这是因为它接收到标识符aa具有类型的类型化树String.由于代码转换,它现在是类型,Option[String]但标识符的类型尚未更新.我尝试创建一个新的(无类型)标识符,这只会产生错误:

[error]  found   : <notype>
[error]  required: Option[String]
[error]     val f = IdiomBracket.testMacro{
[error]                                   ^
Run Code Online (Sandbox Code Playgroud)

我已经尝试在发送之前对树进行类型检查,希望这样可以填写正确的类型,但这似乎没有任何效果.

作为参考,这里是创建一个新的Ident并执行类型检查的相同的宏,但遗憾的是它仍然不起作用.

def testMacroImpl[T: c.WeakTypeTag](c: Context)(x: c.Expr[T]): c.Expr[Option[T]] = {
    import c.universe._
    val newTree = x.tree match {
      case Block(List(first), second) => {
        val newFirst = first match {
          case ValDef(mods, name, _, rhs) => ValDef(mods, name, EmptyTree, q"Some($rhs)")
        }
        val newSecond = second match {
          case ident: Ident => Ident(ident.name)
        }
        Block(List(newFirst), newSecond)
      }
    }
    c.Expr[Option[T]](c.typecheck(newTree))
  }
Run Code Online (Sandbox Code Playgroud)

jed*_*sah 4

这行:

case ValDef(mods, name, _, rhs) => ValDef(mods, name, EmptyTree, q"Some($rhs)")
Run Code Online (Sandbox Code Playgroud)

需要更改为这一行:

case ValDef(mods, name, _, rhs) => ValDef(mods, name, TypeTree(), q"Some($rhs)")
Run Code Online (Sandbox Code Playgroud)

将 EmptyTree 作为 ValDef 中的类型可能永远没有意义。可惜编译器不能告诉我这一点,这样我就可以节省 48 小时的兼职工作。