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

caseapp.util.AnnotationListMacros.scala Maven / Gradle / Ivy

The newest version!
package caseapp.util

import shapeless._

import scala.reflect.macros.whitebox

class AnnotationListMacros(val c: whitebox.Context) extends CaseClassMacros {
  import c.universe._

  def consTpe = typeOf[scala.::[_]].typeConstructor
  def nilTpe  = typeOf[Nil.type]

  // FIXME Most of the content of this method is cut-n-pasted from generic.scala
  def construct(tpe: Type): List[Tree] => Tree = {
    // FIXME Cut-n-pasted from generic.scala
    val sym         = tpe.typeSymbol
    val isCaseClass = sym.asClass.isCaseClass
    def hasNonGenericCompanionMember(name: String): Boolean = {
      val mSym = sym.companion.typeSignature.member(TermName(name))
      mSym != NoSymbol && !isNonGeneric(mSym)
    }

    if (isCaseClass || hasNonGenericCompanionMember("apply"))
      args => q"${companionRef(tpe)}(..$args)"
    else
      args => q"new $tpe(..$args)"
  }

  def materializeAnnotationList[A: WeakTypeTag, T: WeakTypeTag, Out: WeakTypeTag]: Tree = {
    val annTpe = weakTypeOf[A]

    if (!isProduct(annTpe))
      abort(s"$annTpe is not a case class-like type")

    val construct0 = construct(annTpe)

    val tpe = weakTypeOf[T]

    val annTreeLists =
      if (isProduct(tpe)) {
        val constructorSyms = tpe
          .member(termNames.CONSTRUCTOR)
          .asMethod
          .paramLists
          .flatten
          .map(sym => nameAsString(sym.name) -> sym)
          .toMap

        val fields = fieldsOf(tpe)
        // Looking at these unveils extra annotations below
        // (what we call a "side effect")
        fields.map { case (name, _) =>
          tpe.member(name).annotations
        }

        fields.map { case (name, _) =>
          val paramConstrSym = constructorSyms(nameAsString(name))

          paramConstrSym.annotations.collect {
            case ann if ann.tree.tpe =:= annTpe => construct0(ann.tree.children.tail)
          }
        }
      }
      else if (isCoproduct(tpe))
        ctorsOf(tpe).map { cTpe =>
          cTpe.typeSymbol.annotations.collect {
            case ann if ann.tree.tpe =:= annTpe => construct0(ann.tree.children.tail)
          }
        }
      else
        abort(s"$tpe is not case class like or the root of a sealed family of types")

    val wrapTpeTrees = annTreeLists.map {
      case Nil => nilTpe -> q"_root_.scala.Nil"
      case l =>
        def listTree(trees: List[Tree]): Tree = {
          import scala.::
          trees match {
            case Nil    => q"_root_.scala.Nil"
            case h :: t => q"_root_.scala.::($h, ${listTree(t)})"
          }
        }

        appliedType(consTpe, annTpe) -> listTree(l)
    }

    val outTpe = mkHListTpe(wrapTpeTrees.map { case (aTpe, _) => aTpe })
    val outTree = wrapTpeTrees.foldRight(q"_root_.shapeless.HNil": Tree) {
      case ((_, bound), acc) => pq"_root_.shapeless.::($bound, $acc)"
    }

    q"_root_.caseapp.util.AnnotationList.instance[$annTpe, $tpe, $outTpe]($outTree)"
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy