Ошибка компиляции "not found value ..." после раскрытия макроса
Я новичок в макросах скала. Я пишу автоматизированный JSON Writer/ Reader для клиента 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
}
}
Тест Spec:
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)
Почему это не может найти это? Я думаю, что он не может распознать его как выражение для сопоставления с образцом и не может вызвать экстрактор. Если да, то как это изменить? Может быть, это как-то называет это по счету? Я использую scala 2.12.4. Сб 1.1.0. Исходный код можно найти здесь
Спасибо всем.
1 ответ
Это было исправлено путем переписывания кода с помощьюpq
quasiquotes. Также весь идентификатор должен быть перенесен в TermName(_)
, Рабочий пример можно найти здесь.