Scala 3:查找具有给定注释的函数

Scala 3 : Finding functions with the given annotation

对于 Scala 3 宏,有谁知道找到具有给定注释的所有函数的方法吗?

例如:

@fruit
def apple(): Int = ???

@fruit
def banana(): Int = ???

@fruit
def coconut(): Int = ???

@fruit
def durian(): Int = ???

def elephant(): Int = ???

@fruit
def fig(): Int = ???

我想查找 apple, banana, coconut, durian, fig 的列表。它们可以在任何地方定义,但在我的例子中,它们都在一个包中。

此解决方案将从给定包中提取所有带有注释的定义。我还将利用 compile-time 反射。

此解决方案将从给定包中提取所有带有一些注释的定义。我还将利用 compile-time 反射。 所以,为了解决你的问题,我们需要把它分成:

  • 从包中收集方法;
  • 仅过滤具有给定注释的方法;
  • 在函数应用中转换符号。 我想您可以将包和注释(以及 return 类型)作为类型参数传递。所以宏签名是这样的:
inline def findAllFunction[P, A <: ConstantAnnotation, R]: List[() => R] = 
    ${Implementation.myMacroImpl[P, A, R]()}

第一点很简单。我们可以提取所有定义为的方法:

def methodsFromPackage(packageSymbol: Symbol): List[Symbol] =
  packageSymbol.declaredTypes
    .filter(_.isClassDef)
    .flatMap(_.declaredMethods)

第二点也很简单。 Symbol class 有可以在这种情况下使用的方法 hasAnnotation

def methodsAnnotatatedWith(
    methods: List[Symbol],
    annotation: Symbol
): List[Symbol] =
  methods.filter(_.hasAnnotation(annotation))

最后一点有点挑战性。这里我们应该构造方法调用。所以我们需要创建对应于方法调用的 AST。受此 example 的启发,我们可以使用 Apply 调用定义。 SelectThis 用于 select 将调用的正确方法:

def transformToFunctionApplication(methods: List[Symbol]): Expr[List[() => R]] =
  val appliedDef = methods
    .map(definition => Select(This(definition.owner), definition))
    .map(select => Apply(select, List.empty))
    .map(apply => '{ () => ${ apply.asExprOf[R] } })
  Expr.ofList(appliedDef)

这里我使用了lamba调用,如果你想直接return这个值你应该改变最后两条指令:

def transformToFunctionApplication(methods: List[Symbol]): Expr[List[R]] =
  val appliedDef = methods
    .map(definition => Select(This(definition.owner), definition))
    .map(select => Apply(select, List.empty))
    .map(apply => apply.asExprOf[R])

  Expr.ofList(appliedDef)

综上所述,所有方法可以定义为:

def myMacroImpl[P: Type, A: Type, R: Type]()(using
    Quotes
): Expr[List[() => R]] = {
  import quotes.reflect.*
  val annotation = TypeRepr.of[A].typeSymbol
  val moduleTarget = TypeRepr.of[P].typeSymbol

  def methodsFromPackage(packageSymbol: Symbol): List[Symbol] =
    packageSymbol.declaredTypes
      .filter(_.isClassDef)
      .flatMap(_.declaredMethods)

  def methodsAnnotatatedWith(
      methods: List[Symbol],
      annotation: Symbol
  ): List[Symbol] =
    methods.filter(_.hasAnnotation(annotation))

  def transformToFunctionApplication(
      methods: List[Symbol]
  ): Expr[List[() => R]] =
    val appliedDef = methods
      .map(definition => Select(This(definition.owner), definition))
      .map(select => Apply(select, List.empty))
      .map(apply => '{ () => ${ apply.asExprOf[R] } })
    Expr.ofList(appliedDef)

  val methods = methodsFromPackage(moduleTarget)
  val annotatedMethod = methodsAnnotatatedWith(methods, annotation)
  transformToFunctionApplication(annotatedMethod)
}

最后,您可以将宏用作:

package org.tests
import org.tests.Macros.fruit

package foo {
  @fruit
  def check(): Int = 10
  @fruit
  def other(): Int = 11
}


@main def hello: Unit = 
  println("Hello world!")
  println(Macros.findAllFunction[org.tests.foo, fruit, Int].map(_.apply())) /// List(10, 11)

Scastie