0
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]
  }
}
Michael Zajac
  • 55,144
  • 7
  • 113
  • 138
Bate
  • 23
  • 8
  • i am just trying to do something similar to what is described here https://github.com/scalameta/sips/blob/3520d5c761abfdfeff66d396456791971795f6af/sips/pending/_posts/2016-09-09-inline-meta.md#inlinemeta – Bate Jan 21 '17 at 08:34

1 Answers1

1

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

David Dudson
  • 116
  • 9
  • Thank you @David Dudson I am getting an error while trying to compile this class https://github.com/DavidDudson/Elysium/blob/master/gen/src/main/scala/nz/daved/elysium/gen/MacroAnnotation.scala can you provide the paradise_2.11.8-3.0.0-SNAPSHOT.jar ? `[error] /Users/batemady/tools/scala/samples/meta/scalameta-tutorial/src/main/scala/scala/meta/serialiser/MacroAnnotation.scala:14: ; expected but def found [error] val newStat = q"$compileTimeOnly inline def apply(a: Any): Any = meta { ..$inMetaBlockStats }"` – Bate Jan 21 '17 at 13:05
  • I get the following error while trying to compile the whole Elysium project `[error] Missing required plugin: macroparadise` – Bate Jan 21 '17 at 13:29
  • I finally got it compile sucessfuly by just commenting in the line adding the Paradise plugin – Bate Jan 21 '17 at 13:31
  • That project is using this PR https://github.com/scalameta/paradise/pulls. Elysium should not compile without that PR as it deals with macros generating other macros – David Dudson Jan 21 '17 at 19:56
  • What i would like todo is using type parameters passed to the macro while expanding. In the following macro i can print the parameter passed to the macro, but dont know how to use and replace T and L with the passed in while generating the returned tree( defn ) `import scala.annotation.StaticAnnotation class tparam[T, L] extends StaticAnnotation { inline def apply(defn: Any): Any = meta { assert(T.toString == "Int") assert(L.toString == "String") println(T.toString ) println(L.toString) defn } } ` – Bate Jan 22 '17 at 14:11