收集范围内的所有标识符

时间:2014-01-29 23:42:43

标签: scala scala-macros

我正在尝试使用隐式宏来收集范围中存在的所有标识符,但我找不到一种优雅的方法来查找它们。

val endPos = c.enclosingPosition.endOrPoint
val mNames = c.enclosingMethod
             .collect {case v: ValDef => v}
                 .filter(_.pos.start > endPos)
                 .map {case ValDef(_, name, _, _) => Ident(name)}
val cNames = c.enclosingClass
             .collect {case ValDef(_, name, _, _) => Ident(name)}
val names = mNames ++ cNames

前者捕获所有变量,即使它们是用不同的方法定义的,但我需要一种方法来过滤cNames方法中出现的ValDef。调用children似乎可能有效,但我仍然从其他块中获取ValDefs。

我的最终目标是能够Logger.trace()并记录范围内的所有变量。所以

def test() = {
  val v = 2
  val z = "fdsaf"
  Logger.trace()
}

会记录v: 2, z: "fdsaf"

编辑2:

import c.universe._
val callingPos = c.enclosingPosition
val encMet = Option(c.enclosingMethod)

val classElements = c.enclosingClass
                    .asInstanceOf[ImplDefApi]
                    .impl.children
                    .collect {
    case v: Block => v.asInstanceOf[Tree]
    case v: DefDef => v.asInstanceOf[Tree]
    case v: ValDef => v.asInstanceOf[Tree]
    case v: ModuleDef => v.asInstanceOf[Tree]
}

val classFields = classElements.collect {case v@ValDef(_, name, _, _) if !v.isEmpty => Ident(name)}
val blockFields = classElements.collectFirst{
    case Block(body, ret) if positionIsInList(c)(body) => body.collect {
        case v@ValDef(_, name, _, _) if v.pos precedes callingPos => Ident(name)
    }
}.getOrElse(List())
val methodFields = encMet
                   .map(_.collect {case v@ValDef(_, name, _, _) if v.pos precedes callingPos => Ident(name)})
                   .getOrElse(List())

val names = classFields ++ blockFields ++ methodFields

在大多数情况下都适用,但在块上失败。

1 个答案:

答案 0 :(得分:2)

不幸的是,此用例不是Scala宏目前支持的用例。当然,正如你所展示的那样,或多或少的工作可以被掀起(顺便说一下,非常感谢这些例子!),但据我所知,没有可靠的方法来实现你的目标。

将来我们可能能够应对这一挑战,但目前存在一些技术限制,使我们无法在此时暴露此类API。即使转换为编译器内部,有时有助于处理棘手的情况,也不会对此有所帮助。实际上,这正是我们在Scala 2.11中弃用了c.enclosingTree风格的API的原因:https://groups.google.com/forum/#!topic/scala-internals/nf_ooEBn6-k

如果您可以详细说明您要实现的目标,我很乐意帮助您查找可能最终对您的用例有用的变通方法。