scala.meta.internal.quasiquotes.ConversionMacros.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of parsers_native0.5_2.13 Show documentation
Show all versions of parsers_native0.5_2.13 Show documentation
Scalameta APIs for parsing and their baseline implementation
package scala.meta
package internal
package quasiquotes
import scala.meta.internal.trees.{Reflection => AstReflection}
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context
// NOTE: we don't have the signature as [O, I] to keep symmetry with Unlift
object Lift {
def apply[I](outside: Any): I = macro ConversionMacros.liftApply[I]
def unapply[I](outside: Any): Option[I] = macro ConversionMacros.liftUnapply[I]
}
// NOTE: here we can't have the signature be [I, O], because we never know I
// in the case of Unlift.apply, we've just assembled the reified result and don't know its type yet
// in the case of Unlift.unapply, we only know the expected type of the unquote, not its actual type
// it would be nice if Scala supported partially provided type argument lists
object Unlift {
def apply[O](inside: Any): O = macro ConversionMacros.unliftApply[O]
def unapply[O](inside: Any): Option[O] = macro ConversionMacros.unliftUnapply[O]
}
class ConversionMacros(val c: Context) extends AstReflection {
lazy val u: c.universe.type = c.universe
lazy val mirror: u.Mirror = c.mirror
val XtensionQuasiquoteTerm = "shadow scala.meta quasiquotes"
import u._
val MetaLift = mirror.staticClass("scala.meta.quasiquotes.Lift")
val MetaUnlift = mirror.staticClass("scala.meta.quasiquotes.Unlift")
private def typeMismatchMessage(found: c.Type, req: c.Type): String = {
val g = c.universe.asInstanceOf[scala.tools.nsc.Global]
val msg = g.analyzer.foundReqMsg(found.asInstanceOf[g.Type], req.asInstanceOf[g.Type])
val foundReqMessage = msg.replace("meta.", "scala.meta.").replace("scala.scala.", "scala.")
"type mismatch when unquoting" + foundReqMessage
}
def liftApply[I](outside: c.Tree)(implicit I: c.WeakTypeTag[I]): c.Tree = {
val outsideTpe = outside.tpe
val insideTpe = I.tpe
if (outsideTpe <:< insideTpe) outside
else {
val liftable = c
.inferImplicitValue(appliedType(MetaLift, outsideTpe, insideTpe), silent = true)
if (liftable.nonEmpty) q"$liftable.apply($outside)"
else c.abort(c.enclosingPosition, typeMismatchMessage(outsideTpe, insideTpe))
}
}
def liftUnapply[I](outside: c.Tree)(implicit I: c.WeakTypeTag[I]): c.Tree =
// NOTE: Here's an interesting idea that I'd like to explore.
// How about we allow things like `42 match { case q"$x" => x }`?
// For that to work, we just need to wrap the reification result into `Lift.unapply`!
???
def unliftApply[O](inside: c.Tree)(implicit O: c.WeakTypeTag[O]): c.Tree =
// NOTE: here we just disregard the expected outside type, because I can't find uses for it
// duality is a fun thing, but it looks like here it just led me into a dead-end
q"$inside"
def unliftUnapply[O](inside: c.Tree)(implicit O: c.WeakTypeTag[O]): c.Tree = {
val insideTpe = inside.tpe
val outsideTpe = O.tpe
if (insideTpe <:< outsideTpe) q"_root_.scala.Some($inside: $insideTpe)"
else {
val unliftable = c
.inferImplicitValue(appliedType(MetaUnlift, insideTpe, outsideTpe), silent = true)
if (unliftable.nonEmpty) q"$unliftable.apply($inside)"
else c.abort(c.enclosingPosition, typeMismatchMessage(insideTpe, outsideTpe))
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy