简体   繁体   中英

Passing type parameter to scala meta macro/annotations

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]
  }
}

I would like to modify the macro above and add type parameter [A]. I tried the following but it does not compile

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]
  }
}

The arguments passed into macro annotations are passed in as meta trees.

Although literals such as Int/Double/String can be extracted via the Lit() extractor. this is not the case for other things.

When parsed in meta

  • @someMacro(1) becomes @someMacro(Lit(1))
  • @someMacro("Foo") becomes @someMacro(Lit("Foo"))

Everything else gets passed as it would be normally

  • @someMacro(foo) becomes @someMacro(Term.Name("foo"))
  • @someMacro(Option(2)) becomes @someMacro(Term.Apply(Term.Name("Option"), Seq(Lit(2))))

This means you do not have runtime access to this thing. You cannot even instantiate an object properly without the Semantic Api to resolve symbols etc. It may be possible in scalameta 2 and paradise 4, but it definately is not possible now. What you could do, is make a runtime pattern match to check the value.

I do some similar things here (Note this is very WIP): https://github.com/DavidDudson/Elysium/blob/master/gen/src/main/scala/nz/daved/elysium/gen/MacroAnnotation.scala

See specifically https://github.com/DavidDudson/Elysium/blob/master/gen/src/main/scala/nz/daved/elysium/gen/MacroAnnotation.scala#L149

Note: This means that at runtime (which just so happens to be compile time in that example), if the arg is passed in of an incorrect type, a runtime exception

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM