From a8a7f4a0e6bc418073f8b9ed43abbc96ee0141d9 Mon Sep 17 00:00:00 2001 From: Denys Shabalin Date: Thu, 30 Jan 2014 11:39:55 +0100 Subject: SI-8202 bug compatibility with SI-8211 for quasiquotes --- test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'test/files/scalacheck/quasiquotes') diff --git a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala index dcd4f63a4d..39e4c48073 100644 --- a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala @@ -207,6 +207,10 @@ trait ValDefConstruction { self: QuasiquoteProperties => property("splice term name into var") = forAll { (name: TermName, tpt: Tree, rhs: Tree) => q"var $name: $tpt = $rhs" ≈ ValDef(Modifiers(MUTABLE), name, tpt, rhs) } + + property("SI-8202") = test { + assertEqAst(q"val (x: Int) = 1", "val x: Int = 1") + } } trait MethodConstruction { self: QuasiquoteProperties => -- cgit v1.2.3 From 8a27336c0e896d3fee6213068c77f23e62a0cd18 Mon Sep 17 00:00:00 2001 From: Denys Shabalin Date: Sun, 9 Feb 2014 15:02:44 +0100 Subject: Improve support for patterns in vals This commits adds construction-only support for splicing patterns into vals (a.k.a. PatDef). Due to non-locality of the desugaring it would have been quite expensive to support deconstruction as the only way to do it with current trees is to perform implodePatDefs transformation on every single tree. --- .../scala/tools/reflect/quasiquotes/Parsers.scala | 7 +- .../scala/tools/reflect/quasiquotes/Reifiers.scala | 25 ++++--- src/reflect/scala/reflect/api/BuildUtils.scala | 6 ++ .../scala/reflect/internal/BuildUtils.scala | 84 ++++++++++++++++------ src/reflect/scala/reflect/internal/StdNames.scala | 2 + src/reflect/scala/reflect/internal/TreeGen.scala | 4 +- .../quasiquotes/DefinitionConstructionProps.scala | 68 ++++++++++++++---- .../quasiquotes/QuasiquoteProperties.scala | 2 +- 8 files changed, 145 insertions(+), 53 deletions(-) (limited to 'test/files/scalacheck/quasiquotes') 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 */ - 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 */ - 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) -- cgit v1.2.3 From 2b67f8b94f9e95c5fb7be0e6d6c4718a6e045ec4 Mon Sep 17 00:00:00 2001 From: Denys Shabalin Date: Mon, 10 Feb 2014 14:29:59 +0100 Subject: Make handling of tuples more consistent in quasi-quotes On one hand we know that q"($expr)" is the same as q"$expr". On the other if we wrap it into a list and splice as q"(..$expr)" we get a Tuple1 constructor call which is inconsistent. This pull request fixes this inconsistency by making q"(..$expr)" being equivalent q"(${expr.head})" for single-element list. We also add support for matching of expressions as single-element tuples (similarly to blocks) and remove liftables and unliftables for Tuple1 (which aren't clearly defined any longer due to q"(foo)" == q"foo" invariant). --- src/reflect/scala/reflect/api/StandardLiftables.scala | 6 ------ src/reflect/scala/reflect/internal/BuildUtils.scala | 8 ++++++-- test/files/scalacheck/quasiquotes/LiftableProps.scala | 1 - test/files/scalacheck/quasiquotes/TermConstructionProps.scala | 5 +++++ test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala | 5 +++++ test/files/scalacheck/quasiquotes/TypeConstructionProps.scala | 5 +++++ test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala | 5 +++++ test/files/scalacheck/quasiquotes/UnliftableProps.scala | 2 -- 8 files changed, 26 insertions(+), 11 deletions(-) (limited to 'test/files/scalacheck/quasiquotes') diff --git a/src/reflect/scala/reflect/api/StandardLiftables.scala b/src/reflect/scala/reflect/api/StandardLiftables.scala index 5a03996dd9..6c2c8ca618 100644 --- a/src/reflect/scala/reflect/api/StandardLiftables.scala +++ b/src/reflect/scala/reflect/api/StandardLiftables.scala @@ -53,9 +53,6 @@ trait StandardLiftables { self: Universe => case right: Right[L, R] => lift(right) } - implicit def liftTuple1[T1](implicit liftT1: Liftable[T1]): Liftable[Tuple1[T1]] = Liftable { t => - SyntacticTuple(liftT1(t._1) :: Nil) - } implicit def liftTuple2[T1, T2](implicit liftT1: Liftable[T1], liftT2: Liftable[T2]): Liftable[Tuple2[T1, T2]] = Liftable { t => SyntacticTuple(liftT1(t._1) :: liftT2(t._2) :: Nil) } @@ -147,9 +144,6 @@ trait StandardLiftables { self: Universe => implicit def unliftType: Unliftable[Type] = Unliftable[Type] { case tt: TypeTree if tt.tpe != null => tt.tpe } implicit def unliftConstant: Unliftable[Constant] = Unliftable[Constant] { case Literal(const) => const } - implicit def unliftTuple1[T1](implicit UnliftT1: Unliftable[T1]): Unliftable[Tuple1[T1]] = Unliftable { - case SyntacticTuple(UnliftT1(v1) :: Nil) => Tuple1(v1) - } implicit def unliftTuple2[T1, T2](implicit UnliftT1: Unliftable[T1], UnliftT2: Unliftable[T2]): Unliftable[Tuple2[T1, T2]] = Unliftable { case SyntacticTuple(UnliftT1(v1) :: UnliftT2(v2) :: Nil) => Tuple2(v1, v2) } diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala index c5581601de..738eb316f0 100644 --- a/src/reflect/scala/reflect/internal/BuildUtils.scala +++ b/src/reflect/scala/reflect/internal/BuildUtils.scala @@ -350,7 +350,7 @@ trait BuildUtils { self: SymbolTable => object SyntacticTuple extends SyntacticTupleExtractor { 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) + gen.mkTuple(args) } def unapply(tree: Tree): Option[List[Tree]] = tree match { @@ -360,6 +360,8 @@ trait BuildUtils { self: SymbolTable => if sym == TupleClass(args.length).companionModule && (targs.isEmpty || targs.length == args.length) => Some(args) + case _ if tree.isTerm => + Some(tree :: Nil) case _ => None } @@ -368,7 +370,7 @@ trait BuildUtils { self: SymbolTable => object SyntacticTupleType extends SyntacticTupleExtractor { 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) + gen.mkTupleType(args) } def unapply(tree: Tree): Option[List[Tree]] = tree match { @@ -377,6 +379,8 @@ trait BuildUtils { self: SymbolTable => case MaybeTypeTreeOriginal(AppliedTypeTree(TupleClassRef(sym), args)) if sym == TupleClass(args.length) => Some(args) + case _ if tree.isType => + Some(tree :: Nil) case _ => None } diff --git a/test/files/scalacheck/quasiquotes/LiftableProps.scala b/test/files/scalacheck/quasiquotes/LiftableProps.scala index bd631b8734..20cfcbe139 100644 --- a/test/files/scalacheck/quasiquotes/LiftableProps.scala +++ b/test/files/scalacheck/quasiquotes/LiftableProps.scala @@ -99,7 +99,6 @@ object LiftableProps extends QuasiquoteProperties("liftable") { } property("lift tuple") = test { - assert(q"${Tuple1(1)}" ≈ q"scala.Tuple1(1)") assert(q"${(1, 2)}" ≈ q"(1, 2)") assert(q"${(1, 2, 3)}" ≈ q"(1, 2, 3)") assert(q"${(1, 2, 3, 4)}" ≈ q"(1, 2, 3, 4)") diff --git a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala index 058880a25c..c1aa23ac23 100644 --- a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala @@ -139,6 +139,11 @@ object TermConstructionProps extends QuasiquoteProperties("term construction") { assert(q"(..$empty)" ≈ q"()") } + property("splice single element list into tuple") = test { + val xs = q"x" :: Nil + assert(q"(..$xs)" ≈ xs.head) + } + property("function param flags are the same") = test { val xy = q"val x: A" :: q"val y: B" :: Nil assertEqAst(q"(..$xy) => x + y", "(x: A, y: B) => x + y") diff --git a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala index 148bb383b0..7c9b5ead20 100644 --- a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala @@ -82,6 +82,11 @@ object TermDeconstructionProps extends QuasiquoteProperties("term deconstruction assert(last ≈ q"d") } + property("deconstruct expr as tuple") = test { + val q"(..$elems)" = q"foo" + assert(elems ≈ List(q"foo")) + } + property("deconstruct cases") = test { val q"$x match { case ..$cases }" = q"x match { case 1 => case 2 => }" assert(x ≈ q"x") diff --git a/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala b/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala index 78b54a4e49..08ed15e8a5 100644 --- a/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala @@ -18,6 +18,11 @@ object TypeConstructionProps extends QuasiquoteProperties("type construction") assert(tq"(t0, ..$ts)" ≈ tq"scala.Tuple3[t0, t1, t2]") } + property("single-element tuple type") = test { + val ts = q"T" :: Nil + assert(tq"(..$ts)" ≈ ts.head) + } + property("refined type") = test { val stats = q"def foo" :: q"val x: Int" :: q"type Y = String" :: Nil assert(tq"T { ..$stats }" ≈ tq"T { def foo; val x: Int; type Y = String }") diff --git a/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala index 0fdcc19052..8ec1779353 100644 --- a/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala @@ -35,6 +35,11 @@ object TypeDeconstructionProps extends QuasiquoteProperties("type deconstruction assert(last ≈ tq"t2") } + property("tuple type (5)") = test { + val tq"(..$ts)" = tq"T" + assert(ts ≈ List(tq"T")) + } + property("refined type") = test { val tq"T { ..$stats }" = tq"T { def foo; val x: Int; type Y = String }" assert(stats ≈ List(q"def foo", q"val x: Int", q"type Y = String")) diff --git a/test/files/scalacheck/quasiquotes/UnliftableProps.scala b/test/files/scalacheck/quasiquotes/UnliftableProps.scala index 8b827c98ff..4e996c90d7 100644 --- a/test/files/scalacheck/quasiquotes/UnliftableProps.scala +++ b/test/files/scalacheck/quasiquotes/UnliftableProps.scala @@ -111,8 +111,6 @@ object UnliftableProps extends QuasiquoteProperties("unliftable") { } property("unlift tuple") = test { - // fails due to SI-8045 - // val q"${t1: Tuple1[Int]}" = q"_root_.scala.Tuple1(1)" val q"${t2: (Int, Int)}" = q"(1, 2)" val q"${t3: (Int, Int, Int)}" = q"(1, 2, 3)" val q"${t4: (Int, Int, Int, Int)}" = q"(1, 2, 3, 4)" -- cgit v1.2.3