Scala宏:从ValDef派生类型签名
一个非常简单的宏compat支持的宏注释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
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在这个问题上的回答和评论: