Why does the runtime reflective universe and the macro universe create two different trees for scala.None?

StackOverflow https://stackoverflow.com/questions/19564521

  •  01-07-2022
  •  | 
  •  

Question

If I have a macro which tranforms code such as:

  (src: a.b.c.TestEntity) =>
    {
      z.y.TestTable(None)
    }

To match the None part of that AST I can use an extractor such as:

  object NoneExtractor {
    def unapply(t: Tree): Boolean = t match {
      case Select(Ident(scala), none) if scala.encoded == "scala" && none.encoded == "None" => true
      case _ => false
    }
  }

As the showRaw of the None part of the AST looks like:

Select(Ident(scala), None)

Yet if I want to write a unit test of the NoneExtractor I don't want to compile and rebuild the macros and host the test in the project the macro is compiling. I want to unit test the extractor in the macro's project which suggests runtime reflection is the way to go with:

val t = reify {

  (src: a.b.c.TestEntity) =>
    {
      z.y.TestTable(None)
    }

}.tree 

Yet the tree is totally different and in the showRaw of that tree the None looks like:

Ident(scala.None)

This is bad news for writing negative tests and checking the error handling of my macro. You cannot write negative tests for a macro using the macro from another project as the code would not compile (and you cannot debug your negative test with compile errors).

Why are the representations of something as fundamental as None so different between compile time reflection and runtime reflection? Is there a way to create the testable tree fragments within the macro project which is the same AST as would be handed to the macro during compile time reflection?

Was it helpful?

Solution

To work around this inconsistency, you can use the upcoming quasiqoutes in your pattern match. They abstract away the AST and thus will work with both representations (the AST is compiler specific anyway, Scala is a single compiler language for now but it is not so nice to rely on internal representation of the compiler):

case q"_root_.scala.None" => ...

would match both ASTs. Also you can create the tree with q"_root_.scala.None" so that you don't have to worry about the representation. Reify is going to be obsolete when quasiquotes are released with scala 2.11. To use quasiquotes with scala 2.10 you can use macro paradise.

Here is a nice WIP guide on scala quasiquotes.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top