2014-01-29 4 views
1

Я пытаюсь собрать все идентификаторы, существующие в области видимости, с помощью неявного макроса, но я не могу найти элегантный способ их найти.Собирать все идентификаторы в области

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 

предыдущие захватывает все переменные, даже если они определены в различных методах, но мне нужен способ фильтрации ValDefs, которые происходят в методах от cNames. Вызов children выглядел так, как будто он сработал, но я все равно получаю ValDefs из других блоков.

Моя конечная цель состоит в том, чтобы сделать Logger.trace() и иметь все переменные в области регистрации. Так

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

бы войти v: 2, z: "fdsaf"

Edit 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 [email protected](_, name, _, _) if !v.isEmpty => Ident(name)} 
val blockFields = classElements.collectFirst{ 
    case Block(body, ret) if positionIsInList(c)(body) => body.collect { 
     case [email protected](_, name, _, _) if v.pos precedes callingPos => Ident(name) 
    } 
}.getOrElse(List()) 
val methodFields = encMet 
        .map(_.collect {case [email protected](_, name, _, _) if v.pos precedes callingPos => Ident(name)}) 
        .getOrElse(List()) 

val names = classFields ++ blockFields ++ methodFields 

Работает в большинстве случаев, но не работает на блоках.

ответ

2

К сожалению, этот прецедент не является чем-то, что поддерживается макросами Scala на данный момент. Конечно, что-то более или менее работающее можно взломать, как вы продемонстрировали (спасибо большое за примеры, кстати!), Но, насколько мне известно, нет надежного способа достижения вашей цели.

В будущем мы сможем решить эту проблему, но в настоящее время существуют технические ограничения, которые мешают нам разоблачить этот вид API на данный момент. Даже приведение к внутренним компонентам компилятора, которое иногда помогает в сложных случаях, здесь не поможет. На самом деле, именно по этой причине в Scala 2.11 мы устарели c.enclosingTree-style API: https://groups.google.com/forum/#!topic/scala-internals/nf_ooEBn6-k.

Если вы могли бы уточнить, чего вы пытаетесь достичь, я был бы рад помочь разобраться в обходных методах, которые могут оказаться полезными для вашего случая использования.

+0

Я отредактировал свой вопрос, чтобы ответить на мою конечную цель, прямо перед «Редактировать 2:». Я видел ваш запрос на pulling для приложения thewner, который выглядит так, как будто это почти решит это, но я не могу понять, как получить «Tree» из «Symbol». – user60561

+0

Получение дерева из символа невозможно, потому что символы - это просто записи в таблице определений, доступных компилятору, без какой-либо информации об АСТ, прикрепленной к ним. Это может измениться в будущем, но пока это не поддерживается. –

+0

Я не думаю, что на данный момент можно написать такой макрос. Самое близкое, что вы можете найти в сфере действия, - это c.inferImplicitValue, но для этого вам нужно, чтобы ваша вещь была неявной и определенного типа была известна заранее. –

Смежные вопросы