To jest odpowiedź, która pokazuje odmianę techniki Federico, jeśli chcesz użyć adnotacji statycznej, która ma opcjonalne nazwane argumenty. W takim przypadku należy wziąć pod uwagę możliwe wyrazy wywołania w instrukcji dopasowania sprawy. Opcjonalny argument może być jawnie nazwany, może być podany bez nazwy lub może nie być obecny. Każdy z nich pojawia się w czasie kompilacji jako osobny wzorzec w c.prefix.tree
, jak pokazano poniżej.
@compileTimeOnly("Must enable the Scala macro paradise compiler plugin to expand static annotations")
class noop(arg1: Int, arg2: Int = 0) extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro AnnotationMacros.noop
}
class AnnotationMacros(val c: whitebox.Context) {
import c.universe._
// an annotation that doesn't do anything:
def noop(annottees: c.Expr[Any]*): c.Expr[Any] = {
// cases for handling optional arguments
val (arg1q, arg2q) = c.prefix.tree match {
case q"new noop($arg1, arg2 = $arg2)" => (arg1, arg2) // user gave named arg2
case q"new noop($arg1, $arg2)" => (arg1, arg2) // arg2 without name
case q"new noop($arg1)" => (arg1, q"0") // arg2 defaulted
case _ => c.abort(c.enclosingPosition, "unexpected annotation pattern!")
}
// print out the values
println(s"arg1= ${evalTree[Int](arg1q)} arg2= ${evalTree[Int](arg2q)}")
// just return the original annotee:
annottees.length match {
case 1 => c.Expr(q"{ ${annottees(0)} }")
case _ => c.abort(c.enclosingPosition, "Only one annottee!")
}
}
def evalTree[T](tree: Tree) = c.eval(c.Expr[T(c.untypecheck(tree.duplicate)))
}
Oto przykład inwokacja, że nazwy arg2
, a więc będzie pasować do pierwszego wzoru - case q"new noop($arg1, arg2 = $arg2)"
- powyżej:
object demo {
// I will match this pattern: case q"new noop($arg1, arg2 = $arg2)"
@noop(1, arg2 = 2)
trait someDeclarationToAnnotate
}
Należy również zauważyć, że ze względu na sposób działają te wzory, trzeba jawnie podaj domyślną wartość argumentu wewnątrz kodu makr, która jest niestety nieco zhakowana, ale ostateczna oceniana klasa nie jest dla ciebie dostępna.
W ramach eksperymentu próbowałem faktycznie utworzyć klasę, dzwoniąc pod numer evalTree[scope.of.class.noop](c.prefix.tree)
, ale kompilator Scali zgłosił błąd, ponieważ uznał, że odwołanie do adnotacji wewnątrz kodu makr adnotacji jest nielegalne.
To rozwiązanie jest interesujące, ale z jakiegoś powodu wydaje mi się "hacky". Również chcę, aby '' b'' był typu Boolean, a nie typu Any. Tak jak ktoś może powiedzieć '' @Foo ("AAA") '' i myśleć, że ich kod skompiluje się, patrząc na sygnaturę konstruktora. Poza tym stanie się bardziej zagmatwany, jeśli zdecyduję się dodać więcej argumentów. Ale to rozwiązanie działa, więc dziękuję! –
Odpowiedź edytowano, aby odzwierciedlić Twoje obawy. –
Ten kod nie jest teraz kompilowany. Po zmianie typu parametru z Boolean na Any wszystko jest w porządku. Komunikat o błędzie: '' wyrażenie typu Null nie kwalifikuje się do niejawnej konwersji '' –