如何使用tasty inspector提取scala 3中的所有方法?

jpfvwuh4  于 2022-12-23  发布在  Scala
关注(0)|答案(1)|浏览(205)

我尝试使用一个tasty inspector将方法参数转换为case类,但是在运行时遇到了一个classcast异常。
我的代码:

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)

我对这个类运行这个代码(在我编译它并且编译器创建一个. tasty文件之后):

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)

但我得到了这个例外:

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)

导致此问题的行如下:

stats.collect { case TypeDef(typeName, Template(constr, parentsOrDerived, self, preBody: List[_])) =>

但是不应该这样,因为这是一个集合。错误是因为Cp1Exports中有一个导入而导致的。如果我删除导入,它就可以工作。
此外,任何建议,以简化代码将不胜感激。
我使用的是scala 3.2.1(包括该版本的scala编译器)
编辑:
好了,按照下面的建议,我结束了这个代码的工作(但似乎相当复杂):

import 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
      given dotty.tools.dotc.core.Contexts.Context = scala.quoted.quotes.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx
      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)

谢啦,谢啦

bejyjqdl

bejyjqdl1#

我注意到

stats.collect { case dotty.tools.dotc.ast.Trees.TypeDef(_, _) => }

不扔的时候

stats.collect { TypeDef(_, _) => }

akka

stats.collect { tasty.quotes.reflect.TypeDef(_, _) => }

确实如此。
区别似乎在于dotty.tools.dotc.ast.Trees.TypeDef是case类,而tasty.quotes.reflect.TypeDef是抽象类型。
原因似乎是类型擦除
Understanding why inlining function fixes runtime crash when collecting
复制更简单:

import scala.reflect.TypeTest

trait X {
  type A

  type B <: A
  trait BModule {
    def unapply(b: B): Option[Int]
  }
  val B: BModule
  given BTypeTest: TypeTest[A, B]

  type C <: A
  trait CModule {
    def unapply(c: C): Option[String]
  }
  val C: CModule
  given CTypeTest: TypeTest[A, C]
}

object XImpl extends X {
  sealed trait A

  case class B(i: Int) extends A
  object B extends BModule {
    def unapply(b: B): Option[Int] = Some(b.i)
  }
  object BTypeTest extends TypeTest[A, B] {
    override def unapply(x: A): Option[x.type & B] = x match {
      case x: (B & x.type) => Some(x)
      case _ => None
    }
  }

  case class C(s: String) extends A
  object C extends CModule {
    def unapply(c: C): Option[String] = Some(c.s)
  }
  object CTypeTest extends TypeTest[A, C] {
    override def unapply(x: A): Option[x.type & C] = x match {
      case x: (C & x.type) => Some(x)
      case _ => None
    }
  }
}

def foo()(using x: X) = {
  import x.*

  List(XImpl.B(1), XImpl.C("a")).collect { case C(s) => println(s) }
}

given X = XImpl

foo() // ClassCastException: XImpl$B cannot be cast to XImpl$C

在这里,XABCQuotesTreeValDefTypeDef类似。

相关问题