summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/tools/nsc/plugins/Plugin.scala93
-rw-r--r--src/compiler/scala/tools/nsc/plugins/PluginLoadException.scala15
-rw-r--r--src/compiler/scala/tools/nsc/plugins/Plugins.scala21
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala4
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Holes.scala280
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala7
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala41
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala183
8 files changed, 344 insertions, 300 deletions
diff --git a/src/compiler/scala/tools/nsc/plugins/Plugin.scala b/src/compiler/scala/tools/nsc/plugins/Plugin.scala
index d194c095f8..183752d4a2 100644
--- a/src/compiler/scala/tools/nsc/plugins/Plugin.scala
+++ b/src/compiler/scala/tools/nsc/plugins/Plugin.scala
@@ -12,7 +12,8 @@ import scala.reflect.io.{ Directory, File, Path }
import java.io.InputStream
import java.util.zip.ZipException
-import scala.collection.mutable.ListBuffer
+import scala.collection.mutable
+import mutable.ListBuffer
import scala.util.{ Try, Success, Failure }
/** Information about a plugin loaded from a jar file.
@@ -99,7 +100,7 @@ object Plugin {
private def loadDescriptionFromJar(jarp: Path): Try[PluginDescription] = {
// XXX Return to this once we have more ARM support
def read(is: Option[InputStream]) = is match {
- case None => throw new RuntimeException(s"Missing $PluginXML in $jarp")
+ case None => throw new PluginLoadException(jarp.path, s"Missing $PluginXML in $jarp")
case Some(is) => PluginDescription.fromXML(is)
}
Try(new Jar(jarp.jfile).withEntryStream(PluginXML)(read))
@@ -113,11 +114,14 @@ object Plugin {
/** Use a class loader to load the plugin class.
*/
def load(classname: String, loader: ClassLoader): Try[AnyClass] = {
- Try[AnyClass] {
- loader loadClass classname
- } recoverWith {
- case _: Exception =>
- Failure(new RuntimeException(s"Warning: class not found: ${classname}"))
+ import scala.util.control.NonFatal
+ try {
+ Success[AnyClass](loader loadClass classname)
+ } catch {
+ case NonFatal(e) =>
+ Failure(new PluginLoadException(classname, s"Error: unable to load class: $classname"))
+ case e: NoClassDefFoundError =>
+ Failure(new PluginLoadException(classname, s"Error: class not found: ${e.getMessage} required by $classname"))
}
}
@@ -128,33 +132,54 @@ object Plugin {
* A single classloader is created and used to load all of them.
*/
def loadAllFrom(
- jars: List[Path],
+ paths: List[List[Path]],
dirs: List[Path],
ignoring: List[String]): List[Try[AnyClass]] =
{
- // List[(jar, Success(descriptor))] in dir
- def scan(d: Directory) = for {
- f <- d.files.toList sortBy (_.name)
- if Jar isJarOrZip f
- pd = loadDescriptionFromJar(f)
- if pd.isSuccess
- } yield (f, pd)
- // (dir, Try(descriptor))
- def explode(d: Directory) = d -> loadDescriptionFromFile(d / PluginXML)
- // (j, Try(descriptor))
- def required(j: Path) = j -> loadDescriptionFromJar(j)
-
- type Paired = Tuple2[Path, Try[PluginDescription]]
- val included: List[Paired] = (dirs flatMap (_ ifDirectory scan)).flatten
- val exploded: List[Paired] = jars flatMap (_ ifDirectory explode)
- val explicit: List[Paired] = jars flatMap (_ ifFile required)
- def ignored(p: Paired) = p match {
- case (path, Success(pd)) => ignoring contains pd.name
- case _ => false
+ // List[(jar, Try(descriptor))] in dir
+ def scan(d: Directory) =
+ d.files.toList sortBy (_.name) filter (Jar isJarOrZip _) map (j => (j, loadDescriptionFromJar(j)))
+
+ type PDResults = List[Try[(PluginDescription, ScalaClassLoader)]]
+
+ // scan plugin dirs for jars containing plugins, ignoring dirs with none and other jars
+ val fromDirs: PDResults = dirs filter (_.isDirectory) flatMap { d =>
+ scan(d.toDirectory) collect {
+ case (j, Success(pd)) => Success((pd, loaderFor(Seq(j))))
+ }
+ }
+
+ // scan jar paths for plugins, taking the first plugin you find.
+ // a path element can be either a plugin.jar or an exploded dir.
+ def findDescriptor(ps: List[Path]) = {
+ def loop(qs: List[Path]): Try[PluginDescription] = qs match {
+ case Nil => Failure(new MissingPluginException(ps))
+ case p :: rest =>
+ if (p.isDirectory) loadDescriptionFromFile(p.toDirectory / PluginXML)
+ else if (p.isFile) loadDescriptionFromJar(p.toFile)
+ else loop(rest)
+ }
+ loop(ps)
+ }
+ val fromPaths: PDResults = paths map (p => (p, findDescriptor(p))) map {
+ case (p, Success(pd)) => Success((pd, loaderFor(p)))
+ case (_, Failure(e)) => Failure(e)
}
- val (locs, pds) = ((explicit ::: exploded ::: included) filterNot ignored).unzip
- val loader = loaderFor(locs.distinct)
- (pds filter (_.isSuccess) map (_.get.classname)).distinct map (Plugin load (_, loader))
+
+ val seen = mutable.HashSet[String]()
+ val enabled = (fromPaths ::: fromDirs) map {
+ case Success((pd, loader)) if seen(pd.classname) =>
+ // a nod to SI-7494, take the plugin classes distinctly
+ Failure(new PluginLoadException(pd.name, s"Ignoring duplicate plugin ${pd.name} (${pd.classname})"))
+ case Success((pd, loader)) if ignoring contains pd.name =>
+ Failure(new PluginLoadException(pd.name, s"Disabling plugin ${pd.name}"))
+ case Success((pd, loader)) =>
+ seen += pd.classname
+ Plugin.load(pd.classname, loader)
+ case Failure(e) =>
+ Failure(e)
+ }
+ enabled // distinct and not disabled
}
/** Instantiate a plugin class, given the class and
@@ -164,3 +189,11 @@ object Plugin {
(clazz getConstructor classOf[Global] newInstance global).asInstanceOf[Plugin]
}
}
+
+class PluginLoadException(val path: String, message: String, cause: Exception) extends Exception(message, cause) {
+ def this(path: String, message: String) = this(path, message, null)
+}
+
+class MissingPluginException(path: String) extends PluginLoadException(path, s"No plugin in path $path") {
+ def this(paths: List[Path]) = this(paths mkString File.pathSeparator)
+}
diff --git a/src/compiler/scala/tools/nsc/plugins/PluginLoadException.scala b/src/compiler/scala/tools/nsc/plugins/PluginLoadException.scala
deleted file mode 100644
index c5da24993e..0000000000
--- a/src/compiler/scala/tools/nsc/plugins/PluginLoadException.scala
+++ /dev/null
@@ -1,15 +0,0 @@
-/* NSC -- new Scala compiler
- * Copyright 2007-2013 LAMP/EPFL
- * @author Lex Spoon
- */
-
-package scala.tools.nsc
-package plugins
-
-/** ...
- *
- * @author Lex Spoon
- * @version 1.0, 2007-5-21
- */
-class PluginLoadException(filename: String, cause: Exception)
-extends Exception(cause)
diff --git a/src/compiler/scala/tools/nsc/plugins/Plugins.scala b/src/compiler/scala/tools/nsc/plugins/Plugins.scala
index 4769705404..12f9aeba27 100644
--- a/src/compiler/scala/tools/nsc/plugins/Plugins.scala
+++ b/src/compiler/scala/tools/nsc/plugins/Plugins.scala
@@ -8,6 +8,7 @@ package scala.tools.nsc
package plugins
import scala.reflect.io.{ File, Path }
+import scala.tools.nsc.util.ClassPath
import scala.tools.util.PathResolver.Defaults
/** Support for run-time loading of compiler plugins.
@@ -16,8 +17,7 @@ import scala.tools.util.PathResolver.Defaults
* @version 1.1, 2009/1/2
* Updated 2009/1/2 by Anders Bach Nielsen: Added features to implement SIP 00002
*/
-trait Plugins {
- self: Global =>
+trait Plugins { global: Global =>
/** Load a rough list of the plugins. For speed, it
* does not instantiate a compiler run. Therefore it cannot
@@ -25,13 +25,20 @@ trait Plugins {
* filtered from the final list of plugins.
*/
protected def loadRoughPluginsList(): List[Plugin] = {
- val jars = settings.plugin.value map Path.apply
- def injectDefault(s: String) = if (s.isEmpty) Defaults.scalaPluginPath else s
- val dirs = (settings.pluginsDir.value split File.pathSeparator).toList map injectDefault map Path.apply
- val maybes = Plugin.loadAllFrom(jars, dirs, settings.disable.value)
+ def asPath(p: String) = ClassPath split p
+ val paths = settings.plugin.value filter (_ != "") map (s => asPath(s) map Path.apply)
+ val dirs = {
+ def injectDefault(s: String) = if (s.isEmpty) Defaults.scalaPluginPath else s
+ asPath(settings.pluginsDir.value) map injectDefault map Path.apply
+ }
+ val maybes = Plugin.loadAllFrom(paths, dirs, settings.disable.value)
val (goods, errors) = maybes partition (_.isSuccess)
// Explicit parameterization of recover to suppress -Xlint warning about inferred Any
- errors foreach (_.recover[Any] { case e: Exception => inform(e.getMessage) })
+ errors foreach (_.recover[Any] {
+ // legacy behavior ignores altogether, so at least warn devs
+ case e: MissingPluginException => if (global.isDeveloper) warning(e.getMessage)
+ case e: Exception => inform(e.getMessage)
+ })
val classes = goods map (_.get) // flatten
// Each plugin must only be instantiated once. A common pattern
diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala
index ba135d7d25..069d6d5fb2 100644
--- a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala
@@ -391,8 +391,10 @@ trait PatternTypers {
else freshUnapplyArgType()
)
)
+ val unapplyArgTree = Ident(unapplyArg) updateAttachment SubpatternsAttachment(args)
+
// clearing the type is necessary so that ref will be stabilized; see bug 881
- val fun1 = typedPos(fun.pos)(Apply(Select(fun.clearType(), unapplyMethod), Ident(unapplyArg) :: Nil))
+ val fun1 = typedPos(fun.pos)(Apply(Select(fun.clearType(), unapplyMethod), unapplyArgTree :: Nil))
def makeTypedUnApply() = {
// the union of the expected type and the inferred type of the argument to unapply
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala b/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala
index f92c9aa845..f5bcaf68e0 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala
@@ -3,6 +3,7 @@ package quasiquotes
import scala.collection.{immutable, mutable}
import scala.reflect.internal.Flags._
+import scala.reflect.macros.TypecheckException
class Cardinality private[Cardinality](val value: Int) extends AnyVal {
def pred = { assert(value - 1 >= 0); new Cardinality(value - 1) }
@@ -30,158 +31,173 @@ trait Holes { self: Quasiquotes =>
import definitions._
import universeTypes._
- /** Location characterizes a kind of a non-terminal in Scala syntax where something is going to be spliced.
- * A location is typically associated with a type of the things that can be spliced there.
- * Associated type might be different from an actual tpe of a splicee due to lifting.
- * This is the first pillar of modularity in the quasiquote reifier.
- */
- sealed abstract class Location(val tpe: Type)
- case object UnknownLocation extends Location(NoType)
- case class TreeLocation(override val tpe: Type) extends Location(tpe)
- case object NameLocation extends Location(nameType)
- case object ModsLocation extends Location(modsType)
- case object FlagsLocation extends Location(flagsType)
- case object SymbolLocation extends Location(symbolType)
- case class IterableLocation(card: Cardinality, sublocation: TreeLocation) extends Location(NoType) {
- override val tpe = {
- def loop(n: Cardinality, tpe: Type): Type =
- if (n == NoDot) tpe
- else appliedType(IterableClass.toType, List(loop(n.pred, tpe)))
- loop(card, sublocation.tpe)
+ protected lazy val IterableTParam = IterableClass.typeParams(0).asType.toType
+ protected def inferParamImplicit(tfun: Type, targ: Type) = c.inferImplicitValue(appliedType(tfun, List(targ)), silent = true)
+ protected def inferLiftable(tpe: Type): Tree = inferParamImplicit(liftableType, tpe)
+ protected def inferUnliftable(tpe: Type): Tree = inferParamImplicit(unliftableType, tpe)
+ protected def isLiftableType(tpe: Type) = inferLiftable(tpe) != EmptyTree
+ protected def isNativeType(tpe: Type) =
+ (tpe <:< treeType) || (tpe <:< nameType) || (tpe <:< modsType) ||
+ (tpe <:< flagsType) || (tpe <:< symbolType)
+ protected def isBottomType(tpe: Type) =
+ tpe <:< NothingClass.tpe || tpe <:< NullClass.tpe
+ protected def stripIterable(tpe: Type, limit: Option[Cardinality] = None): (Cardinality, Type) =
+ if (limit.map { _ == NoDot }.getOrElse { false }) (NoDot, tpe)
+ else if (tpe != null && !isIterableType(tpe)) (NoDot, tpe)
+ else if (isBottomType(tpe)) (NoDot, tpe)
+ else {
+ val targ = IterableTParam.asSeenFrom(tpe, IterableClass)
+ val (card, innerTpe) = stripIterable(targ, limit.map { _.pred })
+ (card.succ, innerTpe)
}
+ protected def iterableTypeFromCard(n: Cardinality, tpe: Type): Type = {
+ if (n == NoDot) tpe
+ else appliedType(IterableClass.toType, List(iterableTypeFromCard(n.pred, tpe)))
}
- /** Hole type describes location, cardinality and a pre-reification routine associated with a hole.
- * An interesting thing about HoleType is that it can be completely inferred from the type of the splicee.
- * This is the second pillar of modularity in the quasiquote reifier.
+ /** Hole encapsulates information about splices in quasiquotes.
+ * It packs together a cardinality of a splice, pre-reified tree
+ * representation (possibly preprocessed) and position.
*/
- case class HoleType(preprocessor: Tree => Tree, location: Location, cardinality: Cardinality) {
- def makeHole(tree: Tree) = Hole(preprocessor(tree), location, cardinality)
+ abstract class Hole {
+ val tree: Tree
+ val pos: Position
+ val cardinality: Cardinality
}
- object HoleType {
- def unapply(tpe: Type): Option[HoleType] = tpe match {
- case NativeType(holeTpe) => Some(holeTpe)
- case LiftableType(holeTpe) => Some(holeTpe)
- case IterableTreeType(holeTpe) => Some(holeTpe)
- case IterableLiftableType(holeTpe) => Some(holeTpe)
- case _ => None
- }
- trait HoleTypeExtractor {
- def unapply(tpe: Type): Option[HoleType] = {
- for {
- preprocessor <- this.preprocessor(tpe)
- location <- this.location(tpe)
- cardinality <- Some(this.cardinality(tpe))
- } yield HoleType(preprocessor, location, cardinality)
- }
- def preprocessor(tpe: Type): Option[Tree => Tree]
- def location(tpe: Type): Option[Location]
- def cardinality(tpe: Type): Cardinality = parseCardinality(tpe)._1
-
- def lifter(tpe: Type): Option[Tree => Tree] = {
- val lifterTpe = appliedType(LiftableClass.toType, List(tpe))
- val lifter = c.inferImplicitValue(lifterTpe, silent = true)
- if (lifter != EmptyTree) Some(tree => {
- val lifted = Apply(lifter, List(u, tree))
- val targetType = Select(u, tpnme.Tree)
- atPos(tree.pos)(TypeApply(Select(lifted, nme.asInstanceOf_), List(targetType)))
- }) else None
- }
+ object Hole {
+ def apply(card: Cardinality, tree: Tree): Hole =
+ if (method != nme.unapply) new ApplyHole(card, tree)
+ else new UnapplyHole(card, tree)
+ def unapply(hole: Hole): Some[(Tree, Cardinality)] = Some((hole.tree, hole.cardinality))
+ }
- def iterator(tpe: Type)(elementTransform: Tree => Tree): Option[Tree => Tree] = {
- def reifyIterable(tree: Tree, n: Cardinality): Tree = {
- def loop(tree: Tree, n: Cardinality) =
- if (n == NoDot) elementTransform(tree)
- else {
- val x: TermName = c.freshName()
- val wrapped = reifyIterable(Ident(x), n.pred)
- val xToWrapped = Function(List(ValDef(Modifiers(PARAM), x, TypeTree(), EmptyTree)), wrapped)
- Select(Apply(Select(tree, nme.map), List(xToWrapped)), nme.toList)
- }
- if (tree.tpe != null && (tree.tpe <:< listTreeType || tree.tpe <:< listListTreeType)) tree
- else atPos(tree.pos)(loop(tree, n))
- }
- val card = parseCardinality(tpe)._1
- if (card != NoDot) Some(reifyIterable(_, card)) else None
- }
+ class ApplyHole(card: Cardinality, splicee: Tree) extends Hole {
+ val (strippedTpe, tpe): (Type, Type) = {
+ if (stripIterable(splicee.tpe)._1.value < card.value) cantSplice()
+ val (_, strippedTpe) = stripIterable(splicee.tpe, limit = Some(card))
+ if (isBottomType(strippedTpe)) cantSplice()
+ else if (isNativeType(strippedTpe)) (strippedTpe, iterableTypeFromCard(card, strippedTpe))
+ else if (isLiftableType(strippedTpe)) (strippedTpe, iterableTypeFromCard(card, treeType))
+ else cantSplice()
}
- object NativeType extends HoleTypeExtractor {
- def preprocessor(tpe: Type) = Some(identity)
- def location(tpe: Type) = {
- if (tpe <:< treeType) Some(TreeLocation(tpe))
- else if (tpe <:< nameType) Some(NameLocation)
- else if (tpe <:< modsType) Some(ModsLocation)
- else if (tpe <:< flagsType) Some(FlagsLocation)
- else if (tpe <:< symbolType) Some(SymbolLocation)
- else None
- }
+ val tree = {
+ def inner(itpe: Type)(tree: Tree) =
+ if (isNativeType(itpe)) tree
+ else if (isLiftableType(itpe)) lifted(itpe)(tree)
+ else global.abort("unreachable")
+ if (card == NoDot) inner(strippedTpe)(splicee)
+ else iterated(card, strippedTpe, inner(strippedTpe))(splicee)
}
- object LiftableType extends HoleTypeExtractor {
- def preprocessor(tpe: Type) = lifter(tpe)
- def location(tpe: Type) = Some(TreeLocation(treeType))
+ val pos = splicee.pos
+
+ val cardinality = stripIterable(tpe)._1
+
+ protected def cantSplice(): Nothing = {
+ val (iterableCard, iterableType) = stripIterable(splicee.tpe)
+ val holeCardMsg = if (card != NoDot) s" with $card" else ""
+ val action = "splice " + splicee.tpe + holeCardMsg
+ val suggestCard = card != iterableCard || card != NoDot
+ val spliceeCardMsg = if (card != iterableCard && iterableCard != NoDot) s"using $iterableCard" else "omitting the dots"
+ val cardSuggestion = if (suggestCard) spliceeCardMsg else ""
+ val suggestLifting = (card == NoDot || iterableCard != NoDot) && !(iterableType <:< treeType) && !isLiftableType(iterableType)
+ val liftedTpe = if (card != NoDot) iterableType else splicee.tpe
+ val liftSuggestion = if (suggestLifting) s"providing an implicit instance of Liftable[$liftedTpe]" else ""
+ val advice =
+ if (isBottomType(iterableType)) "bottom type values often indicate programmer mistake"
+ else "consider " + List(cardSuggestion, liftSuggestion).filter(_ != "").mkString(" or ")
+ c.abort(splicee.pos, s"Can't $action, $advice")
}
- object IterableTreeType extends HoleTypeExtractor {
- def preprocessor(tpe: Type) = iterator(tpe)(identity)
- def location(tpe: Type) = {
- val (card, elementTpe) = parseCardinality(tpe)
- if (card != NoDot && elementTpe <:< treeType) Some(IterableLocation(card, TreeLocation(elementTpe)))
- else None
- }
+ protected def lifted(tpe: Type)(tree: Tree): Tree = {
+ val lifter = inferLiftable(tpe)
+ assert(lifter != EmptyTree, s"couldnt find a liftable for $tpe")
+ val lifted = Apply(lifter, List(tree))
+ val targetType = Select(u, tpnme.Tree)
+ atPos(tree.pos)(TypeApply(Select(lifted, nme.asInstanceOf_), List(targetType)))
}
- object IterableLiftableType extends HoleTypeExtractor {
- def preprocessor(tpe: Type) = {
- val (_, elementTpe) = parseCardinality(tpe)
- for {
- lifter <- this.lifter(elementTpe)
- iterator <- this.iterator(tpe)(lifter)
- } yield iterator
+ protected def iterated(card: Cardinality, tpe: Type, elementTransform: Tree => Tree = identity)(tree: Tree): Tree = {
+ assert(card != NoDot)
+ def reifyIterable(tree: Tree, n: Cardinality): Tree = {
+ def loop(tree: Tree, n: Cardinality): Tree =
+ if (n == NoDot) elementTransform(tree)
+ else {
+ val x: TermName = c.freshName()
+ val wrapped = reifyIterable(Ident(x), n.pred)
+ val xToWrapped = Function(List(ValDef(Modifiers(PARAM), x, TypeTree(), EmptyTree)), wrapped)
+ Select(Apply(Select(tree, nme.map), List(xToWrapped)), nme.toList)
+ }
+ if (tree.tpe != null && (tree.tpe <:< listTreeType || tree.tpe <:< listListTreeType)) tree
+ else atPos(tree.pos)(loop(tree, n))
}
- def location(tpe: Type) = Some(IterableLocation(cardinality(tpe), TreeLocation(treeType)))
+ reifyIterable(tree, card)
}
}
- /** Hole encapsulates information about splices in quasiquotes.
- * It packs together a cardinality of a splice, a splicee (possibly preprocessed)
- * and the description of the location in Scala syntax where the splicee can be spliced.
- * This is the third pillar of modularity in the quasiquote reifier.
- */
- case class Hole(tree: Tree, location: Location, cardinality: Cardinality)
-
- object Hole {
- def apply(splicee: Tree, holeCard: Cardinality): Hole = {
- if (method == nme.unapply) return new Hole(splicee, UnknownLocation, holeCard)
- val (spliceeCard, elementTpe) = parseCardinality(splicee.tpe)
- def cantSplice() = {
- val holeCardMsg = if (holeCard != NoDot) s" with $holeCard" else ""
- val action = "splice " + splicee.tpe + holeCardMsg
- val suggestCard = holeCard != spliceeCard || holeCard != NoDot
- val spliceeCardMsg = if (holeCard != spliceeCard && spliceeCard != NoDot) s"using $spliceeCard" else "omitting the dots"
- val cardSuggestion = if (suggestCard) spliceeCardMsg else ""
- def canBeLifted(tpe: Type) = HoleType.LiftableType.unapply(tpe).nonEmpty
- val suggestLifting = (holeCard == NoDot || spliceeCard != NoDot) && !(elementTpe <:< treeType) && !canBeLifted(elementTpe)
- val liftedTpe = if (holeCard != NoDot) elementTpe else splicee.tpe
- val liftSuggestion = if (suggestLifting) s"providing an implicit instance of Liftable[$liftedTpe]" else ""
- val advice = List(cardSuggestion, liftSuggestion).filter(_ != "").mkString(" or ")
- c.abort(splicee.pos, s"Can't $action, consider $advice")
- }
- val holeTpe = splicee.tpe match {
- case _ if holeCard != spliceeCard => cantSplice()
- case HoleType(holeTpe) => holeTpe
- case _ => cantSplice()
- }
- holeTpe.makeHole(splicee)
+ class UnapplyHole(val cardinality: Cardinality, pat: Tree) extends Hole {
+ val (placeholderName, pos, tptopt) = pat match {
+ case Bind(pname, inner @ Bind(_, Typed(Ident(nme.WILDCARD), tpt))) => (pname, inner.pos, Some(tpt))
+ case Bind(pname, inner @ Typed(Ident(nme.WILDCARD), tpt)) => (pname, inner.pos, Some(tpt))
+ case Bind(pname, inner) => (pname, inner.pos, None)
}
+ val treeNoUnlift = Bind(placeholderName, Ident(nme.WILDCARD))
+ lazy val tree =
+ tptopt.map { tpt =>
+ val TypeDef(_, _, _, typedTpt) =
+ try c.typeCheck(TypeDef(NoMods, TypeName("T"), Nil, tpt))
+ catch { case TypecheckException(pos, msg) => c.abort(pos.asInstanceOf[c.Position], msg) }
+ val tpe = typedTpt.tpe
+ val (iterableCard, _) = stripIterable(tpe)
+ if (iterableCard.value < cardinality.value)
+ c.abort(pat.pos, s"Can't extract $tpe with $cardinality, consider using $iterableCard")
+ val (_, strippedTpe) = stripIterable(tpe, limit = Some(cardinality))
+ if (strippedTpe <:< treeType) treeNoUnlift
+ else
+ unlifters.spawn(strippedTpe, cardinality).map {
+ Apply(_, treeNoUnlift :: Nil)
+ }.getOrElse {
+ c.abort(pat.pos, s"Can't find $unliftableType[$strippedTpe], consider providing it")
+ }
+ }.getOrElse { treeNoUnlift }
}
- def parseCardinality(tpe: Type): (Cardinality, Type) = {
- if (tpe != null && isIterableType(tpe)) {
- val (card, innerTpe) = parseCardinality(tpe.typeArguments.head)
- (card.succ, innerTpe)
- } else (NoDot, tpe)
+ /** Full support for unliftable implies that it's possible to interleave
+ * deconstruction with higher cardinality and unlifting of the values.
+ * In particular extraction of List[Tree] as List[T: Unliftable] requires
+ * helper extractors that would do the job: UnliftHelper1[T]. Similarly
+ * List[List[Tree]] needs UnliftHelper2[T].
+ *
+ * See also "unlift list" tests in UnapplyProps.scala
+ */
+ object unlifters {
+ private var records = List.empty[(Type, Cardinality)]
+ // Request an UnliftHelperN[T] where n == card and T == tpe.
+ // If card == 0 then helper is not needed and plain instance
+ // of unliftable is returned.
+ def spawn(tpe: Type, card: Cardinality): Option[Tree] = {
+ val unlifter = inferUnliftable(tpe)
+ if (unlifter == EmptyTree) None
+ else if (card == NoDot) Some(unlifter)
+ else {
+ val idx = records.indexWhere { p => p._1 =:= tpe && p._2 == card }
+ val resIdx = if (idx != -1) idx else { records +:= (tpe, card); records.length - 1}
+ Some(Ident(TermName(nme.QUASIQUOTE_UNLIFT_HELPER + resIdx)))
+ }
+ }
+ // Returns a list of vals that will defined required unlifters
+ def preamble(): List[Tree] =
+ records.zipWithIndex.map { case ((tpe, card), idx) =>
+ val name = TermName(nme.QUASIQUOTE_UNLIFT_HELPER + idx)
+ val helperName = card match { case DotDot => nme.UnliftHelper1 case DotDotDot => nme.UnliftHelper2 }
+ val lifter = inferUnliftable(tpe)
+ assert(helperName.isTermName)
+ // q"val $name: $u.build.${helperName.toTypeName} = $u.build.$helperName($lifter)"
+ ValDef(NoMods, name,
+ AppliedTypeTree(Select(Select(u, nme.build), helperName.toTypeName), List(TypeTree(tpe))),
+ Apply(Select(Select(u, nme.build), helperName), lifter :: Nil))
+ }
}
-} \ No newline at end of file
+}
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
index 126c14ac81..6e6b617e5c 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
@@ -161,7 +161,12 @@ trait Parsers { self: Quasiquotes =>
}
object TermParser extends Parser {
- def entryPoint = { parser => gen.mkTreeOrBlock(parser.templateOrTopStatSeq()) }
+ def entryPoint = { parser =>
+ parser.templateOrTopStatSeq() match {
+ case head :: Nil => Block(Nil, head)
+ case lst => gen.mkTreeOrBlock(lst)
+ }
+ }
}
object TypeParser extends Parser {
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
index 54be9123c7..bdb44ad9a2 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
@@ -13,6 +13,7 @@ import scala.collection.{immutable, mutable}
trait Placeholders { self: Quasiquotes =>
import global._
import Cardinality._
+ import universeTypes._
// Step 1: Transform Scala source with holes into vanilla Scala source
@@ -32,13 +33,17 @@ trait Placeholders { self: Quasiquotes =>
def appendHole(tree: Tree, cardinality: Cardinality) = {
val placeholderName = c.freshName(TermName(nme.QUASIQUOTE_PREFIX + sessionSuffix))
sb.append(placeholderName)
- val holeTree = if (method == nme.unapply) Bind(placeholderName, Ident(nme.WILDCARD)) else tree
- holeMap(placeholderName) = Hole(holeTree, cardinality)
+ val holeTree =
+ if (method != nme.unapply) tree
+ else Bind(placeholderName, tree)
+ holeMap(placeholderName) = Hole(cardinality, holeTree)
}
val iargs = method match {
case nme.apply => args
- case nme.unapply => List.fill(parts.length - 1)(EmptyTree)
+ case nme.unapply =>
+ val (dummy @ Ident(nme.SELECTOR_DUMMY)) :: Nil = args
+ dummy.attachments.get[SubpatternsAttachment].get.patterns
case _ => global.abort("unreachable")
}
@@ -78,9 +83,9 @@ trait Placeholders { self: Quasiquotes =>
trait HolePlaceholder {
def matching: PartialFunction[Any, Name]
- def unapply(scrutinee: Any): Option[(Tree, Location, Cardinality)] = {
+ def unapply(scrutinee: Any): Option[Hole] = {
val name = matching.lift(scrutinee)
- name.flatMap { holeMap.get(_).map { case Hole(repr, loc, card) => (repr, loc, card) } }
+ name.flatMap { holeMap.get(_) }
}
}
@@ -128,44 +133,44 @@ trait Placeholders { self: Quasiquotes =>
}
object SymbolPlaceholder {
- def unapply(scrutinee: Any): Option[Tree] = scrutinee match {
- case Placeholder(tree, SymbolLocation, _) => Some(tree)
+ def unapply(scrutinee: Any): Option[Hole] = scrutinee match {
+ case Placeholder(hole: ApplyHole) if hole.tpe <:< symbolType => Some(hole)
case _ => None
}
}
object CasePlaceholder {
- def unapply(tree: Tree): Option[(Tree, Location, Cardinality)] = tree match {
- case CaseDef(Apply(Ident(nme.QUASIQUOTE_CASE), List(Placeholder(tree, location, card))), EmptyTree, EmptyTree) => Some((tree, location, card))
+ def unapply(tree: Tree): Option[Hole] = tree match {
+ case CaseDef(Apply(Ident(nme.QUASIQUOTE_CASE), List(Placeholder(hole))), EmptyTree, EmptyTree) => Some(hole)
case _ => None
}
}
object RefineStatPlaceholder {
- def unapply(tree: Tree): Option[(Tree, Location, Cardinality)] = tree match {
- case ValDef(_, Placeholder(tree, location, card), Ident(tpnme.QUASIQUOTE_REFINE_STAT), _) => Some((tree, location, card))
+ def unapply(tree: Tree): Option[Hole] = tree match {
+ case ValDef(_, Placeholder(hole), Ident(tpnme.QUASIQUOTE_REFINE_STAT), _) => Some(hole)
case _ => None
}
}
object EarlyDefPlaceholder {
- def unapply(tree: Tree): Option[(Tree, Location, Cardinality)] = tree match {
- case ValDef(_, Placeholder(tree, location, card), Ident(tpnme.QUASIQUOTE_EARLY_DEF), _) => Some((tree, location, card))
+ def unapply(tree: Tree): Option[Hole] = tree match {
+ case ValDef(_, Placeholder(hole), Ident(tpnme.QUASIQUOTE_EARLY_DEF), _) => Some(hole)
case _ => None
}
}
object PackageStatPlaceholder {
- def unapply(tree: Tree): Option[(Tree, Location, Cardinality)] = tree match {
- case ValDef(NoMods, Placeholder(tree, location, card), Ident(tpnme.QUASIQUOTE_PACKAGE_STAT), EmptyTree) => Some((tree, location, card))
+ def unapply(tree: Tree): Option[Hole] = tree match {
+ case ValDef(NoMods, Placeholder(hole), Ident(tpnme.QUASIQUOTE_PACKAGE_STAT), EmptyTree) => Some(hole)
case _ => None
}
}
object ForEnumPlaceholder {
- def unapply(tree: Tree): Option[(Tree, Location, Cardinality)] = tree match {
- case build.SyntacticValFrom(Bind(Placeholder(tree, location, card), Ident(nme.WILDCARD)), Ident(nme.QUASIQUOTE_FOR_ENUM)) =>
- Some((tree, location, card))
+ def unapply(tree: Tree): Option[Hole] = tree match {
+ case build.SyntacticValFrom(Bind(Placeholder(hole), Ident(nme.WILDCARD)), Ident(nme.QUASIQUOTE_FOR_ENUM)) =>
+ Some(hole)
case _ => None
}
}
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
index b28c85cfc2..6d7aafe266 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
@@ -13,7 +13,7 @@ trait Reifiers { self: Quasiquotes =>
import Cardinality._
import universeTypes._
- abstract class Reifier extends {
+ abstract class Reifier(val isReifyingExpressions: Boolean) extends {
val global: self.global.type = self.global
val universe = self.universe
val reifee = EmptyTree
@@ -22,7 +22,6 @@ trait Reifiers { self: Quasiquotes =>
} with ReflectReifier {
lazy val typer = throw new UnsupportedOperationException
- def isReifyingExpressions: Boolean
def isReifyingPatterns: Boolean = !isReifyingExpressions
def action = if (isReifyingExpressions) "splice" else "extract"
def holesHaveTypes = isReifyingExpressions
@@ -94,12 +93,12 @@ trait Reifiers { self: Quasiquotes =>
// cq"$tree if $guard => $succ" :: cq"_ => $fail" :: Nil
CaseDef(tree, guard, succ) :: CaseDef(Ident(nme.WILDCARD), EmptyTree, fail) :: Nil
}
- // q"new { def unapply(tree: $AnyClass) = tree match { case ..$cases } }.unapply(..$args)"
+ // q"new { def unapply(tree: $AnyClass) = { ..${unlifters.preamble()}; tree match { case ..$cases } } }.unapply(..$args)"
Apply(
Select(
SyntacticNew(Nil, Nil, noSelfType, List(
DefDef(NoMods, nme.unapply, Nil, List(List(ValDef(NoMods, nme.tree, TypeTree(AnyClass.toType), EmptyTree))), TypeTree(),
- Match(Ident(nme.tree), cases)))),
+ SyntacticBlock(unlifters.preamble() :+ Match(Ident(nme.tree), cases))))),
nme.unapply),
args)
}
@@ -107,7 +106,7 @@ trait Reifiers { self: Quasiquotes =>
def reifyFillingHoles(tree: Tree): Tree = {
val reified = reifyTree(tree)
holeMap.unused.foreach { hole =>
- c.abort(holeMap(hole).tree.pos, s"Don't know how to $action here")
+ c.abort(holeMap(hole).pos, s"Don't know how to $action here")
}
wrap(reified)
}
@@ -117,21 +116,27 @@ trait Reifiers { self: Quasiquotes =>
reifyTreeSyntactically(tree)
def reifyTreePlaceholder(tree: Tree): Tree = tree match {
- case Placeholder(tree, TreeLocation(_), _) if isReifyingExpressions => tree
- case Placeholder(tree, _, NoDot) if isReifyingPatterns => tree
- case Placeholder(tree, _, card @ Dot()) => c.abort(tree.pos, s"Can't $action with $card here")
+ case Placeholder(hole: ApplyHole) if hole.tpe <:< treeType => hole.tree
+ case Placeholder(Hole(tree, NoDot)) if isReifyingPatterns => tree
+ case Placeholder(hole @ Hole(_, card @ Dot())) => c.abort(hole.pos, s"Can't $action with $card here")
case TuplePlaceholder(args) => reifyTuple(args)
case TupleTypePlaceholder(args) => reifyTupleType(args)
case FunctionTypePlaceholder(argtpes, restpe) => reifyFunctionType(argtpes, restpe)
- case CasePlaceholder(tree, location, _) => reifyCase(tree, location)
- case RefineStatPlaceholder(tree, _, _) => reifyRefineStat(tree)
- case EarlyDefPlaceholder(tree, _, _) => reifyEarlyDef(tree)
- case PackageStatPlaceholder(tree, _, _) => reifyPackageStat(tree)
- case ForEnumPlaceholder(tree, _, _) => tree
+ case CasePlaceholder(hole) => hole.tree
+ case RefineStatPlaceholder(hole) => reifyRefineStat(hole)
+ case EarlyDefPlaceholder(hole) => reifyEarlyDef(hole)
+ case PackageStatPlaceholder(hole) => reifyPackageStat(hole)
+ // for enumerators are checked not during splicing but during
+ // desugaring of the for loop in SyntacticFor & SyntacticForYield
+ case ForEnumPlaceholder(hole) => hole.tree
case _ => EmptyTree
}
override def reifyTreeSyntactically(tree: Tree) = tree match {
+ case RefTree(qual, SymbolPlaceholder(Hole(tree, _))) if isReifyingExpressions =>
+ mirrorBuildCall(nme.RefTree, reify(qual), tree)
+ case This(SymbolPlaceholder(Hole(tree, _))) if isReifyingExpressions =>
+ mirrorCall(nme.This, tree)
case SyntacticTraitDef(mods, name, tparams, earlyDefs, parents, selfdef, body) =>
reifyBuildCall(nme.SyntacticTraitDef, mods, name, tparams, earlyDefs, parents, selfdef, body)
case SyntacticClassDef(mods, name, tparams, constrmods, vparamss, earlyDefs, parents, selfdef, body) =>
@@ -161,17 +166,24 @@ trait Reifiers { self: Quasiquotes =>
reifyBuildCall(nme.SyntacticForYield, enums, body)
case SyntacticAssign(lhs, rhs) =>
reifyBuildCall(nme.SyntacticAssign, lhs, rhs)
- case SyntacticApplied(fun, List(args))
- if args.forall { case Placeholder(_, _, DotDotDot) => false case _ => true } =>
- reifyBuildCall(nme.SyntacticApply, fun, args)
case SyntacticApplied(fun, argss) if argss.nonEmpty =>
reifyBuildCall(nme.SyntacticApplied, fun, argss)
case SyntacticTypeApplied(fun, targs) if targs.nonEmpty =>
reifyBuildCall(nme.SyntacticTypeApplied, fun, targs)
case SyntacticFunction(args, body) =>
reifyBuildCall(nme.SyntacticFunction, args, body)
+ case SyntacticIdent(name, isBackquoted) =>
+ reifyBuildCall(nme.SyntacticIdent, name, isBackquoted)
+ case Block(Nil, Placeholder(Hole(tree, DotDot))) =>
+ mirrorBuildCall(nme.SyntacticBlock, tree)
+ case Block(Nil, other) =>
+ reifyTree(other)
case Block(stats, last) =>
reifyBuildCall(nme.SyntacticBlock, stats :+ last)
+ case Try(block, catches, finalizer) =>
+ reifyBuildCall(nme.SyntacticTry, block, catches, finalizer)
+ case Match(selector, cases) =>
+ reifyBuildCall(nme.SyntacticMatch, selector, cases)
// parser emits trees with scala package symbol to ensure
// that some names hygienically point to various scala package
// members; we need to preserve this symbol to preserve
@@ -183,9 +195,10 @@ trait Reifiers { self: Quasiquotes =>
}
override def reifyName(name: Name): Tree = name match {
- case Placeholder(tree, location, _) =>
- if (holesHaveTypes && !(location.tpe <:< nameType)) c.abort(tree.pos, s"$nameType expected but ${location.tpe} found")
- tree
+ case Placeholder(hole: ApplyHole) =>
+ if (!(hole.tpe <:< nameType)) c.abort(hole.pos, s"$nameType expected but ${hole.tpe} found")
+ hole.tree
+ case Placeholder(hole: UnapplyHole) => hole.treeNoUnlift
case FreshName(prefix) if prefix != nme.QUASIQUOTE_NAME_PREFIX =>
def fresh() = c.freshName[TermName](nme.QUASIQUOTE_NAME_PREFIX)
def introduceName() = { val n = fresh(); nameMap(name) += n; n}
@@ -196,15 +209,10 @@ trait Reifiers { self: Quasiquotes =>
super.reifyName(name)
}
- def reifyCase(tree: Tree, location: Location) = {
- if (holesHaveTypes && !(location.tpe <:< caseDefType)) c.abort(tree.pos, s"$caseDefType expected but ${location.tpe} found")
- tree
- }
-
def reifyTuple(args: List[Tree]) = args match {
case Nil => reify(Literal(Constant(())))
- case List(hole @ Placeholder(_, _, NoDot)) => reify(hole)
- case List(Placeholder(_, _, _)) => reifyBuildCall(nme.SyntacticTuple, args)
+ case List(hole @ Placeholder(Hole(_, NoDot))) => reify(hole)
+ case List(Placeholder(_)) => reifyBuildCall(nme.SyntacticTuple, args)
// in a case we only have one element tuple without
// any cardinality annotations this means that this is
// just an expression wrapped in parentheses
@@ -214,8 +222,8 @@ trait Reifiers { self: Quasiquotes =>
def reifyTupleType(args: List[Tree]) = args match {
case Nil => reify(Select(Ident(nme.scala_), tpnme.Unit))
- case List(hole @ Placeholder(_, _, NoDot)) => reify(hole)
- case List(Placeholder(_, _, _)) => reifyBuildCall(nme.SyntacticTupleType, args)
+ case List(hole @ Placeholder(Hole(_, NoDot))) => reify(hole)
+ case List(Placeholder(_)) => reifyBuildCall(nme.SyntacticTupleType, args)
case List(other) => reify(other)
case _ => reifyBuildCall(nme.SyntacticTupleType, args)
}
@@ -223,13 +231,18 @@ trait Reifiers { self: Quasiquotes =>
def reifyFunctionType(argtpes: List[Tree], restpe: Tree) =
reifyBuildCall(nme.SyntacticFunctionType, argtpes, restpe)
- def reifyRefineStat(tree: Tree) = tree
+ def reifyConstructionCheck(name: TermName, hole: Hole) = hole match {
+ case _: UnapplyHole => hole.tree
+ case _: ApplyHole => mirrorBuildCall(name, hole.tree)
+ }
+
+ def reifyRefineStat(hole: Hole) = reifyConstructionCheck(nme.mkRefineStat, hole)
- def reifyEarlyDef(tree: Tree) = tree
+ def reifyEarlyDef(hole: Hole) = reifyConstructionCheck(nme.mkEarlyDef, hole)
- def reifyAnnotation(tree: Tree) = tree
+ def reifyAnnotation(hole: Hole) = reifyConstructionCheck(nme.mkAnnotation, hole)
- def reifyPackageStat(tree: Tree) = tree
+ def reifyPackageStat(hole: Hole) = reifyConstructionCheck(nme.mkPackageStat, hole)
/** Splits list into a list of groups where subsequent elements are considered
* similar by the corresponding function.
@@ -262,7 +275,7 @@ trait Reifiers { self: Quasiquotes =>
*
* reifyMultiCardinalityList(lst) {
* // first we define patterns that extract high-cardinality holeMap (currently ..)
- * case Placeholder(CorrespondsTo(tree, tpe)) if tpe <:< iterableTreeType => tree
+ * case Placeholder(IterableType(_, _)) => tree
* } {
* // in the end we define how single elements are reified, typically with default reify call
* reify(_)
@@ -281,21 +294,22 @@ trait Reifiers { self: Quasiquotes =>
* elements.
*/
override def reifyList(xs: List[Any]): Tree = reifyMultiCardinalityList(xs) {
- case Placeholder(tree, _, DotDot) => tree
- case CasePlaceholder(tree, _, DotDot) => tree
- case RefineStatPlaceholder(tree, _, DotDot) => reifyRefineStat(tree)
- case EarlyDefPlaceholder(tree, _, DotDot) => reifyEarlyDef(tree)
- case PackageStatPlaceholder(tree, _, DotDot) => reifyPackageStat(tree)
- case ForEnumPlaceholder(tree, _, DotDot) => tree
- case List(Placeholder(tree, _, DotDotDot)) => tree
+ case Placeholder(Hole(tree, DotDot)) => tree
+ case CasePlaceholder(Hole(tree, DotDot)) => tree
+ case RefineStatPlaceholder(h @ Hole(_, DotDot)) => reifyRefineStat(h)
+ case EarlyDefPlaceholder(h @ Hole(_, DotDot)) => reifyEarlyDef(h)
+ case PackageStatPlaceholder(h @ Hole(_, DotDot)) => reifyPackageStat(h)
+ case ForEnumPlaceholder(Hole(tree, DotDot)) => tree
+ case List(Placeholder(Hole(tree, DotDotDot))) => tree
} {
reify(_)
}
def reifyAnnotList(annots: List[Tree]): Tree = reifyMultiCardinalityList(annots) {
- case AnnotPlaceholder(tree, _, DotDot) => reifyAnnotation(tree)
+ case AnnotPlaceholder(h @ Hole(_, DotDot)) => reifyAnnotation(h)
} {
- case AnnotPlaceholder(tree, UnknownLocation | TreeLocation(_), NoDot) => reifyAnnotation(tree)
+ case AnnotPlaceholder(h: ApplyHole) if h.tpe <:< treeType => reifyAnnotation(h)
+ case AnnotPlaceholder(h: UnapplyHole) if h.cardinality == NoDot => reifyAnnotation(h)
case other => reify(other)
}
@@ -321,78 +335,55 @@ trait Reifiers { self: Quasiquotes =>
override def mirrorBuildCall(name: TermName, args: Tree*): Tree =
Apply(Select(Select(universe, nme.build), name), args.toList)
- }
-
- class ApplyReifier extends Reifier {
- def isReifyingExpressions = true
- override def reifyTreeSyntactically(tree: Tree): Tree = tree match {
- case RefTree(qual, SymbolPlaceholder(tree)) =>
- mirrorBuildCall(nme.RefTree, reify(qual), tree)
- case This(SymbolPlaceholder(tree)) =>
- mirrorCall(nme.This, tree)
- case _ =>
- super.reifyTreeSyntactically(tree)
- }
+ override def scalaFactoryCall(name: String, args: Tree*): Tree =
+ call("scala." + name, args: _*)
+ }
- override def reifyMultiCardinalityList[T](xs: List[T])(fill: PartialFunction[T, Tree])(fallback: T => Tree): Tree = xs match {
- case Nil => mkList(Nil)
- case _ =>
+ class ApplyReifier extends Reifier(isReifyingExpressions = true) {
+ def reifyMultiCardinalityList[T](xs: List[T])(fill: PartialFunction[T, Tree])(fallback: T => Tree): Tree =
+ if (xs.isEmpty) mkList(Nil)
+ else {
def reifyGroup(group: List[T]): Tree = group match {
case List(elem) if fill.isDefinedAt(elem) => fill(elem)
case elems => mkList(elems.map(fallback))
}
val head :: tail = group(xs) { (a, b) => !fill.isDefinedAt(a) && !fill.isDefinedAt(b) }
tail.foldLeft[Tree](reifyGroup(head)) { (tree, lst) => Apply(Select(tree, nme.PLUSPLUS), List(reifyGroup(lst))) }
- }
+ }
override def reifyModifiers(m: Modifiers) =
if (m == NoMods) super.reifyModifiers(m)
else {
val (modsPlaceholders, annots) = m.annotations.partition {
- case ModsPlaceholder(_, _, _) => true
+ case ModsPlaceholder(_) => true
case _ => false
}
val (mods, flags) = modsPlaceholders.map {
- case ModsPlaceholder(tree, location, card) => (tree, location)
- }.partition { case (tree, location) =>
- location match {
- case ModsLocation => true
- case FlagsLocation => false
- case _ => c.abort(tree.pos, s"$flagsType or $modsType expected but ${tree.tpe} found")
- }
+ case ModsPlaceholder(hole: ApplyHole) => hole
+ }.partition { hole =>
+ if (hole.tpe <:< modsType) true
+ else if (hole.tpe <:< flagsType) false
+ else c.abort(hole.pos, s"$flagsType or $modsType expected but ${hole.tpe} found")
}
mods match {
- case (tree, _) :: Nil =>
- if (flags.nonEmpty) c.abort(flags(0)._1.pos, "Can't splice flags together with modifiers, consider merging flags into modifiers")
- if (annots.nonEmpty) c.abort(tree.pos, "Can't splice modifiers together with annotations, consider merging annotations into modifiers")
- ensureNoExplicitFlags(m, tree.pos)
- tree
- case _ :: (second, _) :: Nil =>
- c.abort(second.pos, "Can't splice multiple modifiers, consider merging them into a single modifiers instance")
+ case hole :: Nil =>
+ if (flags.nonEmpty) c.abort(flags(0).pos, "Can't splice flags together with modifiers, consider merging flags into modifiers")
+ if (annots.nonEmpty) c.abort(hole.pos, "Can't splice modifiers together with annotations, consider merging annotations into modifiers")
+ ensureNoExplicitFlags(m, hole.pos)
+ hole.tree
+ case _ :: hole :: Nil =>
+ c.abort(hole.pos, "Can't splice multiple modifiers, consider merging them into a single modifiers instance")
case _ =>
val baseFlags = reifyFlags(m.flags)
- val reifiedFlags = flags.foldLeft[Tree](baseFlags) { case (flag, (tree, _)) => Apply(Select(flag, nme.OR), List(tree)) }
+ val reifiedFlags = flags.foldLeft[Tree](baseFlags) { case (flag, hole) => Apply(Select(flag, nme.OR), List(hole.tree)) }
mirrorFactoryCall(nme.Modifiers, reifiedFlags, reify(m.privateWithin), reifyAnnotList(annots))
}
}
- override def reifyRefineStat(tree: Tree) = mirrorBuildCall(nme.mkRefineStat, tree)
-
- override def reifyEarlyDef(tree: Tree) = mirrorBuildCall(nme.mkEarlyDef, tree)
-
- override def reifyAnnotation(tree: Tree) = mirrorBuildCall(nme.mkAnnotation, tree)
-
- override def reifyPackageStat(tree: Tree) = mirrorBuildCall(nme.mkPackageStat, tree)
}
-
- class UnapplyReifier extends Reifier {
- def isReifyingExpressions = false
-
- override def scalaFactoryCall(name: String, args: Tree*): Tree =
- call("scala." + name, args: _*)
-
- override def reifyMultiCardinalityList[T](xs: List[T])(fill: PartialFunction[T, Tree])(fallback: T => Tree) = xs match {
+ class UnapplyReifier extends Reifier(isReifyingExpressions = false) {
+ def reifyMultiCardinalityList[T](xs: List[T])(fill: PartialFunction[T, Tree])(fallback: T => Tree): Tree = xs match {
case init :+ last if fill.isDefinedAt(last) =>
init.foldRight[Tree](fill(last)) { (el, rest) =>
val cons = Select(Select(Select(Ident(nme.scala_), nme.collection), nme.immutable), nme.CONS)
@@ -405,14 +396,14 @@ trait Reifiers { self: Quasiquotes =>
override def reifyModifiers(m: Modifiers) =
if (m == NoMods) super.reifyModifiers(m)
else {
- val mods = m.annotations.collect { case ModsPlaceholder(tree, _, _) => tree }
+ val mods = m.annotations.collect { case ModsPlaceholder(hole: UnapplyHole) => hole }
mods match {
- case tree :: Nil =>
- if (m.annotations.length != 1) c.abort(tree.pos, "Can't extract modifiers together with annotations, consider extracting just modifiers")
- ensureNoExplicitFlags(m, tree.pos)
- tree
- case _ :: second :: rest =>
- c.abort(second.pos, "Can't extract multiple modifiers together, consider extracting a single modifiers instance")
+ case hole :: Nil =>
+ if (m.annotations.length != 1) c.abort(hole.pos, "Can't extract modifiers together with annotations, consider extracting just modifiers")
+ ensureNoExplicitFlags(m, hole.pos)
+ hole.treeNoUnlift
+ case _ :: hole :: _ =>
+ c.abort(hole.pos, "Can't extract multiple modifiers together, consider extracting a single modifiers instance")
case Nil =>
mirrorFactoryCall(nme.Modifiers, reifyFlags(m.flags), reify(m.privateWithin), reifyAnnotList(m.annotations))
}