From 7553b0afa9e4a071c7ea1cd51effd57030b66be6 Mon Sep 17 00:00:00 2001 From: Den Shabalin Date: Mon, 8 Jul 2013 20:21:33 +0200 Subject: tests for quasiquotes Introduces an extensive ScalaCheck-based test suite for recently implemented quasiquotes. Provides tools for syntactic tree comparison and verifying compilation error messages. --- .../quasiquotes/ArbitraryTreesAndNames.scala | 307 +++++++++++++++++++ test/files/scalacheck/quasiquotes/ErrorProps.scala | 199 ++++++++++++ .../scalacheck/quasiquotes/LiftableProps.scala | 84 +++++ .../quasiquotes/PatternConstructionProps.scala | 37 +++ .../quasiquotes/PatternDeconstructionProps.scala | 35 +++ .../quasiquotes/QuasiquoteProperties.scala | 89 ++++++ .../quasiquotes/TermConstructionProps.scala | 341 +++++++++++++++++++++ .../quasiquotes/TermDeconstructionProps.scala | 122 ++++++++ test/files/scalacheck/quasiquotes/Test.scala | 12 + .../quasiquotes/TypeConstructionProps.scala | 25 ++ .../quasiquotes/TypeDeconstructionProps.scala | 29 ++ 11 files changed, 1280 insertions(+) create mode 100644 test/files/scalacheck/quasiquotes/ArbitraryTreesAndNames.scala create mode 100644 test/files/scalacheck/quasiquotes/ErrorProps.scala create mode 100644 test/files/scalacheck/quasiquotes/LiftableProps.scala create mode 100644 test/files/scalacheck/quasiquotes/PatternConstructionProps.scala create mode 100644 test/files/scalacheck/quasiquotes/PatternDeconstructionProps.scala create mode 100644 test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala create mode 100644 test/files/scalacheck/quasiquotes/TermConstructionProps.scala create mode 100644 test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala create mode 100644 test/files/scalacheck/quasiquotes/Test.scala create mode 100644 test/files/scalacheck/quasiquotes/TypeConstructionProps.scala create mode 100644 test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala (limited to 'test/files/scalacheck') diff --git a/test/files/scalacheck/quasiquotes/ArbitraryTreesAndNames.scala b/test/files/scalacheck/quasiquotes/ArbitraryTreesAndNames.scala new file mode 100644 index 0000000000..03f8aa58d3 --- /dev/null +++ b/test/files/scalacheck/quasiquotes/ArbitraryTreesAndNames.scala @@ -0,0 +1,307 @@ +import org.scalacheck._ +import Prop._ +import Gen._ +import Arbitrary._ + +import scala.reflect.api._ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.universe.Flag._ + +trait ArbitraryTreesAndNames { + def smallList[T](size: Int, g: Gen[T]) = { + val n: Int = choose(0, size / 2 + 1).sample match { + case Some(i) => i + case None => 0 + } + containerOfN[List, T](n, g) + } + + def shortIdent(len: Int) = + for(name <- identifier) + yield if(name.length <= len) name + else name.substring(0, len - 1) + + def genTermName = for(name <- shortIdent(8)) yield TermName(name) + def genTypeName = for(name <- shortIdent(8)) yield TypeName(name) + def genName = oneOf(genTermName, genTypeName) + + def genFlagSet = oneOf( + TRAIT, INTERFACE, MUTABLE, MACRO, + DEFERRED, ABSTRACT, FINAL, SEALED, + IMPLICIT, LAZY, OVERRIDE, PRIVATE, + PROTECTED, LOCAL, CASE, ABSOVERRIDE, + BYNAMEPARAM, PARAM, COVARIANT, CONTRAVARIANT, + DEFAULTPARAM, PRESUPER, DEFAULTINIT + ) + + def genModifiers = for(flagset <- genFlagSet) yield Modifiers(flagset) + + def genConstant = + for(value <- oneOf(arbitrary[Byte], arbitrary[Short], arbitrary[Char], + arbitrary[Int], arbitrary[Long], arbitrary[Float], + arbitrary[Double], arbitrary[Boolean], arbitrary[String])) + yield Constant(value) + + def genAnnotated(size: Int, argGen: Int => Gen[Tree]) = + for(annot <- genTree(size - 1); arg <- argGen(size - 1)) + yield Annotated(annot, arg) + + def genAlternative(size: Int): Gen[Alternative] = + for(trees <- smallList(size, genTree(size - 1))) + yield Alternative(trees) + + def genAppliedTypeTree(size: Int) = + for(tpt <- genTree(size - 1) if tpt.isType; + args <- smallList(size, genTree(size - 1))) + yield AppliedTypeTree(tpt, args) + + def genApply(size: Int) = + for(fun <- genTree(size - 1); + args <- smallList(size, genTree(size - 1))) + yield Apply(fun, args) + + def genAssign(size: Int) = + for(lhs <- genTree(size - 1); rhs <- genTree(size - 1)) + yield Assign(lhs, rhs) + + def genAssignOrNamedArg(size: Int) = + for(lhs <- genTree(size - 1); rhs <- genTree(size - 1)) + yield AssignOrNamedArg(lhs, rhs) + + def genBind(size: Int, nameGen: Gen[Name]) = + for(name <- nameGen; body <- genTree(size - 1)) + yield Bind(name, body) + + def genBlock(size: Int) = + for(stats <- smallList(size, genTree(size - 1)); expr <- genTree(size - 1)) + yield Block(stats, expr) + + def genCaseDef(size: Int) = + for(pat <- genTree(size - 1); guard <- genTree(size - 1); body <- genTree(size - 1)) + yield CaseDef(pat, guard, body) + + def genClassDef(size: Int) = + for(mods <- genModifiers; name <- genTypeName; + tparams <- smallList(size, genTypeDef(size - 1)); + impl <- genTemplate(size - 1)) + yield ClassDef(mods, name, tparams, impl) + + def genCompoundTypeTree(size: Int) = + for(templ <- genTemplate(size - 1)) + yield CompoundTypeTree(templ) + + def genDefDef(size: Int) = + for(mods <- genModifiers; name <- genName; + tpt <- genTree(size -1); rhs <- genTree(size - 1); + tparams <- smallList(size, genTypeDef(size - 1)); + vparamss <- smallList(size, smallList(size, genValDef(size - 1)))) + yield DefDef(mods, name, tparams, vparamss, tpt, rhs) + + def genExistentialTypeTree(size: Int) = + for(tpt <- genTree(size - 1); where <- smallList(size, genTree(size - 1))) + yield ExistentialTypeTree(tpt, where) + + def genFunction(size: Int) = + for(vparams <- smallList(size, genValDef(size - 1)); body <- genTree(size - 1)) + yield Function(vparams, body) + + def genIdent(nameGen: Gen[Name] = genName) = + for(name <- nameGen) yield Ident(name) + + def genIf(size: Int) = + for(cond <- genTree(size - 1); thenp <- genTree(size - 1); elsep <- genTree(size - 1)) + yield If(cond, thenp, elsep) + + def genImport(size: Int) = + for(expr <- genTree(size - 1); selectors <- smallList(size, genImportSelector(size - 1))) + yield Import(expr, selectors) + + def genImportSelector(size: Int) = + for(name <- genName; namePos <- arbitrary[Int]; rename <- genName; renamePos <- arbitrary[Int]) + yield ImportSelector(name, namePos, rename, renamePos) + + def genTemplate(size: Int) = + for(parents <- smallList(size, genTree(size - 1)); + self <- genValDef(size - 1); + body <- smallList(size, genTree(size - 1))) + yield Template(parents, self, body) + + def genLabelDef(size: Int) = + for(name <- genTermName; params <- smallList(size, genIdent()); rhs <- genTree(size - 1)) + yield LabelDef(name, params, rhs) + + def genLiteral = + for(const <- genConstant) yield Literal(const) + + def genMatch(size: Int) = + for(selector <- genTree(size - 1); cases <- smallList(size, genCaseDef(size - 1))) + yield Match(selector, cases) + + def genModuleDef(size: Int) = + for(mods <- genModifiers; name <- genTermName; impl <- genTemplate(size - 1)) + yield ModuleDef(mods, name, impl) + + def genNew(size: Int) = + for(tpt <- genTree(size - 1)) + yield New(tpt) + + def genRefTree(size: Int) = + oneOf(genSelect(size), genIdent(), genSelectFromTypeTree(size)) + + def genPackageDef(size: Int) = + for(reftree <- genRefTree(size - 1); stats <- smallList(size, genTree(size - 1))) + yield PackageDef(reftree, stats) + + def genTypeSelect(size: Int) = + for(qual <- genTree(size - 1); name <- genTypeName) + yield Select(qual, name) + + def genSelect(size: Int, nameGen: Gen[Name] = genName) = + for(qual <- genTree(size - 1); name <- nameGen) + yield Select(qual, name) + + def genSelectFromTypeTree(size: Int) = + for(qual <- genTreeIsType(size - 1); name <- genTypeName) + yield SelectFromTypeTree(qual, name) + + def genReferenceToBoxed(size: Int) = + for(ident <- genIdent()) + yield ReferenceToBoxed(ident) + + def genReturn(size: Int) = + for(expr <- genTree(size - 1)) + yield Return(expr) + + def genSingletonTypeTree(size: Int) = + for(expr <- genTree(size - 1)) + yield SingletonTypeTree(expr) + + def genStar(size: Int) = + for(expr <- genTree(size - 1)) + yield Star(expr) + + def genSuper(size: Int) = + for(qual <- genTree(size - 1); mix <- genTypeName) + yield Super(qual, mix) + + def genThis(size: Int) = + for(qual <- genTypeName) + yield This(qual) + + def genThrow(size: Int) = + for(expr <- genTree(size - 1)) + yield Throw(expr) + + def genTry(size: Int) = + for(block <- genTree(size - 1); + catches <- smallList(size, genCaseDef(size - 1)); + finalizer <- genTree(size - 1)) + yield Try(block, catches, finalizer) + + def genTypeApply(size: Int) = + for(fun <- genTreeIsTerm(size - 1); args <- smallList(size, genTree(size - 1))) + yield TypeApply(fun, args) + + def genTypeBoundsTree(size: Int) = + for(lo <- genTree(size - 1); hi <- genTree(size - 1)) + yield TypeBoundsTree(lo, hi) + + def genTypeDef(size: Int): Gen[TypeDef] = + for(mods <- genModifiers; name <- genTypeName; + tparams <- smallList(size, genTypeDef(size - 1)); rhs <- genTree(size - 1)) + yield TypeDef(mods, name, tparams, rhs) + + def genTypeTree: Gen[TypeTree] = TypeTree() + + def genTyped(size: Int) = + for(expr <- genTree(size - 1); tpt <- genTree(size - 1)) + yield Typed(expr, tpt) + + def genUnApply(size: Int) = + for(fun <- genTree(size - 1); args <- smallList(size, genTree(size - 1))) + yield UnApply(fun, args) + + def genValDef(size: Int) = + for(mods <- genModifiers; name <- genTermName; + tpt <- genTree(size - 1); rhs <- genTree(size - 1)) + yield ValDef(mods, name, tpt, rhs) + + def genTree(size: Int): Gen[Tree] = + if (size <= 1) oneOf(EmptyTree, genTreeIsTerm(size), genTreeIsType(size)) + else oneOf(genTree(1), + // these trees are neither terms nor types + genPackageDef(size - 1), genModuleDef(size - 1), + genCaseDef(size - 1), genDefDef(size - 1), + genTypeDef(size - 1), genTemplate(size - 1), + genClassDef(size - 1), genValDef(size - 1), + genImport(size - 1)) + + def genTreeIsTerm(size: Int): Gen[Tree] = + if (size <= 1) oneOf(genLiteral, genIdent(genTermName)) + else oneOf(genTreeIsTerm(1), genBind(size - 1, genTermName), + genAnnotated(size - 1, genTreeIsTerm), genSelect(size - 1, genTermName), + genAlternative(size - 1), genApply(size - 1), genAssign(size - 1), + genAssignOrNamedArg(size - 1), genBlock(size - 1), genFunction(size - 1), + genIf(size - 1), genLabelDef(size - 1), genMatch(size - 1), genNew(size - 1), + genReturn(size - 1), genStar(size - 1), genSuper(size - 1), genThis(size - 1), + genThrow(size - 1), genTry(size - 1), genTypeApply(size - 1), + genTyped(size - 1), genUnApply(size - 1)) + + def genTreeIsType(size: Int): Gen[Tree] = + if (size <= 1) genIdent(genTypeName) + else oneOf(genTreeIsType(1), genAnnotated(size - 1, genTreeIsType), + genBind(size - 1, genTypeName), genSelect(size - 1, genTypeName), + genSingletonTypeTree(size - 1), genSelectFromTypeTree(size - 1), + genExistentialTypeTree(size - 1), genCompoundTypeTree(size - 1), + genAppliedTypeTree(size - 1), genTypeBoundsTree(size - 1)) + + /* These are marker types that allow to write tests that + * depend specificly on Trees that are terms or types. + * They are transperantly tranformed to trees through + * implicit conversions and liftables for quasiquotes. + */ + + case class TreeIsTerm(tree: Tree) { require(tree.isTerm, showRaw(tree)) } + case class TreeIsType(tree: Tree) { require(tree.isType, showRaw(tree)) } + + def genTreeIsTermWrapped(size: Int) = + for(tit <- genTreeIsTerm(size)) yield TreeIsTerm(tit) + + def genTreeIsTypeWrapped(size: Int) = + for(tit <- genTreeIsType(size)) yield TreeIsType(tit) + + implicit object liftTreeIsTerm extends Liftable[TreeIsTerm] { + def apply(universe: Universe, value: TreeIsTerm): universe.Tree = + value.tree.asInstanceOf[universe.Tree] + } + implicit object liftTreeIsType extends Liftable[TreeIsType] { + def apply(universe: Universe, value: TreeIsType): universe.Tree = + value.tree.asInstanceOf[universe.Tree] + } + implicit def treeIsTerm2tree(tit: TreeIsTerm) = tit.tree + implicit def treeIsType2tree(tit: TreeIsType) = tit.tree + + implicit val arbConstant: Arbitrary[Constant] = Arbitrary(genConstant) + implicit val arbModifiers: Arbitrary[Modifiers] = Arbitrary(genModifiers) + implicit val arbTermName: Arbitrary[TermName] = Arbitrary(genTermName) + implicit val arbTypeName: Arbitrary[TypeName] = Arbitrary(genTypeName) + implicit val arbName: Arbitrary[Name] = Arbitrary(genName) + + // Trees generators are bound by this size to make + // generation times shorter and less memory hungry. + // TODO: is there any better solution? + val maxTreeSize = 5 + + def arbitrarySized[T](gen: Int => Gen[T]) = + Arbitrary(sized(s => gen(s.min(maxTreeSize)))) + + implicit val arbLiteral: Arbitrary[Literal] = Arbitrary(genLiteral) + implicit val arbIdent: Arbitrary[Ident] = Arbitrary(genIdent()) + implicit val arbValDef: Arbitrary[ValDef] = arbitrarySized(genValDef) + implicit val arbDefDef: Arbitrary[DefDef] = arbitrarySized(genDefDef) + implicit val arbTypeDef: Arbitrary[TypeDef] = arbitrarySized(genTypeDef) + implicit val arbBind: Arbitrary[Bind] = arbitrarySized(genBind(_, genName)) + implicit val arbTree: Arbitrary[Tree] = arbitrarySized(genTree) + implicit val arbTreeIsTerm: Arbitrary[TreeIsTerm] = arbitrarySized(genTreeIsTermWrapped) + implicit val arbTreeIsType: Arbitrary[TreeIsType] = arbitrarySized(genTreeIsTypeWrapped) +} \ No newline at end of file diff --git a/test/files/scalacheck/quasiquotes/ErrorProps.scala b/test/files/scalacheck/quasiquotes/ErrorProps.scala new file mode 100644 index 0000000000..044a332a04 --- /dev/null +++ b/test/files/scalacheck/quasiquotes/ErrorProps.scala @@ -0,0 +1,199 @@ +import org.scalacheck._ +import Prop._ +import Gen._ +import Arbitrary._ + +import scala.reflect.runtime.universe._ +import Flag._ + +object ErrorProps extends QuasiquoteProperties("errors") { + property("can't extract two .. cardinalities in a row") = fails( + "Can't extract with .. here", + """ + val xs = List(q"x1", q"x2") + val q"f(..$xs1, ..$xs2)" = xs + """) + + property("can't splice with given cardinality") = fails( + "Can't splice List[reflect.runtime.universe.Ident], consider using ..", + """ + val xs = List(q"x", q"x") + q"$xs" + """) + + property("splice typename into typedef with default bounds") = fails( + "reflect.runtime.universe.Name expected but reflect.runtime.universe.TypeDef found", + """ + val T1 = TypeName("T1") + val T2 = q"type T" + val t = EmptyTree + q"type $T1[$T2 >: _root_.scala.Any <: _root_.scala.Nothing] = $t" ≈ + TypeDef(Modifiers(), T1, List(T2), t) + """) + + property("can't splice annotations with ... cardinality") = fails( + "Can't splice with ... here", + """ + val annots = List(List(q"Foo")) + q"@...$annots def foo" + """) + + property("@..$first @$rest def foo") = fails( + "Can't extract with .. here", + """ + val a = annot("a") + val b = annot("b") + val c = annot("c") + val q"@..$first @$rest def foo" = q"@$a @$b @$c def foo" + """) + + property("only literal string arguments") = fails( + "Quasiquotes can only be used with literal strings", + """ + val s: String = "foo" + StringContext(s).q() + """) + + property("don't know how to splice inside of strings") = fails( + "Don't know how to splice here", + """ + val x: Tree = EmptyTree + StringContext("\"", "\"").q(x) + """) + + property("expected different cardinality") = fails( + "Can't splice List[reflect.runtime.universe.Tree] with ..., consider using ..", + """ + val args: List[Tree] = Nil + q"f(...$args)" + """) + + property("non-liftable type ..") = fails( + "Can't splice List[StringBuilder] with .., consider omitting the dots or providing an implicit instance of Liftable[StringBuilder]", + """ + import java.lang.StringBuilder + val bazs = List(new StringBuilder) + q"f(..$bazs)" + """) + + property("non-liftable type ...") = fails( + "Can't splice List[List[StringBuilder]] with .., consider using ... or providing an implicit instance of Liftable[StringBuilder]", + """ + import java.lang.StringBuilder + val bazs = List(List(new StringBuilder)) + q"f(..$bazs)" + """) + + property("use .. card or provide liftable") = fails( + "Can't splice List[StringBuilder], consider using .. or providing an implicit instance of Liftable[List[StringBuilder]]", + """ + import java.lang.StringBuilder + val lst: List[StringBuilder] = Nil + q"f($lst)" + """) + + property("use ... card or provide liftable") = fails( + "Can't splice List[List[reflect.runtime.universe.Ident]], consider using ...", + """ + val xs = List(List(q"x", q"x")) + q"$xs" + """) + + property("use zero card") = fails( + "Can't splice reflect.runtime.universe.Tree with .., consider omitting the dots", + """ + val t = EmptyTree + q"f(..$t)" + """) + + property("not liftable or natively supported") = fails( + "Can't splice StringBuilder, consider providing an implicit instance of Liftable[StringBuilder]", + """ + import java.lang.StringBuilder + val sb = new StringBuilder + q"f($sb)" + """) + + property("casedef expected") = fails( + "reflect.runtime.universe.CaseDef expected but reflect.runtime.universe.Tree found", + """ + val t = EmptyTree + q"_ { case $t }" + """) + + property("can't splice with ... card here") = fails( + "Can't splice with ... here", + """ + val lst: List[List[Tree]] = Nil; val t = EmptyTree + q"f(...$lst, $t)" + """) + + property("name expected") = fails( + "reflect.runtime.universe.Name expected but reflect.runtime.universe.Tree found", + """ + val t = EmptyTree + q"class $t" + """) + + property("flags or mods expected") = fails( + "reflect.runtime.universe.FlagSet or reflect.runtime.universe.Modifiers expected but reflect.runtime.universe.Tree found", + """ + val t = EmptyTree + q"$t def foo" + """) + + property("cant splice flags together with mods") = fails( + "Can't splice flags together with modifiers, consider merging flags into modifiers", + """ + val f = Flag.IMPLICIT; val m = NoMods + q"$f $m def foo" + """) + + property("can't splice mods with annots") = fails( + "Can't splice modifiers together with annotations, consider merging annotations into modifiers", + """ + val m = NoMods + q"@annot $m def foo" + """) + + property("can't splice modifiers with inline flags") = fails( + "Can't splice modifiers together with flags, consider merging flags into modifiers", + """ + val m = NoMods + q"$m implicit def foo" + """) + + property("can't splice multiple mods") = fails( + "Can't splice multiple modifiers, consider merging them into a single modifiers instance", + """ + val m1 = NoMods; val m2 = NoMods + 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", + """ + val q"@$annot $mods def foo" = EmptyTree + """) + + property("can't extract multiple mods") = fails( + "Can't extract multiple modifiers together, consider extracting a single modifiers instance", + """ + val q"$m1 $m2 def foo" = EmptyTree + """) + + property("can't parse more than one casedef") = fails( + "Can't parse more than one casedef, consider generating a match tree instead", + """ + cq"1 => 2 case 3 => 5" + """) + + // // Make sure a nice error is reported in this case + // { import Flag._; val mods = NoMods; q"lazy $mods val x: Int" } +} \ No newline at end of file diff --git a/test/files/scalacheck/quasiquotes/LiftableProps.scala b/test/files/scalacheck/quasiquotes/LiftableProps.scala new file mode 100644 index 0000000000..510ab99068 --- /dev/null +++ b/test/files/scalacheck/quasiquotes/LiftableProps.scala @@ -0,0 +1,84 @@ +import org.scalacheck._ +import Prop._ +import Gen._ +import Arbitrary._ + +import scala.reflect.runtime.universe._ +import Flag._ + +object LiftableProps extends QuasiquoteProperties("liftable") { + property("splice byte") = test { + val c: Byte = 0 + assert(q"$c" ≈ Literal(Constant(c))) + } + + property("splice short") = test { + val c: Short = 0 + assert(q"$c" ≈ Literal(Constant(c))) + } + + property("splice char") = test { + val c: Char = 'c' + assert(q"$c" ≈ Literal(Constant(c))) + } + + property("splice int") = test { + val c: Int = 0 + assert(q"$c" ≈ Literal(Constant(c))) + } + + property("splice long") = test { + val c: Long = 0 + assert(q"$c" ≈ Literal(Constant(c))) + } + + property("splice float") = test { + val c: Float = 0.0f + assert(q"$c" ≈ Literal(Constant(c))) + } + + property("splice double") = test { + val c: Double = 0.0 + assert(q"$c" ≈ Literal(Constant(c))) + } + + property("splice boolean") = test { + val c: Boolean = false + assert(q"$c" ≈ Literal(Constant(c))) + } + + property("splice string") = test { + val c: String = "s" + assert(q"$c" ≈ Literal(Constant(c))) + } + + property("splice unit") = test { + val c: Unit = () + assert(q"$c" ≈ Literal(Constant(c))) + } + + property("lift symbol") = test { + val s = rootMirror.staticClass("scala.Int") + assert(q"$s" ≈ Ident(s)) + } + + property("lift type") = test { + val tpe = rootMirror.staticClass("scala.Int").toType + assert(q"$tpe" ≈ TypeTree(tpe)) + } + + property("lift type tag") = test { + val tag = TypeTag.Int + assert(q"$tag" ≈ TypeTree(tag.tpe)) + } + + property("lift weak type tag") = test { + val tag = WeakTypeTag.Int + assert(q"$tag" ≈ TypeTree(tag.tpe)) + } + + property("lift constant") = test { + val const = Constant(0) + assert(q"$const" ≈ q"0") + } +} \ No newline at end of file diff --git a/test/files/scalacheck/quasiquotes/PatternConstructionProps.scala b/test/files/scalacheck/quasiquotes/PatternConstructionProps.scala new file mode 100644 index 0000000000..aee50c9c5f --- /dev/null +++ b/test/files/scalacheck/quasiquotes/PatternConstructionProps.scala @@ -0,0 +1,37 @@ +import org.scalacheck._ +import Prop._ +import Gen._ +import Arbitrary._ + +import scala.reflect.runtime.universe._ +import Flag._ + +object PatternConstructionProps extends QuasiquoteProperties("pattern construction") { + property("splice bind") = forAll { (bind: Bind) => + pq"$bind" ≈ bind + } + + property("splice name into bind") = forAll { (name: TermName) => + pq"$name" ≈ Bind(name, Ident(nme.WILDCARD)) + } + + property("splice name and tree into bind") = forAll { (name: TermName, tree: Tree) => + pq"$name @ $tree" ≈ Bind(name, tree) + } + + property("splice type name into typed") = forAll { (name: TypeName) => + pq"_ : $name" ≈ Typed(Ident(nme.WILDCARD), Ident(name)) + } + + property("splice tree into typed") = forAll { (typ: Tree) => + pq"_ : $typ" ≈ Typed(Ident(nme.WILDCARD), typ) + } + + property("splice into apply") = forAll { (pat: Tree, subpat: Tree) => + pq"$pat($subpat)" ≈ Apply(pat, List(subpat)) + } + + property("splice into casedef") = forAll { (pat: Tree, cond: Tree, body: Tree) => + cq"$pat if $cond => $body" ≈ CaseDef(pat, cond, Block(List(), body)) + } +} \ No newline at end of file diff --git a/test/files/scalacheck/quasiquotes/PatternDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/PatternDeconstructionProps.scala new file mode 100644 index 0000000000..f73fd29b22 --- /dev/null +++ b/test/files/scalacheck/quasiquotes/PatternDeconstructionProps.scala @@ -0,0 +1,35 @@ +import org.scalacheck._ +import Prop._ +import Gen._ +import Arbitrary._ + +import scala.reflect.runtime.universe._ +import Flag._ +import definitions._ + +object PatternDeconstructionProps extends QuasiquoteProperties("pattern deconstruction") { + property("extract bind") = forAll { (bind: Bind) => + val pq"$bind0" = pq"$bind" + bind0 ≈ bind + } + + property("extract bind and subpattern") = forAll { (name: TermName, subp: Tree) => + val pq"$name0 @ $subp0" = pq"$name @ $subp" + name0 ≈ name && subp0 ≈ subp + } + + property("extract typed") = forAll { (typ: Tree) => + val pq"_ : $typ0" = pq"_ : $typ" + typ0 ≈ typ + } + + property("extract apply") = forAll { (pat: Tree, subpat: Tree) => + val pq"$pat0($subpat0)" = pq"$pat($subpat)" + pat0 ≈ pat && subpat0 ≈ subpat + } + + 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 new file mode 100644 index 0000000000..5e87aa57cc --- /dev/null +++ b/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala @@ -0,0 +1,89 @@ +import scala.reflect.runtime.universe._ +import scala.tools.reflect.ToolBox +import scala.tools.reflect.ToolBoxError +import scala.reflect.macros.TypecheckException + +import org.scalacheck._ +import Prop._ +import Gen._ +import Arbitrary._ + +class QuasiquoteProperties(name: String) extends Properties(name) with ArbitraryTreesAndNames with Helpers + +trait Helpers { + /** Runs a code block and returns proof confirmation + * if no exception has been thrown while executing code + * block. This is useful for simple one-off tests. + */ + def test[T](block: => T)= + Prop { (params) => + block + Result(Prop.Proof) + } + + implicit class TestSimilarTree(tree1: Tree) { + def ≈(tree2: Tree) = tree1.equalsStructure(tree2) + } + + implicit class TestSimilarListTree(lst: List[Tree]) { + def ≈(other: List[Tree]) = (lst.length == other.length) && lst.zip(other).forall { case (t1, t2) => t1 ≈ t2 } + } + + implicit class TestSimilarListListTree(lst: List[List[Tree]]) { + def ≈(other: List[List[Tree]]) = (lst.length == other.length) && lst.zip(other).forall { case (l1, l2) => l1 ≈ l2 } + } + + implicit class TestSimilarName(name: Name) { + def ≈(other: Name) = name == other + } + + implicit class TestSimilarMods(mods: Modifiers) { + def ≈(other: Modifiers) = (mods.flags == other.flags) && (mods.privateWithin ≈ other.privateWithin) && (mods.annotations ≈ other.annotations) + } + + def assertThrows[T <: AnyRef](f: => Any)(implicit manifest: Manifest[T]): Unit = { + val clazz = manifest.erasure.asInstanceOf[Class[T]] + val thrown = + try { + f + false + } catch { + case u: Throwable => + if (!clazz.isAssignableFrom(u.getClass)) + assert(false, s"wrong exception: $u") + true + } + if(!thrown) + assert(false, "exception wasn't thrown") + } + + def fails(msg: String, block: String) = { + def result(ok: Boolean, description: String = "") = { + val status = if (ok) Prop.Proof else Prop.False + val labels = if (description != "") Set(description) else Set.empty[String] + Prop { new Prop.Result(status, Nil, Set.empty, labels) } + } + try { + val tb = rootMirror.mkToolBox() + val tree = tb.parse(s""" + object Wrapper extends Helpers { + import scala.reflect.runtime.universe._ + $block + } + """) + tb.compile(tree) + result(false, "given code doesn't fail to typecheck") + } catch { + case ToolBoxError(emsg, _) => + if (!emsg.contains(msg)) + result(false, s"error message '${emsg}' is not the same as expected '$msg'") + else + result(true) + } + } + + 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)" +} \ No newline at end of file diff --git a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala new file mode 100644 index 0000000000..b14945f24b --- /dev/null +++ b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala @@ -0,0 +1,341 @@ +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" + } + } +} diff --git a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala new file mode 100644 index 0000000000..114c9f112b --- /dev/null +++ b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala @@ -0,0 +1,122 @@ +import org.scalacheck._ +import Prop._ +import Gen._ +import Arbitrary._ + +import scala.reflect.runtime.universe._ +import Flag._ + +object TermDeconstructionProps extends QuasiquoteProperties("term deconstruction") { + + property("f(..x) = f") = test { + assertThrows[MatchError] { + val q"f(..$argss)" = 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(...xss)") = forAll { (x1: Tree, x2: Tree) => + val q"f(...$argss)" = q"f($x1)($x2)" + argss ≈ List(List(x1), List(x2)) + } + + property("f(...xss) = f") = forAll { (x1: Tree, x2: Tree) => + val q"f(...$argss)" = q"f" + argss ≈ List() + } + + property("@$annot def foo") = forAll { (annotName: TypeName) => + val q"@$annot def foo" = q"@$annotName def foo" + annot ≈ Apply(Select(New(Ident(annotName)), nme.CONSTRUCTOR), List()) + } + + property("@$annot(..$args) def foo") = forAll { (annotName: TypeName, tree: Tree) => + val q"@$annot(..$args) def foo" = q"@$annotName($tree) def foo" + annot ≈ Ident(annotName) && args ≈ List(tree) + } + + property("@..$annots def foo") = test { + val a = annot("a") + val b = annot("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 q"@$first @..$rest def foo" = q"@$a @$b @$c def foo" + first ≈ a && rest ≈ List(b, c) + } + + property("class without params") = test { + val q"class $name { ..$body }" = q"class Foo { def bar = 3 }" + assert(body ≈ List(q"def bar = 3")) + } + + property("class constructor") = test { + val q"class $name(...$argss)" = q"class Foo(x: Int)(y: Int)" + assert(argss.length == 2) + } + + property("class parents") = test { + val q"class $name extends ..$parents" = q"class Foo extends Bar with Blah" + assert(parents ≈ List(tq"Bar", tq"Blah")) + } + + property("class selfdef") = test { + val q"class $name { $self => }" = q"class Foo { self: T => }" + assert(self.name ≈ TermName("self") && self.tpt ≈ tq"T") + } + + property("class tparams") = test { + val q"class $name[..$tparams]" = q"class Foo[A, B]" + assert(tparams.map { _.name } == List(TypeName("A"), TypeName("B"))) + } + + 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" && rest ≈ List(q"b", q"c")) + } + + 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 =>") + } + + property("deconstruct mods") = test { + val mods = Modifiers(IMPLICIT | PRIVATE, TermName("foobar"), Nil) + val q"$mods0 def foo" = q"$mods def foo" + assert(mods0 ≈ mods) + } +} \ No newline at end of file diff --git a/test/files/scalacheck/quasiquotes/Test.scala b/test/files/scalacheck/quasiquotes/Test.scala new file mode 100644 index 0000000000..2387a9b008 --- /dev/null +++ b/test/files/scalacheck/quasiquotes/Test.scala @@ -0,0 +1,12 @@ +import org.scalacheck._ + +object Test extends Properties("quasiquotes") { + include(TermConstructionProps) + include(TermDeconstructionProps) + include(TypeConstructionProps) + include(TypeDeconstructionProps) + include(PatternConstructionProps) + include(PatternDeconstructionProps) + include(LiftableProps) + include(ErrorProps) +} \ No newline at end of file diff --git a/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala b/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala new file mode 100644 index 0000000000..535ed8ecbf --- /dev/null +++ b/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala @@ -0,0 +1,25 @@ +import org.scalacheck._ +import Prop._ +import Gen._ +import Arbitrary._ + +import scala.reflect.runtime.universe._ +import Flag._ + +object TypeConstructionProps extends QuasiquoteProperties("type construction") { + property("bare idents contain type names") = test { + tq"x" ≈ Ident(TypeName("x")) + } + + property("splice type names into AppliedTypeTree") = forAll { (name1: TypeName, name2: TypeName) => + tq"$name1[$name2]" ≈ AppliedTypeTree(Ident(name1), List(Ident(name2))) + } + + property("tuple type") = test { + val empty = List[Tree]() + val ts = List(tq"t1", tq"t2") + assert(tq"(..$empty)" ≈ tq"scala.Unit") + assert(tq"(..$ts)" ≈ tq"Tuple2[t1, t2]") + assert(tq"(t0, ..$ts)" ≈ tq"Tuple3[t0, t1, t2]") + } +} \ No newline at end of file diff --git a/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala new file mode 100644 index 0000000000..6ab699d4f0 --- /dev/null +++ b/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala @@ -0,0 +1,29 @@ +import org.scalacheck._ +import Prop._ +import Gen._ +import Arbitrary._ + +import scala.reflect.runtime.universe._ +import Flag._ + +object TypeDeconstructionProps extends QuasiquoteProperties("type deconstruction") { + property("ident(type name)") = forAll { (name: TypeName) => + val t = Ident(name) + val tq"$t1" = t + t1 ≈ t + } + + property("applied type tree") = forAll { (name1: TypeName, name2: TypeName) => + val tq"$a[$b]" = AppliedTypeTree(Ident(name1), List(Ident(name2))) + a ≈ Ident(name1) && b ≈ Ident(name2) + } + + property("tuple type") = test { + val tq"(..$empty)" = tq"scala.Unit" + assert(empty.isEmpty) + val tq"(..$ts)" = tq"(t1, t2)" + assert(ts ≈ List(tq"t1", tq"t2")) + val tq"($head, ..$tail)" = tq"(t0, t1, t2)" + assert(head ≈ tq"t0" && tail ≈ List(tq"t1", tq"t2")) + } +} \ No newline at end of file -- cgit v1.2.3