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

anorm.macros.ImplicitResolver.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) from 2022 The Play Framework Contributors , 2011-2021 Lightbend Inc. 
 */

package anorm.macros

import scala.reflect.macros.whitebox

import anorm.Macro.Placeholder

object ImplicitResolver {

  /**
   * @param boundTypes per each symbol of the type parameters,
   * which type is bound to
   */
  def apply(c: whitebox.Context)(
      tpe: c.Type,
      boundTypes: Map[String, c.Type],
      forwardName: c.TermName
  ): Function3[c.Name, c.Type, c.Type, Implicit[c.Type, c.Name, c.Tree]] = {
    import c.universe._

    // The placeholder type
    val PlaceholderType: Type = typeOf[Placeholder]

    new Function3[Name, Type, Type, Implicit[Type, Name, Tree]] {

      /**
       * @param name the name of the field
       * @param ptype the type of the field
       * @param typeclass the type of the higher kinded typeclass (e.g. `Column[_]`)
       */
      def apply(name: Name, ptype: Type, typeclass: Type): Implicit[Type, Name, Tree] = {
        val (ntpe, selfRef) = normalized(ptype)
        val ptpe            = boundTypes.getOrElse(ntpe.typeSymbol.fullName, ntpe)

        // infers implicit
        val neededImplicitType = appliedType(typeclass, ptpe)
        val neededImplicit = if (!selfRef) {
          c.inferImplicitValue(neededImplicitType)
        } else
          c.untypecheck(
            // Reset the type attributes on the refactored tree for the implicit
            ImplicitTransformer.transform(c.inferImplicitValue(neededImplicitType))
          )

        Implicit(name, ptype, neededImplicit, tpe, selfRef)
      }

      /* Refactor the input types, by replacing any type matching the `filter`,
       * by the given `replacement`.
       */
      @annotation.tailrec
      private def refactor(
          in: List[Type],
          base: TypeSymbol,
          out: List[Type],
          tail: List[(List[Type], TypeSymbol, List[Type])],
          filter: Type => Boolean,
          replacement: Type,
          altered: Boolean
      ): (Type, Boolean) = in match {
        case itpe :: ts =>
          boundTypes.getOrElse(itpe.typeSymbol.fullName, itpe) match {
            case t if filter(t) =>
              refactor(ts, base, replacement :: out, tail, filter, replacement, true)

            case TypeRef(_, sym, as) if as.nonEmpty =>
              refactor(as, sym.asType, List.empty, (ts, base, out) :: tail, filter, replacement, altered)

            case t => refactor(ts, base, t :: out, tail, filter, replacement, altered)
          }

        case _ => {
          val itpe = appliedType(base, out.reverse)

          tail match {
            case (x, y, more) :: ts =>
              refactor(x, y, itpe :: more, ts, filter, replacement, altered)

            case _ => itpe -> altered
          }
        }
      }

      /**
       * Replaces any reference to the type itself by the Placeholder type.
       * @return the normalized type + whether any self reference has been found
       */
      private def normalized(ptype: Type): (Type, Boolean) =
        boundTypes.getOrElse(ptype.typeSymbol.fullName, ptype) match {
          case t if t =:= tpe =>
            PlaceholderType -> true

          case TypeRef(_, sym, args) if args.nonEmpty =>
            refactor(args, sym.asType, List.empty, List.empty, _ =:= tpe, PlaceholderType, false)

          case t => t -> false
        }

      /* Restores reference to the type itself when Placeholder is found. */
      private def denormalized(ptype: Type): Type = ptype match {
        case PlaceholderType => tpe

        case TypeRef(_, sym, args) =>
          refactor(args, sym.asType, List.empty, List.empty, _ == PlaceholderType, tpe, false)._1

        case _ => ptype
      }

      private object ImplicitTransformer extends Transformer {
        override def transform(tree: Tree): Tree = tree match {
          case tt: TypeTree =>
            super.transform(TypeTree(denormalized(tt.tpe)))

          case Select(Select(This(TypeName("Macro")), t), sym)
              if t.toString == "Placeholder" && sym.toString == "Parser" =>
            super.transform(q"$forwardName")

          case _ =>
            super.transform(tree)
        }
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy