如果我有一个tranforms
代码的宏,例如:
(src: a.b.c.TestEntity) =>
{
z.y.TestTable(None)
}
为了匹配AST的None部分,我可以使用提取器,例如:
object NoneExtractor {
def unapply(t: Tree): Boolean = t match {
case Select(Ident(scala), none) if scala.encoded == "scala" && none.encoded == "None" => true
case _ => false
}
}
由于AST的None部分的showRaw
看起来像:
Select(Ident(scala), None)
然而,如果我想编写NoneExtractor
的单元测试,我不想编译和重建宏,并在宏编译的项目中托管测试。我想在宏的项目中对提取器进行单元测试,这表明运行时反射是可行的方法:
val t = reify {
(src: a.b.c.TestEntity) =>
{
z.y.TestTable(None)
}
}.tree
然而树完全不同,在那棵树的showRaw
中看起来像是:
Ident(scala.None)
这对于编写负面测试和检查宏的错误处理来说是个坏消息。您不能使用来自另一个项目的宏为宏编写负面测试,因为代码无法编译(并且您无法使用编译错误调试负面测试)。
为什么在编译时反射和运行时反射之间,像None一样基本的表示形式如此不同?有没有办法在宏项目中创建可测试的树片段,这与在编译时反射期间传递给宏的AST相同?
答案 0 :(得分:0)
要解决此不一致问题,您可以在模式匹配中使用即将发布的quasiqoutes。它们抽象出AST,因此可以使用两种表示形式(AST无论如何都是编译器特定的,Scala现在只是一种编译器语言,但依赖于编译器的内部表示并不是那么好):
case q"_root_.scala.None" => ...
将匹配两个AST。您还可以使用q"_root_.scala.None"
创建树,这样您就不必担心表示。当使用scala 2.11发布quasiquotes时,Reify将会过时。要使用scala 2.10的quasiquotes,您可以使用macro paradise。