查找所有具有scala 3反射的包级对象



我试图获得Expr[Any]对应于所有包级对象的列表。我可以找到包符号本身(通过追踪任何顶级对象的符号所有者),然后遍历declardfields。我可以通过这种方式找到顶层对象ValDef节点,但在ValDef中没有相关的Term。我也不会用Term。在包树上选择以获得声明的成员,因为我无法获得包符号的树。

似乎应该有可能从它们的符号中获得与这些顶级单例对象相关的Term(而不仅仅是ValDef),但我无法弄清楚。

基本上我想这样做:

inline def packageToucher[T](something : T) : List[Any] = {
${ packageToucherImpl('something) } 
}
def packageToucherImpl[T](something : Expr[T])(using Quotes, Type[T]): Expr[List[Any]] = {
import quotes.reflect.*
// assume something is a top level object
val _package = TypeRepr.of[T].typeSymbol.owner 
val fields = _package.declaredFields
val packageExpr = _package.tree.asExpr //  assertion failed: Cannot get tree of package symbol
val packageTree = packageExpr.asTerm
val fieldExprs : List[Expr[Any]] = fields.map( f => packageTree.select(f).asExpr)
Expr.ofList(fieldExprs)
}

,一个用法的例子是:

// in actions.scala
package potato
object Actions // dummy object for finding package
object yelling extends Action("yell")
object sleeping extends Action("sleep")
object typing extends Action("type")

import potato.*
val actions = Macros.packageToucher(Actions) // should be List(Actions, yelling, sleeping, typing)

任何帮助都会很感激。对于scala 3的宏,我还是个新手。

谢谢!

嗯,经过大量的努力、尝试和错误,我找到了一个解决方案。也许它对别人会有用。

inline def packageToucher[T](something : T) : List[Any] = {
${ packageToucherImpl('something) }
}
@experimental // needed for access to Symbol.termRef
def packageToucherImpl[T](something : Expr[T])(using Quotes, Type[T]): Expr[List[Any]] = {
import quotes.reflect.*
// assume something is a top level object
val _package = TypeRepr.of[T].typeSymbol.owner
val fields = _package.fieldMembers
// for some reason, scala generates erased companion objects for Classes that don't have them.
// Luckily they can be filtered out by checking for the synthetic flag
val valdefs = fields.filter(f => f.isValDef && !f.flags.is(Flags.Synthetic))
// here's where the magic happens, we can't access the package's companion object tree directly
// but we can construct one using AST nodes
val packageTerm = Ident(_package.companionModule.termRef)
val exprs = valdefs.map(Select(packageTerm, _).asExpr)
Expr.ofList(exprs)
}

最新更新