Не эксперт по макросам, поэтому возьмите его с крошкой соли.Но я проверил это с Intellij.
Во-первых, чтобы использовать weakTypeOf
, вам нужно взять WeakTypeTag
в качестве неявного в вашем макросе, например:
def implValList[T](c: whitebox.Context)(implicit wt: c.WeakTypeTag[T]) ...
Во-вторых, чтобысоздавая литералы, вы используете эту конструкцию вместо своей квазицитаты (которая, я полагаю, на самом деле ничего не делает):
Literal(Constant(m.name.toString))
В заключение, я рекомендую использовать эту охрану вместо isVal
:
m.isCaseAccessor && m.isGetter
Правильно проверяет параметр класса случая, а также является получателем (параметры класса случая дублируются, один как isGetter
, другой как isParam
).Причиной этого является то, что isVal
имена для классов case неожиданно создают имя, оканчивающееся пробелом.
Окончательная реализация, которая работает для меня, выглядит следующим образом:
object CaseClass {
def valList[T]: List[String] = macro implValList[T]
def implValList[T](c: whitebox.Context)(implicit wt: c.WeakTypeTag[T]): c.Expr[List[String]] = {
import c.universe._
val listApply = Select(reify(List).tree, TermName("apply"))
val vals = weakTypeOf[T].decls.collect {
case m: TermSymbol if m.isCaseAccessor && m.isGetter => Literal(Constant(m.name.toString))
}
c.Expr[List[String]](Apply(listApply, vals.toList))
}
}
Как альтернатива(поскольку макросы немного затрудняют настройку - вы не можете использовать макрос в том же подпроекте, который его определяет), и вам он не нужен очень часто, вы можете избежать неприятностей с shapeless однострочник:
import shapeless._
import shapeless.ops.record.Keys
case class Foo(a: Int, b: String)
Keys[the.`LabelledGeneric[Foo]`.Repr].apply().toList.map(_.name) // List("a", "b")