当我调用我的scala宏时,使用“MatchError:AnyRef”来破坏编译器

时间:2014-01-18 23:25:50

标签: scala compiler-construction scala-macros

修改:我已修复此问题 - 我在.map(f => f.typeSignature.asInstanceOf[TypeRef].args.head)上错误地调用了recursiveOpt,这意味着field.name在我的copy中给了我错误的字段名称方法。我删除了map,现在一切正常。


我正在编写一个宏,它将为案例类创建更新方法的映射,例如

case class Inner(innerStr: String)
case class Outer(outerStr: String, inner: Inner, innerOpt: Option[Inner])

应为Outer生成类似

的更新地图
val innerMap = Map("innerStr" -> {(json: JsValue) => Try{(inner: Inner) => inner.copy(innerStr = json)}})

val outerMap = Map("outerStr" -> {(json: JsValue) => Try{(outer: Outer) => outer.copy(outerStr = json)}},
  "inner.innerStr" -> {(json: JsValue) => Try{(outer: Outer) => innerMap.get("innerStr").get(json).flatMap(update => outer.copy(inner = update(outer.inner)))},
  "innerOpt.innerStr" -> {(json: JsValue) => Try{(outer: Outer) => innerMap.get("innerStr").get(json).flatMap(update => outer.copy(inner = outer.inner.map(inner => update(inner))))})

然后会被称为

val oldOuter = Outer("str", Inner("str"), Some(Inner("str")))
val updatedOuter = outerMap.get("inner.innerStr").get(JsString("newStr")).get(oldOuter)

我的想法是,给定一个json kv对,我可以使用该键从映射中检索适当的更新方法,然后使用该值应用更新,使用隐式转换从json值转换为适当的类型。 / p>

我的宏适用于扁平案例类的情况,例如Inner(innerStr: String),以及嵌套的案例类,例如Outer(outerStr: String, inner: Inner)。但是,嵌套选项案例类Outer(outerStr: String, innerOpt: Option[Inner])的情况正在使编译器崩溃。我不确定我是否正在做一些灾难性的错误,或者编译器中是否存在错误,或者第三种选择。这是使用Scala 2.11.0-M7 REPL

完成的

下面是我的代码 - 我正在构建一个Map,它接受​​String输入而不是JsValue输入,这样我就不需要将play框架导入到我的REPL中。 blacklist过滤掉不应该在更新映射中的字段(例如,我们应用此字段的一个案例类具有“crypted_pa​​ssword”和“salt”等字段,永远不应通过json发送来更新一条REST路线)。 baseMethods构建密钥 - >扁平大小写的方法元组,recursiveMethods构造嵌套大小写的键方法元组,recursiveOptMethods构造嵌套选项大小写的键值元组;在宏的底部,这些都被合并为一个平面序列,并放置在Map中。

我已经在recursiveOptMethods quasiquotes中测试了代码,以确保我构建一个正确类型的元组序列并且没有发现错误(同样,这段代码与{{1}极为相似} quasiquotes,它运行正常),我已经测试了构造recursiveMethodsbaserecursive符号序列的代码,它们似乎正在完成它们的工作。

对于我为什么会崩溃编译器的任何帮助都将非常感激。


recursiveOpt

调用import scala.language.experimental.macros def copyTestImpl[T: c.WeakTypeTag](c: scala.reflect.macros.Context)(blacklist: c.Expr[String]*): c.Expr[Map[String, (String) => scala.util.Try[(T) => T]]] = { import c.universe._ val blacklistList: Seq[String] = blacklist.map(e => c.eval(c.Expr[String](c.resetAllAttrs(e.tree)))) def isCaseClass(tpe: Type): Boolean = tpe.typeSymbol.isClass && tpe.typeSymbol.asClass.isCaseClass def isCaseClassOpt(tpe: Type): Boolean = tpe.typeSymbol.name.decoded == "Option" && isCaseClass(tpe.asInstanceOf[TypeRef].args.head) def rec(tpe: Type): c.Expr[Map[String, (String) => scala.util.Try[(T) => T]]] = { val typeName = tpe.typeSymbol.name.decoded val fields = tpe.declarations.collectFirst { case m: MethodSymbol if m.isPrimaryConstructor => m }.get.paramss.head.filterNot(field => blacklistList.contains(typeName + "." + field.name.decoded)) val recursive = fields.filter(f => isCaseClass(f.typeSignature)) val recursiveOpt = fields.filter(f => isCaseClassOpt(f.typeSignature)) val base = fields.filterNot(f => isCaseClass(f.typeSignature) || isCaseClassOpt(f.typeSignature)) val recursiveMethods = recursive.map { field => { val fieldName = field.name val fieldNameDecoded = fieldName.decoded val map = rec(field.typeSignature) q"""{ val innerMap = $map innerMap.toSeq.map(tuple => ($fieldNameDecoded + "." + tuple._1) -> { (str: String) => { val innerUpdate = tuple._2(str) innerUpdate.map(innerUpdate => (outer: $tpe) => outer.copy($fieldName = innerUpdate(outer.$fieldName))) } })}""" }} val recursiveOptMethods = recursiveOpt.map { field => { val fieldName = field.name val fieldNameDecoded = fieldName.decoded val map = rec(field.typeSignature.asInstanceOf[TypeRef].args.head) q"""{ val innerMap = $map innerMap.toSeq.map(tuple => ($fieldNameDecoded + "." + tuple._1) -> { (str: String) => { val innerUpdate = tuple._2(str) innerUpdate.map(innerUpdate => (outer: $tpe) => outer.copy($fieldName = (outer.$fieldName).map(inner => innerUpdate(inner)))) } })}""" }} val baseMethods = base.map { field => { val fieldName = field.name val fieldNameDecoded = fieldName.decoded val fieldType = field.typeSignature val fieldTypeName = fieldType.toString q"""{ $fieldNameDecoded -> { (str: String) => scala.util.Try { val x: $fieldType = str (t: $tpe) => t.copy($fieldName = x) }.recoverWith { case e: Exception => scala.util.Failure(new IllegalArgumentException("Failed to parse " + str + " as " + $typeName + "." + $fieldNameDecoded + ": " + $fieldTypeName)) } }}""" }} c.Expr[Map[String, (String) => scala.util.Try[(T) => T]]] { q"""{ Map((List(..$recursiveMethods).flatten ++ List(..$recursiveOptMethods).flatten ++ List(..$baseMethods)):_*) }""" } } rec(weakTypeOf[T]) } def copyTest[T](blacklist: String*) = macro copyTestImpl[T] (其中copyTest[Outer]()Outer字段时)2.11.0-M7 REPL的错误的顶部和底部

Option[Inner]

scala> copyTest[Outer]()

scala.MatchError: AnyRef
    with Product
    with Serializable {
  val innerStr: String
  private[this] val innerStr: String
  def <init>(innerStr: String): Inner
  def copy(innerStr: String): Inner
  def copy$default$1: String @scala.annotation.unchecked.uncheckedVariance
  override def productPrefix: String
  def productArity: Int
  def productElement(x$1: Int): Any
  override def productIterator: Iterator[Any]
  def canEqual(x$1: Any): Boolean
  override def hashCode(): Int
  override def toString(): String
  override def equals(x$1: Any): Boolean
} (of class scala.reflect.internal.Types$ClassInfoType)
    at scala.reflect.internal.Variances$class.inType$1(Variances.scala:181)
    at scala.reflect.internal.Variances$$anonfun$inArgs$1$1.apply(Variances.scala:176)
    at scala.reflect.internal.Variances$$anonfun$inArgs$1$1.apply(Variances.scala:176)
    at scala.reflect.internal.util.Collections$class.map2(Collections.scala:55)
    at scala.reflect.internal.SymbolTable.map2(SymbolTable.scala:14)
    at scala.reflect.internal.Variances$class.inArgs$1(Variances.scala:176)
    at scala.reflect.internal.Variances$class.inType$1(Variances.scala:189)
    at scala.reflect.internal.Variances$$anonfun$inArgs$1$1.apply(Variances.scala:176)
    at scala.reflect.internal.Variances$$anonfun$inArgs$1$1.apply(Variances.scala:176)
    at scala.reflect.internal.util.Collections$class.map2(Collections.scala:55)
    at scala.reflect.internal.SymbolTable.map2(SymbolTable.scala:14)

1 个答案:

答案 0 :(得分:0)

我发现了问题 - 最初我有val recursiveOpt = fields.filter(f => isCaseClassOpt(f.typeSignature)).map(f => f.typeSignature.asInstanceOf[TypeRef].args.head),这意味着当我在field.name字段上调用recursiveOpt时,我的名字就错了。