Передача параметра типа в мета-макрос / аннотации scala

package scalaworld.macros
import scala.meta._

class Argument(arg: Int) extends scala.annotation.StaticAnnotation {
  inline def apply(defn: Any): Any = meta {
    println(this.structure)
    val arg = this match {
      // The argument needs to be a literal like `1` or a string like `"foobar"`.
      // You can't pass in a variable name.
      case q"new $_(${Lit(arg: Int)})"                      => arg
      // Example if you have more than one argument.
      case q"new $_(${Lit(arg: Int)}, ${Lit(foo: String)})" => arg
      case _                                                => ??? // default     value
    }
    println(s"Arg is $arg")
    defn.asInstanceOf[Stat]
  }
}

Я хотел бы изменить макрос выше и добавить параметр типа [A]. Я попробовал следующее, но он не компилируется

package scalaworld.macros
import scala.meta._

class Argument2[A](arg: A) extends scala.annotation.StaticAnnotation {
  inline def apply(defn: Any): Any = meta {
    println(this.structure)
    val arg = this match {
      case q"new $_(${Lit(arg: A)})"                      => arg
      case q"new $_(${Lit(arg: A)}, ${Lit(foo: String)})" => arg
      case _                                              => ???
    }
    println(s"Arg is $arg")
    defn.asInstanceOf[Stat]
  }
}

1 ответ

Решение

Аргументы, передаваемые в макроаннотации, передаются в виде мета-деревьев.

Хотя литералы, такие как Int/Double/String, могут быть извлечены через Lit() экстрактор. это не относится к другим вещам.

Когда анализируется в мета

  • @someMacro(1) становится @someMacro(Lit(1))
  • @someMacro("Foo") становится @someMacro(Lit("Foo"))

Все остальное проходит как обычно

  • @someMacro(foo) становится @someMacro(Term.Name("foo"))
  • @someMacro(Option(2)) становится @someMacro(Term.Apply(Term.Name("Option"), Seq(Lit(2))))

Это означает, что у вас нет доступа к этой вещи во время выполнения. Вы даже не можете создать экземпляр объекта должным образом без Semantic Api для разрешения символов и т. Д. Это может быть возможно в scalameta 2 и в рае 4, но сейчас это определенно невозможно. Что вы можете сделать, так это сопоставить шаблон времени выполнения, чтобы проверить значение.

Я делаю некоторые подобные вещи здесь (обратите внимание, что это очень WIP): https://github.com/DavidDudson/Elysium/blob/master/gen/src/main/scala/nz/daved/elysium/gen/MacroAnnotation.scala

В частности, см. https://github.com/DavidDudson/Elysium/blob/master/gen/src/main/scala/nz/daved/elysium/gen/MacroAnnotation.scala

Примечание. Это означает, что во время выполнения (которое в данном примере является временем компиляции), если аргумент arg передан неверного типа, исключение времени выполнения

Другие вопросы по тегам