import org.scalacheck._, Prop._, Gen._, Arbitrary._
import scala.reflect.runtime.universe._, Flag._
object TermDeconstructionProps extends QuasiquoteProperties("term deconstruction") {
property("f(..x) = f") = test {
// see SI-8008
assertThrows[MatchError] {
val q"f(..$args)" = q"f"
}
}
property("f(x)") = forAll { (x: Tree) =>
val q"f($x1)" = q"f($x)"
x1 ≈ x
}
property("f(..xs)") = forAll { (x1: Tree, x2: Tree) =>
val q"f(..$xs)" = q"f($x1, $x2)"
xs ≈ List(x1, x2)
}
property("f(y, ..ys)") = forAll { (x1: Tree, x2: Tree, x3: Tree) =>
val q"f($y, ..$ys)" = q"f($x1, $x2, $x3)"
y ≈ x1 && ys ≈ List(x2, x3)
}
property("f(y1, y2, ..ys)") = forAll { (x1: Tree, x2: Tree, x3: Tree) =>
val q"f($y1, $y2, ..$ys)" = q"f($x1, $x2, $x3)"
y1 ≈ x1 && y2 ≈ x2 && ys ≈ List(x3)
}
property("f(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(...$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(...$xss)" = q"f"
xss ≈ List()
}
property("deconstruct unit as tuple") = test {
val q"(..$xs)" = q"()"
assert(xs.isEmpty)
}
property("deconstruct tuple") = test {
val q"(..$xs)" = q"(a, b)"
assert(xs ≈ List(q"a", q"b"))
}
property("deconstruct tuple mixed") = test {
val q"($first, ..$rest)" = q"(a, b, c)"
assert(first ≈ q"a")
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 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")
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 {
val q"{ ..$xs }" = q"{ x1; x2; x3 }"
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")
matches("(y: Y) => y oh y")
matches("(x: X, y: Y) => x and y")
}
property("exhaustive new pattern") = test {
def matches(line: String) {
val q"new { ..$early } with $name[..$targs](...$vargss) with ..$mixin { $self => ..$body }" = parse(line)
}
matches("new foo")
matches("new foo { body }")
matches("new foo[t]")
matches("new foo(x)")
matches("new foo[t](x)")
matches("new foo[t](x) { body }")
matches("new foo with bar")
matches("new foo with bar { body }")
matches("new { anonymous }")
matches("new { val early = 1 } with Parent[Int] { body }")
matches("new Foo { selfie => }")
}
property("exhaustive assign pattern") = test {
def matches(tree: Tree) { val q"$rhs = $lhs" = tree }
matches(parse("left = right"))
matches(parse("arr(1) = 2"))
matches(AssignOrNamedArg(EmptyTree, EmptyTree))
}
property("deconstruct update 1") = test {
val q"$obj(..$args) = $value" = q"foo(bar) = baz"
assert(obj ≈ q"foo")
assert(args ≈ List(q"bar"))
assert(value ≈ q"baz")
}
property("deconstruct update 2") = test {
val q"$left = $value" = q"foo(bar) = baz"
assert(left ≈ q"foo(bar)")
assert(value ≈ q"baz")
}
property("deconstruct while loop") = test {
val q"while($cond) $body" = parse("while(cond) body")
assert(cond ≈ q"cond")
assert(body ≈ q"body")
}
property("deconstruct do while loop") = test {
val q"do $body while($cond)" = parse("do body while(cond)")
assert(cond ≈ q"cond")
assert(body ≈ q"body")
}
property("deconstruct anonymous function with placeholders") = test {
val q"{ $f(_) }" = q"{ foo(_) }"
assert(f ≈ q"foo")
val q"{ _.$member }" = q"{ _.foo }"
assert(member ≈ TermName("foo"))
val q"{ _ + $x }" = q"{ _ + x }"
assert(x ≈ q"x")
val q"{ _ * _ }" = q"{ _ * _ }"
}
property("si-8275 a") = test {
val cq"_ => ..$stats" = cq"_ => foo; bar"
assert(stats ≈ List(q"foo", q"bar"))
}
property("si-8275 b") = test {
val cq"_ => ..$init; $last" = cq"_ => a; b; c"
assert(init ≈ List(q"a", q"b"))
assert(last ≈ q"c")
}
property("si-8275 c") = test {
val cq"_ => ..$stats" = cq"_ =>"
assert(stats.isEmpty)
assertEqAst(q"{ case _ => ..$stats }", "{ case _ => }")
}
property("can't flatten type into block") = test {
assertThrows[IllegalArgumentException] {
val tpt = tq"List[Int]"
q"..$tpt; ()"
}
}
property("term select doesn't match type select") = test {
assertThrows[MatchError] {
val q"$qual.$name" = tq"foo.bar"
}
}
property("type application doesn't match applied type") = test {
assertThrows[MatchError] {
val q"$f[..$targs]" = tq"foo[bar]"
}
}
}