Scala: возможно ли аннотировать поле конструктора класса с помощью макроаннотации? (макро рай) - PullRequest
4 голосов
/ 04 апреля 2019

Я пытаюсь комментировать значения конструктора класса, используя макро-аннотации. Предположим, что макроаннотация с именем @identity реализована и используется в определении класса A следующим образом:

class A(@identity val foo: String, // causes error
        val bar: String) {
@identity val foobar: String = "" // doesn't cause error
}

Когда просто комментируете foobar все компилируется просто отлично. Однако при аннотировании foo я получаю следующую ошибку во время компиляции:

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

Может ли кто-нибудь уточнить эту ошибку и почему она возникает?

1 Ответ

3 голосов
/ 04 апреля 2019

Я подозреваю, что вы называете макрос

  import scala.annotation.{StaticAnnotation, compileTimeOnly}
  import scala.language.experimental.macros
  import scala.reflect.macros.whitebox

  @compileTimeOnly("enable macro paradise to expand macro annotations")
  class identity extends StaticAnnotation {
    def macroTransform(annottees: Any*): Any = macro identity.impl
  }

  object identity {
    def impl(c: whitebox.Context)(annottees: c.Tree*): c.Tree = {
      import c.universe._
      println(s"$annottees")
      q"..$annottees"
    }
  }

как

  class A(@identity val foo: String,
          val bar: String) {
    @identity val foobar: String = ""
  }

  object A

Тогда у вас есть ошибка

Warning:scalac: List(<paramaccessor> val foo: String = _, class A extends scala.AnyRef {
  <paramaccessor> val foo: String = _;
  <paramaccessor> val bar: String = _;
  def <init>(foo: String, bar: String) = {
    super.<init>();
    ()
  };
  @new identity() val foobar: String = ""
}, object A extends scala.AnyRef {
  def <init>() = {
    super.<init>();
    ()
  }
})
Warning:scalac: 
Warning:scalac: List(<paramaccessor> val foo: String = _, def <init>(foo: String, bar: String) = {
  super.<init>();
  ()
})
Warning:scalac: List(val foobar: String = "")
Error:(8, 12) top-level class with companion can only expand into a block consisting in eponymous companions
  class A(@identity val foo: String,
Error:(8, 12) foo is already defined as value foo
  class A(@identity val foo: String,
Error:(8, 12) foo  is already defined as value foo
  class A(@identity val foo: String,

Дело в том, что вы берете класс (и, возможно, объект-компаньон) и возвращаете не только их, но и val foo, поэтому вы меняете число / вид определений верхнего уровня, что запрещено https://docs.scala -lang. орг / обзоры / макросы / annotations.html

Расширения верхнего уровня должны сохранять количество аннотированных, их разновидности и их имена, за исключением того, что класс может расширяться в класс с тем же именем и модуль с тем же именем, и в этом случае они автоматически становятся компаньонами при согласно предыдущему правилу.

Например, если мы изменим макрос

   def impl(c: whitebox.Context)(annottees: c.Tree*): c.Tree = {
      import c.universe._
      println(s"$annottees")
      q"..${annottees.tail}" // addded tail
    }

тогда все скомпилируется.

...