summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreeDSL.scala2
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreeGen.scala10
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala49
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala362
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala33
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala8
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala1
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala13
-rw-r--r--src/reflect/scala/reflect/api/BuildUtils.scala29
-rw-r--r--src/reflect/scala/reflect/api/Quasiquotes.scala1
-rw-r--r--src/reflect/scala/reflect/internal/BuildUtils.scala303
-rw-r--r--src/reflect/scala/reflect/internal/Importers.scala14
-rw-r--r--src/reflect/scala/reflect/internal/StdAttachments.scala13
-rw-r--r--src/reflect/scala/reflect/internal/StdNames.scala9
-rw-r--r--src/reflect/scala/reflect/internal/TreeGen.scala450
-rw-r--r--src/reflect/scala/reflect/internal/Trees.scala2
-rw-r--r--src/reflect/scala/reflect/macros/Attachments.scala4
-rw-r--r--src/reflect/scala/reflect/runtime/JavaUniverseForce.scala3
-rw-r--r--test/files/scalacheck/quasiquotes/ArbitraryTreesAndNames.scala11
-rw-r--r--test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala10
-rw-r--r--test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala9
-rw-r--r--test/files/scalacheck/quasiquotes/ErrorProps.scala8
-rw-r--r--test/files/scalacheck/quasiquotes/ForProps.scala70
-rw-r--r--test/files/scalacheck/quasiquotes/LiftableProps.scala9
-rw-r--r--test/files/scalacheck/quasiquotes/PatternConstructionProps.scala9
-rw-r--r--test/files/scalacheck/quasiquotes/PatternDeconstructionProps.scala10
-rw-r--r--test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala54
-rw-r--r--test/files/scalacheck/quasiquotes/TermConstructionProps.scala19
-rw-r--r--test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala9
-rw-r--r--test/files/scalacheck/quasiquotes/Test.scala2
-rw-r--r--test/files/scalacheck/quasiquotes/TypeConstructionProps.scala15
-rw-r--r--test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala9
-rw-r--r--test/files/scalacheck/quasiquotes/TypecheckedProps.scala53
33 files changed, 1046 insertions, 557 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/TreeDSL.scala b/src/compiler/scala/tools/nsc/ast/TreeDSL.scala
index 0020528c5b..6dda30b5e7 100644
--- a/src/compiler/scala/tools/nsc/ast/TreeDSL.scala
+++ b/src/compiler/scala/tools/nsc/ast/TreeDSL.scala
@@ -137,7 +137,7 @@ trait TreeDSL {
def IF(tree: Tree) = new IfStart(tree, EmptyTree)
def TRY(tree: Tree) = new TryStart(tree, Nil, EmptyTree)
def BLOCK(xs: Tree*) = Block(xs.init.toList, xs.last)
- def SOME(xs: Tree*) = Apply(SomeClass.companionSymbol, treeBuilder.makeTupleTerm(xs.toList, flattenUnary = true))
+ def SOME(xs: Tree*) = Apply(SomeClass.companionSymbol, gen.mkTuple(xs.toList))
/** Typed trees from symbols. */
def REF(sym: Symbol) = gen.mkAttributedRef(sym)
diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
index 28b127698f..d4ac21a6b8 100644
--- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala
+++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
@@ -53,13 +53,6 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {
NewFromConstructor(constructor, expr)
}
- // annotate the expression with @unchecked
- def mkUnchecked(expr: Tree): Tree = atPos(expr.pos) {
- // This can't be "Annotated(New(UncheckedClass), expr)" because annotations
- // are very picky about things and it crashes the compiler with "unexpected new".
- Annotated(New(scalaDot(UncheckedClass.name), Nil), expr)
- }
-
// Builds a tree of the form "{ lhs = rhs ; lhs }"
def mkAssignAndReturn(lhs: Symbol, rhs: Tree): Tree = {
def lhsRef = if (lhs.owner.isClass) Select(This(lhs.owner), lhs) else Ident(lhs)
@@ -263,7 +256,4 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {
val stats1 = if (stats.isEmpty) List(Literal(Constant(()))) else stats
mkNew(Nil, noSelfType, stats1, NoPosition, NoPosition)
}
-
- def mkSyntheticParam(pname: TermName) =
- ValDef(Modifiers(PARAM | SYNTHETIC), pname, TypeTree(), EmptyTree)
}
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index cfa60cabc3..cd1869340a 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -861,7 +861,7 @@ self =>
atPos(start, in.skipToken()) { makeFunctionTypeTree(ts, typ()) }
else {
ts foreach checkNotByNameOrVarargs
- val tuple = atPos(start) { makeTupleType(ts, flattenUnary = true) }
+ val tuple = atPos(start) { makeTupleType(ts) }
infixTypeRest(
compoundTypeRest(
annotTypeRest(
@@ -923,7 +923,7 @@ self =>
def simpleType(): Tree = {
val start = in.offset
simpleTypeRest(in.token match {
- case LPAREN => atPos(start)(makeTupleType(inParens(types()), flattenUnary = true))
+ case LPAREN => atPos(start)(makeTupleType(inParens(types())))
case USCORE => wildcardType(in.skipToken())
case _ =>
path(thisOK = false, typeOK = true) match {
@@ -1394,9 +1394,9 @@ self =>
newLinesOpt()
if (in.token == YIELD) {
in.nextToken()
- makeForYield(enums, expr())
+ gen.mkFor(enums, gen.Yield(expr()))
} else {
- makeFor(enums, expr())
+ gen.mkFor(enums, expr())
}
}
def adjustStart(tree: Tree) =
@@ -1700,22 +1700,25 @@ self =>
* | val Pattern1 `=' Expr
* }}}
*/
- def enumerators(): List[Enumerator] = {
- val enums = new ListBuffer[Enumerator]
- generator(enums, eqOK = false)
+ def enumerators(): List[Tree] = {
+ val enums = new ListBuffer[Tree]
+ enums ++= enumerator(isFirst = true)
while (isStatSep) {
in.nextToken()
- if (in.token == IF) enums += makeFilter(in.offset, guard())
- else generator(enums, eqOK = true)
+ enums ++= enumerator(isFirst = false)
}
enums.toList
}
+ def enumerator(isFirst: Boolean, allowNestedIf: Boolean = true): List[Tree] =
+ if (in.token == IF && !isFirst) makeFilter(in.offset, guard()) :: Nil
+ else generator(!isFirst, allowNestedIf)
+
/** {{{
* Generator ::= Pattern1 (`<-' | `=') Expr [Guard]
* }}}
*/
- def generator(enums: ListBuffer[Enumerator], eqOK: Boolean) {
+ def generator(eqOK: Boolean, allowNestedIf: Boolean = true): List[Tree] = {
val start = in.offset
val hasVal = in.token == VAL
if (hasVal)
@@ -1733,13 +1736,22 @@ self =>
if (hasEq && eqOK) in.nextToken()
else accept(LARROW)
val rhs = expr()
- enums += makeGenerator(r2p(start, point, in.lastOffset max start), pat, hasEq, rhs)
- // why max above? IDE stress tests have shown that lastOffset could be less than start,
+
+ def loop(): List[Tree] =
+ if (in.token != IF) Nil
+ else makeFilter(in.offset, guard()) :: loop()
+
+ val tail =
+ if (allowNestedIf) loop()
+ else Nil
+
+ // why max? IDE stress tests have shown that lastOffset could be less than start,
// I guess this happens if instead if a for-expression we sit on a closing paren.
- while (in.token == IF) enums += makeFilter(in.offset, guard())
+ val genPos = r2p(start, point, in.lastOffset max start)
+ gen.mkGenerator(genPos, pat, hasEq, rhs) :: tail
}
- def makeFilter(start: Offset, tree: Tree) = Filter(r2p(start, tree.pos.point, tree.pos.end), tree)
+ def makeFilter(start: Offset, tree: Tree) = gen.Filter(tree).setPos(r2p(start, tree.pos.point, tree.pos.end))
/* -------- PATTERNS ------------------------------------------- */
@@ -2454,11 +2466,10 @@ self =>
EmptyTree
}
def mkDefs(p: Tree, tp: Tree, rhs: Tree): List[Tree] = {
- val trees =
- makePatDef(newmods,
- if (tp.isEmpty) p
- else Typed(p, tp) setPos (p.pos union tp.pos),
- rhs)
+ val trees = {
+ val pat = if (tp.isEmpty) p else Typed(p, tp) setPos (p.pos union tp.pos)
+ gen.mkPatDef(newmods, pat, rhs)
+ }
if (newmods.isDeferred) {
trees match {
case List(ValDef(_, _, _, EmptyTree)) =>
diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
index 28d5aefc2b..d88470bd5e 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
@@ -31,88 +31,6 @@ abstract class TreeBuilder {
def convertToTypeName(t: Tree) = gen.convertToTypeName(t)
- /** Convert all occurrences of (lower-case) variables in a pattern as follows:
- * x becomes x @ _
- * x: T becomes x @ (_: T)
- */
- object patvarTransformer extends Transformer {
- override def transform(tree: Tree): Tree = tree match {
- case Ident(name) if (treeInfo.isVarPattern(tree) && name != nme.WILDCARD) =>
- atPos(tree.pos)(Bind(name, atPos(tree.pos.focus) (Ident(nme.WILDCARD))))
- case Typed(id @ Ident(name), tpt) if (treeInfo.isVarPattern(id) && name != nme.WILDCARD) =>
- atPos(tree.pos.withPoint(id.pos.point)) {
- Bind(name, atPos(tree.pos.withStart(tree.pos.point)) {
- Typed(Ident(nme.WILDCARD), tpt)
- })
- }
- case Apply(fn @ Apply(_, _), args) =>
- treeCopy.Apply(tree, transform(fn), transformTrees(args))
- case Apply(fn, args) =>
- treeCopy.Apply(tree, fn, transformTrees(args))
- case Typed(expr, tpt) =>
- treeCopy.Typed(tree, transform(expr), tpt)
- case Bind(name, body) =>
- treeCopy.Bind(tree, name, transform(body))
- case Alternative(_) | Star(_) =>
- super.transform(tree)
- case _ =>
- tree
- }
- }
-
- /** Traverse pattern and collect all variable names with their types in buffer
- * The variables keep their positions; whereas the pattern is converted to be
- * synthetic for all nodes that contain a variable position.
- */
- class GetVarTraverser extends Traverser {
- val buf = new ListBuffer[(Name, Tree, Position)]
-
- def namePos(tree: Tree, name: Name): Position =
- if (!tree.pos.isRange || name.containsName(nme.raw.DOLLAR)) tree.pos.focus
- else {
- val start = tree.pos.start
- val end = start + name.decode.length
- r2p(start, start, end)
- }
-
- override def traverse(tree: Tree): Unit = {
- def seenName(name: Name) = buf exists (_._1 == name)
- def add(name: Name, t: Tree) = if (!seenName(name)) buf += ((name, t, namePos(tree, name)))
- val bl = buf.length
-
- tree match {
- case Bind(nme.WILDCARD, _) =>
- super.traverse(tree)
-
- case Bind(name, Typed(tree1, tpt)) =>
- val newTree = if (treeInfo.mayBeTypePat(tpt)) TypeTree() else tpt.duplicate
- add(name, newTree)
- traverse(tree1)
-
- case Bind(name, tree1) =>
- // can assume only name range as position, as otherwise might overlap
- // with binds embedded in pattern tree1
- add(name, TypeTree())
- traverse(tree1)
-
- case _ =>
- super.traverse(tree)
- }
- if (buf.length > bl)
- tree setPos tree.pos.makeTransparent
- }
- def apply(tree: Tree) = {
- traverse(tree)
- buf.toList
- }
- }
-
- /** Returns list of all pattern variables, possibly with their types,
- * without duplicates
- */
- private def getVariables(tree: Tree): List[(Name, Tree, Position)] =
- new GetVarTraverser apply tree
-
def byNameApplication(tpe: Tree): Tree =
AppliedTypeTree(rootScalaDot(tpnme.BYNAME_PARAM_CLASS_NAME), List(tpe))
def repeatedApplication(tpe: Tree): Tree =
@@ -121,25 +39,12 @@ abstract class TreeBuilder {
def makeImportSelector(name: Name, nameOffset: Int): ImportSelector =
ImportSelector(name, nameOffset, name, nameOffset)
- private def makeTuple(trees: List[Tree], isType: Boolean): Tree = {
- val tupString = "Tuple" + trees.length
- Apply(scalaDot(if (isType) newTypeName(tupString) else newTermName(tupString)), trees)
- }
-
- def makeTupleTerm(trees: List[Tree], flattenUnary: Boolean): Tree = trees match {
- case Nil => Literal(Constant(()))
- case List(tree) if flattenUnary => tree
- case _ => makeTuple(trees, isType = false)
- }
+ def makeTupleTerm(elems: List[Tree]) = gen.mkTuple(elems)
- def makeTupleType(trees: List[Tree], flattenUnary: Boolean): Tree = trees match {
- case Nil => scalaUnitConstr
- case List(tree) if flattenUnary => tree
- case _ => AppliedTypeTree(scalaDot(newTypeName("Tuple" + trees.length)), trees)
- }
+ def makeTupleType(elems: List[Tree]) = gen.mkTupleType(elems)
def stripParens(t: Tree) = t match {
- case Parens(ts) => atPos(t.pos) { makeTupleTerm(ts, flattenUnary = true) }
+ case Parens(ts) => atPos(t.pos) { makeTupleTerm(ts) }
case _ => t
}
@@ -149,22 +54,6 @@ abstract class TreeBuilder {
def makeSelfDef(name: TermName, tpt: Tree): ValDef =
ValDef(Modifiers(PRIVATE), name, tpt, EmptyTree)
- /** If tree is a variable pattern, return Some("its name and type").
- * Otherwise return none */
- private def matchVarPattern(tree: Tree): Option[(Name, Tree)] = {
- def wildType(t: Tree): Option[Tree] = t match {
- case Ident(x) if x.toTermName == nme.WILDCARD => Some(TypeTree())
- case Typed(Ident(x), tpt) if x.toTermName == nme.WILDCARD => Some(tpt)
- case _ => None
- }
- tree match {
- case Ident(name) => Some((name, TypeTree()))
- case Bind(name, body) => wildType(body) map (x => (name, x))
- case Typed(Ident(name), tpt) => Some((name, tpt))
- case _ => None
- }
- }
-
/** Create tree representing (unencoded) binary operation expression or pattern. */
def makeBinop(isExpr: Boolean, left: Tree, op: TermName, right: Tree, opPos: Position): Tree = {
def mkNamed(args: List[Tree]) = if (isExpr) args map treeInfo.assignmentToMaybeNamedArg else args
@@ -214,173 +103,12 @@ abstract class TreeBuilder {
/** Create block of statements `stats` */
def makeBlock(stats: List[Tree]): Tree = gen.mkBlock(stats)
- def makeFilter(tree: Tree, condition: Tree, scrutineeName: String): Tree = {
- val cases = List(
- CaseDef(condition, EmptyTree, Literal(Constant(true))),
- CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false)))
- )
- val matchTree = makeVisitor(cases, checkExhaustive = false, scrutineeName)
-
- atPos(tree.pos)(Apply(Select(tree, nme.withFilter), matchTree :: Nil))
- }
-
- /** Create tree for for-comprehension generator <val pat0 <- rhs0> */
- def makeGenerator(pos: Position, pat: Tree, valeq: Boolean, rhs: Tree): Enumerator = {
- val pat1 = patvarTransformer.transform(pat)
- val rhs1 =
- if (valeq || treeInfo.isVarPatternDeep(pat)) rhs
- else makeFilter(rhs, pat1.duplicate, nme.CHECK_IF_REFUTABLE_STRING)
-
- if (valeq) ValEq(pos, pat1, rhs1)
- else ValFrom(pos, pat1, rhs1)
- }
-
def makeParam(pname: TermName, tpe: Tree) =
ValDef(Modifiers(PARAM), pname, tpe, EmptyTree)
def makeSyntheticTypeParam(pname: TypeName, bounds: Tree) =
TypeDef(Modifiers(DEFERRED | SYNTHETIC), pname, Nil, bounds)
- abstract class Enumerator { def pos: Position }
- case class ValFrom(pos: Position, pat: Tree, rhs: Tree) extends Enumerator
- case class ValEq(pos: Position, pat: Tree, rhs: Tree) extends Enumerator
- case class Filter(pos: Position, test: Tree) extends Enumerator
-
- /** Create tree for for-comprehension <for (enums) do body> or
- * <for (enums) yield body> where mapName and flatMapName are chosen
- * corresponding to whether this is a for-do or a for-yield.
- * The creation performs the following rewrite rules:
- *
- * 1.
- *
- * for (P <- G) E ==> G.foreach (P => E)
- *
- * Here and in the following (P => E) is interpreted as the function (P => E)
- * if P is a variable pattern and as the partial function { case P => E } otherwise.
- *
- * 2.
- *
- * for (P <- G) yield E ==> G.map (P => E)
- *
- * 3.
- *
- * for (P_1 <- G_1; P_2 <- G_2; ...) ...
- * ==>
- * G_1.flatMap (P_1 => for (P_2 <- G_2; ...) ...)
- *
- * 4.
- *
- * for (P <- G; E; ...) ...
- * =>
- * for (P <- G.filter (P => E); ...) ...
- *
- * 5. For N < MaxTupleArity:
- *
- * for (P_1 <- G; P_2 = E_2; val P_N = E_N; ...)
- * ==>
- * for (TupleN(P_1, P_2, ... P_N) <-
- * for (x_1 @ P_1 <- G) yield {
- * val x_2 @ P_2 = E_2
- * ...
- * val x_N & P_N = E_N
- * TupleN(x_1, ..., x_N)
- * } ...)
- *
- * If any of the P_i are variable patterns, the corresponding `x_i @ P_i' is not generated
- * and the variable constituting P_i is used instead of x_i
- *
- * @param mapName The name to be used for maps (either map or foreach)
- * @param flatMapName The name to be used for flatMaps (either flatMap or foreach)
- * @param enums The enumerators in the for expression
- * @param body The body of the for expression
- */
- private def makeFor(mapName: TermName, flatMapName: TermName, enums: List[Enumerator], body: Tree): Tree = {
-
- /* make a closure pat => body.
- * The closure is assigned a transparent position with the point at pos.point and
- * the limits given by pat and body.
- */
- def makeClosure(pos: Position, pat: Tree, body: Tree): Tree = {
- def splitpos = wrappingPos(List(pat, body)).withPoint(pos.point).makeTransparent
- matchVarPattern(pat) match {
- case Some((name, tpt)) =>
- Function(
- List(atPos(pat.pos) { ValDef(Modifiers(PARAM), name.toTermName, tpt, EmptyTree) }),
- body) setPos splitpos
- case None =>
- atPos(splitpos) {
- makeVisitor(List(CaseDef(pat, EmptyTree, body)), checkExhaustive = false)
- }
- }
- }
-
- /* Make an application qual.meth(pat => body) positioned at `pos`.
- */
- def makeCombination(pos: Position, meth: TermName, qual: Tree, pat: Tree, body: Tree): Tree =
- Apply(Select(qual, meth) setPos qual.pos, List(makeClosure(pos, pat, body))) setPos pos
-
- /* If `pat` is not yet a `Bind` wrap it in one with a fresh name */
- def makeBind(pat: Tree): Tree = pat match {
- case Bind(_, _) => pat
- case _ => Bind(freshTermName(), pat) setPos pat.pos
- }
-
- /* A reference to the name bound in Bind `pat`. */
- def makeValue(pat: Tree): Tree = pat match {
- case Bind(name, _) => Ident(name) setPos pat.pos.focus
- }
-
- /* The position of the closure that starts with generator at position `genpos`. */
- def closurePos(genpos: Position) = {
- val end = body.pos match {
- case NoPosition => genpos.point
- case bodypos => bodypos.end
- }
- r2p(genpos.start, genpos.point, end)
- }
-
-// val result =
- enums match {
- case ValFrom(pos, pat, rhs) :: Nil =>
- makeCombination(closurePos(pos), mapName, rhs, pat, body)
- case ValFrom(pos, pat, rhs) :: (rest @ (ValFrom(_, _, _) :: _)) =>
- makeCombination(closurePos(pos), flatMapName, rhs, pat,
- makeFor(mapName, flatMapName, rest, body))
- case ValFrom(pos, pat, rhs) :: Filter(_, test) :: rest =>
- makeFor(mapName, flatMapName,
- ValFrom(pos, pat, makeCombination(rhs.pos union test.pos, nme.withFilter, rhs, pat.duplicate, test)) :: rest,
- body)
- case ValFrom(pos, pat, rhs) :: rest =>
- val valeqs = rest.take(definitions.MaxTupleArity - 1).takeWhile(_.isInstanceOf[ValEq])
- assert(!valeqs.isEmpty)
- val rest1 = rest.drop(valeqs.length)
- val pats = valeqs map { case ValEq(_, pat, _) => pat }
- val rhss = valeqs map { case ValEq(_, _, rhs) => rhs }
- val defpat1 = makeBind(pat)
- val defpats = pats map makeBind
- val pdefs = (defpats, rhss).zipped flatMap makePatDef
- val ids = (defpat1 :: defpats) map makeValue
- val rhs1 = makeForYield(
- List(ValFrom(pos, defpat1, rhs)),
- Block(pdefs, atPos(wrappingPos(ids)) { makeTupleTerm(ids, flattenUnary = true) }) setPos wrappingPos(pdefs))
- val allpats = (pat :: pats) map (_.duplicate)
- val vfrom1 = ValFrom(r2p(pos.start, pos.point, rhs1.pos.end), atPos(wrappingPos(allpats)) { makeTuple(allpats, isType = false) } , rhs1)
- makeFor(mapName, flatMapName, vfrom1 :: rest1, body)
- case _ =>
- EmptyTree //may happen for erroneous input
- }
-// println("made for "+result)
-// result
- }
-
- /** Create tree for for-do comprehension <for (enums) body> */
- def makeFor(enums: List[Enumerator], body: Tree): Tree =
- makeFor(nme.foreach, nme.foreach, enums, body)
-
- /** Create tree for for-yield comprehension <for (enums) yield body> */
- def makeForYield(enums: List[Enumerator], body: Tree): Tree =
- makeFor(nme.map, nme.flatMap, enums, body)
-
/** Create tree for a pattern alternative */
def makeAlternative(ts: List[Tree]): Tree = {
def alternatives(t: Tree): List[Tree] = t match {
@@ -390,21 +118,9 @@ abstract class TreeBuilder {
Alternative(ts flatMap alternatives)
}
- /** Create visitor <x => x match cases> */
- def makeVisitor(cases: List[CaseDef], checkExhaustive: Boolean): Tree =
- makeVisitor(cases, checkExhaustive, "x$")
-
- /** Create visitor <x => x match cases> */
- def makeVisitor(cases: List[CaseDef], checkExhaustive: Boolean, prefix: String): Tree = {
- val x = freshTermName(prefix)
- val id = Ident(x)
- val sel = if (checkExhaustive) id else gen.mkUnchecked(id)
- Function(List(gen.mkSyntheticParam(x)), Match(sel, cases))
- }
-
/** Create tree for case definition <case pat if guard => rhs> */
def makeCaseDef(pat: Tree, guard: Tree, rhs: Tree): CaseDef =
- CaseDef(patvarTransformer.transform(pat), guard, rhs)
+ CaseDef(gen.patvarTransformer.transform(pat), guard, rhs)
/** Creates tree representing:
* { case x: Throwable =>
@@ -428,76 +144,6 @@ abstract class TreeBuilder {
makeCaseDef(pat, EmptyTree, body)
}
- /** Create tree for pattern definition <val pat0 = rhs> */
- def makePatDef(pat: Tree, rhs: Tree): List[Tree] =
- makePatDef(Modifiers(0), pat, rhs)
-
- /** Create tree for pattern definition <mods val pat0 = rhs> */
- def makePatDef(mods: Modifiers, pat: Tree, rhs: Tree): List[Tree] = matchVarPattern(pat) match {
- case Some((name, tpt)) =>
- List(atPos(pat.pos union rhs.pos) {
- ValDef(mods, name.toTermName, tpt, rhs)
- })
-
- case None =>
- // in case there is exactly one variable x_1 in pattern
- // val/var p = e ==> val/var x_1 = e.match (case p => (x_1))
- //
- // in case there are zero or more than one variables in pattern
- // val/var p = e ==> private synthetic val t$ = e.match (case p => (x_1, ..., x_N))
- // val/var x_1 = t$._1
- // ...
- // val/var x_N = t$._N
-
- val rhsUnchecked = gen.mkUnchecked(rhs)
-
- // TODO: clean this up -- there is too much information packked into makePatDef's `pat` argument
- // when it's a simple identifier (case Some((name, tpt)) -- above),
- // pat should have the type ascription that was specified by the user
- // however, in `case None` (here), we must be careful not to generate illegal pattern trees (such as `(a, b): Tuple2[Int, String]`)
- // i.e., this must hold: pat1 match { case Typed(expr, tp) => assert(expr.isInstanceOf[Ident]) case _ => }
- // if we encounter such an erroneous pattern, we strip off the type ascription from pat and propagate the type information to rhs
- val (pat1, rhs1) = patvarTransformer.transform(pat) match {
- // move the Typed ascription to the rhs
- case Typed(expr, tpt) if !expr.isInstanceOf[Ident] =>
- val rhsTypedUnchecked =
- if (tpt.isEmpty) rhsUnchecked
- else Typed(rhsUnchecked, tpt) setPos (rhs.pos union tpt.pos)
- (expr, rhsTypedUnchecked)
- case ok =>
- (ok, rhsUnchecked)
- }
- val vars = getVariables(pat1)
- val matchExpr = atPos((pat1.pos union rhs.pos).makeTransparent) {
- Match(
- rhs1,
- List(
- atPos(pat1.pos) {
- CaseDef(pat1, EmptyTree, makeTupleTerm(vars map (_._1) map Ident.apply, flattenUnary = true))
- }
- ))
- }
- vars match {
- case List((vname, tpt, pos)) =>
- List(atPos(pat.pos union pos union rhs.pos) {
- ValDef(mods, vname.toTermName, tpt, matchExpr)
- })
- case _ =>
- val tmp = freshTermName()
- val firstDef =
- atPos(matchExpr.pos) {
- ValDef(Modifiers(PrivateLocal | SYNTHETIC | ARTIFACT | (mods.flags & LAZY)),
- tmp, TypeTree(), matchExpr)
- }
- var cnt = 0
- val restDefs = for ((vname, tpt, pos) <- vars) yield atPos(pos) {
- cnt += 1
- ValDef(mods, vname.toTermName, tpt, Select(Ident(tmp), newTermName("_" + cnt)))
- }
- firstDef :: restDefs
- }
- }
-
/** Create a tree representing the function type (argtpes) => restpe */
def makeFunctionTypeTree(argtpes: List[Tree], restpe: Tree): Tree = gen.mkFunctionTypeTree(argtpes, restpe)
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
index 0b5ade0b4c..3901184c25 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
@@ -63,11 +63,11 @@ trait Parsers { self: Quasiquotes =>
override implicit def fresh: FreshNameCreator = parser.fresh
// q"(..$xs)"
- override def makeTupleTerm(trees: List[Tree], flattenUnary: Boolean): Tree =
+ override def makeTupleTerm(trees: List[Tree]): Tree =
Apply(Ident(nme.QUASIQUOTE_TUPLE), trees)
// tq"(..$xs)"
- override def makeTupleType(trees: List[Tree], flattenUnary: Boolean): Tree =
+ override def makeTupleType(trees: List[Tree]): Tree =
AppliedTypeTree(Ident(tpnme.QUASIQUOTE_TUPLE), trees)
// q"{ $x }"
@@ -152,6 +152,13 @@ trait Parsers { self: Quasiquotes =>
in.nextToken()
stats
}
+
+ override def enumerator(isFirst: Boolean, allowNestedIf: Boolean = true) =
+ if (isHole && lookingAhead { in.token == EOF || in.token == RPAREN || isStatSep }) {
+ val res = build.SyntacticValFrom(Bind(in.name, Ident(nme.WILDCARD)), Ident(nme.QUASIQUOTE_FOR_ENUM)) :: Nil
+ in.nextToken()
+ res
+ } else super.enumerator(isFirst, allowNestedIf)
}
}
@@ -170,15 +177,29 @@ trait Parsers { self: Quasiquotes =>
object PatternParser extends Parser {
def entryPoint = { parser =>
val pat = parser.noSeq.pattern1()
- parser.treeBuilder.patvarTransformer.transform(pat)
+ gen.patvarTransformer.transform(pat)
+ }
+ }
+
+ object ForEnumeratorParser extends Parser {
+ def entryPoint = { parser =>
+ val enums = parser.enumerator(isFirst = false, allowNestedIf = false)
+ assert(enums.length == 1)
+ enums.head
}
}
+ // Extractor that matches names which were generated by call to
+ // freshTermName or freshTypeName within quasiquotes. Such names
+ // have qq$some$random$prefix$0 shape where qq$ part is added
+ // by modified fresh name creator in QuasiquoteParser.
object FreshName {
def unapply(name: Name): Option[String] =
- name.toString.split("\\$") match {
- case Array(qq, left, right) if qq + "$" == nme.QUASIQUOTE_PREFIX && Try(right.toInt).isSuccess =>
- Some(left + "$")
+ name.toString.split("\\$").toSeq match {
+ case qq +: (middle :+ last)
+ if qq + "$" == nme.QUASIQUOTE_PREFIX
+ && Try(last.toInt).isSuccess && middle.nonEmpty =>
+ Some(middle.mkString("", "$", "$"))
case _ =>
None
}
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
index c31d1fcd12..54be9123c7 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
@@ -161,4 +161,12 @@ trait Placeholders { self: Quasiquotes =>
case _ => None
}
}
+
+ object ForEnumPlaceholder {
+ def unapply(tree: Tree): Option[(Tree, Location, Cardinality)] = tree match {
+ case build.SyntacticValFrom(Bind(Placeholder(tree, location, card), Ident(nme.WILDCARD)), Ident(nme.QUASIQUOTE_FOR_ENUM)) =>
+ Some((tree, location, card))
+ 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
index f4d6b39d02..7d777ef7d5 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala
@@ -31,6 +31,7 @@ abstract class Quasiquotes extends Parsers
case nme.tq => TypeParser.parse(_)
case nme.cq => CaseParser.parse(_)
case nme.pq => PatternParser.parse(_)
+ case nme.fq => ForEnumeratorParser.parse(_)
case other => global.abort(s"Unknown quasiquote flavor: $other")
}
(universe0, args0, parts1, parse0, reify0, method0)
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
index 3d1ecf95b2..b28c85cfc2 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
@@ -127,6 +127,7 @@ trait Reifiers { self: Quasiquotes =>
case RefineStatPlaceholder(tree, _, _) => reifyRefineStat(tree)
case EarlyDefPlaceholder(tree, _, _) => reifyEarlyDef(tree)
case PackageStatPlaceholder(tree, _, _) => reifyPackageStat(tree)
+ case ForEnumPlaceholder(tree, _, _) => tree
case _ => EmptyTree
}
@@ -148,6 +149,16 @@ trait Reifiers { self: Quasiquotes =>
reifyBuildCall(nme.SyntacticValDef, mods, name, tpt, rhs)
case SyntacticVarDef(mods, name, tpt, rhs) =>
reifyBuildCall(nme.SyntacticVarDef, mods, name, tpt, rhs)
+ case SyntacticValFrom(pat, rhs) =>
+ reifyBuildCall(nme.SyntacticValFrom, pat, rhs)
+ case SyntacticValEq(pat, rhs) =>
+ reifyBuildCall(nme.SyntacticValEq, pat, rhs)
+ case SyntacticFilter(cond) =>
+ reifyBuildCall(nme.SyntacticFilter, cond)
+ case SyntacticFor(enums, body) =>
+ reifyBuildCall(nme.SyntacticFor, enums, body)
+ case SyntacticForYield(enums, body) =>
+ reifyBuildCall(nme.SyntacticForYield, enums, body)
case SyntacticAssign(lhs, rhs) =>
reifyBuildCall(nme.SyntacticAssign, lhs, rhs)
case SyntacticApplied(fun, List(args))
@@ -275,7 +286,7 @@ trait Reifiers { self: Quasiquotes =>
case RefineStatPlaceholder(tree, _, DotDot) => reifyRefineStat(tree)
case EarlyDefPlaceholder(tree, _, DotDot) => reifyEarlyDef(tree)
case PackageStatPlaceholder(tree, _, DotDot) => reifyPackageStat(tree)
-
+ case ForEnumPlaceholder(tree, _, DotDot) => tree
case List(Placeholder(tree, _, DotDotDot)) => tree
} {
reify(_)
diff --git a/src/reflect/scala/reflect/api/BuildUtils.scala b/src/reflect/scala/reflect/api/BuildUtils.scala
index 28551b1dcd..cf05aefe72 100644
--- a/src/reflect/scala/reflect/api/BuildUtils.scala
+++ b/src/reflect/scala/reflect/api/BuildUtils.scala
@@ -223,5 +223,34 @@ private[reflect] trait BuildUtils { self: Universe =>
def apply(lhs: Tree, rhs: Tree): Tree
def unapply(tree: Tree): Option[(Tree, Tree)]
}
+
+ val SyntacticValFrom: SyntacticValFromExtractor
+
+ trait SyntacticValFromExtractor {
+ def apply(pat: Tree, rhs: Tree): Tree
+ def unapply(tree: Tree): Option[(Tree, Tree)]
+ }
+
+ val SyntacticValEq: SyntacticValEqExtractor
+
+ trait SyntacticValEqExtractor {
+ def apply(pat: Tree, rhs: Tree): Tree
+ def unapply(tree: Tree): Option[(Tree, Tree)]
+ }
+
+ val SyntacticFilter: SyntacticFilterExtractor
+
+ trait SyntacticFilterExtractor {
+ def apply(test: Tree): Tree
+ def unapply(tree: Tree): Option[(Tree)]
+ }
+
+ val SyntacticFor: SyntacticForExtractor
+ val SyntacticForYield: SyntacticForExtractor
+
+ trait SyntacticForExtractor {
+ def apply(enums: List[Tree], body: Tree): Tree
+ def unapply(tree: Tree): Option[(List[Tree], Tree)]
+ }
}
}
diff --git a/src/reflect/scala/reflect/api/Quasiquotes.scala b/src/reflect/scala/reflect/api/Quasiquotes.scala
index 3687ccba63..fcf8edcec7 100644
--- a/src/reflect/scala/reflect/api/Quasiquotes.scala
+++ b/src/reflect/scala/reflect/api/Quasiquotes.scala
@@ -14,5 +14,6 @@ trait Quasiquotes { self: Universe =>
object tq extends api
object cq extends api
object pq extends api
+ object fq extends api
}
}
diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala
index fc6b26db3f..8fc1869dd2 100644
--- a/src/reflect/scala/reflect/internal/BuildUtils.scala
+++ b/src/reflect/scala/reflect/internal/BuildUtils.scala
@@ -9,7 +9,6 @@ trait BuildUtils { self: SymbolTable =>
import definitions.{TupleClass, FunctionClass, ScalaPackage, UnitClass}
class BuildImpl extends BuildApi {
-
def selectType(owner: Symbol, name: String): TypeSymbol =
select(owner, newTypeName(name)).asType
@@ -19,7 +18,7 @@ trait BuildUtils { self: SymbolTable =>
else result
}
- private def select(owner: Symbol, name: Name): Symbol = {
+ protected def select(owner: Symbol, name: Name): Symbol = {
val result = owner.info decl name
if (result ne NoSymbol) result
else
@@ -135,7 +134,7 @@ trait BuildUtils { self: SymbolTable =>
def withFreshTypeName[T](prefix: String)(f: TypeName => T): T = f(freshTypeName(prefix))
- private implicit def fresh: FreshNameCreator = self.currentFreshNameCreator
+ protected implicit def fresh: FreshNameCreator = self.currentFreshNameCreator
object FlagsRepr extends FlagsReprExtractor {
def apply(bits: Long): FlagSet = bits
@@ -175,7 +174,8 @@ trait BuildUtils { self: SymbolTable =>
}
}
- private object UnCtor {
+ // recover constructor contents generated by gen.mkTemplate
+ protected object UnCtor {
def unapply(tree: Tree): Option[(Modifiers, List[List[ValDef]], List[Tree])] = tree match {
case DefDef(mods, nme.MIXIN_CONSTRUCTOR, _, _, _, Block(lvdefs, _)) =>
Some((mods | Flag.TRAIT, Nil, lvdefs))
@@ -185,7 +185,8 @@ trait BuildUtils { self: SymbolTable =>
}
}
- private object UnMkTemplate {
+ // undo gen.mkTemplate
+ protected object UnMkTemplate {
def unapply(templ: Template): Option[(List[Tree], ValDef, Modifiers, List[List[ValDef]], List[Tree], List[Tree])] = {
val Template(parents, selfType, tbody) = templ
def result(ctorMods: Modifiers, vparamss: List[List[ValDef]], edefs: List[Tree], body: List[Tree]) =
@@ -297,8 +298,8 @@ trait BuildUtils { self: SymbolTable =>
}
}
- private trait ScalaMemberRef {
- val symbols: Seq[Symbol]
+ // match references to `scala.$name`
+ protected class ScalaMemberRef(symbols: Seq[Symbol]) {
def result(name: Name): Option[Symbol] =
symbols.collect { case sym if sym.name == name => sym }.headOption
def unapply(tree: Tree): Option[Symbol] = tree match {
@@ -311,31 +312,23 @@ trait BuildUtils { self: SymbolTable =>
case _ => None
}
}
- private object TupleClassRef extends ScalaMemberRef {
- val symbols = TupleClass.seq
- }
- private object TupleCompanionRef extends ScalaMemberRef {
- val symbols = TupleClass.seq.map { _.companionModule }
- }
- private object UnitClassRef extends ScalaMemberRef {
- val symbols = Seq(UnitClass)
- }
- private object FunctionClassRef extends ScalaMemberRef {
- val symbols = FunctionClass.seq
- }
+ protected object TupleClassRef extends ScalaMemberRef(TupleClass.seq)
+ protected object TupleCompanionRef extends ScalaMemberRef(TupleClass.seq.map { _.companionModule })
+ protected object UnitClassRef extends ScalaMemberRef(Seq(UnitClass))
+ protected object FunctionClassRef extends ScalaMemberRef(FunctionClass.seq)
object SyntacticTuple extends SyntacticTupleExtractor {
- def apply(args: List[Tree]): Tree = args match {
- case Nil => Literal(Constant(()))
- case _ =>
- require(TupleClass(args.length).exists, s"Tuples with ${args.length} arity aren't supported")
- self.Apply(TupleClass(args.length).companionModule, args: _*)
+ def apply(args: List[Tree]): Tree = {
+ require(args.isEmpty || TupleClass(args.length).exists, s"Tuples with ${args.length} arity aren't supported")
+ gen.mkTuple(args, flattenUnary = false)
}
def unapply(tree: Tree): Option[List[Tree]] = tree match {
case Literal(Constant(())) =>
Some(Nil)
- case Apply(TupleCompanionRef(sym), args) if sym == TupleClass(args.length).companionModule =>
+ case Apply(MaybeTypeTreeOriginal(SyntacticTypeApplied(MaybeSelectApply(TupleCompanionRef(sym)), targs)), args)
+ if sym == TupleClass(args.length).companionModule
+ && (targs.isEmpty || targs.length == args.length) =>
Some(args)
case _ =>
None
@@ -343,17 +336,16 @@ trait BuildUtils { self: SymbolTable =>
}
object SyntacticTupleType extends SyntacticTupleExtractor {
- def apply(args: List[Tree]): Tree = args match {
- case Nil => self.Select(self.Ident(nme.scala_), tpnme.Unit)
- case _ =>
- require(TupleClass(args.length).exists, s"Tuples with ${args.length} arity aren't supported")
- AppliedTypeTree(Ident(TupleClass(args.length)), args)
+ def apply(args: List[Tree]): Tree = {
+ require(args.isEmpty || TupleClass(args.length).exists, s"Tuples with ${args.length} arity aren't supported")
+ gen.mkTupleType(args, flattenUnary = false)
}
- def unapply(tree: Tree): Option[List[Tree]] = tree match {
- case UnitClassRef(_) =>
+ def unapply(tree: Tree): Option[List[Tree]] = tree match {
+ case MaybeTypeTreeOriginal(UnitClassRef(_)) =>
Some(Nil)
- case AppliedTypeTree(TupleClassRef(sym), args) if sym == TupleClass(args.length) =>
+ case MaybeTypeTreeOriginal(AppliedTypeTree(TupleClassRef(sym), args))
+ if sym == TupleClass(args.length) =>
Some(args)
case _ =>
None
@@ -367,7 +359,8 @@ trait BuildUtils { self: SymbolTable =>
}
def unapply(tree: Tree): Option[(List[Tree], Tree)] = tree match {
- case AppliedTypeTree(FunctionClassRef(sym), args @ (argtpes :+ restpe)) if sym == FunctionClass(args.length - 1) =>
+ case MaybeTypeTreeOriginal(AppliedTypeTree(FunctionClassRef(sym), args @ (argtpes :+ restpe)))
+ if sym == FunctionClass(args.length - 1) =>
Some((argtpes, restpe))
case _ => None
}
@@ -423,9 +416,7 @@ trait BuildUtils { self: SymbolTable =>
}
}
- trait SyntacticValDefBase extends SyntacticValDefExtractor {
- val isMutable: Boolean
-
+ protected class SyntacticValDefBase(isMutable: Boolean) extends SyntacticValDefExtractor {
def apply(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree) = {
val mods1 = if (isMutable) mods | MUTABLE else mods
ValDef(mods1, name, tpt, rhs)
@@ -438,9 +429,8 @@ trait BuildUtils { self: SymbolTable =>
None
}
}
-
- object SyntacticValDef extends SyntacticValDefBase { val isMutable = false }
- object SyntacticVarDef extends SyntacticValDefBase { val isMutable = true }
+ object SyntacticValDef extends SyntacticValDefBase(isMutable = false)
+ object SyntacticVarDef extends SyntacticValDefBase(isMutable = true)
object SyntacticAssign extends SyntacticAssignExtractor {
def apply(lhs: Tree, rhs: Tree): Tree = gen.mkAssign(lhs, rhs)
@@ -451,7 +441,238 @@ trait BuildUtils { self: SymbolTable =>
case _ => None
}
}
+
+ object SyntacticValFrom extends SyntacticValFromExtractor {
+ def apply(pat: Tree, rhs: Tree): Tree = gen.ValFrom(pat, gen.mkCheckIfRefutable(pat, rhs))
+ def unapply(tree: Tree): Option[(Tree, Tree)] = tree match {
+ case gen.ValFrom(pat, UnCheckIfRefutable(pat1, rhs1)) if pat.equalsStructure(pat1) =>
+ Some((pat, rhs1))
+ case gen.ValFrom(pat, rhs) =>
+ Some((pat, rhs))
+ case _ => None
+ }
+ }
+
+ object SyntacticValEq extends SyntacticValEqExtractor {
+ def apply(pat: Tree, rhs: Tree): Tree = gen.ValEq(pat, rhs)
+ def unapply(tree: Tree): Option[(Tree, Tree)] = gen.ValEq.unapply(tree)
+ }
+
+ object SyntacticFilter extends SyntacticFilterExtractor {
+ def apply(tree: Tree): Tree = gen.Filter(tree)
+ def unapply(tree: Tree): Option[Tree] = gen.Filter.unapply(tree)
+ }
+
+ // abstract over possible alternative representations of no type in valdef
+ protected object EmptyTypTree {
+ def unapply(tree: Tree): Boolean = tree match {
+ case EmptyTree => true
+ case tt: TypeTree if (tt.original == null || tt.original.isEmpty) => true
+ case _ => false
+ }
+ }
+
+ // match a sequence of desugared `val $pat = $value`
+ protected object UnPatSeq {
+ def unapply(trees: List[Tree]): Option[List[(Tree, Tree)]] = trees match {
+ case Nil => Some(Nil)
+ // case q"$mods val ${_}: ${_} = ${MaybeUnchecked(value)} match { case $pat => (..$ids) }" :: tail
+ case ValDef(mods, _, _, Match(MaybeUnchecked(value), CaseDef(pat, EmptyTree, SyntacticTuple(ids)) :: Nil)) :: tail
+ if mods.hasFlag(SYNTHETIC) && mods.hasFlag(ARTIFACT) =>
+ tail.drop(ids.length) match {
+ case UnPatSeq(rest) => Some((pat, value) :: rest)
+ case _ => None
+ }
+ // case q"${_} val $name1: ${_} = ${MaybeUnchecked(value)} match { case $pat => ${Ident(name2)} }" :: UnPatSeq(rest)
+ case ValDef(_, name1, _, Match(MaybeUnchecked(value), CaseDef(pat, EmptyTree, Ident(name2)) :: Nil)) :: UnPatSeq(rest)
+ if name1 == name2 =>
+ Some((pat, value) :: rest)
+ // case q"${_} val $name: ${EmptyTypTree()} = $value" :: UnPatSeq(rest) =>
+ case ValDef(_, name, EmptyTypTree(), value) :: UnPatSeq(rest) =>
+ Some((Bind(name, self.Ident(nme.WILDCARD)), value) :: rest)
+ // case q"${_} val $name: $tpt = $value" :: UnPatSeq(rest) =>
+ case ValDef(_, name, tpt, value) :: UnPatSeq(rest) =>
+ Some((Bind(name, Typed(self.Ident(nme.WILDCARD), tpt)), value) :: rest)
+ case _ => None
+ }
+ }
+
+ // match a sequence of desugared `val $pat = $value` with a tuple in the end
+ protected object UnPatSeqWithRes {
+ def unapply(tree: Tree): Option[(List[(Tree, Tree)], List[Tree])] = tree match {
+ case SyntacticBlock(UnPatSeq(trees) :+ SyntacticTuple(elems)) => Some((trees, elems))
+ case _ => None
+ }
+ }
+
+ // undo gen.mkSyntheticParam
+ protected object UnSyntheticParam {
+ def unapply(tree: Tree): Option[TermName] = tree match {
+ case ValDef(mods, name, _, EmptyTree)
+ if mods.hasFlag(SYNTHETIC) && mods.hasFlag(PARAM) =>
+ Some(name)
+ case _ => None
+ }
+ }
+
+ // undo gen.mkVisitor
+ protected object UnVisitor {
+ def unapply(tree: Tree): Option[(TermName, List[CaseDef])] = tree match {
+ case Function(UnSyntheticParam(x1) :: Nil, Match(MaybeUnchecked(Ident(x2)), cases))
+ if x1 == x2 =>
+ Some((x1, cases))
+ case _ => None
+ }
+ }
+
+ // undo gen.mkFor:makeClosure
+ protected object UnClosure {
+ def unapply(tree: Tree): Option[(Tree, Tree)] = tree match {
+ case Function(ValDef(Modifiers(PARAM, _, _), name, tpt, EmptyTree) :: Nil, body) =>
+ tpt match {
+ case EmptyTypTree() => Some((Bind(name, self.Ident(nme.WILDCARD)), body))
+ case _ => Some((Bind(name, Typed(self.Ident(nme.WILDCARD), tpt)), body))
+ }
+ case UnVisitor(_, CaseDef(pat, EmptyTree, body) :: Nil) =>
+ Some((pat, body))
+ case _ => None
+ }
+ }
+
+ // match call to either withFilter or filter
+ protected object FilterCall {
+ def unapply(tree: Tree): Option[(Tree,Tree)] = tree match {
+ case Apply(Select(obj, nme.withFilter | nme.filter), arg :: Nil) =>
+ Some(obj, arg)
+ case _ => None
+ }
+ }
+
+ // transform a chain of withFilter calls into a sequence of for filters
+ protected object UnFilter {
+ def unapply(tree: Tree): Some[(Tree, List[Tree])] = tree match {
+ case UnCheckIfRefutable(_, _) =>
+ Some((tree, Nil))
+ case FilterCall(UnFilter(rhs, rest), UnClosure(_, test)) =>
+ Some((rhs, rest :+ SyntacticFilter(test)))
+ case _ =>
+ Some((tree, Nil))
+ }
+ }
+
+ // undo gen.mkCheckIfRefutable
+ protected object UnCheckIfRefutable {
+ def unapply(tree: Tree): Option[(Tree, Tree)] = tree match {
+ case FilterCall(rhs, UnVisitor(name,
+ CaseDef(pat, EmptyTree, Literal(Constant(true))) ::
+ CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false))) :: Nil))
+ if name.toString.contains(nme.CHECK_IF_REFUTABLE_STRING) =>
+ Some((pat, rhs))
+ case _ => None
+ }
+ }
+
+ // undo gen.mkFor:makeCombination accounting for possible extra implicit argument
+ protected class UnForCombination(name: TermName) {
+ def unapply(tree: Tree) = tree match {
+ case SyntacticApplied(SyntacticTypeApplied(sel @ Select(lhs, meth), _), (f :: Nil) :: Nil)
+ if name == meth && sel.hasAttachment[ForAttachment.type] =>
+ Some(lhs, f)
+ case SyntacticApplied(SyntacticTypeApplied(sel @ Select(lhs, meth), _), (f :: Nil) :: _ :: Nil)
+ if name == meth && sel.hasAttachment[ForAttachment.type] =>
+ Some(lhs, f)
+ case _ => None
+ }
+ }
+ protected object UnMap extends UnForCombination(nme.map)
+ protected object UnForeach extends UnForCombination(nme.foreach)
+ protected object UnFlatMap extends UnForCombination(nme.flatMap)
+
+ // undo desugaring done in gen.mkFor
+ protected object UnFor {
+ def unapply(tree: Tree): Option[(List[Tree], Tree)] = {
+ val interm = tree match {
+ case UnFlatMap(UnFilter(rhs, filters), UnClosure(pat, UnFor(rest, body))) =>
+ Some(((pat, rhs), filters ::: rest, body))
+ case UnForeach(UnFilter(rhs, filters), UnClosure(pat, UnFor(rest, body))) =>
+ Some(((pat, rhs), filters ::: rest, body))
+ case UnMap(UnFilter(rhs, filters), UnClosure(pat, cbody)) =>
+ Some(((pat, rhs), filters, gen.Yield(cbody)))
+ case UnForeach(UnFilter(rhs, filters), UnClosure(pat, cbody)) =>
+ Some(((pat, rhs), filters, cbody))
+ case _ => None
+ }
+ interm.flatMap {
+ case ((Bind(_, SyntacticTuple(_)) | SyntacticTuple(_),
+ UnFor(SyntacticValFrom(pat, rhs) :: innerRest, gen.Yield(UnPatSeqWithRes(pats, elems2)))),
+ outerRest, fbody) =>
+ val valeqs = pats.map { case (pat, rhs) => SyntacticValEq(pat, rhs) }
+ Some((SyntacticValFrom(pat, rhs) :: innerRest ::: valeqs ::: outerRest, fbody))
+ case ((pat, rhs), filters, body) =>
+ Some((SyntacticValFrom(pat, rhs) :: filters, body))
+ }
+ }
+ }
+
+ // check that enumerators are valid
+ protected def mkEnumerators(enums: List[Tree]): List[Tree] = {
+ require(enums.nonEmpty, "enumerators can't be empty")
+ enums.head match {
+ case SyntacticValFrom(_, _) =>
+ case t => throw new IllegalArgumentException(s"$t is not a valid fist enumerator of for loop")
+ }
+ enums.tail.foreach {
+ case SyntacticValEq(_, _) | SyntacticValFrom(_, _) | SyntacticFilter(_) =>
+ case t => throw new IllegalArgumentException(s"$t is not a valid representation of a for loop enumerator")
+ }
+ enums
+ }
+
+ object SyntacticFor extends SyntacticForExtractor {
+ def apply(enums: List[Tree], body: Tree): Tree = gen.mkFor(mkEnumerators(enums), body)
+ def unapply(tree: Tree) = tree match {
+ case UnFor(enums, gen.Yield(body)) => None
+ case UnFor(enums, body) => Some((enums, body))
+ case _ => None
+ }
+ }
+
+ object SyntacticForYield extends SyntacticForExtractor {
+ def apply(enums: List[Tree], body: Tree): Tree = gen.mkFor(mkEnumerators(enums), gen.Yield(body))
+ def unapply(tree: Tree) = tree match {
+ case UnFor(enums, gen.Yield(body)) => Some((enums, body))
+ case _ => None
+ }
+ }
+
+ // use typetree's original instead of typetree itself
+ protected object MaybeTypeTreeOriginal {
+ def unapply(tree: Tree): Some[Tree] = tree match {
+ case tt: TypeTree => Some(tt.original)
+ case _ => Some(tree)
+ }
+ }
+
+ // drop potential extra call to .apply
+ protected object MaybeSelectApply {
+ def unapply(tree: Tree): Some[Tree] = tree match {
+ case Select(f, nme.apply) => Some(f)
+ case other => Some(other)
+ }
+ }
+
+ // drop potential @scala.unchecked annotation
+ protected object MaybeUnchecked {
+ def unapply(tree: Tree): Some[Tree] = tree match {
+ case Annotated(SyntacticNew(Nil, Apply(ScalaDot(tpnme.unchecked), Nil) :: Nil, noSelfType, Nil), annottee) =>
+ Some(annottee)
+ case Typed(annottee, MaybeTypeTreeOriginal(
+ Annotated(SyntacticNew(Nil, Apply(ScalaDot(tpnme.unchecked), Nil) :: Nil, noSelfType, Nil), _))) =>
+ Some(annottee)
+ case annottee => Some(annottee)
+ }
+ }
}
- val build: BuildApi = new BuildImpl
+ val build: BuildImpl = new BuildImpl
}
diff --git a/src/reflect/scala/reflect/internal/Importers.scala b/src/reflect/scala/reflect/internal/Importers.scala
index 72c8ccfa62..cc6e55192f 100644
--- a/src/reflect/scala/reflect/internal/Importers.scala
+++ b/src/reflect/scala/reflect/internal/Importers.scala
@@ -8,6 +8,16 @@ import scala.ref.WeakReference
// SI-6241: move importers to a mirror
trait Importers extends api.Importers { to: SymbolTable =>
+ /** Attachment that knows how to import itself into another universe. */
+ trait ImportableAttachment {
+ def importAttachment(importer: Importer): this.type
+ }
+
+ /** Attachment that doesn't contain any reflection artificats and can be imported as-is. */
+ trait PlainAttachment extends ImportableAttachment {
+ def importAttachment(importer: Importer): this.type = this
+ }
+
def mkImporter(from0: api.Universe): Importer { val from: from0.type } = (
if (to eq from0) {
new Importer {
@@ -417,11 +427,15 @@ trait Importers extends api.Importers { to: SymbolTable =>
my.setPos(importPosition(their.pos))
}
}
+ importAttachments(their.attachments.all).foreach { my.updateAttachment(_) }
my
}
// ============== MISCELLANEOUS ==============
+ def importAttachments(attachments: Set[Any]): Set[Any] =
+ attachments.collect { case ia: ImportableAttachment => ia.importAttachment(this) }
+
def importAnnotationInfo(ann: from.AnnotationInfo): AnnotationInfo = {
val atp1 = importType(ann.atp)
val args1 = ann.args map importTree
diff --git a/src/reflect/scala/reflect/internal/StdAttachments.scala b/src/reflect/scala/reflect/internal/StdAttachments.scala
index 9eb66db01e..46f241643b 100644
--- a/src/reflect/scala/reflect/internal/StdAttachments.scala
+++ b/src/reflect/scala/reflect/internal/StdAttachments.scala
@@ -14,6 +14,7 @@ trait StdAttachments {
def setAttachments(attachments: scala.reflect.macros.Attachments { type Pos = Position }): this.type = { rawatt = attachments; this }
def updateAttachment[T: ClassTag](attachment: T): this.type = { rawatt = rawatt.update(attachment); this }
def removeAttachment[T: ClassTag]: this.type = { rawatt = rawatt.remove[T]; this }
+ def hasAttachment[T: ClassTag]: Boolean = rawatt.contains[T]
// cannot be final due to SynchronizedSymbols
def pos: Position = rawatt.pos
@@ -21,13 +22,17 @@ trait StdAttachments {
def setPos(newpos: Position): this.type = { pos = newpos; this }
}
- /** When present, indicates that the host `Ident` has been created from a backquoted identifier.
- */
- case object BackquotedIdentifierAttachment
-
/** Stores the trees that give rise to a refined type to be used in reification.
* Unfortunately typed `CompoundTypeTree` is lacking essential info, and the reifier cannot use `CompoundTypeTree.tpe`.
* Therefore we need this hack (see `Reshape.toPreTyperTypeTree` for a detailed explanation).
*/
case class CompoundTypeTreeOriginalAttachment(parents: List[Tree], stats: List[Tree])
+
+ /** When present, indicates that the host `Ident` has been created from a backquoted identifier.
+ */
+ case object BackquotedIdentifierAttachment extends PlainAttachment
+
+ /** Identifies trees are either result or intermidiate value of for loop desugaring.
+ */
+ case object ForAttachment extends PlainAttachment
}
diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala
index 02f22a16f6..c26e815df1 100644
--- a/src/reflect/scala/reflect/internal/StdNames.scala
+++ b/src/reflect/scala/reflect/internal/StdNames.scala
@@ -229,6 +229,7 @@ trait StdNames {
final val Serializable: NameType = "Serializable"
final val Singleton: NameType = "Singleton"
final val Throwable: NameType = "Throwable"
+ final val unchecked: NameType = "unchecked"
final val api: NameType = "api"
final val Annotation: NameType = "Annotation"
@@ -326,6 +327,7 @@ trait StdNames {
val QUASIQUOTE_FILE: String = "<quasiquote>"
val QUASIQUOTE_TUPLE: NameType = "$quasiquote$tuple$"
val QUASIQUOTE_CASE: NameType = "$quasiquote$case$"
+ val QUASIQUOTE_FOR_ENUM: NameType = "$quasiquote$for$enum$"
val MIXIN_CONSTRUCTOR: NameType = "$init$"
val MODULE_INSTANCE_FIELD: NameType = NameTransformer.MODULE_INSTANCE_NAME // "MODULE$"
val OUTER: NameType = "$outer"
@@ -591,6 +593,9 @@ trait StdNames {
val SyntacticBlock: NameType = "SyntacticBlock"
val SyntacticClassDef: NameType = "SyntacticClassDef"
val SyntacticDefDef: NameType = "SyntacticDefDef"
+ val SyntacticFilter: NameType = "SyntacticFilter"
+ val SyntacticFor: NameType = "SyntacticFor"
+ val SyntacticForYield: NameType = "SyntacticForYield"
val SyntacticFunction: NameType = "SyntacticFunction"
val SyntacticFunctionType: NameType = "SyntacticFunctionType"
val SyntacticPackageObjectDef: NameType = "SyntacticPackageObjectDef"
@@ -601,6 +606,8 @@ trait StdNames {
val SyntacticTupleType: NameType = "SyntacticTupleType"
val SyntacticTypeApplied: NameType = "SyntacticTypeApplied"
val SyntacticValDef: NameType = "SyntacticValDef"
+ val SyntacticValEq: NameType = "SyntacticValEq"
+ val SyntacticValFrom: NameType = "SyntacticValFrom"
val SyntacticVarDef: NameType = "SyntacticVarDef"
val This: NameType = "This"
val ThisType: NameType = "ThisType"
@@ -744,7 +751,6 @@ 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"
@@ -763,6 +769,7 @@ trait StdNames {
val tq: NameType = "tq"
val cq: NameType = "cq"
val pq: NameType = "pq"
+ val fq: NameType = "fq"
// unencoded operators
object raw {
diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala
index cf7c729a6a..a0bd64f850 100644
--- a/src/reflect/scala/reflect/internal/TreeGen.scala
+++ b/src/reflect/scala/reflect/internal/TreeGen.scala
@@ -4,6 +4,7 @@ package internal
import Flags._
import util._
+import scala.collection.mutable.ListBuffer
abstract class TreeGen extends macros.TreeBuilder {
val global: SymbolTable
@@ -279,11 +280,23 @@ abstract class TreeGen extends macros.TreeBuilder {
def mkNamedArg(lhs: Tree, rhs: Tree): Tree = atPos(rhs.pos)(AssignOrNamedArg(lhs, rhs))
/** Builds a tuple */
- def mkTuple(elems: List[Tree]): Tree =
- if (elems.isEmpty) Literal(Constant(()))
- else Apply(
- Select(mkAttributedRef(TupleClass(elems.length).caseModule), nme.apply),
- elems)
+ def mkTuple(elems: List[Tree], flattenUnary: Boolean = true): Tree = elems match {
+ case Nil =>
+ Literal(Constant(()))
+ case tree :: Nil if flattenUnary =>
+ tree
+ case _ =>
+ Apply(scalaDot(TupleClass(elems.length).companionModule.name), elems)
+ }
+
+ def mkTupleType(elems: List[Tree], flattenUnary: Boolean = true): Tree = elems match {
+ case Nil =>
+ scalaDot(tpnme.Unit)
+ case List(tree) if flattenUnary =>
+ tree
+ case _ =>
+ AppliedTypeTree(scalaDot(TupleClass(elems.length).name), elems)
+ }
// tree1 AND tree2
def mkAnd(tree1: Tree, tree2: Tree): Tree =
@@ -299,7 +312,7 @@ abstract class TreeGen extends macros.TreeBuilder {
}
def mkSeqApply(arg: Tree): Apply = {
- val factory = Select(gen.mkAttributedRef(SeqModule), nme.apply)
+ val factory = Select(mkAttributedRef(SeqModule), nme.apply)
Apply(factory, List(arg))
}
@@ -436,17 +449,15 @@ abstract class TreeGen extends macros.TreeBuilder {
else Block(stats.init, stats.last)
def mkTreeOrBlock(stats: List[Tree]) = stats match {
- case Nil => EmptyTree
+ case Nil => EmptyTree
case head :: Nil => head
- case _ => gen.mkBlock(stats)
+ case _ => mkBlock(stats)
}
/** Create a tree representing an assignment <lhs = rhs> */
def mkAssign(lhs: Tree, rhs: Tree): Tree = lhs match {
- case Apply(fn, args) =>
- Apply(atPos(fn.pos)(Select(fn, nme.update)), args :+ rhs)
- case _ =>
- Assign(lhs, rhs)
+ case Apply(fn, args) => Apply(atPos(fn.pos)(Select(fn, nme.update)), args :+ rhs)
+ case _ => Assign(lhs, rhs)
}
def mkPackageObject(defn: ModuleDef, pidPos: Position = NoPosition, pkgPos: Position = NoPosition) = {
@@ -454,4 +465,419 @@ abstract class TreeGen extends macros.TreeBuilder {
val pid = atPos(pidPos)(Ident(defn.name))
atPos(pkgPos)(PackageDef(pid, module :: Nil))
}
+
+ // Following objects represent encoding of for loop enumerators
+ // into the regular trees. Such representations are used for:
+ //
+ // - as intermediate value of enumerators inside of the parser
+ // right before the mkFor desugaring is being called
+ //
+ // - as intermediate value of enumerators obtained after
+ // re-sugaring of for loops through build.SyntacticFor
+ // and build.SyntacticForYield (which are used by quasiquotes)
+ //
+ // The encoding uses regular trees with ForAttachment that helps
+ // to reliably differentiate them from normal trees that can have
+ // similar shape. fq"$pat <- $rhs" for example is represented in
+ // the same way as "`<-`($pat, $rhs)"" but with added attachment to
+ // the `<-` identifier.
+ //
+ // The primary rationale behind such representation in favor of
+ // simple case classes is a wish to re-use the same representation
+ // between quasiquotes and parser without exposing compiler internals.
+ // Opaque tree encoding can be changed/adapted at any time without
+ // breaking end users code.
+
+ /** Encode/decode fq"$pat <- $rhs" enumerator as q"`<-`($pat, $rhs)" */
+ object ValFrom {
+ def apply(pat: Tree, rhs: Tree): Tree =
+ Apply(Ident(nme.LARROWkw).updateAttachment(ForAttachment),
+ List(pat, rhs))
+
+ def unapply(tree: Tree): Option[(Tree, Tree)] = tree match {
+ case Apply(id @ Ident(nme.LARROWkw), List(pat, rhs))
+ if id.hasAttachment[ForAttachment.type] =>
+ Some((pat, rhs))
+ case _ => None
+ }
+ }
+
+ /** Encode/decode fq"$pat = $rhs" enumerator as q"$pat = $rhs" */
+ object ValEq {
+ def apply(pat: Tree, rhs: Tree): Tree =
+ Assign(pat, rhs).updateAttachment(ForAttachment)
+
+ def unapply(tree: Tree): Option[(Tree, Tree)] = tree match {
+ case Assign(pat, rhs)
+ if tree.hasAttachment[ForAttachment.type] =>
+ Some((pat, rhs))
+ case _ => None
+ }
+ }
+
+ /** Encode/decode fq"if $cond" enumerator as q"`if`($cond)" */
+ object Filter {
+ def apply(tree: Tree) =
+ Apply(Ident(nme.IFkw).updateAttachment(ForAttachment), List(tree))
+
+ def unapply(tree: Tree): Option[Tree] = tree match {
+ case Apply(id @ Ident(nme.IFkw), List(cond))
+ if id.hasAttachment[ForAttachment.type] =>
+ Some((cond))
+ case _ => None
+ }
+ }
+
+ /** Encode/decode body of for yield loop as q"`yield`($tree)" */
+ object Yield {
+ def apply(tree: Tree): Tree =
+ Apply(Ident(nme.YIELDkw).updateAttachment(ForAttachment), List(tree))
+
+ def unapply(tree: Tree): Option[Tree] = tree match {
+ case Apply(id @ Ident(nme.YIELDkw), List(tree))
+ if id.hasAttachment[ForAttachment.type] =>
+ Some(tree)
+ case _ => None
+ }
+ }
+
+ /** Create tree for for-comprehension <for (enums) do body> or
+ * <for (enums) yield body> where mapName and flatMapName are chosen
+ * corresponding to whether this is a for-do or a for-yield.
+ * The creation performs the following rewrite rules:
+ *
+ * 1.
+ *
+ * for (P <- G) E ==> G.foreach (P => E)
+ *
+ * Here and in the following (P => E) is interpreted as the function (P => E)
+ * if P is a variable pattern and as the partial function { case P => E } otherwise.
+ *
+ * 2.
+ *
+ * for (P <- G) yield E ==> G.map (P => E)
+ *
+ * 3.
+ *
+ * for (P_1 <- G_1; P_2 <- G_2; ...) ...
+ * ==>
+ * G_1.flatMap (P_1 => for (P_2 <- G_2; ...) ...)
+ *
+ * 4.
+ *
+ * for (P <- G; E; ...) ...
+ * =>
+ * for (P <- G.filter (P => E); ...) ...
+ *
+ * 5. For N < MaxTupleArity:
+ *
+ * for (P_1 <- G; P_2 = E_2; val P_N = E_N; ...)
+ * ==>
+ * for (TupleN(P_1, P_2, ... P_N) <-
+ * for (x_1 @ P_1 <- G) yield {
+ * val x_2 @ P_2 = E_2
+ * ...
+ * val x_N & P_N = E_N
+ * TupleN(x_1, ..., x_N)
+ * } ...)
+ *
+ * If any of the P_i are variable patterns, the corresponding `x_i @ P_i' is not generated
+ * and the variable constituting P_i is used instead of x_i
+ *
+ * @param mapName The name to be used for maps (either map or foreach)
+ * @param flatMapName The name to be used for flatMaps (either flatMap or foreach)
+ * @param enums The enumerators in the for expression
+ * @param body The body of the for expression
+ */
+ def mkFor(enums: List[Tree], sugarBody: Tree)(implicit fresh: FreshNameCreator): Tree = {
+ val (mapName, flatMapName, body) = sugarBody match {
+ case Yield(tree) => (nme.map, nme.flatMap, tree)
+ case _ => (nme.foreach, nme.foreach, sugarBody)
+ }
+
+ /* make a closure pat => body.
+ * The closure is assigned a transparent position with the point at pos.point and
+ * the limits given by pat and body.
+ */
+ def makeClosure(pos: Position, pat: Tree, body: Tree): Tree = {
+ def wrapped = wrappingPos(List(pat, body))
+ def splitpos = (if (pos != NoPosition) wrapped.withPoint(pos.point) else pos).makeTransparent
+ matchVarPattern(pat) match {
+ case Some((name, tpt)) =>
+ Function(
+ List(atPos(pat.pos) { ValDef(Modifiers(PARAM), name.toTermName, tpt, EmptyTree) }),
+ body) setPos splitpos
+ case None =>
+ atPos(splitpos) {
+ mkVisitor(List(CaseDef(pat, EmptyTree, body)), checkExhaustive = false)
+ }
+ }
+ }
+
+ /* Make an application qual.meth(pat => body) positioned at `pos`.
+ */
+ def makeCombination(pos: Position, meth: TermName, qual: Tree, pat: Tree, body: Tree): Tree =
+ // ForAttachment on the method selection is used to differentiate
+ // result of for desugaring from a regular method call
+ Apply(Select(qual, meth) setPos qual.pos updateAttachment ForAttachment,
+ List(makeClosure(pos, pat, body))) setPos pos
+
+ /* If `pat` is not yet a `Bind` wrap it in one with a fresh name */
+ def makeBind(pat: Tree): Tree = pat match {
+ case Bind(_, _) => pat
+ case _ => Bind(freshTermName(), pat) setPos pat.pos
+ }
+
+ /* A reference to the name bound in Bind `pat`. */
+ def makeValue(pat: Tree): Tree = pat match {
+ case Bind(name, _) => Ident(name) setPos pat.pos.focus
+ }
+
+ /* The position of the closure that starts with generator at position `genpos`. */
+ def closurePos(genpos: Position) =
+ if (genpos == NoPosition) NoPosition
+ else {
+ val end = body.pos match {
+ case NoPosition => genpos.point
+ case bodypos => bodypos.end
+ }
+ rangePos(genpos.source, genpos.start, genpos.point, end)
+ }
+
+ enums match {
+ case (t @ ValFrom(pat, rhs)) :: Nil =>
+ makeCombination(closurePos(t.pos), mapName, rhs, pat, body)
+ case (t @ ValFrom(pat, rhs)) :: (rest @ (ValFrom(_, _) :: _)) =>
+ makeCombination(closurePos(t.pos), flatMapName, rhs, pat,
+ mkFor(rest, sugarBody))
+ case (t @ ValFrom(pat, rhs)) :: Filter(test) :: rest =>
+ mkFor(ValFrom(pat, makeCombination(rhs.pos union test.pos, nme.withFilter, rhs, pat.duplicate, test)).setPos(t.pos) :: rest, sugarBody)
+ case (t @ ValFrom(pat, rhs)) :: rest =>
+ val valeqs = rest.take(definitions.MaxTupleArity - 1).takeWhile { ValEq.unapply(_).nonEmpty }
+ assert(!valeqs.isEmpty)
+ val rest1 = rest.drop(valeqs.length)
+ val pats = valeqs map { case ValEq(pat, _) => pat }
+ val rhss = valeqs map { case ValEq(_, rhs) => rhs }
+ val defpat1 = makeBind(pat)
+ val defpats = pats map makeBind
+ val pdefs = (defpats, rhss).zipped flatMap mkPatDef
+ val ids = (defpat1 :: defpats) map makeValue
+ val rhs1 = mkFor(
+ List(ValFrom(defpat1, rhs).setPos(t.pos)),
+ Yield(Block(pdefs, atPos(wrappingPos(ids)) { mkTuple(ids) }) setPos wrappingPos(pdefs)))
+ val allpats = (pat :: pats) map (_.duplicate)
+ val pos1 =
+ if (t.pos == NoPosition) NoPosition
+ else rangePos(t.pos.source, t.pos.start, t.pos.point, rhs1.pos.end)
+ val vfrom1 = ValFrom(atPos(wrappingPos(allpats)) { mkTuple(allpats) }, rhs1).setPos(pos1)
+ mkFor(vfrom1 :: rest1, sugarBody)
+ case _ =>
+ EmptyTree //may happen for erroneous input
+
+ }
+ }
+
+ /** Create tree for pattern definition <val pat0 = rhs> */
+ def mkPatDef(pat: Tree, rhs: Tree)(implicit fresh: FreshNameCreator): List[Tree] =
+ mkPatDef(Modifiers(0), pat, rhs)
+
+ /** Create tree for pattern definition <mods val pat0 = rhs> */
+ def mkPatDef(mods: Modifiers, pat: Tree, rhs: Tree)(implicit fresh: FreshNameCreator): List[Tree] = matchVarPattern(pat) match {
+ case Some((name, tpt)) =>
+ List(atPos(pat.pos union rhs.pos) {
+ ValDef(mods, name.toTermName, tpt, rhs)
+ })
+
+ case None =>
+ // in case there is exactly one variable x_1 in pattern
+ // val/var p = e ==> val/var x_1 = e.match (case p => (x_1))
+ //
+ // in case there are zero or more than one variables in pattern
+ // val/var p = e ==> private synthetic val t$ = e.match (case p => (x_1, ..., x_N))
+ // val/var x_1 = t$._1
+ // ...
+ // val/var x_N = t$._N
+
+ val rhsUnchecked = mkUnchecked(rhs)
+
+ // TODO: clean this up -- there is too much information packked into mkPatDef's `pat` argument
+ // when it's a simple identifier (case Some((name, tpt)) -- above),
+ // pat should have the type ascription that was specified by the user
+ // however, in `case None` (here), we must be careful not to generate illegal pattern trees (such as `(a, b): Tuple2[Int, String]`)
+ // i.e., this must hold: pat1 match { case Typed(expr, tp) => assert(expr.isInstanceOf[Ident]) case _ => }
+ // if we encounter such an erroneous pattern, we strip off the type ascription from pat and propagate the type information to rhs
+ val (pat1, rhs1) = patvarTransformer.transform(pat) match {
+ // move the Typed ascription to the rhs
+ case Typed(expr, tpt) if !expr.isInstanceOf[Ident] =>
+ val rhsTypedUnchecked =
+ if (tpt.isEmpty) rhsUnchecked
+ else Typed(rhsUnchecked, tpt) setPos (rhs.pos union tpt.pos)
+ (expr, rhsTypedUnchecked)
+ case ok =>
+ (ok, rhsUnchecked)
+ }
+ val vars = getVariables(pat1)
+ val matchExpr = atPos((pat1.pos union rhs.pos).makeTransparent) {
+ Match(
+ rhs1,
+ List(
+ atPos(pat1.pos) {
+ CaseDef(pat1, EmptyTree, mkTuple(vars map (_._1) map Ident.apply))
+ }
+ ))
+ }
+ vars match {
+ case List((vname, tpt, pos)) =>
+ List(atPos(pat.pos union pos union rhs.pos) {
+ ValDef(mods, vname.toTermName, tpt, matchExpr)
+ })
+ case _ =>
+ val tmp = freshTermName()
+ val firstDef =
+ atPos(matchExpr.pos) {
+ ValDef(Modifiers(PrivateLocal | SYNTHETIC | ARTIFACT | (mods.flags & LAZY)),
+ tmp, TypeTree(), matchExpr)
+ }
+ var cnt = 0
+ val restDefs = for ((vname, tpt, pos) <- vars) yield atPos(pos) {
+ cnt += 1
+ ValDef(mods, vname.toTermName, tpt, Select(Ident(tmp), newTermName("_" + cnt)))
+ }
+ firstDef :: restDefs
+ }
+ }
+
+ /** Create tree for for-comprehension generator <val pat0 <- rhs0> */
+ def mkGenerator(pos: Position, pat: Tree, valeq: Boolean, rhs: Tree)(implicit fresh: FreshNameCreator): Tree = {
+ val pat1 = patvarTransformer.transform(pat)
+ if (valeq) ValEq(pat1, rhs).setPos(pos)
+ else ValFrom(pat1, mkCheckIfRefutable(pat1, rhs)).setPos(pos)
+ }
+
+ def mkCheckIfRefutable(pat: Tree, rhs: Tree)(implicit fresh: FreshNameCreator) =
+ if (treeInfo.isVarPatternDeep(pat)) rhs
+ else {
+ val cases = List(
+ CaseDef(pat.duplicate, EmptyTree, Literal(Constant(true))),
+ CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false)))
+ )
+ val visitor = mkVisitor(cases, checkExhaustive = false, nme.CHECK_IF_REFUTABLE_STRING)
+ atPos(rhs.pos)(Apply(Select(rhs, nme.withFilter), visitor :: Nil))
+ }
+
+ /** If tree is a variable pattern, return Some("its name and type").
+ * Otherwise return none */
+ private def matchVarPattern(tree: Tree): Option[(Name, Tree)] = {
+ def wildType(t: Tree): Option[Tree] = t match {
+ case Ident(x) if x.toTermName == nme.WILDCARD => Some(TypeTree())
+ case Typed(Ident(x), tpt) if x.toTermName == nme.WILDCARD => Some(tpt)
+ case _ => None
+ }
+ tree match {
+ case Ident(name) => Some((name, TypeTree()))
+ case Bind(name, body) => wildType(body) map (x => (name, x))
+ case Typed(Ident(name), tpt) => Some((name, tpt))
+ case _ => None
+ }
+ }
+
+ /** Create visitor <x => x match cases> */
+ def mkVisitor(cases: List[CaseDef], checkExhaustive: Boolean, prefix: String = "x$")(implicit fresh: FreshNameCreator): Tree = {
+ val x = freshTermName(prefix)
+ val id = Ident(x)
+ val sel = if (checkExhaustive) id else mkUnchecked(id)
+ Function(List(mkSyntheticParam(x)), Match(sel, cases))
+ }
+
+ /** Traverse pattern and collect all variable names with their types in buffer
+ * The variables keep their positions; whereas the pattern is converted to be
+ * synthetic for all nodes that contain a variable position.
+ */
+ class GetVarTraverser extends Traverser {
+ val buf = new ListBuffer[(Name, Tree, Position)]
+
+ def namePos(tree: Tree, name: Name): Position =
+ if (!tree.pos.isRange || name.containsName(nme.raw.DOLLAR)) tree.pos.focus
+ else {
+ val start = tree.pos.start
+ val end = start + name.decode.length
+ rangePos(tree.pos.source, start, start, end)
+ }
+
+ override def traverse(tree: Tree): Unit = {
+ def seenName(name: Name) = buf exists (_._1 == name)
+ def add(name: Name, t: Tree) = if (!seenName(name)) buf += ((name, t, namePos(tree, name)))
+ val bl = buf.length
+
+ tree match {
+ case Bind(nme.WILDCARD, _) =>
+ super.traverse(tree)
+
+ case Bind(name, Typed(tree1, tpt)) =>
+ val newTree = if (treeInfo.mayBeTypePat(tpt)) TypeTree() else tpt.duplicate
+ add(name, newTree)
+ traverse(tree1)
+
+ case Bind(name, tree1) =>
+ // can assume only name range as position, as otherwise might overlap
+ // with binds embedded in pattern tree1
+ add(name, TypeTree())
+ traverse(tree1)
+
+ case _ =>
+ super.traverse(tree)
+ }
+ if (buf.length > bl)
+ tree setPos tree.pos.makeTransparent
+ }
+ def apply(tree: Tree) = {
+ traverse(tree)
+ buf.toList
+ }
+ }
+
+ /** Returns list of all pattern variables, possibly with their types,
+ * without duplicates
+ */
+ private def getVariables(tree: Tree): List[(Name, Tree, Position)] =
+ new GetVarTraverser apply tree
+
+ /** Convert all occurrences of (lower-case) variables in a pattern as follows:
+ * x becomes x @ _
+ * x: T becomes x @ (_: T)
+ */
+ object patvarTransformer extends Transformer {
+ override def transform(tree: Tree): Tree = tree match {
+ case Ident(name) if (treeInfo.isVarPattern(tree) && name != nme.WILDCARD) =>
+ atPos(tree.pos)(Bind(name, atPos(tree.pos.focus) (Ident(nme.WILDCARD))))
+ case Typed(id @ Ident(name), tpt) if (treeInfo.isVarPattern(id) && name != nme.WILDCARD) =>
+ atPos(tree.pos.withPoint(id.pos.point)) {
+ Bind(name, atPos(tree.pos.withStart(tree.pos.point)) {
+ Typed(Ident(nme.WILDCARD), tpt)
+ })
+ }
+ case Apply(fn @ Apply(_, _), args) =>
+ treeCopy.Apply(tree, transform(fn), transformTrees(args))
+ case Apply(fn, args) =>
+ treeCopy.Apply(tree, fn, transformTrees(args))
+ case Typed(expr, tpt) =>
+ treeCopy.Typed(tree, transform(expr), tpt)
+ case Bind(name, body) =>
+ treeCopy.Bind(tree, name, transform(body))
+ case Alternative(_) | Star(_) =>
+ super.transform(tree)
+ case _ =>
+ tree
+ }
+ }
+
+ // annotate the expression with @unchecked
+ def mkUnchecked(expr: Tree): Tree = atPos(expr.pos) {
+ // This can't be "Annotated(New(UncheckedClass), expr)" because annotations
+ // are very picky about things and it crashes the compiler with "unexpected new".
+ Annotated(New(scalaDot(tpnme.unchecked), Nil), expr)
+ }
+
+ def mkSyntheticParam(pname: TermName) =
+ ValDef(Modifiers(PARAM | SYNTHETIC), pname, TypeTree(), EmptyTree)
}
diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala
index 743c674eea..af0af8afe8 100644
--- a/src/reflect/scala/reflect/internal/Trees.scala
+++ b/src/reflect/scala/reflect/internal/Trees.scala
@@ -490,7 +490,7 @@ trait Trees extends api.Trees {
case class Ident(name: Name) extends RefTree with IdentContextApi {
def qualifier: Tree = EmptyTree
- def isBackquoted = this.attachments.get[BackquotedIdentifierAttachment.type].isDefined
+ def isBackquoted = this.hasAttachment[BackquotedIdentifierAttachment.type]
}
object Ident extends IdentExtractor
diff --git a/src/reflect/scala/reflect/macros/Attachments.scala b/src/reflect/scala/reflect/macros/Attachments.scala
index c1ab269268..039e75fbee 100644
--- a/src/reflect/scala/reflect/macros/Attachments.scala
+++ b/src/reflect/scala/reflect/macros/Attachments.scala
@@ -41,6 +41,10 @@ abstract class Attachments { self =>
def get[T: ClassTag]: Option[T] =
(all filter matchesTag[T]).headOption.asInstanceOf[Option[T]]
+ /** Check underlying payload contains an instance of type `T`. */
+ def contains[T: ClassTag]: Boolean =
+ all exists matchesTag[T]
+
/** Creates a copy of this attachment with the payload slot of T added/updated with the provided value.
* Replaces an existing payload of the same type, if exists.
*/
diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
index 4d69a6673c..711456f6c7 100644
--- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
+++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
@@ -53,8 +53,9 @@ trait JavaUniverseForce { self: runtime.JavaUniverse =>
this.perRunCaches
this.FixedMirrorTreeCreator
this.FixedMirrorTypeCreator
- this.BackquotedIdentifierAttachment
this.CompoundTypeTreeOriginalAttachment
+ this.BackquotedIdentifierAttachment
+ this.ForAttachment
this.noPrint
this.typeDebug
// inaccessible: this.maxFree
diff --git a/test/files/scalacheck/quasiquotes/ArbitraryTreesAndNames.scala b/test/files/scalacheck/quasiquotes/ArbitraryTreesAndNames.scala
index 4118d92076..c4b93dae48 100644
--- a/test/files/scalacheck/quasiquotes/ArbitraryTreesAndNames.scala
+++ b/test/files/scalacheck/quasiquotes/ArbitraryTreesAndNames.scala
@@ -1,11 +1,6 @@
-import org.scalacheck._
-import Prop._
-import Gen._
-import Arbitrary._
-
-import scala.reflect.api._
-import scala.reflect.runtime.universe._
-import scala.reflect.runtime.universe.Flag._
+import org.scalacheck._, Prop._, Gen._, Arbitrary._
+import scala.reflect.api.{Liftable, Universe}
+import scala.reflect.runtime.universe._, Flag._
trait ArbitraryTreesAndNames {
def smallList[T](size: Int, g: Gen[T]) = {
diff --git a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala
index e8ddb4b72a..2ec679e78b 100644
--- a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala
+++ b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala
@@ -1,11 +1,5 @@
-import org.scalacheck._
-import Prop._
-import Gen._
-import Arbitrary._
-
-import scala.reflect.runtime.universe._
-import scala.reflect.runtime.universe.build.ScalaDot
-import Flag._
+import org.scalacheck._, Prop._, Gen._, Arbitrary._
+import scala.reflect.runtime.universe._, Flag._, build.ScalaDot
object DefinitionConstructionProps
extends QuasiquoteProperties("definition construction")
diff --git a/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala
index 993ef899b0..dbd26bf72a 100644
--- a/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala
+++ b/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala
@@ -1,10 +1,5 @@
-import org.scalacheck._
-import Prop._
-import Gen._
-import Arbitrary._
-
-import scala.reflect.runtime.universe._
-import Flag._
+import org.scalacheck._, Prop._, Gen._, Arbitrary._
+import scala.reflect.runtime.universe._, Flag._
object DefinitionDeconstructionProps
extends QuasiquoteProperties("definition deconstruction")
diff --git a/test/files/scalacheck/quasiquotes/ErrorProps.scala b/test/files/scalacheck/quasiquotes/ErrorProps.scala
index b0a7641577..cb46a60dbe 100644
--- a/test/files/scalacheck/quasiquotes/ErrorProps.scala
+++ b/test/files/scalacheck/quasiquotes/ErrorProps.scala
@@ -1,10 +1,4 @@
-import org.scalacheck._
-import Prop._
-import Gen._
-import Arbitrary._
-
-import scala.reflect.runtime.universe._
-import Flag._
+import org.scalacheck._, Prop._, Gen._, Arbitrary._
object ErrorProps extends QuasiquoteProperties("errors") {
property("can't extract two .. cardinalities in a row") = fails(
diff --git a/test/files/scalacheck/quasiquotes/ForProps.scala b/test/files/scalacheck/quasiquotes/ForProps.scala
new file mode 100644
index 0000000000..e71822aaea
--- /dev/null
+++ b/test/files/scalacheck/quasiquotes/ForProps.scala
@@ -0,0 +1,70 @@
+import org.scalacheck._, Prop._, Gen._, Arbitrary._
+import scala.reflect.runtime.universe._, Flag._, build.{Ident => _, _}
+
+object ForProps extends QuasiquoteProperties("for") {
+ case class ForEnums(val value: List[Tree])
+
+ def genSimpleBind: Gen[Bind] =
+ for(name <- genTermName)
+ yield pq"$name @ _"
+
+ def genForFilter: Gen[Tree] =
+ for(cond <- genIdent(genTermName))
+ yield fq"if $cond"
+
+ def genForFrom: Gen[Tree] =
+ for(lhs <- genSimpleBind; rhs <- genIdent(genTermName))
+ yield fq"$lhs <- $rhs"
+
+ def genForEq: Gen[Tree] =
+ for(lhs <- genSimpleBind; rhs <- genIdent(genTermName))
+ yield fq"$lhs = $rhs"
+
+ def genForEnums(size: Int): Gen[ForEnums] =
+ for(first <- genForFrom; rest <- listOfN(size, oneOf(genForFrom, genForFilter, genForEq)))
+ yield new ForEnums(first :: rest)
+
+ implicit val arbForEnums: Arbitrary[ForEnums] = arbitrarySized(genForEnums)
+
+ property("construct-reconstruct for") = forAll { (enums: ForEnums, body: Tree) =>
+ val SyntacticFor(recoveredEnums, recoveredBody) = SyntacticFor(enums.value, body)
+ recoveredEnums ≈ enums.value && recoveredBody ≈ body
+ }
+
+ property("construct-reconstruct for-yield") = forAll { (enums: ForEnums, body: Tree) =>
+ val SyntacticForYield(recoveredEnums, recoveredBody) = SyntacticForYield(enums.value, body)
+ recoveredEnums ≈ enums.value && recoveredBody ≈ body
+ }
+
+ val abcde = List(fq"a <-b", fq"if c", fq"d = e")
+ val foobarbaz = pq"foo @ Bar(baz)"
+ val fv = q"f(v)"
+
+ property("construct/deconstruct for loop with fq") = test {
+ val for0 = q"for(..$abcde) $fv"
+ assertEqAst(for0, "for(a <- b; if c; d = e) f(v)")
+ val q"for(..$enums) $body" = for0
+ assert(enums ≈ abcde)
+ assert(body ≈ fv)
+ }
+
+ property("construct/deconstruct valfrom with fq") = test {
+ assert(fq"$foobarbaz <- $fv" ≈ fq"foo @ Bar(baz) <- f(v)")
+ val fq"$lhs <- $rhs" = fq"$foobarbaz <- $fv"
+ assert(lhs ≈ foobarbaz)
+ assert(rhs ≈ fv)
+ }
+
+ property("construct/deconstruct valeq with fq") = test {
+ assert(fq"$foobarbaz = $fv" ≈ fq"foo @ Bar(baz) = f(v)")
+ val fq"$lhs = $rhs" = fq"$foobarbaz = $fv"
+ assert(lhs ≈ foobarbaz)
+ assert(rhs ≈ fv)
+ }
+
+ property("construct/deconstruct filter with fq") = test {
+ assert(fq"if $fv" ≈ fq"if f(v)")
+ val fq"if $cond" = fq"if $fv"
+ assert(cond ≈ fv)
+ }
+} \ No newline at end of file
diff --git a/test/files/scalacheck/quasiquotes/LiftableProps.scala b/test/files/scalacheck/quasiquotes/LiftableProps.scala
index 510ab99068..1271e1accd 100644
--- a/test/files/scalacheck/quasiquotes/LiftableProps.scala
+++ b/test/files/scalacheck/quasiquotes/LiftableProps.scala
@@ -1,10 +1,5 @@
-import org.scalacheck._
-import Prop._
-import Gen._
-import Arbitrary._
-
-import scala.reflect.runtime.universe._
-import Flag._
+import org.scalacheck._, Prop._, Gen._, Arbitrary._
+import scala.reflect.runtime.universe._, Flag._
object LiftableProps extends QuasiquoteProperties("liftable") {
property("splice byte") = test {
diff --git a/test/files/scalacheck/quasiquotes/PatternConstructionProps.scala b/test/files/scalacheck/quasiquotes/PatternConstructionProps.scala
index 504cb2a77d..582e915258 100644
--- a/test/files/scalacheck/quasiquotes/PatternConstructionProps.scala
+++ b/test/files/scalacheck/quasiquotes/PatternConstructionProps.scala
@@ -1,10 +1,5 @@
-import org.scalacheck._
-import Prop._
-import Gen._
-import Arbitrary._
-
-import scala.reflect.runtime.universe._
-import Flag._
+import org.scalacheck._, Prop._, Gen._, Arbitrary._
+import scala.reflect.runtime.universe._, Flag._
object PatternConstructionProps extends QuasiquoteProperties("pattern construction") {
property("splice bind") = forAll { (bind: Bind) =>
diff --git a/test/files/scalacheck/quasiquotes/PatternDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/PatternDeconstructionProps.scala
index f73fd29b22..cccf8095db 100644
--- a/test/files/scalacheck/quasiquotes/PatternDeconstructionProps.scala
+++ b/test/files/scalacheck/quasiquotes/PatternDeconstructionProps.scala
@@ -1,11 +1,5 @@
-import org.scalacheck._
-import Prop._
-import Gen._
-import Arbitrary._
-
-import scala.reflect.runtime.universe._
-import Flag._
-import definitions._
+import org.scalacheck._, Prop._, Gen._, Arbitrary._
+import scala.reflect.runtime.universe._, Flag._
object PatternDeconstructionProps extends QuasiquoteProperties("pattern deconstruction") {
property("extract bind") = forAll { (bind: Bind) =>
diff --git a/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala b/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala
index 6a531071bf..b2bce124ee 100644
--- a/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala
+++ b/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala
@@ -1,15 +1,7 @@
-import scala.reflect.runtime.universe._
-import scala.reflect.runtime.universe.definitions._
-import scala.reflect.runtime.universe.Flag._
-import scala.reflect.runtime.currentMirror
-import scala.reflect.api.{Liftable, Universe}
-import scala.reflect.macros.TypecheckException
+import org.scalacheck._, Prop._, Gen._, Arbitrary._
import scala.tools.reflect.{ToolBox, ToolBoxError}
-
-import org.scalacheck._
-import Prop._
-import Gen._
-import Arbitrary._
+import scala.reflect.runtime.currentMirror
+import scala.reflect.runtime.universe._, Flag._
class QuasiquoteProperties(name: String) extends Properties(name) with ArbitraryTreesAndNames with Helpers
@@ -18,14 +10,36 @@ trait Helpers {
* 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) =>
+ def test[T](block: => T) =
+ Prop { params =>
block
Result(Prop.Proof)
}
+ object simplify extends Transformer {
+ object SimplifiedName {
+ def unapply[T <: Name](name: T): Option[T] =
+ name.toString.split("\\$").toSeq match {
+ case first :+ last if scala.util.Try(last.toInt).isSuccess && first.nonEmpty =>
+ val value = first.mkString("", "$", "$")
+ Some((if (name.isTermName) TermName(value) else TypeName(value)).asInstanceOf[T])
+ case _ => None
+ }
+ }
+
+ override def transform(tree: Tree): Tree = tree match {
+ case Ident(SimplifiedName(name)) => Ident(name)
+ case ValDef(mods, SimplifiedName(name), tpt, rhs) => ValDef(mods, name, tpt, rhs)
+ case Bind(SimplifiedName(name), rhs) => Bind(name, rhs)
+ case _ =>
+ super.transform(tree)
+ }
+
+ def apply(tree: Tree): Tree = transform(tree)
+ }
+
implicit class TestSimilarTree(tree1: Tree) {
- def ≈(tree2: Tree) = tree1.equalsStructure(tree2)
+ def ≈(tree2: Tree) = simplify(tree1).equalsStructure(simplify(tree2))
}
implicit class TestSimilarListTree(lst: List[Tree]) {
@@ -68,6 +82,18 @@ trait Helpers {
val compile = toolbox.compile(_)
val eval = toolbox.eval(_)
+ def typecheck(tree: Tree) = toolbox.typeCheck(tree)
+
+ def typecheckTyp(tree: Tree) = {
+ val q"type $_ = $res" = typecheck(q"type T = $tree")
+ res
+ }
+
+ def typecheckPat(tree: Tree) = {
+ val q"$_ match { case $res => }" = typecheck(q"((): Any) match { case $tree => }")
+ res
+ }
+
def fails(msg: String, block: String) = {
def result(ok: Boolean, description: String = "") = {
val status = if (ok) Prop.Proof else Prop.False
diff --git a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala
index f68656d0f7..cdd96205de 100644
--- a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala
+++ b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala
@@ -1,10 +1,5 @@
-import org.scalacheck._
-import Prop._
-import Gen._
-import Arbitrary._
-
-import scala.reflect.runtime.universe._
-import Flag._
+import org.scalacheck._, Prop._, Gen._, Arbitrary._
+import scala.reflect.runtime.universe._, Flag._
object TermConstructionProps extends QuasiquoteProperties("term construction") {
property("splice single tree return tree itself") = forAll { (t: Tree) =>
@@ -148,8 +143,8 @@ object TermConstructionProps extends QuasiquoteProperties("term construction") {
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)")
+ assert(q"(..$as)" ≈ q"scala.Tuple2($a1, $a2)")
+ assert(q"(a0, ..$as)" ≈ q"scala.Tuple3(a0, $a1, $a2)")
}
property("splice empty list into tuple") = test {
@@ -193,11 +188,11 @@ object TermConstructionProps extends QuasiquoteProperties("term construction") {
property("fresh names are regenerated at each evaluation") = test {
def plusOne = q"{ _ + 1 }"
- assert(!(plusOne ≈ plusOne))
+ assert(!plusOne.equalsStructure(plusOne))
def whileTrue = q"while(true) false"
- assert(!(whileTrue ≈ whileTrue))
+ assert(!whileTrue.equalsStructure(whileTrue))
def withEvidence = q"def foo[T: X]"
- assert(!(withEvidence ≈ withEvidence))
+ assert(!withEvidence.equalsStructure(withEvidence))
}
property("make sure inference doesn't infer any") = test {
diff --git a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala
index f37e4d9975..bd81afa125 100644
--- a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala
+++ b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala
@@ -1,10 +1,5 @@
-import org.scalacheck._
-import Prop._
-import Gen._
-import Arbitrary._
-
-import scala.reflect.runtime.universe._
-import Flag._
+import org.scalacheck._, Prop._, Gen._, Arbitrary._
+import scala.reflect.runtime.universe._, Flag._
object TermDeconstructionProps extends QuasiquoteProperties("term deconstruction") {
property("f(..x) = f") = test {
diff --git a/test/files/scalacheck/quasiquotes/Test.scala b/test/files/scalacheck/quasiquotes/Test.scala
index f41d961888..8b1e779ab2 100644
--- a/test/files/scalacheck/quasiquotes/Test.scala
+++ b/test/files/scalacheck/quasiquotes/Test.scala
@@ -12,4 +12,6 @@ object Test extends Properties("quasiquotes") {
include(DefinitionConstructionProps)
include(DefinitionDeconstructionProps)
include(DeprecationProps)
+ include(ForProps)
+ include(TypecheckedProps)
}
diff --git a/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala b/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala
index cac83ff8ac..be7a96d91e 100644
--- a/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala
+++ b/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala
@@ -1,10 +1,5 @@
-import org.scalacheck._
-import Prop._
-import Gen._
-import Arbitrary._
-
-import scala.reflect.runtime.universe._
-import Flag._
+import org.scalacheck._, Prop._, Gen._, Arbitrary._
+import scala.reflect.runtime.universe._, Flag._
object TypeConstructionProps extends QuasiquoteProperties("type construction") {
property("bare idents contain type names") = test {
@@ -18,9 +13,9 @@ object TypeConstructionProps extends QuasiquoteProperties("type construction")
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]")
+ assert(tq"(..$empty)" ≈ build.ScalaDot(TypeName("Unit")))
+ assert(tq"(..$ts)" ≈ tq"scala.Tuple2[t1, t2]")
+ assert(tq"(t0, ..$ts)" ≈ tq"scala.Tuple3[t0, t1, t2]")
}
property("refined type") = test {
diff --git a/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala
index e1d5f4df96..499f5d6d8e 100644
--- a/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala
+++ b/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala
@@ -1,10 +1,5 @@
-import org.scalacheck._
-import Prop._
-import Gen._
-import Arbitrary._
-
-import scala.reflect.runtime.universe._
-import Flag._
+import org.scalacheck._, Prop._, Gen._, Arbitrary._
+import scala.reflect.runtime.universe._, Flag._
object TypeDeconstructionProps extends QuasiquoteProperties("type deconstruction") {
property("ident(type name)") = forAll { (name: TypeName) =>
diff --git a/test/files/scalacheck/quasiquotes/TypecheckedProps.scala b/test/files/scalacheck/quasiquotes/TypecheckedProps.scala
new file mode 100644
index 0000000000..f443330e0b
--- /dev/null
+++ b/test/files/scalacheck/quasiquotes/TypecheckedProps.scala
@@ -0,0 +1,53 @@
+import org.scalacheck._, Prop._, Gen._, Arbitrary._
+import scala.reflect.runtime.universe._, Flag._, build.{Ident => _, _}
+
+object TypecheckedProps extends QuasiquoteProperties("typechecked") {
+ def original(tree: Tree) = tree match {
+ case tt: TypeTree => Some(tt.original)
+ case _ => None
+ }
+ def originals(trees: List[Tree]) = trees.flatMap(original)
+ val int = ScalaDot(TypeName("Int"))
+ val intint = List(int, int)
+
+ property("tuple term") = test {
+ val q"(..$elements)" = typecheck(q"(1, 2)")
+ assert(elements ≈ List(q"1", q"2"))
+ }
+
+ property("tuple type") = test {
+ val tq"(..$els0)" = typecheckTyp(tq"Unit")
+ assert(els0.isEmpty)
+ val tq"(..$els1)" = typecheckTyp(tq"(Int, Int)")
+ assert(originals(els1) ≈ intint)
+ }
+
+ property("function type") = test {
+ val tq"(..$argtpes) => $restpe" = typecheckTyp(tq"(Int, Int) => Int")
+ assert(originals(argtpes) ≈ intint)
+ assert(original(restpe).get ≈ int)
+ }
+
+ property("for/for-yield") = test {
+ val enums = fq"x <- xs" :: fq"x1 = x + 1" :: fq"if x1 % 2 == 0" :: Nil
+ val body = q"x1"
+ val xs = q"val xs = List(1, 2, 3)"
+ val q"$_; for(..$enums0) yield $body0" = typecheck(q"$xs; for(..$enums) yield $body")
+ assert(enums0 ≈ enums)
+ assert(body0 ≈ body)
+ val q"$_; for(..$enums1) $body1" = typecheck(q"$xs; for(..$enums) $body")
+ assert(enums1 ≈ enums)
+ assert(body1 ≈ body)
+ }
+
+ property("for .filter instead of .withFilter") = test {
+ val enums = fq"foo <- new Foo" :: fq"if foo != null" :: Nil
+ val body = q"foo"
+ val q"$_; for(..$enums1) yield $body1" = typecheck(q"""
+ class Foo { def map(f: Any => Any) = this; def filter(cond: Any => Boolean) = this }
+ for(..$enums) yield $body
+ """)
+ assert(enums1 ≈ enums)
+ assert(body1 ≈ body)
+ }
+} \ No newline at end of file