2
我想爲向伴隨對象添加方法的案例類編寫一個簡單的宏註釋。問題在於新方法必須考慮帶註釋的案例類的類型參數。斯卡拉宏註釋 - 具有類型參數的案例類
下面是我需要傳遞
package my.macros
import org.scalatest._
class DefaultApplyTest extends FlatSpec with Matchers {
@defaultApply case class Generic[A, B](a: A, b: B)
it should "define defaultApply method in companion object" in {
assert(Generic.defaultApply("str", 1) == Generic("str", 1))
}
}
這裏是我寫我的理解是,當我嘗試lift的做到這一點
package my.macros
import scala.reflect.macros._
import scala.language.experimental.macros
import scala.annotation.StaticAnnotation
class defaultApply extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro DefaultApply.impl
}
object DefaultApply {
def impl(c: blackbox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
def defaultApplyCompanion(classDecl: ClassDef) = {
val (name, typeParams, valueParams) = try {
val q"case class ${name: TypeName}[..${typeParams: Seq[TypeDef]}](..${valueParams: Seq[ValDef]}) extends ..$bases { ..$body }" = classDecl
(name, typeParams, valueParams)
} catch {
case e: MatchError =>
c.warning(c.enclosingPosition, e.toString)
c.abort(c.enclosingPosition, "Annotation is only supported on case class")
}
val applyDef = q"""${name.toTermName}.apply[..$typeParams]"""
c.warning(c.enclosingPosition, showRaw(applyDef))
q"""
object ${name.toTermName} {
def defaultApply: (..${valueParams.map(_.tpt)}) => $name[..$typeParams] = $applyDef
}
"""
}
def modifiedDeclaration(classDecl: ClassDef) = {
val compDecl = defaultApplyCompanion(classDecl)
c.Expr(q"""
$classDecl
$compDecl
""")
}
annottees.map(_.tree) match {
case (classDecl: ClassDef) :: Nil => modifiedDeclaration(classDecl)
case _ => c.abort(c.enclosingPosition, "Invalid annottee")
}
}
}
該問題的代碼測試類型參數列表到結果語法樹中,它們不會被識別爲與原始樹相同的類型參數。
那麼我專注於是,宏觀
val applyDef = q"""${name.toTermName}.apply[..$typeParams]"""
c.warning(c.enclosingPosition, showRaw(applyDef))
這部分原料語法樹的發射是由於
TypeApply(Select(Ident(TermName("Generic")), TermName("apply")), List(TypeDef(Modifiers(PARAM), TypeName("A"), List(), TypeBoundsTree(EmptyTree, EmptyTree)), TypeDef(Modifiers(PARAM), TypeName("B"), List(), TypeBoundsTree(EmptyTree, EmptyTree))))
但編譯器並不滿意這點
type arguments [<notype>,<notype>] do not conform to method apply's type parameter bounds [A,B]
最終用例用於生成觸及的可緩存類型類的實例1k行代碼。非參數化版本已經有效,這只是錦上添花。 scalac有一些我不明白的東西,但願意。你花時間閱讀這是非常感謝。
我使用Scala的2.11.8與macro paradise 2.1.0
就是這樣。謝謝! – dbaumann