问题描述
我有以下宏,它正在为源类的给定访问器构造一些 Field 类:
case class Field[S,F](name: String,lens: Lens[S,F])
class Fields[S] {
def field[D](property: S => D): Field[S,D] = macro FieldsMacro.impl[S,D]
}
class FieldsMacro(val c: whiteBox.Context) {
import c.universe._
def impl[S: c.WeakTypeTag,D: c.WeakTypeTag](property: c.Expr[S => D]): c.Expr[Field[S,D]] = {
val sourceType = weakTypeOf[S]
val destinationType = weakTypeOf[D]
val field = sourceType.decls.collect {
case m: MethodSymbol if m.isCaseAccessor =>
val methodName = m.name.decodedname.toString
val q"($x) => $x2.$name" = property.tree
if (name.toString().equals(methodName)) {
Some(q"Field($methodName,Lens[${sourceType.typeSymbol},${destinationType.typeSymbol}]($property)(_ => x => x))")
} else None
}.filter(_.isDefined).map(_.get).head
val result =
q"""
//some imports
$field
"""
println(showCode(result))
c.Expr[Field[S,D]](result)
}
}
用法示例:
case class TestClass(i: Int,s: String,seq: Seq[Int])
object TestObject extends Fields[TestClass] {
val i = field[Int](_.i)
val seq = field[Seq[Int]](_.seq)
}
问题在于 weakTypeOf[D]
返回 Seq
而不是所需的 Seq[Int]
。所以我需要在像 Map[String,Int]
或 List[Option[SomeClass]]
这样的宏中传递复杂的完整类型,所以我可以用准引号替换它。
解决方法
我只需要删除 .typeSymbol
调用
class FieldsMacro(val c: whitebox.Context) {
import c.universe._
def impl[S: c.WeakTypeTag,D: c.WeakTypeTag](property: c.Expr[S => D]): c.Expr[Field[S,D]] = {
val sourceType = weakTypeOf[S]
val destinationType = weakTypeOf[D]
val field = sourceType.decls.collect {
case m: MethodSymbol if m.isCaseAccessor =>
val methodName = m.name.decodedName.toString
val q"($x) => $x2.$name" = property.tree
if (name.toString().equals(methodName)) {
Some(q"Field($methodName,Lens[${sourceType},${destinationType}]($property)(_ => x => x))")
} else None
}.filter(_.isDefined).map(_.get).head
val result =
q"""
import com.github.nikodemin.validation.Validators.Field
import com.github.nikodemin.validation.Fields
import monocle.Lens
$field
"""
println(showCode(result))
c.Expr[Field[S,D]](result)
}
}