1

我有以下宏,它正在为源类的给定访问器构造一些 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]or List[Option[SomeClass]],所以我可以用准引号替换它。

4

1 回答 1

0

我只需要删除.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)
  }
}
于 2021-01-28T13:23:35.853 回答