diff options
Diffstat (limited to 'test/scalacheck/scala/reflect')
17 files changed, 2938 insertions, 0 deletions
diff --git a/test/scalacheck/scala/reflect/quasiquotes/ArbitraryTreesAndNames.scala b/test/scalacheck/scala/reflect/quasiquotes/ArbitraryTreesAndNames.scala new file mode 100644 index 0000000000..2f2be70403 --- /dev/null +++ b/test/scalacheck/scala/reflect/quasiquotes/ArbitraryTreesAndNames.scala @@ -0,0 +1,297 @@ +package scala.reflect.quasiquotes + +import org.scalacheck._, Prop._, Gen._, Arbitrary._ +import scala.reflect.runtime.universe._, internal._, 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 <- genTermName; + 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, oneOf(genValDef(size - 1), genTypeDef(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: Gen[Tree], 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 specifically on Trees that are terms or types. + * They are transparently transformed 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 val liftTreeIsTerm = Liftable[TreeIsTerm] { _.tree } + implicit val liftTreeIsType = Liftable[TreeIsType] { _.tree } + implicit def treeIsTerm2tree(tit: TreeIsTerm): Tree = tit.tree + implicit def treeIsType2tree(tit: TreeIsType): Tree = 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/scalacheck/scala/reflect/quasiquotes/DefinitionConstructionProps.scala b/test/scalacheck/scala/reflect/quasiquotes/DefinitionConstructionProps.scala new file mode 100644 index 0000000000..9d35c9229d --- /dev/null +++ b/test/scalacheck/scala/reflect/quasiquotes/DefinitionConstructionProps.scala @@ -0,0 +1,455 @@ +package scala.reflect.quasiquotes + +import org.scalacheck._, Prop._, Gen._, Arbitrary._ +import scala.reflect.runtime.universe._, Flag._, internal.reificationSupport.ScalaDot + +object DefinitionConstructionProps + extends QuasiquoteProperties("definition construction") + with ClassConstruction + with TraitConstruction + with TypeDefConstruction + with ValDefConstruction + with PatDefConstruction + with DefConstruction + with PackageConstruction + with ImportConstruction { + + val x: Tree = q"val x: Int" + property("SI-6842 a1") = test { assertEqAst(q"def f($x) = 0", "def f(x: Int) = 0") } + property("SI-6842 a2") = test { assertEqAst(q"class C($x)", "class C(val x: Int)") } + property("SI-6842 a3") = test { assertEqAst(q"class C { $x => }", "class C { x: Int => }") } + property("SI-6842 a4") = test { assertEqAst(q"trait B { $x => }", "trait B { x: Int => }") } + property("SI-6842 a5") = test { assertEqAst(q"object A { $x => }", "object A { x: Int => }") } + + val t: Tree = q"type T" + property("SI-6842 b1") = test { assertEqAst(q"def f[$t] = 0", "def f[T] = 0") } + property("SI-6842 b2") = test { assertEqAst(q"class C[$t]", "class C[T]") } + property("SI-6842 b3") = test { assertEqAst(q"trait B[$t]", "trait B[T]") } +} + +trait ClassConstruction { self: QuasiquoteProperties => + val anyRef = ScalaDot(TypeName("AnyRef")) + val emtpyConstructor = + DefDef(Modifiers(), termNames.CONSTRUCTOR, List(), + List(List()), TypeTree(), Block(List(pendingSuperCall), Literal(Constant(())))) + def classWith(name: TypeName, parents: List[Tree] = List(anyRef), body: List[DefDef] = Nil) = + ClassDef( + Modifiers(), name, List(), + Template(parents, emptyValDef, emtpyConstructor :: body)) + + property("construct case class") = test { + val params = q"val x: Int" :: q"val y: Int" :: Nil + val name = TypeName("Point") + assertEqAst(q"$CASE class $name(..$params)", "case class Point(x: Int, y: Int)") + } + + property("case class bare param") = test { + assertEqAst(q"$CASE class Point(x: Int, y: Int)", "case class Point(private[this] val x: Int, private[this] val y: Int)") + } + + property("generate default constructors automatically") = test { + val parents = List.empty[Tree] + assertEqAst(q"class Foo extends ..$parents", "class Foo") + } + + property("unquote term name into class") = forAll { (rname: TypeName) => + // add prefix to avoid failure in case rname is keyword + val name = TypeName("prefix$" + rname) + eqAst(q"class $name", "class " + name.toString) + } + + property("unquote method into class") = forAll { (name: TypeName, method: DefDef) => + q"class $name { $method }" ≈ classWith(name, body = List(method)) + } + + property("unquote members into class") = forAll { (name: TypeName, defs: List[DefDef], extra: DefDef) => + q"""class $name { + ..$defs + $extra + }""" ≈ classWith(name, body = defs :+ extra) + } + + property("unquote type name into class parents") = forAll { (name: TypeName, parent: TypeName) => + q"class $name extends $parent" ≈ classWith(name, parents = List(Ident(parent))) + } + + property("param flags are consistent with raw code") = test { + val pubx = q"val x: Int" + val privx = q"private[this] val x: Int" + assertEqAst(q" class C(x: Int)", " class C(x: Int) ") + assertEqAst(q"case class C(x: Int)", "case class C(x: Int) ") + assertEqAst(q" class C($pubx) ", " class C(val x: Int) ") + assertEqAst(q"case class C($pubx) ", "case class C(x: Int) ") + assertEqAst(q" class C($privx)", " class C(x: Int) ") + assertEqAst(q"case class C($privx)", "case class C(private[this] val x: Int)") + } + + property("SI-8333") = test { + assertEqAst(q"{ $NoMods class C }", "{ class C }") + } + + property("SI-8332") = test { + val args = q"val a: Int; val b: Int" + assertEqAst(q"class C(implicit ..$args)", "class C(implicit val a: Int, val b: Int)") + } + + property("SI-8451: inline secondary constructors") = test { + assertEqAst(q"class C(x: Int) { def this() = this(0) }", "class C(x: Int) { def this() = this(0) }") + } + + property("SI-8451: unquoted secondary constructors") = test { + val secondaryCtor = q"def this() = this(0)" + assertEqAst(q"class C(x: Int) { $secondaryCtor }", "class C(x: Int) { def this() = this(0) }") + } +} + +trait TraitConstruction { self: QuasiquoteProperties => + property("unquote name into trait def") = test { + val Foo = TypeName("Foo") + assert(q"trait $Foo" ≈ q"trait Foo") + } + + property("unquote type params into trait def") = test { + val tparams = q"type A" :: q"type B" :: Nil + assert(q"trait Foo[..$tparams]" ≈ q"trait Foo[A, B]") + } + + property("unquote defs into trait body") = test { + val body = q"def foo" :: q"val bar: Baz" :: Nil + assert(q"trait Foo { ..$body }" ≈ q"trait Foo { def foo; val bar: Baz }") + } + + property("unquote parents into trait") = test { + val parents = tq"A" :: tq"B" :: Nil + assert(q"trait Foo extends ..$parents" ≈ q"trait Foo extends A with B") + } + + property("unquote early valdef into trait") = test { + val x = q"val x: Int = 1" + assertEqAst(q"trait T extends { $x } with Any", "trait T extends { val x: Int = 1} with Any") + } + + property("construct trait with early valdef") = test { + assertEqAst(q"trait T extends { val x: Int = 1 } with Any", "trait T extends { val x: Int = 1 } with Any") + } + + property("unquote defs into early block") = test { + val defs = q"val x: Int = 0" :: q"type Foo = Bar" :: Nil + assert(q"trait T extends { ..$defs } with Bippy" ≈ + q"trait T extends { val x: Int = 0; type Foo = Bar} with Bippy") + } + + property("fail on splicing of non-valid early tree") = test { + val defn = q"def x: Int = 0" + assertThrows[IllegalArgumentException] { q"trait T extends { $defn } with Bar" } + } +} + +trait TypeDefConstruction { self: QuasiquoteProperties => + property("unquote type name into typedef") = forAll { (name1: TypeName, name2: TypeName) => + q"type $name1 = $name2" ≈ TypeDef(Modifiers(), name1, List(), Ident(name2)) + } + + property("unquote 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("unquote 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("unquote tparams into typedef (1)") = forAll { (T: TypeName, targs: List[TypeDef], t: Tree) => + q"type $T[..$targs] = $t" ≈ TypeDef(Modifiers(), T, targs, t) + } + + property("unquote 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("unquote 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("unquote 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("unquote 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), termNames.WILDCARD, TypeTree(), EmptyTree), List()))) + } + + property("unquote 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("unquote tree into singleton type tree") = forAll { (name: TypeName, t: Tree) => + q"type $name = $t.type" ≈ q"type $name = ${SingletonTypeTree(t)}" + } + + property("unquote into applied type tree") = forAll { (T1: TypeName, T2: TypeName, args: List[Tree]) => + q"type $T1 = $T2[..$args]" ≈ + TypeDef(Modifiers(), T1, List(), + if(args.nonEmpty) AppliedTypeTree(Ident(T2), args) else Ident(T2)) + } +} + +trait ValDefConstruction { self: QuasiquoteProperties => + property("unquote into val") = forAll { (name: TermName, tpt: Tree, rhs: Tree) => + q"val $name: $tpt = $rhs" ≈ ValDef(Modifiers(), name, tpt, rhs) + } + + property("unquote into var") = forAll { (name: TermName, tpt: Tree, rhs: Tree) => + q"var $name: $tpt = $rhs" ≈ ValDef(Modifiers(MUTABLE), name, tpt, rhs) + } + + // left tree is not a pattern due to Si-8211 + property("SI-8202") = test { + assertEqAst(q"val (x: Int) = 1", "val x: Int = 1") + } +} + +trait PatDefConstruction { self: QuasiquoteProperties => + property("unquote pattern into pat def") = test { + val pat = pq"(a, b)" + assertEqAst(q"val $pat = (1, 2)", "val (a, b) = (1, 2)") + val tpt = tq"(Int, Int)" + assertEqAst(q"val $pat: $tpt = (1, 2)", "val (a, b): (Int, Int) = (1, 2)") + } + + property("unquote pattern into pat def within other pattern (1)") = test { + val pat = pq"(a, b)" + assertEqAst(q"val Foo($pat) = Foo((1, 2))", "val Foo((a, b)) = Foo((1, 2))") + val tpt = tq"Foo" + assertEqAst(q"val Foo($pat): $tpt = Foo((1, 2))", "val Foo((a, b)): Foo = Foo((1, 2))") + } + + property("unquote patterns into pat def within other pattern (2)") = test { + val pat1 = pq"(a, b)"; val pat2 = pq"(c, d)" + assertEqAst(q"val ($pat1, $pat2) = ((1, 2), (3, 4))", "val ((a, b), (c, d)) = ((1, 2), (3, 4))") + val tpt = tq"((Int, Int), (Int, Int))" + assertEqAst(q"val ($pat1, $pat2): $tpt = ((1, 2), (3, 4))", "val ((a, b), (c, d)): ((Int, Int), (Int, Int)) = ((1, 2), (3, 4))") + } + + property("unquote pattern without free vars into pat def") = test { + val pat = pq"((1, 2), 3)" + assertEqAst(q"val $pat = ((1, 2), 3)", "{ val ((1, 2), 3) = ((1, 2), 3) }") + val tpt = tq"((Int, Int), Int)" + assertEqAst(q"val $pat: $tpt = ((1, 2), 3)","{ val ((1, 2), 3): ((Int, Int), Int) = ((1, 2), 3) }") + } + + // won't result into pattern match due to SI-8211 + property("unquote typed pat into pat def") = test { + val pat = pq"x: Int" + assertEqAst(q"val $pat = 2", "{ val x: Int = 2 }") + } +} + +trait MethodConstruction { self: QuasiquoteProperties => + property("unquote paramss into defdef") = test { + val paramss = List(q"val x: Int") :: List(q"val y: Int = 1") :: Nil + assert(q"def foo(...$paramss)" ≈ parse("def foo(x: Int)(y: Int = 1)")) + } + + property("unquote tparams into defdef") = test { + val tparams = q"type A" :: q"type B <: Bippy" :: Nil + assert(q"def foo[..$tparams]" ≈ parse("def foo[A, B <: Bippy]")) + } + + 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("unquote type name into annotation") = test { + val name = TypeName("annot") + assertSameAnnots(q"@$name def foo", List(q"new $name")) + } + + property("unquote ident into annotation") = test { + val name = TypeName("annot") + val ident = Ident(name) + assertSameAnnots(q"@$ident def foo", List(q"new $name")) + } + + property("unquote 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), termNames.CONSTRUCTOR), List()) }) + } + + property("unquote constructor calls into annotation") = test { + val ctorcalls = List(q"new a1", q"new a2") + assertSameAnnots(q"@..$ctorcalls def foo", ctorcalls) + } + + property("unquote multiple annotations (1)") = test { + val annot1 = q"new a1" + val annot2 = q"new a2" + val res = q"@$annot1 @$annot2 def foo" + assertSameAnnots(res, List(annot1, annot2)) + } + + property("unquote multiple annotations (2)") = test { + 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("unquote annotations with arguments (1)") = test { + val a = q"new a(x)" + assertSameAnnots(q"@$a def foo", q"@a(x) def foo") + } + + property("unquote annotations with arguments (2)") = test { + val a = TypeName("a") + assertSameAnnots(q"@$a(x) def foo", q"@a(x) def foo") + } + + property("unquote annotations with arguments (3") = test { + val a = Ident(TypeName("a")) + assertSameAnnots(q"@$a(x) def foo", q"@a(x) def foo") + } + + property("unquote improper tree into annot") = test { + val t = tq"Foo[Baz]" + assertThrows[IllegalArgumentException] { + q"@$t def foo" + } + } + + property("can't unquote annotations with arguments specified twice") = test { + val a = q"new a(x)" + assertThrows[IllegalArgumentException] { + q"@$a(y) def foo" + } + } + + property("unquote annotation with targs") = test { + val a = q"new Foo[A, B]" + assertEqAst(q"@$a def foo", "@Foo[A,B] def foo") + } + + property("unquote annotation with multiple argument lists") = test { + val a = q"new Foo(a)(b)" + assertEqAst(q"@$a def foo", "@Foo(a)(b) def foo") + } +} + +trait PackageConstruction { self: QuasiquoteProperties => + property("unquote select into package name") = test { + val name = q"foo.bar" + assertEqAst(q"package $name { }", "package foo.bar { }") + } + + property("splice name into package name") = test{ + val name = TermName("bippy") + assertEqAst(q"package $name { }", "package bippy { }") + } + + property("unquote members into package body") = test { + val members = q"class C" :: q"object O" :: Nil + assertEqAst(q"package foo { ..$members }", "package foo { class C; object O }") + } + + property("unquote illegal members into package body") = test { + val f = q"def f" + assertThrows[IllegalArgumentException] { q"package foo { $f }" } + val v = q"val v = 0" + assertThrows[IllegalArgumentException] { q"package foo { $v }" } + val expr = q"x + 1" + assertThrows[IllegalArgumentException] { q"package foo { $expr }" } + } + + property("unquote name into package object") = test { + val foo = TermName("foo") + assertEqAst(q"package object $foo", "package object foo") + } + + property("unquote parents into package object") = test { + val parents = tq"a" :: tq"b" :: Nil + assertEqAst(q"package object foo extends ..$parents", + "package object foo extends a with b") + } + + property("unquote members into package object") = test { + val members = q"def foo" :: q"val x = 1" :: Nil + assertEqAst(q"package object foo { ..$members }", + "package object foo { def foo; val x = 1 }") + } + + property("unquote early def into package object") = test { + val edefs = q"val x = 1" :: q"type I = Int" :: Nil + assertEqAst(q"package object foo extends { ..$edefs } with Any", + "package object foo extends { val x = 1; type I = Int } with Any") + } +} + +trait DefConstruction { self: QuasiquoteProperties => + property("construct implicit args (1)") = test { + val x = q"val x: Int" + assertEqAst(q"def foo(implicit $x) = x", "def foo(implicit x: Int) = x") + } + + property("construct implicit args (2)") = test { + val xs = q"val x1: Int" :: q"val x2: Long" :: Nil + assertEqAst(q"def foo(implicit ..$xs) = x1 + x2", "def foo(implicit x1: Int, x2: Long) = x1 + x2") + } +} + +trait ImportConstruction { self: QuasiquoteProperties => + property("construct wildcard import") = test { + val sel = pq"_" + assert(q"import foo.$sel" ≈ q"import foo._") + } + + property("construct named import") = test { + val sel = pq"bar" + assert(q"import foo.$sel" ≈ q"import foo.bar") + } + + property("construct renaming import") = test { + val sel = pq"bar -> baz" + assert(q"import foo.$sel" ≈ q"import foo.{bar => baz}") + } + + property("construct unimport import") = test { + val sels = pq"poison -> _" :: pq"_" :: Nil + assert(q"import foo.{..$sels}" ≈ q"import foo.{poison => _, _}") + } + + property("construct mixed import") = test { + val sels = pq"a -> b" :: pq"c -> _" :: pq"_" :: Nil + assert(q"import foo.{..$sels}" ≈ q"import foo.{a => b, c => _, _}") + } +} diff --git a/test/scalacheck/scala/reflect/quasiquotes/DefinitionDeconstructionProps.scala b/test/scalacheck/scala/reflect/quasiquotes/DefinitionDeconstructionProps.scala new file mode 100644 index 0000000000..54ec966836 --- /dev/null +++ b/test/scalacheck/scala/reflect/quasiquotes/DefinitionDeconstructionProps.scala @@ -0,0 +1,292 @@ +package scala.reflect.quasiquotes + +import org.scalacheck._, Prop._, Gen._, Arbitrary._ +import scala.reflect.runtime.universe._, Flag._, internal.reificationSupport.SyntacticClassDef + +object DefinitionDeconstructionProps + extends QuasiquoteProperties("definition deconstruction") + with TraitDeconstruction + with ClassDeconstruction + with ObjectDeconstruction + with ModsDeconstruction + with ValVarDeconstruction + with DefDeconstruction + with PackageDeconstruction + with ImportDeconstruction + +trait TraitDeconstruction { self: QuasiquoteProperties => + property("exhaustive trait matcher") = test { + def matches(line: String) { + val q"""$mods trait $name[..$targs] + extends { ..$early } with ..$parents { $self => ..$body }""" = parse(line) + } + matches("trait Foo") + matches("trait Foo[T]") + matches("trait Foo { def bar }") + matches("trait Foo extends Bar with Baz") + matches("trait Foo { self: Bippy => val x: Int = 1}") + matches("trait Foo extends { val early: Int = 1 } with Bar { val late = early }") + matches("private[Gap] trait Foo") + } +} + +trait ObjectDeconstruction { self: QuasiquoteProperties => + property("exhaustive object matcher") = test { + def matches(line: String) = { + val q"""$mods object $name extends { ..$early } with ..$parents { $self => ..$body }""" = parse(line) + } + matches("object Foo") + matches("object Foo extends Bar[T]") + matches("object Foo extends { val early: T = v } with Bar") + matches("object Foo extends Foo { selfy => body }") + matches("private[Bippy] object Foo extends Bar with Baz") + } +} + +trait ClassDeconstruction { self: QuasiquoteProperties => + 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 bare case class") = test { + val q"$mods class $name(..$args) extends ..$parents" = q"case class Foo(x: Int)" + } + + property("exhaustive class matcher") = test { + def matches(line: String) { + val tree = parse(line) + val q"""$classMods0 class $name0[..$targs0] $ctorMods0(...$argss0) + extends { ..$early0 } with ..$parents0 { $self0 => ..$body0 }""" = tree + val q"""$classMods1 class $name1[..$targs1] $ctorMods1(...$argss1)(implicit ..$impl) + extends { ..$early1 } with ..$parents1 { $self1 => ..$body1 }""" = tree + } + matches("class Foo") + matches("class Foo[T]") + matches("class Foo[T] @annot") + matches("class Foo extends Bar with Baz") + matches("class Foo { body }") + matches("class Foo extends { val early = 0 } with Any") + matches("abstract class Foo") + matches("private[Baz] class Foo") + matches("class Foo(first: A)(second: B)") + matches("class Foo(first: A) extends Bar(first) with Baz") + matches("class Foo private (first: A) { def bar }") + matches("class Foo { self => bar(self) }") + matches("case class Foo(x: Int)") + } + + property("SI-7979") = test { + val PARAMACCESSOR = (1 << 29).toLong.asInstanceOf[FlagSet] + assertThrows[MatchError] { + val SyntacticClassDef(_, _, _, _, _, _, _, _, _) = + ClassDef( + Modifiers(), TypeName("Foo"), List(), + Template( + List(Select(Ident(TermName("scala")), TypeName("AnyRef"))), + noSelfType, + List( + //ValDef(Modifiers(PRIVATE | LOCAL | PARAMACCESSOR), TermName("x"), Ident(TypeName("Int")), EmptyTree), + DefDef(Modifiers(), termNames.CONSTRUCTOR, List(), List(List(ValDef(Modifiers(PARAM | PARAMACCESSOR), TermName("x"), + Ident(TypeName("Int")), EmptyTree))), TypeTree(), Block(List(pendingSuperCall), Literal(Constant(()))))))) + } + } + + property("SI-8332") = test { + val q"class C(implicit ..$args)" = q"class C(implicit i: I, j: J)" + val q"$imods val i: I" :: q"$jmods val j: J" :: Nil = args + assert(imods.hasFlag(IMPLICIT)) + assert(jmods.hasFlag(IMPLICIT)) + } +} + +trait ModsDeconstruction { self: QuasiquoteProperties => + property("deconstruct mods") = test { + val mods = Modifiers(IMPLICIT | PRIVATE, TermName("foobar"), Nil) + val q"$mods0 def foo" = q"$mods def foo" + assert(mods0 ≈ mods) + } + + property("@$annot def foo") = forAll { (annotName: TypeName) => + val q"@$annot def foo" = q"@$annotName def foo" + annot ≈ Apply(Select(New(Ident(annotName)), termNames.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 = 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 = 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" + 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) + } +} + +trait ValVarDeconstruction { self: QuasiquoteProperties => + property("exhaustive val matcher") = test { + def matches(line: String) { val q"$mods val $name: $tpt = $rhs" = parse(line) } + matches("val x: Int") + matches("val x: Int = 1") + matches("lazy val x: Int = 1") + matches("implicit val x = 1") + assertThrows[MatchError] { matches("var x = 1") } + } + + property("exhaustive var matcher") = test { + def matches(line: String) { val q"$mods var $name: $tpt = $rhs" = parse(line) } + matches("var x: Int") + matches("var x: Int = 1") + matches("var x = 1") + assertThrows[MatchError] { matches("val x = 1") } + } +} + +trait PackageDeconstruction { self: QuasiquoteProperties => + property("exhaustive package matcher") = test { + def matches(line: String) { val q"package $name { ..$body }" = parse(line) } + matches("package foo { }") + matches("package foo { class C }") + matches("package foo.bar { }") + matches("package bippy.bongo { object A; object B }") + matches("package bippy { package bongo { object O } }") + } + + property("exhaustive package object matcher") = test { + def matches(line: String) { + val q"package object $name extends { ..$early } with ..$parents { $self => ..$body }" = parse(line) + } + matches("package object foo") + matches("package object foo { def baz }") + matches("package object foo { self => }") + matches("package object foo extends mammy with daddy { def baz }") + matches("package object foo extends { val early = 1 } with daddy") + assertThrows[MatchError] { matches("object foo") } + } +} + +trait DefDeconstruction { self: QuasiquoteProperties => + property("exhaustive def matcher") = test { + def matches(line: String) = { + val t = parse(line) + val q"$mods0 def $name0[..$targs0](...$argss0): $restpe0 = $body0" = t + val q"$mods1 def $name1[..$targs1](...$argss1)(implicit ..$impl1): $restpe1 = $body1" = t + } + matches("def foo = foo") + matches("implicit def foo: Int = 2") + matches("def foo[T](x: T): T = x") + matches("def foo[A: B] = implicitly[B[A]]") + matches("private def foo = 0") + matches("def foo[A <% B] = null") + matches("def foo(one: One)(two: Two) = (one, two)") + matches("def foo[T](args: T*) = args.toList") + } + + property("extract implicit arg list (1)") = test { + val q"def foo(...$argss)(implicit ..$impl)" = q"def foo(x: Int)(implicit y: Int)" + assert(impl ≈ List(q"${Modifiers(IMPLICIT | PARAM)} val y: Int")) + } + + property("extract implicit arg list (2)") = test { + val q"def foo(...$argss)(implicit ..$impl)" = q"def foo(x: Int)" + assert(impl.isEmpty) + } + + property("SI-8451") = test { + val q"def this(..$params) = this(..$args)" = q"def this(x: Int) = this(0)" + assert(params ≈ List(q"${Modifiers(PARAM)} val x: Int")) + assert(args ≈ List(q"0")) + } +} + +trait ImportDeconstruction { self: QuasiquoteProperties => + property("exhaustive import matcher") = test { + def matches(line: String) = { + val q"import $ref.{..$sels}" = parse(line) + } + matches("import foo.bar") + matches("import foo.{bar, baz}") + matches("import foo.{a => b, c => d}") + matches("import foo.{poision => _, _}") + matches("import foo.bar.baz._") + } + + property("extract import binding") = test { + val q"import $_.$sel" = q"import foo.bar" + val pq"bar" = sel + } + + property("extract import wildcard") = test { + val q"import $_.$sel" = q"import foo._" + val pq"_" = sel + } + + property("extract import rename") = test { + val q"import $_.$sel" = q"import foo.{bar => baz}" + val pq"bar -> baz" = sel + val pq"$left -> $right" = sel + val pq"bar" = left + val pq"baz" = right + } + + property("extract import unimport") = test { + val q"import $_.$sel" = q"import foo.{bar => _}" + val pq"bar -> _" = sel + val pq"$left -> $right" = sel + val pq"bar" = left + val pq"_" = right + } + + property("unquote names into import selector") = forAll { + (expr: Tree, plain: TermName, oldname: TermName, newname: TermName, discard: TermName) => + + 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 == termNames.WILDCARD + } +} diff --git a/test/scalacheck/scala/reflect/quasiquotes/DeprecationProps.scala b/test/scalacheck/scala/reflect/quasiquotes/DeprecationProps.scala new file mode 100644 index 0000000000..9662586aef --- /dev/null +++ b/test/scalacheck/scala/reflect/quasiquotes/DeprecationProps.scala @@ -0,0 +1,54 @@ +package scala.reflect.quasiquotes + +import org.scalacheck._, Prop._, Gen._, Arbitrary._ +import scala.reflect.runtime.universe._ + +object DeprecationProps extends QuasiquoteProperties("deprecation") { + val tname = TypeName("Foo") + val tpt = tq"Foo" + val tpe = typeOf[Int] + val sym = tpe.typeSymbol.asType + val argss = List(q"x") :: List(q"y") :: Nil + val args = q"x" :: q"y" :: Nil + + property("new tpt argss") = test { + assert(q"new $tpt(...$argss)" ≈ New(tpt, argss)) + } + + property("new tpe args") = test { + assert(q"new $tpe(..$args)" ≈ New(tpe, args: _*)) + } + + property("new tpe args") = test { + assert(q"new ${sym.toType}(..$args)" ≈ New(sym, args: _*)) + } + + property("apply sym args") = test { + assert(q"$sym(..$args)" ≈ Apply(sym, args: _*)) + } + + property("applyconstructor") = test { + assert(q"new $tpt(..$args)" ≈ ApplyConstructor(tpt, args)) + } + + property("super sym name") = test { + assert(q"$sym.super[$tname].x".qualifier ≈ Super(sym, tname)) + } + + property("throw tpe args") = test { + assert(q"throw new $tpe(..$args)" ≈ Throw(tpe, args: _*)) + } + + property("casedef pat body") = test { + val pat = pq"foo" + val body = q"bar" + assert(cq"$pat => $body" ≈ CaseDef(pat, body)) + } + + property("try body cases") = test { + val cases = (pq"a", q"b") :: (pq"c", q"d") :: Nil + val newcases = cases.map { case (pat, body) => cq"$pat => $body" } + val body = q"foo" + assert(q"try $body catch { case ..$newcases }" ≈ Try(body, cases: _*)) + } +}
\ No newline at end of file diff --git a/test/scalacheck/scala/reflect/quasiquotes/ErrorProps.scala b/test/scalacheck/scala/reflect/quasiquotes/ErrorProps.scala new file mode 100644 index 0000000000..4f1c61eeff --- /dev/null +++ b/test/scalacheck/scala/reflect/quasiquotes/ErrorProps.scala @@ -0,0 +1,215 @@ +package scala.reflect.quasiquotes + +import org.scalacheck._, Prop._, Gen._, Arbitrary._ + +object ErrorProps extends QuasiquoteProperties("errors") { + property("can't extract two .. rankinalities 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 unquote with given rank") = fails( + "Can't unquote List[StringBuilder], consider using .. or providing an implicit instance of Liftable[List[StringBuilder]]", + """ + import java.lang.StringBuilder + val xs: List[StringBuilder] = Nil + q"$xs" + """) + + property("unquote 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 unquote annotations with ... rank") = fails( + "Can't unquote with ... here", + """ + val annots = List(List(q"Foo")) + q"@...$annots 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 unquote inside of strings") = fails( + "Don't know how to unquote here", + """ + val x: Tree = EmptyTree + StringContext("\"", "\"").q(x) + """) + + property("non-liftable type ..") = fails( + "Can't unquote 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 unquote 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 .. rank or provide liftable") = fails( + "Can't unquote 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 ... rank or provide liftable") = fails( + "Can't unquote List[List[StringBuilder]], consider using ... or providing an implicit instance of Liftable[List[List[StringBuilder]]]", + """ + import java.lang.StringBuilder + val xs: List[List[StringBuilder]] = Nil + q"$xs" + """) + + property("not liftable or natively supported") = fails( + "Can't unquote StringBuilder, consider providing an implicit instance of Liftable[StringBuilder]", + """ + import java.lang.StringBuilder + val sb = new StringBuilder + q"f($sb)" + """) + + property("can't unquote with ... rank here") = fails( + "Can't unquote 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 unquote flags together with mods") = fails( + "Can't unquote flags together with modifiers, consider merging flags into modifiers", + """ + val f = Flag.IMPLICIT; val m = NoMods + q"$f $m def foo" + """) + + property("can't unquote mods with annots") = fails( + "Can't unquote modifiers together with annotations, consider merging annotations into modifiers", + """ + val m = NoMods + q"@annot $m def foo" + """) + + property("can't unquote modifiers with inline flags") = fails( + "Can't unquote modifiers together with flags, consider merging flags into modifiers", + """ + val m = NoMods + q"$m implicit def foo" + """) + + property("can't unquote multiple mods") = fails( + "Can't unquote 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 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 unquote values of Null") = fails( + "Can't unquote Null, bottom type values often indicate programmer mistake", + """ + val n = null + q"$n" + """) + + property("can't unquote values of Nothing") = fails( + "Can't unquote Nothing, bottom type values often indicate programmer mistake", + """ + def n = ??? + q"$n" + """) + + property("SI-8211: check unbound placeholder parameters") = fails( + "unbound placeholder parameter", + """ + q"_" + """) + + property("SI-8211: check unbound wildcard types") = fails( + "unbound wildcard type", + """ + tq"_" + """) + + property("SI-8420: don't crash on splicing of non-unliftable native type (1)") = fails( + "Can't unquote List[reflect.runtime.universe.Symbol] with .., consider omitting the dots or providing an implicit instance of Liftable[reflect.runtime.universe.Symbol]", + """ + val l: List[Symbol] = Nil + q"f(..$l)" + """) + + property("SI-8420: don't crash on splicing of non-unliftable native type (2)") = fails( + "Can't unquote List[reflect.runtime.universe.FlagSet] with .., consider omitting the dots or providing an implicit instance of Liftable[reflect.runtime.universe.FlagSet]", + """ + val l: List[FlagSet] = Nil + q"f(..$l)" + """) + + property("SI-8420: don't crash on splicing of non-unliftable native type (3)") = fails( + "Can't unquote List[reflect.runtime.universe.Modifiers] with .., consider omitting the dots or providing an implicit instance of Liftable[reflect.runtime.universe.Modifiers]", + """ + val l: List[Modifiers] = Nil + q"f(..$l)" + """) + + property("SI-8451 construction: disallow everything except for constructor calls in secondary constructor bodies") = fails( + "'this' expected but unquotee found", + """ + val rhs1 = q"this(0)" + val ctor1 = q"def this(x: Int) = $rhs1" + """) + + property("SI-8451 deconstruction: disallow everything except for constructor calls in secondary constructor bodies") = fails( + "'this' expected but unquotee found", + """ + val q"def this(..$params) = $rhs2" = q"def this(x: Int) = this(0)" + """) + + // // Make sure a nice error is reported in this case + // { import Flag._; val mods = NoMods; q"lazy $mods val x: Int" } +} diff --git a/test/scalacheck/scala/reflect/quasiquotes/ForProps.scala b/test/scalacheck/scala/reflect/quasiquotes/ForProps.scala new file mode 100644 index 0000000000..d19ead8792 --- /dev/null +++ b/test/scalacheck/scala/reflect/quasiquotes/ForProps.scala @@ -0,0 +1,72 @@ +package scala.reflect.quasiquotes + +import org.scalacheck._, Prop._, Gen._, Arbitrary._ +import scala.reflect.runtime.universe._, Flag._, internal.reificationSupport._ + +object ForProps extends QuasiquoteProperties("for") { + case class ForEnums(val value: List[Tree]) + + def genSimpleBind: Gen[Bind] = + for(name <- genTermName) + yield pq"$name @ _" + + def genForFilter: Gen[Tree] = + for(cond <- genIdent(genTermName)) + yield fq"if $cond" + + def genForFrom: Gen[Tree] = + for(lhs <- genSimpleBind; rhs <- genIdent(genTermName)) + yield fq"$lhs <- $rhs" + + def genForEq: Gen[Tree] = + for(lhs <- genSimpleBind; rhs <- genIdent(genTermName)) + yield fq"$lhs = $rhs" + + def genForEnums(size: Int): Gen[ForEnums] = + for(first <- genForFrom; rest <- listOfN(size, oneOf(genForFrom, genForFilter, genForEq))) + yield new ForEnums(first :: rest) + + implicit val arbForEnums: Arbitrary[ForEnums] = arbitrarySized(genForEnums) + + property("construct-reconstruct for") = forAll { (enums: ForEnums, body: Tree) => + val SyntacticFor(recoveredEnums, recoveredBody) = SyntacticFor(enums.value, body) + recoveredEnums ≈ enums.value && recoveredBody ≈ body + } + + property("construct-reconstruct for-yield") = forAll { (enums: ForEnums, body: Tree) => + val SyntacticForYield(recoveredEnums, recoveredBody) = SyntacticForYield(enums.value, body) + recoveredEnums ≈ enums.value && recoveredBody ≈ body + } + + val abcde = List(fq"a <-b", fq"if c", fq"d = e") + val foobarbaz = pq"foo @ Bar(baz)" + val fv = q"f(v)" + + property("construct/deconstruct for loop with fq") = test { + val for0 = q"for(..$abcde) $fv" + assertEqAst(for0, "for(a <- b; if c; d = e) f(v)") + val q"for(..$enums) $body" = for0 + assert(enums ≈ abcde) + assert(body ≈ fv) + } + + property("construct/deconstruct valfrom with fq") = test { + assert(fq"$foobarbaz <- $fv" ≈ fq"foo @ Bar(baz) <- f(v)") + val fq"$lhs <- $rhs" = fq"$foobarbaz <- $fv" + assert(lhs ≈ foobarbaz) + assert(rhs ≈ fv) + } + + property("construct/deconstruct valeq with fq") = test { + assert(fq"$foobarbaz = $fv" ≈ fq"foo @ Bar(baz) = f(v)") + val fq"$lhs = $rhs" = fq"$foobarbaz = $fv" + assert(lhs ≈ foobarbaz) + assert(rhs ≈ fv) + } + + property("construct/deconstruct filter with fq") = test { + assert(fq"if $fv" ≈ fq"if f(v)") + val fq"if $cond" = fq"if $fv" + assert(cond ≈ fv) + } +}
\ No newline at end of file diff --git a/test/scalacheck/scala/reflect/quasiquotes/LiftableProps.scala b/test/scalacheck/scala/reflect/quasiquotes/LiftableProps.scala new file mode 100644 index 0000000000..90e5adba58 --- /dev/null +++ b/test/scalacheck/scala/reflect/quasiquotes/LiftableProps.scala @@ -0,0 +1,176 @@ +package scala.reflect.quasiquotes + +import org.scalacheck._, Prop._, Gen._, Arbitrary._ +import scala.reflect.runtime.universe._, Flag._ + +object LiftableProps extends QuasiquoteProperties("liftable") { + property("unquote byte") = test { + val c: Byte = 0 + assert(q"$c" ≈ Literal(Constant(c))) + assert(q"${0: Byte}" ≈ Literal(Constant(c))) + } + + property("unquote short") = test { + val c: Short = 0 + assert(q"$c" ≈ Literal(Constant(c))) + assert(q"${0: Short}" ≈ Literal(Constant(c))) + } + + property("unquote char") = test { + val c: Char = 'c' + assert(q"$c" ≈ Literal(Constant(c))) + assert(q"${'c'}" ≈ Literal(Constant(c))) + } + + property("unquote int") = test { + val c: Int = 0 + assert(q"$c" ≈ Literal(Constant(c))) + assert(q"${0: Int}" ≈ Literal(Constant(c))) + } + + property("unquote long") = test { + val c: Long = 0 + assert(q"$c" ≈ Literal(Constant(c))) + assert(q"${0: Long}" ≈ Literal(Constant(c))) + } + + property("unquote float") = test { + val c: Float = 0.0f + assert(q"$c" ≈ Literal(Constant(c))) + assert(q"${0.0f: Float}" ≈ Literal(Constant(c))) + } + + property("unquote double") = test { + val c: Double = 0.0 + assert(q"$c" ≈ Literal(Constant(c))) + assert(q"${0.0: Double}" ≈ Literal(Constant(c))) + } + + property("unquote boolean") = test { + val c: Boolean = false + assert(q"$c" ≈ Literal(Constant(c))) + assert(q"${true}" ≈ Literal(Constant(true))) + assert(q"${false}" ≈ Literal(Constant(false))) + } + + property("unquote string") = test { + val c: String = "s" + assert(q"$c" ≈ Literal(Constant(c))) + assert(q"${"s"}" ≈ Literal(Constant(c))) + } + + property("unquote unit") = test { + val c: Unit = () + assert(q"$c" ≈ Literal(Constant(c))) + assert(q"${()}" ≈ 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") + } + + val immutable = q"$scalapkg.collection.immutable" + + property("lift list variants") = test { + val lst = List(1, 2) + assert(q"$lst" ≈ q"$immutable.List(1, 2)") + assert(q"f(..$lst)" ≈ q"f(1, 2)") + val llst = List(List(1), List(2)) + assert(q"f(..$llst)" ≈ q"f($immutable.List(1), $immutable.List(2))") + assert(q"f(...$llst)" ≈ q"f(1)(2)") + } + + property("lift list of tree") = test { + val lst = List(q"a", q"b") + assert(q"$lst" ≈ q"$immutable.List(a, b)") + } + + property("lift tuple") = test { + assert(q"${(1, 2)}" ≈ q"(1, 2)") + assert(q"${(1, 2, 3)}" ≈ q"(1, 2, 3)") + assert(q"${(1, 2, 3, 4)}" ≈ q"(1, 2, 3, 4)") + assert(q"${(1, 2, 3, 4, 5)}" ≈ q"(1, 2, 3, 4, 5)") + assert(q"${(1, 2, 3, 4, 5, 6)}" ≈ q"(1, 2, 3, 4, 5, 6)") + assert(q"${(1, 2, 3, 4, 5, 6, 7)}" ≈ q"(1, 2, 3, 4, 5, 6, 7)") + assert(q"${(1, 2, 3, 4, 5, 6, 7, 8)}" ≈ q"(1, 2, 3, 4, 5, 6, 7, 8)") + assert(q"${(1, 2, 3, 4, 5, 6, 7, 8, 9)}" ≈ q"(1, 2, 3, 4, 5, 6, 7, 8, 9)") + assert(q"${(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)}" ≈ q"(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)") + assert(q"${(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)}" ≈ q"(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)") + assert(q"${(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)}" ≈ q"(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)") + assert(q"${(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)}" ≈ q"(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)") + assert(q"${(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)}" ≈ q"(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)") + assert(q"${(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)}" ≈ q"(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)") + assert(q"${(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)}" ≈ q"(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)") + assert(q"${(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17)}" ≈ q"(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17)") + assert(q"${(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18)}" ≈ q"(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18)") + assert(q"${(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19)}" ≈ q"(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19)") + assert(q"${(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)}" ≈ q"(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)") + assert(q"${(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21)}" ≈ q"(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21)") + assert(q"${(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22)}" ≈ q"(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22)") + } + + property("lift nil") = test { + val nil = Nil + assert(q"$nil" ≈ q"scala.collection.immutable.Nil") + } + + property("lift some") = test { + val some1 = Some(1) + assert(q"$some1" ≈ q"scala.Some(1)") + val some2: Option[Int] = Some(1) + assert(q"$some2" ≈ q"scala.Some(1)") + } + + property("lift none") = test { + val none1 = None + assert(q"$none1" ≈ q"scala.None") + val none2: Option[Int] = None + assert(q"$none2" ≈ q"scala.None") + } + + property("lift left") = test { + val left1 = Left(1) + assert(q"$left1" ≈ q"scala.util.Left(1)") + val left2: Left[Int, Int] = Left(1) + assert(q"$left2" ≈ q"scala.util.Left(1)") + val left3: Either[Int, Int] = Left(1) + assert(q"$left3" ≈ q"scala.util.Left(1)") + } + + property("lift right") = test { + val right1 = Right(1) + assert(q"$right1" ≈ q"scala.util.Right(1)") + val right2: Right[Int, Int] = Right(1) + assert(q"$right2" ≈ q"scala.util.Right(1)") + val right3: Either[Int, Int] = Right(1) + assert(q"$right3" ≈ q"scala.util.Right(1)") + } + + property("lift xml comment") = test { + implicit val liftXmlComment = Liftable[xml.Comment] { comment => + q"new _root_.scala.xml.Comment(${comment.commentText})" + } + assert(q"${xml.Comment("foo")}" ≈ q"<!--foo-->") + } +} diff --git a/test/scalacheck/scala/reflect/quasiquotes/PatternConstructionProps.scala b/test/scalacheck/scala/reflect/quasiquotes/PatternConstructionProps.scala new file mode 100644 index 0000000000..e62a004adc --- /dev/null +++ b/test/scalacheck/scala/reflect/quasiquotes/PatternConstructionProps.scala @@ -0,0 +1,38 @@ +package scala.reflect.quasiquotes + +import org.scalacheck._, Prop._, Gen._, Arbitrary._ +import scala.reflect.runtime.universe._, Flag._ + +object PatternConstructionProps extends QuasiquoteProperties("pattern construction") { + property("unquote bind") = forAll { (bind: Bind) => + pq"$bind" ≈ bind + } + + property("unquote name into bind") = forAll { (name: TermName) => + pq"$name" ≈ Bind(name, Ident(termNames.WILDCARD)) + } + + property("unquote name and tree into bind") = forAll { (name: TermName, tree: Tree) => + pq"$name @ $tree" ≈ Bind(name, tree) + } + + property("unquote type name into typed") = forAll { (name: TypeName) => + pq"_ : $name" ≈ Typed(Ident(termNames.WILDCARD), Ident(name)) + } + + property("unquote tree into typed") = forAll { (typ: Tree) => + pq"_ : $typ" ≈ Typed(Ident(termNames.WILDCARD), typ) + } + + property("unquote into apply") = forAll { (pat: Tree, subpat: Tree) => + pq"$pat($subpat)" ≈ Apply(pat, List(subpat)) + } + + property("unquote into casedef") = forAll { (pat: Tree, cond: Tree, body: Tree) => + cq"$pat if $cond => $body" ≈ CaseDef(pat, cond, body) + } + + property("unquote into alternative") = forAll { (first: Tree, rest: List[Tree]) => + pq"$first | ..$rest" ≈ Alternative(first :: rest) + } +} diff --git a/test/scalacheck/scala/reflect/quasiquotes/PatternDeconstructionProps.scala b/test/scalacheck/scala/reflect/quasiquotes/PatternDeconstructionProps.scala new file mode 100644 index 0000000000..182e905c04 --- /dev/null +++ b/test/scalacheck/scala/reflect/quasiquotes/PatternDeconstructionProps.scala @@ -0,0 +1,46 @@ +package scala.reflect.quasiquotes + +import org.scalacheck._, Prop._, Gen._, Arbitrary._ +import scala.reflect.runtime.universe._, Flag._ + +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 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 + } + + property("extract alternative") = forAll { (first: Tree, rest: List[Tree]) => + val pq"$first1 | ..$rest1" = pq"$first | ..$rest" + first1 ≈ first && rest1 ≈ rest + } +} diff --git a/test/scalacheck/scala/reflect/quasiquotes/QuasiquoteProperties.scala b/test/scalacheck/scala/reflect/quasiquotes/QuasiquoteProperties.scala new file mode 100644 index 0000000000..13e231891d --- /dev/null +++ b/test/scalacheck/scala/reflect/quasiquotes/QuasiquoteProperties.scala @@ -0,0 +1,122 @@ +package scala.reflect.quasiquotes + +import org.scalacheck._, Prop._, Gen._, Arbitrary._ +import scala.tools.reflect.{ToolBox, ToolBoxError} +import scala.reflect.runtime.currentMirror +import scala.reflect.runtime.universe._, Flag._, internal.reificationSupport.setSymbol + +abstract 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) + } + + object simplify extends Transformer { + object SimplifiedName { + val st = scala.reflect.runtime.universe.asInstanceOf[scala.reflect.internal.SymbolTable] + val FreshName = new st.FreshNameExtractor + def unapply[T <: Name](name: T): Option[T] = name.asInstanceOf[st.Name] match { + case FreshName(prefix) => + Some((if (name.isTermName) TermName(prefix) else TypeName(prefix)).asInstanceOf[T]) + } + } + + override def transform(tree: Tree): Tree = tree match { + case Ident(SimplifiedName(name)) => Ident(name) + case ValDef(mods, SimplifiedName(name), tpt, rhs) => ValDef(mods, name, transform(tpt), transform(rhs)) + case Bind(SimplifiedName(name), rhs) => Bind(name, rhs) + case _ => + super.transform(tree) + } + + def apply(tree: Tree): Tree = transform(tree) + } + + implicit class TestSimilarTree(tree1: Tree) { + def ≈(tree2: Tree) = simplify(tree1).equalsStructure(simplify(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.runtimeClass.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 assertEqAst(tree: Tree, code: String) = assert(eqAst(tree, code)) + def eqAst(tree: Tree, code: String) = tree ≈ parse(code) + + val toolbox = currentMirror.mkToolBox() + val parse = toolbox.parse(_) + val compile = toolbox.compile(_) + val eval = toolbox.eval(_) + + def typecheck(tree: Tree) = toolbox.typecheck(tree) + + def typecheckTyp(tree: Tree) = { + val q"type $_ = $res" = typecheck(q"type T = $tree") + res + } + + def typecheckPat(tree: Tree) = { + val q"$_ match { case $res => }" = typecheck(q"((): Any) match { case $tree => }") + res + } + + 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 { + compile(parse(s""" + object Wrapper extends Helpers { + import scala.reflect.runtime.universe._ + $block + } + """)) + 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) + } + } + + val scalapkg = setSymbol(Ident(TermName("scala")), definitions.ScalaPackage) +} diff --git a/test/scalacheck/scala/reflect/quasiquotes/RuntimeErrorProps.scala b/test/scalacheck/scala/reflect/quasiquotes/RuntimeErrorProps.scala new file mode 100644 index 0000000000..4e389f1560 --- /dev/null +++ b/test/scalacheck/scala/reflect/quasiquotes/RuntimeErrorProps.scala @@ -0,0 +1,77 @@ +package scala.reflect.quasiquotes + +import org.scalacheck._, Prop._, Gen._, Arbitrary._ +import scala.reflect.runtime.universe._, Flag._ + +object RuntimeErrorProps extends QuasiquoteProperties("errors") { + def testFails[T](block: =>T) = test { + assertThrows[IllegalArgumentException] { + block + } + } + + property("default param anon function") = testFails { + val param = q"val x: Int = 1" + q"{ $param => x + 1 }" + } + + property("non-casedef case") = testFails { + val x = q"x" + q"foo match { case $x }" + } + + property("non-new annotation") = testFails { + val annot = q"foo" + q"@$annot def foo" + } + + property("non-valdef param") = testFails { + val param = q"foo" + q"def foo($param)" + } + + property("non-valdef class param") = testFails { + val param = q"foo" + q"class Foo($param)" + } + + property("non-typedef type param") = testFails { + val tparam = tq"T" + q"class C[$tparam]" + } + + property("non-definition refine stat") = testFails { + val stat = q"foo" + tq"Foo { $stat }" + } + + property("non-definition early def") = testFails { + val stat = q"foo" + q"class Foo extends { $stat } with Bar" + } + + property("type apply for definition") = testFails { + val defn = q"def foo" + q"$defn[foo]" + } + + property("non-val selftype") = testFails { + val foo = q"foo" + q"class Foo { $foo => }" + } + + property("for empty enums") = testFails { + val enums = List.empty[Tree] + q"for(..$enums) 0" + } + + property("for starts with non-from enum") = testFails { + val enums = fq"foo = bar" :: Nil + q"for(..$enums) 0" + } + + property("for invalid enum") = testFails { + val enums = q"foo" :: Nil + q"for(..$enums) 0" + } +} diff --git a/test/scalacheck/scala/reflect/quasiquotes/TermConstructionProps.scala b/test/scalacheck/scala/reflect/quasiquotes/TermConstructionProps.scala new file mode 100644 index 0000000000..61faaefe51 --- /dev/null +++ b/test/scalacheck/scala/reflect/quasiquotes/TermConstructionProps.scala @@ -0,0 +1,327 @@ +package scala.reflect.quasiquotes + +import org.scalacheck._, Prop._, Gen._, Arbitrary._ +import scala.reflect.runtime.universe._, Flag._ + +object TermConstructionProps extends QuasiquoteProperties("term construction") { + property("unquote single tree return tree itself") = forAll { (t: Tree) => + q"$t" ≈ t + } + + property("unquote trees into if expression") = forAll { (t1: Tree, t2: Tree, t3: Tree) => + q"if($t1) $t2 else $t3" ≈ If(t1, t2, t3) + } + + property("unquote trees into ascriptiopn") = forAll { (t1: Tree, t2: Tree) => + q"$t1 : $t2" ≈ Typed(t1, t2) + } + + property("unquote trees into apply") = forAll { (t1: Tree, t2: Tree, t3: Tree) => + q"$t1($t2, $t3)" ≈ Apply(t1, List(t2, t3)) + } + + property("unquote trees with .. rank into apply") = forAll { (ts: List[Tree]) => + q"f(..$ts)" ≈ Apply(q"f", ts) + } + + property("unquote iterable into apply") = forAll { (trees: List[Tree]) => + val itrees: Iterable[Tree] = trees + q"f(..$itrees)" ≈ Apply(q"f", trees) + } + + property("unquote trees with ... rank into apply") = forAll { (ts1: List[Tree], ts2: List[Tree]) => + val argss = List(ts1, ts2) + q"f(...$argss)" ≈ Apply(Apply(q"f", ts1), ts2) + } + + property("unquote term name into assign") = forAll { (name: TermName, t: Tree) => + q"$name = $t" ≈ Assign(Ident(name), t) + } + + property("unquote trees into block") = forAll { (t1: Tree, t2: Tree, t3: Tree) => + blockInvariant(q"""{ + $t1 + $t2 + $t3 + }""", List(t1, t2, t3)) + } + + + property("unquote tree into new") = forAll { (tree: Tree) => + q"new $tree" ≈ Apply(Select(New(tree), termNames.CONSTRUCTOR), List()) + } + + property("unquote tree into return") = forAll { (tree: Tree) => + q"return $tree" ≈ Return(tree) + } + + property("unquote a list of arguments") = forAll { (fun: Tree, args: List[Tree]) => + q"$fun(..$args)" ≈ Apply(fun, args) + } + + property("unquote 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("unquote into new") = forAll { (name: TypeName, body: List[Tree]) => + q"new $name { ..$body }" ≈ + q"""{ + final class $$anon extends $name { + ..$body + } + new $$anon + }""" + } + + property("unquote type name into this") = forAll { (T: TypeName) => + q"$T.this" ≈ This(T) + } + + property("unquote tree into throw") = forAll { (t: Tree) => + q"throw $t" ≈ Throw(t) + } + + property("unquote trees into type apply") = forAll { (fun: TreeIsTerm, types: List[Tree]) => + q"$fun[..$types]" ≈ (if (types.nonEmpty) TypeApply(fun, types) else fun) + } + + property("unquote 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("unquote 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 + } + + 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("unquote list of trees into block (1)") = forAll { (trees: List[Tree]) => + blockInvariant(q"{ ..$trees }", trees) + } + + property("unquote list of trees into block (2)") = forAll { (trees1: List[Tree], trees2: List[Tree]) => + blockInvariant(q"{ ..$trees1 ; ..$trees2 }", trees1 ++ trees2) + } + + property("unquote list of trees into block (3)") = forAll { (trees: List[Tree], tree: Tree) => + blockInvariant(q"{ ..$trees; $tree }", trees :+ tree) + } + + property("unquote term into brackets") = test { + val a = q"a" + assert(q"($a)" ≈ a) + } + + property("unquote 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("unquote empty list into tuple") = test { + val empty = List[Tree]() + assert(q"(..$empty)" ≈ q"()") + } + + property("unquote single element list into tuple") = test { + val xs = q"x" :: Nil + assert(q"(..$xs)" ≈ xs.head) + } + + 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("unquote 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 is not a block") = test { + assertThrows[MatchError] { + val q"{ ..$stats1 }" = q" " + } + } + + property("empty block is synthetic unit") = test { + val q"()" = q"{}" + val q"{..$stats}" = q"{}" + assert(stats.isEmpty) + assertEqAst(q"{..$stats}", "{}") + assertEqAst(q"{..$stats}", "()") + } + + 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") + } + + property("SI-8385 a") = test { + assertEqAst(q"(foo.x = 1)(2)", "(foo.x = 1)(2)") + } + + property("SI-8385 b") = test { + assertEqAst(q"(() => ())()", "(() => ())()") + } + + property("match scrutinee may not be empty") = test { + assertThrows[IllegalArgumentException] { + val scrutinee = q"" + val cases = List(cq"_ =>") + q"$scrutinee match { case ..$cases }" + } + } + + property("construct partial function") = test { + val cases = List(cq"a => b", cq"c => d") + assertEqAst(q"{ case ..$cases }", "{ case a => b case c => d }") + } + + property("SI-8609 a") = test { + val q1 = q"val x = 1" + val q2 = q"..$q1; val y = 2" + assert(q2 ≈ q"{ val x = 1; val y = 2 }") + } + + property("SI-8609 b") = test { + val q1 = q"import foo.bar" + val q2 = q"..$q1; val y = 2" + assert(q2 ≈ q"{ import foo.bar; val y = 2 }") + } +} diff --git a/test/scalacheck/scala/reflect/quasiquotes/TermDeconstructionProps.scala b/test/scalacheck/scala/reflect/quasiquotes/TermDeconstructionProps.scala new file mode 100644 index 0000000000..73bfba41bc --- /dev/null +++ b/test/scalacheck/scala/reflect/quasiquotes/TermDeconstructionProps.scala @@ -0,0 +1,258 @@ +package scala.reflect.quasiquotes + +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]" + } + } + + property("match doesn't match partial function") = test { + assertThrows[MatchError] { + val q"$_ match { case ..$_ }" = q"{ case _ => }" + } + } + + property("deconstruct partial function") = test { + val q"{ case ..$cases }" = q"{ case a => b case c => d }" + val List(cq"a => b", cq"c => d") = cases + } + + property("SI-8350 `new C` and `new C()` are equivalent") = test { + val q"new C" = q"new C()" + val q"new C()" = q"new C" + } + + property("SI-8350 new applications extracted only for non-empty ctor calls") = test{ + val q"new $c1" = q"new C()" + assert(c1 ≈ tq"C") + val q"new $c2" = q"new C(x)" + assert(c2 ≈ q"${tq"C"}(x)") + } + + property("SI-8350 original test case") = test { + val q"new ..$parents" = q"new Foo with Bar" + assert(parents ≈ List(tq"Foo", tq"Bar")) + } + + property("SI-8387 new is not an application") = test { + val `new` = q"new F(x)" + val q"$f(...$argss)" = `new` + assert(f ≈ `new`) + assert(argss.isEmpty) + } + + property("SI-8703 extract block with single expression") = test { + val q"{ $a }" = Block(Nil, q"1") + val Literal(Constant(1)) = a + val q"{ $b }" = q"2" + val Literal(Constant(2)) = b + } +} diff --git a/test/scalacheck/scala/reflect/quasiquotes/TypeConstructionProps.scala b/test/scalacheck/scala/reflect/quasiquotes/TypeConstructionProps.scala new file mode 100644 index 0000000000..c96018b317 --- /dev/null +++ b/test/scalacheck/scala/reflect/quasiquotes/TypeConstructionProps.scala @@ -0,0 +1,44 @@ +package scala.reflect.quasiquotes + +import org.scalacheck._, Prop._, Gen._, Arbitrary._ +import scala.reflect.runtime.universe._, Flag._, internal.reificationSupport.ScalaDot + +object TypeConstructionProps extends QuasiquoteProperties("type construction") { + property("bare idents contain type names") = test { + tq"x" ≈ Ident(TypeName("x")) + } + + property("unquote 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)" ≈ ScalaDot(TypeName("Unit"))) + assert(tq"(..$ts)" ≈ tq"scala.Tuple2[t1, t2]") + assert(tq"(t0, ..$ts)" ≈ tq"scala.Tuple3[t0, t1, t2]") + } + + property("single-element tuple type") = test { + val ts = q"T" :: Nil + assert(tq"(..$ts)" ≈ ts.head) + } + + property("refined type") = test { + val stats = q"def foo" :: q"val x: Int" :: q"type Y = String" :: Nil + assert(tq"T { ..$stats }" ≈ tq"T { def foo; val x: Int; type Y = String }") + } + + property("function type") = test { + val argtpes = tq"A" :: tq"B" :: Nil + val restpe = tq"C" + assert(tq"..$argtpes => $restpe" ≈ tq"(A, B) => C") + } + + property("empty tq") = test { + val tt: TypeTree = tq"" + assert(tt.tpe == null) + assert(tt.original == null) + } +} diff --git a/test/scalacheck/scala/reflect/quasiquotes/TypeDeconstructionProps.scala b/test/scalacheck/scala/reflect/quasiquotes/TypeDeconstructionProps.scala new file mode 100644 index 0000000000..fc8554d61f --- /dev/null +++ b/test/scalacheck/scala/reflect/quasiquotes/TypeDeconstructionProps.scala @@ -0,0 +1,80 @@ +package scala.reflect.quasiquotes + +import org.scalacheck._, Prop._, Gen._, Arbitrary._ +import scala.reflect.runtime.universe._, 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 (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") + 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("tuple type (5)") = test { + val tq"(..$ts)" = tq"T" + assert(ts ≈ List(tq"T")) + } + + property("refined type") = test { + val tq"T { ..$stats }" = tq"T { def foo; val x: Int; type Y = String }" + assert(stats ≈ List(q"def foo", q"val x: Int", q"type Y = String")) + } + + property("function type (1)") = test { + val tq"..$argtpes => $restpe" = tq"(A, B) => C" + assert(argtpes ≈ List(tq"A", tq"B")) + assert(restpe ≈ tq"C") + } + + 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") + } + + property("match empty type tree") = test { + val tq"" = TypeTree() + // matches because type tree isn't syntactic without original + val tq"" = tq"${typeOf[Int]}" + } + + property("type select doesn't match term select") = test { + assertThrows[MatchError] { + val tq"$qual.$name" = q"foo.bar" + } + } + + property("applied type doesn't match type appliction") = test { + assertThrows[MatchError] { + val tq"$tpt[..$tpts]" = q"foo[bar]" + } + } +} diff --git a/test/scalacheck/scala/reflect/quasiquotes/TypecheckedProps.scala b/test/scalacheck/scala/reflect/quasiquotes/TypecheckedProps.scala new file mode 100644 index 0000000000..4646388c86 --- /dev/null +++ b/test/scalacheck/scala/reflect/quasiquotes/TypecheckedProps.scala @@ -0,0 +1,217 @@ +package scala.reflect.quasiquotes + +import org.scalacheck._, Prop._, Gen._, Arbitrary._ +import scala.reflect.runtime.universe._, Flag._, internal.reificationSupport._ + +object TypecheckedProps extends QuasiquoteProperties("typechecked") + with TypecheckedTypes { + property("tuple term") = test { + val q"(..$elements)" = typecheck(q"(1, 2)") + assert(elements ≈ List(q"1", q"2")) + } + + property("for/for-yield") = test { + val enums = fq"x <- xs" :: fq"x1 = x + 1" :: fq"if x1 % 2 == 0" :: Nil + val body = q"x1" + val xs = q"val xs = List(1, 2, 3)" + val q"$_; for(..$enums0) yield $body0" = typecheck(q"$xs; for(..$enums) yield $body") + assert(enums0 ≈ enums) + assert(body0 ≈ body) + val q"$_; for(..$enums1) $body1" = typecheck(q"$xs; for(..$enums) $body") + assert(enums1 ≈ enums) + assert(body1 ≈ body) + } + + property("for .filter instead of .withFilter") = test { + val enums = fq"foo <- new Foo" :: fq"if foo != null" :: Nil + val body = q"foo" + val q"$_; for(..$enums1) yield $body1" = typecheck(q""" + class Foo { def map(f: Any => Any) = this; def withFilter(cond: Any => Boolean) = this } + for(..$enums) yield $body + """) + assert(enums1 ≈ enums) + assert(body1 ≈ body) + } + + property("extract UnApply (1)") = test { + val q"object $_ { $_; $_; $m }" = typecheck(q""" + object Test { + class Cell(val x: Int) + object Cell { def unapply(c: Cell) = Some(c.x) } + new Cell(0) match { case Cell(v) => v } + } + """) + val q"$_ match { case $f(..$args) => $_ }" = m + assert(f ≈ pq"Test.this.Cell") + assert(args ≈ List(pq"v")) + } + + property("extract UnApply (2)") = test { + val q"object $_ { $_; $m }" = typecheck(q""" + object Test { + case class Cell(val x: Int) + new Cell(0) match { case Cell(v) => v } + } + """) + val q"$_ match { case ${f: TypeTree}(..$args) => $_ }" = m + assert(f.original ≈ pq"Test.this.Cell") + assert(args ≈ List(pq"v")) + } + + property("extract inferred val type") = test { + val typechecked = typecheck(q"val x = 42") + val q"val x = 42" = typechecked + val q"val x: ${tq""} = 42" = typechecked + val q"val x: ${t: Type} = 42" = typechecked + } + + property("class with param (1)") = test { + val paramName = TermName("x") + val q"class $_($param)" = typecheck(q"class Test(val $paramName: Int)") + + assert(param.name == paramName) + } + + property("class with param (2)") = test { + val paramName = TermName("y") + val q"{class $_($param)}" = typecheck(q"class Test(val $paramName: Int = 3)") + + assert(param.name == paramName) + assert(param.rhs ≈ q"3") + } + + property("class with params") = test { + val pName1 = TermName("x1") + val pName2 = TermName("x2") + val q"{class $_($param1)(..$params2)}" = typecheck(q"class Test(val x0: Float)(val $pName1: Int = 3, $pName2: String)") + + val List(p1, p2, _*) = params2 + + assert(p1.name == pName1) + assert(p2.name == pName2) + assert(params2.size == 2) + } + + property("implicit class") = test { + val clName = TypeName("Test") + val paramName = TermName("x") + val q"{implicit class $name($param)}" = typecheck(q"implicit class $clName(val $paramName: String)") + + assert(name == clName) + assert(param.name == paramName) + } + + property("block with lazy") = test { + val lazyName = TermName("x") + val lazyRhsVal = 42 + val lazyRhs = Literal(Constant(lazyRhsVal)) + val q"{ $mods val $pname: $_ = $rhs }" = typecheck(q"{lazy val $lazyName = $lazyRhsVal}") + + assert(pname == lazyName) + assert(rhs ≈ lazyRhs) + } + + property("class with lazy") = test { + val clName = TypeName("Test") + val paramName = TermName("x") + val q"class $name{lazy val $pname = $_}" = typecheck(q"class $clName {lazy val $paramName = 42}") + + assert(name == clName) + assert(pname == paramName) + } + + property("case class with object") = test { + val defName = TermName("z") + val defRhsVal = 42 + val defRhs = Literal(Constant(defRhsVal)) + val q"object $_{ $_; object $_ extends ..$_ {def $name = $rhs} }" = + typecheck(q""" + object Test{ + case class C(x: Int) { def y = x }; + object C { def $defName = $defRhsVal } + }""") + + assert(name == defName) + assert(rhs ≈ defRhs) + } + + property("partial function") = test { + val q"{ case ..$cases }: $ascr" = typecheck(q"{ case 1 => () }: PartialFunction[Int, Unit]") + assert(cases ≈ q"{ case 1 => () }".cases) + } +} + +trait TypecheckedTypes { self: QuasiquoteProperties => + property("type ident") = test { + val q"$_; type $_ = $tpt" = typecheck(q"class C; type T = C") + val tq"C" = tpt + } + + property("type select") = test { + val tq"scala.Int" = typecheckTyp(tq"Int") + } + + property("this type select") = test { + val q"class $_ { $_; type $_ = $tpt }" = typecheck(q"class C { type A = Int; type B = this.A }") + val tq"this.$name" = tpt + val TypeName("A") = name + } + + property("super type select") = test { + val q"$_; class $_ extends $_ { type $_ = $tpt }" = + typecheck(q"class C1 { type A = Int }; class C2 extends C1 { type B = super[C1].A }") + val tq"$empty.super[$c1].$a" = tpt + val TypeName("") = empty + val TypeName("C1") = c1 + val TypeName("A") = a + } + + property("applied type") = test { + val tt = typecheckTyp(tq"Map[Int, Int]") + val tq"$tpt[..$tpts]" = tt + val tq"scala.Predef.Map" = tpt + val List(tq"scala.Int", tq"scala.Int") = tpts + } + + property("tuple type") = test { + val tq"(..$els0)" = typecheckTyp(tq"Unit") + assert(els0.isEmpty) + val tq"(..$els1)" = typecheckTyp(tq"(Int, Int)") + val List(tq"scala.Int", tq"scala.Int") = els1 + } + + property("function type") = test { + val tq"(..$argtpes) => $restpe" = typecheckTyp(tq"(Int, Int) => Int") + val List(tq"scala.Int", tq"scala.Int") = argtpes + val tq"scala.Int" = restpe + } + + property("compound type") = test { + val tq"..$parents { ..$defns }" = typecheckTyp(tq"Int { def x: Int }") + val List(tq"Int") = parents + val List(q"def x: Int") = defns + } + + property("singleton type") = test { + val tq"$ref.type" = typecheckTyp(tq"scala.Predef.type") + val q"scala.Predef" = ref + } + + property("type projection") = test { + val tq"$tpt#$name" = typecheckTyp(tq"({ type T = Int })#T") + val TypeName("T") = name + val tq"{ type T = Int }" = tpt + } + + property("annotated type") = test { + val tq"$tpt @$annot" = typecheckTyp(tq"Int @unchecked") + val tq"scala.Int" = tpt + val tq"unchecked" = annot + } + + property("existential type") = test { + val tq"$tpt forSome { ..$defns }" = typecheckTyp(tq"T forSome { type T }") + val tq"T" = tpt + val q"type T" :: Nil = defns + } +} diff --git a/test/scalacheck/scala/reflect/quasiquotes/UnliftableProps.scala b/test/scalacheck/scala/reflect/quasiquotes/UnliftableProps.scala new file mode 100644 index 0000000000..4c2f2280ca --- /dev/null +++ b/test/scalacheck/scala/reflect/quasiquotes/UnliftableProps.scala @@ -0,0 +1,168 @@ +package scala.reflect.quasiquotes + +import org.scalacheck._, Prop._, Gen._, Arbitrary._ +import scala.reflect.runtime.universe._, Flag._ + +object UnliftableProps extends QuasiquoteProperties("unliftable") { + property("unlift name") = test { + val termname0 = TermName("foo") + val typename0 = TypeName("foo") + val q"${termname1: TermName}" = Ident(termname0) + assert(termname1 == termname0) + val q"${typename1: TypeName}" = Ident(typename0) + assert(typename1 == typename0) + val q"${name1: Name}" = Ident(termname0) + assert(name1 == termname0) + val q"${name2: Name}" = Ident(typename0) + assert(name2 == typename0) + } + + property("unlift type") = test { + val q"${tpe: Type}" = TypeTree(typeOf[Int]) + assert(tpe =:= typeOf[Int]) + } + + property("unlift constant") = test { + val q"${const: Constant}" = Literal(Constant("foo")) + assert(const == Constant("foo")) + } + + property("unlift char") = test { + val q"${c: Char}" = Literal(Constant('0')) + assert(c.isInstanceOf[Char] && c == '0') + } + + property("unlift byte") = test { + val q"${b: Byte}" = Literal(Constant(0: Byte)) + assert(b.isInstanceOf[Byte] && b == 0) + } + + property("unlift short") = test { + val q"${s: Short}" = Literal(Constant(0: Short)) + assert(s.isInstanceOf[Short] && s == 0) + } + + property("unlift int") = test { + val q"${i: Int}" = Literal(Constant(0: Int)) + assert(i.isInstanceOf[Int] && i == 0) + } + + property("unlift long") = test { + val q"${l: Long}" = Literal(Constant(0L: Long)) + assert(l.isInstanceOf[Long] && l == 0L) + } + + property("unlift float") = test { + val q"${f: Float}" = Literal(Constant(0.0f: Float)) + assert(f.isInstanceOf[Float] && f == 0.0f) + } + + property("unlift double") = test { + val q"${d: Double}" = Literal(Constant(0.0: Double)) + assert(d.isInstanceOf[Double] && d == 0.0) + } + + property("unlift bool") = test { + val q"${b: Boolean}" = q"true" + assert(b.isInstanceOf[Boolean] && b == true) + } + + property("unlift string") = test { + val q"${s: String}" = q""" "foo" """ + assert(s.isInstanceOf[String] && s == "foo") + } + + property("unlift scala.symbol") = test { + val q"${s: scala.Symbol}" = q"'foo" + assert(s.isInstanceOf[scala.Symbol] && s == 'foo) + } + + implicit def unliftList[T: Unliftable]: Unliftable[List[T]] = Unliftable { + case q"scala.collection.immutable.List(..$args)" if args.forall { implicitly[Unliftable[T]].unapply(_).nonEmpty } => + val ut = implicitly[Unliftable[T]] + args.flatMap { ut.unapply(_) } + } + + property("unlift list (1)") = test { + val orig = List(1, 2) + val q"${l1: List[Int]}" = q"$orig" // q"List(1, 2)" + assert(l1 == orig) + val q"f(..${l2: List[Int]})" = q"f(..$orig)" // q"f(1, 2) + assert(l2 == orig) + } + + property("unlift list (2)") = test { + val orig2 = List(List(1, 2), List(3)) + val q"f(${l3: List[List[Int]]})" = q"f($orig2)" // q"f(List(List(1, 2), List(3))) + assert(l3 == orig2) + val q"f(..${l4: List[List[Int]]})" = q"f(..$orig2)" // q"f(List(1, 2), List(3))" + assert(l4 == orig2) + val q"f(...${l5: List[List[Int]]})" = q"f(...$orig2)" // q"f(1, 2)(3) + assert(l5 == orig2) + } + + property("don't unlift non-tree unquotee (1)") = test { + val q"${a: TermName}.${b: TermName}" = q"a.b" + assert(a == TermName("a")) + assert(b == TermName("b")) + } + + property("don't unlift non-tree unquotee (2)") = test { + val q"${mods: Modifiers} def foo" = q"def foo" + assert(mods == Modifiers(DEFERRED)) + } + + property("unlift tuple") = test { + val q"${t2: (Int, Int)}" = q"(1, 2)" + val q"${t3: (Int, Int, Int)}" = q"(1, 2, 3)" + val q"${t4: (Int, Int, Int, Int)}" = q"(1, 2, 3, 4)" + val q"${t5: (Int, Int, Int, Int, Int)}" = q"(1, 2, 3, 4, 5)" + val q"${t6: (Int, Int, Int, Int, Int, Int)}" = q"(1, 2, 3, 4, 5, 6)" + val q"${t7: (Int, Int, Int, Int, Int, Int, Int)}" = q"(1, 2, 3, 4, 5, 6, 7)" + val q"${t8: (Int, Int, Int, Int, Int, Int, Int, Int)}" = q"(1, 2, 3, 4, 5, 6, 7, 8)" + val q"${t9: (Int, Int, Int, Int, Int, Int, Int, Int, Int)}" = q"(1, 2, 3, 4, 5, 6, 7, 8, 9)" + val q"${t10: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)}" = q"(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)" + val q"${t11: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)}" = q"(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)" + val q"${t12: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)}" = q"(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)" + val q"${t13: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)}" = q"(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)" + val q"${t14: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)}" = q"(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)" + val q"${t15: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)}" = q"(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)" + val q"${t16: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)}" = q"(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)" + val q"${t17: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)}" = q"(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17)" + val q"${t18: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)}" = q"(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18)" + val q"${t19: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)}" = q"(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19)" + val q"${t20: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)}" = q"(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)" + val q"${t21: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)}" = q"(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21)" + val q"${t22: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)}" = q"(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22)" + // assert(t1 == Tuple1(1)) + assert(t2 == (1, 2)) + assert(t3 == (1, 2, 3)) + assert(t4 == (1, 2, 3, 4)) + assert(t5 == (1, 2, 3, 4, 5)) + assert(t6 == (1, 2, 3, 4, 5, 6)) + assert(t7 == (1, 2, 3, 4, 5, 6, 7)) + assert(t8 == (1, 2, 3, 4, 5, 6, 7, 8)) + assert(t9 == (1, 2, 3, 4, 5, 6, 7, 8, 9)) + assert(t10 == (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) + assert(t11 == (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)) + assert(t12 == (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)) + assert(t13 == (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)) + assert(t14 == (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)) + assert(t15 == (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)) + assert(t16 == (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)) + assert(t17 == (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17)) + assert(t18 == (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18)) + assert(t19 == (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19)) + assert(t20 == (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)) + assert(t21 == (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21)) + assert(t22 == (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22)) + } + + property("unlift xml comment") = test { + implicit val unliftXmlComment = Unliftable[xml.Comment] { + case q"new _root_.scala.xml.Comment(${value: String})" => xml.Comment(value) + } + val q"${comment: xml.Comment}" = q"<!--foo-->" + assert(comment.commentText == "foo") + } +} |