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) =>
q"$t" ≈ t
}
property("splice trees into if expression") = forAll { (t1: Tree, t2: Tree, t3: Tree) =>
q"if($t1) $t2 else $t3" ≈ If(t1, t2, t3)
}
property("splice trees into ascriptiopn") = forAll { (t1: Tree, t2: Tree) =>
q"$t1 : $t2" ≈ Typed(t1, t2)
}
property("splice trees into apply") = forAll { (t1: Tree, t2: Tree, t3: Tree) =>
q"$t1($t2, $t3)" ≈ Apply(t1, List(t2, t3))
}
property("splice trees with .. cardinality into apply") = forAll { (ts: List[Tree]) =>
q"f(..$ts)" ≈ Apply(q"f", ts)
}
property("splice iterable into apply") = forAll { (trees: List[Tree]) =>
val itrees: Iterable[Tree] = trees
q"f(..$itrees)" ≈ Apply(q"f", trees)
}
property("splice trees with ... cardinality into apply") = forAll { (ts1: List[Tree], ts2: List[Tree]) =>
val argss = List(ts1, ts2)
q"f(...$argss)" ≈ Apply(Apply(q"f", ts1), ts2)
}
property("splice term name into assign") = forAll { (name: TermName, t: Tree) =>
q"$name = $t" ≈ Assign(Ident(name), t)
}
property("splice trees into block") = forAll { (t1: Tree, t2: Tree, t3: Tree) =>
blockInvariant(q"""{
$t1
$t2
$t3
}""", List(t1, t2, t3))
}
property("splice tree into new") = forAll { (tree: Tree) =>
q"new $tree" ≈ Apply(Select(New(tree), nme.CONSTRUCTOR), List())
}
property("splice tree into return") = forAll { (tree: Tree) =>
q"return $tree" ≈ Return(tree)
}
property("splice a list of arguments") = forAll { (fun: Tree, args: List[Tree]) =>
q"$fun(..$args)" ≈ Apply(fun, args)
}
property("splice list and non-list fun arguments") = forAll { (fun: Tree, arg1: Tree, arg2: Tree, args: List[Tree]) =>
q"$fun(..$args, $arg1, $arg2)" ≈ Apply(fun, args ++ List(arg1) ++ List(arg2)) &&
q"$fun($arg1, ..$args, $arg2)" ≈ Apply(fun, List(arg1) ++ args ++ List(arg2)) &&
q"$fun($arg1, $arg2, ..$args)" ≈ Apply(fun, List(arg1) ++ List(arg2) ++ args)
}
property("splice into new") = forAll { (name: TypeName, body: List[Tree]) =>
q"new $name { ..$body }" ≈
q"""{
final class $$anon extends $name {
..$body
}
new $$anon
}"""
}
property("splice type name into this") = forAll { (T: TypeName) =>
q"$T.this" ≈ This(T)
}
property("splice tree into throw") = forAll { (t: Tree) =>
q"throw $t" ≈ Throw(t)
}
property("splice trees into type apply") = forAll { (fun: TreeIsTerm, types: List[Tree]) =>
q"$fun[..$types]" ≈ (if (types.nonEmpty) TypeApply(fun, types) else fun)
}
property("splice trees into while loop") = forAll { (cond: Tree, body: Tree) =>
val LabelDef(_, List(), If(cond1, Block(List(body1), Apply(_, List())), Literal(Constant(())))) = q"while($cond) $body"
body1 ≈ body && cond1 ≈ cond
}
property("splice trees into do while loop") = forAll { (cond: Tree, body: Tree) =>
val LabelDef(_, List(), Block(List(body1), If(cond1, Apply(_, List()), Literal(Constant(()))))) = q"do $body while($cond)"
body1 ≈ body && cond1 ≈ cond
}
property("splice trees into alternative") = forAll { (c: Tree, A: Tree, B: Tree) =>
q"$c match { case $A | $B => }" ≈
Match(c, List(
CaseDef(Alternative(List(A, B)), EmptyTree, Literal(Constant(())))))
}
def blockInvariant(quote: Tree, trees: List[Tree]) =
quote ≈ (trees match {
case Nil => q""
case _ :+ last if !last.isTerm => Block(trees, q"()")
case head :: Nil => head
case init :+ last => Block(init, last)
})
property("splice list of trees into block (1)") = forAll { (trees: List[Tree]) =>
blockInvariant(q"{ ..$trees }", trees)
}
property("splice list of trees into block (2)") = forAll { (trees1: List[Tree], trees2: List[Tree]) =>
blockInvariant(q"{ ..$trees1 ; ..$trees2 }", trees1 ++ trees2)
}
property("splice list of trees into block (3)") = forAll { (trees: List[Tree], tree: Tree) =>
blockInvariant(q"{ ..$trees; $tree }", trees :+ tree)
}
property("splice term into brackets") = test {
val a = q"a"
assert(q"($a)" ≈ a)
}
property("splice terms into tuple") = test {
val a1 = q"a1"
val a2 = q"a2"
val as = List(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 {
val empty = List[Tree]()
assert(q"(..$empty)" ≈ q"()")
}
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")
}
property("anonymous functions don't support default values") = test {
val x = q"val x: Int = 1"
assertThrows[IllegalArgumentException] { q"($x) => x" }
}
property("assign variable") = test {
val v = q"v"
val value = q"foo"
assertEqAst(q"$v = $value", "v = foo")
}
property("assign update 1") = test {
val v = q"v"
val args = q"1" :: q"2" :: Nil
val value = q"foo"
assertEqAst(q"$v(..$args) = $value", "v(1, 2) = foo")
}
property("assign update 2") = test {
val a = q"v(0)"
val value = q"foo"
assertEqAst(q"$a = $value", "v(0) = foo")
}
property("assign or named arg") = test {
val assignx = q"x = 1"
assertEqAst(q"f($assignx)", "f(x = 1)")
}
property("fresh names are regenerated at each evaluation") = test {
def plusOne = q"{ _ + 1 }"
assert(!plusOne.equalsStructure(plusOne))
def whileTrue = q"while(true) false"
assert(!whileTrue.equalsStructure(whileTrue))
def withEvidence = q"def foo[T: X]"
assert(!withEvidence.equalsStructure(withEvidence))
}
property("make sure inference doesn't infer any") = test {
val l1 = List(q"foo")
val l2 = List(q"bar")
val baz = q"baz"
assert(q"f(..${l1 ++ l2})" ≈ q"f(foo, bar)")
assert(q"f(..${l1 ++ l2}, $baz)" ≈ q"f(foo, bar, baz)")
assert(q"f(${if (true) q"a" else q"b"})" ≈ q"f(a)")
}
property("splice iterable of non-parametric type") = test {
object O extends Iterable[Tree] { def iterator = List(q"foo").iterator }
q"f(..$O)"
}
property("SI-8016") = test {
val xs = q"1" :: q"2" :: Nil
assertEqAst(q"..$xs", "{1; 2}")
assertEqAst(q"{..$xs}", "{1; 2}")
}
property("SI-6842") = test {
val cases: List[Tree] = cq"a => b" :: cq"_ => c" :: Nil
assertEqAst(q"1 match { case ..$cases }", "1 match { case a => b case _ => c }")
assertEqAst(q"try 1 catch { case ..$cases }", "try 1 catch { case a => b case _ => c }")
}
property("SI-8009") = test {
q"`foo`".asInstanceOf[reflect.internal.SymbolTable#Ident].isBackquoted
}
property("SI-8148") = test {
val q"($a, $b) => $_" = q"_ + _"
assert(a.name != b.name)
}
property("SI-7275 a") = test {
val t = q"stat1; stat2"
assertEqAst(q"..$t", "{stat1; stat2}")
}
property("SI-7275 b") = test {
def f(t: Tree) = q"..$t"
assertEqAst(f(q"stat1; stat2"), "{stat1; stat2}")
}
property("SI-7275 c1") = test {
object O
implicit val liftO = Liftable[O.type] { _ => q"foo; bar" }
assertEqAst(q"f(..$O)", "f(foo, bar)")
}
property("SI-7275 c2") = test {
object O
implicit val liftO = Liftable[O.type] { _ => q"{ foo; bar }; { baz; bax }" }
assertEqAst(q"f(...$O)", "f(foo, bar)(baz, bax)")
}
property("SI-7275 d") = test {
val l = q"a; b" :: q"c; d" :: Nil
assertEqAst(q"f(...$l)", "f(a, b)(c, d)")
val l2: Iterable[Tree] = l
assertEqAst(q"f(...$l2)", "f(a, b)(c, d)")
}
property("SI-7275 e") = test {
val t = q"{ a; b }; { c; d }"
assertEqAst(q"f(...$t)", "f(a, b)(c, d)")
}
property("SI-7275 e2") = test {
val t = q"{ a; b }; c; d"
assertEqAst(q"f(...$t)", "f(a, b)(c)(d)")
}
property("remove synthetic unit") = test {
val q"{ ..$stats1 }" = q"{ def x = 2 }"
assert(stats1 ≈ List(q"def x = 2"))
val q"{ ..$stats2 }" = q"{ class X }"
assert(stats2 ≈ List(q"class X"))
val q"{ ..$stats3 }" = q"{ type X = Int }"
assert(stats3 ≈ List(q"type X = Int"))
val q"{ ..$stats4 }" = q"{ val x = 2 }"
assert(stats4 ≈ List(q"val x = 2"))
}
property("don't remove user-defined unit") = test {
val q"{ ..$stats }" = q"{ def x = 2; () }"
assert(stats ≈ List(q"def x = 2", q"()"))
}
property("empty-tree as block") = test {
val q"{ ..$stats1 }" = q" "
assert(stats1.isEmpty)
val stats2 = List.empty[Tree]
assert(q"{ ..$stats2 }" ≈ q"")
}
property("consistent variable order") = test {
val q"$a = $b = $c = $d = $e = $f = $g = $h = $k = $l" = q"a = b = c = d = e = f = g = h = k = l"
assert(a ≈ q"a" && b ≈ q"b" && c ≈ q"c" && d ≈ q"d" && e ≈ q"e" && g ≈ q"g" && h ≈ q"h" && k ≈ q"k" && l ≈ q"l")
}
}