You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)

报错原因

  1. 类型冲突:初始代码同时导入了底层dotty.tools.dotc.ast.Trees的PackageDef和反射层tasty.quotes.reflect.*的类型,导致模式匹配时类型不兼容。
  2. 未过滤非目标节点: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)

代码简化建议

  1. 去掉@unchecked注解:反射层类型支持安全模式匹配,无需手动添加该注解。
  2. 移除hack式上下文注入:原修改代码中手动转换Quotes获取Context的操作完全多余,反射层API已自动处理上下文。
  3. 简化遍历逻辑:用foreach替代collect后再遍历,逻辑更直观,同时明确忽略无用节点。
  4. 省略无用参数:模式匹配中用_代替不需要的变量(如构造函数、父类定义等),精简代码。

内容的提问来源于stack exchange,提问作者kostas.kougios

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 17:15:27