Ошибка компиляции не найдено значение после раскрытия макроса

Я новичок в scala-макросах. Я пишу автоматизированную программу записи/чтения JSON для клиента InfluxDB.

Читатель выглядит так:

trait InfluxReader[T] {
  def read(js: JsArray): T
} 

InfluxFormatter:

object InfluxFormatter {
  /**
    * Generate InfluxReader for type ${A}
    */
  def reader[A]: InfluxReader[A] = macro InfluxFormatterImpl.reader_impl[A]
}

InfluxFormatterImpl:

private[macros] object InfluxFormatterImpl {
  /***
    * Generate AST for current type at compile time.
    * @tparam T - Type parameter for whom will be generated AST
    */
  def reader_impl[T: c.WeakTypeTag](c: blackbox.Context): c.universe.Tree = {
    import c.universe._

    val tpe = c.weakTypeOf[T]

    val methods = tpe.decls.toList collect {
      case m: MethodSymbol if m.isCaseAccessor =>
        m.name.decodedName.toString -> m.returnType.dealias
    }

    if (methods.lengthCompare(1) < 0) {
      c.abort(c.enclosingPosition, "Type parameter must be a case class with more then 1 fields")
    }

    val bool = typeOf[Boolean].dealias
    val int = typeOf[Int].dealias
    val long = typeOf[Long].dealias
    val double = typeOf[Double].dealias
    val string = typeOf[String].dealias

    val params = methods
      .map(_._1)
      .sorted
      .map(v => TermName(v))
      .map(v => q"$v = $v")

    val jsParams = methods
      .sortBy(_._1) // influx return results in alphabetical order
      .map { case (k, v) => TermName(k) -> v }
      .map {
        case (name, `bool`) => q"JsBoolean($name)"
        case (name, `string`) => q"JsString($name)"
        case (name, `int`) => q"JsNumber($name)"
        case (name, `long`) => q"JsNumber($name)"
        case (name, `double`) => q"JsNumber($name)"
        case (_, other) => c.abort(c.enclosingPosition, s"Unknown type $other")
      }

    val failureMsg = s"Can't deserialize $tpe object"

    val result = q"""
        new InfluxReader[$tpe] {
          def read(js: JsArray): $tpe = js.elements.tail match {
            case Vector(..$jsParams) => new $tpe(..$params)
            case _ => throw DeserializationException($failureMsg)
          }
       }"""

    result
  }
}

Спецификация теста:

import com.github.fsanaulla.core.model._
import com.github.fsanaulla.core.utils._
import com.github.fsanaulla.macros.InfluxFormatter
import org.scalatest.{FlatSpec, Matchers}
import spray.json._

class MacroReaderSpec extends FlatSpec with Matchers {

  "Macros" should "generate reader" in {
    case class Test(name: String, age: Int)

    val rd: InfluxReader[Test] = InfluxFormatter.reader[Test]

    rd.read(JsArray(JsNumber(234324), JsNumber(4), JsString("Fz"))) shouldEqual Test("Fz", 4)
  }
}

Компиляция MacroReaderSpec завершается с ошибкой компиляции:

Error:(14, 36) not found: value age
val rd = InfluxFormatter.reader[Test]
Error:(14, 36) not found: value name
val rd = InfluxFormatter.reader[Test]

С опциями компилятора "-Ymacro-debug-lite" это выглядит так:

Warning:scalac: {
  final class $anon extends InfluxReader[Test] {
    def <init>() = {
      super.<init>();
      ()
    };
    def read(js: JsArray): Test = js.elements.tail match {
      case Vector(JsNumber(age), JsString(name)) => new Test(age = age, name = name)
      case _ => throw DeserializationException("Can\'t deserialize Test object")
    }
  };
  new $anon()
}

Ошибка появляется в этой строке кода в части шаблона:

case Vector(JsNumber(age), JsString(name)) => new Test(age = age, name = name)

Почему не может найти? Я думаю, что он не может распознать это как выражение для сопоставления с образцом и не может вызвать экстрактор. Если да, то как его изменить? Может быть, это как-то называется под партитуру? Я использую Скала 2.12.4. Сбт 1.1.0. Исходный код можно найти здесь

Спасибо всем.


person fsanaulla    schedule 06.03.2018    source источник


Ответы (1)


Это было исправлено переписыванием кода с использованием pq квазикавычек. Также все идентификаторы должны быть обернуты в TermName(_). Рабочий пример можно найти здесь.

person fsanaulla    schedule 03.09.2018