Я использую следующий Scala макрос (сильно вдохновленный код из this SO question), чтобы получить список всех объектов, содержащихся в данном пакете, наследующих определенную черту:символ класса Instantiate с помощью макроса
object Macros {
def allObjects[T <: AnyRef](packageName: String): List[Any] = macro allObjectsImpl[T]
def allObjectsImpl[T <: AnyRef: c.WeakTypeTag](c: Context)(packageName: c.Expr[String]) = {
import c.universe._
val baseTraitSymbol = c.weakTypeOf[T].typeSymbol
val pkg = packageName.tree match {
case Literal(Constant(name: String)) => c.mirror.staticPackage(name)
}
val types = pkg.typeSignature.members.collect {
case moduleSymbol: ModuleSymbol if moduleSymbol.moduleClass.asClass.baseClasses contains baseTraitSymbol => Ident(moduleSymbol)
}.toList
val listApply = Select(reify(List).tree, TermName("apply"))
c.Expr[List[T]](Apply(listApply, types))
}
}
Что хорошо работает.
Я хочу изменить макрос так, что вместо того, чтобы получать все объектам в пакете, он получает все конкретные классы и предоставляет список, содержащий экземпляр каждого из них.
АСТ при создании экземпляра объекта выглядит следующим образом:
scala> import scala.reflect.runtime.{universe => u}
import scala.reflect.runtime.{universe=>u}
scala> u showRaw (u reify {new Object})
res42: String = Expr(Apply(Select(New(Ident(java.lang.Object)), termNames.CONSTRUCTOR), List()))
Так я думал, меняя мой код, это будет работать:
object Macros {
def allInstances[T <: AnyRef](packageName: String): List[Any] = macro allInstancesImpl[T]
def allInstancesImpl[T <: AnyRef: c.WeakTypeTag](c: Context)(packageName: c.Expr[String]) = {
import c.universe._
val baseTraitSymbol = c.weakTypeOf[T].typeSymbol
val pkg = packageName.tree match {
case Literal(Constant(name: String)) => c.mirror.staticPackage(name)
}
def isConcreteChildClass(child: ClassSymbol, base: Symbol) = {
!child.isAbstract && (child.baseClasses contains base)
}
val types = pkg.typeSignature.members.collect {
case classSymbol: ClassSymbol if isConcreteChildClass(classSymbol, baseTraitSymbol) => {
Apply(Select(New(Ident(classSymbol.primaryConstructor)), termNames.CONSTRUCTOR), List())
}
}.toList
val listApply = Select(reify(List).tree, TermName("apply"))
c.Expr[List[T]](Apply(listApply, types))
}
}
Однако, когда я пытаюсь использовать обновленный код макроса в тестовом пакете, я получаю следующую ошибку:
scala> Macros.allInstances[AnyRef]("test")
<console>:9: error: class type required but()test.TestClass found
Macros.allInstances[AnyRef]("test")
Из того, что я нахожу eing, похоже, что макрос фактически возвращает сам конструктор вместо того, чтобы возвращать экземпляр, который должен быть построен конструктором, но я не могу понять, что мне не хватает.