All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scala.meta.internal.trees.Reflection.scala Maven / Gradle / Ivy

Go to download

Bag of private and public helpers used in scala.meta's APIs and implementations

The newest version!
package scala.meta
package internal
package trees

import org.scalameta.adt.{Reflection => AdtReflection}

import scala.annotation.tailrec
import scala.collection.mutable
import scala.language.implicitConversions

trait Reflection extends AdtReflection {
  import u.Flag._
  import u._
  import u.internal._
  import u.internal.decorators._

  def Protected: Modifiers = Modifiers(PROTECTED)
  def PrivateMeta: Modifiers = PrivateMeta(NoFlags)
  def PrivateMeta(flags: FlagSet): Modifiers = Modifiers(flags, TypeName("meta"), Nil)

  lazy val TreeSymbol = mirror.staticClass("scala.meta.Tree")
  lazy val QuasiSymbol = mirror.staticClass("scala.meta.internal.trees.Quasi")
  lazy val AllModule = mirror.staticModule("scala.meta.internal.trees.All")
  lazy val RegistryAnnotation = mirror.staticModule("scala.meta.internal.trees.Metadata").info
    .member(TypeName("registry")).asClass

  override protected def figureOutDirectSubclasses(sym: ClassSymbol): List[Symbol] = {
    def fail = sys.error(s"failed to figure out direct subclasses for ${sym.fullName}")
    if (sym.isSealed) sym.knownDirectSubclasses.toList.sortBy(_.fullName)
    else if (sym.baseClasses.contains(TreeSymbol)) scalaMetaRegistry.getOrElse(sym, fail)
    else fail
  }

  // NOTE: this is supposed to map root/branch/ast classes to their direct subclasses
  private lazy val scalaMetaRegistry: Map[Symbol, List[Symbol]] =
    AllModule.initialize.annotations match {
      case List(ann) if ann.tree.tpe =:= RegistryAnnotation.toType =>
        val q"new $_($_.$_[..$_](..${astPaths: List[String]}))" = ann.tree
        val astClasses = astPaths.map { astPath =>
          @tailrec
          def locateModule(root: ModuleSymbol, parts: List[String]): ModuleSymbol = parts match {
            case Nil => root
            case head :: rest => locateModule(root.info.member(TermName(head)).asModule, rest)
          }
          val modulePath :+ className = astPath.split('.').toList
          locateModule(mirror.RootPackage, modulePath).info.member(TypeName(className)).asClass
        }
        val entireHierarchy = {
          var result = astClasses.flatMap(_.baseClasses.map(_.asClass))
          result = result.filter(sym => sym.toType <:< TreeSymbol.toType)
          result = result
            .flatMap(sym => List(sym, sym.companion.info.member(TypeName("Quasi")).asClass))
          result :+= QuasiSymbol
          result.distinct
        }
        val registry = mutable.Map[Symbol, List[Symbol]]()
        entireHierarchy.foreach(sym => registry(sym) = Nil)
        entireHierarchy.foreach { sym =>
          val parents = sym.info.asInstanceOf[ClassInfoType].parents.map(_.typeSymbol)
          val relevantParents = parents
            .filter(p => p.isClass && p.asClass.baseClasses.contains(TreeSymbol))
          relevantParents.foreach(parent => registry(parent) :+= sym)
        }
        registry.toMap
      case _ => sys.error("failed to figure out meta trees")
    }

  implicit class XtensionAstTree(tree: Tree) {
    def detectAst: List[String] = {
      object astClassDetector extends Traverser {
        val result = scala.collection.mutable.ListBuffer[String]()
        var module = "_root_"
        var inner = false
        trait Path
        object Path {
          private def path(s: String): Path = new Path {
            override def toString = s
          }
          implicit def nameToPath(name: Name): Path = path(name.decodedName.toString)
          implicit def reftreeToPath(tree: RefTree): Path = path(tree.toString)
        }
        def drilldown[T](current: Path, inner: Boolean)(op: => T) = {
          val savedModule = this.module
          val savedInner = this.inner
          this.module = module + "." + current
          this.inner = inner
          val result = op
          this.inner = savedInner
          this.module = savedModule
          result
        }
        override def traverse(tree: Tree): Unit = tree match {
          case PackageDef(pid, stats) =>
            if (pid.name == termNames.EMPTY_PACKAGE_NAME) super.traverse(tree)
            else drilldown(pid, inner = false)(super.traverse(tree))
          case ModuleDef(_, name, _) => drilldown(name, inner = false)(super.traverse(tree))
          case ClassDef(Modifiers(_, _, anns), name, _, impl) =>
            if (anns.exists(_.toString == "new ast()")) {
              if (inner) sys.error("@ast classes can't be inner: " + name)
              val q"$_ class $_[..$_] $_(...$paramss) extends { ..$_ } with ..$parents { $_ => ..$_ }" =
                tree
              drilldown(name, inner = true)(result += module)
            }
            drilldown(name, inner = true)(super.traverse(tree))
          case _ => super.traverse(tree)
        }
      }
      astClassDetector.traverse(tree)
      astClassDetector.result.toList
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy