summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@typesafe.com>2013-07-10 13:05:06 -0700
committerAdriaan Moors <adriaan.moors@typesafe.com>2013-07-10 13:05:06 -0700
commit9def2ada8261ca14f0059c7d2b01e85830705e9b (patch)
tree3dd0ee2c58ab3ff94a99c32f89e6d15dd6950777
parent59947119eec1cb49c46f9ddf930741d1fb554f61 (diff)
parent114d52b1f8be07a95125397879b170e8496c0c2e (diff)
downloadscala-9def2ada8261ca14f0059c7d2b01e85830705e9b.tar.gz
scala-9def2ada8261ca14f0059c7d2b01e85830705e9b.tar.bz2
scala-9def2ada8261ca14f0059c7d2b01e85830705e9b.zip
Merge pull request #2714 from scalamacros/topic/quasiquotes
Quasiquotes
-rw-r--r--src/compiler/scala/reflect/macros/compiler/Errors.scala4
-rw-r--r--src/compiler/scala/reflect/reify/codegen/GenTrees.scala27
-rw-r--r--src/compiler/scala/reflect/reify/codegen/GenUtils.scala3
-rw-r--r--src/compiler/scala/reflect/reify/package.scala2
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala12
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreeGen.scala49
-rw-r--r--src/compiler/scala/tools/nsc/ast/Trees.scala77
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala54
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Scanners.scala33
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala49
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala1
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala13
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Macros.scala107
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala5
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Unapplies.scala10
-rw-r--r--src/compiler/scala/tools/nsc/util/CharArrayReader.scala31
-rw-r--r--src/compiler/scala/tools/reflect/FastTrack.scala8
-rw-r--r--src/compiler/scala/tools/reflect/ToolBoxFactory.scala2
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Holes.scala187
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala134
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala123
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala51
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala290
-rw-r--r--src/reflect/scala/reflect/api/BuildUtils.scala42
-rw-r--r--src/reflect/scala/reflect/api/Liftable.scala32
-rw-r--r--src/reflect/scala/reflect/api/Quasiquotes.scala20
-rw-r--r--src/reflect/scala/reflect/api/StandardLiftables.scala36
-rw-r--r--src/reflect/scala/reflect/api/Universe.scala2
-rw-r--r--src/reflect/scala/reflect/internal/BuildUtils.scala120
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala40
-rw-r--r--src/reflect/scala/reflect/internal/StdNames.scala49
-rw-r--r--src/reflect/scala/reflect/internal/TreeGen.scala76
-rw-r--r--test/files/neg/macro-quasiquotes.check7
-rw-r--r--test/files/neg/macro-quasiquotes/Macros_1.scala15
-rw-r--r--test/files/neg/macro-quasiquotes/Test_2.scala5
-rw-r--r--test/files/run/macro-quasiquotes.check4
-rw-r--r--test/files/run/macro-quasiquotes/Macros_1.scala15
-rw-r--r--test/files/run/macro-quasiquotes/Test_2.scala5
-rw-r--r--test/files/scalacheck/quasiquotes/ArbitraryTreesAndNames.scala307
-rw-r--r--test/files/scalacheck/quasiquotes/ErrorProps.scala199
-rw-r--r--test/files/scalacheck/quasiquotes/LiftableProps.scala84
-rw-r--r--test/files/scalacheck/quasiquotes/PatternConstructionProps.scala37
-rw-r--r--test/files/scalacheck/quasiquotes/PatternDeconstructionProps.scala35
-rw-r--r--test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala89
-rw-r--r--test/files/scalacheck/quasiquotes/TermConstructionProps.scala341
-rw-r--r--test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala122
-rw-r--r--test/files/scalacheck/quasiquotes/Test.scala12
-rw-r--r--test/files/scalacheck/quasiquotes/TypeConstructionProps.scala25
-rw-r--r--test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala29
49 files changed, 2780 insertions, 240 deletions
diff --git a/src/compiler/scala/reflect/macros/compiler/Errors.scala b/src/compiler/scala/reflect/macros/compiler/Errors.scala
index dd3142127e..a60a2c2306 100644
--- a/src/compiler/scala/reflect/macros/compiler/Errors.scala
+++ b/src/compiler/scala/reflect/macros/compiler/Errors.scala
@@ -60,8 +60,8 @@ trait Errors extends Traces {
(rtpe, atpe) match {
case _ if rtpe eq atpe => success()
case (TypeRef(_, RepeatedParamClass, rtpe :: Nil), TypeRef(_, RepeatedParamClass, atpe :: Nil)) => check(rtpe, atpe)
- case (ExprClassOf(_), TreeType()) => success()
- case (TreeType(), ExprClassOf(_)) => success()
+ case (ExprClassOf(_), TreeType()) if rtpe.prefix =:= atpe.prefix => success()
+ case (SubtreeType(), ExprClassOf(_)) if rtpe.prefix =:= atpe.prefix => success()
case _ => rtpe <:< atpe
}
}
diff --git a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala
index 78bdf7e132..3507c2a173 100644
--- a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala
+++ b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala
@@ -42,12 +42,6 @@ trait GenTrees {
// the second prototype reified external types, but avoided reifying local ones => this created an ugly irregularity
// current approach is uniform and compact
var rtree = tree match {
- case global.EmptyTree =>
- reifyMirrorObject(EmptyTree)
- case global.emptyValDef =>
- mirrorSelect(nme.emptyValDef)
- case global.pendingSuperCall =>
- mirrorSelect(nme.pendingSuperCall)
case FreeDef(_, _, _, _, _) =>
reifyNestedFreeDef(tree)
case FreeRef(_, _) =>
@@ -56,12 +50,8 @@ trait GenTrees {
reifyBoundTerm(tree)
case BoundType(tree) =>
reifyBoundType(tree)
- case Literal(const @ Constant(_)) =>
- mirrorCall(nme.Literal, reifyProduct(const))
- case Import(expr, selectors) =>
- mirrorCall(nme.Import, reify(expr), mkList(selectors map reifyProduct))
case _ =>
- reifyProduct(tree)
+ reifyTreeSyntactically(tree)
}
// usually we don't reify symbols/types, because they can be re-inferred during subsequent reflective compilation
@@ -78,6 +68,21 @@ trait GenTrees {
rtree
}
+ def reifyTreeSyntactically(tree: Tree) = tree match {
+ case global.EmptyTree =>
+ reifyMirrorObject(EmptyTree)
+ case global.emptyValDef =>
+ mirrorSelect(nme.emptyValDef)
+ case global.pendingSuperCall =>
+ mirrorSelect(nme.pendingSuperCall)
+ case Literal(const @ Constant(_)) =>
+ mirrorCall(nme.Literal, reifyProduct(const))
+ case Import(expr, selectors) =>
+ mirrorCall(nme.Import, reify(expr), mkList(selectors map reifyProduct))
+ case _ =>
+ reifyProduct(tree)
+ }
+
def reifyModifiers(m: global.Modifiers) =
mirrorFactoryCall(nme.Modifiers, mirrorBuildCall(nme.flagsFromBits, reify(m.flags)), reify(m.privateWithin), reify(m.annotations))
diff --git a/src/compiler/scala/reflect/reify/codegen/GenUtils.scala b/src/compiler/scala/reflect/reify/codegen/GenUtils.scala
index e0570d61f2..de9fec0df5 100644
--- a/src/compiler/scala/reflect/reify/codegen/GenUtils.scala
+++ b/src/compiler/scala/reflect/reify/codegen/GenUtils.scala
@@ -42,6 +42,9 @@ trait GenUtils {
def mirrorBuildCall(name: TermName, args: Tree*): Tree =
call("" + nme.UNIVERSE_BUILD_PREFIX + name, args: _*)
+ def reifyBuildCall(name: TermName, args: Any*) =
+ mirrorBuildCall(name, args map reify: _*)
+
def mirrorMirrorCall(name: TermName, args: Tree*): Tree =
call("" + nme.MIRROR_PREFIX + name, args: _*)
diff --git a/src/compiler/scala/reflect/reify/package.scala b/src/compiler/scala/reflect/reify/package.scala
index d3cae3d123..30cfec8e2a 100644
--- a/src/compiler/scala/reflect/reify/package.scala
+++ b/src/compiler/scala/reflect/reify/package.scala
@@ -32,7 +32,7 @@ package object reify {
// If we're in the constructor of an object or others don't have easy access to `this`, we have no good way to grab
// the class of that object. Instead, we construct an anonymous class and grab his class file, assuming
// this is enough to get the correct class loadeer for the class we *want* a mirror for, the object itself.
- rClassTree orElse Apply(Select(treeBuilder.makeAnonymousNew(Nil), sn.GetClass), Nil)
+ rClassTree orElse Apply(Select(gen.mkAnonymousNew(Nil), sn.GetClass), Nil)
}
// JavaUniverse is defined in scala-reflect.jar, so we must be very careful in case someone reifies stuff having only scala-library.jar on the classpath
val isJavaUniverse = JavaUniverseClass != NoSymbol && universe.tpe <:< JavaUniverseClass.toTypeConstructor
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index 603f9af1b4..ea6543bb71 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -103,16 +103,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
typer.typed(mkCast(tree, pt))
}
- /** Trees fresh from the oven, mostly for use by the parser. */
- object treeBuilder extends {
- val global: Global.this.type = Global.this
- } with TreeBuilder {
- def freshName(prefix: String): Name = freshTermName(prefix)
- def freshTermName(prefix: String): TermName = currentUnit.freshTermName(prefix)
- def freshTypeName(prefix: String): TypeName = currentUnit.freshTypeName(prefix)
- def o2p(offset: Int): Position = new OffsetPosition(currentUnit.source, offset)
- def r2p(start: Int, mid: Int, end: Int): Position = rangePos(currentUnit.source, start, mid, end)
- }
+ /** A spare instance of TreeBuilder left for backwards compatibility. */
+ lazy val treeBuilder: TreeBuilder { val global: Global.this.type } = new syntaxAnalyzer.ParserTreeBuilder
/** Fold constants */
object constfold extends {
diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
index c28a6ba337..ad1977b9aa 100644
--- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala
+++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
@@ -112,7 +112,6 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {
else AppliedTypeTree(Ident(clazz), targs map TypeTree)
))
}
- def mkSuperInitCall: Select = Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR)
def wildcardStar(tree: Tree) =
atPos(tree.pos) { Typed(tree, Ident(tpnme.WILDCARD_STAR)) }
@@ -255,4 +254,52 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {
attrThis,
If(cond, Block(syncBody: _*), EmptyTree)) ::
stats: _*)
+
+ /** Creates a tree representing new Object { stats }.
+ * To make sure an anonymous subclass of Object is created,
+ * if there are no stats, a () is added.
+ */
+ def mkAnonymousNew(stats: List[Tree]): Tree = {
+ val stats1 = if (stats.isEmpty) List(Literal(Constant(()))) else stats
+ mkNew(Nil, emptyValDef, stats1, NoPosition, NoPosition)
+ }
+
+ /** Create positioned tree representing an object creation <new parents { stats }
+ * @param npos the position of the new
+ * @param cpos the position of the anonymous class starting with parents
+ */
+ def mkNew(parents: List[Tree], self: ValDef, stats: List[Tree],
+ npos: Position, cpos: Position): Tree =
+ if (parents.isEmpty)
+ mkNew(List(scalaAnyRefConstr), self, stats, npos, cpos)
+ else if (parents.tail.isEmpty && stats.isEmpty) {
+ // `Parsers.template` no longer differentiates tpts and their argss
+ // e.g. `C()` will be represented as a single tree Apply(Ident(C), Nil)
+ // instead of parents = Ident(C), argss = Nil as before
+ // this change works great for things that are actually templates
+ // but in this degenerate case we need to perform postprocessing
+ val app = treeInfo.dissectApplied(parents.head)
+ atPos(npos union cpos) { New(app.callee, app.argss) }
+ } else {
+ val x = tpnme.ANON_CLASS_NAME
+ atPos(npos union cpos) {
+ Block(
+ List(
+ atPos(cpos) {
+ ClassDef(
+ Modifiers(FINAL), x, Nil,
+ mkTemplate(parents, self, NoMods, ListOfNil, stats, cpos.focus))
+ }),
+ atPos(npos) {
+ New(
+ Ident(x) setPos npos.focus,
+ Nil)
+ }
+ )
+ }
+ }
+
+ def mkSyntheticParam(pname: TermName) =
+ ValDef(Modifiers(PARAM | SYNTHETIC), pname, TypeTree(), EmptyTree)
+
}
diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala
index 41d89aa3b4..641ab9c279 100644
--- a/src/compiler/scala/tools/nsc/ast/Trees.scala
+++ b/src/compiler/scala/tools/nsc/ast/Trees.scala
@@ -54,77 +54,6 @@ trait Trees extends scala.reflect.internal.Trees { self: Global =>
case xs :: rest => rest.foldLeft(Apply(gen.mkSuperInitCall, xs): Tree)(Apply.apply)
}
- /** Generates a template with constructor corresponding to
- *
- * constrmods (vparams1_) ... (vparams_n) preSuper { presupers }
- * extends superclass(args_1) ... (args_n) with mixins { self => body }
- *
- * This gets translated to
- *
- * extends superclass with mixins { self =>
- * presupers' // presupers without rhs
- * vparamss // abstract fields corresponding to value parameters
- * def <init>(vparamss) {
- * presupers
- * super.<init>(args)
- * }
- * body
- * }
- */
- def Template(parents: List[Tree], self: ValDef, constrMods: Modifiers, vparamss: List[List[ValDef]], body: List[Tree], superPos: Position): Template = {
- /* Add constructor to template */
-
- // create parameters for <init> as synthetic trees.
- var vparamss1 = mmap(vparamss) { vd =>
- atPos(vd.pos.focus) {
- val mods = Modifiers(vd.mods.flags & (IMPLICIT | DEFAULTPARAM | BYNAMEPARAM) | PARAM | PARAMACCESSOR)
- ValDef(mods withAnnotations vd.mods.annotations, vd.name, vd.tpt.duplicate, vd.rhs.duplicate)
- }
- }
- val (edefs, rest) = body span treeInfo.isEarlyDef
- val (evdefs, etdefs) = edefs partition treeInfo.isEarlyValDef
- val gvdefs = evdefs map {
- case vdef @ ValDef(_, _, tpt, _) =>
- copyValDef(vdef)(
- // atPos for the new tpt is necessary, since the original tpt might have no position
- // (when missing type annotation for ValDef for example), so even though setOriginal modifies the
- // position of TypeTree, it would still be NoPosition. That's what the author meant.
- tpt = atPos(vdef.pos.focus)(TypeTree() setOriginal tpt setPos tpt.pos.focus),
- rhs = EmptyTree
- )
- }
- val lvdefs = evdefs collect { case vdef: ValDef => copyValDef(vdef)(mods = vdef.mods | PRESUPER) }
-
- val constrs = {
- if (constrMods hasFlag TRAIT) {
- if (body forall treeInfo.isInterfaceMember) List()
- else List(
- atPos(wrappingPos(superPos, lvdefs)) (
- DefDef(NoMods, nme.MIXIN_CONSTRUCTOR, List(), ListOfNil, TypeTree(), Block(lvdefs, Literal(Constant(()))))))
- } else {
- // convert (implicit ... ) to ()(implicit ... ) if its the only parameter section
- if (vparamss1.isEmpty || !vparamss1.head.isEmpty && vparamss1.head.head.mods.isImplicit)
- vparamss1 = List() :: vparamss1
- val superCall = pendingSuperCall // we can't know in advance which of the parents will end up as a superclass
- // this requires knowing which of the parents is a type macro and which is not
- // and that's something that cannot be found out before typer
- // (the type macros aren't in the trunk yet, but there is a plan for them to land there soon)
- // this means that we don't know what will be the arguments of the super call
- // therefore here we emit a dummy which gets populated when the template is named and typechecked
- List(
- // TODO: previously this was `wrappingPos(superPos, lvdefs ::: argss.flatten)`
- // is it going to be a problem that we can no longer include the `argss`?
- atPos(wrappingPos(superPos, lvdefs)) (
- DefDef(constrMods, nme.CONSTRUCTOR, List(), vparamss1, TypeTree(), Block(lvdefs ::: List(superCall), Literal(Constant(()))))))
- }
- }
- constrs foreach (ensureNonOverlapping(_, parents ::: gvdefs, focus=false))
- // Field definitions for the class - remove defaults.
- val fieldDefs = vparamss.flatten map (vd => copyValDef(vd)(mods = vd.mods &~ DEFAULTPARAM, rhs = EmptyTree))
-
- Template(parents, self, gvdefs ::: fieldDefs ::: constrs ::: etdefs ::: rest)
- }
-
/** Construct class definition with given class symbol, value parameters,
* supercall arguments and template body.
*
@@ -143,9 +72,9 @@ trait Trees extends scala.reflect.internal.Trees { self: Global =>
)
ClassDef(sym,
- Template(sym.info.parents map TypeTree,
- if (sym.thisSym == sym || phase.erasedTypes) emptyValDef else ValDef(sym.thisSym),
- constrMods, vparamss, body, superPos))
+ gen.mkTemplate(sym.info.parents map TypeTree,
+ if (sym.thisSym == sym || phase.erasedTypes) emptyValDef else ValDef(sym.thisSym),
+ constrMods, vparamss, body, superPos))
}
// --- subcomponents --------------------------------------------------
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index ef5872986c..eb924a811b 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -26,13 +26,22 @@ import util.FreshNameCreator
* the beginnings of a campaign against this latest incursion by Cutty
* McPastington and his army of very similar soldiers.
*/
-trait ParsersCommon extends ScannersCommon {
+trait ParsersCommon extends ScannersCommon { self =>
val global : Global
import global._
def newLiteral(const: Any) = Literal(Constant(const))
def literalUnit = newLiteral(())
+ class ParserTreeBuilder extends TreeBuilder {
+ val global: self.global.type = self.global
+ def freshName(prefix: String): Name = freshTermName(prefix)
+ def freshTermName(prefix: String): TermName = currentUnit.freshTermName(prefix)
+ def freshTypeName(prefix: String): TypeName = currentUnit.freshTypeName(prefix)
+ def o2p(offset: Int): Position = new OffsetPosition(currentUnit.source, offset)
+ def r2p(start: Int, mid: Int, end: Int): Position = rangePos(currentUnit.source, start, mid, end)
+ }
+
/** This is now an abstract class, only to work around the optimizer:
* methods in traits are never inlined.
*/
@@ -147,6 +156,17 @@ self =>
def newScanner(): Scanner = new SourceFileScanner(source)
+ /** Scoping operator used to temporarily look into the future.
+ * Backs up scanner data before evaluating a block and restores it after.
+ */
+ def lookingAhead[T](body: => T): T = {
+ val snapshot = (new ScannerData{}).copyFrom(in)
+ in.nextToken()
+ val res = body
+ in copyFrom snapshot
+ res
+ }
+
val in = newScanner()
in.init()
@@ -290,6 +310,7 @@ self =>
/** whether a non-continuable syntax error has been seen */
private var lastErrorOffset : Int = -1
+ val treeBuilder = new ParserTreeBuilder
import treeBuilder.{global => _, _}
/** The types of the context bounds of type parameters of the surrounding class
@@ -399,7 +420,7 @@ self =>
def mainParamType = AppliedTypeTree(Ident(tpnme.Array), List(Ident(tpnme.String)))
def mainParameter = List(ValDef(Modifiers(Flags.PARAM), nme.argv, mainParamType, EmptyTree))
def mainSetArgv = List(ValDef(NoMods, nme.args, TypeTree(), Ident(nme.argv)))
- def mainDef = DefDef(NoMods, nme.main, Nil, List(mainParameter), scalaDot(tpnme.Unit), Block(mainSetArgv, makeAnonymousNew(stmts)))
+ def mainDef = DefDef(NoMods, nme.main, Nil, List(mainParameter), scalaDot(tpnme.Unit), Block(mainSetArgv, gen.mkAnonymousNew(stmts)))
// object Main
def moduleName = newTermName(ScriptRunner scriptMain settings)
@@ -604,6 +625,8 @@ self =>
case _ => false
}
+ def isAnnotation: Boolean = in.token == AT
+
def isLocalModifier: Boolean = in.token match {
case ABSTRACT | FINAL | SEALED | IMPLICIT | LAZY => true
case _ => false
@@ -731,7 +754,7 @@ self =>
}
@inline final def commaSeparated[T](part: => T): List[T] = tokenSeparated(COMMA, sepFirst = false, part)
@inline final def caseSeparated[T](part: => T): List[T] = tokenSeparated(CASE, sepFirst = true, part)
- @inline final def readAnnots[T](part: => T): List[T] = tokenSeparated(AT, sepFirst = true, part)
+ def readAnnots(part: => Tree): List[Tree] = tokenSeparated(AT, sepFirst = true, part)
/* --------- OPERAND/OPERATOR STACK --------------------------------------- */
@@ -1365,7 +1388,7 @@ self =>
} else {
syntaxErrorOrIncomplete("`*' expected", skipIt = true)
}
- } else if (in.token == AT) {
+ } else if (isAnnotation) {
t = (t /: annotations(skipNewLines = false))(makeAnnotated)
} else {
t = atPos(t.pos.startOrPoint, colonPos) {
@@ -1501,7 +1524,7 @@ self =>
val pname = freshName("x$")
in.nextToken()
val id = atPos(start) (Ident(pname))
- val param = atPos(id.pos.focus){ makeSyntheticParam(pname.toTermName) }
+ val param = atPos(id.pos.focus){ gen.mkSyntheticParam(pname.toTermName) }
placeholderParams = param :: placeholderParams
id
case LPAREN =>
@@ -1516,7 +1539,7 @@ self =>
val tstart = in.offset
val (parents, self, stats) = template()
val cpos = r2p(tstart, tstart, in.lastOffset max tstart)
- makeNew(parents, self, stats, npos, cpos)
+ gen.mkNew(parents, self, stats, npos, cpos)
case _ =>
syntaxErrorOrIncompleteAnd("illegal start of simple expression", skipIt = true)(errorTermTree)
}
@@ -1602,13 +1625,16 @@ self =>
*/
def block(): Tree = makeBlock(blockStatSeq())
+ def caseClause(): CaseDef =
+ atPos(in.offset)(makeCaseDef(pattern(), guard(), caseBlock()))
+
/** {{{
* CaseClauses ::= CaseClause {CaseClause}
* CaseClause ::= case Pattern [Guard] `=>' Block
* }}}
*/
def caseClauses(): List[CaseDef] = {
- val cases = caseSeparated { atPos(in.offset)(makeCaseDef(pattern(), guard(), caseBlock())) }
+ val cases = caseSeparated { caseClause() }
if (cases.isEmpty) // trigger error if there are no cases
accept(CASE)
@@ -2050,6 +2076,8 @@ self =>
/* -------- PARAMETERS ------------------------------------------- */
+ def allowTypelessParams = false
+
/** {{{
* ParamClauses ::= {ParamClause} [[nl] `(' implicit Params `)']
* ParamClause ::= [nl] `(' [Params] `)'
@@ -2086,7 +2114,7 @@ self =>
val name = ident()
var bynamemod = 0
val tpt =
- if (settings.YmethodInfer && !owner.isTypeName && in.token != COLON) {
+ if (((settings.YmethodInfer && !owner.isTypeName) || allowTypelessParams) && in.token != COLON) {
TypeTree()
} else { // XX-METHOD-INFER
accept(COLON)
@@ -2804,7 +2832,7 @@ self =>
if (inScalaRootPackage && ScalaValueClassNames.contains(name))
Template(parents0, self, anyvalConstructor :: body)
else
- Template(anyrefParents(), self, constrMods, vparamss, body, o2p(tstart))
+ gen.mkTemplate(anyrefParents(), self, constrMods, vparamss, body, o2p(tstart))
}
}
@@ -2867,7 +2895,7 @@ self =>
case IMPORT =>
in.flushDoc
importClause()
- case x if x == AT || isTemplateIntro || isModifier =>
+ case x if isAnnotation || isTemplateIntro || isModifier =>
joinComment(topLevelTmplDef :: Nil)
case _ =>
if (isStatSep) Nil
@@ -2923,11 +2951,11 @@ self =>
if (in.token == IMPORT) {
in.flushDoc
stats ++= importClause()
+ } else if (isDefIntro || isModifier || isAnnotation) {
+ stats ++= joinComment(nonLocalDefOrDcl)
} else if (isExprIntro) {
in.flushDoc
stats += statement(InTemplate)
- } else if (isDefIntro || isModifier || in.token == AT) {
- stats ++= joinComment(nonLocalDefOrDcl)
} else if (!isStatSep) {
syntaxErrorOrIncomplete("illegal start of definition", skipIt = true)
}
@@ -3007,7 +3035,7 @@ self =>
stats += statement(InBlock)
if (in.token != RBRACE && in.token != CASE) acceptStatSep()
}
- else if (isDefIntro || isLocalModifier || in.token == AT) {
+ else if (isDefIntro || isLocalModifier || isAnnotation) {
if (in.token == IMPLICIT) {
val start = in.skipToken()
if (isIdent) stats += implicitClosure(start, InBlock)
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
index 2dca39f7a3..03cdead472 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
@@ -5,7 +5,7 @@
package scala.tools.nsc
package ast.parser
-import scala.tools.nsc.util.CharArrayReader
+import scala.tools.nsc.util.{ CharArrayReader, CharArrayReaderData }
import scala.reflect.internal.util._
import scala.reflect.internal.Chars._
import Tokens._
@@ -71,17 +71,37 @@ trait Scanners extends ScannersCommon {
/** the base of a number */
var base: Int = 0
- def copyFrom(td: TokenData) = {
+ def copyFrom(td: TokenData): this.type = {
this.token = td.token
this.offset = td.offset
this.lastOffset = td.lastOffset
this.name = td.name
this.strVal = td.strVal
this.base = td.base
+ this
}
}
- abstract class Scanner extends CharArrayReader with TokenData with ScannerCommon {
+ /** An interface to most of mutable data in Scanner defined in TokenData
+ * and CharArrayReader (+ next, prev fields) with copyFrom functionality
+ * to backup/restore data (used by quasiquotes' lookingAhead).
+ */
+ trait ScannerData extends TokenData with CharArrayReaderData {
+ /** we need one token lookahead and one token history
+ */
+ val next: TokenData = new TokenData{}
+ val prev: TokenData = new TokenData{}
+
+ def copyFrom(sd: ScannerData): this.type = {
+ this.next copyFrom sd.next
+ this.prev copyFrom sd.prev
+ super[CharArrayReaderData].copyFrom(sd)
+ super[TokenData].copyFrom(sd)
+ this
+ }
+ }
+
+ abstract class Scanner extends CharArrayReader with TokenData with ScannerData with ScannerCommon {
private def isDigit(c: Char) = java.lang.Character isDigit c
private var openComments = 0
@@ -194,13 +214,6 @@ trait Scanners extends ScannersCommon {
cbuf.clear()
}
- private class TokenData0 extends TokenData
-
- /** we need one token lookahead and one token history
- */
- val next : TokenData = new TokenData0
- val prev : TokenData = new TokenData0
-
/** a stack of tokens which indicates whether line-ends can be statement separators
* also used for keeping track of nesting levels.
* We keep track of the closing symbol of a region. This can be
diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
index 0ef71fa1b5..666f19851d 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
@@ -190,50 +190,6 @@ abstract class TreeBuilder {
}
}
- /** Creates a tree representing new Object { stats }.
- * To make sure an anonymous subclass of Object is created,
- * if there are no stats, a () is added.
- */
- def makeAnonymousNew(stats: List[Tree]): Tree = {
- val stats1 = if (stats.isEmpty) List(Literal(Constant(()))) else stats
- makeNew(Nil, emptyValDef, stats1, NoPosition, NoPosition)
- }
-
- /** Create positioned tree representing an object creation <new parents { stats }
- * @param npos the position of the new
- * @param cpos the position of the anonymous class starting with parents
- */
- def makeNew(parents: List[Tree], self: ValDef, stats: List[Tree],
- npos: Position, cpos: Position): Tree =
- if (parents.isEmpty)
- makeNew(List(scalaAnyRefConstr), self, stats, npos, cpos)
- else if (parents.tail.isEmpty && stats.isEmpty) {
- // `Parsers.template` no longer differentiates tpts and their argss
- // e.g. `C()` will be represented as a single tree Apply(Ident(C), Nil)
- // instead of parents = Ident(C), argss = Nil as before
- // this change works great for things that are actually templates
- // but in this degenerate case we need to perform postprocessing
- val app = treeInfo.dissectApplied(parents.head)
- atPos(npos union cpos) { New(app.callee, app.argss) }
- } else {
- val x = tpnme.ANON_CLASS_NAME
- atPos(npos union cpos) {
- Block(
- List(
- atPos(cpos) {
- ClassDef(
- Modifiers(FINAL), x, Nil,
- Template(parents, self, NoMods, ListOfNil, stats, cpos.focus))
- }),
- atPos(npos) {
- New(
- Ident(x) setPos npos.focus,
- Nil)
- }
- )
- }
- }
-
/** Create a tree representing an assignment <lhs = rhs> */
def makeAssign(lhs: Tree, rhs: Tree): Tree = lhs match {
case Apply(fn, args) =>
@@ -303,9 +259,6 @@ abstract class TreeBuilder {
def makeParam(pname: TermName, tpe: Tree) =
ValDef(Modifiers(PARAM), pname, tpe, EmptyTree)
- def makeSyntheticParam(pname: TermName) =
- ValDef(Modifiers(PARAM | SYNTHETIC), pname, TypeTree(), EmptyTree)
-
def makeSyntheticTypeParam(pname: TypeName, bounds: Tree) =
TypeDef(Modifiers(DEFERRED | SYNTHETIC), pname, Nil, bounds)
@@ -467,7 +420,7 @@ abstract class TreeBuilder {
val x = freshTermName(prefix)
val id = Ident(x)
val sel = if (checkExhaustive) id else gen.mkUnchecked(id)
- Function(List(makeSyntheticParam(x)), Match(sel, cases))
+ Function(List(gen.mkSyntheticParam(x)), Match(sel, cases))
}
/** Create tree for case definition <case pat if guard => rhs> */
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index 321baba562..993f735c72 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -190,6 +190,7 @@ trait ScalaSettings extends AbsScalaSettings
val Yreifydebug = BooleanSetting("-Yreify-debug", "Trace reification.")
val Ytyperdebug = BooleanSetting("-Ytyper-debug", "Trace all type assignments.")
val Ypatmatdebug = BooleanSetting("-Ypatmat-debug", "Trace pattern matching translation.")
+ val Yquasiquotedebug = BooleanSetting("-Yquasiquote-debug", "Trace quasiquote-related activities.")
/** Groups of Settings.
*/
diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
index a3ab948171..81f5545695 100644
--- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
@@ -697,7 +697,7 @@ trait ContextErrors {
protected def macroExpansionError(expandee: Tree, msg: String, pos: Position = NoPosition) = {
def msgForLog = if (msg != null && (msg contains "exception during macro expansion")) msg.split(EOL).drop(1).headOption.getOrElse("?") else msg
macroLogLite("macro expansion has failed: %s".format(msgForLog))
- if (msg != null) context.error(pos, msg) // issueTypeError(PosAndMsgTypeError(..)) won't work => swallows positions
+ if (msg != null) context.error(if (pos.isDefined) pos else expandee.pos, msg) // issueTypeError(PosAndMsgTypeError(..)) won't work => swallows positions
setError(expandee)
throw MacroExpansionException
}
@@ -741,7 +741,7 @@ trait ContextErrors {
try {
// [Eugene] is there a better way?
// [Paul] See Exceptional.scala and Origins.scala.
- val relevancyThreshold = realex.getStackTrace().indexWhere(_.getMethodName endsWith "macroExpand1")
+ val relevancyThreshold = realex.getStackTrace().indexWhere(_.getMethodName endsWith "macroExpandWithRuntime")
if (relevancyThreshold == -1) None
else {
var relevantElements = realex.getStackTrace().take(relevancyThreshold + 1)
@@ -782,13 +782,16 @@ trait ContextErrors {
}
def MacroExpansionHasInvalidTypeError(expandee: Tree, expanded: Any) = {
+ def isUnaffiliatedExpr = expanded.isInstanceOf[scala.reflect.api.Exprs#Expr[_]]
+ def isUnaffiliatedTree = expanded.isInstanceOf[scala.reflect.api.Trees#TreeApi]
val expected = "expr or tree"
- val isPathMismatch = expanded != null && expanded.isInstanceOf[scala.reflect.api.Exprs#Expr[_]]
+ val actual = if (isUnaffiliatedExpr) "an expr" else if (isUnaffiliatedTree) "a tree" else "unexpected"
+ val isPathMismatch = expanded != null && (isUnaffiliatedExpr || isUnaffiliatedTree)
macroExpansionError(expandee,
s"macro must return a compiler-specific $expected; returned value is " + (
if (expanded == null) "null"
- else if (isPathMismatch) s" $expected, but it doesn't belong to this compiler"
- else " of " + expanded.getClass
+ else if (isPathMismatch) s"$actual, but it doesn't belong to this compiler's universe"
+ else "of " + expanded.getClass
))
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
index 86ba3d2164..6b9537e27d 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
@@ -142,7 +142,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
case Literal(Constant(s: String)) => s
case Literal(Constant(d: Double)) => d
case Literal(Constant(b: Boolean)) => b
- case Literal(Constant(i: Int)) => new Fingerprint(i)
+ case Literal(Constant(i: Int)) => Fingerprint(i)
}
def pickle(macroImplRef: Tree): Tree = {
@@ -464,9 +464,9 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
/** Describes the role that the macro expandee is performing.
*/
- type MacroRole = String
- final def APPLY_ROLE: MacroRole = "APPLY_ROLE"
- private val roleNames = Map(APPLY_ROLE -> "apply")
+ type MacroRole = scala.tools.nsc.typechecker.MacroRole
+ final def APPLY_ROLE = MacroRole.Apply
+ final def UNAPPLY_ROLE = MacroRole.Unapply
/** Performs macro expansion:
*
@@ -482,9 +482,10 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
* ========= Macro expansion =========
*
* First of all `macroExpandXXX`:
- * 1) If necessary desugars the `expandee` to fit into `macroExpand1`
+ * 1) If necessary desugars the `expandee` to fit into the default expansion scheme
+ * that is understood by `macroExpandWithRuntime` / `macroExpandWithoutRuntime`
*
- * Then `macroExpand1`:
+ * Then `macroExpandWithRuntime`:
* 2) Checks whether the expansion needs to be delayed
* 3) Loads macro implementation using `macroMirror`
* 4) Synthesizes invocation arguments for the macro implementation
@@ -532,26 +533,41 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
def summary() = s"expander = $this, expandee = ${showDetailed(expandee)}, desugared = ${if (expandee == desugared) () else showDetailed(desugared)}"
if (macroDebugVerbose) println(s"macroExpand: ${summary()}")
assert(allowExpandee(expandee), summary())
+ linkExpandeeAndDesugared(expandee, desugared, role)
val start = if (Statistics.canEnable) Statistics.startTimer(macroExpandNanos) else null
if (Statistics.canEnable) Statistics.incCounter(macroExpandCount)
try {
- linkExpandeeAndDesugared(expandee, desugared, role)
- macroExpand1(typer, desugared) match {
- case Success(expanded) =>
- if (allowExpanded(expanded)) {
- // also see http://groups.google.com/group/scala-internals/browse_thread/thread/492560d941b315cc
- val expanded1 = try onSuccess(duplicateAndKeepPositions(expanded)) finally popMacroContext()
- if (!hasMacroExpansionAttachment(expanded1)) linkExpandeeAndExpanded(expandee, expanded1)
- if (allowResult(expanded1)) expanded1 else onFailure(expanded)
- } else {
- typer.TyperErrorGen.MacroInvalidExpansionError(expandee, roleNames(role), allowedExpansions)
- onFailure(expanded)
+ withInfoLevel(nodePrinters.InfoLevel.Quiet) { // verbose printing might cause recursive macro expansions
+ if (expandee.symbol.isErroneous || (expandee exists (_.isErroneous))) {
+ val reason = if (expandee.symbol.isErroneous) "not found or incompatible macro implementation" else "erroneous arguments"
+ macroLogVerbose(s"cancelled macro expansion because of $reason: $expandee")
+ onFailure(typer.infer.setError(expandee))
+ } else try {
+ val expanded = {
+ val runtime = macroRuntime(expandee.symbol)
+ if (runtime != null) macroExpandWithRuntime(typer, expandee, runtime)
+ else macroExpandWithoutRuntime(typer, expandee)
+ }
+ expanded match {
+ case Success(expanded) =>
+ if (allowExpanded(expanded)) {
+ // also see http://groups.google.com/group/scala-internals/browse_thread/thread/492560d941b315cc
+ val expanded1 = try onSuccess(duplicateAndKeepPositions(expanded)) finally popMacroContext()
+ if (!hasMacroExpansionAttachment(expanded1)) linkExpandeeAndExpanded(expandee, expanded1)
+ if (allowResult(expanded1)) expanded1 else onFailure(expanded)
+ } else {
+ typer.TyperErrorGen.MacroInvalidExpansionError(expandee, role.name, allowedExpansions)
+ onFailure(expanded)
+ }
+ case Fallback(fallback) => onFallback(fallback)
+ case Delayed(delayed) => onDelayed(delayed)
+ case Skipped(skipped) => onSkipped(skipped)
+ case Failure(failure) => onFailure(failure)
}
- case Fallback(fallback) => onFallback(fallback)
- case Delayed(delayed) => onDelayed(delayed)
- case Skipped(skipped) => onSkipped(skipped)
- case Failure(failure) => onFailure(failure)
+ } catch {
+ case typer.TyperErrorGen.MacroExpansionException => onFailure(expandee)
+ }
}
} finally {
if (Statistics.canEnable) Statistics.stopTimer(macroExpandNanos, start)
@@ -622,8 +638,21 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
expander(expandee)
}
- /** Captures statuses of macro expansions performed by `macroExpand1'.
+ /** Expands a term macro used in unapply role as `u.Quasiquote(StringContext("", "")).q.unapply(x)` in `case q"$x" => ...`.
+ * @see MacroExpander
*/
+ def macroExpandUnapply(typer: Typer, original: Tree, fun: Tree, unapply: Symbol, args: List[Tree], mode: Mode, pt: Type) = {
+ val expandee = treeCopy.Apply(original, gen.mkAttributedSelect(fun, unapply), args)
+ object expander extends TermMacroExpander(UNAPPLY_ROLE, typer, expandee, mode, pt) {
+ override def allowedExpansions: String = "unapply trees"
+ override def allowExpandee(expandee: Tree) = expandee.isInstanceOf[Apply]
+ private def unsupported(what: String) = abort("unapply macros currently don't support " + what)
+ override def onFallback(fallback: Tree) = unsupported("fallback")
+ override def onDelayed(delayed: Tree) = unsupported("advanced interaction with type inference")
+ }
+ expander(original)
+ }
+
private sealed abstract class MacroStatus(val result: Tree)
private case class Success(expanded: Tree) extends MacroStatus(expanded)
private case class Fallback(fallback: Tree) extends MacroStatus(fallback) { currentRun.seenMacroExpansionsFallingBack = true }
@@ -632,28 +661,6 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
private case class Failure(failure: Tree) extends MacroStatus(failure)
private def Delay(expanded: Tree) = Delayed(expanded)
private def Skip(expanded: Tree) = Skipped(expanded)
- private def Cancel(expandee: Tree) = Failure(expandee)
-
- /** Does the same as `macroExpand`, but without typechecking the expansion
- * Meant for internal use within the macro infrastructure, don't use it elsewhere.
- */
- private def macroExpand1(typer: Typer, expandee: Tree): MacroStatus = {
- // verbose printing might cause recursive macro expansions, so I'm shutting it down here
- withInfoLevel(nodePrinters.InfoLevel.Quiet) {
- if (expandee.symbol.isErroneous || (expandee exists (_.isErroneous))) {
- val reason = if (expandee.symbol.isErroneous) "not found or incompatible macro implementation" else "erroneous arguments"
- macroLogVerbose(s"cancelled macro expansion because of $reason: $expandee")
- Cancel(typer.infer.setError(expandee))
- }
- else try {
- val runtime = macroRuntime(expandee.symbol)
- if (runtime != null) macroExpandWithRuntime(typer, expandee, runtime)
- else macroExpandWithoutRuntime(typer, expandee)
- } catch {
- case typer.TyperErrorGen.MacroExpansionException => Failure(expandee)
- }
- }
- }
/** Expands a macro when a runtime (i.e. the macro implementation) can be successfully loaded
* Meant for internal use within the macro infrastructure, don't use it elsewhere.
@@ -804,7 +811,7 @@ object MacrosStats {
val macroExpandNanos = Statistics.newSubTimer("time spent in macroExpand", typerNanos)
}
-class Fingerprint(val value: Int) extends AnyVal {
+class Fingerprint private[Fingerprint](val value: Int) extends AnyVal {
def paramPos = { assert(isTag, this); value }
def isTag = value >= 0
def isOther = this == Other
@@ -819,8 +826,18 @@ class Fingerprint(val value: Int) extends AnyVal {
}
object Fingerprint {
+ def apply(value: Int) = new Fingerprint(value)
def Tagged(tparamPos: Int) = new Fingerprint(tparamPos)
val Other = new Fingerprint(-1)
val LiftedTyped = new Fingerprint(-2)
val LiftedUntyped = new Fingerprint(-3)
}
+
+class MacroRole private[MacroRole](val name: String) extends AnyVal {
+ override def toString = name
+}
+
+object MacroRole {
+ val Apply = new MacroRole("apply")
+ val Unapply = new MacroRole("unapply")
+}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 0ae68d2ba1..1a9a30c2ad 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -3380,8 +3380,9 @@ trait Typers extends Adaptations with Tags {
if (!tree.isErrorTyped) setError(tree) else tree
// @H change to setError(treeCopy.Apply(tree, fun, args))
- case otpe if mode.inPatternMode && unapplyMember(otpe).exists =>
- doTypedUnapply(tree, fun0, fun, args, mode, pt)
+ case ExtractorType(unapply) if mode.inPatternMode =>
+ if (unapply == QuasiquoteClass_api_unapply) macroExpandUnapply(this, tree, fun, unapply, args, mode, pt)
+ else doTypedUnapply(tree, fun0, fun, args, mode, pt)
case _ =>
if (treeInfo.isMacroApplication(tree)) duplErrorTree(MacroTooManyArgumentListsError(tree, fun.symbol))
diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala
index af3f772f79..47c859bb5c 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala
@@ -51,6 +51,14 @@ trait Unapplies extends ast.TreeDSL
case NoSymbol => tp member nme.unapplySeq
case unapp => unapp
}
+
+ object ExtractorType {
+ def unapply(tp: Type): Option[Symbol] = {
+ val member = unapplyMember(tp)
+ if (member.exists) Some(member) else None
+ }
+ }
+
/** returns unapply member's parameter type. */
def unapplyParameterType(extractor: Symbol) = extractor.tpe.params match {
case p :: Nil => p.tpe.typeSymbol
@@ -142,7 +150,7 @@ trait Unapplies extends ast.TreeDSL
ModuleDef(
Modifiers(cdef.mods.flags & AccessFlags | SYNTHETIC, cdef.mods.privateWithin),
cdef.name.toTermName,
- Template(parents, emptyValDef, NoMods, Nil, body, cdef.impl.pos.focus))
+ gen.mkTemplate(parents, emptyValDef, NoMods, Nil, body, cdef.impl.pos.focus))
}
private val caseMods = Modifiers(SYNTHETIC | CASE)
diff --git a/src/compiler/scala/tools/nsc/util/CharArrayReader.scala b/src/compiler/scala/tools/nsc/util/CharArrayReader.scala
index 5c6f525c6f..f116e4af34 100644
--- a/src/compiler/scala/tools/nsc/util/CharArrayReader.scala
+++ b/src/compiler/scala/tools/nsc/util/CharArrayReader.scala
@@ -8,15 +8,7 @@ package util
import scala.reflect.internal.Chars._
-abstract class CharArrayReader { self =>
-
- val buf: Array[Char]
-
- def decodeUni: Boolean = true
-
- /** An error routine to call on bad unicode escapes \\uxxxx. */
- protected def error(offset: Int, msg: String): Unit
-
+trait CharArrayReaderData {
/** the last read character */
var ch: Char = _
@@ -29,7 +21,26 @@ abstract class CharArrayReader { self =>
/** The start offset of the line before the current one */
var lastLineStartOffset: Int = 0
- private var lastUnicodeOffset = -1
+ protected var lastUnicodeOffset = -1
+
+ def copyFrom(cd: CharArrayReaderData): this.type = {
+ this.ch = cd.ch
+ this.charOffset = cd.charOffset
+ this.lineStartOffset = cd.lineStartOffset
+ this.lastLineStartOffset = cd.lastLineStartOffset
+ this.lastUnicodeOffset = cd.lastUnicodeOffset
+ this
+ }
+}
+
+abstract class CharArrayReader extends CharArrayReaderData { self =>
+
+ val buf: Array[Char]
+
+ def decodeUni: Boolean = true
+
+ /** An error routine to call on bad unicode escapes \\uxxxx. */
+ protected def error(offset: Int, msg: String): Unit
/** Is last character a unicode escape \\uxxxx? */
def isUnicodeEscape = charOffset == lastUnicodeOffset
diff --git a/src/compiler/scala/tools/reflect/FastTrack.scala b/src/compiler/scala/tools/reflect/FastTrack.scala
index 5a0ff4f6db..ad1d4c896b 100644
--- a/src/compiler/scala/tools/reflect/FastTrack.scala
+++ b/src/compiler/scala/tools/reflect/FastTrack.scala
@@ -5,7 +5,7 @@ import scala.reflect.reify.Taggers
import scala.tools.nsc.typechecker.{ Analyzer, Macros }
import scala.reflect.runtime.Macros.currentMirror
import scala.reflect.api.Universe
-import scala.reflect.macros.compiler.DefaultMacroCompiler
+import scala.tools.reflect.quasiquotes.{ Quasiquotes => QuasiquoteImpls }
/** Optimizes system macro expansions by hardwiring them directly to their implementations
* bypassing standard reflective load and invoke to avoid the overhead of Java/Scala reflection.
@@ -22,6 +22,8 @@ trait FastTrack {
new { val c: c0.type = c0 } with Taggers
private implicit def context2macroimplementations(c0: MacroContext): MacroImplementations { val c: c0.type } =
new { val c: c0.type = c0 } with MacroImplementations
+ private implicit def context2quasiquote(c0: MacroContext): QuasiquoteImpls { val c: c0.type } =
+ new { val c: c0.type = c0 } with QuasiquoteImpls
private def make(sym: Symbol)(pf: PartialFunction[Applied, MacroContext => Tree]) =
sym -> new FastTrackEntry(pf)
@@ -41,6 +43,8 @@ trait FastTrack {
make( materializeTypeTag) { case Applied(_, ttag :: Nil, (u :: _) :: _) => _.materializeTypeTag(u, EmptyTree, ttag.tpe, concrete = true) },
make( ApiUniverseReify) { case Applied(_, ttag :: Nil, (expr :: _) :: _) => c => c.materializeExpr(c.prefix.tree, EmptyTree, expr) },
make( StringContext_f) { case Applied(Select(Apply(_, ps), _), _, args) => c => c.macro_StringInterpolation_f(ps, args.flatten, c.expandee.pos) },
- make(ReflectRuntimeCurrentMirror) { case _ => c => currentMirror(c).tree }
+ make(ReflectRuntimeCurrentMirror) { case _ => c => currentMirror(c).tree },
+ make( QuasiquoteClass_api_apply) { case _ => _.expandQuasiquote },
+ make(QuasiquoteClass_api_unapply) { case _ => _.expandQuasiquote }
)
}
diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
index c53d10bd87..afaca3396c 100644
--- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
+++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
@@ -217,7 +217,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>
val moduledef = ModuleDef(
obj,
- Template(
+ gen.mkTemplate(
List(TypeTree(ObjectTpe)),
emptyValDef,
NoMods,
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala b/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala
new file mode 100644
index 0000000000..9d171d52d2
--- /dev/null
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala
@@ -0,0 +1,187 @@
+package scala.tools.reflect
+package quasiquotes
+
+import scala.collection.{immutable, mutable}
+import scala.reflect.internal.Flags._
+
+class Cardinality private[Cardinality](val value: Int) extends AnyVal {
+ def pred = { assert(value - 1 >= 0); new Cardinality(value - 1) }
+ def succ = new Cardinality(value + 1)
+ override def toString = if (value == 0) "no dots" else "." * (value + 1)
+}
+
+object Cardinality {
+ val NoDot = new Cardinality(0)
+ val DotDot = new Cardinality(1)
+ val DotDotDot = new Cardinality(2)
+ object Dot { def unapply(card: Cardinality) = card != NoDot }
+ def parseDots(part: String) = {
+ if (part.endsWith("...")) (part.stripSuffix("..."), DotDotDot)
+ else if (part.endsWith("..")) (part.stripSuffix(".."), DotDot)
+ else (part, NoDot)
+ }
+}
+
+/** Defines abstractions that provide support for splicing into Scala syntax.
+ */
+trait Holes { self: Quasiquotes =>
+ import global._
+ import Cardinality._
+ 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)
+ }
+ }
+
+ /** 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.
+ */
+ case class HoleType(preprocessor: Tree => Tree, location: Location, cardinality: Cardinality) {
+ def makeHole(tree: Tree) = Hole(preprocessor(tree), location, 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
+ }
+
+ 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, elementTpe) = parseCardinality(tpe)
+ if (card != NoDot) Some(reifyIterable(_, card)) else None
+ }
+ }
+
+ 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
+ }
+ }
+
+ object LiftableType extends HoleTypeExtractor {
+ def preprocessor(tpe: Type) = lifter(tpe)
+ def location(tpe: Type) = Some(TreeLocation(treeType))
+ }
+
+ 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
+ }
+ }
+
+ object IterableLiftableType extends HoleTypeExtractor {
+ def preprocessor(tpe: Type) = {
+ val (_, elementTpe) = parseCardinality(tpe)
+ for {
+ lifter <- this.lifter(elementTpe)
+ iterator <- this.iterator(tpe)(lifter)
+ } yield iterator
+ }
+ def location(tpe: Type) = Some(IterableLocation(cardinality(tpe), TreeLocation(treeType)))
+ }
+ }
+
+ /** 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 (splicee.tpe == null) 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)
+ }
+ }
+
+ def parseCardinality(tpe: Type): (Cardinality, Type) = {
+ if (tpe != null && isIterableType(tpe)) {
+ val (card, innerTpe) = parseCardinality(tpe.typeArguments.head)
+ (card.succ, innerTpe)
+ } else (NoDot, tpe)
+ }
+} \ 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
new file mode 100644
index 0000000000..9a6ba56c18
--- /dev/null
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
@@ -0,0 +1,134 @@
+package scala.tools.reflect
+package quasiquotes
+
+import scala.tools.nsc.ast.parser.{Parsers => ScalaParser}
+import scala.tools.nsc.ast.parser.Tokens._
+import scala.compat.Platform.EOL
+import scala.reflect.internal.util.{BatchSourceFile, SourceFile}
+import scala.collection.mutable.ListBuffer
+
+/** Builds upon the vanilla Scala parser and teams up together with Placeholders.scala to emulate holes.
+ * A principled solution to splicing into Scala syntax would be a parser that natively supports holes.
+ * Unfortunately, that's outside of our reach in Scala 2.11, so we have to emulate.
+ */
+trait Parsers { self: Quasiquotes =>
+ import global._
+
+ abstract class Parser extends {
+ val global: self.global.type = self.global
+ } with ScalaParser {
+ /** Wraps given code to obtain a desired parser mode.
+ * This way we can just re-use standard parser entry point.
+ */
+ def wrapCode(code: String): String =
+ s"object wrapper { self => $EOL $code $EOL }"
+
+ def unwrapTree(wrappedTree: Tree): Tree = {
+ val PackageDef(_, List(ModuleDef(_, _, Template(_, _, _ :: parsed)))) = wrappedTree
+ parsed match {
+ case tree :: Nil => tree
+ case stats :+ tree => Block(stats, tree)
+ }
+ }
+
+ def parse(code: String): Tree = {
+ try {
+ val wrapped = wrapCode(code)
+ debug(s"wrapped code\n=${wrapped}\n")
+ val file = new BatchSourceFile(nme.QUASIQUOTE_FILE, wrapped)
+ val tree = new QuasiquoteParser(file).parse()
+ unwrapTree(tree)
+ } catch {
+ case mi: MalformedInput => c.abort(c.macroApplication.pos, s"syntax error: ${mi.msg}")
+ }
+ }
+
+ class QuasiquoteParser(source0: SourceFile) extends SourceFileParser(source0) {
+ override val treeBuilder = new ParserTreeBuilder {
+ // q"(..$xs)"
+ override def makeTupleTerm(trees: List[Tree], flattenUnary: Boolean): Tree =
+ Apply(Ident(nme.QUASIQUOTE_TUPLE), trees)
+
+ // tq"(..$xs)"
+ override def makeTupleType(trees: List[Tree], flattenUnary: Boolean): Tree =
+ AppliedTypeTree(Ident(tpnme.QUASIQUOTE_TUPLE), trees)
+
+ // q"{ $x }"
+ override def makeBlock(stats: List[Tree]): Tree = stats match {
+ case (head @ Ident(name)) :: Nil if holeMap.contains(name) => Block(Nil, head)
+ case _ => super.makeBlock(stats)
+ }
+ }
+ import treeBuilder.{global => _, _}
+
+ // q"def foo($x)"
+ override def allowTypelessParams = true
+
+ // q"foo match { case $x }"
+ override def caseClause(): CaseDef =
+ if (isHole && lookingAhead { in.token == CASE || in.token == RBRACE || in.token == SEMI }) {
+ val c = makeCaseDef(Apply(Ident(nme.QUASIQUOTE_CASE), List(Ident(ident()))), EmptyTree, EmptyTree)
+ while (in.token == SEMI) in.nextToken()
+ c
+ } else
+ super.caseClause()
+
+ def isHole = isIdent && holeMap.contains(in.name)
+
+ override def isAnnotation: Boolean = super.isAnnotation || (isHole && lookingAhead { isAnnotation })
+
+ override def isModifier: Boolean = super.isModifier || (isHole && lookingAhead { isModifier })
+
+ override def isLocalModifier: Boolean = super.isLocalModifier || (isHole && lookingAhead { isLocalModifier })
+
+ override def isTemplateIntro: Boolean = super.isTemplateIntro || (isHole && lookingAhead { isTemplateIntro })
+
+ override def isDclIntro: Boolean = super.isDclIntro || (isHole && lookingAhead { isDclIntro })
+
+ // $mods def foo
+ // $mods T
+ override def readAnnots(annot: => Tree): List[Tree] = in.token match {
+ case AT =>
+ in.nextToken()
+ annot :: readAnnots(annot)
+ case _ if isHole && lookingAhead { in.token == AT || isModifier || isDefIntro || isIdent} =>
+ val ann = Apply(Select(New(Ident(tpnme.QUASIQUOTE_MODS)), nme.CONSTRUCTOR), List(Literal(Constant(in.name.toString))))
+ in.nextToken()
+ ann :: readAnnots(annot)
+ case _ =>
+ Nil
+ }
+ }
+ }
+
+ object TermParser extends Parser
+
+ object CaseParser extends Parser {
+ override def wrapCode(code: String) = super.wrapCode("something match { case " + code + " }")
+
+ override def unwrapTree(wrappedTree: Tree): Tree = {
+ val Match(_, head :: tail) = super.unwrapTree(wrappedTree)
+ if (tail.nonEmpty)
+ c.abort(c.macroApplication.pos, "Can't parse more than one casedef, consider generating a match tree instead")
+ head
+ }
+ }
+
+ object PatternParser extends Parser {
+ override def wrapCode(code: String) = super.wrapCode("something match { case " + code + " => }")
+
+ override def unwrapTree(wrappedTree: Tree): Tree = {
+ val Match(_, List(CaseDef(pat, _, _))) = super.unwrapTree(wrappedTree)
+ pat
+ }
+ }
+
+ object TypeParser extends Parser {
+ override def wrapCode(code: String) = super.wrapCode("type T = " + code)
+
+ override def unwrapTree(wrappedTree: Tree): Tree = {
+ val TypeDef(_, _, _, rhs) = super.unwrapTree(wrappedTree)
+ rhs
+ }
+ }
+} \ No newline at end of file
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
new file mode 100644
index 0000000000..b680c25f76
--- /dev/null
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
@@ -0,0 +1,123 @@
+package scala.tools.reflect
+package quasiquotes
+
+import java.util.UUID.randomUUID
+import scala.collection.{immutable, mutable}
+
+/** Emulates hole support (see Holes.scala) in the quasiquote parser (see Parsers.scala).
+ * A principled solution to splicing into Scala syntax would be a parser that natively supports holes.
+ * Unfortunately, that's outside of our reach in Scala 2.11, so we have to emulate.
+ * This trait stores knowledge of how to represent the holes as something understandable by the parser
+ * and how to recover holes from the results of parsing the produced representation.
+ */
+trait Placeholders { self: Quasiquotes =>
+ import global._
+ import Cardinality._
+
+ // Step 1: Transform Scala source with holes into vanilla Scala source
+
+ lazy val holeMap = new HoleMap()
+ lazy val code = {
+ val sb = new StringBuilder()
+ val sessionSuffix = randomUUID().toString.replace("-", "").substring(0, 8) + "$"
+
+ foreach2(args, parts.init) { (tree, p) =>
+ val (part, cardinality) = parseDots(p)
+ val placeholderName = c.freshName(TermName(nme.QUASIQUOTE_PREFIX + sessionSuffix))
+ sb.append(part)
+ sb.append(placeholderName)
+ holeMap(placeholderName) = Hole(tree, cardinality)
+ }
+ sb.append(parts.last)
+
+ sb.toString
+ }
+
+ class HoleMap {
+ private val underlying = mutable.ListMap[String, Hole]()
+ private val accessed = mutable.Set[String]()
+ def unused: Set[Name] = (underlying.keys.toSet -- accessed).map(TermName(_))
+ def contains(key: Name) = underlying.contains(key.toString)
+ def apply(key: Name) = {
+ val s = key.toString
+ accessed += s
+ underlying(s)
+ }
+ def update(key: Name, hole: Hole) = {
+ underlying += key.toString -> hole
+ }
+ def get(key: Name) = {
+ val s = key.toString
+ accessed += s
+ underlying.get(s)
+ }
+ }
+
+ // Step 2: Transform vanilla Scala AST into an AST with holes
+
+ trait HolePlaceholder {
+ def matching: PartialFunction[Any, Name]
+ def unapply(scrutinee: Any): Option[(Tree, Location, Cardinality)] = {
+ val name = matching.lift(scrutinee)
+ name.flatMap { holeMap.get(_).map { case Hole(repr, loc, card) => (repr, loc, card) } }
+ }
+ }
+
+ object Placeholder extends HolePlaceholder {
+ def matching = {
+ case name: Name => name
+ case Ident(name) => name
+ case Bind(name, Ident(nme.WILDCARD)) => name
+ case TypeDef(_, name, List(), TypeBoundsTree(EmptyTree, EmptyTree)) => name
+ case ValDef(_, name, TypeTree(), EmptyTree) => name
+ }
+ }
+
+ object ModsPlaceholder extends HolePlaceholder {
+ def matching = {
+ case Apply(Select(New(Ident(tpnme.QUASIQUOTE_MODS)), nme.CONSTRUCTOR), List(Literal(Constant(s: String)))) => TermName(s)
+ }
+ }
+
+ object AnnotPlaceholder {
+ def unapply(tree: Tree): Option[(Tree, Location, Cardinality, List[Tree])] = tree match {
+ case Apply(Select(New(Placeholder(tree, loc, card)), nme.CONSTRUCTOR), args) => Some(tree, loc, card, args)
+ case _ => None
+ }
+ }
+
+ object TuplePlaceholder {
+ def unapply(tree: Tree): Option[List[Tree]] = tree match {
+ case Apply(Ident(nme.QUASIQUOTE_TUPLE), args) => Some(args)
+ case _ => None
+ }
+ }
+
+ object TupleTypePlaceholder {
+ def unapply(tree: Tree): Option[List[Tree]] = tree match {
+ case AppliedTypeTree(Ident(tpnme.QUASIQUOTE_TUPLE), args) => Some(args)
+ case _ => None
+ }
+ }
+
+ object SymbolPlaceholder {
+ def unapply(scrutinee: Any): Option[Tree] = scrutinee match {
+ case Placeholder(tree, SymbolLocation, _) => Some(tree)
+ 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))
+ case _ => None
+ }
+ }
+
+ object ClassPlaceholder {
+ def unapply(tree: Tree): Option[Tree] = tree match {
+ case ClassDef(_, _, _, _) => Some(tree)
+ case _ => None
+ }
+ }
+} \ No newline at end of file
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala b/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala
new file mode 100644
index 0000000000..fe954e0bfd
--- /dev/null
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala
@@ -0,0 +1,51 @@
+package scala.tools.reflect
+package quasiquotes
+
+import scala.reflect.macros.runtime.Context
+
+abstract class Quasiquotes extends Parsers
+ with Holes
+ with Placeholders
+ with Reifiers {
+ val c: Context
+ val global: c.universe.type = c.universe
+ import c.universe._
+
+ def debug(msg: String): Unit =
+ if (settings.Yquasiquotedebug.value) println(msg)
+
+ lazy val (universe: Tree, args, parts, parse, reify) = c.macroApplication match {
+ case Apply(Select(Select(Apply(Select(universe0, _), List(Apply(_, parts0))), interpolator0), method0), args0) =>
+ val parts1 = parts0.map {
+ case Literal(Constant(s: String)) => s
+ case part => c.abort(part.pos, "Quasiquotes can only be used with literal strings")
+ }
+ val reify0 = method0 match {
+ case nme.apply => new ApplyReifier().reifyFillingHoles(_)
+ case nme.unapply => new UnapplyReifier().reifyFillingHoles(_)
+ case other => global.abort(s"Unknown quasiquote api method: $other")
+ }
+ val parse0 = interpolator0 match {
+ case nme.q => TermParser.parse(_)
+ case nme.tq => TypeParser.parse(_)
+ case nme.cq => CaseParser.parse(_)
+ case nme.pq => PatternParser.parse(_)
+ case other => global.abort(s"Unknown quasiquote flavor: $other")
+ }
+ (universe0, args0, parts1, parse0, reify0)
+ case _ =>
+ global.abort(s"Couldn't parse call prefix tree ${c.macroApplication}.")
+ }
+
+ lazy val u = universe // shortcut
+ lazy val universeTypes = new definitions.UniverseDependentTypes(universe)
+
+ def expandQuasiquote = {
+ debug(s"\ncode to parse=\n$code\n")
+ val tree = parse(code)
+ debug(s"parsed tree\n=${tree}\n=${showRaw(tree)}\n")
+ val reified = reify(tree)
+ debug(s"reified tree\n=${reified}\n=${showRaw(reified)}\n")
+ reified
+ }
+}
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
new file mode 100644
index 0000000000..ec113036a3
--- /dev/null
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
@@ -0,0 +1,290 @@
+package scala.tools.reflect
+package quasiquotes
+
+import java.lang.UnsupportedOperationException
+import scala.reflect.reify.{Reifier => ReflectReifier}
+import scala.reflect.internal.Flags._
+
+trait Reifiers { self: Quasiquotes =>
+ import global._
+ import global.build.SyntacticClassDef
+ import global.treeInfo._
+ import global.definitions._
+ import Cardinality._
+ import universeTypes._
+
+ abstract class Reifier extends {
+ val global: self.global.type = self.global
+ } with ReflectReifier {
+ val reifee = EmptyTree
+ val universe = self.universe
+ val mirror = EmptyTree
+ val concrete = false
+ lazy val typer = throw new UnsupportedOperationException
+
+ def isReifyingExpressions: Boolean
+ def isReifyingPatterns: Boolean = !isReifyingExpressions
+ def action = if (isReifyingExpressions) "splice" else "extract"
+ def holesHaveTypes = isReifyingExpressions
+
+ 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")
+ }
+ reified
+ }
+
+ override def reifyTree(tree: Tree): Tree = {
+ val reified =
+ reifyTreePlaceholder(tree) orElse
+ reifyTreeSyntactically(tree)
+ //println(s"reified ${showRaw(tree)} as $reified")
+ reified
+ }
+
+ 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 TuplePlaceholder(args) => reifyTuple(args)
+ case TupleTypePlaceholder(args) => reifyTupleType(args)
+ case CasePlaceholder(tree, location, _) => reifyCase(tree, location)
+ case ClassPlaceholder(tree) => reifyClass(tree)
+ case _ => EmptyTree
+ }
+
+ 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 _ =>
+ 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.TupleN, 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
+ case List(other) => reify(other)
+ case _ => reifyBuildCall(nme.TupleN, args)
+ }
+
+ 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.TupleTypeN, args)
+ case List(other) => reify(other)
+ case _ => reifyBuildCall(nme.TupleTypeN, args)
+ }
+
+ def reifyClass(tree: Tree) = {
+ val SyntacticClassDef(mods, name, tparams, constrmods, argss, parents, selfval, body) = tree
+ reifyBuildCall(nme.SyntacticClassDef, mods, name, tparams, constrmods, argss, parents, selfval, body)
+ }
+
+ /** Splits list into a list of groups where subsequent elements are considered
+ * similar by the corresponding function.
+ *
+ * Example:
+ *
+ * > group(List(1, 1, 0, 0, 1, 0)) { _ == _ }
+ * List(List(1, 1), List(0, 0), List(1), List(0))
+ *
+ */
+ def group[T](lst: List[T])(similar: (T, T) => Boolean) = lst.foldLeft[List[List[T]]](List()) {
+ case (Nil, el) => List(List(el))
+ case (ll :+ (last @ (lastinit :+ lastel)), el) if similar(lastel, el) => ll :+ (last :+ el)
+ case (ll, el) => ll :+ List(el)
+ }
+
+ /** Reifies list filling all the valid holeMap.
+ *
+ * Reification of non-trivial list is done in two steps:
+ *
+ * 1. split the list into groups where every placeholder is always
+ * put in a group of it's own and all subsquent non-holeMap are
+ * grouped together; element is considered to be a placeholder if it's
+ * in the domain of the fill function;
+ *
+ * 2. fold the groups into a sequence of lists added together with ++ using
+ * fill reification for holeMap and fallback reification for non-holeMap.
+ *
+ * Example:
+ *
+ * reifyMultiCardinalityList(lst) {
+ * // first we define patterns that extract high-cardinality holeMap (currently ..)
+ * case Placeholder(CorrespondsTo(tree, tpe)) if tpe <:< iterableTreeType => tree
+ * } {
+ * // in the end we define how single elements are reified, typically with default reify call
+ * reify(_)
+ * }
+ *
+ * Sample execution of previous concrete list reifier:
+ *
+ * > val lst = List(foo, bar, qq$f3948f9s$1)
+ * > reifyMultiCardinalityList(lst) { ... } { ... }
+ * q"List($foo, $bar) ++ ${holeMap(qq$f3948f9s$1).tree}"
+ */
+ def reifyMultiCardinalityList[T](xs: List[T])(fill: PartialFunction[T, Tree])(fallback: T => Tree): Tree
+
+ /** Reifies arbitrary list filling ..$x and ...$y holeMap when they are put
+ * in the correct position. Fallbacks to regular reification for non-high cardinality
+ * elements.
+ */
+ override def reifyList(xs: List[Any]): Tree = reifyMultiCardinalityList(xs) {
+ case Placeholder(tree, _, DotDot) => tree
+ case CasePlaceholder(tree, _, DotDot) => tree
+ case List(Placeholder(tree, _, DotDotDot)) => tree
+ } {
+ reify(_)
+ }
+
+ def reifyAnnotList(annots: List[Tree]): Tree
+
+ def ensureNoExplicitFlags(m: Modifiers, pos: Position) =
+ if ((m.flags & ExplicitFlags) != 0L) c.abort(pos, s"Can't $action modifiers together with flags, consider merging flags into modifiers")
+
+ override def mirrorSelect(name: String): Tree =
+ Select(universe, TermName(name))
+
+ override def mirrorCall(name: TermName, args: Tree*): Tree =
+ Apply(Select(universe, name), args.toList)
+
+ 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 Block(stats, p @ Placeholder(_, _, _)) => reifyBuildCall(nme.Block, stats :+ p)
+ case Apply(f, List(Placeholder(argss, _, DotDotDot))) => reifyCallWithArgss(f, argss)
+ case RefTree(qual, SymbolPlaceholder(tree)) => mirrorBuildCall(nme.RefTree, reify(qual), tree)
+ case _ => super.reifyTreeSyntactically(tree)
+ }
+
+ def reifyCallWithArgss(f: Tree, argss: Tree) = {
+ val f1 = reifyTree(f)
+ val foldLeftF1 = Apply(TypeApply(Select(argss, nme.foldLeft), List(Select(u, tpnme.Tree))), List(f1))
+ val uDotApply = Function(
+ List(gen.mkSyntheticParam(nme.x_1), gen.mkSyntheticParam(nme.x_2)),
+ Apply(Select(u, nme.Apply), List(Ident(nme.x_1), Ident(nme.x_2))))
+ Apply(foldLeftF1, List(uDotApply))
+ }
+
+ override def reifyMultiCardinalityList[T](xs: List[T])(fill: PartialFunction[T, Tree])(fallback: T => Tree): Tree = xs match {
+ case Nil => mkList(Nil)
+ case _ =>
+ 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 reifyAnnotList(annots: List[Tree]): Tree = reifyMultiCardinalityList(annots) {
+ case AnnotPlaceholder(tree, _, DotDot, args) =>
+ val x: TermName = c.freshName()
+ val xToAnnotationCtor = Function(
+ List(ValDef(Modifiers(PARAM), x, TypeTree(), EmptyTree)),
+ mirrorBuildCall(nme.mkAnnotationCtor, Ident(x), reify(args)))
+ Apply(Select(tree, nme.map), List(xToAnnotationCtor))
+ } {
+ case AnnotPlaceholder(tree, _: TreeLocation, _, args) =>
+ mirrorBuildCall(nme.mkAnnotationCtor, tree, reify(args))
+ case other => reify(other)
+ }
+
+ override def reifyModifiers(m: Modifiers) = {
+ val (modsPlaceholders, annots) = m.annotations.partition {
+ 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")
+ }
+ }
+ 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 _ =>
+ val baseFlags = reifyBuildCall(nme.flagsFromBits, m.flags)
+ val reifiedFlags = flags.foldLeft[Tree](baseFlags) { case (flag, (tree, _)) => Apply(Select(flag, nme.OR), List(tree)) }
+ mirrorFactoryCall(nme.Modifiers, reifiedFlags, reify(m.privateWithin), reifyAnnotList(annots))
+ }
+ }
+ }
+
+ class UnapplyReifier extends Reifier {
+ def isReifyingExpressions = false
+
+ override def reifyTreeSyntactically(tree: Tree): Tree = tree match {
+ case treeInfo.Applied(fun, Nil, argss) if fun != tree && !tree.isInstanceOf[AppliedTypeTree] =>
+ reifyBuildCall(nme.Applied, fun, argss)
+ case treeInfo.Applied(fun, targs, argss) if fun != tree & !tree.isInstanceOf[AppliedTypeTree] =>
+ mirrorBuildCall(nme.Applied, reifyBuildCall(nme.TypeApplied, fun, targs), reifyList(argss))
+ 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) = 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)
+ Apply(cons, List(fallback(el), rest))
+ }
+ case _ =>
+ mkList(xs.map(fallback))
+ }
+
+ override def reifyAnnotList(annots: List[Tree]): Tree = reifyMultiCardinalityList(annots) {
+ case AnnotPlaceholder(tree, _, DotDot, Nil) => tree
+ } {
+ case AnnotPlaceholder(tree, _, NoDot, Nil) => tree
+ case AnnotPlaceholder(tree, _, NoDot, args) =>
+ val selectCONSTRUCTOR = Apply(Select(u, nme.Select), List(Apply(Select(u, nme.New), List(tree)), Select(Select(u, nme.nmeNme), nme.nmeCONSTRUCTOR)))
+ Apply(Select(u, nme.Apply), List(selectCONSTRUCTOR, reify(args)))
+ case other =>
+ reify(other)
+ }
+
+ override def reifyModifiers(m: Modifiers) = {
+ val mods = m.annotations.collect { case ModsPlaceholder(tree, _, _) => tree }
+ 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 Nil =>
+ mirrorFactoryCall(nme.Modifiers, reifyBuildCall(nme.FlagsAsBits, m.flags),
+ reify(m.privateWithin), reifyAnnotList(m.annotations))
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/reflect/scala/reflect/api/BuildUtils.scala b/src/reflect/scala/reflect/api/BuildUtils.scala
index 2e95a01176..c568cf74c0 100644
--- a/src/reflect/scala/reflect/api/BuildUtils.scala
+++ b/src/reflect/scala/reflect/api/BuildUtils.scala
@@ -66,6 +66,8 @@ private[reflect] trait BuildUtils { self: Universe =>
def Ident(sym: Symbol): Ident
+ def Block(stats: List[Tree]): Block
+
def TypeTree(tp: Type): TypeTree
def thisPrefix(sym: Symbol): Type
@@ -73,5 +75,45 @@ private[reflect] trait BuildUtils { self: Universe =>
def setType[T <: Tree](tree: T, tpe: Type): T
def setSymbol[T <: Tree](tree: T, sym: Symbol): T
+
+ def mkAnnotationCtor(tree: Tree, args: List[Tree]): Tree
+
+ val FlagsAsBits: FlagsAsBitsExtractor
+
+ trait FlagsAsBitsExtractor {
+ def unapply(flags: Long): Option[Long]
+ }
+
+ val TypeApplied: TypeAppliedExtractor
+
+ trait TypeAppliedExtractor {
+ def unapply(tree: Tree): Some[(Tree, List[Tree])]
+ }
+
+ val Applied: AppliedExtractor
+
+ trait AppliedExtractor {
+ def unapply(tree: Tree): Some[(Tree, List[List[Tree]])]
+ }
+
+ val SyntacticClassDef: SyntacticClassDefExtractor
+
+ trait SyntacticClassDefExtractor {
+ def apply(mods: Modifiers, name: TypeName, tparams: List[TypeDef],
+ constrMods: Modifiers, vparamss: List[List[ValDef]], parents: List[Tree],
+ selfdef: ValDef, body: List[Tree]): Tree
+ def unapply(tree: Tree): Option[(Modifiers, TypeName, List[TypeDef], Modifiers,
+ List[List[ValDef]], List[Tree], ValDef, List[Tree])]
+ }
+
+ val TupleN: TupleNExtractor
+ val TupleTypeN: TupleNExtractor
+
+ trait TupleNExtractor {
+ def apply(args: List[Tree]): Tree
+ def unapply(tree: Tree): Option[List[Tree]]
+ }
+
+ def RefTree(qual: Tree, sym: Symbol): Tree
}
}
diff --git a/src/reflect/scala/reflect/api/Liftable.scala b/src/reflect/scala/reflect/api/Liftable.scala
new file mode 100644
index 0000000000..8f6fe066dd
--- /dev/null
+++ b/src/reflect/scala/reflect/api/Liftable.scala
@@ -0,0 +1,32 @@
+package scala.reflect
+package api
+
+trait Liftable[T] {
+ def apply(universe: api.Universe, value: T): universe.Tree
+}
+
+object Liftable {
+ private class LiftableConstant[T] extends Liftable[T] {
+ def apply(universe: Universe, value: T): universe.Tree =
+ universe.Literal(universe.Constant(value))
+ }
+
+ implicit lazy val liftByte: Liftable[Byte] = new LiftableConstant[Byte]
+ implicit lazy val liftShort: Liftable[Short] = new LiftableConstant[Short]
+ implicit lazy val liftChar: Liftable[Char] = new LiftableConstant[Char]
+ implicit lazy val liftInt: Liftable[Int] = new LiftableConstant[Int]
+ implicit lazy val liftLong: Liftable[Long] = new LiftableConstant[Long]
+ implicit lazy val liftFloat: Liftable[Float] = new LiftableConstant[Float]
+ implicit lazy val liftDouble: Liftable[Double] = new LiftableConstant[Double]
+ implicit lazy val liftBoolean: Liftable[Boolean] = new LiftableConstant[Boolean]
+ implicit lazy val liftString: Liftable[String] = new LiftableConstant[String]
+ implicit lazy val liftUnit: Liftable[Unit] = new LiftableConstant[Unit]
+
+ implicit lazy val liftScalaSymbol: Liftable[scala.Symbol] = new Liftable[scala.Symbol] {
+ def apply(universe: Universe, value: scala.Symbol): universe.Tree = {
+ import universe._
+ val symbol = Select(Ident(TermName("scala")), TermName("Symbol"))
+ Apply(symbol, List(Literal(Constant(value.name))))
+ }
+ }
+}
diff --git a/src/reflect/scala/reflect/api/Quasiquotes.scala b/src/reflect/scala/reflect/api/Quasiquotes.scala
new file mode 100644
index 0000000000..3895b8b95f
--- /dev/null
+++ b/src/reflect/scala/reflect/api/Quasiquotes.scala
@@ -0,0 +1,20 @@
+package scala.reflect
+package api
+
+import language.experimental.macros
+
+trait Quasiquotes { self: Universe =>
+
+ // implementation is hardwired to `dispatch` method of `scala.tools.reflect.quasiquotes.Quasiquotes`
+ // using the mechanism implemented in `scala.tools.reflect.FastTrack`
+ implicit class Quasiquote(ctx: StringContext) {
+ protected trait api {
+ def apply(args: Any*): Any = macro ???
+ def unapply(subpatterns: Any*): Option[Any] = macro ???
+ }
+ object q extends api
+ object tq extends api
+ object cq extends api
+ object pq extends api
+ }
+}
diff --git a/src/reflect/scala/reflect/api/StandardLiftables.scala b/src/reflect/scala/reflect/api/StandardLiftables.scala
new file mode 100644
index 0000000000..ecea550225
--- /dev/null
+++ b/src/reflect/scala/reflect/api/StandardLiftables.scala
@@ -0,0 +1,36 @@
+package scala.reflect
+package api
+
+trait StandardLiftables { self: Universe =>
+
+ private def requireSameUniverse[T](universe: Universe, tp: String, value: T) =
+ require(universe eq self, s"Can't lift $tp ${showRaw(value)} from universe ${showRaw(universe)} using lift$tp defined for ${showRaw(self)}.")
+
+ implicit def liftExpr[T <: Expr[_]]: Liftable[T] = new Liftable[T] {
+ def apply(universe: Universe, value: T): universe.Tree = {
+ requireSameUniverse(universe, "Expr", value)
+ value.tree.asInstanceOf[universe.Tree]
+ }
+ }
+
+ implicit def liftType[T <: Type]: Liftable[T] = new Liftable[T] {
+ def apply(universe: Universe, value: T): universe.Tree = {
+ requireSameUniverse(universe, "Type", value)
+ universe.TypeTree(value.asInstanceOf[universe.Type])
+ }
+ }
+
+ implicit def liftTypeTag[T <: WeakTypeTag[_]]: Liftable[T] = new Liftable[T] {
+ def apply(universe: Universe, value: T): universe.Tree = {
+ requireSameUniverse(universe, "TypeTag", value)
+ universe.TypeTree(value.asInstanceOf[universe.WeakTypeTag[_]].tpe)
+ }
+ }
+
+ implicit def liftConstant[T <: Constant]: Liftable[T] = new Liftable[T] {
+ def apply(universe: Universe, value: T): universe.Tree = {
+ requireSameUniverse(universe, "Constant", value)
+ universe.Literal(value.asInstanceOf[universe.Constant])
+ }
+ }
+}
diff --git a/src/reflect/scala/reflect/api/Universe.scala b/src/reflect/scala/reflect/api/Universe.scala
index cb629f9c5a..77b4827eab 100644
--- a/src/reflect/scala/reflect/api/Universe.scala
+++ b/src/reflect/scala/reflect/api/Universe.scala
@@ -72,10 +72,12 @@ abstract class Universe extends Symbols
with ImplicitTags
with StandardDefinitions
with StandardNames
+ with StandardLiftables
with BuildUtils
with Mirrors
with Printers
with Importers
+ with Quasiquotes
{
/** Use `refiy` to produce the abstract syntax tree representing a given Scala expression.
*
diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala
index ece2d28be3..cdebfe52f8 100644
--- a/src/reflect/scala/reflect/internal/BuildUtils.scala
+++ b/src/reflect/scala/reflect/internal/BuildUtils.scala
@@ -2,7 +2,10 @@ package scala
package reflect
package internal
+import Flags._
+
trait BuildUtils { self: SymbolTable =>
+ import definitions.{TupleClass, MaxTupleArity, ScalaPackage, UnitClass}
class BuildImpl extends BuildApi {
@@ -52,6 +55,12 @@ trait BuildUtils { self: SymbolTable =>
def Ident(sym: Symbol): Ident = self.Ident(sym)
+ def Block(stats: List[Tree]): Block = stats match {
+ case Nil => self.Block(Nil, Literal(Constant(())))
+ case elem :: Nil => self.Block(Nil, elem)
+ case elems => self.Block(elems.init, elems.last)
+ }
+
def TypeTree(tp: Type): TypeTree = self.TypeTree(tp)
def thisPrefix(sym: Symbol): Type = sym.thisPrefix
@@ -59,6 +68,117 @@ trait BuildUtils { self: SymbolTable =>
def setType[T <: Tree](tree: T, tpe: Type): T = { tree.setType(tpe); tree }
def setSymbol[T <: Tree](tree: T, sym: Symbol): T = { tree.setSymbol(sym); tree }
+
+ def mkAnnotationCtor(tree: Tree, args: List[Tree]): Tree = tree match {
+ case ident: Ident => Apply(self.Select(New(ident), nme.CONSTRUCTOR: TermName), args)
+ case call @ Apply(Select(New(ident: Ident), nme.CONSTRUCTOR), _) =>
+ if (args.nonEmpty)
+ throw new IllegalArgumentException("Can't splice annotation that already contains args with extra args, consider merging these lists together")
+ call
+ case _ => throw new IllegalArgumentException(s"Tree ${showRaw(tree)} isn't a correct representation of annotation, consider passing Ident as a first argument")
+ }
+
+ object FlagsAsBits extends FlagsAsBitsExtractor {
+ def unapply(flags: Long): Some[Long] = Some(flags)
+ }
+
+ object TypeApplied extends TypeAppliedExtractor {
+ def unapply(tree: Tree): Some[(Tree, List[Tree])] = tree match {
+ case TypeApply(fun, targs) => Some((fun, targs))
+ case _ => Some((tree, Nil))
+ }
+ }
+
+ object Applied extends AppliedExtractor {
+ def unapply(tree: Tree): Some[(Tree, List[List[Tree]])] = {
+ val treeInfo.Applied(fun, targs, argss) = tree
+ targs match {
+ case Nil => Some((fun, argss))
+ case _ => Some((TypeApply(fun, targs), argss))
+ }
+ }
+ }
+
+ object SyntacticClassDef extends SyntacticClassDefExtractor {
+ def apply(mods: Modifiers, name: TypeName, tparams: List[TypeDef],
+ constrMods: Modifiers, vparamss: List[List[ValDef]], parents: List[Tree],
+ selfdef: ValDef, body: List[Tree]): Tree =
+ ClassDef(mods, name, tparams, gen.mkTemplate(parents, selfdef, constrMods, vparamss, body, NoPosition))
+
+ def unapply(tree: Tree): Option[(Modifiers, TypeName, List[TypeDef], Modifiers,
+ List[List[ValDef]], List[Tree], ValDef, List[Tree])] = tree match {
+ case ClassDef(mods, name, tparams, Template(parents, selfdef, tbody)) =>
+ // extract generated fieldDefs and constructor
+ val (defs, (ctor: DefDef) :: body) = tbody.splitAt(tbody.indexWhere {
+ case DefDef(_, nme.CONSTRUCTOR, _, _, _, _) => true
+ case _ => false
+ })
+ val (earlyDefs, fieldDefs) = defs.span(treeInfo.isEarlyDef)
+
+ // undo conversion from (implicit ... ) to ()(implicit ... ) when its the only parameter section
+ val vparamssRestoredImplicits = ctor.vparamss match {
+ case Nil :: rest if !rest.isEmpty && !rest.head.isEmpty && rest.head.head.mods.isImplicit => rest
+ case other => other
+ }
+
+ // undo flag modifications by mergeing flag info from constructor args and fieldDefs
+ val modsMap = fieldDefs.map { case ValDef(mods, name, _, _) => name -> mods }.toMap
+ val vparamss = mmap(vparamssRestoredImplicits) { vd =>
+ val originalMods = modsMap(vd.name) | (vd.mods.flags & DEFAULTPARAM)
+ atPos(vd.pos)(ValDef(originalMods, vd.name, vd.tpt, vd.rhs))
+ }
+
+ Some((mods, name, tparams, ctor.mods, vparamss, parents, selfdef, earlyDefs ::: body))
+ case _ =>
+ None
+ }
+ }
+
+ object TupleN extends TupleNExtractor {
+ def apply(args: List[Tree]): Tree = args match {
+ case Nil => Literal(Constant(()))
+ case _ =>
+ require(args.length <= MaxTupleArity, s"Tuples with arity bigger than $MaxTupleArity aren't supported")
+ self.Apply(TupleClass(args.length).companionModule, args: _*)
+ }
+
+ def unapply(tree: Tree): Option[List[Tree]] = tree match {
+ case Literal(Constant(())) =>
+ Some(Nil)
+ case Apply(id: Ident, args)
+ if args.length <= MaxTupleArity && id.symbol == TupleClass(args.length).companionModule =>
+ Some(args)
+ case Apply(Select(Ident(nme.scala_), TermName(tuple)), args)
+ if args.length <= MaxTupleArity && tuple == TupleClass(args.length).name =>
+ Some(args)
+ case _ =>
+ None
+ }
+ }
+
+ object TupleTypeN extends TupleNExtractor {
+ def apply(args: List[Tree]): Tree = args match {
+ case Nil => self.Select(self.Ident(nme.scala_), tpnme.Unit)
+ case _ =>
+ require(args.length <= MaxTupleArity, s"Tuples with arity bigger than $MaxTupleArity aren't supported")
+ AppliedTypeTree(Ident(TupleClass(args.length)), args)
+ }
+
+ def unapply(tree: Tree): Option[List[Tree]] = tree match {
+ case Select(Ident(nme.scala_), tpnme.Unit) =>
+ Some(Nil)
+ case AppliedTypeTree(id: Ident, args)
+ if args.length <= MaxTupleArity && id.symbol == TupleClass(args.length) =>
+ Some(args)
+ case AppliedTypeTree(Select(id @ Ident(nme.scala_), TermName(tuple)), args)
+ if args.length <= MaxTupleArity && id.symbol == ScalaPackage && tuple == TupleClass(args.length).name =>
+ Some(args)
+ case _ =>
+ None
+ }
+ }
+
+ def RefTree(qual: Tree, sym: Symbol) = self.RefTree(qual, sym.name) setSymbol sym
}
val build: BuildApi = new BuildImpl
diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala
index bf8ef79a63..6a9fa9a884 100644
--- a/src/reflect/scala/reflect/internal/Definitions.scala
+++ b/src/reflect/scala/reflect/internal/Definitions.scala
@@ -442,6 +442,7 @@ trait Definitions extends api.StandardDefinitions {
// collections classes
lazy val ConsClass = requiredClass[scala.collection.immutable.::[_]]
lazy val IteratorClass = requiredClass[scala.collection.Iterator[_]]
+ lazy val IterableClass = requiredClass[scala.collection.Iterable[_]]
lazy val ListClass = requiredClass[scala.collection.immutable.List[_]]
lazy val SeqClass = requiredClass[scala.collection.Seq[_]]
lazy val StringBuilderClass = requiredClass[scala.collection.mutable.StringBuilder]
@@ -491,10 +492,8 @@ trait Definitions extends api.StandardDefinitions {
lazy val TreesClass = getClassIfDefined("scala.reflect.api.Trees") // defined in scala-reflect.jar, so we need to be careful
lazy val TreesTreeType = TreesClass.map(sym => getTypeMember(sym, tpnme.Tree))
- object TreeType {
- def unapply(tpe: Type): Boolean = unapply(tpe.typeSymbol)
- def unapply(sym: Symbol): Boolean = sym.overrideChain contains TreesTreeType
- }
+ object TreeType { def unapply(tpe: Type): Boolean = tpe.typeSymbol.overrideChain contains TreesTreeType }
+ object SubtreeType { def unapply(tpe: Type): Boolean = tpe.typeSymbol.overrideChain exists (_.tpe <:< TreesTreeType.tpe) }
lazy val ExprsClass = getClassIfDefined("scala.reflect.api.Exprs") // defined in scala-reflect.jar, so we need to be careful
lazy val ExprClass = ExprsClass.map(sym => getMemberClass(sym, tpnme.Expr))
@@ -520,6 +519,7 @@ trait Definitions extends api.StandardDefinitions {
lazy val TypeCreatorClass = getClassIfDefined("scala.reflect.api.TypeCreator") // defined in scala-reflect.jar, so we need to be careful
lazy val TreeCreatorClass = getClassIfDefined("scala.reflect.api.TreeCreator") // defined in scala-reflect.jar, so we need to be careful
+ lazy val LiftableClass = getClassIfDefined("scala.reflect.api.Liftable") // defined in scala-reflect.jar, so we need to be careful
lazy val MacroClass = getClassIfDefined("scala.reflect.macros.Macro") // defined in scala-reflect.jar, so we need to be careful
lazy val MacroContextClass = getClassIfDefined("scala.reflect.macros.Context") // defined in scala-reflect.jar, so we need to be careful
@@ -534,6 +534,11 @@ trait Definitions extends api.StandardDefinitions {
lazy val StringContextClass = requiredClass[scala.StringContext]
def StringContext_f = getMemberMethod(StringContextClass, nme.f)
+ lazy val QuasiquoteClass = if (ApiUniverseClass != NoSymbol) getMember(ApiUniverseClass, tpnme.Quasiquote) else NoSymbol
+ lazy val QuasiquoteClass_api = if (QuasiquoteClass != NoSymbol) getMember(QuasiquoteClass, tpnme.api) else NoSymbol
+ lazy val QuasiquoteClass_api_apply = if (QuasiquoteClass_api != NoSymbol) getMember(QuasiquoteClass_api, nme.apply) else NoSymbol
+ lazy val QuasiquoteClass_api_unapply = if (QuasiquoteClass_api != NoSymbol) getMember(QuasiquoteClass_api, nme.unapply) else NoSymbol
+
lazy val ScalaSignatureAnnotation = requiredClass[scala.reflect.ScalaSignature]
lazy val ScalaLongSignatureAnnotation = requiredClass[scala.reflect.ScalaLongSignature]
@@ -645,6 +650,10 @@ trait Definitions extends api.StandardDefinitions {
isNonTrivial && isMacroCompatible
}
+ def isLiftableType(tp: Type) = tp <:< classExistentialType(LiftableClass)
+
+ def isIterableType(tp: Type) = tp <:< classExistentialType(IterableClass)
+
lazy val ProductRootClass: ClassSymbol = requiredClass[scala.Product]
def Product_productArity = getMemberMethod(ProductRootClass, nme.productArity)
def Product_productElement = getMemberMethod(ProductRootClass, nme.productElement)
@@ -1185,5 +1194,28 @@ trait Definitions extends api.StandardDefinitions {
val _ = symbolsNotPresentInBytecode
isInitialized = true
} //init
+
+ class UniverseDependentTypes(universe: Tree) {
+ lazy val universeType = universe.tpe
+ lazy val universeSym = universe.symbol
+ lazy val nameType = universeMemberType(tpnme.Name)
+ lazy val termNameType = universeMemberType(tpnme.TypeName)
+ lazy val typeNameType = universeMemberType(tpnme.TermName)
+ lazy val modsType = universeMemberType(tpnme.Modifiers)
+ lazy val flagsType = universeMemberType(tpnme.FlagSet)
+ lazy val symbolType = universeMemberType(tpnme.Symbol)
+ lazy val treeType0 = universeMemberType(tpnme.Tree)
+ lazy val treeType = universeMemberType(tpnme.Tree)
+ lazy val typeDefType = universeMemberType(tpnme.TypeDef)
+ lazy val caseDefType = universeMemberType(tpnme.CaseDef)
+ lazy val iterableTreeType = appliedType(IterableClass, treeType)
+ lazy val iterableCaseDefType = appliedType(IterableClass, caseDefType)
+ lazy val iterableIterableTreeType = appliedType(IterableClass, iterableTreeType)
+ lazy val listTreeType = appliedType(ListClass, treeType)
+ lazy val listListTreeType = appliedType(ListClass, listTreeType)
+ lazy val optionTreeType = appliedType(OptionClass, treeType)
+ lazy val optionNameType = appliedType(OptionClass, nameType)
+ def universeMemberType(name: TypeName) = universe.tpe.memberType(getTypeMember(universe.symbol, name))
+ }
}
}
diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala
index 30aaaa1f3a..64713b8d41 100644
--- a/src/reflect/scala/reflect/internal/StdNames.scala
+++ b/src/reflect/scala/reflect/internal/StdNames.scala
@@ -216,23 +216,39 @@ trait StdNames {
final val Any: NameType = "Any"
final val AnyVal: NameType = "AnyVal"
+ final val FlagSet: NameType = "FlagSet"
final val Mirror: NameType = "Mirror"
+ final val Modifiers: NameType = "Modifiers"
final val Nothing: NameType = "Nothing"
final val Null: NameType = "Null"
final val Object: NameType = "Object"
+ final val Option: NameType = "Option"
final val PrefixType: NameType = "PrefixType"
final val Product: NameType = "Product"
final val Serializable: NameType = "Serializable"
final val Singleton: NameType = "Singleton"
final val Throwable: NameType = "Throwable"
+ final val api: NameType = "api"
final val Annotation: NameType = "Annotation"
+ final val CaseDef: NameType = "CaseDef"
final val ClassfileAnnotation: NameType = "ClassfileAnnotation"
final val ClassManifest: NameType = "ClassManifest"
final val Enum: NameType = "Enum"
final val Group: NameType = "Group"
+ final val Name: NameType = "Name"
final val Tree: NameType = "Tree"
+ final val TermName: NameType = "TermName"
final val Type : NameType = "Type"
+ final val TypeName: NameType = "TypeName"
+ final val TypeDef: NameType = "TypeDef"
+ final val Tuple: NameType = "Tuple"
+ final val Universe: NameType = "Universe"
+ final val Quasiquote: NameType = "Quasiquote"
+
+ // quasiquote-specific names
+ final val QUASIQUOTE_MODS: NameType = "$quasiquote$mods$"
+ final val QUASIQUOTE_TUPLE: NameType = "$quasiquote$tuple$"
// Annotation simple names, used in Namer
final val BeanPropertyAnnot: NameType = "BeanProperty"
@@ -304,6 +320,10 @@ trait StdNames {
val REIFY_FREE_THIS_SUFFIX: NameType = "$this"
val REIFY_FREE_VALUE_SUFFIX: NameType = "$value"
val REIFY_SYMDEF_PREFIX: NameType = "symdef$"
+ val QUASIQUOTE_PREFIX: String = "qq$"
+ val QUASIQUOTE_FILE: String = "<quasiquote>"
+ val QUASIQUOTE_TUPLE: NameType = "$quasiquote$tuple$"
+ val QUASIQUOTE_CASE: NameType = "$quasiquote$case$"
val MIXIN_CONSTRUCTOR: NameType = "$init$"
val MODULE_INSTANCE_FIELD: NameType = NameTransformer.MODULE_INSTANCE_NAME // "MODULE$"
val OUTER: NameType = "$outer"
@@ -539,17 +559,23 @@ trait StdNames {
val Annotation: NameType = "Annotation"
val Any: NameType = "Any"
val AnyVal: NameType = "AnyVal"
+ val Apply: NameType = "Apply"
+ val Applied: NameType = "Applied"
val ArrayAnnotArg: NameType = "ArrayAnnotArg"
+ val Block: NameType = "Block"
val ConstantType: NameType = "ConstantType"
val EmptyPackage: NameType = "EmptyPackage"
val EmptyPackageClass: NameType = "EmptyPackageClass"
+ val False : NameType = "False"
val Flag : NameType = "Flag"
+ val FlagsAsBits: NameType = "FlagsAsBits"
val Ident: NameType = "Ident"
val Import: NameType = "Import"
val Literal: NameType = "Literal"
val LiteralAnnotArg: NameType = "LiteralAnnotArg"
val Modifiers: NameType = "Modifiers"
val NestedAnnotArg: NameType = "NestedAnnotArg"
+ val New: NameType = "New"
val NoFlags: NameType = "NoFlags"
val NoSymbol: NameType = "NoSymbol"
val Nothing: NameType = "Nothing"
@@ -560,10 +586,15 @@ trait StdNames {
val Select: NameType = "Select"
val SelectFromTypeTree: NameType = "SelectFromTypeTree"
val StringContext: NameType = "StringContext"
+ val SyntacticClassDef: NameType = "SyntacticClassDef"
val This: NameType = "This"
val ThisType: NameType = "ThisType"
+ val True : NameType = "True"
val Tuple2: NameType = "Tuple2"
+ val TupleN: NameType = "TupleN"
+ val TupleTypeN: NameType = "TupleTypeN"
val TYPE_ : NameType = "TYPE"
+ val TypeApplied: NameType = "TypeApplied"
val TypeRef: NameType = "TypeRef"
val TypeTree: NameType = "TypeTree"
val UNIT : NameType = "UNIT"
@@ -593,6 +624,7 @@ trait StdNames {
val checkInitialized: NameType = "checkInitialized"
val classOf: NameType = "classOf"
val clone_ : NameType = "clone"
+ val collection: NameType = "collection"
val conforms: NameType = "conforms"
val copy: NameType = "copy"
val create: NameType = "create"
@@ -619,10 +651,13 @@ trait StdNames {
val find_ : NameType = "find"
val flagsFromBits : NameType = "flagsFromBits"
val flatMap: NameType = "flatMap"
+ val flatten: NameType = "flatten"
+ val foldLeft: NameType = "foldLeft"
val foreach: NameType = "foreach"
val get: NameType = "get"
val hashCode_ : NameType = "hashCode"
val hash_ : NameType = "hash"
+ val immutable: NameType = "immutable"
val implicitly: NameType = "implicitly"
val in: NameType = "in"
val inlinedEquals: NameType = "inlinedEquals"
@@ -644,12 +679,15 @@ trait StdNames {
val materializeWeakTypeTag: NameType = "materializeWeakTypeTag"
val materializeTypeTag: NameType = "materializeTypeTag"
val moduleClass : NameType = "moduleClass"
+ val mkAnnotationCtor: NameType = "mkAnnotationCtor"
val ne: NameType = "ne"
val newArray: NameType = "newArray"
val newFreeTerm: NameType = "newFreeTerm"
val newFreeType: NameType = "newFreeType"
val newNestedSymbol: NameType = "newNestedSymbol"
val newScopeWith: NameType = "newScopeWith"
+ val nmeCONSTRUCTOR: NameType = "CONSTRUCTOR"
+ val nmeNme: NameType = "nme"
val notifyAll_ : NameType = "notifyAll"
val notify_ : NameType = "notify"
val null_ : NameType = "null"
@@ -665,6 +703,7 @@ trait StdNames {
val runtime: NameType = "runtime"
val runtimeClass: NameType = "runtimeClass"
val runtimeMirror: NameType = "runtimeMirror"
+ val RefTree: NameType = "RefTree"
val scala_ : NameType = "scala"
val selectDynamic: NameType = "selectDynamic"
val selectOverloadedMethod: NameType = "selectOverloadedMethod"
@@ -684,6 +723,7 @@ trait StdNames {
val this_ : NameType = "this"
val thisPrefix : NameType = "thisPrefix"
val toArray: NameType = "toArray"
+ val toList: NameType = "toList"
val toObjectArray : NameType = "toObjectArray"
val TopScope: NameType = "TopScope"
val toString_ : NameType = "toString"
@@ -694,6 +734,7 @@ trait StdNames {
val typedProductIterator: NameType = "typedProductIterator"
val TypeName: NameType = "TypeName"
val typeTagToManifest: NameType = "typeTagToManifest"
+
val unapply: NameType = "unapply"
val unapplySeq: NameType = "unapplySeq"
val unbox: NameType = "unbox"
@@ -708,6 +749,12 @@ trait StdNames {
val withFilter: NameType = "withFilter"
val zero: NameType = "zero"
+ // quasiquote interpolators:
+ val q: NameType = "q"
+ val tq: NameType = "tq"
+ val cq: NameType = "cq"
+ val pq: NameType = "pq"
+
// unencoded operators
object raw {
final val BANG : NameType = "!"
@@ -744,6 +791,7 @@ trait StdNames {
val ADD = encode("+")
val AND = encode("&")
val ASR = encode(">>")
+ val CONS = encode("::")
val DIV = encode("/")
val EQ = encode("==")
val EQL = encode("=")
@@ -760,6 +808,7 @@ trait StdNames {
val NE = encode("!=")
val OR = encode("|")
val PLUS = ADD // technically redundant, but ADD looks funny with MINUS
+ val PLUSPLUS = encode("++")
val SUB = MINUS // ... as does SUB with PLUS
val XOR = encode("^")
val ZAND = encode("&&")
diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala
index b75fd72526..1af8c225f5 100644
--- a/src/reflect/scala/reflect/internal/TreeGen.scala
+++ b/src/reflect/scala/reflect/internal/TreeGen.scala
@@ -2,6 +2,8 @@ package scala
package reflect
package internal
+import Flags._
+
abstract class TreeGen extends macros.TreeBuilder {
val global: SymbolTable
@@ -302,4 +304,78 @@ abstract class TreeGen extends macros.TreeBuilder {
val factory = Select(gen.mkAttributedRef(SeqModule), nme.apply)
Apply(factory, List(arg))
}
+
+ def mkSuperInitCall: Select = Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR)
+
+ /** Generates a template with constructor corresponding to
+ *
+ * constrmods (vparams1_) ... (vparams_n) preSuper { presupers }
+ * extends superclass(args_1) ... (args_n) with mixins { self => body }
+ *
+ * This gets translated to
+ *
+ * extends superclass with mixins { self =>
+ * presupers' // presupers without rhs
+ * vparamss // abstract fields corresponding to value parameters
+ * def <init>(vparamss) {
+ * presupers
+ * super.<init>(args)
+ * }
+ * body
+ * }
+ */
+ def mkTemplate(parents: List[Tree], self: ValDef, constrMods: Modifiers, vparamss: List[List[ValDef]], body: List[Tree], superPos: Position): Template = {
+ /* Add constructor to template */
+
+ // create parameters for <init> as synthetic trees.
+ var vparamss1 = mmap(vparamss) { vd =>
+ atPos(vd.pos.focus) {
+ val mods = Modifiers(vd.mods.flags & (IMPLICIT | DEFAULTPARAM | BYNAMEPARAM) | PARAM | PARAMACCESSOR)
+ ValDef(mods withAnnotations vd.mods.annotations, vd.name, vd.tpt.duplicate, vd.rhs.duplicate)
+ }
+ }
+ val (edefs, rest) = body span treeInfo.isEarlyDef
+ val (evdefs, etdefs) = edefs partition treeInfo.isEarlyValDef
+ val gvdefs = evdefs map {
+ case vdef @ ValDef(_, _, tpt, _) =>
+ copyValDef(vdef)(
+ // atPos for the new tpt is necessary, since the original tpt might have no position
+ // (when missing type annotation for ValDef for example), so even though setOriginal modifies the
+ // position of TypeTree, it would still be NoPosition. That's what the author meant.
+ tpt = atPos(vdef.pos.focus)(TypeTree() setOriginal tpt setPos tpt.pos.focus),
+ rhs = EmptyTree
+ )
+ }
+ val lvdefs = evdefs collect { case vdef: ValDef => copyValDef(vdef)(mods = vdef.mods | PRESUPER) }
+
+ val constrs = {
+ if (constrMods hasFlag TRAIT) {
+ if (body forall treeInfo.isInterfaceMember) List()
+ else List(
+ atPos(wrappingPos(superPos, lvdefs)) (
+ DefDef(NoMods, nme.MIXIN_CONSTRUCTOR, List(), List(Nil), TypeTree(), Block(lvdefs, Literal(Constant())))))
+ } else {
+ // convert (implicit ... ) to ()(implicit ... ) if its the only parameter section
+ if (vparamss1.isEmpty || !vparamss1.head.isEmpty && vparamss1.head.head.mods.isImplicit)
+ vparamss1 = List() :: vparamss1
+ val superRef: Tree = atPos(superPos)(mkSuperInitCall)
+ val superCall = pendingSuperCall // we can't know in advance which of the parents will end up as a superclass
+ // this requires knowing which of the parents is a type macro and which is not
+ // and that's something that cannot be found out before typer
+ // (the type macros aren't in the trunk yet, but there is a plan for them to land there soon)
+ // this means that we don't know what will be the arguments of the super call
+ // therefore here we emit a dummy which gets populated when the template is named and typechecked
+ List(
+ // TODO: previously this was `wrappingPos(superPos, lvdefs ::: argss.flatten)`
+ // is it going to be a problem that we can no longer include the `argss`?
+ atPos(wrappingPos(superPos, lvdefs)) (
+ DefDef(constrMods, nme.CONSTRUCTOR, List(), vparamss1, TypeTree(), Block(lvdefs ::: List(superCall), Literal(Constant())))))
+ }
+ }
+ constrs foreach (ensureNonOverlapping(_, parents ::: gvdefs, focus=false))
+ // Field definitions for the class - remove defaults.
+ val fieldDefs = vparamss.flatten map (vd => copyValDef(vd)(mods = vd.mods &~ DEFAULTPARAM, rhs = EmptyTree))
+
+ global.Template(parents, self, gvdefs ::: fieldDefs ::: constrs ::: etdefs ::: rest)
+ }
}
diff --git a/test/files/neg/macro-quasiquotes.check b/test/files/neg/macro-quasiquotes.check
new file mode 100644
index 0000000000..a2d48723b5
--- /dev/null
+++ b/test/files/neg/macro-quasiquotes.check
@@ -0,0 +1,7 @@
+Macros_1.scala:14: error: macro implementation has wrong shape:
+ required: (x: Impls.this.c.Expr[Int]): Impls.this.c.Expr[Any]
+ found : (x: Impls.this.c.universe.Block): Impls.this.c.universe.Apply
+type mismatch for parameter x: Impls.this.c.Expr[Int] does not conform to Impls.this.c.universe.Block
+ def m3(x: Int) = macro Impls.impl3
+ ^
+one error found
diff --git a/test/files/neg/macro-quasiquotes/Macros_1.scala b/test/files/neg/macro-quasiquotes/Macros_1.scala
new file mode 100644
index 0000000000..17c1034720
--- /dev/null
+++ b/test/files/neg/macro-quasiquotes/Macros_1.scala
@@ -0,0 +1,15 @@
+import language.experimental.macros
+import scala.reflect.macros.Macro
+
+trait Impls extends Macro {
+ import c.universe._
+ def impl1(x: Expr[Int]) = q"println(x)"
+ def impl2(x: Tree) = q"println(x)"
+ def impl3(x: Block) = q"println(x)"
+}
+
+object Macros {
+ def m1(x: Int) = macro Impls.impl1
+ def m2(x: Int) = macro Impls.impl2
+ def m3(x: Int) = macro Impls.impl3
+} \ No newline at end of file
diff --git a/test/files/neg/macro-quasiquotes/Test_2.scala b/test/files/neg/macro-quasiquotes/Test_2.scala
new file mode 100644
index 0000000000..c7b8948d79
--- /dev/null
+++ b/test/files/neg/macro-quasiquotes/Test_2.scala
@@ -0,0 +1,5 @@
+object Test extends App {
+ Macros.m1
+ Macros.m2
+ Macros.m3
+}
diff --git a/test/files/run/macro-quasiquotes.check b/test/files/run/macro-quasiquotes.check
new file mode 100644
index 0000000000..94ebaf9001
--- /dev/null
+++ b/test/files/run/macro-quasiquotes.check
@@ -0,0 +1,4 @@
+1
+2
+3
+4
diff --git a/test/files/run/macro-quasiquotes/Macros_1.scala b/test/files/run/macro-quasiquotes/Macros_1.scala
new file mode 100644
index 0000000000..b64eec8743
--- /dev/null
+++ b/test/files/run/macro-quasiquotes/Macros_1.scala
@@ -0,0 +1,15 @@
+import language.experimental.macros
+import scala.reflect.macros.Macro
+
+trait Impls extends Macro {
+ import c.universe._
+ def impl1 = q"println(1)"
+ def impl2 = q"{ println(2); println(3) }"
+ def impl3 = q"4"
+}
+
+object Macros {
+ def m1 = macro Impls.impl1
+ def m2 = macro Impls.impl2
+ def m3 = macro Impls.impl3
+} \ No newline at end of file
diff --git a/test/files/run/macro-quasiquotes/Test_2.scala b/test/files/run/macro-quasiquotes/Test_2.scala
new file mode 100644
index 0000000000..4be193938f
--- /dev/null
+++ b/test/files/run/macro-quasiquotes/Test_2.scala
@@ -0,0 +1,5 @@
+object Test extends App {
+ Macros.m1
+ Macros.m2
+ println(Macros.m3)
+}
diff --git a/test/files/scalacheck/quasiquotes/ArbitraryTreesAndNames.scala b/test/files/scalacheck/quasiquotes/ArbitraryTreesAndNames.scala
new file mode 100644
index 0000000000..03f8aa58d3
--- /dev/null
+++ b/test/files/scalacheck/quasiquotes/ArbitraryTreesAndNames.scala
@@ -0,0 +1,307 @@
+import org.scalacheck._
+import Prop._
+import Gen._
+import Arbitrary._
+
+import scala.reflect.api._
+import scala.reflect.runtime.universe._
+import scala.reflect.runtime.universe.Flag._
+
+trait ArbitraryTreesAndNames {
+ def smallList[T](size: Int, g: Gen[T]) = {
+ val n: Int = choose(0, size / 2 + 1).sample match {
+ case Some(i) => i
+ case None => 0
+ }
+ containerOfN[List, T](n, g)
+ }
+
+ def shortIdent(len: Int) =
+ for(name <- identifier)
+ yield if(name.length <= len) name
+ else name.substring(0, len - 1)
+
+ def genTermName = for(name <- shortIdent(8)) yield TermName(name)
+ def genTypeName = for(name <- shortIdent(8)) yield TypeName(name)
+ def genName = oneOf(genTermName, genTypeName)
+
+ def genFlagSet = oneOf(
+ TRAIT, INTERFACE, MUTABLE, MACRO,
+ DEFERRED, ABSTRACT, FINAL, SEALED,
+ IMPLICIT, LAZY, OVERRIDE, PRIVATE,
+ PROTECTED, LOCAL, CASE, ABSOVERRIDE,
+ BYNAMEPARAM, PARAM, COVARIANT, CONTRAVARIANT,
+ DEFAULTPARAM, PRESUPER, DEFAULTINIT
+ )
+
+ def genModifiers = for(flagset <- genFlagSet) yield Modifiers(flagset)
+
+ def genConstant =
+ for(value <- oneOf(arbitrary[Byte], arbitrary[Short], arbitrary[Char],
+ arbitrary[Int], arbitrary[Long], arbitrary[Float],
+ arbitrary[Double], arbitrary[Boolean], arbitrary[String]))
+ yield Constant(value)
+
+ def genAnnotated(size: Int, argGen: Int => Gen[Tree]) =
+ for(annot <- genTree(size - 1); arg <- argGen(size - 1))
+ yield Annotated(annot, arg)
+
+ def genAlternative(size: Int): Gen[Alternative] =
+ for(trees <- smallList(size, genTree(size - 1)))
+ yield Alternative(trees)
+
+ def genAppliedTypeTree(size: Int) =
+ for(tpt <- genTree(size - 1) if tpt.isType;
+ args <- smallList(size, genTree(size - 1)))
+ yield AppliedTypeTree(tpt, args)
+
+ def genApply(size: Int) =
+ for(fun <- genTree(size - 1);
+ args <- smallList(size, genTree(size - 1)))
+ yield Apply(fun, args)
+
+ def genAssign(size: Int) =
+ for(lhs <- genTree(size - 1); rhs <- genTree(size - 1))
+ yield Assign(lhs, rhs)
+
+ def genAssignOrNamedArg(size: Int) =
+ for(lhs <- genTree(size - 1); rhs <- genTree(size - 1))
+ yield AssignOrNamedArg(lhs, rhs)
+
+ def genBind(size: Int, nameGen: Gen[Name]) =
+ for(name <- nameGen; body <- genTree(size - 1))
+ yield Bind(name, body)
+
+ def genBlock(size: Int) =
+ for(stats <- smallList(size, genTree(size - 1)); expr <- genTree(size - 1))
+ yield Block(stats, expr)
+
+ def genCaseDef(size: Int) =
+ for(pat <- genTree(size - 1); guard <- genTree(size - 1); body <- genTree(size - 1))
+ yield CaseDef(pat, guard, body)
+
+ def genClassDef(size: Int) =
+ for(mods <- genModifiers; name <- genTypeName;
+ tparams <- smallList(size, genTypeDef(size - 1));
+ impl <- genTemplate(size - 1))
+ yield ClassDef(mods, name, tparams, impl)
+
+ def genCompoundTypeTree(size: Int) =
+ for(templ <- genTemplate(size - 1))
+ yield CompoundTypeTree(templ)
+
+ def genDefDef(size: Int) =
+ for(mods <- genModifiers; name <- genName;
+ tpt <- genTree(size -1); rhs <- genTree(size - 1);
+ tparams <- smallList(size, genTypeDef(size - 1));
+ vparamss <- smallList(size, smallList(size, genValDef(size - 1))))
+ yield DefDef(mods, name, tparams, vparamss, tpt, rhs)
+
+ def genExistentialTypeTree(size: Int) =
+ for(tpt <- genTree(size - 1); where <- smallList(size, genTree(size - 1)))
+ yield ExistentialTypeTree(tpt, where)
+
+ def genFunction(size: Int) =
+ for(vparams <- smallList(size, genValDef(size - 1)); body <- genTree(size - 1))
+ yield Function(vparams, body)
+
+ def genIdent(nameGen: Gen[Name] = genName) =
+ for(name <- nameGen) yield Ident(name)
+
+ def genIf(size: Int) =
+ for(cond <- genTree(size - 1); thenp <- genTree(size - 1); elsep <- genTree(size - 1))
+ yield If(cond, thenp, elsep)
+
+ def genImport(size: Int) =
+ for(expr <- genTree(size - 1); selectors <- smallList(size, genImportSelector(size - 1)))
+ yield Import(expr, selectors)
+
+ def genImportSelector(size: Int) =
+ for(name <- genName; namePos <- arbitrary[Int]; rename <- genName; renamePos <- arbitrary[Int])
+ yield ImportSelector(name, namePos, rename, renamePos)
+
+ def genTemplate(size: Int) =
+ for(parents <- smallList(size, genTree(size - 1));
+ self <- genValDef(size - 1);
+ body <- smallList(size, genTree(size - 1)))
+ yield Template(parents, self, body)
+
+ def genLabelDef(size: Int) =
+ for(name <- genTermName; params <- smallList(size, genIdent()); rhs <- genTree(size - 1))
+ yield LabelDef(name, params, rhs)
+
+ def genLiteral =
+ for(const <- genConstant) yield Literal(const)
+
+ def genMatch(size: Int) =
+ for(selector <- genTree(size - 1); cases <- smallList(size, genCaseDef(size - 1)))
+ yield Match(selector, cases)
+
+ def genModuleDef(size: Int) =
+ for(mods <- genModifiers; name <- genTermName; impl <- genTemplate(size - 1))
+ yield ModuleDef(mods, name, impl)
+
+ def genNew(size: Int) =
+ for(tpt <- genTree(size - 1))
+ yield New(tpt)
+
+ def genRefTree(size: Int) =
+ oneOf(genSelect(size), genIdent(), genSelectFromTypeTree(size))
+
+ def genPackageDef(size: Int) =
+ for(reftree <- genRefTree(size - 1); stats <- smallList(size, genTree(size - 1)))
+ yield PackageDef(reftree, stats)
+
+ def genTypeSelect(size: Int) =
+ for(qual <- genTree(size - 1); name <- genTypeName)
+ yield Select(qual, name)
+
+ def genSelect(size: Int, nameGen: Gen[Name] = genName) =
+ for(qual <- genTree(size - 1); name <- nameGen)
+ yield Select(qual, name)
+
+ def genSelectFromTypeTree(size: Int) =
+ for(qual <- genTreeIsType(size - 1); name <- genTypeName)
+ yield SelectFromTypeTree(qual, name)
+
+ def genReferenceToBoxed(size: Int) =
+ for(ident <- genIdent())
+ yield ReferenceToBoxed(ident)
+
+ def genReturn(size: Int) =
+ for(expr <- genTree(size - 1))
+ yield Return(expr)
+
+ def genSingletonTypeTree(size: Int) =
+ for(expr <- genTree(size - 1))
+ yield SingletonTypeTree(expr)
+
+ def genStar(size: Int) =
+ for(expr <- genTree(size - 1))
+ yield Star(expr)
+
+ def genSuper(size: Int) =
+ for(qual <- genTree(size - 1); mix <- genTypeName)
+ yield Super(qual, mix)
+
+ def genThis(size: Int) =
+ for(qual <- genTypeName)
+ yield This(qual)
+
+ def genThrow(size: Int) =
+ for(expr <- genTree(size - 1))
+ yield Throw(expr)
+
+ def genTry(size: Int) =
+ for(block <- genTree(size - 1);
+ catches <- smallList(size, genCaseDef(size - 1));
+ finalizer <- genTree(size - 1))
+ yield Try(block, catches, finalizer)
+
+ def genTypeApply(size: Int) =
+ for(fun <- genTreeIsTerm(size - 1); args <- smallList(size, genTree(size - 1)))
+ yield TypeApply(fun, args)
+
+ def genTypeBoundsTree(size: Int) =
+ for(lo <- genTree(size - 1); hi <- genTree(size - 1))
+ yield TypeBoundsTree(lo, hi)
+
+ def genTypeDef(size: Int): Gen[TypeDef] =
+ for(mods <- genModifiers; name <- genTypeName;
+ tparams <- smallList(size, genTypeDef(size - 1)); rhs <- genTree(size - 1))
+ yield TypeDef(mods, name, tparams, rhs)
+
+ def genTypeTree: Gen[TypeTree] = TypeTree()
+
+ def genTyped(size: Int) =
+ for(expr <- genTree(size - 1); tpt <- genTree(size - 1))
+ yield Typed(expr, tpt)
+
+ def genUnApply(size: Int) =
+ for(fun <- genTree(size - 1); args <- smallList(size, genTree(size - 1)))
+ yield UnApply(fun, args)
+
+ def genValDef(size: Int) =
+ for(mods <- genModifiers; name <- genTermName;
+ tpt <- genTree(size - 1); rhs <- genTree(size - 1))
+ yield ValDef(mods, name, tpt, rhs)
+
+ def genTree(size: Int): Gen[Tree] =
+ if (size <= 1) oneOf(EmptyTree, genTreeIsTerm(size), genTreeIsType(size))
+ else oneOf(genTree(1),
+ // these trees are neither terms nor types
+ genPackageDef(size - 1), genModuleDef(size - 1),
+ genCaseDef(size - 1), genDefDef(size - 1),
+ genTypeDef(size - 1), genTemplate(size - 1),
+ genClassDef(size - 1), genValDef(size - 1),
+ genImport(size - 1))
+
+ def genTreeIsTerm(size: Int): Gen[Tree] =
+ if (size <= 1) oneOf(genLiteral, genIdent(genTermName))
+ else oneOf(genTreeIsTerm(1), genBind(size - 1, genTermName),
+ genAnnotated(size - 1, genTreeIsTerm), genSelect(size - 1, genTermName),
+ genAlternative(size - 1), genApply(size - 1), genAssign(size - 1),
+ genAssignOrNamedArg(size - 1), genBlock(size - 1), genFunction(size - 1),
+ genIf(size - 1), genLabelDef(size - 1), genMatch(size - 1), genNew(size - 1),
+ genReturn(size - 1), genStar(size - 1), genSuper(size - 1), genThis(size - 1),
+ genThrow(size - 1), genTry(size - 1), genTypeApply(size - 1),
+ genTyped(size - 1), genUnApply(size - 1))
+
+ def genTreeIsType(size: Int): Gen[Tree] =
+ if (size <= 1) genIdent(genTypeName)
+ else oneOf(genTreeIsType(1), genAnnotated(size - 1, genTreeIsType),
+ genBind(size - 1, genTypeName), genSelect(size - 1, genTypeName),
+ genSingletonTypeTree(size - 1), genSelectFromTypeTree(size - 1),
+ genExistentialTypeTree(size - 1), genCompoundTypeTree(size - 1),
+ genAppliedTypeTree(size - 1), genTypeBoundsTree(size - 1))
+
+ /* These are marker types that allow to write tests that
+ * depend specificly on Trees that are terms or types.
+ * They are transperantly tranformed to trees through
+ * implicit conversions and liftables for quasiquotes.
+ */
+
+ case class TreeIsTerm(tree: Tree) { require(tree.isTerm, showRaw(tree)) }
+ case class TreeIsType(tree: Tree) { require(tree.isType, showRaw(tree)) }
+
+ def genTreeIsTermWrapped(size: Int) =
+ for(tit <- genTreeIsTerm(size)) yield TreeIsTerm(tit)
+
+ def genTreeIsTypeWrapped(size: Int) =
+ for(tit <- genTreeIsType(size)) yield TreeIsType(tit)
+
+ implicit object liftTreeIsTerm extends Liftable[TreeIsTerm] {
+ def apply(universe: Universe, value: TreeIsTerm): universe.Tree =
+ value.tree.asInstanceOf[universe.Tree]
+ }
+ implicit object liftTreeIsType extends Liftable[TreeIsType] {
+ def apply(universe: Universe, value: TreeIsType): universe.Tree =
+ value.tree.asInstanceOf[universe.Tree]
+ }
+ implicit def treeIsTerm2tree(tit: TreeIsTerm) = tit.tree
+ implicit def treeIsType2tree(tit: TreeIsType) = tit.tree
+
+ implicit val arbConstant: Arbitrary[Constant] = Arbitrary(genConstant)
+ implicit val arbModifiers: Arbitrary[Modifiers] = Arbitrary(genModifiers)
+ implicit val arbTermName: Arbitrary[TermName] = Arbitrary(genTermName)
+ implicit val arbTypeName: Arbitrary[TypeName] = Arbitrary(genTypeName)
+ implicit val arbName: Arbitrary[Name] = Arbitrary(genName)
+
+ // Trees generators are bound by this size to make
+ // generation times shorter and less memory hungry.
+ // TODO: is there any better solution?
+ val maxTreeSize = 5
+
+ def arbitrarySized[T](gen: Int => Gen[T]) =
+ Arbitrary(sized(s => gen(s.min(maxTreeSize))))
+
+ implicit val arbLiteral: Arbitrary[Literal] = Arbitrary(genLiteral)
+ implicit val arbIdent: Arbitrary[Ident] = Arbitrary(genIdent())
+ implicit val arbValDef: Arbitrary[ValDef] = arbitrarySized(genValDef)
+ implicit val arbDefDef: Arbitrary[DefDef] = arbitrarySized(genDefDef)
+ implicit val arbTypeDef: Arbitrary[TypeDef] = arbitrarySized(genTypeDef)
+ implicit val arbBind: Arbitrary[Bind] = arbitrarySized(genBind(_, genName))
+ implicit val arbTree: Arbitrary[Tree] = arbitrarySized(genTree)
+ implicit val arbTreeIsTerm: Arbitrary[TreeIsTerm] = arbitrarySized(genTreeIsTermWrapped)
+ implicit val arbTreeIsType: Arbitrary[TreeIsType] = arbitrarySized(genTreeIsTypeWrapped)
+} \ No newline at end of file
diff --git a/test/files/scalacheck/quasiquotes/ErrorProps.scala b/test/files/scalacheck/quasiquotes/ErrorProps.scala
new file mode 100644
index 0000000000..044a332a04
--- /dev/null
+++ b/test/files/scalacheck/quasiquotes/ErrorProps.scala
@@ -0,0 +1,199 @@
+import org.scalacheck._
+import Prop._
+import Gen._
+import Arbitrary._
+
+import scala.reflect.runtime.universe._
+import Flag._
+
+object ErrorProps extends QuasiquoteProperties("errors") {
+ property("can't extract two .. cardinalities in a row") = fails(
+ "Can't extract with .. here",
+ """
+ val xs = List(q"x1", q"x2")
+ val q"f(..$xs1, ..$xs2)" = xs
+ """)
+
+ property("can't splice with given cardinality") = fails(
+ "Can't splice List[reflect.runtime.universe.Ident], consider using ..",
+ """
+ val xs = List(q"x", q"x")
+ q"$xs"
+ """)
+
+ property("splice typename into typedef with default bounds") = fails(
+ "reflect.runtime.universe.Name expected but reflect.runtime.universe.TypeDef found",
+ """
+ val T1 = TypeName("T1")
+ val T2 = q"type T"
+ val t = EmptyTree
+ q"type $T1[$T2 >: _root_.scala.Any <: _root_.scala.Nothing] = $t" ≈
+ TypeDef(Modifiers(), T1, List(T2), t)
+ """)
+
+ property("can't splice annotations with ... cardinality") = fails(
+ "Can't splice with ... here",
+ """
+ val annots = List(List(q"Foo"))
+ q"@...$annots def foo"
+ """)
+
+ property("@..$first @$rest def foo") = fails(
+ "Can't extract with .. here",
+ """
+ val a = annot("a")
+ val b = annot("b")
+ val c = annot("c")
+ val q"@..$first @$rest def foo" = q"@$a @$b @$c def foo"
+ """)
+
+ property("only literal string arguments") = fails(
+ "Quasiquotes can only be used with literal strings",
+ """
+ val s: String = "foo"
+ StringContext(s).q()
+ """)
+
+ property("don't know how to splice inside of strings") = fails(
+ "Don't know how to splice here",
+ """
+ val x: Tree = EmptyTree
+ StringContext("\"", "\"").q(x)
+ """)
+
+ property("expected different cardinality") = fails(
+ "Can't splice List[reflect.runtime.universe.Tree] with ..., consider using ..",
+ """
+ val args: List[Tree] = Nil
+ q"f(...$args)"
+ """)
+
+ property("non-liftable type ..") = fails(
+ "Can't splice List[StringBuilder] with .., consider omitting the dots or providing an implicit instance of Liftable[StringBuilder]",
+ """
+ import java.lang.StringBuilder
+ val bazs = List(new StringBuilder)
+ q"f(..$bazs)"
+ """)
+
+ property("non-liftable type ...") = fails(
+ "Can't splice List[List[StringBuilder]] with .., consider using ... or providing an implicit instance of Liftable[StringBuilder]",
+ """
+ import java.lang.StringBuilder
+ val bazs = List(List(new StringBuilder))
+ q"f(..$bazs)"
+ """)
+
+ property("use .. card or provide liftable") = fails(
+ "Can't splice List[StringBuilder], consider using .. or providing an implicit instance of Liftable[List[StringBuilder]]",
+ """
+ import java.lang.StringBuilder
+ val lst: List[StringBuilder] = Nil
+ q"f($lst)"
+ """)
+
+ property("use ... card or provide liftable") = fails(
+ "Can't splice List[List[reflect.runtime.universe.Ident]], consider using ...",
+ """
+ val xs = List(List(q"x", q"x"))
+ q"$xs"
+ """)
+
+ property("use zero card") = fails(
+ "Can't splice reflect.runtime.universe.Tree with .., consider omitting the dots",
+ """
+ val t = EmptyTree
+ q"f(..$t)"
+ """)
+
+ property("not liftable or natively supported") = fails(
+ "Can't splice StringBuilder, consider providing an implicit instance of Liftable[StringBuilder]",
+ """
+ import java.lang.StringBuilder
+ val sb = new StringBuilder
+ q"f($sb)"
+ """)
+
+ property("casedef expected") = fails(
+ "reflect.runtime.universe.CaseDef expected but reflect.runtime.universe.Tree found",
+ """
+ val t = EmptyTree
+ q"_ { case $t }"
+ """)
+
+ property("can't splice with ... card here") = fails(
+ "Can't splice with ... here",
+ """
+ val lst: List[List[Tree]] = Nil; val t = EmptyTree
+ q"f(...$lst, $t)"
+ """)
+
+ property("name expected") = fails(
+ "reflect.runtime.universe.Name expected but reflect.runtime.universe.Tree found",
+ """
+ val t = EmptyTree
+ q"class $t"
+ """)
+
+ property("flags or mods expected") = fails(
+ "reflect.runtime.universe.FlagSet or reflect.runtime.universe.Modifiers expected but reflect.runtime.universe.Tree found",
+ """
+ val t = EmptyTree
+ q"$t def foo"
+ """)
+
+ property("cant splice flags together with mods") = fails(
+ "Can't splice flags together with modifiers, consider merging flags into modifiers",
+ """
+ val f = Flag.IMPLICIT; val m = NoMods
+ q"$f $m def foo"
+ """)
+
+ property("can't splice mods with annots") = fails(
+ "Can't splice modifiers together with annotations, consider merging annotations into modifiers",
+ """
+ val m = NoMods
+ q"@annot $m def foo"
+ """)
+
+ property("can't splice modifiers with inline flags") = fails(
+ "Can't splice modifiers together with flags, consider merging flags into modifiers",
+ """
+ val m = NoMods
+ q"$m implicit def foo"
+ """)
+
+ property("can't splice multiple mods") = fails(
+ "Can't splice multiple modifiers, consider merging them into a single modifiers instance",
+ """
+ val m1 = NoMods; val m2 = NoMods
+ q"$m1 $m2 def foo"
+ """)
+
+ property("can't extract with .. card here") = fails(
+ "Can't extract with .. here",
+ """
+ val q"f(..$xs, $y)" = EmptyTree
+ """)
+
+ property("can't extract mods with annots") = fails(
+ "Can't extract modifiers together with annotations, consider extracting just modifiers",
+ """
+ val q"@$annot $mods def foo" = EmptyTree
+ """)
+
+ property("can't extract multiple mods") = fails(
+ "Can't extract multiple modifiers together, consider extracting a single modifiers instance",
+ """
+ val q"$m1 $m2 def foo" = EmptyTree
+ """)
+
+ property("can't parse more than one casedef") = fails(
+ "Can't parse more than one casedef, consider generating a match tree instead",
+ """
+ cq"1 => 2 case 3 => 5"
+ """)
+
+ // // Make sure a nice error is reported in this case
+ // { import Flag._; val mods = NoMods; q"lazy $mods val x: Int" }
+} \ No newline at end of file
diff --git a/test/files/scalacheck/quasiquotes/LiftableProps.scala b/test/files/scalacheck/quasiquotes/LiftableProps.scala
new file mode 100644
index 0000000000..510ab99068
--- /dev/null
+++ b/test/files/scalacheck/quasiquotes/LiftableProps.scala
@@ -0,0 +1,84 @@
+import org.scalacheck._
+import Prop._
+import Gen._
+import Arbitrary._
+
+import scala.reflect.runtime.universe._
+import Flag._
+
+object LiftableProps extends QuasiquoteProperties("liftable") {
+ property("splice byte") = test {
+ val c: Byte = 0
+ assert(q"$c" ≈ Literal(Constant(c)))
+ }
+
+ property("splice short") = test {
+ val c: Short = 0
+ assert(q"$c" ≈ Literal(Constant(c)))
+ }
+
+ property("splice char") = test {
+ val c: Char = 'c'
+ assert(q"$c" ≈ Literal(Constant(c)))
+ }
+
+ property("splice int") = test {
+ val c: Int = 0
+ assert(q"$c" ≈ Literal(Constant(c)))
+ }
+
+ property("splice long") = test {
+ val c: Long = 0
+ assert(q"$c" ≈ Literal(Constant(c)))
+ }
+
+ property("splice float") = test {
+ val c: Float = 0.0f
+ assert(q"$c" ≈ Literal(Constant(c)))
+ }
+
+ property("splice double") = test {
+ val c: Double = 0.0
+ assert(q"$c" ≈ Literal(Constant(c)))
+ }
+
+ property("splice boolean") = test {
+ val c: Boolean = false
+ assert(q"$c" ≈ Literal(Constant(c)))
+ }
+
+ property("splice string") = test {
+ val c: String = "s"
+ assert(q"$c" ≈ Literal(Constant(c)))
+ }
+
+ property("splice unit") = test {
+ val c: Unit = ()
+ assert(q"$c" ≈ Literal(Constant(c)))
+ }
+
+ property("lift symbol") = test {
+ val s = rootMirror.staticClass("scala.Int")
+ assert(q"$s" ≈ Ident(s))
+ }
+
+ property("lift type") = test {
+ val tpe = rootMirror.staticClass("scala.Int").toType
+ assert(q"$tpe" ≈ TypeTree(tpe))
+ }
+
+ property("lift type tag") = test {
+ val tag = TypeTag.Int
+ assert(q"$tag" ≈ TypeTree(tag.tpe))
+ }
+
+ property("lift weak type tag") = test {
+ val tag = WeakTypeTag.Int
+ assert(q"$tag" ≈ TypeTree(tag.tpe))
+ }
+
+ property("lift constant") = test {
+ val const = Constant(0)
+ assert(q"$const" ≈ q"0")
+ }
+} \ No newline at end of file
diff --git a/test/files/scalacheck/quasiquotes/PatternConstructionProps.scala b/test/files/scalacheck/quasiquotes/PatternConstructionProps.scala
new file mode 100644
index 0000000000..aee50c9c5f
--- /dev/null
+++ b/test/files/scalacheck/quasiquotes/PatternConstructionProps.scala
@@ -0,0 +1,37 @@
+import org.scalacheck._
+import Prop._
+import Gen._
+import Arbitrary._
+
+import scala.reflect.runtime.universe._
+import Flag._
+
+object PatternConstructionProps extends QuasiquoteProperties("pattern construction") {
+ property("splice bind") = forAll { (bind: Bind) =>
+ pq"$bind" ≈ bind
+ }
+
+ property("splice name into bind") = forAll { (name: TermName) =>
+ pq"$name" ≈ Bind(name, Ident(nme.WILDCARD))
+ }
+
+ property("splice name and tree into bind") = forAll { (name: TermName, tree: Tree) =>
+ pq"$name @ $tree" ≈ Bind(name, tree)
+ }
+
+ property("splice type name into typed") = forAll { (name: TypeName) =>
+ pq"_ : $name" ≈ Typed(Ident(nme.WILDCARD), Ident(name))
+ }
+
+ property("splice tree into typed") = forAll { (typ: Tree) =>
+ pq"_ : $typ" ≈ Typed(Ident(nme.WILDCARD), typ)
+ }
+
+ property("splice into apply") = forAll { (pat: Tree, subpat: Tree) =>
+ pq"$pat($subpat)" ≈ Apply(pat, List(subpat))
+ }
+
+ property("splice into casedef") = forAll { (pat: Tree, cond: Tree, body: Tree) =>
+ cq"$pat if $cond => $body" ≈ CaseDef(pat, cond, Block(List(), body))
+ }
+} \ No newline at end of file
diff --git a/test/files/scalacheck/quasiquotes/PatternDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/PatternDeconstructionProps.scala
new file mode 100644
index 0000000000..f73fd29b22
--- /dev/null
+++ b/test/files/scalacheck/quasiquotes/PatternDeconstructionProps.scala
@@ -0,0 +1,35 @@
+import org.scalacheck._
+import Prop._
+import Gen._
+import Arbitrary._
+
+import scala.reflect.runtime.universe._
+import Flag._
+import definitions._
+
+object PatternDeconstructionProps extends QuasiquoteProperties("pattern deconstruction") {
+ property("extract bind") = forAll { (bind: Bind) =>
+ val pq"$bind0" = pq"$bind"
+ bind0 ≈ bind
+ }
+
+ property("extract bind and subpattern") = forAll { (name: TermName, subp: Tree) =>
+ val pq"$name0 @ $subp0" = pq"$name @ $subp"
+ name0 ≈ name && subp0 ≈ subp
+ }
+
+ property("extract typed") = forAll { (typ: Tree) =>
+ val pq"_ : $typ0" = pq"_ : $typ"
+ typ0 ≈ typ
+ }
+
+ property("extract apply") = forAll { (pat: Tree, subpat: Tree) =>
+ val pq"$pat0($subpat0)" = pq"$pat($subpat)"
+ pat0 ≈ pat && subpat0 ≈ subpat
+ }
+
+ property("extract casedef") = forAll { (pat: Tree, cond: Tree, body: Tree) =>
+ val cq"$pat0 if $cond0 => $body0" = cq"$pat if $cond => $body"
+ pat0 ≈ pat && cond0 ≈ cond && body0 ≈ body
+ }
+} \ No newline at end of file
diff --git a/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala b/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala
new file mode 100644
index 0000000000..5e87aa57cc
--- /dev/null
+++ b/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala
@@ -0,0 +1,89 @@
+import scala.reflect.runtime.universe._
+import scala.tools.reflect.ToolBox
+import scala.tools.reflect.ToolBoxError
+import scala.reflect.macros.TypecheckException
+
+import org.scalacheck._
+import Prop._
+import Gen._
+import Arbitrary._
+
+class QuasiquoteProperties(name: String) extends Properties(name) with ArbitraryTreesAndNames with Helpers
+
+trait Helpers {
+ /** Runs a code block and returns proof confirmation
+ * if no exception has been thrown while executing code
+ * block. This is useful for simple one-off tests.
+ */
+ def test[T](block: => T)=
+ Prop { (params) =>
+ block
+ Result(Prop.Proof)
+ }
+
+ implicit class TestSimilarTree(tree1: Tree) {
+ def ≈(tree2: Tree) = tree1.equalsStructure(tree2)
+ }
+
+ implicit class TestSimilarListTree(lst: List[Tree]) {
+ def ≈(other: List[Tree]) = (lst.length == other.length) && lst.zip(other).forall { case (t1, t2) => t1 ≈ t2 }
+ }
+
+ implicit class TestSimilarListListTree(lst: List[List[Tree]]) {
+ def ≈(other: List[List[Tree]]) = (lst.length == other.length) && lst.zip(other).forall { case (l1, l2) => l1 ≈ l2 }
+ }
+
+ implicit class TestSimilarName(name: Name) {
+ def ≈(other: Name) = name == other
+ }
+
+ implicit class TestSimilarMods(mods: Modifiers) {
+ def ≈(other: Modifiers) = (mods.flags == other.flags) && (mods.privateWithin ≈ other.privateWithin) && (mods.annotations ≈ other.annotations)
+ }
+
+ def assertThrows[T <: AnyRef](f: => Any)(implicit manifest: Manifest[T]): Unit = {
+ val clazz = manifest.erasure.asInstanceOf[Class[T]]
+ val thrown =
+ try {
+ f
+ false
+ } catch {
+ case u: Throwable =>
+ if (!clazz.isAssignableFrom(u.getClass))
+ assert(false, s"wrong exception: $u")
+ true
+ }
+ if(!thrown)
+ assert(false, "exception wasn't thrown")
+ }
+
+ def fails(msg: String, block: String) = {
+ def result(ok: Boolean, description: String = "") = {
+ val status = if (ok) Prop.Proof else Prop.False
+ val labels = if (description != "") Set(description) else Set.empty[String]
+ Prop { new Prop.Result(status, Nil, Set.empty, labels) }
+ }
+ try {
+ val tb = rootMirror.mkToolBox()
+ val tree = tb.parse(s"""
+ object Wrapper extends Helpers {
+ import scala.reflect.runtime.universe._
+ $block
+ }
+ """)
+ tb.compile(tree)
+ result(false, "given code doesn't fail to typecheck")
+ } catch {
+ case ToolBoxError(emsg, _) =>
+ if (!emsg.contains(msg))
+ result(false, s"error message '${emsg}' is not the same as expected '$msg'")
+ else
+ result(true)
+ }
+ }
+
+ def annot(name: String): Tree = annot(TypeName(name), Nil)
+ def annot(name: TypeName): Tree = annot(name, Nil)
+ def annot(name: String, args: List[Tree]): Tree = annot(TypeName(name), args)
+ def annot(name: TypeName, args: List[Tree]): Tree = q"new $name(..$args)"
+} \ No newline at end of file
diff --git a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala
new file mode 100644
index 0000000000..b14945f24b
--- /dev/null
+++ b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala
@@ -0,0 +1,341 @@
+import org.scalacheck._
+import Prop._
+import Gen._
+import Arbitrary._
+
+import scala.reflect.runtime.universe._
+import Flag._
+
+object TermConstructionProps extends QuasiquoteProperties("term construction") {
+ val anyRef = Select(Ident(TermName("scala")), TypeName("AnyRef"))
+ val emtpyConstructor =
+ DefDef(
+ Modifiers(), nme.CONSTRUCTOR, List(),
+ List(List()), TypeTree(), Block(List(Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), List())), Literal(Constant(()))))
+
+ def classWithMethods(name: TypeName, methods: List[DefDef] = Nil) =
+ ClassDef(
+ Modifiers(), name, List(),
+ Template(List(anyRef), emptyValDef, List(emtpyConstructor) ++ methods))
+
+ property("splice single tree return tree itself") = forAll { (t: Tree) =>
+ q"$t" ≈ t
+ }
+
+ property("splice trees into if expression") = forAll { (t1: Tree, t2: Tree, t3: Tree) =>
+ q"if($t1) $t2 else $t3" ≈ If(t1, t2, t3)
+ }
+
+ property("splice term name into val") = forAll { (name: TermName) =>
+ q"val $name = 0" ≈ ValDef(Modifiers(), name, TypeTree(), Literal(Constant(0)))
+ }
+
+ property("splice type name into typedef") = forAll { (name1: TypeName, name2: TypeName) =>
+ q"type $name1 = $name2" ≈ TypeDef(Modifiers(), name1, List(), Ident(name2))
+ }
+
+ property("splice term name into class") = forAll { (name: TypeName) =>
+ q"class $name" ≈ classWithMethods(name)
+ }
+
+ property("splice method into class") = forAll { (name: TypeName, method: DefDef) =>
+ q"class $name { $method }" ≈ classWithMethods(name, List(method))
+ }
+
+ property("splice trees into ascriptiopn") = forAll { (t1: Tree, t2: Tree) =>
+ q"$t1 : $t2" ≈ Typed(t1, t2)
+ }
+
+ property("splice trees into apply") = forAll { (t1: Tree, t2: Tree, t3: Tree) =>
+ q"$t1($t2, $t3)" ≈ Apply(t1, List(t2, t3))
+ }
+
+ property("splice trees with .. cardinality into apply") = forAll { (ts: List[Tree]) =>
+ q"f(..$ts)" ≈ Apply(q"f", ts)
+ }
+
+ property("splice iterable into apply") = forAll { (trees: List[Tree]) =>
+ val itrees: Iterable[Tree] = trees
+ q"f(..$itrees)" ≈ Apply(q"f", trees)
+ }
+
+ property("splice trees with ... cardinality into apply") = forAll { (ts1: List[Tree], ts2: List[Tree]) =>
+ val argss = List(ts1, ts2)
+ q"f(...$argss)" ≈ Apply(Apply(q"f", ts1), ts2)
+ }
+
+ property("splice term name into assign") = forAll { (name: TermName, t: Tree) =>
+ q"$name = $t" ≈ Assign(Ident(name), t)
+ }
+
+ property("splice trees into block") = forAll { (t1: Tree, t2: Tree, t3: Tree) =>
+ q"""{
+ $t1
+ $t2
+ $t3
+ }""" ≈ Block(List(t1, t2), t3)
+ }
+
+ property("splice type name into class parents") = forAll { (name: TypeName, parent: TypeName) =>
+ q"class $name extends $parent" ≈
+ ClassDef(
+ Modifiers(), name, List(),
+ Template(List(Ident(parent)), emptyValDef, List(emtpyConstructor)))
+ }
+
+ property("splice tree into new") = forAll { (tree: Tree) =>
+ q"new $tree" ≈ Apply(Select(New(tree), nme.CONSTRUCTOR), List())
+ }
+
+ property("splice tree into return") = forAll { (tree: Tree) =>
+ q"return $tree" ≈ Return(tree)
+ }
+
+ property("splice a list of arguments") = forAll { (fun: Tree, args: List[Tree]) =>
+ q"$fun(..$args)" ≈ Apply(fun, args)
+ }
+
+ property("splice list and non-list fun arguments") = forAll { (fun: Tree, arg1: Tree, arg2: Tree, args: List[Tree]) =>
+ q"$fun(..$args, $arg1, $arg2)" ≈ Apply(fun, args ++ List(arg1) ++ List(arg2)) &&
+ q"$fun($arg1, ..$args, $arg2)" ≈ Apply(fun, List(arg1) ++ args ++ List(arg2)) &&
+ q"$fun($arg1, $arg2, ..$args)" ≈ Apply(fun, List(arg1) ++ List(arg2) ++ args)
+ }
+
+ property("splice members into class") = forAll { (name: TypeName, defs: List[DefDef], extra: DefDef) =>
+ q"""class $name {
+ ..$defs
+ $extra
+ }""" ≈ classWithMethods(name, defs ++ List(extra))
+ }
+
+ property("splice into new") = forAll { (name: TypeName, body: List[Tree]) =>
+ q"new $name { ..$body }" ≈
+ q"""{
+ final class $$anon extends $name {
+ ..$body
+ }
+ new $$anon
+ }"""
+ }
+
+
+ property("splice tree into singleton type tree") = forAll { (name: TypeName, t: Tree) =>
+ q"type $name = $t.type" ≈ q"type $name = ${SingletonTypeTree(t)}"
+ }
+
+ property("splice type name into this") = forAll { (T: TypeName) =>
+ q"$T.this" ≈ This(T)
+ }
+
+ property("splice tree into throw") = forAll { (t: Tree) =>
+ q"throw $t" ≈ Throw(t)
+ }
+
+ property("splice trees into type apply") = forAll { (fun: TreeIsTerm, types: List[Tree]) =>
+ q"$fun[..$types]" ≈ TypeApply(fun, types)
+ }
+
+ property("splice type names into type bounds") = forAll { (T1: TypeName, T2: TypeName, T3: TypeName) =>
+ q"type $T1 >: $T2 <: $T3" ≈
+ TypeDef(
+ Modifiers(DEFERRED), T1, List(),
+ TypeBoundsTree(Ident(T2), Ident(T3)))
+ }
+
+ property("splice trees names into type bounds") = forAll { (T: TypeName, t1: Tree, t2: Tree) =>
+ q"type $T >: $t1 <: $t2" ≈
+ TypeDef(
+ Modifiers(DEFERRED), T, List(),
+ TypeBoundsTree(t1, t2))
+ }
+
+ property("splice tparams into typedef (1)") = forAll { (T: TypeName, targs: List[TypeDef], t: Tree) =>
+ q"type $T[..$targs] = $t" ≈ TypeDef(Modifiers(), T, targs, t)
+ }
+
+ property("splice tparams into typedef (2)") = forAll { (T: TypeName, targs1: List[TypeDef], targs2: List[TypeDef], t: Tree) =>
+ q"type $T[..$targs1, ..$targs2] = $t" ≈ TypeDef(Modifiers(), T, targs1 ++ targs2, t)
+ }
+
+ property("splice tparams into typedef (3)") = forAll { (T: TypeName, targ: TypeDef, targs: List[TypeDef], t: Tree) =>
+ q"type $T[$targ, ..$targs] = $t" ≈ TypeDef(Modifiers(), T, targ :: targs, t)
+ }
+
+ property("splice typename into typedef with default bounds") = forAll { (T1: TypeName, T2: TypeName, t: Tree) =>
+ q"type $T1[$T2 >: Any <: Nothing] = $t" ≈
+ TypeDef(
+ Modifiers(), T1,
+ List(TypeDef(
+ Modifiers(PARAM), T2,
+ List(),
+ TypeBoundsTree(
+ Ident(TypeName("Any")),
+ Ident(TypeName("Nothing"))))),
+ t)
+ }
+
+ property("splice type names into compound type tree") = forAll { (T: TypeName, A: TypeName, B: TypeName) =>
+ q"type $T = $A with $B" ≈
+ TypeDef(
+ Modifiers(), T, List(),
+ CompoundTypeTree(
+ Template(List(Ident(A), Ident(B)), ValDef(Modifiers(PRIVATE), nme.WILDCARD, TypeTree(), EmptyTree), List())))
+ }
+
+ property("splice trees into existential type tree") = forAll {
+ (T1: TypeName, T2: TypeName, X: TypeName, Lo: TypeName, Hi: TypeName) =>
+
+ q"type $T1 = $T2[$X] forSome { type $X >: $Lo <: $Hi }" ≈
+ TypeDef(
+ Modifiers(), T1, List(),
+ ExistentialTypeTree(
+ AppliedTypeTree(Ident(T2), List(Ident(X))),
+ List(
+ TypeDef(Modifiers(DEFERRED), X, List(), TypeBoundsTree(Ident(Lo), Ident(Hi))))))
+ }
+
+ property("splice names into import selector") = forAll {
+ (expr: Tree, plain: Name, oldname: Name, newname: Name, discard: Name) =>
+
+ val Import(expr1, List(
+ ImportSelector(plain11, _, plain12, _),
+ ImportSelector(oldname1, _, newname1, _),
+ ImportSelector(discard1, _, wildcard, _))) =
+ q"import $expr.{$plain, $oldname => $newname, $discard => _}"
+
+ expr1 ≈ expr && plain11 == plain12 && plain12 == plain &&
+ oldname1 == oldname && newname1 == newname && discard1 == discard && wildcard == nme.WILDCARD
+ }
+
+ property("splice trees into while loop") = forAll { (cond: Tree, body: Tree) =>
+ val LabelDef(_, List(), If(cond1, Block(List(body1), Apply(_, List())), Literal(Constant(())))) = q"while($cond) $body"
+ body1 ≈ body && cond1 ≈ cond
+ }
+
+ property("splice trees into do while loop") = forAll { (cond: Tree, body: Tree) =>
+ val LabelDef(_, List(), Block(List(body1), If(cond1, Apply(_, List()), Literal(Constant(()))))) = q"do $body while($cond)"
+ body1 ≈ body && cond1 ≈ cond
+ }
+
+ property("splice trees into alternative") = forAll { (c: Tree, A: Tree, B: Tree) =>
+ q"$c match { case $A | $B => }" ≈
+ Match(c, List(
+ CaseDef(Alternative(List(A, B)), EmptyTree, Literal(Constant(())))))
+ }
+
+ property("splice into applied type tree") = forAll { (T1: TypeName, T2: TypeName, args: List[Tree]) =>
+ q"type $T1 = $T2[..$args]" ≈
+ TypeDef(
+ Modifiers(), T1, List(),
+ AppliedTypeTree(Ident(T2), args))
+ }
+
+ property("splice list of trees into block (1)") = forAll { (trees: List[Tree]) =>
+ q"{ ..$trees }" ≈ (trees match {
+ case Nil => Block(Nil, q"()")
+ case _ => Block(trees.init, trees.last)
+ })
+ }
+
+ property("splice list of trees into block (2)") = forAll { (trees1: List[Tree], trees2: List[Tree]) =>
+ q"{ ..$trees1 ; ..$trees2 }" ≈ ((trees1 ++ trees2) match {
+ case Nil => Block(Nil, Literal(Constant(())))
+ case trees => Block(trees.init, trees.last)
+ })
+ }
+
+ property("splice list of trees into block (3)") = forAll { (trees: List[Tree], tree: Tree) =>
+ q"{ ..$trees; $tree }" ≈ Block(trees, tree)
+ }
+
+ def assertSameAnnots(tree: {def mods: Modifiers}, annots: List[Tree]) =
+ assert(tree.mods.annotations ≈ annots,
+ s"${tree.mods.annotations} =/= ${annots}")
+
+ def assertSameAnnots(tree1: {def mods: Modifiers}, tree2: {def mods: Modifiers}) =
+ assert(tree1.mods.annotations ≈ tree2.mods.annotations,
+ s"${tree1.mods.annotations} =/= ${tree2.mods.annotations}")
+
+ property("splice type name into annotation") = test {
+ val name = TypeName("annot")
+ assertSameAnnots(q"@$name def foo", List(annot(name)))
+ }
+
+ property("splice ident into annotation") = test {
+ val name = TypeName("annot")
+ val ident = Ident(name)
+ assertSameAnnots(q"@$ident def foo", List(annot(name)))
+ }
+
+ property("splice idents into annotation") = test {
+ val idents = List(Ident(TypeName("annot1")), Ident(TypeName("annot2")))
+ assertSameAnnots(q"@..$idents def foo",
+ idents.map { ident => Apply(Select(New(ident), nme.CONSTRUCTOR), List()) })
+ }
+
+ property("splice constructor calls into annotation") = test {
+ val ctorcalls = List(annot("a1"), annot("a2"))
+ assertSameAnnots(q"@..$ctorcalls def foo", ctorcalls)
+ }
+
+ property("splice multiple annotations (1)") = test {
+ val annot1 = annot("a1")
+ val annot2 = annot("a2")
+ val res = q"@$annot1 @$annot2 def foo"
+ assertSameAnnots(res, List(annot1, annot2))
+ }
+
+ property("splice multiple annotations (2)") = test {
+ val annot1 = annot("a1")
+ val annots = List(annot("a2"), annot("a3"))
+ val res = q"@$annot1 @..$annots def foo"
+ assertSameAnnots(res, annot1 :: annots)
+ }
+
+ property("splice annotations with arguments (1)") = test {
+ val a = annot("a", List(q"x"))
+ assertSameAnnots(q"@$a def foo", q"@a(x) def foo")
+ }
+
+ property("splice annotations with arguments (2)") = test {
+ val a = newTypeName("a")
+ assertSameAnnots(q"@$a(x) def foo", q"@a(x) def foo")
+ }
+
+ property("splice annotations with arguments (3") = test {
+ val a = Ident(newTypeName("a"))
+ assertSameAnnots(q"@$a(x) def foo", q"@a(x) def foo")
+ }
+
+ property("can't splice annotations with arguments specificed twice") = test {
+ val a = annot("a", List(q"x"))
+ assertThrows[IllegalArgumentException] {
+ q"@$a(y) def foo"
+ }
+ }
+
+ property("splice term into brackets") = test {
+ val a = q"a"
+ assert(q"($a)" ≈ a)
+ }
+
+ property("splice terms into tuple") = test {
+ val a1 = q"a1"
+ val a2 = q"a2"
+ val as = List(a1, a2)
+ assert(q"(..$as)" ≈ q"Tuple2($a1, $a2)")
+ assert(q"(a0, ..$as)" ≈ q"Tuple3(a0, $a1, $a2)")
+ }
+
+ property("splice empty list into tuple") = test {
+ val empty = List[Tree]()
+ assert(q"(..$empty)" ≈ q"()")
+ }
+
+ property("splice improper tree into annot") = test {
+ val t = tq"Foo[Baz]"
+ assertThrows[IllegalArgumentException] {
+ q"@$t def foo"
+ }
+ }
+}
diff --git a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala
new file mode 100644
index 0000000000..114c9f112b
--- /dev/null
+++ b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala
@@ -0,0 +1,122 @@
+import org.scalacheck._
+import Prop._
+import Gen._
+import Arbitrary._
+
+import scala.reflect.runtime.universe._
+import Flag._
+
+object TermDeconstructionProps extends QuasiquoteProperties("term deconstruction") {
+
+ property("f(..x) = f") = test {
+ assertThrows[MatchError] {
+ val q"f(..$argss)" = q"f"
+ }
+ }
+
+ property("f(x)") = forAll { (x: Tree) =>
+ val q"f($x1)" = q"f($x)"
+ x1 ≈ x
+ }
+
+ property("f(..xs)") = forAll { (x1: Tree, x2: Tree) =>
+ val q"f(..$xs)" = q"f($x1, $x2)"
+ xs ≈ List(x1, x2)
+ }
+
+ property("f(y, ..ys)") = forAll { (x1: Tree, x2: Tree, x3: Tree) =>
+ val q"f($y, ..$ys)" = q"f($x1, $x2, $x3)"
+ y ≈ x1 && ys ≈ List(x2, x3)
+ }
+
+ property("f(y1, y2, ..ys)") = forAll { (x1: Tree, x2: Tree, x3: Tree) =>
+ val q"f($y1, $y2, ..$ys)" = q"f($x1, $x2, $x3)"
+ y1 ≈ x1 && y2 ≈ x2 && ys ≈ List(x3)
+ }
+
+ property("f(...xss)") = forAll { (x1: Tree, x2: Tree) =>
+ val q"f(...$argss)" = q"f($x1)($x2)"
+ argss ≈ List(List(x1), List(x2))
+ }
+
+ property("f(...xss) = f") = forAll { (x1: Tree, x2: Tree) =>
+ val q"f(...$argss)" = q"f"
+ argss ≈ List()
+ }
+
+ property("@$annot def foo") = forAll { (annotName: TypeName) =>
+ val q"@$annot def foo" = q"@$annotName def foo"
+ annot ≈ Apply(Select(New(Ident(annotName)), nme.CONSTRUCTOR), List())
+ }
+
+ property("@$annot(..$args) def foo") = forAll { (annotName: TypeName, tree: Tree) =>
+ val q"@$annot(..$args) def foo" = q"@$annotName($tree) def foo"
+ annot ≈ Ident(annotName) && args ≈ List(tree)
+ }
+
+ property("@..$annots def foo") = test {
+ val a = annot("a")
+ val b = annot("b")
+ val q"@..$annots def foo" = q"@$a @$b def foo"
+ annots ≈ List(a, b)
+ }
+
+ property("@$annot @..$annots def foo") = test {
+ val a = annot("a")
+ val b = annot("b")
+ val c = annot("c")
+ val q"@$first @..$rest def foo" = q"@$a @$b @$c def foo"
+ first ≈ a && rest ≈ List(b, c)
+ }
+
+ property("class without params") = test {
+ val q"class $name { ..$body }" = q"class Foo { def bar = 3 }"
+ assert(body ≈ List(q"def bar = 3"))
+ }
+
+ property("class constructor") = test {
+ val q"class $name(...$argss)" = q"class Foo(x: Int)(y: Int)"
+ assert(argss.length == 2)
+ }
+
+ property("class parents") = test {
+ val q"class $name extends ..$parents" = q"class Foo extends Bar with Blah"
+ assert(parents ≈ List(tq"Bar", tq"Blah"))
+ }
+
+ property("class selfdef") = test {
+ val q"class $name { $self => }" = q"class Foo { self: T => }"
+ assert(self.name ≈ TermName("self") && self.tpt ≈ tq"T")
+ }
+
+ property("class tparams") = test {
+ val q"class $name[..$tparams]" = q"class Foo[A, B]"
+ assert(tparams.map { _.name } == List(TypeName("A"), TypeName("B")))
+ }
+
+ property("deconstruct unit as tuple") = test {
+ val q"(..$xs)" = q"()"
+ assert(xs.isEmpty)
+ }
+
+ property("deconstruct tuple") = test {
+ val q"(..$xs)" = q"(a, b)"
+ assert(xs ≈ List(q"a", q"b"))
+ }
+
+ property("deconstruct tuple mixed") = test {
+ val q"($first, ..$rest)" = q"(a, b, c)"
+ assert(first ≈ q"a" && rest ≈ List(q"b", q"c"))
+ }
+
+ property("deconstruct cases") = test {
+ val q"$x match { case ..$cases }" = q"x match { case 1 => case 2 => }"
+ x ≈ q"x" && cases ≈ List(cq"1 =>", cq"2 =>")
+ }
+
+ property("deconstruct mods") = test {
+ val mods = Modifiers(IMPLICIT | PRIVATE, TermName("foobar"), Nil)
+ val q"$mods0 def foo" = q"$mods def foo"
+ assert(mods0 ≈ mods)
+ }
+} \ No newline at end of file
diff --git a/test/files/scalacheck/quasiquotes/Test.scala b/test/files/scalacheck/quasiquotes/Test.scala
new file mode 100644
index 0000000000..2387a9b008
--- /dev/null
+++ b/test/files/scalacheck/quasiquotes/Test.scala
@@ -0,0 +1,12 @@
+import org.scalacheck._
+
+object Test extends Properties("quasiquotes") {
+ include(TermConstructionProps)
+ include(TermDeconstructionProps)
+ include(TypeConstructionProps)
+ include(TypeDeconstructionProps)
+ include(PatternConstructionProps)
+ include(PatternDeconstructionProps)
+ include(LiftableProps)
+ include(ErrorProps)
+} \ No newline at end of file
diff --git a/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala b/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala
new file mode 100644
index 0000000000..535ed8ecbf
--- /dev/null
+++ b/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala
@@ -0,0 +1,25 @@
+import org.scalacheck._
+import Prop._
+import Gen._
+import Arbitrary._
+
+import scala.reflect.runtime.universe._
+import Flag._
+
+object TypeConstructionProps extends QuasiquoteProperties("type construction") {
+ property("bare idents contain type names") = test {
+ tq"x" ≈ Ident(TypeName("x"))
+ }
+
+ property("splice type names into AppliedTypeTree") = forAll { (name1: TypeName, name2: TypeName) =>
+ tq"$name1[$name2]" ≈ AppliedTypeTree(Ident(name1), List(Ident(name2)))
+ }
+
+ property("tuple type") = test {
+ val empty = List[Tree]()
+ val ts = List(tq"t1", tq"t2")
+ assert(tq"(..$empty)" ≈ tq"scala.Unit")
+ assert(tq"(..$ts)" ≈ tq"Tuple2[t1, t2]")
+ assert(tq"(t0, ..$ts)" ≈ tq"Tuple3[t0, t1, t2]")
+ }
+} \ No newline at end of file
diff --git a/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala
new file mode 100644
index 0000000000..6ab699d4f0
--- /dev/null
+++ b/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala
@@ -0,0 +1,29 @@
+import org.scalacheck._
+import Prop._
+import Gen._
+import Arbitrary._
+
+import scala.reflect.runtime.universe._
+import Flag._
+
+object TypeDeconstructionProps extends QuasiquoteProperties("type deconstruction") {
+ property("ident(type name)") = forAll { (name: TypeName) =>
+ val t = Ident(name)
+ val tq"$t1" = t
+ t1 ≈ t
+ }
+
+ property("applied type tree") = forAll { (name1: TypeName, name2: TypeName) =>
+ val tq"$a[$b]" = AppliedTypeTree(Ident(name1), List(Ident(name2)))
+ a ≈ Ident(name1) && b ≈ Ident(name2)
+ }
+
+ property("tuple type") = test {
+ val tq"(..$empty)" = tq"scala.Unit"
+ assert(empty.isEmpty)
+ val tq"(..$ts)" = tq"(t1, t2)"
+ assert(ts ≈ List(tq"t1", tq"t2"))
+ val tq"($head, ..$tail)" = tq"(t0, t1, t2)"
+ assert(head ≈ tq"t0" && tail ≈ List(tq"t1", tq"t2"))
+ }
+} \ No newline at end of file