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

izumi.reflect.dottyreflection.FullDbInspector.scala Maven / Gradle / Ivy

There is a newer version: 2.3.10
Show newest version
package izumi.reflect.dottyreflection

import izumi.reflect.internal.fundamentals.collections.IzCollections.toRich
import izumi.reflect.macrortti.LightTypeTagRef
import izumi.reflect.macrortti.LightTypeTagRef._

import scala.collection.mutable
import scala.quoted._

abstract class FullDbInspector(protected val shift: Int) extends InspectorBase {
  import qctx.reflect._

  private lazy val inspector = new Inspector(0) { val qctx: FullDbInspector.this.qctx.type = FullDbInspector.this.qctx }

  def buildFullDb[T <: AnyKind: Type]: Map[AbstractReference, Set[AbstractReference]] = {
    new Run()
      .inspectTypeReprToFullBases(TypeRepr.of[T])
      .distinct
      .toMultimap
      .map {
        case (t, parents) =>
          t -> parents.filterNot(_ == t)
      }
      .filterNot(_._2.isEmpty)
  }

  class Run() {
    private val termination = mutable.HashSet.empty[TypeRepr]

    def inspectTypeReprToFullBases(tpe0: TypeRepr): List[(AbstractReference, AbstractReference)] = {
      val tpe = tpe0.dealias.simplified
      lazy val selfRef = inspector.inspectTypeRepr(tpe) // FIXME duplicate work for top-level type

      tpe match {
        case t if ignored(t) =>
          Nil

        case a: AppliedType =>
          extractBase(a, selfRef, recurseIntoBases = false)

        case l: TypeLambda =>
          val parents = inspectTypeBoundsToFull(l.resType)
          val selfL = selfRef.asInstanceOf[LightTypeTagRef.Lambda]
          val out = parents.map {
            case (c, p) =>
              if (c == selfL.output) {
                (selfL, p)
              } else {
                (c, p)
              }
          }
          out.distinct

        case a: AndType =>
          inspectTypeReprToFullBases(a.left) ++ inspectTypeReprToFullBases(a.right)

        case o: OrType =>
          inspectTypeReprToFullBases(o.left) ++ inspectTypeReprToFullBases(o.right)

        case typeRef: TypeRef =>
          processSymbol(typeRef, selfRef)

        case paramRef: ParamRef =>
          processSymbol(paramRef, selfRef)

        case termRef: TermRef =>
          extractBase(termRef, selfRef, false)

        case b: TypeBounds =>
          inspectTypeReprToFullBases(b.hi) ++ inspectTypeReprToFullBases(b.low)

        case c: ConstantType =>
          extractBase(c, selfRef, false)

        case t: ThisType =>
          inspectTypeReprToFullBases(t.tref)

        case other =>
          log(s"FullDbInspector: UNSUPPORTED: $other")
          extractBase(other, selfRef, false)
      }
    }

    private def processSymbol(r: TypeRef | ParamRef, selfRef: AbstractReference): List[(AbstractReference, AbstractReference)] = {
      r.typeSymbol match {
        case s if s.isClassDef =>
          extractBase(r, selfRef, recurseIntoBases = true)

        case s if s.isTypeDef =>
          inspectTypeReprToFullBases(r._underlying)

        case o =>
          throw new RuntimeException(s"Shit tree: ${o.getClass} $o $r ${o.tree}")
      }
    }

    private def extractBase(tpe: TypeRepr, selfRef: AbstractReference, recurseIntoBases: Boolean): List[(AbstractReference, AbstractReference)] = {
      val baseTypes = tpe.baseClasses.iterator.map(tpe.baseType).filterNot(ignored).filterNot(termination).toList
      log(s"For `$tpe` found base types $baseTypes")

      val recursiveParentBases = if (recurseIntoBases) {
        baseTypes.filterNot(_ == tpe).flatMap {
          t =>
            inspectTypeReprToFullBases(t)
        }
      } else {
        List.empty
      }
      val main = recursiveParentBases ++ baseTypes.map {
        bt =>
          val parentRef = inspector.inspectTypeRepr(bt)
          (selfRef, parentRef)
      }

      val typeArgs: List[TypeRepr] = tpe match {
        case a: AppliedType =>
          a.args
        case _ =>
          val m = qctx.reflect.TypeReprMethods
          val mm = m.getClass.getMethods.collect { case m if m.getName == "typeArgs" => m }.head
          val out = mm.invoke(m, tpe).asInstanceOf[List[TypeRepr]]
          out
      }

      val argInheritance = typeArgs.filterNot(termination.contains).flatMap {
        x =>
          termination.add(x)
          inspectTypeBoundsToFull(x)
      }

      (main ++ argInheritance).distinct
    }

    private def inspectTypeBoundsToFull(tpe: TypeRepr): List[(AbstractReference, AbstractReference)] = {
      tpe.dealias match {
        case t: TypeBounds =>
          inspectTypeReprToFullBases(t.hi) ++ inspectTypeReprToFullBases(t.low)
        case t: TypeRepr =>
          inspectTypeReprToFullBases(t)
      }
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy