我可以将 class 作为宏注释的参数吗
Can I take a class as an argument to a macro annotation
我正在尝试基于一些由第三方注释预处理器生成的 java classes 构建一个 scala class。
我希望能够从带注释的对象 "point" 到 class,例如:
@MyAnnotation(classOf[GeneratedJavaClass]) object MyObject
或
@MyAnnotation object MyObject extends PlaceHolderTrait[GeneratedJavaClass]
一旦我进入实际的宏实现,我想反思 GeneratedJavaClass 以找到它的成员,我将使用这些成员来构建 MyObject
的实现
到目前为止,我的出发点是基于 https://github.com/travisbrown/type-provider-examples/blob/master/rdfs-public/src/main/scala/public/PrefixGenerator.scala。
我试图理解如何将 Class[T] 作为注释的参数,然后在 c.macroApplication
上与 Apply(Select(Apply(_, List(TypeApply(_, List(catalog)))), _), _)
匹配,但我得到的类型是 TypeApply(_, List(Trees$Ident)
并且我看不到从那里获取 class 的方法(我假设 classOf[T] 不是文字)。
作为替代方案,我想我会尝试从我有对象扩展的特征中提取我想要的类型。我尝试将注释者与 case List(q"object $name extends PlaceHolderTrait[$parent] { ..$body }")
进行匹配,但再次以 Trees$Ident 结束,我不确定如何获取被引用的 class。
我意识到我可能只传递完全限定名称的字符串并使用反射来获取 class,但我希望有更好的选择。请注意,我并不局限于指定 class 的那 2 个备选方案,它们只是我能够提出的 2 个选项。
另一个想法:
使用另一个 class 注释保存 class 信息
class AnnotationArgumentClass[T](clazz: Class[T]) extends StaticAnnotation
class AnnotationArgument extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro AnnotationArgumentImpl.impl
}
class AnnotationArgumentImpl(val c: blackbox.Context) {
import c.universe._
def impl(annottees: c.Expr[Any]*): c.Tree = {
val classValue = annottees.head.tree match {
case x: MemberDefApi => x.mods.annotations collectFirst {
case q"new $annotation($classValue)" => classValue
}
}
/*edit :*/
val x = (c.eval(c.Expr[Type](c.typecheck(classValue.get))))
println(x.typeSymbol.fullName)
q"..${annottees}"
}
}
测试:
package aaa
class AAA
//
import aaa.AAA
@AnnotationArgument
@AnnotationArgumentClass(classOf[AAA])
class AnnotationArgumentTestClass
好的,基于 https://github.com/scalamacros/paradise/issues/69,我终于开始工作了,我意识到在这个阶段,我 运行 打字机还没有 运行,因此期待给定一个类型有点傻。然而,宏 api 确实提供了 typeCheck
方法,它可以让你 运行 树上的打字机,如下所示:
class AnnotationArgument[T] extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro AnnotationArgumentImpl.impl
}
class AnnotationArgumentImpl(val c: blackbox.Context) {
import c.universe._
def impl(annottees: c.Expr[Any]*): c.Tree = {
val macroTypeWithArguments = c.typeCheck(q"${c.prefix.tree}").tpe // my.package.AnnotationArgument[my.package.MyClass]
val annotationClass: ClassSymbol = macroTypeWithArguments.typeSymbol.asClass // my.package.AnnotationArgument
val annotationTypePlaceholder: Type = annotationClass.typeParams.head.asType.toType // T
val argumentType: Type = annotationTypePlaceholder.asSeenFrom(args, annotationClass) // my.package.MyClass
println(s"the argument's type is $argumentType")
q"..${annottees}"
}
}
import my.package.MyClass
@AnnotationArgument[MyClass]
class AnnotationArgumentTestClass
我正在尝试基于一些由第三方注释预处理器生成的 java classes 构建一个 scala class。
我希望能够从带注释的对象 "point" 到 class,例如:
@MyAnnotation(classOf[GeneratedJavaClass]) object MyObject
或
@MyAnnotation object MyObject extends PlaceHolderTrait[GeneratedJavaClass]
一旦我进入实际的宏实现,我想反思 GeneratedJavaClass 以找到它的成员,我将使用这些成员来构建 MyObject
的实现到目前为止,我的出发点是基于 https://github.com/travisbrown/type-provider-examples/blob/master/rdfs-public/src/main/scala/public/PrefixGenerator.scala。
我试图理解如何将 Class[T] 作为注释的参数,然后在 c.macroApplication
上与 Apply(Select(Apply(_, List(TypeApply(_, List(catalog)))), _), _)
匹配,但我得到的类型是 TypeApply(_, List(Trees$Ident)
并且我看不到从那里获取 class 的方法(我假设 classOf[T] 不是文字)。
作为替代方案,我想我会尝试从我有对象扩展的特征中提取我想要的类型。我尝试将注释者与 case List(q"object $name extends PlaceHolderTrait[$parent] { ..$body }")
进行匹配,但再次以 Trees$Ident 结束,我不确定如何获取被引用的 class。
我意识到我可能只传递完全限定名称的字符串并使用反射来获取 class,但我希望有更好的选择。请注意,我并不局限于指定 class 的那 2 个备选方案,它们只是我能够提出的 2 个选项。
另一个想法:
使用另一个 class 注释保存 class 信息
class AnnotationArgumentClass[T](clazz: Class[T]) extends StaticAnnotation
class AnnotationArgument extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro AnnotationArgumentImpl.impl
}
class AnnotationArgumentImpl(val c: blackbox.Context) {
import c.universe._
def impl(annottees: c.Expr[Any]*): c.Tree = {
val classValue = annottees.head.tree match {
case x: MemberDefApi => x.mods.annotations collectFirst {
case q"new $annotation($classValue)" => classValue
}
}
/*edit :*/
val x = (c.eval(c.Expr[Type](c.typecheck(classValue.get))))
println(x.typeSymbol.fullName)
q"..${annottees}"
}
}
测试:
package aaa
class AAA
//
import aaa.AAA
@AnnotationArgument
@AnnotationArgumentClass(classOf[AAA])
class AnnotationArgumentTestClass
好的,基于 https://github.com/scalamacros/paradise/issues/69,我终于开始工作了,我意识到在这个阶段,我 运行 打字机还没有 运行,因此期待给定一个类型有点傻。然而,宏 api 确实提供了 typeCheck
方法,它可以让你 运行 树上的打字机,如下所示:
class AnnotationArgument[T] extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro AnnotationArgumentImpl.impl
}
class AnnotationArgumentImpl(val c: blackbox.Context) {
import c.universe._
def impl(annottees: c.Expr[Any]*): c.Tree = {
val macroTypeWithArguments = c.typeCheck(q"${c.prefix.tree}").tpe // my.package.AnnotationArgument[my.package.MyClass]
val annotationClass: ClassSymbol = macroTypeWithArguments.typeSymbol.asClass // my.package.AnnotationArgument
val annotationTypePlaceholder: Type = annotationClass.typeParams.head.asType.toType // T
val argumentType: Type = annotationTypePlaceholder.asSeenFrom(args, annotationClass) // my.package.MyClass
println(s"the argument's type is $argumentType")
q"..${annottees}"
}
}
import my.package.MyClass
@AnnotationArgument[MyClass]
class AnnotationArgumentTestClass