Извлечь ClassSymbols для границ параметров метода

Я пытаюсь извлечь ClassSymbols для всех границ параметров типа метода.

Решение, которое я придумал:

Реализация аннотации макроса:

@compileTimeOnly("Compile-time only annotation")
class classSyms extends StaticAnnotation {
  def macroTransform(annottees: Any*): Any = macro impl
}

object classSyms {
  def impl(c: whitebox.Context)(annottees: c.Tree*) = {
    import c.universe._
    annottees.toList foreach {
      case q"$mods def $templatename[..$typeparams](...$paramss): $tpt = $body" =>
        typeparams foreach {
          case q"$mods type $name[..$tparams] >: $low <: $high" =>
            if (!high.isEmpty) {
              //requires FQCN, does not work with imported names
              val classSymbol = c.mirror.staticClass(high.toString)
              println(classSymbol)
            }
        }
    }
    q"..$annottees"
  }
}

Пример:

package pack.age

trait Test

package another.pack.age

import pack.age._

trait Bar{
  @classSyms
  def foo[M <: pack.age.Test, T](): Unit //works ok


  @classSyms
  def baz[M <: Test, T](): Unit //throws scala.ScalaReflectionException: class Test not found.
}

Проблема заключается в том, что такие требования по указанию полного имени класса в качестве привязки параметра не делают эту реализацию макроса очень полезной (никто не хочет писать этот длинный материал fqcn, особенно если имя импортировано).

Можно ли извлечь ClassSymbol по импортированному имени?


person Some Name    schedule 31.10.2020    source источник


Ответы (1)


Попробуйте использовать c.typecheck.

Заменять

val classSymbol = c.mirror.staticClass(high.toString)

с

val classSymbol = c.typecheck(tq"$high", mode = c.TYPEmode).symbol.asClass
person Dmytro Mitin    schedule 31.10.2020
comment
Хорошо, спасибо. Я упустил момент проверки типов. Чтение docs Я обнаружил, что Помимо структуры программы, деревья также содержат важную информацию о семантике программы, закодированную в символе (символе, присвоенном деревьям, которые вводят или ссылаются на определения) и т.п. (вид дерева). По умолчанию эти поля пусты, но они заполняются при проверке типов. - person Some Name; 31.10.2020
comment
@SomeName Test в разных местах может иметь разное значение. Test, относящийся конкретно к pack.age.Test, становится известным после проверки типов дерева в текущем контексте c. - person Dmytro Mitin; 31.10.2020
comment
@SomeName См. этапы компиляции: parser (здесь строки исходного кода становятся нетипизированными деревьями), namer (здесь Test становится pack.age.Test), typer (здесь нетипизированное дерево q"..." становится типизированным, т.е. с типами, прикрепленными ко всем его узлам) ... typelevel.org/scala/docs/phases.html - person Dmytro Mitin; 31.10.2020
comment
@SomeName Возможно, в вашем конкретном случае использования namer без typer было бы достаточно, но для этого нет API. Но еще важнее то, что в Scala обычно имена не могут быть разрешены без проверки типов. Например, имплициты могут влиять на разрешение имен, а имплициты разрешаются во время проверки типов. - person Dmytro Mitin; 31.10.2020
comment
@SomeName В отличие от макросов def, управляющих типизированными деревьями (без c.untypecheck), аннотации макросов управляют нетипизированными деревьями (без c.typecheck). Аннотации макросов раскрываются перед проверкой типов, макросы def раскрываются во время проверки типов. - person Dmytro Mitin; 31.10.2020