2016-09-14 3 views
1

Очень простая макрокоманда макросов, поддерживаемая макросом.Макросы Scala: подпись типа типа от ValDef

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, поэтому DEFS не наберется, но мне нужен тип подписи Params, чтобы достичь того, чего я хочу. Как я могу получить подпись типа params без его взрыва?

Как ни странно, showCode(tpt) действительно производит строку правильного типа, так что это можно обойти с TypeName(tpt.toString), но я не знаю, почему tpe не доступен.

+0

Вы можете взглянуть на ответ и комментарии Евгения Burmako в этом вопросе: http://stackoverflow.com/questions/23671379/proper-way-to-pattern-match-the- Valu е-оф-а-typetree-с-а-valdef-в-макро-лестницы –

ответ

0

правильный способ сделать это, чтобы оценить аргументы типа с использованием c.typepcheck в c.TypeMode следующим образом:

/** 
    * 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 class, который должен быть определен внутри сферы, где import c.universe._ доступен :

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

    def symbol = paramType.typeSymbol 
    } 
Смежные вопросы