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

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

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

import scala.annotation.unused
import scala.quoted.Quotes

private[dottyreflection] trait ReflectionUtil { this: InspectorBase =>

  import qctx.reflect._

  protected def flattenAnd(tpe: TypeRepr): List[TypeRepr] =
    tpe.dealias match {
      case AndType(lhs, rhs) => flattenAnd(lhs) ++ flattenAnd(rhs)
      case _ => List(tpe)
    }

  protected def flattenOr(tpe: TypeRepr): List[TypeRepr] =
    tpe.dealias match {
      case OrType(lhs, rhs) => flattenOr(lhs) ++ flattenOr(rhs)
      case _ => List(tpe)
    }

  protected def intersectionUnionClassPartsOf(tpe: TypeRepr): List[TypeRepr] = {
    tpe.dealias match {
      case AndType(lhs, rhs) =>
        intersectionUnionClassPartsOf(lhs) ++ intersectionUnionClassPartsOf(rhs)
      case OrType(lhs, rhs) =>
        intersectionUnionClassPartsOf(lhs) ++ intersectionUnionClassPartsOf(rhs)
      case refinement: Refinement =>
        intersectionUnionClassPartsOf(refinement.parent)
      case _ =>
        List(tpe)
    }
  }

  protected def allPartsStrong(typeRepr: TypeRepr): Boolean = {
    ReflectionUtil.allPartsStrong(using qctx)(shift, Set.empty, typeRepr)
  }

  import ReflectionUtil.reflectiveUncheckedNonOverloadedSelectable

  extension (typeRef: TypeRef | ParamRef) {
    protected def _underlying: TypeRepr = {
      // This works as a substitution for `TypeRef#underlying` call,
      // but I'm not sure if it's a reliable substitution.

//      typeRef.typeSymbol.owner._typeRef.memberType(typeRef.typeSymbol)

      // No, It's not a reliable substitution. When used on a TypeParamRef it returns Any instead of the underlying TypeBounds
      // https://github.com/lampepfl/dotty/issues/15799

//      val underlying = typeRef
//        .getClass.getMethods.collect { case m if m.getName == "underlying" => m }.head.invoke(
//          typeRef,
//          qctx.getClass.getMethods.collect { case m if m.getName == "ctx" => m }.head.invoke(qctx)
//        )
//      underlying.asInstanceOf[TypeRepr]

      typeRef.asInstanceOf[InternalTypeRefOrParamRef].underlying(using qctx._ctx)
    }
  }

  extension (typeRepr: TypeRepr) {
    protected def _declaredVariancesIfHKTypeLambda: Option[List[Flags]] = {
      try {
        Some(typeRepr.asInstanceOf[InternalHKTypeLambda].declaredVariances)
      } catch {
        case _: NoSuchMethodException => None
      }
    }
  }

  extension (qctx: Quotes) {
    def _ctx: InternalContext = qctx.asInstanceOf[{ def ctx: InternalContext }].ctx
  }

  type InternalTypeRefOrParamRef = {
    def underlying(using InternalContext): TypeRepr
  }

  type InternalHKTypeLambda = {
    def declaredVariances: List[Flags]
  }

  opaque type InternalContext = Any

}

object ReflectionUtil {

  inline implicit def reflectiveUncheckedNonOverloadedSelectable(x: Any): UncheckedNonOverloadedSelectable = new UncheckedNonOverloadedSelectable(x)

  /**
    * Returns true if the given type contains no type parameters
    * (this means the type is not "weak" https://stackoverflow.com/questions/29435985/weaktypetag-v-typetag)
    */
  private[reflect] def allPartsStrong(using qctx: Quotes)(shift: Int, lambdas: Set[qctx.reflect.TypeRepr], typeRepr: qctx.reflect.TypeRepr): Boolean = {
    import qctx.reflect.*
    typeRepr.dealias match {
      case x if topLevelWeakType(lambdas, x) => false
      case AppliedType(tpe, args) => allPartsStrong(shift, lambdas, tpe) && args.forall(allPartsStrong(shift, lambdas, _))
      case AndType(lhs, rhs) => allPartsStrong(shift, lambdas, lhs) && allPartsStrong(shift, lambdas, rhs)
      case OrType(lhs, rhs) => allPartsStrong(shift, lambdas, lhs) && allPartsStrong(shift, lambdas, rhs)
      case TypeRef(tpe, _) => allPartsStrong(shift, lambdas, tpe)
      case TermRef(tpe, _) => allPartsStrong(shift, lambdas, tpe)
      case ThisType(tpe) => allPartsStrong(shift, lambdas, tpe)
      case NoPrefix() => true
      case TypeBounds(lo, hi) => allPartsStrong(shift, lambdas, lo) && allPartsStrong(shift, lambdas, hi)
      case lam @ TypeLambda(_, _, body) => allPartsStrong(shift, lambdas + lam, body)
      case strange =>
        InspectorBase.log(shift, s"Got unknown type component when checking strength: $strange")
        true
    }
  }

  private[reflect] def topLevelWeakType(using qctx: Quotes)(lambdas: Set[qctx.reflect.TypeRepr], typeRepr: qctx.reflect.TypeRepr): Boolean = {
    import qctx.reflect.*
    typeRepr match {
      case x if x.typeSymbol.isTypeParam =>
        x match {
          case t: ParamRef if lambdas.contains(t.binder) => false
          case _ => true
        }
      // we regard abstract types like T in trait X { type T; Tag[this.T] } - when we are _inside_ the definition template
      // as 'type parameters' too. So that you could define `implicit def tagForT: Tag[this.T]` and the tag would be resolved
      // to this implicit correctly, instead of generating a useless `X::this.type::T` tag.
      case x @ TypeRef(ThisType(_), _) if x.typeSymbol.isAbstractType && !x.typeSymbol.isClassDef => true
      case _ => false
    }
  }

  final class UncheckedNonOverloadedSelectable(private val selectable: Any) extends AnyVal with Selectable {

    inline def selectDynamic(name: String): Any = {
      applyDynamic(name)()
    }

    def applyDynamic(name: String, @unused paramTypes: Class[_]*)(args: Any*): Any = {
      val cls = selectable.getClass
      val method = {
        if (args.isEmpty) {
          cls.getMethod(name)
        } else {
          cls.getMethods.collectFirst { case m if m.getName == name => m } match {
            case Some(m) => m
            case None => throw new NoSuchMethodException(s"No method named `$name` found in class `$cls`")
          }
        }
      }
      method.invoke(selectable, args*)
    }

  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy