Scala Macros: Getting a List of TypeSymbols to be used at runtime

macros, scala, scala-macros

Solution

You have to use `reifyType` to create reflection artifacts in the runtime universe:

import scala.language.experimental.macros
import scala.reflect.macros.Context

object PackageMacros {
  def allTypeSymbols[T](packageName: String) = macro allTypeSymbols_impl[T]

  def allTypeSymbols_impl[T: c.WeakTypeTag](c: Context)(
    packageName: c.Expr[String]
  ) = {
    import c.universe._

    val pkg = packageName.tree match {
      case Literal(Constant(name: String)) => c.mirror.staticPackage(name)
    }

    val types = pkg.typeSignature.members.collect {
      case sym: ClassSymbol =>
        c.reifyType(treeBuild.mkRuntimeUniverseRef, EmptyTree, sym.toType)
    }.toList

    val listApply = Select(reify(List).tree, newTermName("apply"))

    c.Expr[List[Any]](Apply(listApply, types))
  }
}

This will give you a list of type tags, not symbols, but you can pretty easily get the symbols, either like this:

scala> PackageMacros.allTypeSymbols("foo").map(_.tpe.typeSymbol) foreach println
class Baz$
class Bar
class Baz
trait FooTrait
class Bar$

Or in the macro itself.

Problem

Is there a way to return a `List` of `TypeSymbol`s for each class under a package using macros? What I am trying to achieve is to write a macro that gives out something equivalent to this list: ``` scala> import scala.reflect.runtime.universe._ import scala.reflect.runtime.universe._ scala> case class MyClass1() defined class MyClass1 scala> case class MyClass2() defined class MyClass2 scala> val typeSymbols = List(typeOf[MyClass1].typeSymbol, typeOf[MyClass2].typeSymbol) typeSymbols: List[reflect.runtime.universe.Symbol] = List(class MyClass1, class MyClass2) ``` Here's my setup: I have a package named `foo`, under which these are defined: ``` trait FooTrait case class Bar() extends FooTrait case class Bar() extends FooTrait ``` Here's my macro that gets all type symbols for the classes under foo that extend `FooTrait`: ``` def allTypeSymbols_impl[T: c.WeakTypeTag](c: Context)(packageName: c.Expr[String]) = { import c.universe._ // Get package name from the expression tree val Literal(Constant(name: String)) = packageName.tree // Get all classes under given package name val pkg = c.mirror.staticPackage(name) // Obtain type symbols for the classes - implementation omitted val types = getTypeSymbols(c.universe)(List(pkg)) // Apply method for List. For easy readability in later applications val listApply = Select(reify(List).tree, newTermName("apply")) val result = types.map { t => val typeName = c.Expr[TypeSymbol](Ident(t)) println(s"Typename: $typeName, $t, ${t.toType}") reify(typeName.splice).tree } println(s"RESULT: ${showRaw(result)}") c.Expr[List[reflect.runtime.universe.TypeSymbol]](Apply(listApply, result.toList)) } ``` The first `println` prints: ``` Typename: Expr[c.universe.TypeSymbol](Bar), class Bar, foo.Bar Typename: Expr[c.universe.TypeSymbol](Baz), class Baz, foo.Baz ``` The second one prints: ``` RESULT: List(Ident(foo.Bar), Ident(foo.Baz)) ``` But I get this error message: ``` [error] no type parameters for method any2ArrowAssoc: (x: A)ArrowAssoc[A] exist so that it can be applied to arguments (<notype>) [error] --- because --- [error] argument expression's type is not compatible with formal parameter type; [error] found : <notype> [error] required: ?A [error] Note that <none> extends Any, not AnyRef. [error] Such types can participate in value classes, but instances [error] cannot appear in singleton types or in reference comparisons. ``` What should I do to make this work? I suspect that I have to write something else instead of `Ident`, but I couldn't figure out what. Using Scala 2.10.2. Thanks in advance!

Original source