Scala 3使用Tasty Inspector提取方法时遇类型转换异常
解决Tasty Inspector中的ClassCastException问题
问题重现
尝试用Tasty Inspector解析编译后的.tasty文件,将方法参数转换为case class定义,但遇到ClassCastException,报错提示Import无法转换为TypeDef。
目标待解析代码
package cp1 import java.time.LocalDate trait Cp1Exports: def add(a: Int, b: Int): Int def subtract(a: Int, b: Int): Int def friends(p: Person, from: LocalDate): Seq[Person] case class Person(id: Int, name: String)
初始解析代码
import dotty.tools.dotc.ast.Trees.{PackageDef, Template} import scala.quoted.* import scala.tasty.inspector.* class MyInspector extends Inspector: def inspect(using Quotes)(tastys: List[Tasty[quotes.type]]): Unit = for tasty <- tastys do import tasty.quotes.reflect.* tasty.ast match { case PackageDef(pid, stats) => stats.collect { case TypeDef(typeName, Template(constr, parentsOrDerived, self, preBody: List[_])) => preBody.collect { case DefDef(name, paramss: List[List[_]] @unchecked, tpt, preRhs) => val params = paramss.flatten.map { case ValDef(name, tpt, preRhs) => s"$name : ${tpt.show}" } println(s""" |case class ${typeName}_${name}_ccIn(${params.mkString(", ")}) |""".stripMargin) println("------------------------") } } } @main def tryit() = val tastyFiles = List("../example-commands/classpath-1/target/scala-3.2.1/classes/cp1/Cp1Exports.tasty") TastyInspector.inspectTastyFiles(tastyFiles)(new MyInspector)
报错信息
Exception in thread "main" java.lang.ClassCastException: class dotty.tools.dotc.ast.Trees$Import cannot be cast to class dotty.tools.dotc.ast.Trees$TypeDef (dotty.tools.dotc.ast.Trees$Import and dotty.tools.dotc.ast.Trees$TypeDef are in unnamed module of loader 'app') at scala.quoted.runtime.impl.QuotesImpl$reflect$TypeDef$.unapply(QuotesImpl.scala:339) at console.macros.MyInspector$$anon$1.applyOrElse(MyInspector.scala:15)
报错原因
- 类型冲突:初始代码同时导入了底层
dotty.tools.dotc.ast.Trees的PackageDef和反射层tasty.quotes.reflect.*的类型,导致模式匹配时类型不兼容。 - 未过滤非目标节点:
PackageDef的stats集合包含目标代码中import语句对应的Import节点,当collect的偏函数用TypeDef模式匹配这些节点时,底层unapply方法强制转换出错。
正确解决方案
移除对dotty.tools.dotc.ast.Trees的直接导入,完全使用反射层类型,同时明确过滤非目标节点:
import scala.quoted.* import scala.tasty.inspector.* class MyInspector extends Inspector: def inspect(using Quotes)(tastys: List[Tasty[quotes.type]]): Unit = for tasty <- tastys do import tasty.quotes.reflect.* tasty.ast match { case PackageDef(_, stats) => stats.foreach { // 只处理TypeDef节点 case TypeDef(typeName, Template(_, _, _, preBody)) => preBody.foreach { // 只处理DefDef节点 case DefDef(name, paramss, _, _) => val params = paramss.flatten.map { case ValDef(paramName, paramType, _) => s"$paramName : ${paramType.show}" } println(s""" |case class ${typeName}_${name}_ccIn(${params.mkString(", ")}) |""".stripMargin) println("------------------------") case _ => // 忽略非方法定义节点 } case _ => // 忽略非类型定义节点 } } @main def tryit() = val tastyFiles = List("../example-commands/classpath-1/target/scala-3.2.1/classes/cp1/Cp1Exports.tasty") TastyInspector.inspectTastyFiles(tastyFiles)(new MyInspector)
代码简化建议
- 去掉@unchecked注解:反射层类型支持安全模式匹配,无需手动添加该注解。
- 移除hack式上下文注入:原修改代码中手动转换
Quotes获取Context的操作完全多余,反射层API已自动处理上下文。 - 简化遍历逻辑:用
foreach替代collect后再遍历,逻辑更直观,同时明确忽略无用节点。 - 省略无用参数:模式匹配中用
_代替不需要的变量(如构造函数、父类定义等),精简代码。
内容的提问来源于stack exchange,提问作者kostas.kougios
相关产品推荐
相关产品推荐

