我可以将 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