макрос аннотации, который переписывает и вводит черту, дженерики не обрабатываются правильно

Я пишу макрос, который должен создать класс, который переписывает черту, имея те же методы / аргументы черты, но другой тип возвращаемого значения.

Так скажем, мы получили:

trait MyTrait[T]
{
  def x(t1: T)(t2: T): T
}

@AnnProxy
class MyClass[T] extends MyTrait[T]

MyClass будет переписан на:

class MyClass[T] {
 def x(t1: T)(t2: T): R[T]
}

(поэтому x теперь будет возвращать R [T] вместо T)

Я написал макрос и отладил его, он производит этот код:

Expr[Any](class MyClass[T] extends scala.AnyRef {
   def <init>() = {
     super.<init>();
     ()
   };
   def x(t1: T)(t2: T): macrotests.R[T] = $qmark$qmark$qmark
 })
@AnnProxy

Как видите, подпись кажется нормальной. Но при попытке использовать макрос я получаю ошибку компиляции:

    val my = new MyClass[Int]
    my.x(5)(6)

Ошибка: (14, 7) несоответствие типов; найдено: Int (5) требуется: T x.x (5) (6) ^

Так что, похоже, метод T общего типа не совпадает с классом [T]. Есть идеи как починить?

Это мой макрос до сих пор. Я не очень хорошо разбираюсь в макросах (это помогло много помощи от stackoverflow), но это текущее состояние:

@compileTimeOnly("enable macro paradise to expand macro annotations")
class AnnProxy extends StaticAnnotation
{
    def macroTransform(annottees: Any*): Any = macro IdentityMacro.impl
}

trait R[T]

object IdentityMacro
{

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

def impl(c: whitebox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
    import c.universe._

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

    val classDef = annottees.map(_.tree).head.asInstanceOf[ClassDef]
    val clazz = c.typecheck(classDef).symbol.asClass
    val tparams = clazz.typeParams
    val baseClasses = clazz.baseClasses.tail.filter(clz => !SDKClasses(clz.fullName))
    val methods = baseClasses.flatMap {
        base =>
            base.info.decls.filter(d => d.isMethod && d.isPublic).map { decl =>
                val termName = decl.name.toTermName
                val method = decl.asMethod
                val params = method.paramLists.map(_.map {
                    s =>
                        val vd = internal.valDef(s)

                        val f = tparams.find(_.name == vd.tpt.symbol.name)
                        val sym = if (f.nonEmpty) f.get else vd.tpt.symbol

                        q"val ${vd.name} : $sym "
                })
                val paramVars = method.paramLists.flatMap(_.map(_.name))

                q""" def $termName (...$params)(timeout:scala.concurrent.duration.FiniteDuration) : macrotests.R[${method.returnType}] = {
            ???
           }"""
            }
    }

    val cde = c.Expr[Any] {
        q"""
      class ${classDef.name} [..${classDef.tparams}] {
        ..$methods
      }
  """
    }
    showInfo(show(cde))
    cde
}
}

РЕДАКТИРОВАТЬ: мне удалось обойти, построив класс в виде строки, а затем с помощью c.parse для его компиляции. Похоже на взлом, но это работает. Должен быть лучший способ, манипулируя деревом.

package macrotests

import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.whitebox

@compileTimeOnly("enable macro paradise to expand macro annotations")
class AnnProxy extends StaticAnnotation
{
    def macroTransform(annottees: Any*): Any = macro AnnProxyMacro.impl
}

trait R[T]

trait Remote[T]

object AnnProxyMacro
{

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

def impl(c: whitebox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
    import c.universe._

    val classDef = annottees.map(_.tree).head.asInstanceOf[ClassDef]
    val clazz = c.typecheck(classDef).symbol.asClass

    val baseClasses = clazz.baseClasses.tail.filter(clz => !SDKClasses(clz.fullName))
    val methods = baseClasses.flatMap {
        base =>
            base.info.decls.filter(d => d.isMethod && d.isPublic).map { decl =>
                val termName = decl.name.toTermName
                val method = decl.asMethod
                val params = method.paramLists.map(_.map {
                    s =>
                        val vd = internal.valDef(s)
                        val tq = vd.tpt
                        s"${vd.name} : $tq"
                })
                val paramVars = method.paramLists.flatMap(_.map(_.name))
                val paramVarsArray = paramVars.mkString("Array(", ",", ")")


                val paramsStr = params.map(_.mkString("(", ",", ")")).mkString(" ")
                val retTpe = method.returnType.typeArgs.mkString("-unexpected-")
                s""" def $termName $paramsStr (timeout:scala.concurrent.duration.FiniteDuration) : macrotests.Remote[$retTpe] = {
      println($paramVarsArray.toList)
            new macrotests.Remote[$retTpe] {}
           }"""
            }
    }

    val tparams = clazz.typeParams.map(_.name)
    val tparamsStr = if (tparams.isEmpty) "" else tparams.mkString("[", ",", "]")
    val code =
        s"""
           |class ${classDef.name}$tparamsStr (x:Int) {
           |${methods.mkString("\n")}
           |}
         """.stripMargin
    //      print(code)
    val cde = c.Expr[Any](c.parse(code))
    cde
}
}

Ответы на вопрос(1)

Ваш ответ на вопрос