2016-09-14 59 views
1

非常简单的宏compat支持的宏注释。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) 
     } 
    } 
    } 

ValDeftpe回来为null,所以DEFS不会打字,但我需要的则params的类型签名,以达到我想要的。如何在没有炸毁的情况下获得参数的类型签名?

具有讽刺意味的是,showCode(tpt)不会产生正确的字符串,所以这可以解决TypeName(tpt.toString),但我不知道为什么tpe不可访问。

+0

你可能想看看在这个问题的答案和尤金Burmako的评论:http://stackoverflow.com/questions/23671379/proper-way-to-pattern-match-the- VALU电子对的一typetree-从-A-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 
    }