Scala 3 宏开发:如何获取宏调用所处的封闭模块
Scala 3 宏获取调用所在封闭模块全限定名实现方案
实现核心是利用Scala 3 宏的quoted反射API,获取宏调用点的所有者符号,遍历拿到封闭类/对象的全限定名即可,具体实现如下:
第一步:定义宏接口与实现
你需要将宏的定义和实现拆分,宏实现部分需要使用scala.quoted相关API:
import scala.quoted.* object SomeMacro: // 对外暴露的宏调用接口 inline def macroCall(inline expr: String): String = ${ macroImpl('expr) } // 宏具体实现逻辑 private def macroImpl(expr: Expr[String])(using Quotes): Expr[String] = import quotes.reflect.* // 获取宏调用点的符号 val callSiteSymbol = Symbol.spliceOwner // 向上遍历所有者链,找到最近的类/对象/特质符号 def findEnclosingModule(symbol: Symbol): Symbol = if symbol.isClassDef then symbol else if symbol.isNoSymbol then report.errorAndAbort("宏调用点不存在封闭模块") else findEnclosingModule(symbol.owner) val enclosingModule = findEnclosingModule(callSiteSymbol) // 直接获取全限定名,格式为 包名.类名 val fullQualifiedName = enclosingModule.fullName // 将常量字符串返回给调用点 Expr(fullQualifiedName)
第二步:调用验证
你的业务代码直接调用宏即可拿到预期结果:
package foo.bar class MyClass: // 该变量的值编译后就固定为 "foo.bar.MyClass" val macroReturnValue = SomeMacro.macroCall("someExpression") end MyClass
注意事项
- 宏代码需要和业务调用代码分属不同编译单元,避免出现循环编译错误
- 如果你需要适配顶层方法、嵌套类等特殊场景,只需要调整
findEnclosingModule的终止判断逻辑即可,比如要获取包名就将终止条件改为symbol.isPackageDef - 如果调用宏的位置在局部方法、匿名类内部,上述实现会返回最接近的外层封闭类全限定名,符合常规使用需求
内容的提问来源于stack exchange,提问作者Felix
相关产品推荐
相关产品推荐

