Очень простая аннотация макроса, поддерживаемая совместимостью с макросами.
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)
}
}
}
tpe
на ValDef
возвращается как null
, поэтому определения не типизированы, но мне нужна подпись типа параметров для достижения того, что я хочу. Как я могу получить сигнатуру типа параметров без ее взрыва?
По иронии судьбы, showCode(tpt)
создает строку правильного типа, так что это можно обойти с помощью TypeName(tpt.toString)
, но я не уверен, почему tpe
недоступен.