annotation macro that rewrites and impls a trait, generics not processed correctly

旧时模样 提交于 2019-12-02 10:04:57

the code is very long , you can look at the github: https://github.com/1178615156/scala-macro-example/blob/master/stackoverflow/src/main/scala/so/AnnotationWithTrait.scala

import scala.annotation.StaticAnnotation
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context

/**
  * Created by yu jie shui on 2015/12/2.
  */

class AnnotationWithTrait extends StaticAnnotation {
  def macroTransform(annottees: Any*): Any = macro AnnotationWithTraitImpl.apply

}

class AnnotationWithTraitImpl(val c: Context) {

  import c.universe._

  val SDKClasses = Set("java.lang.Object", "scala.Any")

  def showInfo(s: String) = c.info(c.enclosingPosition, s.split("\n").mkString("\n |---macro info---\n |", "\n |", ""), true)

  def apply(annottees: c.Expr[Any]*) = {

    val classDef = annottees.map(_.tree).head.asInstanceOf[ClassDef]

    val superClassSymbol= c.typecheck(classDef).symbol.asClass.baseClasses.tail
      .filterNot(e => SDKClasses.contains(e.fullName)).reverse

    val superClassTree= classDef match {
      case q"$mod class $name[..$t](..$params) extends ..$superClass { ..$body }" =>
        (superClass: List[Tree]).filterNot(e =>
          typeOf[Object].members.exists(_.name == e.children.head.toString())
        )
    }

    showInfo(show(superClassSymbol))
    showInfo(show(superClassTree))

    val impl = q"private[this] object ${TermName("impl")} extends ..${superClassTree}"
    //

    //get super class all can call method
    val methods = superClassSymbol.map(_.info.members
      .filterNot(_.isConstructor)
      .filterNot(e => typeOf[Object].members.exists(_.name == e.name)).map(_.asMethod)).toList

    case class ReplaceTypeParams(from: String, to: String)
    type ClassReplace = List[ReplaceTypeParams]

    //trait a[A]
    //class b[B] extends a[B]
    //need replace type params A to B
    val classReplaceList: List[ClassReplace] = superClassTree zip superClassSymbol map {
      case (superClassTree, superClassSymbol) =>
        superClassSymbol.asClass.typeParams.map(_.name) zip superClassTree.children.tail map
          (e => ReplaceTypeParams(e._1.toString, e._2.toString()))
    }

    val out = classReplaceList zip methods map {
      case (classReplace, func) =>

        func map { e => {

          val funcName = e.name

          val funcTypeParams = e.typeParams.map(_.name.toString).map(name => {
            TypeDef(Modifiers(Flag.PARAM), TypeName(name), List(), TypeBoundsTree(EmptyTree, EmptyTree))
          })

          val funcParams = e.paramLists.map(_.map(e => q"${e.name.toTermName}:${
            TypeName(
              classReplace.find(_.from == e.info.toString).map(_.to).getOrElse(e.info.toString)
            )} "))

          val funcResultType = TypeName(
            classReplace.find(_.from == e.returnType.toString).map(_.to).getOrElse(e.info.toString)
          )
          q"""
           def ${funcName}[..${funcTypeParams}](...$funcParams):${funcResultType}=
              impl.${funcName}[..${funcTypeParams}](...$funcParams)
            """
        }
        }

    }

    showInfo(show(out))

    q"""
       class ${classDef.name}[..${classDef.tparams}]{
        $impl
        ..${out.flatten}
       }
      """
  }
}

test

trait MyTrait[MT1] {

  def x(t1: MT1)(t2: MT1): MT1 = t1

}

trait MyTrait2[MT2] {
  def t(t2: MT2): MT2 = t2
}


@AnnotationWithTrait
class MyClass[MCT1, MCT2] extends MyTrait[MCT1] with MyTrait2[MCT2]

object AnnotationWithTraitUsing extends App {
  assert(new MyClass[Int, String].x(1)(2) == 1)
  assert(new MyClass[Int, String].t("aaa") == "aaa")
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!