首页 > 解决方案 > Scala宏如何使用参数默认值将MethodSymbol转换为DefDef?

问题描述

我一直在编写的宏通常需要处理来自库等中其他对象/模块的所有定义,因此树不可用。在宏内部,我经常需要将从另一个对象获得的 MethodSymbol 转换为我的目标 trait 中的一个新 API 调用,该 API 调用复制包含默认参数的函数签名。

但是,我还没有找到一种方法来获取 MethodSymbol 中参数的默认参数值,因此当我构建结果时,我无法按照我的意愿“复制”一些方法签名。

有没有办法获得默认参数,比如从符号“类型”的“成员”属性获得的参数,对于 MethodSymbol 中的参数?

标签: scalascala-macros

解决方案


您可以使用以下技术

import scala.language.experimental.macros
import scala.reflect.macros.blackbox

def foo[A]: Unit = macro impl[A]

def impl[A: c.WeakTypeTag](c: blackbox.Context): c.Tree = {
  import c.universe._

  // just for testing, in actual use case methodSymbol is obtained from somewhere 
  val methodSymbol = weakTypeOf[A].decl(TermName("smth")).asMethod

  val traverser = new Traverser {
    override def traverse(tree: Tree): Unit = {
      tree match {
        case t @ q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" if t.symbol == methodSymbol =>
          paramss.flatten.foreach {
            case q"$mods1 val $tname1: $tpt1 = $expr1" =>
              println(s"method $tname: default value of $tname1 is $expr1")
            case _ =>
          }
        case _ =>
      }
      super.traverse(tree)
    }
  }

  c.enclosingRun.units.foreach(unit =>
    traverser.traverse(unit.body)
  )

  q"()"
}

object App {

  class MyClass {
    def smth(x: Int = 1): Unit = ()
  }

  foo[MyClass] //Warning:scalac: method smth: default value of x is 1
}

推荐阅读