Я не уверен, что именно вы пробовали, но на первый взгляд кажется, что вам не хватает как минимум двух частей: вам нужно использовать Traverser
для обхода всех потомков дерева, и вам нужно проверить тип каждого дерева-кандидата, чтобы убедиться, что оно обезличено до точки что вы можете сказать, что у вас есть приложение SeqFactory
.
Например, вот быстрая реализация, которая печатает все деревья создания последовательности в классе во время компиляции:
import scala.language.experimental.macros
import scala.reflect.macros.Context
object SeqSearch {
def printCreatesInClass = macro printCreatesInClass_impl
def printCreatesInClass_impl(c: Context) = {
import c.universe._
import scala.collection.generic.SeqFactory
val factorySym = c.typeOf[SeqFactory[Seq]].typeSymbol
def isCreation(tree: Tree) = c.typeCheck(tree) match {
case Apply(TypeApply(Select(factory, name), _), _) if
factory.tpe.baseClasses.contains(factorySym) &&
name == newTermName("apply") => true
case _ => false
}
object printCreates extends Traverser {
override def traverse(tree: Tree) = tree match {
case application @ Apply(_, args) if isCreation(application) =>
println("Matched create: " + application)
super.traverseTrees(args)
case _ => super.traverse(tree)
}
}
printCreates(c.enclosingClass)
c.literalUnit
}
}
Это работает следующим образом:
scala> class Foo {
| SeqSearch.printCreatesInClass
| val l = List(1, 2)
| val v = Vector(1, 2)
| val ab = collection.mutable.ArrayBuffer(1, 2)
| val s = Seq(1, 2)
| }
Matched create: List(1, 2)
Matched create: Vector(1, 2)
Matched create: collection.mutable.ArrayBuffer(1, 2)
Matched create: Seq(1, 2)
defined class Foo
Поиск доступа аналогичен — просто добавьте следующие методы к объекту выше:
def printAccessesInClass = macro printAccessesInClass_impl
def printAccessesInClass_impl(c: Context) = {
import c.universe._
def isAccess(tree: Tree) = c.typeCheck(tree) match {
case Apply(Select(seq, name), _) if
seq.tpe <:< typeOf[Seq[Any]] &&
name == newTermName("apply") => true
case _ => false
}
object printAccesses extends Traverser {
override def traverse(tree: Tree) = tree match {
case application @ Apply(_, args) if isAccess(application) =>
println("Matched access: " + application)
super.traverseTrees(args)
case _ => super.traverse(tree)
}
}
printAccesses(c.enclosingClass)
c.literalUnit
}
А потом:
scala> class Foo {
| SeqSearch.printCreatesInClass
| SeqSearch.printAccessesInClass
| val xs = List(1, 2, 3)
| val xh = xs(0)
| }
Matched create: List(1, 2, 3)
Matched access: xs(0)
defined class Foo
Должно быть довольно легко настроить побочные эффекты Traverser
для захвата любой интересующей вас информации вместо того, чтобы просто печатать деревья во время компиляции.
person
Travis Brown
schedule
31.07.2013