Defn.Object的父级的scala.meta父级

rseugnpd  于 2022-11-09  发布在  Scala
关注(0)|答案(1)|浏览(123)

它的层次结构如下:

object X extends Y{
...
}
trait Y extends Z {
...
}
trait Z {
  def run(): Unit
}

我解析了包含X的Scala文件
我想知道它的父代还是祖辈是Z
我可以如下检查父类:假设x: Defn.Object是我解析的X类,

x
.children.collect { case c: Template => c }
.flatMap(p => p.children.collectFirst { case c: Init => c }

将给Y

**问题:**知道如何获取X(上例中为Z)父对象的父对象吗?

加载Y(与我加载X的方式相同)并找到它的父类似乎不是一个好主意,因为上面是扫描过程的一部分,在src/main/scala下的所有文件中,我试图找到扩展Z并实现run的所有类,所以我看不到一种简单而有效的方法来创建一个包含所有中间类的图,以便以正确的顺序加载它们并检查它们的父类。

pxiryf3j

pxiryf3j1#

您似乎希望Scalameta不是在语法上而是在语义上处理您的源代码。那么你需要SemanticDB。可能最方便的使用Semancdb的方法是Scalafix

  • 规则/src/main/scala/MyRule.scala*
import scalafix.v1._
import scala.meta._

class MyRule extends SemanticRule("MyRule") {
  override def isRewrite: Boolean = true
  override def description: String = "My Rule"

  override def fix(implicit doc: SemanticDocument): Patch = {
    doc.tree.traverse {
      case q"""..$mods object $ename extends ${template"""
        { ..$stats } with ..$inits { $self => ..$stats1 }"""}""" =>
        val initsParents = inits.collect(_.symbol.info.map(_.signature) match {
          case Some(ClassSignature(_, parents, _, _)) => parents
        }).flatten
        println(s"object: $ename, parents: $inits, grand-parents: $initsParents")
    }

    Patch.empty
  }
}
  • 位于/src/main/scala/App.scala中*
object X extends Y{
  override def run(): Unit = ???
}

trait Y extends Z {
}

trait Z {
  def run(): Unit
}

sbt out/compile的输出

object: X, parents: List(Y), grand-parents: List(AnyRef, Z)
  • Build.sbt*
name := "scalafix-codegen"

inThisBuild(
  List(
    //scalaVersion := "2.13.2",
    scalaVersion := "2.11.12",
    addCompilerPlugin(scalafixSemanticdb),
    scalacOptions ++= List(
      "-Yrangepos"
    )
  )
)

lazy val rules = project
  .settings(
    libraryDependencies += "ch.epfl.scala" %% "scalafix-core" % "0.9.16",
    organization := "com.example",
    version := "0.1",
  )

lazy val in = project

lazy val out = project
  .settings(    
    sourceGenerators.in(Compile) += Def.taskDyn {
      val root = baseDirectory.in(ThisBuild).value.toURI.toString
      val from = sourceDirectory.in(in, Compile).value
      val to = sourceManaged.in(Compile).value
      val outFrom = from.toURI.toString.stripSuffix("/").stripPrefix(root)
      val outTo = to.toURI.toString.stripSuffix("/").stripPrefix(root)
      Def.task {
        scalafix
          .in(in, Compile)
          .toTask(s" --rules=file:rules/src/main/scala/MyRule.scala --out-from=$outFrom --out-to=$outTo")
          .value
        (to**"*.scala").get
      }
    }.taskValue
  )
  • project/plugins.sbt*
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.16")

其他示例:
https://github.com/olafurpg/scalafix-codegen(语义)
https://github.com/DmytroMitin/scalafix-codegen(语义)
https://github.com/DmytroMitin/scalameta-demo(句法)
Is it possible to using macro to modify the generated code of structural-typing instance invocation?(语义)
Scala conditional compilation(语法)
Macro annotation to override toString of Scala function(语法)
How to merge multiple imports in scala?(句法)
您可以避免使用Scalafix,但这样您就必须手动使用SemancDB的内部

import scala.meta._
import scala.meta.interactive.InteractiveSemanticdb
import scala.meta.internal.semanticdb.{ClassSignature, Range, SymbolInformation, SymbolOccurrence, TypeRef}

val source: String =
  """object X extends Y{
    |  override def run(): Unit = ???
    |}
    |
    |trait Y extends Z
    |
    |trait Z {
    |  def run(): Unit
    |}""".stripMargin

val textDocument = InteractiveSemanticdb.toTextDocument(
  InteractiveSemanticdb.newCompiler(List(
    "-Yrangepos"
  )),
  source
)

implicit class TreeOps(tree: Tree) {
  val occurence: Option[SymbolOccurrence] = {
    val treeRange = Range(tree.pos.startLine, tree.pos.startColumn, tree.pos.endLine, tree.pos.endColumn)
    textDocument.occurrences
      .find(_.range.exists(occurrenceRange => treeRange == occurrenceRange))
  }

  val info: Option[SymbolInformation] = occurence.flatMap(_.symbol.info)
}

implicit class StringOps(symbol: String) {
  val info: Option[SymbolInformation] = textDocument.symbols.find(_.symbol == symbol)
}

source.parse[Source].get.traverse {
  case tree@q"""..$mods object $ename extends ${template"""
    { ..$stats } with ..$inits { $self => ..$stats1 }"""}""" =>
    val initsParents = inits.collect(_.info.map(_.signature) match {
      case Some(ClassSignature(_, parents, _, _)) =>
        parents.collect {
          case TypeRef(_, symbol, _) => symbol
        }
    }).flatten
    println(s"object = $ename = ${ename.info.map(_.symbol)}, parents = $inits = ${inits.map(_.info.map(_.symbol))}, grand-parents = $initsParents")
}

产出:

object = X = Some(_empty_/X.), parents = List(Y) = List(Some(_empty_/Y#)), grand-parents = List(scala/AnyRef#, _empty_/Z#)
  • Build.sbt*
//scalaVersion := "2.13.3"
scalaVersion := "2.11.12"

lazy val scalametaV = "4.3.18"
libraryDependencies ++= Seq(
  "org.scalameta" %% "scalameta" % scalametaV,
  "org.scalameta" % "semanticdb-scalac" % scalametaV cross CrossVersion.full
)

语义数据库代码似乎在Scala 3中工作正常
https://scastie.scala-lang.org/DmytroMitin/3QQwsDG2Rqm71qa6mMMkTw/36[copy](在SCastie -Dscala.usejavacp=trueobject scala.runtime in compiler mirror not found没有帮助,所以我使用Coursier来保证scala-library在路径上,在本地没有Coursier的情况下它可以工作)

相关问题