Scala宏:从ValDef派生类型签名

Scala宏:从ValDef派生类型签名,scala,macros,Scala,Macros,一个非常简单的宏compat支持的宏注释 def impl(c: blackbox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = { import c.universe._ annottees.map(_.tree) match { case (classDef @ q"$mods class $tpname[..$tparams] $ctorMods(...$params) extends { ..$earlyd

一个非常简单的宏compat支持的宏注释

def impl(c: blackbox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
    import c.universe._

    annottees.map(_.tree) match {
      case (classDef @ q"$mods class $tpname[..$tparams] $ctorMods(...$params) extends { ..$earlydefns } with ..$parents { $self => ..$stats }")
        :: Nil if mods.hasFlag(Flag.CASE) =>
        val name = tpname.toTermName
        val typeName = tpname.toTypeName
        val res = q"""
         $classDef
         object $name {
           ..${doStuff(c)(typeName, name, params.head)}
         }
         """
        c.Expr[Any](res)

      case _ => c.abort(c.enclosingPosition, "Invalid annotation target, this must be a case class")
    }
  }
所以一切都很简单,很简单,很有趣。导致问题的位源于上面的
$params
,它只是
List[List[ValDef]]
,也就是说,不知何故,类型签名丢失了

def accessors(c: blackbox.Context)(
    params: Seq[c.universe.ValDef]
  ): Iterable[(c.universe.TermName, c.universe.TypeName)] = {
    import c.universe._

    params.map {
      case ValDef(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree) => {
        // tpt.tpe = kaboom, null pointer
        name -> TypeName(tpt.tpe.typeSymbol.fullName)
      }
    }
  }
ValDef
上的
tpe
返回为
null
,因此没有键入def,但我需要参数的类型签名来实现我想要的。我怎样才能在不爆炸的情况下得到params的类型签名


具有讽刺意味的是,
showCode(tpt)
确实生成了正确的类型字符串,因此这可以通过
TypeName(tpt.toString)
解决,但我不确定为什么
tpe
无法访问。

正确的方法是在
c.TypeMode
中使用
c.typepcheck
对类型参数求值,如下所示:

  /**
    * Retrieves the accessor fields on a case class and returns an iterable of tuples of the form Name -> Type.
    * For every single field in a case class, a reference to the string name and string type of the field are returned.
    *
    * Example:
    *
    * {{{
    *   case class Test(id: UUID, name: String, age: Int)
    *
    *   accessors(Test) = Iterable("id" -> "UUID", "name" -> "String", age: "Int")
    * }}}
    *
    * @param params The list of params retrieved from the case class.
    * @return An iterable of tuples where each tuple encodes the string name and string type of a field.
    */
  def accessors(
    params: Seq[ValDef]
  ): Iterable[Accessor] = {
    params.map {
      case ValDef(_, name: TermName, tpt: Tree, _) => {
        Accessor(
          name,
          c.typecheck(tq"$tpt", c.TYPEmode).tpe
        )
      }
    }
  }
在这种情况下,
Accessor
是一个自定义的
case类
,必须在
import c.universe的作用域内定义该类。
可用:

  case class Accessor(
    name: TermName,
    paramType: Type
  ) {
    def typeName: TypeName = symbol.name.toTypeName

    def symbol = paramType.typeSymbol
  }

您可能想看看Eugene Burmako在这个问题上的回答和评论: