It seems you want Scalameta to process your sources not syntactically but semantically. Then you need SemanticDB. Probably the most convenient way to work with SemanticDB is Scalafix
rules/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
}
}
in/src/main/scala/App.scala
object X extends Y{
override def run(): Unit = ???
}
trait Y extends Z {
}
trait Z {
def run(): Unit
}
Output of 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")
Other examples:
https://github.com/olafurpg/scalafix-codegen (semantic)
https://github.com/DmytroMitin/scalafix-codegen (semantic)
https://github.com/DmytroMitin/scalameta-demo (syntactic)
Is it possible to using macro to modify the generated code of structural-typing instance invocation? (semantic)
Scala conditional compilation (syntactic)
Macro annotation to override toString of Scala function (syntactic)
How to merge multiple imports in scala? (syntactic)
You can avoid Scalafix but then you'll have to work with internals of SemanticDB manually
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")
}
Output:
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
)
Semanticdb code seems to be working in Scala 3
https://scastie.scala-lang.org/DmytroMitin/3QQwsDG2Rqm71qa6mMMkTw/36 [copy] (at Scastie -Dscala.usejavacp=true
didn't help with object scala.runtime in compiler mirror not found
, so I used Coursier to guarantee that scala-library
is on path, locally it works without Coursier)