From ffc3203c3688b6fc5f47f4043bf3a0090de9d985 Mon Sep 17 00:00:00 2001 From: Denys Shabalin Date: Sat, 1 Feb 2014 19:25:16 +0100 Subject: SI-8173 add support for patterns like init :+ last to quasiquotes Adds support for patterns like: val q"{ ..$init; $last }" = q"{ a; b; c }" // init == List(q"a", q"b") // last == q"c" Which under the hood get compiled as `:+` patterns: SyntacticBlock(init :+ last) --- .../quasiquotes/DefinitionConstructionProps.scala | 22 ++++----- .../DefinitionDeconstructionProps.scala | 22 ++++++--- test/files/scalacheck/quasiquotes/ErrorProps.scala | 12 ----- .../quasiquotes/PatternDeconstructionProps.scala | 12 ++++- .../quasiquotes/QuasiquoteProperties.scala | 5 -- .../quasiquotes/TermDeconstructionProps.scala | 53 +++++++++++++++++++--- .../quasiquotes/TypeDeconstructionProps.scala | 32 ++++++++++--- 7 files changed, 111 insertions(+), 47 deletions(-) (limited to 'test/files') diff --git a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala index 3166eb7a99..798c7adf2e 100644 --- a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala @@ -229,13 +229,13 @@ trait MethodConstruction { self: QuasiquoteProperties => property("splice type name into annotation") = test { val name = TypeName("annot") - assertSameAnnots(q"@$name def foo", List(annot(name))) + assertSameAnnots(q"@$name def foo", List(q"new $name")) } property("splice ident into annotation") = test { val name = TypeName("annot") val ident = Ident(name) - assertSameAnnots(q"@$ident def foo", List(annot(name))) + assertSameAnnots(q"@$ident def foo", List(q"new $name")) } property("splice idents into annotation") = test { @@ -245,36 +245,36 @@ trait MethodConstruction { self: QuasiquoteProperties => } property("splice constructor calls into annotation") = test { - val ctorcalls = List(annot("a1"), annot("a2")) + val ctorcalls = List(q"new a1", q"new a2") assertSameAnnots(q"@..$ctorcalls def foo", ctorcalls) } property("splice multiple annotations (1)") = test { - val annot1 = annot("a1") - val annot2 = annot("a2") + val annot1 = q"new a1" + val annot2 = q"new a2" val res = q"@$annot1 @$annot2 def foo" assertSameAnnots(res, List(annot1, annot2)) } property("splice multiple annotations (2)") = test { - val annot1 = annot("a1") - val annots = List(annot("a2"), annot("a3")) + val annot1 = q"new a1" + val annots = List(q"new a2", q"new a3") val res = q"@$annot1 @..$annots def foo" assertSameAnnots(res, annot1 :: annots) } property("splice annotations with arguments (1)") = test { - val a = annot("a", List(q"x")) + val a = q"new a(x)" assertSameAnnots(q"@$a def foo", q"@a(x) def foo") } property("splice annotations with arguments (2)") = test { - val a = newTypeName("a") + val a = TypeName("a") assertSameAnnots(q"@$a(x) def foo", q"@a(x) def foo") } property("splice annotations with arguments (3") = test { - val a = Ident(newTypeName("a")) + val a = Ident(TypeName("a")) assertSameAnnots(q"@$a(x) def foo", q"@a(x) def foo") } @@ -286,7 +286,7 @@ trait MethodConstruction { self: QuasiquoteProperties => } property("can't splice annotations with arguments specificed twice") = test { - val a = annot("a", List(q"x")) + val a = q"new a(x)" assertThrows[IllegalArgumentException] { q"@$a(y) def foo" } diff --git a/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala index 209fe9bbeb..88e00c734b 100644 --- a/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala @@ -125,18 +125,28 @@ trait ModsDeconstruction { self: QuasiquoteProperties => } property("@..$annots def foo") = test { - val a = annot("a") - val b = annot("b") + val a = q"new a" + val b = q"new b" val q"@..$annots def foo" = q"@$a @$b def foo" annots ≈ List(a, b) } property("@$annot @..$annots def foo") = test { - val a = annot("a") - val b = annot("b") - val c = annot("c") + val a = q"new a" + val b = q"new b" + val c = q"new c" val q"@$first @..$rest def foo" = q"@$a @$b @$c def foo" - first ≈ a && rest ≈ List(b, c) + assert(first ≈ a) + assert(rest ≈ List(b, c)) + } + + property("@..$anots @$annot def foo") = test { + val a = q"new a" + val b = q"new b" + val c = q"new c" + val q"@..$init @$last def foo" = q"@$a @$b @$c def foo" + assert(init ≈ List(a, b)) + assert(last ≈ c) } } diff --git a/test/files/scalacheck/quasiquotes/ErrorProps.scala b/test/files/scalacheck/quasiquotes/ErrorProps.scala index 3a66574c7d..1ba9bba381 100644 --- a/test/files/scalacheck/quasiquotes/ErrorProps.scala +++ b/test/files/scalacheck/quasiquotes/ErrorProps.scala @@ -32,12 +32,6 @@ object ErrorProps extends QuasiquoteProperties("errors") { q"@...$annots def foo" """) - property("@..$first @$rest def foo") = fails( - "Can't extract with .. here", - """ - q"@a @b @c def foo" match { case q"@..$first @$rest def foo" => } - """) - property("only literal string arguments") = fails( "Quasiquotes can only be used with literal strings", """ @@ -140,12 +134,6 @@ object ErrorProps extends QuasiquoteProperties("errors") { q"$m1 $m2 def foo" """) - property("can't extract with .. card here") = fails( - "Can't extract with .. here", - """ - val q"f(..$xs, $y)" = EmptyTree - """) - property("can't extract mods with annots") = fails( "Can't extract modifiers together with annotations, consider extracting just modifiers", """ diff --git a/test/files/scalacheck/quasiquotes/PatternDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/PatternDeconstructionProps.scala index cccf8095db..c8e66c7ef5 100644 --- a/test/files/scalacheck/quasiquotes/PatternDeconstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/PatternDeconstructionProps.scala @@ -22,8 +22,18 @@ object PatternDeconstructionProps extends QuasiquoteProperties("pattern deconstr pat0 ≈ pat && subpat0 ≈ subpat } + property("extract apply many") = forAll { (pat: Tree, subpats: List[Tree]) => + val pq"$pat0(..$subpats0)" = pq"$pat(..$subpats)" + pat0 ≈ pat && subpats0 ≈ subpats + } + + property("extract apply last") = forAll { (pat: Tree, subpats: List[Tree], subpatlast: Tree) => + val pq"$pat0(..$subpats0, $subpatlast0)" = pq"$pat(..$subpats, $subpatlast)" + pat0 ≈ pat && subpats0 ≈ subpats && subpatlast0 ≈ subpatlast + } + property("extract casedef") = forAll { (pat: Tree, cond: Tree, body: Tree) => val cq"$pat0 if $cond0 => $body0" = cq"$pat if $cond => $body" pat0 ≈ pat && cond0 ≈ cond && body0 ≈ body } -} \ No newline at end of file +} diff --git a/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala b/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala index e4ee5dfcae..589b8d4d72 100644 --- a/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala +++ b/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala @@ -116,10 +116,5 @@ trait Helpers { } } - def annot(name: String): Tree = annot(TypeName(name), Nil) - def annot(name: TypeName): Tree = annot(name, Nil) - def annot(name: String, args: List[Tree]): Tree = annot(TypeName(name), args) - def annot(name: TypeName, args: List[Tree]): Tree = q"new $name(..$args)" - val scalapkg = build.setSymbol(Ident(TermName("scala")), definitions.ScalaPackage) } diff --git a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala index 8d1ada342a..148bb383b0 100644 --- a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala @@ -29,14 +29,34 @@ object TermDeconstructionProps extends QuasiquoteProperties("term deconstruction y1 ≈ x1 && y2 ≈ x2 && ys ≈ List(x3) } + property("f(y1, ..ys, yn)") = forAll { (x1: Tree, x2: Tree, x3: Tree, x4: Tree) => + val q"f($y1, ..$ys, $yn)" = q"f($x1, $x2, $x3, $x4)" + y1 ≈ x1 && ys ≈ List(x2, x3) && yn ≈ x4 + } + + property("f(..ys, y_{n-1}, y_n)") = forAll { (x1: Tree, x2: Tree, x3: Tree, x4: Tree) => + val q"f(..$ys, $yn1, $yn)" = q"f($x1, $x2, $x3, $x4)" + ys ≈ List(x1, x2) && yn1 ≈ x3 && yn ≈ x4 + } + property("f(...xss)") = forAll { (x1: Tree, x2: Tree) => - val q"f(...$argss)" = q"f($x1)($x2)" - argss ≈ List(List(x1), List(x2)) + val q"f(...$xss)" = q"f($x1)($x2)" + xss ≈ List(List(x1), List(x2)) + } + + property("f(...$xss)(..$last)") = forAll { (x1: Tree, x2: Tree, x3: Tree) => + val q"f(...$xss)(..$last)" = q"f($x1)($x2)($x3)" + xss ≈ List(List(x1), List(x2)) && last ≈ List(x3) + } + + property("f(...$xss)(..$lastinit, $lastlast)") = forAll { (x1: Tree, x2: Tree, x3: Tree, x4: Tree) => + val q"f(...$xss)(..$lastinit, $lastlast)" = q"f($x1)($x2, $x3, $x4)" + xss ≈ List(List(x1)) && lastinit ≈ List(x2, x3) && lastlast ≈ x4 } property("f(...xss) = f") = forAll { (x1: Tree, x2: Tree) => - val q"f(...$argss)" = q"f" - argss ≈ List() + val q"f(...$xss)" = q"f" + xss ≈ List() } property("deconstruct unit as tuple") = test { @@ -51,12 +71,27 @@ object TermDeconstructionProps extends QuasiquoteProperties("term deconstruction property("deconstruct tuple mixed") = test { val q"($first, ..$rest)" = q"(a, b, c)" - assert(first ≈ q"a" && rest ≈ List(q"b", q"c")) + assert(first ≈ q"a") + assert(rest ≈ List(q"b", q"c")) + } + + property("deconstruct tuple last element") = test { + val q"($first, ..$rest, $last)" = q"(a, b, c, d)" + assert(first ≈ q"a") + assert(rest ≈ List(q"b", q"c")) + assert(last ≈ q"d") } property("deconstruct cases") = test { val q"$x match { case ..$cases }" = q"x match { case 1 => case 2 => }" - x ≈ q"x" && cases ≈ List(cq"1 =>", cq"2 =>") + assert(x ≈ q"x") + assert(cases ≈ List(cq"1 =>", cq"2 =>")) + } + + property("deconstruct splitting last case") = test { + val q"$_ match { case ..$cases case $last }" = q"x match { case 1 => case 2 => case 3 => }" + assert(cases ≈ List(cq"1 =>", cq"2 =>")) + assert(last ≈ cq"3 =>") } property("deconstruct block") = test { @@ -64,6 +99,12 @@ object TermDeconstructionProps extends QuasiquoteProperties("term deconstruction assert(xs ≈ List(q"x1", q"x2", q"x3")) } + property("deconstruct last element of a block") = test { + val q"{ ..$xs; $x }" = q"x1; x2; x3; x4" + assert(xs ≈ List(q"x1", q"x2", q"x3")) + assert(x ≈ q"x4") + } + property("exhaustive function matcher") = test { def matches(line: String) { val q"(..$args) => $body" = parse(line) } matches("() => bippy") diff --git a/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala index 499f5d6d8e..44f110a3d5 100644 --- a/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala @@ -13,23 +13,43 @@ object TypeDeconstructionProps extends QuasiquoteProperties("type deconstruction a ≈ Ident(name1) && b ≈ Ident(name2) } - property("tuple type") = test { + property("tuple type (1)") = test { val tq"(..$empty)" = tq"_root_.scala.Unit" assert(empty.isEmpty) + } + + property("tuple type (2)") = test { val tq"(..$ts)" = tq"(t1, t2)" assert(ts ≈ List(tq"t1", tq"t2")) + } + + property("tuple type (3)") = test { val tq"($head, ..$tail)" = tq"(t0, t1, t2)" - assert(head ≈ tq"t0" && tail ≈ List(tq"t1", tq"t2")) + assert(head ≈ tq"t0") + assert(tail ≈ List(tq"t1", tq"t2")) + } + + property("tuple type (4)") = test { + val tq"(..$init, $last)" = tq"(t0, t1, t2)" + assert(init ≈ List(tq"t0", tq"t1")) + assert(last ≈ tq"t2") } property("refined type") = test { val tq"T { ..$stats }" = tq"T { def foo; val x: Int; type Y = String }" - assert(stats ≈ (q"def foo" :: q"val x: Int" :: q"type Y = String" :: Nil)) + assert(stats ≈ List(q"def foo", q"val x: Int", q"type Y = String")) } - property("function type") = test { + property("function type (1)") = test { val tq"..$argtpes => $restpe" = tq"(A, B) => C" - assert(argtpes ≈ (tq"A" :: tq"B" :: Nil)) + assert(argtpes ≈ List(tq"A", tq"B")) assert(restpe ≈ tq"C") } -} \ No newline at end of file + + property("function type (2)") = test { + val tq"(..$argtpes, $arglast) => $restpe" = tq"(A, B, C) => D" + assert(argtpes ≈ List(tq"A", tq"B")) + assert(arglast ≈ tq"C") + assert(restpe ≈ tq"D") + } +} -- cgit v1.2.3