summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala7
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala25
-rw-r--r--src/reflect/scala/reflect/api/BuildUtils.scala6
-rw-r--r--src/reflect/scala/reflect/internal/BuildUtils.scala84
-rw-r--r--src/reflect/scala/reflect/internal/StdNames.scala2
-rw-r--r--src/reflect/scala/reflect/internal/TreeGen.scala4
-rw-r--r--test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala68
-rw-r--r--test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala2
8 files changed, 145 insertions, 53 deletions
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
index 2410af49e5..36452c2a6d 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
@@ -14,6 +14,7 @@ import scala.util.Try
*/
trait Parsers { self: Quasiquotes =>
import global.{Try => _, _}
+ import build.implodePatDefs
abstract class Parser extends {
val global: self.global.type = self.global
@@ -182,7 +183,7 @@ trait Parsers { self: Quasiquotes =>
}
object TermParser extends Parser {
- def entryPoint = parser => Q(gen.mkTreeOrBlock(parser.templateOrTopStatSeq()))
+ def entryPoint = parser => Q(implodePatDefs(gen.mkTreeOrBlock(parser.templateOrTopStatSeq())))
}
object TypeParser extends Parser {
@@ -195,7 +196,7 @@ trait Parsers { self: Quasiquotes =>
}
object CaseParser extends Parser {
- def entryPoint = _.caseClause()
+ def entryPoint = parser => implodePatDefs(parser.caseClause())
}
object PatternParser extends Parser {
@@ -209,7 +210,7 @@ trait Parsers { self: Quasiquotes =>
def entryPoint = { parser =>
val enums = parser.enumerator(isFirst = false, allowNestedIf = false)
assert(enums.length == 1)
- enums.head
+ implodePatDefs(enums.head)
}
}
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
index 017e966f63..70580adbce 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
@@ -194,8 +194,8 @@ trait Reifiers { self: Quasiquotes =>
reifyBuildCall(nme.SyntacticEmptyTypeTree)
case SyntacticImport(expr, selectors) =>
reifyBuildCall(nme.SyntacticImport, expr, selectors)
- case Q(Placeholder(Hole(tree, DotDot))) =>
- mirrorBuildCall(nme.SyntacticBlock, tree)
+ case Q(tree) if fillListHole.isDefinedAt(tree) =>
+ mirrorBuildCall(nme.SyntacticBlock, fillListHole(tree))
case Q(other) =>
reifyTree(other)
// Syntactic block always matches so we have to be careful
@@ -311,11 +311,7 @@ trait Reifiers { self: Quasiquotes =>
*/
def reifyMultiCardinalityList(xs: List[Any])(fill: PartialFunction[Any, Tree])(fallback: Any => 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) {
+ val fillListHole: PartialFunction[Any, Tree] = {
case Placeholder(Hole(tree, DotDot)) => tree
case CasePlaceholder(Hole(tree, DotDot)) => tree
case RefineStatPlaceholder(h @ Hole(_, DotDot)) => reifyRefineStat(h)
@@ -323,12 +319,23 @@ trait Reifiers { self: Quasiquotes =>
case PackageStatPlaceholder(h @ Hole(_, DotDot)) => reifyPackageStat(h)
case ForEnumPlaceholder(Hole(tree, DotDot)) => tree
case ParamPlaceholder(Hole(tree, DotDot)) => tree
+ case SyntacticPatDef(mods, pat, tpt, rhs) =>
+ reifyBuildCall(nme.SyntacticPatDef, mods, pat, tpt, rhs)
+ case SyntacticValDef(mods, p @ Placeholder(h: ApplyHole), tpt, rhs) if h.tpe <:< treeType =>
+ mirrorBuildCall(nme.SyntacticPatDef, reify(mods), h.tree, reify(tpt), reify(rhs))
+ }
+
+ val fillListOfListsHole: PartialFunction[Any, Tree] = {
case List(ParamPlaceholder(Hole(tree, DotDotDot))) => tree
case List(Placeholder(Hole(tree, DotDotDot))) => tree
- } {
- reify(_)
}
+ /** 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)(fillListHole.orElse(fillListOfListsHole))(reify)
+
def reifyAnnotList(annots: List[Tree]): Tree = reifyMultiCardinalityList(annots) {
case AnnotPlaceholder(h @ Hole(_, DotDot)) => reifyAnnotation(h)
} {
diff --git a/src/reflect/scala/reflect/api/BuildUtils.scala b/src/reflect/scala/reflect/api/BuildUtils.scala
index ec20a89a10..57dc7da6cc 100644
--- a/src/reflect/scala/reflect/api/BuildUtils.scala
+++ b/src/reflect/scala/reflect/api/BuildUtils.scala
@@ -220,6 +220,12 @@ private[reflect] trait BuildUtils { self: Universe =>
def unapply(tree: Tree): Option[(Modifiers, TermName, Tree, Tree)]
}
+ val SyntacticPatDef: SyntacticPatDefExtractor
+
+ trait SyntacticPatDefExtractor {
+ def apply(mods: Modifiers, pat: Tree, tpt: Tree, rhs: Tree): List[ValDef]
+ }
+
val SyntacticAssign: SyntacticAssignExtractor
trait SyntacticAssignExtractor {
diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala
index c5581601de..6f1e4d2e32 100644
--- a/src/reflect/scala/reflect/internal/BuildUtils.scala
+++ b/src/reflect/scala/reflect/internal/BuildUtils.scala
@@ -193,9 +193,9 @@ trait BuildUtils { self: SymbolTable =>
// 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, _)) =>
+ case DefDef(mods, nme.MIXIN_CONSTRUCTOR, _, _, _, SyntacticBlock(lvdefs :+ _)) =>
Some((mods | Flag.TRAIT, Nil, lvdefs))
- case DefDef(mods, nme.CONSTRUCTOR, Nil, vparamss, _, Block(lvdefs :+ _, _)) =>
+ case DefDef(mods, nme.CONSTRUCTOR, Nil, vparamss, _, SyntacticBlock(lvdefs :+ _ :+ _)) =>
Some((mods, vparamss, lvdefs))
case _ => None
}
@@ -474,10 +474,9 @@ trait BuildUtils { self: SymbolTable =>
}
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)
- }
+ def modifiers(mods: Modifiers): Modifiers = if (isMutable) mods | MUTABLE else mods
+
+ def apply(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree): ValDef = ValDef(modifiers(mods), name, tpt, rhs)
def unapply(tree: Tree): Option[(Modifiers, TermName, Tree, Tree)] = tree match {
case ValDef(mods, name, tpt, rhs) if mods.hasFlag(MUTABLE) == isMutable =>
@@ -547,25 +546,57 @@ trait BuildUtils { self: SymbolTable =>
// 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
+ def unapply(trees: List[Tree]): Option[List[(Tree, Tree)]] = {
+ val imploded = implodePatDefs(trees)
+ val patvalues = imploded.flatMap {
+ case SyntacticPatDef(_, pat, EmptyTree, rhs) => Some((pat, rhs))
+ case ValDef(_, name, SyntacticEmptyTypeTree(), rhs) => Some((Bind(name, self.Ident(nme.WILDCARD)), rhs))
+ case ValDef(_, name, tpt, rhs) => Some((Bind(name, Typed(self.Ident(nme.WILDCARD), tpt)), rhs))
+ case _ => None
+ }
+ if (patvalues.length == imploded.length) Some(patvalues) else None
+ }
+ }
+
+ // implode multiple-statement desugaring of pattern definitions
+ // into single-statement valdefs with nme.QUASIQUOTE_PAT_DEF name
+ object implodePatDefs extends Transformer {
+ override def transform(tree: Tree) = tree match {
+ case templ: Template => deriveTemplate(templ)(transformStats)
+ case block: Block =>
+ val Block(init, last) = block
+ Block(transformStats(init), transform(last)).copyAttrs(block)
+ case ValDef(mods, name1, SyntacticEmptyTypeTree(), Match(MaybeTyped(MaybeUnchecked(value), tpt), CaseDef(pat, EmptyTree, Ident(name2)) :: Nil))
+ if name1 == name2 =>
+ ValDef(mods, nme.QUASIQUOTE_PAT_DEF, Typed(pat, tpt), transform(value))
+ case _ =>
+ super.transform(tree)
+ }
+ def transformStats(trees: List[Tree]): List[Tree] = trees match {
+ case Nil => Nil
+ case ValDef(mods, _, SyntacticEmptyTypeTree(), Match(MaybeTyped(MaybeUnchecked(value), tpt), 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
+ ids match {
+ case Nil =>
+ ValDef(NoMods, nme.QUASIQUOTE_PAT_DEF, Typed(pat, tpt), transform(value)) :: transformStats(tail)
+ case _ =>
+ val mods = tail.take(1).head.asInstanceOf[ValDef].mods
+ ValDef(mods, nme.QUASIQUOTE_PAT_DEF, Typed(pat, tpt), transform(value)) :: transformStats(tail.drop(ids.length))
}
- // 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: ${SyntacticEmptyTypeTree()} = $value" :: UnPatSeq(rest) =>
- case ValDef(_, name, SyntacticEmptyTypeTree(), 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 other :: tail =>
+ transform(other) :: transformStats(tail)
+ }
+ def apply(tree: Tree) = transform(tree)
+ def apply(trees: List[Tree]) = transformStats(trees)
+ }
+
+ object SyntacticPatDef extends SyntacticPatDefExtractor {
+ def apply(mods: Modifiers, pat: Tree, tpt: Tree, rhs: Tree): List[ValDef] = tpt match {
+ case SyntacticEmptyTypeTree() => gen.mkPatDef(mods, pat, rhs)
+ case _ => gen.mkPatDef(mods, Typed(pat, tpt), rhs)
+ }
+ def unapply(tree: Tree): Option[(Modifiers, Tree, Tree, Tree)] = tree match {
+ case ValDef(mods, nme.QUASIQUOTE_PAT_DEF, Typed(pat, tpt), rhs) => Some((mods, pat, tpt, rhs))
case _ => None
}
}
@@ -746,6 +777,13 @@ trait BuildUtils { self: SymbolTable =>
}
}
+ protected object MaybeTyped {
+ def unapply(tree: Tree): Some[(Tree, Tree)] = tree match {
+ case Typed(v, tpt) => Some((v, tpt))
+ case v => Some((v, SyntacticEmptyTypeTree()))
+ }
+ }
+
protected def mkCases(cases: List[Tree]): List[CaseDef] = cases.map {
case c: CaseDef => c
case tree => throw new IllegalArgumentException("$tree is not valid representation of pattern match case")
diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala
index 0ac067a399..de582ee5f9 100644
--- a/src/reflect/scala/reflect/internal/StdNames.scala
+++ b/src/reflect/scala/reflect/internal/StdNames.scala
@@ -334,6 +334,7 @@ trait StdNames {
val QUASIQUOTE_NAME_PREFIX: String = "nn$"
val QUASIQUOTE_PACKAGE_STAT: NameType = "$quasiquote$package$stat$"
val QUASIQUOTE_PARAM: NameType = "$quasiquote$param$"
+ val QUASIQUOTE_PAT_DEF: NameType = "$quasiquote$pat$def$"
val QUASIQUOTE_PREFIX: String = "qq$"
val QUASIQUOTE_REFINE_STAT: NameType = "$quasiquote$refine$stat$"
val QUASIQUOTE_TUPLE: NameType = "$quasiquote$tuple$"
@@ -615,6 +616,7 @@ trait StdNames {
val SyntacticNew: NameType = "SyntacticNew"
val SyntacticObjectDef: NameType = "SyntacticObjectDef"
val SyntacticPackageObjectDef: NameType = "SyntacticPackageObjectDef"
+ val SyntacticPatDef: NameType = "SyntacticPatDef"
val SyntacticTraitDef: NameType = "SyntacticTraitDef"
val SyntacticTry: NameType = "SyntacticTry"
val SyntacticTuple: NameType = "SyntacticTuple"
diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala
index 29fdba2781..e7675eb4bf 100644
--- a/src/reflect/scala/reflect/internal/TreeGen.scala
+++ b/src/reflect/scala/reflect/internal/TreeGen.scala
@@ -691,11 +691,11 @@ abstract class TreeGen extends macros.TreeBuilder {
}
/** Create tree for pattern definition <val pat0 = rhs> */
- def mkPatDef(pat: Tree, rhs: Tree)(implicit fresh: FreshNameCreator): List[Tree] =
+ def mkPatDef(pat: Tree, rhs: Tree)(implicit fresh: FreshNameCreator): List[ValDef] =
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 {
+ def mkPatDef(mods: Modifiers, pat: Tree, rhs: Tree)(implicit fresh: FreshNameCreator): List[ValDef] = matchVarPattern(pat) match {
case Some((name, tpt)) =>
List(atPos(pat.pos union rhs.pos) {
ValDef(mods, name.toTermName, tpt, rhs)
diff --git a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala
index 39e4c48073..5f22925335 100644
--- a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala
+++ b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala
@@ -7,21 +7,22 @@ object DefinitionConstructionProps
with TraitConstruction
with TypeDefConstruction
with ValDefConstruction
+ with PatDefConstruction
with DefConstruction
- with PackageConstruction
+ with PackageConstruction
with ImportConstruction {
- property("SI-6842") = test {
- val x: Tree = q"val x: Int"
- assertEqAst(q"def f($x) = 0", "def f(x: Int) = 0")
- assertEqAst(q"class C($x)", "class C(val x: Int)")
- assertEqAst(q"class C { $x => }", "class C { x: Int => }")
- assertEqAst(q"trait B { $x => }", "trait B { x: Int => }")
- assertEqAst(q"object A { $x => }", "object A { x: Int => }")
- val t: Tree = q"type T"
- assertEqAst(q"def f[$t] = 0", "def f[T] = 0")
- assertEqAst(q"class C[$t]", "class C[T]")
- assertEqAst(q"trait B[$t]", "trait B[T]")
- }
+
+ val x: Tree = q"val x: Int"
+ property("SI-6842 a1") = test { assertEqAst(q"def f($x) = 0", "def f(x: Int) = 0") }
+ property("SI-6842 a2") = test { assertEqAst(q"class C($x)", "class C(val x: Int)") }
+ property("SI-6842 a3") = test { assertEqAst(q"class C { $x => }", "class C { x: Int => }") }
+ property("SI-6842 a4") = test { assertEqAst(q"trait B { $x => }", "trait B { x: Int => }") }
+ property("SI-6842 a5") = test { assertEqAst(q"object A { $x => }", "object A { x: Int => }") }
+
+ val t: Tree = q"type T"
+ property("SI-6842 b1") = test { assertEqAst(q"def f[$t] = 0", "def f[T] = 0") }
+ property("SI-6842 b2") = test { assertEqAst(q"class C[$t]", "class C[T]") }
+ property("SI-6842 b3") = test { assertEqAst(q"trait B[$t]", "trait B[T]") }
}
trait ClassConstruction { self: QuasiquoteProperties =>
@@ -200,19 +201,56 @@ trait TypeDefConstruction { self: QuasiquoteProperties =>
}
trait ValDefConstruction { self: QuasiquoteProperties =>
- property("splice term name into val") = forAll { (name: TermName, tpt: Tree, rhs: Tree) =>
+ property("splice into val") = forAll { (name: TermName, tpt: Tree, rhs: Tree) =>
q"val $name: $tpt = $rhs" ≈ ValDef(Modifiers(), name, tpt, rhs)
}
- property("splice term name into var") = forAll { (name: TermName, tpt: Tree, rhs: Tree) =>
+ property("splice into var") = forAll { (name: TermName, tpt: Tree, rhs: Tree) =>
q"var $name: $tpt = $rhs" ≈ ValDef(Modifiers(MUTABLE), name, tpt, rhs)
}
+ // left tree is not a pattern due to Si-8211
property("SI-8202") = test {
assertEqAst(q"val (x: Int) = 1", "val x: Int = 1")
}
}
+trait PatDefConstruction { self: QuasiquoteProperties =>
+ property("splice pattern into pat def") = test {
+ val pat = pq"(a, b)"
+ assertEqAst(q"val $pat = (1, 2)", "val (a, b) = (1, 2)")
+ val tpt = tq"(Int, Int)"
+ assertEqAst(q"val $pat: $tpt = (1, 2)", "val (a, b): (Int, Int) = (1, 2)")
+ }
+
+ property("splice pattern into pat def within other pattern (1)") = test {
+ val pat = pq"(a, b)"
+ assertEqAst(q"val Foo($pat) = Foo((1, 2))", "val Foo((a, b)) = Foo((1, 2))")
+ val tpt = tq"Foo"
+ assertEqAst(q"val Foo($pat): $tpt = Foo((1, 2))", "val Foo((a, b)): Foo = Foo((1, 2))")
+ }
+
+ property("splice patterns into pat def within other pattern (2)") = test {
+ val pat1 = pq"(a, b)"; val pat2 = pq"(c, d)"
+ assertEqAst(q"val ($pat1, $pat2) = ((1, 2), (3, 4))", "val ((a, b), (c, d)) = ((1, 2), (3, 4))")
+ val tpt = tq"((Int, Int), (Int, Int))"
+ assertEqAst(q"val ($pat1, $pat2): $tpt = ((1, 2), (3, 4))", "val ((a, b), (c, d)): ((Int, Int), (Int, Int)) = ((1, 2), (3, 4))")
+ }
+
+ property("splice pattern without free vars into pat def") = test {
+ val pat = pq"((1, 2), 3)"
+ assertEqAst(q"val $pat = ((1, 2), 3)", "{ val ((1, 2), 3) = ((1, 2), 3) }")
+ val tpt = tq"((Int, Int), Int)"
+ assertEqAst(q"val $pat: $tpt = ((1, 2), 3)","{ val ((1, 2), 3): ((Int, Int), Int) = ((1, 2), 3) }")
+ }
+
+ // won't result into pattern match due to SI-8211
+ property("splice typed pat into pat def") = test {
+ val pat = pq"x: Int"
+ assertEqAst(q"val $pat = 2", "{ val x: Int = 2 }")
+ }
+}
+
trait MethodConstruction { self: QuasiquoteProperties =>
property("splice paramss into defdef") = test {
val paramss = List(q"val x: Int") :: List(q"val y: Int = 1") :: Nil
diff --git a/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala b/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala
index 589b8d4d72..5d84984514 100644
--- a/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala
+++ b/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala
@@ -28,7 +28,7 @@ trait Helpers {
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 ValDef(mods, SimplifiedName(name), tpt, rhs) => ValDef(mods, name, transform(tpt), transform(rhs))
case Bind(SimplifiedName(name), rhs) => Bind(name, rhs)
case _ =>
super.transform(tree)