summaryrefslogblamecommitdiff
path: root/test/files/scalacheck/quasiquotes/TermConstructionProps.scala
blob: b14945f24bd5f1ff7a8807d1478b0e4b49f48447 (plain) (tree)




















































































































































































































































































































































                                                                                                                                                  
import org.scalacheck._
import Prop._
import Gen._
import Arbitrary._

import scala.reflect.runtime.universe._
import Flag._

object TermConstructionProps extends QuasiquoteProperties("term construction") {
  val anyRef = Select(Ident(TermName("scala")), TypeName("AnyRef"))
  val emtpyConstructor =
    DefDef(
      Modifiers(), nme.CONSTRUCTOR, List(),
      List(List()), TypeTree(), Block(List(Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), List())), Literal(Constant(()))))

  def classWithMethods(name: TypeName, methods: List[DefDef] = Nil) =
    ClassDef(
      Modifiers(),  name, List(),
      Template(List(anyRef), emptyValDef, List(emtpyConstructor) ++ methods))

  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 term name into val") = forAll { (name: TermName) =>
    q"val $name = 0"  ValDef(Modifiers(), name, TypeTree(), Literal(Constant(0)))
  }

  property("splice type name into typedef") = forAll { (name1: TypeName, name2: TypeName) =>
    q"type $name1 = $name2"  TypeDef(Modifiers(), name1, List(), Ident(name2))
  }

  property("splice term name into class") = forAll { (name: TypeName) =>
    q"class $name"  classWithMethods(name)
  }

  property("splice method into class") = forAll { (name: TypeName, method: DefDef) =>
    q"class $name { $method }"  classWithMethods(name, List(method))
  }

  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) =>
    q"""{
      $t1
      $t2
      $t3
    }"""  Block(List(t1, t2), t3)
  }

  property("splice type name into class parents") = forAll { (name: TypeName, parent: TypeName) =>
    q"class $name extends $parent" 
      ClassDef(
        Modifiers(),  name, List(),
        Template(List(Ident(parent)), emptyValDef, List(emtpyConstructor)))
  }

  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 members into class") = forAll { (name: TypeName, defs: List[DefDef], extra: DefDef) =>
    q"""class $name {
      ..$defs
      $extra
    }"""  classWithMethods(name, defs ++ List(extra))
  }

  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 tree into singleton type tree") = forAll { (name: TypeName, t: Tree) =>
    q"type $name = $t.type"  q"type $name = ${SingletonTypeTree(t)}"
  }

  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]"  TypeApply(fun, types)
  }

  property("splice type names into type bounds") = forAll { (T1: TypeName, T2: TypeName, T3: TypeName) =>
    q"type $T1 >: $T2 <: $T3" 
      TypeDef(
        Modifiers(DEFERRED), T1, List(),
        TypeBoundsTree(Ident(T2), Ident(T3)))
  }

  property("splice trees names into type bounds") = forAll { (T: TypeName, t1: Tree, t2: Tree) =>
    q"type $T >: $t1 <: $t2" 
      TypeDef(
        Modifiers(DEFERRED), T, List(),
        TypeBoundsTree(t1, t2))
  }

  property("splice tparams into typedef (1)") = forAll { (T: TypeName, targs: List[TypeDef], t: Tree) =>
    q"type $T[..$targs] = $t"  TypeDef(Modifiers(), T, targs, t)
  }

  property("splice tparams into typedef (2)") = forAll { (T: TypeName, targs1: List[TypeDef], targs2: List[TypeDef], t: Tree) =>
    q"type $T[..$targs1, ..$targs2] = $t"  TypeDef(Modifiers(), T, targs1 ++ targs2, t)
  }

  property("splice tparams into typedef (3)") = forAll { (T: TypeName, targ: TypeDef, targs: List[TypeDef], t: Tree) =>
    q"type $T[$targ, ..$targs] = $t"  TypeDef(Modifiers(), T, targ :: targs, t)
  }

  property("splice typename into typedef with default bounds") = forAll { (T1: TypeName, T2: TypeName, t: Tree) =>
    q"type $T1[$T2 >: Any <: Nothing] = $t" 
      TypeDef(
        Modifiers(), T1,
        List(TypeDef(
          Modifiers(PARAM), T2,
          List(),
          TypeBoundsTree(
            Ident(TypeName("Any")),
            Ident(TypeName("Nothing"))))),
        t)
  }

  property("splice type names into compound type tree") = forAll { (T: TypeName, A: TypeName, B: TypeName) =>
    q"type $T = $A with $B" 
      TypeDef(
        Modifiers(), T, List(),
        CompoundTypeTree(
          Template(List(Ident(A), Ident(B)), ValDef(Modifiers(PRIVATE), nme.WILDCARD, TypeTree(), EmptyTree), List())))
  }

  property("splice trees into existential type tree") = forAll {
    (T1: TypeName, T2: TypeName, X: TypeName, Lo: TypeName, Hi: TypeName) =>

    q"type $T1 = $T2[$X] forSome { type $X >: $Lo <: $Hi }" 
      TypeDef(
        Modifiers(), T1, List(),
        ExistentialTypeTree(
          AppliedTypeTree(Ident(T2), List(Ident(X))),
          List(
            TypeDef(Modifiers(DEFERRED), X, List(), TypeBoundsTree(Ident(Lo), Ident(Hi))))))
  }

  property("splice names into import selector") = forAll {
    (expr: Tree, plain: Name, oldname: Name, newname: Name, discard: Name) =>

    val Import(expr1, List(
      ImportSelector(plain11, _, plain12, _),
      ImportSelector(oldname1, _, newname1, _),
      ImportSelector(discard1, _, wildcard, _))) =
        q"import $expr.{$plain, $oldname => $newname, $discard => _}"

    expr1  expr && plain11 == plain12 && plain12 == plain &&
    oldname1 == oldname && newname1 == newname && discard1 == discard && wildcard == nme.WILDCARD
  }

  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(())))))
  }

  property("splice into applied type tree") = forAll { (T1: TypeName, T2: TypeName, args: List[Tree]) =>
    q"type $T1 = $T2[..$args]" 
      TypeDef(
        Modifiers(), T1, List(),
        AppliedTypeTree(Ident(T2), args))
  }

  property("splice list of trees into block (1)") = forAll { (trees: List[Tree]) =>
    q"{ ..$trees }"  (trees match {
      case Nil => Block(Nil, q"()")
      case _   => Block(trees.init, trees.last)
    })
  }

  property("splice list of trees into block (2)") = forAll { (trees1: List[Tree], trees2: List[Tree]) =>
    q"{ ..$trees1 ; ..$trees2 }"  ((trees1 ++ trees2) match {
      case Nil   => Block(Nil, Literal(Constant(())))
      case trees => Block(trees.init, trees.last)
    })
  }

  property("splice list of trees into block (3)") = forAll { (trees: List[Tree], tree: Tree) =>
    q"{ ..$trees; $tree }"  Block(trees, tree)
  }

  def assertSameAnnots(tree: {def mods: Modifiers}, annots: List[Tree]) =
    assert(tree.mods.annotations  annots,
           s"${tree.mods.annotations} =/= ${annots}")

  def assertSameAnnots(tree1: {def mods: Modifiers}, tree2: {def mods: Modifiers}) =
    assert(tree1.mods.annotations  tree2.mods.annotations,
           s"${tree1.mods.annotations} =/= ${tree2.mods.annotations}")

  property("splice type name into annotation") = test {
    val name = TypeName("annot")
    assertSameAnnots(q"@$name def foo", List(annot(name)))
  }

  property("splice ident into annotation") = test {
    val name = TypeName("annot")
    val ident = Ident(name)
    assertSameAnnots(q"@$ident def foo", List(annot(name)))
  }

  property("splice idents into annotation") = test {
    val idents = List(Ident(TypeName("annot1")), Ident(TypeName("annot2")))
    assertSameAnnots(q"@..$idents def foo",
      idents.map { ident => Apply(Select(New(ident), nme.CONSTRUCTOR), List()) })
  }

  property("splice constructor calls into annotation") = test {
    val ctorcalls = List(annot("a1"), annot("a2"))
    assertSameAnnots(q"@..$ctorcalls def foo", ctorcalls)
  }

  property("splice multiple annotations (1)") = test {
    val annot1 = annot("a1")
    val annot2 = annot("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 res = q"@$annot1 @..$annots def foo"
    assertSameAnnots(res, annot1 :: annots)
  }

  property("splice annotations with arguments (1)") = test {
    val a = annot("a", List(q"x"))
    assertSameAnnots(q"@$a def foo", q"@a(x) def foo")
  }

  property("splice annotations with arguments (2)") = test {
    val a = newTypeName("a")
    assertSameAnnots(q"@$a(x) def foo", q"@a(x) def foo")
  }

  property("splice annotations with arguments (3") = test {
    val a = Ident(newTypeName("a"))
    assertSameAnnots(q"@$a(x) def foo", q"@a(x) def foo")
  }

  property("can't splice annotations with arguments specificed twice") = test {
    val a = annot("a", List(q"x"))
    assertThrows[IllegalArgumentException] {
      q"@$a(y) def foo"
    }
  }

  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"Tuple2($a1, $a2)")
    assert(q"(a0, ..$as)"  q"Tuple3(a0, $a1, $a2)")
  }

  property("splice empty list into tuple") = test {
    val empty = List[Tree]()
    assert(q"(..$empty)"  q"()")
  }

  property("splice improper tree into annot") = test {
    val t = tq"Foo[Baz]"
    assertThrows[IllegalArgumentException] {
      q"@$t def foo"
    }
  }
}