From 652e96911378e9c53a485263425a26ba687f6540 Mon Sep 17 00:00:00 2001 From: Den Shabalin Date: Mon, 2 Sep 2013 16:45:13 +0200 Subject: add toolbox-based utility methods to quasiquotes' tests --- .../quasiquotes/QuasiquoteProperties.scala | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala b/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala index 5e87aa57cc..6a531071bf 100644 --- a/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala +++ b/test/files/scalacheck/quasiquotes/QuasiquoteProperties.scala @@ -1,7 +1,10 @@ import scala.reflect.runtime.universe._ -import scala.tools.reflect.ToolBox -import scala.tools.reflect.ToolBoxError +import scala.reflect.runtime.universe.definitions._ +import scala.reflect.runtime.universe.Flag._ +import scala.reflect.runtime.currentMirror +import scala.reflect.api.{Liftable, Universe} import scala.reflect.macros.TypecheckException +import scala.tools.reflect.{ToolBox, ToolBoxError} import org.scalacheck._ import Prop._ @@ -57,6 +60,14 @@ trait Helpers { 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 fails(msg: String, block: String) = { def result(ok: Boolean, description: String = "") = { val status = if (ok) Prop.Proof else Prop.False @@ -64,14 +75,12 @@ trait Helpers { Prop { new Prop.Result(status, Nil, Set.empty, labels) } } try { - val tb = rootMirror.mkToolBox() - val tree = tb.parse(s""" + compile(parse(s""" object Wrapper extends Helpers { import scala.reflect.runtime.universe._ $block } - """) - tb.compile(tree) + """)) result(false, "given code doesn't fail to typecheck") } catch { case ToolBoxError(emsg, _) => -- cgit v1.2.3 From c701fb647ac47f66ae2831d085588328a6851c33 Mon Sep 17 00:00:00 2001 From: Den Shabalin Date: Mon, 2 Sep 2013 16:46:25 +0200 Subject: refactor definition tests into separate subsuite --- .../quasiquotes/DefinitionConstructionProps.scala | 218 +++++++++++++++++++++ .../DefinitionDeconstructionProps.scala | 97 +++++++++ .../quasiquotes/TermConstructionProps.scala | 201 +------------------ .../quasiquotes/TermDeconstructionProps.scala | 57 ------ test/files/scalacheck/quasiquotes/Test.scala | 2 + 5 files changed, 326 insertions(+), 249 deletions(-) create mode 100644 test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala create mode 100644 test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala diff --git a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala new file mode 100644 index 0000000000..5d657b931b --- /dev/null +++ b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala @@ -0,0 +1,218 @@ +import org.scalacheck._ +import Prop._ +import Gen._ +import Arbitrary._ + +import scala.reflect.runtime.universe._ +import Flag._ + +object DefinitionConstructionProps + extends QuasiquoteProperties("definition construction") + with ClassConstruction + with TraitConstruction + with TypeDefConstruction + with ValDefConstruction + +trait ClassConstruction { self: QuasiquoteProperties => + val anyRef = Select(Ident(TermName("scala")), TypeName("AnyRef")) + val emtpyConstructor = + DefDef(Modifiers(), nme.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("splice term name into class") = forAll { (name: TypeName) => + eqAst(q"class $name", "class " + name.toString) + } + + property("splice method into class") = forAll { (name: TypeName, method: DefDef) => + q"class $name { $method }" ≈ classWith(name, body = List(method)) + } + + property("splice members into class") = forAll { (name: TypeName, defs: List[DefDef], extra: DefDef) => + q"""class $name { + ..$defs + $extra + }""" ≈ classWith(name, body = defs :+ extra) + } + + property("splice type name into class parents") = forAll { (name: TypeName, parent: TypeName) => + q"class $name extends $parent" ≈ classWith(name, parents = List(Ident(parent))) + } +} + +trait TraitConstruction { self: QuasiquoteProperties => + +} + +trait TypeDefConstruction { self: QuasiquoteProperties => + property("splice type name into typedef") = forAll { (name1: TypeName, name2: TypeName) => + q"type $name1 = $name2" ≈ TypeDef(Modifiers(), name1, List(), Ident(name2)) + } + + property("splice type names into type bounds") = forAll { (T1: TypeName, T2: TypeName, T3: TypeName) => + q"type $T1 >: $T2 <: $T3" ≈ + TypeDef( + Modifiers(DEFERRED), T1, List(), + TypeBoundsTree(Ident(T2), Ident(T3))) + } + + property("splice trees names into type bounds") = forAll { (T: TypeName, t1: Tree, t2: Tree) => + q"type $T >: $t1 <: $t2" ≈ + TypeDef( + Modifiers(DEFERRED), T, List(), + TypeBoundsTree(t1, t2)) + } + + property("splice tparams into typedef (1)") = forAll { (T: TypeName, targs: List[TypeDef], t: Tree) => + q"type $T[..$targs] = $t" ≈ TypeDef(Modifiers(), T, targs, t) + } + + property("splice tparams into typedef (2)") = forAll { (T: TypeName, targs1: List[TypeDef], targs2: List[TypeDef], t: Tree) => + q"type $T[..$targs1, ..$targs2] = $t" ≈ TypeDef(Modifiers(), T, targs1 ++ targs2, t) + } + + property("splice tparams into typedef (3)") = forAll { (T: TypeName, targ: TypeDef, targs: List[TypeDef], t: Tree) => + q"type $T[$targ, ..$targs] = $t" ≈ TypeDef(Modifiers(), T, targ :: targs, t) + } + + property("splice typename into typedef with default bounds") = forAll { (T1: TypeName, T2: TypeName, t: Tree) => + q"type $T1[$T2 >: Any <: Nothing] = $t" ≈ + TypeDef( + Modifiers(), T1, + List(TypeDef( + Modifiers(PARAM), T2, + List(), + TypeBoundsTree( + Ident(TypeName("Any")), + Ident(TypeName("Nothing"))))), + t) + } + + property("splice type names into compound type tree") = forAll { (T: TypeName, A: TypeName, B: TypeName) => + q"type $T = $A with $B" ≈ + TypeDef( + Modifiers(), T, List(), + CompoundTypeTree( + Template(List(Ident(A), Ident(B)), ValDef(Modifiers(PRIVATE), nme.WILDCARD, TypeTree(), EmptyTree), List()))) + } + + property("splice trees into existential type tree") = forAll { + (T1: TypeName, T2: TypeName, X: TypeName, Lo: TypeName, Hi: TypeName) => + + q"type $T1 = $T2[$X] forSome { type $X >: $Lo <: $Hi }" ≈ + TypeDef( + Modifiers(), T1, List(), + ExistentialTypeTree( + AppliedTypeTree(Ident(T2), List(Ident(X))), + List( + TypeDef(Modifiers(DEFERRED), X, List(), TypeBoundsTree(Ident(Lo), Ident(Hi)))))) + } + + property("splice tree into singleton type tree") = forAll { (name: TypeName, t: Tree) => + q"type $name = $t.type" ≈ q"type $name = ${SingletonTypeTree(t)}" + } + + property("splice into applied type tree") = forAll { (T1: TypeName, T2: TypeName, args: List[Tree]) => + q"type $T1 = $T2[..$args]" ≈ + TypeDef(Modifiers(), T1, List(), + AppliedTypeTree(Ident(T2), args)) + } +} + +trait ValDefConstruction { self: QuasiquoteProperties => + property("splice term name into val") = forAll { (name: TermName, tpt: Tree, rhs: Tree) => + q"val $name: $tpt = $rhs" ≈ ValDef(Modifiers(), name, tpt, rhs) + } + + property("splice term name into var") = forAll { (name: TermName, tpt: Tree, rhs: Tree) => + q"var $name: $tpt = $rhs" ≈ ValDef(Modifiers(MUTABLE), name, tpt, rhs) + } +} + +trait MethodConstruction { self: QuasiquoteProperties => + property("splice 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("splice 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("splice type name into annotation") = test { + val name = TypeName("annot") + assertSameAnnots(q"@$name def foo", List(annot(name))) + } + + property("splice ident into annotation") = test { + val name = TypeName("annot") + val ident = Ident(name) + assertSameAnnots(q"@$ident def foo", List(annot(name))) + } + + property("splice idents into annotation") = test { + val idents = List(Ident(TypeName("annot1")), Ident(TypeName("annot2"))) + assertSameAnnots(q"@..$idents def foo", + idents.map { ident => Apply(Select(New(ident), nme.CONSTRUCTOR), List()) }) + } + + property("splice constructor calls into annotation") = test { + val ctorcalls = List(annot("a1"), annot("a2")) + assertSameAnnots(q"@..$ctorcalls def foo", ctorcalls) + } + + property("splice multiple annotations (1)") = test { + val annot1 = annot("a1") + val annot2 = annot("a2") + val res = q"@$annot1 @$annot2 def foo" + assertSameAnnots(res, List(annot1, annot2)) + } + + property("splice multiple annotations (2)") = test { + val annot1 = annot("a1") + val annots = List(annot("a2"), annot("a3")) + val res = q"@$annot1 @..$annots def foo" + assertSameAnnots(res, annot1 :: annots) + } + + property("splice annotations with arguments (1)") = test { + val a = annot("a", List(q"x")) + assertSameAnnots(q"@$a def foo", q"@a(x) def foo") + } + + property("splice annotations with arguments (2)") = test { + val a = newTypeName("a") + assertSameAnnots(q"@$a(x) def foo", q"@a(x) def foo") + } + + property("splice annotations with arguments (3") = test { + val a = Ident(newTypeName("a")) + assertSameAnnots(q"@$a(x) def foo", q"@a(x) def foo") + } + + property("splice improper tree into annot") = test { + val t = tq"Foo[Baz]" + assertThrows[IllegalArgumentException] { + q"@$t def foo" + } + } + + property("can't splice annotations with arguments specificed twice") = test { + val a = annot("a", List(q"x")) + assertThrows[IllegalArgumentException] { + q"@$a(y) def foo" + } + } +} \ No newline at end of file diff --git a/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala new file mode 100644 index 0000000000..31d230d6fd --- /dev/null +++ b/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala @@ -0,0 +1,97 @@ +import org.scalacheck._ +import Prop._ +import Gen._ +import Arbitrary._ + +import scala.reflect.runtime.universe._ +import Flag._ + +object DefinitionDeconstructionProps + extends QuasiquoteProperties("definition deconstruction") + with TraitDeconstruction + with ClassDeconstruction + with ObjectDeconstruction + with ModsDeconstruction + with ValVarDeconstruction + +trait TraitDeconstruction { self: QuasiquoteProperties => + +} + +trait ObjectDeconstruction { self: QuasiquoteProperties => + +} + +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)" + } +} + +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)), nme.CONSTRUCTOR), List()) + } + + property("@$annot(..$args) def foo") = forAll { (annotName: TypeName, tree: Tree) => + val q"@$annot(..$args) def foo" = q"@$annotName($tree) def foo" + annot ≈ Ident(annotName) && args ≈ List(tree) + } + + property("@..$annots def foo") = test { + val a = annot("a") + val b = annot("b") + val q"@..$annots def foo" = q"@$a @$b def foo" + annots ≈ List(a, b) + } + + property("@$annot @..$annots def foo") = test { + val a = annot("a") + val b = annot("b") + val c = annot("c") + val q"@$first @..$rest def foo" = q"@$a @$b @$c def foo" + first ≈ a && rest ≈ List(b, c) + } +} + +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") + } +} \ No newline at end of file diff --git a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala index b14945f24b..5a765f7911 100644 --- a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala @@ -7,16 +7,6 @@ import scala.reflect.runtime.universe._ import Flag._ object TermConstructionProps extends QuasiquoteProperties("term construction") { - val anyRef = Select(Ident(TermName("scala")), TypeName("AnyRef")) - val emtpyConstructor = - DefDef( - Modifiers(), nme.CONSTRUCTOR, List(), - List(List()), TypeTree(), Block(List(Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), List())), Literal(Constant(())))) - - def classWithMethods(name: TypeName, methods: List[DefDef] = Nil) = - ClassDef( - Modifiers(), name, List(), - Template(List(anyRef), emptyValDef, List(emtpyConstructor) ++ methods)) property("splice single tree return tree itself") = forAll { (t: Tree) => q"$t" ≈ t @@ -26,22 +16,6 @@ object TermConstructionProps extends QuasiquoteProperties("term construction") { q"if($t1) $t2 else $t3" ≈ If(t1, t2, t3) } - property("splice term name into val") = forAll { (name: TermName) => - q"val $name = 0" ≈ ValDef(Modifiers(), name, TypeTree(), Literal(Constant(0))) - } - - property("splice type name into typedef") = forAll { (name1: TypeName, name2: TypeName) => - q"type $name1 = $name2" ≈ TypeDef(Modifiers(), name1, List(), Ident(name2)) - } - - property("splice term name into class") = forAll { (name: TypeName) => - q"class $name" ≈ classWithMethods(name) - } - - property("splice method into class") = forAll { (name: TypeName, method: DefDef) => - q"class $name { $method }" ≈ classWithMethods(name, List(method)) - } - property("splice trees into ascriptiopn") = forAll { (t1: Tree, t2: Tree) => q"$t1 : $t2" ≈ Typed(t1, t2) } @@ -69,19 +43,13 @@ object TermConstructionProps extends QuasiquoteProperties("term construction") { } property("splice trees into block") = forAll { (t1: Tree, t2: Tree, t3: Tree) => - q"""{ + blockInvariant(q"""{ $t1 $t2 $t3 - }""" ≈ Block(List(t1, t2), t3) + }""", List(t1, t2, t3)) } - property("splice type name into class parents") = forAll { (name: TypeName, parent: TypeName) => - q"class $name extends $parent" ≈ - ClassDef( - Modifiers(), name, List(), - Template(List(Ident(parent)), emptyValDef, List(emtpyConstructor))) - } property("splice tree into new") = forAll { (tree: Tree) => q"new $tree" ≈ Apply(Select(New(tree), nme.CONSTRUCTOR), List()) @@ -101,13 +69,6 @@ object TermConstructionProps extends QuasiquoteProperties("term construction") { q"$fun($arg1, $arg2, ..$args)" ≈ Apply(fun, List(arg1) ++ List(arg2) ++ args) } - property("splice members into class") = forAll { (name: TypeName, defs: List[DefDef], extra: DefDef) => - q"""class $name { - ..$defs - $extra - }""" ≈ classWithMethods(name, defs ++ List(extra)) - } - property("splice into new") = forAll { (name: TypeName, body: List[Tree]) => q"new $name { ..$body }" ≈ q"""{ @@ -118,11 +79,6 @@ object TermConstructionProps extends QuasiquoteProperties("term construction") { }""" } - - property("splice tree into singleton type tree") = forAll { (name: TypeName, t: Tree) => - q"type $name = $t.type" ≈ q"type $name = ${SingletonTypeTree(t)}" - } - property("splice type name into this") = forAll { (T: TypeName) => q"$T.this" ≈ This(T) } @@ -135,65 +91,6 @@ object TermConstructionProps extends QuasiquoteProperties("term construction") { q"$fun[..$types]" ≈ TypeApply(fun, types) } - property("splice type names into type bounds") = forAll { (T1: TypeName, T2: TypeName, T3: TypeName) => - q"type $T1 >: $T2 <: $T3" ≈ - TypeDef( - Modifiers(DEFERRED), T1, List(), - TypeBoundsTree(Ident(T2), Ident(T3))) - } - - property("splice trees names into type bounds") = forAll { (T: TypeName, t1: Tree, t2: Tree) => - q"type $T >: $t1 <: $t2" ≈ - TypeDef( - Modifiers(DEFERRED), T, List(), - TypeBoundsTree(t1, t2)) - } - - property("splice tparams into typedef (1)") = forAll { (T: TypeName, targs: List[TypeDef], t: Tree) => - q"type $T[..$targs] = $t" ≈ TypeDef(Modifiers(), T, targs, t) - } - - property("splice tparams into typedef (2)") = forAll { (T: TypeName, targs1: List[TypeDef], targs2: List[TypeDef], t: Tree) => - q"type $T[..$targs1, ..$targs2] = $t" ≈ TypeDef(Modifiers(), T, targs1 ++ targs2, t) - } - - property("splice tparams into typedef (3)") = forAll { (T: TypeName, targ: TypeDef, targs: List[TypeDef], t: Tree) => - q"type $T[$targ, ..$targs] = $t" ≈ TypeDef(Modifiers(), T, targ :: targs, t) - } - - property("splice typename into typedef with default bounds") = forAll { (T1: TypeName, T2: TypeName, t: Tree) => - q"type $T1[$T2 >: Any <: Nothing] = $t" ≈ - TypeDef( - Modifiers(), T1, - List(TypeDef( - Modifiers(PARAM), T2, - List(), - TypeBoundsTree( - Ident(TypeName("Any")), - Ident(TypeName("Nothing"))))), - t) - } - - property("splice type names into compound type tree") = forAll { (T: TypeName, A: TypeName, B: TypeName) => - q"type $T = $A with $B" ≈ - TypeDef( - Modifiers(), T, List(), - CompoundTypeTree( - Template(List(Ident(A), Ident(B)), ValDef(Modifiers(PRIVATE), nme.WILDCARD, TypeTree(), EmptyTree), List()))) - } - - property("splice trees into existential type tree") = forAll { - (T1: TypeName, T2: TypeName, X: TypeName, Lo: TypeName, Hi: TypeName) => - - q"type $T1 = $T2[$X] forSome { type $X >: $Lo <: $Hi }" ≈ - TypeDef( - Modifiers(), T1, List(), - ExistentialTypeTree( - AppliedTypeTree(Ident(T2), List(Ident(X))), - List( - TypeDef(Modifiers(DEFERRED), X, List(), TypeBoundsTree(Ident(Lo), Ident(Hi)))))) - } - property("splice names into import selector") = forAll { (expr: Tree, plain: Name, oldname: Name, newname: Name, discard: Name) => @@ -223,95 +120,22 @@ object TermConstructionProps extends QuasiquoteProperties("term construction") { CaseDef(Alternative(List(A, B)), EmptyTree, Literal(Constant(()))))) } - property("splice into applied type tree") = forAll { (T1: TypeName, T2: TypeName, args: List[Tree]) => - q"type $T1 = $T2[..$args]" ≈ - TypeDef( - Modifiers(), T1, List(), - AppliedTypeTree(Ident(T2), args)) - } - - property("splice list of trees into block (1)") = forAll { (trees: List[Tree]) => - q"{ ..$trees }" ≈ (trees match { + def blockInvariant(quote: Tree, trees: List[Tree]) = + quote ≈ (trees match { case Nil => Block(Nil, q"()") case _ => Block(trees.init, trees.last) }) + + property("splice list of trees into block (1)") = forAll { (trees: List[Tree]) => + blockInvariant(q"{ ..$trees }", trees) } property("splice list of trees into block (2)") = forAll { (trees1: List[Tree], trees2: List[Tree]) => - q"{ ..$trees1 ; ..$trees2 }" ≈ ((trees1 ++ trees2) match { - case Nil => Block(Nil, Literal(Constant(()))) - case trees => Block(trees.init, trees.last) - }) + blockInvariant(q"{ ..$trees1 ; ..$trees2 }", trees1 ++ trees2) } property("splice list of trees into block (3)") = forAll { (trees: List[Tree], tree: Tree) => - q"{ ..$trees; $tree }" ≈ Block(trees, tree) - } - - def assertSameAnnots(tree: {def mods: Modifiers}, annots: List[Tree]) = - assert(tree.mods.annotations ≈ annots, - s"${tree.mods.annotations} =/= ${annots}") - - def assertSameAnnots(tree1: {def mods: Modifiers}, tree2: {def mods: Modifiers}) = - assert(tree1.mods.annotations ≈ tree2.mods.annotations, - s"${tree1.mods.annotations} =/= ${tree2.mods.annotations}") - - property("splice type name into annotation") = test { - val name = TypeName("annot") - assertSameAnnots(q"@$name def foo", List(annot(name))) - } - - property("splice ident into annotation") = test { - val name = TypeName("annot") - val ident = Ident(name) - assertSameAnnots(q"@$ident def foo", List(annot(name))) - } - - property("splice idents into annotation") = test { - val idents = List(Ident(TypeName("annot1")), Ident(TypeName("annot2"))) - assertSameAnnots(q"@..$idents def foo", - idents.map { ident => Apply(Select(New(ident), nme.CONSTRUCTOR), List()) }) - } - - property("splice constructor calls into annotation") = test { - val ctorcalls = List(annot("a1"), annot("a2")) - assertSameAnnots(q"@..$ctorcalls def foo", ctorcalls) - } - - property("splice multiple annotations (1)") = test { - val annot1 = annot("a1") - val annot2 = annot("a2") - val res = q"@$annot1 @$annot2 def foo" - assertSameAnnots(res, List(annot1, annot2)) - } - - property("splice multiple annotations (2)") = test { - val annot1 = annot("a1") - val annots = List(annot("a2"), annot("a3")) - val res = q"@$annot1 @..$annots def foo" - assertSameAnnots(res, annot1 :: annots) - } - - property("splice annotations with arguments (1)") = test { - val a = annot("a", List(q"x")) - assertSameAnnots(q"@$a def foo", q"@a(x) def foo") - } - - property("splice annotations with arguments (2)") = test { - val a = newTypeName("a") - assertSameAnnots(q"@$a(x) def foo", q"@a(x) def foo") - } - - property("splice annotations with arguments (3") = test { - val a = Ident(newTypeName("a")) - assertSameAnnots(q"@$a(x) def foo", q"@a(x) def foo") - } - - property("can't splice annotations with arguments specificed twice") = test { - val a = annot("a", List(q"x")) - assertThrows[IllegalArgumentException] { - q"@$a(y) def foo" - } + blockInvariant(q"{ ..$trees; $tree }", trees :+ tree) } property("splice term into brackets") = test { @@ -331,11 +155,4 @@ object TermConstructionProps extends QuasiquoteProperties("term construction") { val empty = List[Tree]() assert(q"(..$empty)" ≈ q"()") } - - property("splice improper tree into annot") = test { - val t = tq"Foo[Baz]" - assertThrows[IllegalArgumentException] { - q"@$t def foo" - } - } } diff --git a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala index 114c9f112b..132baeb977 100644 --- a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala @@ -7,7 +7,6 @@ import scala.reflect.runtime.universe._ import Flag._ object TermDeconstructionProps extends QuasiquoteProperties("term deconstruction") { - property("f(..x) = f") = test { assertThrows[MatchError] { val q"f(..$argss)" = q"f" @@ -44,56 +43,6 @@ object TermDeconstructionProps extends QuasiquoteProperties("term deconstruction argss ≈ List() } - property("@$annot def foo") = forAll { (annotName: TypeName) => - val q"@$annot def foo" = q"@$annotName def foo" - annot ≈ Apply(Select(New(Ident(annotName)), nme.CONSTRUCTOR), List()) - } - - property("@$annot(..$args) def foo") = forAll { (annotName: TypeName, tree: Tree) => - val q"@$annot(..$args) def foo" = q"@$annotName($tree) def foo" - annot ≈ Ident(annotName) && args ≈ List(tree) - } - - property("@..$annots def foo") = test { - val a = annot("a") - val b = annot("b") - val q"@..$annots def foo" = q"@$a @$b def foo" - annots ≈ List(a, b) - } - - property("@$annot @..$annots def foo") = test { - val a = annot("a") - val b = annot("b") - val c = annot("c") - val q"@$first @..$rest def foo" = q"@$a @$b @$c def foo" - first ≈ a && rest ≈ List(b, c) - } - - property("class without params") = test { - val q"class $name { ..$body }" = q"class Foo { def bar = 3 }" - assert(body ≈ List(q"def bar = 3")) - } - - property("class constructor") = test { - val q"class $name(...$argss)" = q"class Foo(x: Int)(y: Int)" - assert(argss.length == 2) - } - - property("class parents") = test { - val q"class $name extends ..$parents" = q"class Foo extends Bar with Blah" - assert(parents ≈ List(tq"Bar", tq"Blah")) - } - - property("class selfdef") = test { - val q"class $name { $self => }" = q"class Foo { self: T => }" - assert(self.name ≈ TermName("self") && self.tpt ≈ tq"T") - } - - property("class tparams") = test { - val q"class $name[..$tparams]" = q"class Foo[A, B]" - assert(tparams.map { _.name } == List(TypeName("A"), TypeName("B"))) - } - property("deconstruct unit as tuple") = test { val q"(..$xs)" = q"()" assert(xs.isEmpty) @@ -113,10 +62,4 @@ object TermDeconstructionProps extends QuasiquoteProperties("term deconstruction val q"$x match { case ..$cases }" = q"x match { case 1 => case 2 => }" x ≈ q"x" && cases ≈ List(cq"1 =>", cq"2 =>") } - - property("deconstruct mods") = test { - val mods = Modifiers(IMPLICIT | PRIVATE, TermName("foobar"), Nil) - val q"$mods0 def foo" = q"$mods def foo" - assert(mods0 ≈ mods) - } } \ No newline at end of file diff --git a/test/files/scalacheck/quasiquotes/Test.scala b/test/files/scalacheck/quasiquotes/Test.scala index 2387a9b008..05097711ef 100644 --- a/test/files/scalacheck/quasiquotes/Test.scala +++ b/test/files/scalacheck/quasiquotes/Test.scala @@ -9,4 +9,6 @@ object Test extends Properties("quasiquotes") { include(PatternDeconstructionProps) include(LiftableProps) include(ErrorProps) + include(DefinitionConstructionProps) + include(DefinitionDeconstructionProps) } \ No newline at end of file -- cgit v1.2.3 From 50855467525c92f9fb87bcc5aab50831608b88f7 Mon Sep 17 00:00:00 2001 From: Den Shabalin Date: Wed, 28 Aug 2013 13:12:54 +0200 Subject: add missing copyTypeDef utility function --- src/reflect/scala/reflect/internal/Trees.scala | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index fab1f45358..84818a6f42 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -1657,6 +1657,22 @@ trait Trees extends api.Trees { self: SymbolTable => case t => sys.error("Not a ValDef: " + t + "/" + t.getClass) } + def copyTypeDef(tree: Tree)( + mods: Modifiers = null, + name: Name = null, + tparams: List[TypeDef] = null, + rhs: Tree = null + ): TypeDef = tree match { + case TypeDef(mods0, name0, tparams0, rhs0) => + treeCopy.TypeDef(tree, + if (mods eq null) mods0 else mods, + if (name eq null) name0 else name, + if (tparams eq null) tparams0 else tparams, + if (rhs eq null) rhs0 else rhs + ) + case t => + sys.error("Not a TypeDef: " + t + "/" + t.getClass) + } def copyClassDef(tree: Tree)( mods: Modifiers = null, name: Name = null, -- cgit v1.2.3 From 9f03b67f5ff85dfcb7a7dbe25234c0428cb25175 Mon Sep 17 00:00:00 2001 From: Den Shabalin Date: Wed, 28 Aug 2013 13:55:27 +0200 Subject: rename mkAnnotationCtor into mkAnnotation --- src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala | 4 ++-- src/reflect/scala/reflect/api/BuildUtils.scala | 2 +- src/reflect/scala/reflect/internal/BuildUtils.scala | 2 +- src/reflect/scala/reflect/internal/StdNames.scala | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala index 900237b00d..fe51098f05 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala @@ -198,11 +198,11 @@ trait Reifiers { self: Quasiquotes => val x: TermName = c.freshName() val xToAnnotationCtor = Function( List(ValDef(Modifiers(PARAM), x, TypeTree(), EmptyTree)), - mirrorBuildCall(nme.mkAnnotationCtor, Ident(x), reify(args))) + mirrorBuildCall(nme.mkAnnotation, Ident(x), reify(args))) Apply(Select(tree, nme.map), List(xToAnnotationCtor)) } { case AnnotPlaceholder(tree, _: TreeLocation, _, args) => - mirrorBuildCall(nme.mkAnnotationCtor, tree, reify(args)) + mirrorBuildCall(nme.mkAnnotation, tree, reify(args)) case other => reify(other) } diff --git a/src/reflect/scala/reflect/api/BuildUtils.scala b/src/reflect/scala/reflect/api/BuildUtils.scala index c568cf74c0..e401325dbc 100644 --- a/src/reflect/scala/reflect/api/BuildUtils.scala +++ b/src/reflect/scala/reflect/api/BuildUtils.scala @@ -76,7 +76,7 @@ private[reflect] trait BuildUtils { self: Universe => def setSymbol[T <: Tree](tree: T, sym: Symbol): T - def mkAnnotationCtor(tree: Tree, args: List[Tree]): Tree + def mkAnnotation(tree: Tree, args: List[Tree]): Tree val FlagsAsBits: FlagsAsBitsExtractor diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala index cdebfe52f8..dc3be59acd 100644 --- a/src/reflect/scala/reflect/internal/BuildUtils.scala +++ b/src/reflect/scala/reflect/internal/BuildUtils.scala @@ -69,7 +69,7 @@ trait BuildUtils { self: SymbolTable => def setSymbol[T <: Tree](tree: T, sym: Symbol): T = { tree.setSymbol(sym); tree } - def mkAnnotationCtor(tree: Tree, args: List[Tree]): Tree = tree match { + def mkAnnotation(tree: Tree, args: List[Tree]): Tree = tree match { case ident: Ident => Apply(self.Select(New(ident), nme.CONSTRUCTOR: TermName), args) case call @ Apply(Select(New(ident: Ident), nme.CONSTRUCTOR), _) => if (args.nonEmpty) diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 7a2287664a..4fd35e1f24 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -681,7 +681,7 @@ trait StdNames { val materializeWeakTypeTag: NameType = "materializeWeakTypeTag" val materializeTypeTag: NameType = "materializeTypeTag" val moduleClass : NameType = "moduleClass" - val mkAnnotationCtor: NameType = "mkAnnotationCtor" + val mkAnnotation: NameType = "mkAnnotation" val ne: NameType = "ne" val newArray: NameType = "newArray" val newFreeTerm: NameType = "newFreeTerm" -- cgit v1.2.3 From 34f0f7dd2a5188aa35979cf45f15854ac0d86aa5 Mon Sep 17 00:00:00 2001 From: Den Shabalin Date: Wed, 28 Aug 2013 15:19:19 +0200 Subject: merge flagsFromBits and FlagsAsBits into FlagsRepr --- .../scala/reflect/reify/codegen/GenSymbols.scala | 8 ++++---- .../scala/reflect/reify/codegen/GenTrees.scala | 2 +- .../scala/reflect/reify/utils/Extractors.scala | 18 ++++++++++++++---- .../scala/reflect/reify/utils/NodePrinters.scala | 2 +- .../scala/tools/reflect/quasiquotes/Reifiers.scala | 4 ++-- src/reflect/scala/reflect/api/BuildUtils.scala | 10 +++++----- src/reflect/scala/reflect/internal/BuildUtils.scala | 5 ++--- src/reflect/scala/reflect/internal/StdNames.scala | 3 +-- 8 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala b/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala index 90fb41f80b..3a97089d51 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala @@ -131,9 +131,9 @@ trait GenSymbols { if (sym.isCapturedVariable) { assert(binding.isInstanceOf[Ident], showRaw(binding)) val capturedBinding = referenceCapturedVariable(sym) - Reification(name, capturedBinding, mirrorBuildCall(nme.newFreeTerm, reify(sym.name.toString), capturedBinding, mirrorBuildCall(nme.flagsFromBits, reify(sym.flags)), reify(origin(sym)))) + Reification(name, capturedBinding, mirrorBuildCall(nme.newFreeTerm, reify(sym.name.toString), capturedBinding, mirrorBuildCall(nme.FlagsRepr, reify(sym.flags)), reify(origin(sym)))) } else { - Reification(name, binding, mirrorBuildCall(nme.newFreeTerm, reify(sym.name.toString), binding, mirrorBuildCall(nme.flagsFromBits, reify(sym.flags)), reify(origin(sym)))) + Reification(name, binding, mirrorBuildCall(nme.newFreeTerm, reify(sym.name.toString), binding, mirrorBuildCall(nme.FlagsRepr, reify(sym.flags)), reify(origin(sym)))) } } @@ -142,7 +142,7 @@ trait GenSymbols { if (reifyDebug) println("Free type: %s (%s)".format(sym, sym.accurateKindString)) state.reificationIsConcrete = false val name: TermName = nme.REIFY_FREE_PREFIX append sym.name - Reification(name, binding, mirrorBuildCall(nme.newFreeType, reify(sym.name.toString), mirrorBuildCall(nme.flagsFromBits, reify(sym.flags)), reify(origin(sym)))) + Reification(name, binding, mirrorBuildCall(nme.newFreeType, reify(sym.name.toString), mirrorBuildCall(nme.FlagsRepr, reify(sym.flags)), reify(origin(sym)))) } def reifySymDef(sym: Symbol): Tree = @@ -150,7 +150,7 @@ trait GenSymbols { if (reifyDebug) println("Sym def: %s (%s)".format(sym, sym.accurateKindString)) val name: TermName = nme.REIFY_SYMDEF_PREFIX append sym.name def reifiedOwner = if (sym.owner.isLocatable) reify(sym.owner) else reifySymDef(sym.owner) - Reification(name, Ident(sym), mirrorBuildCall(nme.newNestedSymbol, reifiedOwner, reify(sym.name), reify(sym.pos), mirrorBuildCall(nme.flagsFromBits, reify(sym.flags)), reify(sym.isClass))) + Reification(name, Ident(sym), mirrorBuildCall(nme.newNestedSymbol, reifiedOwner, reify(sym.name), reify(sym.pos), mirrorBuildCall(nme.FlagsRepr, reify(sym.flags)), reify(sym.isClass))) } case class Reification(name: Name, binding: Tree, tree: Tree) diff --git a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala index 3507c2a173..ff20d529aa 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala @@ -84,7 +84,7 @@ trait GenTrees { } def reifyModifiers(m: global.Modifiers) = - mirrorFactoryCall(nme.Modifiers, mirrorBuildCall(nme.flagsFromBits, reify(m.flags)), reify(m.privateWithin), reify(m.annotations)) + mirrorFactoryCall(nme.Modifiers, mirrorBuildCall(nme.FlagsRepr, reify(m.flags)), reify(m.privateWithin), reify(m.annotations)) private def spliceTree(tree: Tree): Tree = { tree match { diff --git a/src/compiler/scala/reflect/reify/utils/Extractors.scala b/src/compiler/scala/reflect/reify/utils/Extractors.scala index d5f27dc119..9af8f2de2a 100644 --- a/src/compiler/scala/reflect/reify/utils/Extractors.scala +++ b/src/compiler/scala/reflect/reify/utils/Extractors.scala @@ -164,6 +164,16 @@ trait Extractors { } } + // abstract over possible additional .apply select + // which is sometimes inserted after desugaring of calls + object ApplyCall { + def unapply(tree: Tree): Option[(Tree, List[Tree])] = tree match { + case Apply(Select(id, nme.apply), args) => Some((id, args)) + case Apply(id, args) => Some((id, args)) + case _ => None + } + } + sealed abstract class FreeDefExtractor(acceptTerms: Boolean, acceptTypes: Boolean) { def unapply(tree: Tree): Option[(Tree, TermName, Tree, Long, String)] = { def acceptFreeTermFactory(name: Name) = { @@ -175,10 +185,10 @@ trait Extractors { ValDef(_, name, _, Apply( Select(Select(uref1 @ Ident(_), build1), freeTermFactory), _ :+ - Apply(Select(Select(uref2 @ Ident(_), build2), flagsFromBits), List(Literal(Constant(flags: Long)))) :+ + ApplyCall(Select(Select(uref2 @ Ident(_), build2), flagsRepr), List(Literal(Constant(flags: Long)))) :+ Literal(Constant(origin: String)))) if uref1.name == nme.UNIVERSE_SHORT && build1 == nme.build && acceptFreeTermFactory(freeTermFactory) && - uref2.name == nme.UNIVERSE_SHORT && build2 == nme.build && flagsFromBits == nme.flagsFromBits => + uref2.name == nme.UNIVERSE_SHORT && build2 == nme.build && flagsRepr == nme.FlagsRepr => Some((uref1, name, reifyBinding(tree), flags, origin)) case _ => None @@ -208,10 +218,10 @@ trait Extractors { _, _, _, - Apply(Select(Select(uref2 @ Ident(_), build2), flagsFromBits), List(Literal(Constant(flags: Long)))), + ApplyCall(Select(Select(uref2 @ Ident(_), build2), flagsRepr), List(Literal(Constant(flags: Long)))), Literal(Constant(isClass: Boolean))))) if uref1.name == nme.UNIVERSE_SHORT && build1 == nme.build && newNestedSymbol == nme.newNestedSymbol && - uref2.name == nme.UNIVERSE_SHORT && build2 == nme.build && flagsFromBits == nme.flagsFromBits => + uref2.name == nme.UNIVERSE_SHORT && build2 == nme.build && flagsRepr == nme.FlagsRepr => Some((uref1, name, flags, isClass)) case _ => None diff --git a/src/compiler/scala/reflect/reify/utils/NodePrinters.scala b/src/compiler/scala/reflect/reify/utils/NodePrinters.scala index 0903bc481c..e37b861461 100644 --- a/src/compiler/scala/reflect/reify/utils/NodePrinters.scala +++ b/src/compiler/scala/reflect/reify/utils/NodePrinters.scala @@ -32,7 +32,7 @@ trait NodePrinters { s = "List\\[List\\[.*?\\].*?\\]".r.replaceAllIn(s, "List") s = "List\\[.*?\\]".r.replaceAllIn(s, "List") s = s.replace("immutable.this.Nil", "List()") - s = """build\.flagsFromBits\((\d+)[lL]\)""".r.replaceAllIn(s, m => { + s = """build\.FlagsRepr\((\d+)[lL]\)""".r.replaceAllIn(s, m => { flagsAreUsed = true show(m.group(1).toLong) }) diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala index fe51098f05..47ccec17d6 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala @@ -229,7 +229,7 @@ trait Reifiers { self: Quasiquotes => case _ :: (second, _) :: Nil => c.abort(second.pos, "Can't splice multiple modifiers, consider merging them into a single modifiers instance") case _ => - val baseFlags = reifyBuildCall(nme.flagsFromBits, m.flags) + val baseFlags = reifyBuildCall(nme.FlagsRepr, m.flags) val reifiedFlags = flags.foldLeft[Tree](baseFlags) { case (flag, (tree, _)) => Apply(Select(flag, nme.OR), List(tree)) } mirrorFactoryCall(nme.Modifiers, reifiedFlags, reify(m.privateWithin), reifyAnnotList(annots)) } @@ -282,7 +282,7 @@ trait Reifiers { self: Quasiquotes => case _ :: second :: rest => c.abort(second.pos, "Can't extract multiple modifiers together, consider extracting a single modifiers instance") case Nil => - mirrorFactoryCall(nme.Modifiers, reifyBuildCall(nme.FlagsAsBits, m.flags), + mirrorFactoryCall(nme.Modifiers, reifyBuildCall(nme.FlagsRepr, m.flags), reify(m.privateWithin), reifyAnnotList(m.annotations)) } } diff --git a/src/reflect/scala/reflect/api/BuildUtils.scala b/src/reflect/scala/reflect/api/BuildUtils.scala index e401325dbc..394cbf55c5 100644 --- a/src/reflect/scala/reflect/api/BuildUtils.scala +++ b/src/reflect/scala/reflect/api/BuildUtils.scala @@ -58,8 +58,6 @@ private[reflect] trait BuildUtils { self: Universe => */ def setAnnotations[S <: Symbol](sym: S, annots: List[Annotation]): S - def flagsFromBits(bits: Long): FlagSet - def This(sym: Symbol): Tree def Select(qualifier: Tree, sym: Symbol): Select @@ -78,10 +76,12 @@ private[reflect] trait BuildUtils { self: Universe => def mkAnnotation(tree: Tree, args: List[Tree]): Tree - val FlagsAsBits: FlagsAsBitsExtractor - trait FlagsAsBitsExtractor { - def unapply(flags: Long): Option[Long] + val FlagsRepr: FlagsReprExtractor + + trait FlagsReprExtractor { + def apply(value: Long): FlagSet + def unapply(flags: Long): Some[Long] } val TypeApplied: TypeAppliedExtractor diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala index dc3be59acd..73b7b79fdb 100644 --- a/src/reflect/scala/reflect/internal/BuildUtils.scala +++ b/src/reflect/scala/reflect/internal/BuildUtils.scala @@ -47,8 +47,6 @@ trait BuildUtils { self: SymbolTable => def setTypeSignature[S <: Symbol](sym: S, tpe: Type): S = sym.setTypeSignature(tpe) - def flagsFromBits(bits: Long): FlagSet = bits - def This(sym: Symbol): Tree = self.This(sym) def Select(qualifier: Tree, sym: Symbol): Select = self.Select(qualifier, sym) @@ -78,7 +76,8 @@ trait BuildUtils { self: SymbolTable => case _ => throw new IllegalArgumentException(s"Tree ${showRaw(tree)} isn't a correct representation of annotation, consider passing Ident as a first argument") } - object FlagsAsBits extends FlagsAsBitsExtractor { + object FlagsRepr extends FlagsReprExtractor { + def apply(bits: Long): FlagSet = bits def unapply(flags: Long): Some[Long] = Some(flags) } diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 4fd35e1f24..77571d02da 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -568,7 +568,7 @@ trait StdNames { val EmptyPackageClass: NameType = "EmptyPackageClass" val False : NameType = "False" val Flag : NameType = "Flag" - val FlagsAsBits: NameType = "FlagsAsBits" + val FlagsRepr: NameType = "FlagsRepr" val Ident: NameType = "Ident" val Import: NameType = "Import" val Literal: NameType = "Literal" @@ -650,7 +650,6 @@ trait StdNames { val filter: NameType = "filter" val finalize_ : NameType = "finalize" val find_ : NameType = "find" - val flagsFromBits : NameType = "flagsFromBits" val flatMap: NameType = "flatMap" val flatten: NameType = "flatten" val foldLeft: NameType = "foldLeft" -- cgit v1.2.3 From 0d7c4a506fbb566e4f65966b67759b61d688bbea Mon Sep 17 00:00:00 2001 From: Den Shabalin Date: Thu, 29 Aug 2013 14:08:13 +0200 Subject: use NoMods and NoFlags for reification of empty values --- .../scala/reflect/reify/codegen/GenTrees.scala | 6 +- .../scala/tools/reflect/quasiquotes/Reifiers.scala | 83 +++++++++++----------- src/reflect/scala/reflect/internal/StdNames.scala | 1 + 3 files changed, 49 insertions(+), 41 deletions(-) diff --git a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala index ff20d529aa..9de8451873 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala @@ -83,8 +83,12 @@ trait GenTrees { reifyProduct(tree) } + def reifyFlags(flags: FlagSet) = + if (flags != 0) reifyBuildCall(nme.FlagsRepr, flags) else mirrorSelect(nme.NoFlags) + def reifyModifiers(m: global.Modifiers) = - mirrorFactoryCall(nme.Modifiers, mirrorBuildCall(nme.FlagsRepr, reify(m.flags)), reify(m.privateWithin), reify(m.annotations)) + if (m == NoMods) mirrorSelect(nme.NoMods) + else mirrorFactoryCall(nme.Modifiers, reifyFlags(m.flags), reify(m.privateWithin), reify(m.annotations)) private def spliceTree(tree: Tree): Tree = { tree match { diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala index 47ccec17d6..e9c56f1191 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala @@ -206,34 +206,36 @@ trait Reifiers { self: Quasiquotes => case other => reify(other) } - override def reifyModifiers(m: Modifiers) = { - val (modsPlaceholders, annots) = m.annotations.partition { - case ModsPlaceholder(_, _, _) => true - case _ => false - } - val (mods, flags) = modsPlaceholders.map { - case ModsPlaceholder(tree, location, card) => (tree, location) - }.partition { case (tree, location) => - location match { - case ModsLocation => true - case FlagsLocation => false - case _ => c.abort(tree.pos, s"$flagsType or $modsType expected but ${tree.tpe} found") + override def reifyModifiers(m: Modifiers) = + if (m == NoMods) super.reifyModifiers(m) + else { + val (modsPlaceholders, annots) = m.annotations.partition { + case ModsPlaceholder(_, _, _) => true + case _ => false + } + val (mods, flags) = modsPlaceholders.map { + case ModsPlaceholder(tree, location, card) => (tree, location) + }.partition { case (tree, location) => + location match { + case ModsLocation => true + case FlagsLocation => false + case _ => c.abort(tree.pos, s"$flagsType or $modsType expected but ${tree.tpe} found") + } + } + mods match { + case (tree, _) :: Nil => + if (flags.nonEmpty) c.abort(flags(0)._1.pos, "Can't splice flags together with modifiers, consider merging flags into modifiers") + if (annots.nonEmpty) c.abort(tree.pos, "Can't splice modifiers together with annotations, consider merging annotations into modifiers") + ensureNoExplicitFlags(m, tree.pos) + tree + case _ :: (second, _) :: Nil => + c.abort(second.pos, "Can't splice multiple modifiers, consider merging them into a single modifiers instance") + case _ => + val baseFlags = reifyFlags(m.flags) + val reifiedFlags = flags.foldLeft[Tree](baseFlags) { case (flag, (tree, _)) => Apply(Select(flag, nme.OR), List(tree)) } + mirrorFactoryCall(nme.Modifiers, reifiedFlags, reify(m.privateWithin), reifyAnnotList(annots)) } } - mods match { - case (tree, _) :: Nil => - if (flags.nonEmpty) c.abort(flags(0)._1.pos, "Can't splice flags together with modifiers, consider merging flags into modifiers") - if (annots.nonEmpty) c.abort(tree.pos, "Can't splice modifiers together with annotations, consider merging annotations into modifiers") - ensureNoExplicitFlags(m, tree.pos) - tree - case _ :: (second, _) :: Nil => - c.abort(second.pos, "Can't splice multiple modifiers, consider merging them into a single modifiers instance") - case _ => - val baseFlags = reifyBuildCall(nme.FlagsRepr, m.flags) - val reifiedFlags = flags.foldLeft[Tree](baseFlags) { case (flag, (tree, _)) => Apply(Select(flag, nme.OR), List(tree)) } - mirrorFactoryCall(nme.Modifiers, reifiedFlags, reify(m.privateWithin), reifyAnnotList(annots)) - } - } } class UnapplyReifier extends Reifier { @@ -272,19 +274,20 @@ trait Reifiers { self: Quasiquotes => reify(other) } - override def reifyModifiers(m: Modifiers) = { - val mods = m.annotations.collect { case ModsPlaceholder(tree, _, _) => tree } - mods match { - case tree :: Nil => - if (m.annotations.length != 1) c.abort(tree.pos, "Can't extract modifiers together with annotations, consider extracting just modifiers") - ensureNoExplicitFlags(m, tree.pos) - tree - case _ :: second :: rest => - c.abort(second.pos, "Can't extract multiple modifiers together, consider extracting a single modifiers instance") - case Nil => - mirrorFactoryCall(nme.Modifiers, reifyBuildCall(nme.FlagsRepr, m.flags), - reify(m.privateWithin), reifyAnnotList(m.annotations)) + override def reifyModifiers(m: Modifiers) = + if (m == NoMods) super.reifyModifiers(m) + else { + val mods = m.annotations.collect { case ModsPlaceholder(tree, _, _) => tree } + mods match { + case tree :: Nil => + if (m.annotations.length != 1) c.abort(tree.pos, "Can't extract modifiers together with annotations, consider extracting just modifiers") + ensureNoExplicitFlags(m, tree.pos) + tree + case _ :: second :: rest => + c.abort(second.pos, "Can't extract multiple modifiers together, consider extracting a single modifiers instance") + case Nil => + mirrorFactoryCall(nme.Modifiers, reifyFlags(m.flags), reify(m.privateWithin), reifyAnnotList(m.annotations)) + } } - } } -} \ No newline at end of file +} diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 77571d02da..4811010815 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -578,6 +578,7 @@ trait StdNames { val New: NameType = "New" val NoFlags: NameType = "NoFlags" val NoSymbol: NameType = "NoSymbol" + val NoMods: NameType = "NoMods" val Nothing: NameType = "Nothing" val Null: NameType = "Null" val Object: NameType = "Object" -- cgit v1.2.3 From eabe3b408d29a026881ac90f920a4face1be86fd Mon Sep 17 00:00:00 2001 From: Den Shabalin Date: Thu, 29 Aug 2013 17:21:44 +0200 Subject: move mkNew from tools.nsc.ast.TreeGen to reflect.internal.TreeGen This is needed for quasiquotes to implement SyntacticNew combinator. --- src/compiler/scala/tools/nsc/ast/TreeGen.scala | 36 ------------------------ src/reflect/scala/reflect/internal/TreeGen.scala | 35 +++++++++++++++++++++++ 2 files changed, 35 insertions(+), 36 deletions(-) diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index ad1977b9aa..7122e864a4 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -264,42 +264,6 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { mkNew(Nil, emptyValDef, stats1, NoPosition, NoPosition) } - /** Create positioned tree representing an object creation Date: Thu, 29 Aug 2013 17:35:28 +0200 Subject: minor changes to TreeGen.mkTemplate 1. Make superPos parameter optional with default value NoPosition 2. use Option instead of List for local optional constructor variable --- src/reflect/scala/reflect/internal/TreeGen.scala | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala index 29c57fc9c0..a7ea8c816f 100644 --- a/src/reflect/scala/reflect/internal/TreeGen.scala +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -324,7 +324,8 @@ abstract class TreeGen extends macros.TreeBuilder { * body * } */ - def mkTemplate(parents: List[Tree], self: ValDef, constrMods: Modifiers, vparamss: List[List[ValDef]], body: List[Tree], superPos: Position): Template = { + def mkTemplate(parents: List[Tree], self: ValDef, constrMods: Modifiers, + vparamss: List[List[ValDef]], body: List[Tree], superPos: Position = NoPosition): Template = { /* Add constructor to template */ // create parameters for as synthetic trees. @@ -348,10 +349,10 @@ abstract class TreeGen extends macros.TreeBuilder { } val lvdefs = evdefs collect { case vdef: ValDef => copyValDef(vdef)(mods = vdef.mods | PRESUPER) } - val constrs = { - if (constrMods hasFlag TRAIT) { - if (body forall treeInfo.isInterfaceMember) List() - else List( + val constr = { + if (constrMods.isTrait) { + if (body forall treeInfo.isInterfaceMember) None + else Some( atPos(wrappingPos(superPos, lvdefs)) ( DefDef(NoMods, nme.MIXIN_CONSTRUCTOR, List(), List(Nil), TypeTree(), Block(lvdefs, Literal(Constant()))))) } else { @@ -365,18 +366,18 @@ abstract class TreeGen extends macros.TreeBuilder { // (the type macros aren't in the trunk yet, but there is a plan for them to land there soon) // this means that we don't know what will be the arguments of the super call // therefore here we emit a dummy which gets populated when the template is named and typechecked - List( + Some( // TODO: previously this was `wrappingPos(superPos, lvdefs ::: argss.flatten)` // is it going to be a problem that we can no longer include the `argss`? atPos(wrappingPos(superPos, lvdefs)) ( DefDef(constrMods, nme.CONSTRUCTOR, List(), vparamss1, TypeTree(), Block(lvdefs ::: List(superCall), Literal(Constant()))))) } } - constrs foreach (ensureNonOverlapping(_, parents ::: gvdefs, focus=false)) + constr foreach (ensureNonOverlapping(_, parents ::: gvdefs, focus=false)) // Field definitions for the class - remove defaults. val fieldDefs = vparamss.flatten map (vd => copyValDef(vd)(mods = vd.mods &~ DEFAULTPARAM, rhs = EmptyTree)) - global.Template(parents, self, gvdefs ::: fieldDefs ::: constrs ::: etdefs ::: rest) + global.Template(parents, self, gvdefs ::: fieldDefs ::: constr ++: etdefs ::: rest) } /** Create positioned tree representing an object creation Date: Thu, 29 Aug 2013 17:44:22 +0200 Subject: reduce amount of clutter on -Yquasiquote-debug --- src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala b/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala index ee99a5e280..1305e25240 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala @@ -41,11 +41,11 @@ abstract class Quasiquotes extends Parsers lazy val universeTypes = new definitions.UniverseDependentTypes(universe) def expandQuasiquote = { - debug(s"\ncode to parse=\n$code\n") + debug(s"\ncode to parse:\n$code\n") val tree = parse(code) - debug(s"parsed tree\n=${tree}\n=${showRaw(tree)}\n") + debug(s"parsed:\n${showRaw(tree)}\n$tree\n") val reified = reify(tree) - debug(s"reified tree\n=${reified}\n=${showRaw(reified)}\n") + debug(s"reified tree:\n$reified\n") reified } } -- cgit v1.2.3 From f9d5e3d4e8cca61ee75072ab13c2935061a1850e Mon Sep 17 00:00:00 2001 From: Den Shabalin Date: Thu, 29 Aug 2013 17:52:02 +0200 Subject: SI-7196 add support for refineStat splicing and extraction --- .../scala/tools/nsc/ast/parser/Parsers.scala | 20 +++++++++------- .../scala/tools/reflect/quasiquotes/Parsers.scala | 7 ++++++ .../tools/reflect/quasiquotes/Placeholders.scala | 6 ++--- .../scala/tools/reflect/quasiquotes/Reifiers.scala | 28 ++++++++++++---------- src/reflect/scala/reflect/api/BuildUtils.scala | 3 +++ .../scala/reflect/internal/BuildUtils.scala | 12 ++++++++++ src/reflect/scala/reflect/internal/StdNames.scala | 6 +++-- .../quasiquotes/TypeConstructionProps.scala | 5 ++++ .../quasiquotes/TypeDeconstructionProps.scala | 5 ++++ 9 files changed, 67 insertions(+), 25 deletions(-) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index e5101a27a8..158fa147b6 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -3012,19 +3012,23 @@ self => def refineStatSeq(): List[Tree] = checkNoEscapingPlaceholders { val stats = new ListBuffer[Tree] while (!isStatSeqEnd) { - if (isDclIntro) { // don't IDE hook - stats ++= joinComment(defOrDcl(in.offset, NoMods)) - } else if (!isStatSep) { - syntaxErrorOrIncomplete( - "illegal start of declaration"+ - (if (inFunReturnType) " (possible cause: missing `=' in front of current method body)" - else ""), skipIt = true) - } + stats ++= refineStat() if (in.token != RBRACE) acceptStatSep() } stats.toList } + def refineStat(): List[Tree] = + if (isDclIntro) { // don't IDE hook + joinComment(defOrDcl(in.offset, NoMods)) + } else if (!isStatSep) { + syntaxErrorOrIncomplete( + "illegal start of declaration"+ + (if (inFunReturnType) " (possible cause: missing `=' in front of current method body)" + else ""), skipIt = true) + Nil + } else Nil + /** overridable IDE hook for local definitions of blockStatSeq * Here's an idea how to fill in start and end positions. def localDef : List[Tree] = { diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala index 18a806e5ff..d04b65545d 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala @@ -112,6 +112,13 @@ trait Parsers { self: Quasiquotes => case _ => Nil } + + override def refineStat(): List[Tree] = + if (isHole && !isDclIntro) { + val result = ValDef(NoMods, in.name, Ident(tpnme.QUASIQUOTE_REFINE_STAT), EmptyTree) :: Nil + in.nextToken() + result + } else super.refineStat() } } diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala index b3ac1e293a..5d7edcd75e 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala @@ -127,9 +127,9 @@ trait Placeholders { self: Quasiquotes => } } - object ClassPlaceholder { - def unapply(tree: Tree): Option[Tree] = tree match { - case ClassDef(_, _, _, _) => Some(tree) + object RefineStatPlaceholder { + def unapply(tree: Tree): Option[(Tree, Location, Cardinality)] = tree match { + case ValDef(_, Placeholder(tree, location, card), Ident(tpnme.QUASIQUOTE_REFINE_STAT), _) => Some((tree, location, card)) case _ => None } } diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala index e9c56f1191..d4bc2d67c6 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala @@ -35,13 +35,9 @@ trait Reifiers { self: Quasiquotes => reified } - override def reifyTree(tree: Tree): Tree = { - val reified = - reifyTreePlaceholder(tree) orElse - reifyTreeSyntactically(tree) - //println(s"reified ${showRaw(tree)} as $reified") - reified - } + override def reifyTree(tree: Tree): Tree = + reifyTreePlaceholder(tree) orElse + reifyTreeSyntactically(tree) def reifyTreePlaceholder(tree: Tree): Tree = tree match { case Placeholder(tree, TreeLocation(_), _) if isReifyingExpressions => tree @@ -50,10 +46,18 @@ trait Reifiers { self: Quasiquotes => case TuplePlaceholder(args) => reifyTuple(args) case TupleTypePlaceholder(args) => reifyTupleType(args) case CasePlaceholder(tree, location, _) => reifyCase(tree, location) - case ClassPlaceholder(tree) => reifyClass(tree) + case RefineStatPlaceholder(tree, _, _) => reifyRefineStat(tree) case _ => EmptyTree } + override def reifyTreeSyntactically(tree: Tree) = tree match { + case SyntacticClassDef(mods, name, tparams, constrmods, vparamss, parents, selfdef, body) => + reifyBuildCall(nme.SyntacticClassDef, mods, name, tparams, constrmods, vparamss, + parents, selfdef, body) + case _ => + super.reifyTreeSyntactically(tree) + } + override def reifyName(name: Name): Tree = name match { case Placeholder(tree, location, _) => if (holesHaveTypes && !(location.tpe <:< nameType)) c.abort(tree.pos, s"$nameType expected but ${location.tpe} found") @@ -86,10 +90,7 @@ trait Reifiers { self: Quasiquotes => case _ => reifyBuildCall(nme.TupleTypeN, args) } - def reifyClass(tree: Tree) = { - val SyntacticClassDef(mods, name, tparams, constrmods, argss, parents, selfval, body) = tree - reifyBuildCall(nme.SyntacticClassDef, mods, name, tparams, constrmods, argss, parents, selfval, body) - } + def reifyRefineStat(tree: Tree) = tree /** Splits list into a list of groups where subsequent elements are considered * similar by the corresponding function. @@ -143,6 +144,7 @@ trait Reifiers { self: Quasiquotes => override def reifyList(xs: List[Any]): Tree = reifyMultiCardinalityList(xs) { case Placeholder(tree, _, DotDot) => tree case CasePlaceholder(tree, _, DotDot) => tree + case RefineStatPlaceholder(tree, _, DotDot) => reifyRefineStat(tree) case List(Placeholder(tree, _, DotDotDot)) => tree } { reify(_) @@ -236,6 +238,8 @@ trait Reifiers { self: Quasiquotes => mirrorFactoryCall(nme.Modifiers, reifiedFlags, reify(m.privateWithin), reifyAnnotList(annots)) } } + + override def reifyRefineStat(tree: Tree) = mirrorBuildCall(nme.mkRefineStat, tree) } class UnapplyReifier extends Reifier { diff --git a/src/reflect/scala/reflect/api/BuildUtils.scala b/src/reflect/scala/reflect/api/BuildUtils.scala index 394cbf55c5..03980f5f0a 100644 --- a/src/reflect/scala/reflect/api/BuildUtils.scala +++ b/src/reflect/scala/reflect/api/BuildUtils.scala @@ -76,6 +76,9 @@ private[reflect] trait BuildUtils { self: Universe => def mkAnnotation(tree: Tree, args: List[Tree]): Tree + def mkRefineStat(stat: Tree): Tree + + def mkRefineStat(stats: List[Tree]): List[Tree] val FlagsRepr: FlagsReprExtractor diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala index 73b7b79fdb..208a61d9d3 100644 --- a/src/reflect/scala/reflect/internal/BuildUtils.scala +++ b/src/reflect/scala/reflect/internal/BuildUtils.scala @@ -76,6 +76,18 @@ trait BuildUtils { self: SymbolTable => case _ => throw new IllegalArgumentException(s"Tree ${showRaw(tree)} isn't a correct representation of annotation, consider passing Ident as a first argument") } + def mkRefineStat(stat: Tree): Tree = { + stat match { + case dd: DefDef => require(dd.rhs.isEmpty, "can't use DefDef with non-empty body as refine stat") + case vd: ValDef => require(vd.rhs.isEmpty, "can't use ValDef with non-empty rhs as refine stat") + case td: TypeDef => + case _ => throw new IllegalArgumentException(s"not legal refine stat: $stat") + } + stat + } + + def mkRefineStat(stats: List[Tree]): List[Tree] = stats.map(mkRefineStat) + object FlagsRepr extends FlagsReprExtractor { def apply(bits: Long): FlagSet = bits def unapply(flags: Long): Some[Long] = Some(flags) diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 4811010815..eb24706f29 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -247,8 +247,9 @@ trait StdNames { final val Quasiquote: NameType = "Quasiquote" // quasiquote-specific names - final val QUASIQUOTE_MODS: NameType = "$quasiquote$mods$" - final val QUASIQUOTE_TUPLE: NameType = "$quasiquote$tuple$" + final val QUASIQUOTE_MODS: NameType = "$quasiquote$mods$" + final val QUASIQUOTE_TUPLE: NameType = "$quasiquote$tuple$" + final val QUASIQUOTE_REFINE_STAT: NameType = "$quasiquote$refine$stat$" // Annotation simple names, used in Namer final val BeanPropertyAnnot: NameType = "BeanProperty" @@ -682,6 +683,7 @@ trait StdNames { val materializeTypeTag: NameType = "materializeTypeTag" val moduleClass : NameType = "moduleClass" val mkAnnotation: NameType = "mkAnnotation" + val mkRefineStat: NameType = "mkRefineStat" val ne: NameType = "ne" val newArray: NameType = "newArray" val newFreeTerm: NameType = "newFreeTerm" diff --git a/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala b/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala index 535ed8ecbf..459d733733 100644 --- a/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala @@ -22,4 +22,9 @@ object TypeConstructionProps extends QuasiquoteProperties("type construction") assert(tq"(..$ts)" ≈ tq"Tuple2[t1, t2]") assert(tq"(t0, ..$ts)" ≈ tq"Tuple3[t0, t1, t2]") } + + 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 }") + } } \ No newline at end of file diff --git a/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala index 6ab699d4f0..5c919e55cb 100644 --- a/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala @@ -26,4 +26,9 @@ object TypeDeconstructionProps extends QuasiquoteProperties("type deconstruction val tq"($head, ..$tail)" = tq"(t0, t1, t2)" assert(head ≈ tq"t0" && tail ≈ List(tq"t1", tq"t2")) } + + property("refined type") = test { + val tq"T { ..$stats }" = tq"T { def foo; val x: Int; type Y = String }" + assert(stats ≈ (q"def foo" :: q"val x: Int" :: q"type Y = String" :: Nil)) + } } \ No newline at end of file -- cgit v1.2.3 From 901cdc1855e8e80957b18add4991736b9f458d77 Mon Sep 17 00:00:00 2001 From: Den Shabalin Date: Mon, 2 Sep 2013 15:51:56 +0200 Subject: refine block and applied/typeapplied splicing/matching semantics 1. blocks now match single term-level expressions to account for automatic block elimination. E.g. val q"{ ..$stats }" = q"foo" will match into stats = List(q"foo"). This is useful to uniformly deal with blocks on term level. 2. blocks in quasiquotes collapse into single expressions 3. Applied and TypeApplied now have constructors too which helps to unify matching and extraction in quasiquote reifier 4. TypeApplied now matches AppliedTypeTree too 5. Add Syntactic prefix to Applied and TypeApplied --- .../scala/tools/nsc/ast/parser/TreeBuilder.scala | 6 +--- .../scala/tools/reflect/quasiquotes/Parsers.scala | 5 +++ .../scala/tools/reflect/quasiquotes/Reifiers.scala | 36 ++++++++------------- .../tools/selectivecps/SelectiveCPSTransform.scala | 8 ++--- src/reflect/scala/reflect/api/BuildUtils.scala | 21 ++++++++---- .../scala/reflect/internal/BuildUtils.scala | 37 ++++++++++++++-------- src/reflect/scala/reflect/internal/StdNames.scala | 5 +-- src/reflect/scala/reflect/internal/TreeGen.scala | 7 ++++ .../quasiquotes/DefinitionConstructionProps.scala | 2 +- .../quasiquotes/PatternConstructionProps.scala | 2 +- .../quasiquotes/TermConstructionProps.scala | 8 +++-- .../quasiquotes/TermDeconstructionProps.scala | 5 +++ 12 files changed, 82 insertions(+), 60 deletions(-) diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index ed694023d7..ed7c0d1a0a 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -229,11 +229,7 @@ abstract class TreeBuilder { } /** Create block of statements `stats` */ - def makeBlock(stats: List[Tree]): Tree = - if (stats.isEmpty) Literal(Constant(())) - else if (!stats.last.isTerm) Block(stats, Literal(Constant(()))) - else if (stats.length == 1) stats.head - else Block(stats.init, stats.last) + def makeBlock(stats: List[Tree]): Tree = gen.mkBlock(stats) def makeFilter(tree: Tree, condition: Tree, scrutineeName: String): Tree = { val cases = List( diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala index d04b65545d..fe0809c869 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala @@ -79,6 +79,11 @@ trait Parsers { self: Quasiquotes => } else super.caseClause() + override def caseBlock(): Tree = super.caseBlock() match { + case Block(Nil, expr) => expr + case other => other + } + def isHole: Boolean = isIdent && holeMap.contains(in.name) override def isAnnotation: Boolean = super.isAnnotation || (isHole && lookingAhead { isAnnotation }) diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala index d4bc2d67c6..62c246366d 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala @@ -7,7 +7,7 @@ import scala.reflect.internal.Flags._ trait Reifiers { self: Quasiquotes => import global._ - import global.build.SyntacticClassDef + import global.build.{SyntacticClassDef, SyntacticBlock, SyntacticApplied, SyntacticTypeApplied} import global.treeInfo._ import global.definitions._ import Cardinality._ @@ -54,6 +54,14 @@ trait Reifiers { self: Quasiquotes => case SyntacticClassDef(mods, name, tparams, constrmods, vparamss, parents, selfdef, body) => reifyBuildCall(nme.SyntacticClassDef, mods, name, tparams, constrmods, vparamss, parents, selfdef, body) + case SyntacticApplied(fun, argss) if argss.length > 1 => + reifyBuildCall(nme.SyntacticApplied, fun, argss) + case SyntacticApplied(fun, argss @ (_ :+ (_ :+ Placeholder(_, _, DotDotDot)))) => + reifyBuildCall(nme.SyntacticApplied, fun, argss) + case SyntacticTypeApplied(fun, targs) if targs.nonEmpty => + reifyBuildCall(nme.SyntacticTypeApplied, fun, targs) + case Block(stats, last) => + reifyBuildCall(nme.SyntacticBlock, stats :+ last) case _ => super.reifyTreeSyntactically(tree) } @@ -169,19 +177,10 @@ trait Reifiers { self: Quasiquotes => def isReifyingExpressions = true override def reifyTreeSyntactically(tree: Tree): Tree = tree match { - case Block(stats, p @ Placeholder(_, _, _)) => reifyBuildCall(nme.Block, stats :+ p) - case Apply(f, List(Placeholder(argss, _, DotDotDot))) => reifyCallWithArgss(f, argss) - case RefTree(qual, SymbolPlaceholder(tree)) => mirrorBuildCall(nme.RefTree, reify(qual), tree) - case _ => super.reifyTreeSyntactically(tree) - } - - def reifyCallWithArgss(f: Tree, argss: Tree) = { - val f1 = reifyTree(f) - val foldLeftF1 = Apply(TypeApply(Select(argss, nme.foldLeft), List(Select(u, tpnme.Tree))), List(f1)) - val uDotApply = Function( - List(gen.mkSyntheticParam(nme.x_1), gen.mkSyntheticParam(nme.x_2)), - Apply(Select(u, nme.Apply), List(Ident(nme.x_1), Ident(nme.x_2)))) - Apply(foldLeftF1, List(uDotApply)) + case RefTree(qual, SymbolPlaceholder(tree)) => + mirrorBuildCall(nme.RefTree, reify(qual), tree) + case _ => + super.reifyTreeSyntactically(tree) } override def reifyMultiCardinalityList[T](xs: List[T])(fill: PartialFunction[T, Tree])(fallback: T => Tree): Tree = xs match { @@ -245,15 +244,6 @@ trait Reifiers { self: Quasiquotes => class UnapplyReifier extends Reifier { def isReifyingExpressions = false - override def reifyTreeSyntactically(tree: Tree): Tree = tree match { - case treeInfo.Applied(fun, Nil, argss) if fun != tree && !tree.isInstanceOf[AppliedTypeTree] => - reifyBuildCall(nme.Applied, fun, argss) - case treeInfo.Applied(fun, targs, argss) if fun != tree & !tree.isInstanceOf[AppliedTypeTree] => - mirrorBuildCall(nme.Applied, reifyBuildCall(nme.TypeApplied, fun, targs), reifyList(argss)) - case _ => - super.reifyTreeSyntactically(tree) - } - override def scalaFactoryCall(name: String, args: Tree*): Tree = call("scala." + name, args: _*) diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala index 908ffb3713..a2c2ebc3e3 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSTransform.scala @@ -330,8 +330,6 @@ abstract class SelectiveCPSTransform extends PluginComponent with } } - def mkBlock(stms: List[Tree], expr: Tree) = if (stms.nonEmpty) Block(stms, expr) else expr - try { if (specialCaseTrivial) { debuglog("will optimize possible tail call: " + bodyExpr) @@ -350,9 +348,9 @@ abstract class SelectiveCPSTransform extends PluginComponent with val argSym = currentOwner.newValue(vd.symbol.name.toTermName).setInfo(tpe) val argDef = localTyper.typed(ValDef(argSym, Select(ctxRef, ctxRef.tpe.member(cpsNames.getTrivialValue)))) val switchExpr = localTyper.typedPos(vd.symbol.pos) { - val body2 = mkBlock(bodyStms, bodyExpr).duplicate // dup before typing! + val body2 = gen.mkBlock(bodyStms :+ bodyExpr).duplicate // dup before typing! If(Select(ctxRef, ctxSym.tpe.member(cpsNames.isTrivial)), - applyTrivial(argSym, mkBlock(argDef::bodyStms, bodyExpr)), + applyTrivial(argSym, gen.mkBlock((argDef :: bodyStms) :+ bodyExpr)), applyCombinatorFun(ctxRef, body2)) } (List(ctxDef), switchExpr) @@ -360,7 +358,7 @@ abstract class SelectiveCPSTransform extends PluginComponent with // ctx.flatMap { => ... } // or // ctx.map { => ... } - (Nil, applyCombinatorFun(rhs1, mkBlock(bodyStms, bodyExpr))) + (Nil, applyCombinatorFun(rhs1, gen.mkBlock(bodyStms :+ bodyExpr))) } } catch { case ex:TypeError => diff --git a/src/reflect/scala/reflect/api/BuildUtils.scala b/src/reflect/scala/reflect/api/BuildUtils.scala index 03980f5f0a..ef9f9ab834 100644 --- a/src/reflect/scala/reflect/api/BuildUtils.scala +++ b/src/reflect/scala/reflect/api/BuildUtils.scala @@ -64,8 +64,6 @@ private[reflect] trait BuildUtils { self: Universe => def Ident(sym: Symbol): Ident - def Block(stats: List[Tree]): Block - def TypeTree(tp: Type): TypeTree def thisPrefix(sym: Symbol): Type @@ -80,6 +78,8 @@ private[reflect] trait BuildUtils { self: Universe => def mkRefineStat(stats: List[Tree]): List[Tree] + def RefTree(qual: Tree, sym: Symbol): Tree + val FlagsRepr: FlagsReprExtractor trait FlagsReprExtractor { @@ -87,15 +87,17 @@ private[reflect] trait BuildUtils { self: Universe => def unapply(flags: Long): Some[Long] } - val TypeApplied: TypeAppliedExtractor + val SyntacticTypeApplied: SyntacticTypeAppliedExtractor - trait TypeAppliedExtractor { + trait SyntacticTypeAppliedExtractor { + def apply(tree: Tree, targs: List[Tree]): Tree def unapply(tree: Tree): Some[(Tree, List[Tree])] } - val Applied: AppliedExtractor + val SyntacticApplied: SyntacticAppliedExtractor - trait AppliedExtractor { + trait SyntacticAppliedExtractor { + def apply(tree: Tree, argss: List[List[Tree]]): Tree def unapply(tree: Tree): Some[(Tree, List[List[Tree]])] } @@ -117,6 +119,11 @@ private[reflect] trait BuildUtils { self: Universe => def unapply(tree: Tree): Option[List[Tree]] } - def RefTree(qual: Tree, sym: Symbol): Tree + val SyntacticBlock: SyntacticBlockExtractor + + trait SyntacticBlockExtractor { + def apply(stats: List[Tree]): Tree + def unapply(tree: Tree): Option[List[Tree]] + } } } diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala index 208a61d9d3..928395b7c9 100644 --- a/src/reflect/scala/reflect/internal/BuildUtils.scala +++ b/src/reflect/scala/reflect/internal/BuildUtils.scala @@ -53,12 +53,6 @@ trait BuildUtils { self: SymbolTable => def Ident(sym: Symbol): Ident = self.Ident(sym) - def Block(stats: List[Tree]): Block = stats match { - case Nil => self.Block(Nil, Literal(Constant(()))) - case elem :: Nil => self.Block(Nil, elem) - case elems => self.Block(elems.init, elems.last) - } - def TypeTree(tp: Type): TypeTree = self.TypeTree(tp) def thisPrefix(sym: Symbol): Type = sym.thisPrefix @@ -88,25 +82,34 @@ trait BuildUtils { self: SymbolTable => def mkRefineStat(stats: List[Tree]): List[Tree] = stats.map(mkRefineStat) + def RefTree(qual: Tree, sym: Symbol) = self.RefTree(qual, sym.name) setSymbol sym + object FlagsRepr extends FlagsReprExtractor { def apply(bits: Long): FlagSet = bits def unapply(flags: Long): Some[Long] = Some(flags) } - object TypeApplied extends TypeAppliedExtractor { + object SyntacticTypeApplied extends SyntacticTypeAppliedExtractor { + def apply(tree: Tree, targs: List[Tree]): Tree = + if (targs.isEmpty) tree + else if (tree.isTerm) TypeApply(tree, targs) + else if (tree.isType) AppliedTypeTree(tree, targs) + else throw new IllegalArgumentException(s"can't apply types to $tree") + def unapply(tree: Tree): Some[(Tree, List[Tree])] = tree match { case TypeApply(fun, targs) => Some((fun, targs)) + case AppliedTypeTree(tpe, targs) => Some((tpe, targs)) case _ => Some((tree, Nil)) } } - object Applied extends AppliedExtractor { + object SyntacticApplied extends SyntacticAppliedExtractor { + def apply(tree: Tree, argss: List[List[Tree]]): Tree = + argss.foldLeft(tree) { Apply(_, _) } + def unapply(tree: Tree): Some[(Tree, List[List[Tree]])] = { val treeInfo.Applied(fun, targs, argss) = tree - targs match { - case Nil => Some((fun, argss)) - case _ => Some((TypeApply(fun, targs), argss)) - } + Some((SyntacticTypeApplied(fun, targs), argss)) } } @@ -189,7 +192,15 @@ trait BuildUtils { self: SymbolTable => } } - def RefTree(qual: Tree, sym: Symbol) = self.RefTree(qual, sym.name) setSymbol sym + object SyntacticBlock extends SyntacticBlockExtractor { + def apply(stats: List[Tree]): Tree = gen.mkBlock(stats) + + def unapply(tree: Tree): Option[List[Tree]] = tree match { + case self.Block(stats, expr) => Some(stats :+ expr) + case _ if tree.isTerm => Some(tree :: Nil) + case _ => None + } + } } val build: BuildApi = new BuildImpl diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index eb24706f29..6cbf1f1046 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -561,7 +561,6 @@ trait StdNames { val Any: NameType = "Any" val AnyVal: NameType = "AnyVal" val Apply: NameType = "Apply" - val Applied: NameType = "Applied" val ArrayAnnotArg: NameType = "ArrayAnnotArg" val Block: NameType = "Block" val ConstantType: NameType = "ConstantType" @@ -588,7 +587,10 @@ trait StdNames { val Select: NameType = "Select" val SelectFromTypeTree: NameType = "SelectFromTypeTree" val StringContext: NameType = "StringContext" + val SyntacticApplied: NameType = "SyntacticApplied" + val SyntacticBlock: NameType = "SyntacticBlock" val SyntacticClassDef: NameType = "SyntacticClassDef" + val SyntacticTypeApplied: NameType = "SyntacticTypeApplied" val This: NameType = "This" val ThisType: NameType = "ThisType" val True : NameType = "True" @@ -596,7 +598,6 @@ trait StdNames { val TupleN: NameType = "TupleN" val TupleTypeN: NameType = "TupleTypeN" val TYPE_ : NameType = "TYPE" - val TypeApplied: NameType = "TypeApplied" val TypeRef: NameType = "TypeRef" val TypeTree: NameType = "TypeTree" val UNIT : NameType = "UNIT" diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala index a7ea8c816f..9623cc31f6 100644 --- a/src/reflect/scala/reflect/internal/TreeGen.scala +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -414,4 +414,11 @@ abstract class TreeGen extends macros.TreeBuilder { ) } } + + /** Create block of statements `stats` */ + def mkBlock(stats: List[Tree]): Tree = + if (stats.isEmpty) Literal(Constant(())) + else if (!stats.last.isTerm) Block(stats, Literal(Constant(()))) + else if (stats.length == 1) stats.head + else Block(stats.init, stats.last) } diff --git a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala index 5d657b931b..dc3fc60f8c 100644 --- a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala @@ -118,7 +118,7 @@ trait TypeDefConstruction { self: QuasiquoteProperties => property("splice into applied type tree") = forAll { (T1: TypeName, T2: TypeName, args: List[Tree]) => q"type $T1 = $T2[..$args]" ≈ TypeDef(Modifiers(), T1, List(), - AppliedTypeTree(Ident(T2), args)) + if(args.nonEmpty) AppliedTypeTree(Ident(T2), args) else Ident(T2)) } } diff --git a/test/files/scalacheck/quasiquotes/PatternConstructionProps.scala b/test/files/scalacheck/quasiquotes/PatternConstructionProps.scala index aee50c9c5f..504cb2a77d 100644 --- a/test/files/scalacheck/quasiquotes/PatternConstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/PatternConstructionProps.scala @@ -32,6 +32,6 @@ object PatternConstructionProps extends QuasiquoteProperties("pattern constructi } property("splice into casedef") = forAll { (pat: Tree, cond: Tree, body: Tree) => - cq"$pat if $cond => $body" ≈ CaseDef(pat, cond, Block(List(), body)) + cq"$pat if $cond => $body" ≈ CaseDef(pat, cond, body) } } \ No newline at end of file diff --git a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala index 5a765f7911..599dfd442a 100644 --- a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala @@ -88,7 +88,7 @@ object TermConstructionProps extends QuasiquoteProperties("term construction") { } property("splice trees into type apply") = forAll { (fun: TreeIsTerm, types: List[Tree]) => - q"$fun[..$types]" ≈ TypeApply(fun, types) + q"$fun[..$types]" ≈ (if (types.nonEmpty) TypeApply(fun, types) else fun) } property("splice names into import selector") = forAll { @@ -122,8 +122,10 @@ object TermConstructionProps extends QuasiquoteProperties("term construction") { def blockInvariant(quote: Tree, trees: List[Tree]) = quote ≈ (trees match { - case Nil => Block(Nil, q"()") - case _ => Block(trees.init, trees.last) + case Nil => q"()" + case _ :+ last if !last.isTerm => Block(trees, q"()") + case head :: Nil => head + case init :+ last => Block(init, last) }) property("splice list of trees into block (1)") = forAll { (trees: List[Tree]) => diff --git a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala index 132baeb977..6087bbdb74 100644 --- a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala @@ -62,4 +62,9 @@ object TermDeconstructionProps extends QuasiquoteProperties("term deconstruction val q"$x match { case ..$cases }" = q"x match { case 1 => case 2 => }" x ≈ q"x" && cases ≈ List(cq"1 =>", cq"2 =>") } + + property("deconstruct block") = test { + val q"{ ..$xs }" = q"{ x1; x2; x3 }" + assert(xs ≈ List(q"x1", q"x2", q"x3")) + } } \ No newline at end of file -- cgit v1.2.3 From 1585b52b6cbf2e2985ef3a02009466e56baf6e74 Mon Sep 17 00:00:00 2001 From: Den Shabalin Date: Thu, 5 Sep 2013 13:22:22 +0200 Subject: reify ScalaPackage symbol with the help of ScalaDot --- src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala | 6 ++++++ src/reflect/scala/reflect/api/BuildUtils.scala | 7 +++++++ src/reflect/scala/reflect/internal/BuildUtils.scala | 8 ++++++++ src/reflect/scala/reflect/internal/StdNames.scala | 1 + .../scalacheck/quasiquotes/DefinitionConstructionProps.scala | 3 ++- 5 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala index 62c246366d..a817afe741 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala @@ -62,6 +62,12 @@ trait Reifiers { self: Quasiquotes => reifyBuildCall(nme.SyntacticTypeApplied, fun, targs) case Block(stats, last) => reifyBuildCall(nme.SyntacticBlock, stats :+ last) + // parser emits trees with scala package symbol to ensure + // that some names hygienically point to various scala package + // members; we need to preserve this symbol to preserve + // correctness of the trees produced by quasiquotes + case Select(id @ Ident(nme.scala_), name) if id.symbol == ScalaPackage => + reifyBuildCall(nme.ScalaDot, name) case _ => super.reifyTreeSyntactically(tree) } diff --git a/src/reflect/scala/reflect/api/BuildUtils.scala b/src/reflect/scala/reflect/api/BuildUtils.scala index ef9f9ab834..a0a47fe23c 100644 --- a/src/reflect/scala/reflect/api/BuildUtils.scala +++ b/src/reflect/scala/reflect/api/BuildUtils.scala @@ -80,6 +80,13 @@ private[reflect] trait BuildUtils { self: Universe => def RefTree(qual: Tree, sym: Symbol): Tree + val ScalaDot: ScalaDotExtractor + + trait ScalaDotExtractor { + def apply(name: Name): Tree + def unapply(tree: Tree): Option[Name] + } + val FlagsRepr: FlagsReprExtractor trait FlagsReprExtractor { diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala index 928395b7c9..abe9e39067 100644 --- a/src/reflect/scala/reflect/internal/BuildUtils.scala +++ b/src/reflect/scala/reflect/internal/BuildUtils.scala @@ -82,6 +82,14 @@ trait BuildUtils { self: SymbolTable => def mkRefineStat(stats: List[Tree]): List[Tree] = stats.map(mkRefineStat) + object ScalaDot extends ScalaDotExtractor { + def apply(name: Name): Tree = gen.scalaDot(name) + def unapply(tree: Tree): Option[Name] = tree match { + case Select(id @ Ident(nme.scala_), name) if id.symbol == ScalaPackage => Some(name) + case _ => None + } + } + def RefTree(qual: Tree, sym: Symbol) = self.RefTree(qual, sym.name) setSymbol sym object FlagsRepr extends FlagsReprExtractor { diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 6cbf1f1046..0b1f444772 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -724,6 +724,7 @@ trait StdNames { val staticModule : NameType = "staticModule" val staticPackage : NameType = "staticPackage" val synchronized_ : NameType = "synchronized" + val ScalaDot: NameType = "ScalaDot" val TermName: NameType = "TermName" val this_ : NameType = "this" val thisPrefix : NameType = "thisPrefix" diff --git a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala index dc3fc60f8c..707394fdba 100644 --- a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala @@ -4,6 +4,7 @@ import Gen._ import Arbitrary._ import scala.reflect.runtime.universe._ +import scala.reflect.runtime.universe.build.ScalaDot import Flag._ object DefinitionConstructionProps @@ -14,7 +15,7 @@ object DefinitionConstructionProps with ValDefConstruction trait ClassConstruction { self: QuasiquoteProperties => - val anyRef = Select(Ident(TermName("scala")), TypeName("AnyRef")) + val anyRef = ScalaDot(TypeName("AnyRef")) val emtpyConstructor = DefDef(Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(pendingSuperCall), Literal(Constant(())))) -- cgit v1.2.3 From 800f5acd0d7117bf953829da7c6d955e61e63bdc Mon Sep 17 00:00:00 2001 From: Den Shabalin Date: Thu, 5 Sep 2013 13:27:54 +0200 Subject: add support for function type splicing and extraction --- .../scala/tools/nsc/ast/parser/TreeBuilder.scala | 3 +-- .../scala/tools/reflect/quasiquotes/Parsers.scala | 4 ++++ .../tools/reflect/quasiquotes/Placeholders.scala | 7 +++++++ .../scala/tools/reflect/quasiquotes/Reifiers.scala | 4 ++++ src/reflect/scala/reflect/api/BuildUtils.scala | 7 +++++++ .../scala/reflect/internal/BuildUtils.scala | 22 +++++++++++++++++++++- src/reflect/scala/reflect/internal/StdNames.scala | 2 ++ src/reflect/scala/reflect/internal/TreeGen.scala | 4 ++++ .../quasiquotes/TypeConstructionProps.scala | 6 ++++++ .../quasiquotes/TypeDeconstructionProps.scala | 6 ++++++ 10 files changed, 62 insertions(+), 3 deletions(-) diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index ed7c0d1a0a..28e3217449 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -516,8 +516,7 @@ abstract class TreeBuilder { } /** Create a tree representing the function type (argtpes) => restpe */ - def makeFunctionTypeTree(argtpes: List[Tree], restpe: Tree): Tree = - AppliedTypeTree(rootScalaDot(newTypeName("Function" + argtpes.length)), argtpes ::: List(restpe)) + def makeFunctionTypeTree(argtpes: List[Tree], restpe: Tree): Tree = gen.mkFunctionTypeTree(argtpes, restpe) /** Append implicit parameter section if `contextBounds` nonempty */ def addEvidenceParams(owner: Name, vparamss: List[List[ValDef]], contextBounds: List[Tree]): List[List[ValDef]] = { diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala index fe0809c869..868eccebcd 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala @@ -64,6 +64,10 @@ trait Parsers { self: Quasiquotes => case (head @ Ident(name)) :: Nil if holeMap.contains(name) => Block(Nil, head) case _ => super.makeBlock(stats) } + + // tq"$a => $b" + override def makeFunctionTypeTree(argtpes: List[Tree], restpe: Tree): Tree = + AppliedTypeTree(Ident(tpnme.QUASIQUOTE_FUNCTION), argtpes :+ restpe) } import treeBuilder.{global => _, _} diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala index 5d7edcd75e..f0886b5735 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala @@ -113,6 +113,13 @@ trait Placeholders { self: Quasiquotes => } } + object FunctionTypePlaceholder { + def unapply(tree: Tree): Option[(List[Tree], Tree)] = tree match { + case AppliedTypeTree(Ident(tpnme.QUASIQUOTE_FUNCTION), args :+ res) => Some((args, res)) + case _ => None + } + } + object SymbolPlaceholder { def unapply(scrutinee: Any): Option[Tree] = scrutinee match { case Placeholder(tree, SymbolLocation, _) => Some(tree) diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala index a817afe741..9789801fac 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala @@ -45,6 +45,7 @@ trait Reifiers { self: Quasiquotes => case Placeholder(tree, _, card @ Dot()) => c.abort(tree.pos, s"Can't $action with $card here") case TuplePlaceholder(args) => reifyTuple(args) case TupleTypePlaceholder(args) => reifyTupleType(args) + case FunctionTypePlaceholder(argtpes, restpe) => reifyFunctionType(argtpes, restpe) case CasePlaceholder(tree, location, _) => reifyCase(tree, location) case RefineStatPlaceholder(tree, _, _) => reifyRefineStat(tree) case _ => EmptyTree @@ -104,6 +105,9 @@ trait Reifiers { self: Quasiquotes => case _ => reifyBuildCall(nme.TupleTypeN, args) } + def reifyFunctionType(argtpes: List[Tree], restpe: Tree) = + reifyBuildCall(nme.SyntacticFunctionType, argtpes, restpe) + def reifyRefineStat(tree: Tree) = tree /** Splits list into a list of groups where subsequent elements are considered diff --git a/src/reflect/scala/reflect/api/BuildUtils.scala b/src/reflect/scala/reflect/api/BuildUtils.scala index a0a47fe23c..a74d3c137d 100644 --- a/src/reflect/scala/reflect/api/BuildUtils.scala +++ b/src/reflect/scala/reflect/api/BuildUtils.scala @@ -132,5 +132,12 @@ private[reflect] trait BuildUtils { self: Universe => def apply(stats: List[Tree]): Tree def unapply(tree: Tree): Option[List[Tree]] } + + val SyntacticFunctionType: SyntacticFunctionTypeExtractor + + trait SyntacticFunctionTypeExtractor { + def apply(argtpes: List[Tree], restpe: Tree): Tree + def unapply(tree: Tree): Option[(List[Tree], Tree)] + } } } diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala index abe9e39067..e2894c5b94 100644 --- a/src/reflect/scala/reflect/internal/BuildUtils.scala +++ b/src/reflect/scala/reflect/internal/BuildUtils.scala @@ -5,7 +5,7 @@ package internal import Flags._ trait BuildUtils { self: SymbolTable => - import definitions.{TupleClass, MaxTupleArity, ScalaPackage, UnitClass} + import definitions.{TupleClass, FunctionClass, MaxTupleArity, MaxFunctionArity, ScalaPackage, UnitClass} class BuildImpl extends BuildApi { @@ -200,6 +200,26 @@ trait BuildUtils { self: SymbolTable => } } + object SyntacticFunctionType extends SyntacticFunctionTypeExtractor { + def apply(argtpes: List[Tree], restpe: Tree): Tree = { + require(argtpes.length <= MaxFunctionArity + 1, s"Function types with arity bigger than $MaxFunctionArity aren't supported") + gen.mkFunctionTypeTree(argtpes, restpe) + } + + def unapply(tree: Tree): Option[(List[Tree], Tree)] = tree match { + case AppliedTypeTree(id: Ident, args @ (argtpes :+ restpe)) + if args.length - 1 <= MaxFunctionArity && id.symbol == FunctionClass(args.length - 1) => + Some((argtpes, restpe)) + case AppliedTypeTree(Select(pack, fun), args @ (argtpes :+ restpe)) + if args.length - 1 <= MaxFunctionArity && pack.symbol == ScalaPackage && fun == FunctionClass(args.length - 1).name => + Some((argtpes, restpe)) + case AppliedTypeTree(Select(Select(Ident(nme.ROOTPKG), nme.scala_), fun), args @ (argtpes :+ restpe)) + if args.length - 1 <= MaxFunctionArity && fun == FunctionClass(args.length - 1).name => + Some((argtpes, restpe)) + case _ => None + } + } + object SyntacticBlock extends SyntacticBlockExtractor { def apply(stats: List[Tree]): Tree = gen.mkBlock(stats) diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 0b1f444772..f4662296e2 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -249,6 +249,7 @@ trait StdNames { // quasiquote-specific names final val QUASIQUOTE_MODS: NameType = "$quasiquote$mods$" final val QUASIQUOTE_TUPLE: NameType = "$quasiquote$tuple$" + final val QUASIQUOTE_FUNCTION: NameType = "$quasiquote$function$" final val QUASIQUOTE_REFINE_STAT: NameType = "$quasiquote$refine$stat$" // Annotation simple names, used in Namer @@ -590,6 +591,7 @@ trait StdNames { val SyntacticApplied: NameType = "SyntacticApplied" val SyntacticBlock: NameType = "SyntacticBlock" val SyntacticClassDef: NameType = "SyntacticClassDef" + val SyntacticFunctionType: NameType= "SyntacticFunctionType" val SyntacticTypeApplied: NameType = "SyntacticTypeApplied" val This: NameType = "This" val ThisType: NameType = "ThisType" diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala index 9623cc31f6..648c921eb7 100644 --- a/src/reflect/scala/reflect/internal/TreeGen.scala +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -415,6 +415,10 @@ abstract class TreeGen extends macros.TreeBuilder { } } + /** Create a tree representing the function type (argtpes) => restpe */ + def mkFunctionTypeTree(argtpes: List[Tree], restpe: Tree): Tree = + AppliedTypeTree(rootScalaDot(newTypeName("Function" + argtpes.length)), argtpes ::: List(restpe)) + /** Create block of statements `stats` */ def mkBlock(stats: List[Tree]): Tree = if (stats.isEmpty) Literal(Constant(())) diff --git a/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala b/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala index 459d733733..cac83ff8ac 100644 --- a/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TypeConstructionProps.scala @@ -27,4 +27,10 @@ object TypeConstructionProps extends QuasiquoteProperties("type construction") 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") + } } \ No newline at end of file diff --git a/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala index 5c919e55cb..02787c551b 100644 --- a/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala @@ -31,4 +31,10 @@ object TypeDeconstructionProps extends QuasiquoteProperties("type deconstruction val tq"T { ..$stats }" = tq"T { def foo; val x: Int; type Y = String }" assert(stats ≈ (q"def foo" :: q"val x: Int" :: q"type Y = String" :: Nil)) } + + property("function type") = test { + val tq"..$argtpes => $restpe" = tq"(A, B) => C" + assert(argtpes ≈ (tq"A" :: tq"B" :: Nil)) + assert(restpe ≈ tq"C") + } } \ No newline at end of file -- cgit v1.2.3 From 73a4f172c3b4f2c7d8bf9936898f031d09899cef Mon Sep 17 00:00:00 2001 From: Den Shabalin Date: Thu, 5 Sep 2013 20:41:21 +0200 Subject: extract out isHole(name) --- src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala index 868eccebcd..a8ec46261f 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala @@ -50,6 +50,10 @@ trait Parsers { self: Quasiquotes => def entryPoint: QuasiquoteParser => Tree class QuasiquoteParser(source0: SourceFile) extends SourceFileParser(source0) { + def isHole: Boolean = isIdent && isHole(in.name) + + def isHole(name: Name): Boolean = holeMap.contains(name) + override val treeBuilder = new ParserTreeBuilder { // q"(..$xs)" override def makeTupleTerm(trees: List[Tree], flattenUnary: Boolean): Tree = @@ -61,7 +65,7 @@ trait Parsers { self: Quasiquotes => // q"{ $x }" override def makeBlock(stats: List[Tree]): Tree = stats match { - case (head @ Ident(name)) :: Nil if holeMap.contains(name) => Block(Nil, head) + case (head @ Ident(name)) :: Nil if isHole(name) => Block(Nil, head) case _ => super.makeBlock(stats) } @@ -88,8 +92,6 @@ trait Parsers { self: Quasiquotes => case other => other } - def isHole: Boolean = isIdent && holeMap.contains(in.name) - override def isAnnotation: Boolean = super.isAnnotation || (isHole && lookingAhead { isAnnotation }) override def isCaseDefStart: Boolean = super.isCaseDefStart || (in.token == EOF) -- cgit v1.2.3 From 1352fea1c4ecfa0fd66ff1d5ad6e0ee437b1a59f Mon Sep 17 00:00:00 2001 From: Den Shabalin Date: Mon, 2 Sep 2013 16:01:38 +0200 Subject: first-class early def splicing and extraction support --- .../scala/tools/nsc/ast/parser/Parsers.scala | 48 +++---- .../scala/tools/reflect/quasiquotes/Parsers.scala | 7 +- .../tools/reflect/quasiquotes/Placeholders.scala | 7 + .../scala/tools/reflect/quasiquotes/Reifiers.scala | 32 ++++- src/reflect/scala/reflect/api/BuildUtils.scala | 29 +++- .../scala/reflect/internal/BuildUtils.scala | 150 +++++++++++++++++---- src/reflect/scala/reflect/internal/StdNames.scala | 4 + src/reflect/scala/reflect/internal/TreeGen.scala | 14 +- .../quasiquotes/DefinitionConstructionProps.scala | 64 +++++++++ .../DefinitionDeconstructionProps.scala | 45 ++++++- 10 files changed, 334 insertions(+), 66 deletions(-) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 158fa147b6..f52ed60480 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -2704,8 +2704,7 @@ self => syntaxError("classes are not allowed to be virtual", skipIt = false) } val template = templateOpt(mods1, name, constrMods withAnnotations constrAnnots, vparamss, tstart) - if (isInterface(mods1, template.body)) mods1 |= Flags.INTERFACE - val result = ClassDef(mods1, name, tparams, template) + val result = gen.mkClassDef(mods1, name, tparams, template) // Context bounds generate implicit parameters (part of the template) with types // from tparams: we need to ensure these don't overlap if (!classContextBounds.isEmpty) @@ -2796,17 +2795,7 @@ self => // @S: pre template body cannot stub like post body can! val (self, body) = templateBody(isPre = true) if (in.token == WITH && (self eq emptyValDef)) { - val earlyDefs: List[Tree] = body flatMap { - case vdef @ ValDef(mods, _, _, _) if !mods.isDeferred => - List(copyValDef(vdef)(mods = mods | Flags.PRESUPER)) - case tdef @ TypeDef(mods, name, tparams, rhs) => - deprecationWarning(tdef.pos.point, "early type members are deprecated. Move them to the regular body: the semantics are the same.") - List(treeCopy.TypeDef(tdef, mods | Flags.PRESUPER, name, tparams, rhs)) - case stat if !stat.isEmpty => - syntaxError(stat.pos, "only concrete field definitions allowed in early object initialization section", skipIt = false) - List() - case _ => List() - } + val earlyDefs: List[Tree] = body.map(ensureEarlyDef).filter(_.nonEmpty) in.nextToken() val parents = templateParents() val (self1, body1) = templateBodyOpt(parenMeansSyntaxError = false) @@ -2821,8 +2810,18 @@ self => } } - def isInterface(mods: Modifiers, body: List[Tree]): Boolean = - mods.isTrait && (body forall treeInfo.isInterfaceMember) + def ensureEarlyDef(tree: Tree): Tree = tree match { + case vdef @ ValDef(mods, _, _, _) if !mods.isDeferred => + copyValDef(vdef)(mods = mods | Flags.PRESUPER) + case tdef @ TypeDef(mods, name, tparams, rhs) => + deprecationWarning(tdef.pos.point, "early type members are deprecated. Move them to the regular body: the semantics are the same.") + treeCopy.TypeDef(tdef, mods | Flags.PRESUPER, name, tparams, rhs) + case stat if !stat.isEmpty => + syntaxError(stat.pos, "only concrete field definitions allowed in early object initialization section", skipIt = false) + EmptyTree + case _ => + EmptyTree + } /** {{{ * ClassTemplateOpt ::= `extends' ClassTemplate | [[`extends'] TemplateBody] @@ -2831,7 +2830,7 @@ self => * }}} */ def templateOpt(mods: Modifiers, name: Name, constrMods: Modifiers, vparamss: List[List[ValDef]], tstart: Int): Template = { - val (parents0, self, body) = ( + val (parents, self, body) = ( if (in.token == EXTENDS || in.token == SUBTYPE && mods.isTrait) { in.nextToken() template() @@ -2842,26 +2841,21 @@ self => (List(), self, body) } ) - def anyrefParents() = { - val caseParents = if (mods.isCase) List(productConstr, serializableConstr) else Nil - parents0 ::: caseParents match { - case Nil => atInPos(scalaAnyRefConstr) :: Nil - case ps => ps - } - } def anyvalConstructor() = ( // Not a well-formed constructor, has to be finished later - see note // regarding AnyVal constructor in AddInterfaces. DefDef(NoMods, nme.CONSTRUCTOR, Nil, ListOfNil, TypeTree(), Block(Nil, literalUnit)) ) - val tstart0 = if (body.isEmpty && in.lastOffset < tstart) in.lastOffset else tstart + val parentPos = o2p(in.offset) + val tstart1 = if (body.isEmpty && in.lastOffset < tstart) in.lastOffset else tstart - atPos(tstart0) { + atPos(tstart1) { // Exclude only the 9 primitives plus AnyVal. if (inScalaRootPackage && ScalaValueClassNames.contains(name)) - Template(parents0, self, anyvalConstructor :: body) + Template(parents, self, anyvalConstructor :: body) else - gen.mkTemplate(anyrefParents(), self, constrMods, vparamss, body, o2p(tstart)) + gen.mkTemplate(gen.mkParents(mods, parents, parentPos), + self, constrMods, vparamss, body, o2p(tstart)) } } diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala index a8ec46261f..868c74ebcd 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala @@ -116,7 +116,7 @@ trait Parsers { self: Quasiquotes => case AT => in.nextToken() annot :: readAnnots(annot) - case _ if isHole && lookingAhead { in.token == AT || isModifier || isDefIntro || isIdent} => + case _ if isHole && lookingAhead { isAnnotation || isModifier || isDefIntro || isIdent || isStatSep || in.token == LPAREN } => val ann = Apply(Select(New(Ident(tpnme.QUASIQUOTE_MODS)), nme.CONSTRUCTOR), List(Literal(Constant(in.name.toString)))) in.nextToken() ann :: readAnnots(annot) @@ -130,6 +130,11 @@ trait Parsers { self: Quasiquotes => in.nextToken() result } else super.refineStat() + + override def ensureEarlyDef(tree: Tree) = tree match { + case Ident(name: TermName) if isHole(name) => ValDef(NoMods | Flag.PRESUPER, name, Ident(tpnme.QUASIQUOTE_EARLY_DEF), EmptyTree) + case _ => super.ensureEarlyDef(tree) + } } } diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala index f0886b5735..c136e6d785 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala @@ -140,4 +140,11 @@ trait Placeholders { self: Quasiquotes => case _ => None } } + + object EarlyDefPlaceholder { + def unapply(tree: Tree): Option[(Tree, Location, Cardinality)] = tree match { + case ValDef(_, Placeholder(tree, location, card), Ident(tpnme.QUASIQUOTE_EARLY_DEF), _) => Some((tree, location, card)) + case _ => None + } + } } \ No newline at end of file diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala index 9789801fac..53b2e4cc96 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala @@ -7,7 +7,8 @@ import scala.reflect.internal.Flags._ trait Reifiers { self: Quasiquotes => import global._ - import global.build.{SyntacticClassDef, SyntacticBlock, SyntacticApplied, SyntacticTypeApplied} + import global.build.{SyntacticClassDef, SyntacticTraitDef, SyntacticModuleDef, + SyntacticBlock, SyntacticApplied, SyntacticTypeApplied} import global.treeInfo._ import global.definitions._ import Cardinality._ @@ -48,13 +49,18 @@ trait Reifiers { self: Quasiquotes => case FunctionTypePlaceholder(argtpes, restpe) => reifyFunctionType(argtpes, restpe) case CasePlaceholder(tree, location, _) => reifyCase(tree, location) case RefineStatPlaceholder(tree, _, _) => reifyRefineStat(tree) + case EarlyDefPlaceholder(tree, _, _) => reifyEarlyDef(tree) case _ => EmptyTree } override def reifyTreeSyntactically(tree: Tree) = tree match { - case SyntacticClassDef(mods, name, tparams, constrmods, vparamss, parents, selfdef, body) => + case SyntacticTraitDef(mods, name, tparams, earlyDefs, parents, selfdef, body) => + reifyBuildCall(nme.SyntacticTraitDef, mods, name, tparams, earlyDefs, parents, selfdef, body) + case SyntacticClassDef(mods, name, tparams, constrmods, vparamss, earlyDefs, parents, selfdef, body) => reifyBuildCall(nme.SyntacticClassDef, mods, name, tparams, constrmods, vparamss, - parents, selfdef, body) + earlyDefs, parents, selfdef, body) + case SyntacticModuleDef(mods, name, earlyDefs, parents, selfdef, body) => + reifyBuildCall(nme.SyntacticModuleDef, mods, name, earlyDefs, parents, selfdef, body) case SyntacticApplied(fun, argss) if argss.length > 1 => reifyBuildCall(nme.SyntacticApplied, fun, argss) case SyntacticApplied(fun, argss @ (_ :+ (_ :+ Placeholder(_, _, DotDotDot)))) => @@ -110,6 +116,8 @@ trait Reifiers { self: Quasiquotes => def reifyRefineStat(tree: Tree) = tree + def reifyEarlyDef(tree: Tree) = tree + /** Splits list into a list of groups where subsequent elements are considered * similar by the corresponding function. * @@ -163,6 +171,7 @@ trait Reifiers { self: Quasiquotes => case Placeholder(tree, _, DotDot) => tree case CasePlaceholder(tree, _, DotDot) => tree case RefineStatPlaceholder(tree, _, DotDot) => reifyRefineStat(tree) + case EarlyDefPlaceholder(tree, _, DotDot) => reifyEarlyDef(tree) case List(Placeholder(tree, _, DotDotDot)) => tree } { reify(_) @@ -170,8 +179,19 @@ trait Reifiers { self: Quasiquotes => def reifyAnnotList(annots: List[Tree]): Tree - def ensureNoExplicitFlags(m: Modifiers, pos: Position) = - if ((m.flags & ExplicitFlags) != 0L) c.abort(pos, s"Can't $action modifiers together with flags, consider merging flags into modifiers") + // These are explicit flags except those that are used + // to overload the same tree for two different concepts: + // - MUTABLE that is used to override ValDef for vars + // - TRAIT that is used to override ClassDef for traits + val nonoverloadedExplicitFlags = ExplicitFlags & ~MUTABLE & ~TRAIT + + def ensureNoExplicitFlags(m: Modifiers, pos: Position) = { + // Traits automatically have ABSTRACT flag assigned to + // them so in that case it's not an explicit flag + val flags = if (m.isTrait) m.flags & ~ABSTRACT else m.flags + if ((flags & nonoverloadedExplicitFlags) != 0L) + c.abort(pos, s"Can't $action modifiers together with flags, consider merging flags into modifiers") + } override def mirrorSelect(name: String): Tree = Select(universe, TermName(name)) @@ -249,6 +269,8 @@ trait Reifiers { self: Quasiquotes => } override def reifyRefineStat(tree: Tree) = mirrorBuildCall(nme.mkRefineStat, tree) + + override def reifyEarlyDef(tree: Tree) = mirrorBuildCall(nme.mkEarlyDef, tree) } class UnapplyReifier extends Reifier { diff --git a/src/reflect/scala/reflect/api/BuildUtils.scala b/src/reflect/scala/reflect/api/BuildUtils.scala index a74d3c137d..108efee37c 100644 --- a/src/reflect/scala/reflect/api/BuildUtils.scala +++ b/src/reflect/scala/reflect/api/BuildUtils.scala @@ -78,6 +78,10 @@ private[reflect] trait BuildUtils { self: Universe => def mkRefineStat(stats: List[Tree]): List[Tree] + def mkEarlyDef(defn: Tree): Tree + + def mkEarlyDef(defns: List[Tree]): List[Tree] + def RefTree(qual: Tree, sym: Symbol): Tree val ScalaDot: ScalaDotExtractor @@ -112,10 +116,27 @@ private[reflect] trait BuildUtils { self: Universe => trait SyntacticClassDefExtractor { def apply(mods: Modifiers, name: TypeName, tparams: List[TypeDef], - constrMods: Modifiers, vparamss: List[List[ValDef]], parents: List[Tree], - selfdef: ValDef, body: List[Tree]): Tree - def unapply(tree: Tree): Option[(Modifiers, TypeName, List[TypeDef], Modifiers, - List[List[ValDef]], List[Tree], ValDef, List[Tree])] + constrMods: Modifiers, vparamss: List[List[ValDef]], earlyDefs: List[Tree], + parents: List[Tree], selfdef: ValDef, body: List[Tree]): ClassDef + def unapply(tree: Tree): Option[(Modifiers, TypeName, List[TypeDef], Modifiers, List[List[ValDef]], + List[Tree], List[Tree], ValDef, List[Tree])] + } + + val SyntacticTraitDef: SyntacticTraitDefExtractor + + trait SyntacticTraitDefExtractor { + def apply(mods: Modifiers, name: TypeName, tparams: List[TypeDef], + earlyDefs: List[Tree], parents: List[Tree], selfdef: ValDef, body: List[Tree]): ClassDef + def unapply(tree: Tree): Option[(Modifiers, TypeName, List[TypeDef], + List[Tree], List[Tree], ValDef, List[Tree])] + } + + val SyntacticModuleDef: SyntacticModuleDefExtractor + + trait SyntacticModuleDefExtractor { + def apply(mods: Modifiers, name: TermName, earlyDefs: List[Tree], + parents: List[Tree], selfdef: ValDef, body: List[Tree]): Tree + def unapply(tree: Tree): Option[(Modifiers, TermName, List[Tree], List[Tree], ValDef, List[Tree])] } val TupleN: TupleNExtractor diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala index e2894c5b94..528b542361 100644 --- a/src/reflect/scala/reflect/internal/BuildUtils.scala +++ b/src/reflect/scala/reflect/internal/BuildUtils.scala @@ -70,6 +70,19 @@ trait BuildUtils { self: SymbolTable => case _ => throw new IllegalArgumentException(s"Tree ${showRaw(tree)} isn't a correct representation of annotation, consider passing Ident as a first argument") } + def mkVparamss(argss: List[List[ValDef]]): List[List[ValDef]] = { + argss.map { _.map { + case vd @ ValDef(mods, _, _, EmptyTree) => copyValDef(vd)(mods = mods | PARAM) + case vd @ ValDef(mods, _, _, _) => copyValDef(vd)(mods = mods | PARAM | DEFAULTPARAM) + } } + } + + def mkTparams(tparams: List[Tree]): List[TypeDef] = + tparams.map { + case td: TypeDef => copyTypeDef(td)(mods = (td.mods | PARAM) & (~DEFERRED)) + case other => throw new IllegalArgumentException("can't splice $other as type parameter") + } + def mkRefineStat(stat: Tree): Tree = { stat match { case dd: DefDef => require(dd.rhs.isEmpty, "can't use DefDef with non-empty body as refine stat") @@ -90,6 +103,17 @@ trait BuildUtils { self: SymbolTable => } } + def mkEarlyDef(defn: Tree): Tree = defn match { + case vdef @ ValDef(mods, _, _, _) if !mods.isDeferred => + copyValDef(vdef)(mods = mods | PRESUPER) + case tdef @ TypeDef(mods, _, _, _) => + copyTypeDef(tdef)(mods = mods | PRESUPER) + case _ => + throw new IllegalArgumentException(s"not legal early def: $defn") + } + + def mkEarlyDef(defns: List[Tree]): List[Tree] = defns.map(mkEarlyDef) + def RefTree(qual: Tree, sym: Symbol) = self.RefTree(qual, sym.name) setSymbol sym object FlagsRepr extends FlagsReprExtractor { @@ -121,36 +145,110 @@ trait BuildUtils { self: SymbolTable => } } + private object UnCtor { + def unapply(tree: Tree): Option[(Modifiers, List[List[ValDef]], List[Tree])] = tree match { + case DefDef(mods, nme.MIXIN_CONSTRUCTOR, _, _, _, Block(lvdefs, _)) => + Some(mods | Flag.TRAIT, Nil, lvdefs) + case DefDef(mods, nme.CONSTRUCTOR, Nil, vparamss, _, Block(lvdefs :+ _, _)) => + Some(mods, vparamss, lvdefs) + case _ => None + } + } + + private object UnMkTemplate { + def unapply(templ: Template): Option[(List[Tree], ValDef, Modifiers, List[List[ValDef]], List[Tree], List[Tree])] = { + val Template(parents, selfdef, tbody) = templ + def result(ctorMods: Modifiers, vparamss: List[List[ValDef]], edefs: List[Tree], body: List[Tree]) = + Some((parents, selfdef, ctorMods, vparamss, edefs, body)) + def indexOfCtor(trees: List[Tree]) = + trees.indexWhere { case UnCtor(_, _, _) => true ; case _ => false } + + if (tbody forall treeInfo.isInterfaceMember) + result(NoMods | Flag.TRAIT, Nil, Nil, tbody) + else if (indexOfCtor(tbody) == -1) + None + else { + val (rawEdefs, rest) = tbody.span(treeInfo.isEarlyDef) + val (gvdefs, etdefs) = rawEdefs.partition(treeInfo.isEarlyValDef) + val (fieldDefs, UnCtor(ctorMods, ctorVparamss, lvdefs) :: body) = rest.splitAt(indexOfCtor(rest)) + val evdefs = gvdefs.zip(lvdefs).map { + case (gvdef @ ValDef(_, _, tpt: TypeTree, _), ValDef(_, _, _, rhs)) => + copyValDef(gvdef)(tpt = tpt.original, rhs = rhs) + } + val edefs = evdefs ::: etdefs + if (ctorMods.isTrait) + result(ctorMods, Nil, edefs, body) + else { + // undo conversion from (implicit ... ) to ()(implicit ... ) when its the only parameter section + val vparamssRestoredImplicits = ctorVparamss match { + case Nil :: (tail @ ((head :: _) :: _)) if head.mods.isImplicit => tail + case other => other + } + // undo flag modifications by mergeing flag info from constructor args and fieldDefs + val modsMap = fieldDefs.map { case ValDef(mods, name, _, _) => name -> mods }.toMap + val vparamss = mmap(vparamssRestoredImplicits) { vd => + val originalMods = modsMap(vd.name) | (vd.mods.flags & DEFAULTPARAM) + atPos(vd.pos)(ValDef(originalMods, vd.name, vd.tpt, vd.rhs)) + } + result(ctorMods, vparamss, edefs, body) + } + } + } + } + object SyntacticClassDef extends SyntacticClassDefExtractor { def apply(mods: Modifiers, name: TypeName, tparams: List[TypeDef], - constrMods: Modifiers, vparamss: List[List[ValDef]], parents: List[Tree], - selfdef: ValDef, body: List[Tree]): Tree = - ClassDef(mods, name, tparams, gen.mkTemplate(parents, selfdef, constrMods, vparamss, body, NoPosition)) - - def unapply(tree: Tree): Option[(Modifiers, TypeName, List[TypeDef], Modifiers, - List[List[ValDef]], List[Tree], ValDef, List[Tree])] = tree match { - case ClassDef(mods, name, tparams, Template(parents, selfdef, tbody)) => - // extract generated fieldDefs and constructor - val (defs, (ctor: DefDef) :: body) = tbody.splitAt(tbody.indexWhere { - case DefDef(_, nme.CONSTRUCTOR, _, _, _, _) => true - case _ => false - }) - val (earlyDefs, fieldDefs) = defs.span(treeInfo.isEarlyDef) - - // undo conversion from (implicit ... ) to ()(implicit ... ) when its the only parameter section - val vparamssRestoredImplicits = ctor.vparamss match { - case Nil :: rest if !rest.isEmpty && !rest.head.isEmpty && rest.head.head.mods.isImplicit => rest - case other => other - } + constrMods: Modifiers, vparamss: List[List[ValDef]], earlyDefs: List[Tree], + parents: List[Tree], selfdef: ValDef, body: List[Tree]): ClassDef = { + val extraFlags = PARAMACCESSOR | (if (mods.isCase) CASEACCESSOR else 0L) + val vparamss0 = vparamss.map { _.map { vd => copyValDef(vd)(mods = (vd.mods | extraFlags) & (~DEFERRED)) } } + val tparams0 = mkTparams(tparams) + val parents0 = gen.mkParents(mods, + if (mods.isCase) parents.filter { + case ScalaDot(tpnme.Product | tpnme.Serializable | tpnme.AnyRef) => false + case _ => true + } else parents + ) + val body0 = earlyDefs ::: body + val templ = gen.mkTemplate(parents0, selfdef, constrMods, vparamss0, body0) + gen.mkClassDef(mods, name, tparams0, templ) + } - // undo flag modifications by mergeing flag info from constructor args and fieldDefs - val modsMap = fieldDefs.map { case ValDef(mods, name, _, _) => name -> mods }.toMap - val vparamss = mmap(vparamssRestoredImplicits) { vd => - val originalMods = modsMap(vd.name) | (vd.mods.flags & DEFAULTPARAM) - atPos(vd.pos)(ValDef(originalMods, vd.name, vd.tpt, vd.rhs)) - } + def unapply(tree: Tree): Option[(Modifiers, TypeName, List[TypeDef], Modifiers, List[List[ValDef]], + List[Tree], List[Tree], ValDef, List[Tree])] = tree match { + case ClassDef(mods, name, tparams, UnMkTemplate(parents, selfdef, ctorMods, vparamss, earlyDefs, body)) + if !ctorMods.isTrait && !ctorMods.hasFlag(JAVA) => + Some((mods, name, tparams, ctorMods, vparamss, earlyDefs, parents, selfdef, body)) + case _ => + None + } + } + + object SyntacticTraitDef extends SyntacticTraitDefExtractor { + def apply(mods: Modifiers, name: TypeName, tparams: List[TypeDef], earlyDefs: List[Tree], + parents: List[Tree], selfdef: ValDef, body: List[Tree]): ClassDef = { + val mods0 = mods | TRAIT | ABSTRACT + val templ = gen.mkTemplate(parents, selfdef, Modifiers(TRAIT), Nil, earlyDefs ::: body) + gen.mkClassDef(mods0, name, mkTparams(tparams), templ) + } + + def unapply(tree: Tree): Option[(Modifiers, TypeName, List[TypeDef], + List[Tree], List[Tree], ValDef, List[Tree])] = tree match { + case ClassDef(mods, name, tparams, UnMkTemplate(parents, selfdef, ctorMods, vparamss, earlyDefs, body)) + if mods.isTrait => + Some((mods, name, tparams, earlyDefs, parents, selfdef, body)) + case _ => None + } + } + + object SyntacticModuleDef extends SyntacticModuleDefExtractor { + def apply(mods: Modifiers, name: TermName, earlyDefs: List[Tree], + parents: List[Tree], selfdef: ValDef, body: List[Tree]) = + ModuleDef(mods, name, gen.mkTemplate(parents, selfdef, NoMods, Nil, earlyDefs ::: body)) - Some((mods, name, tparams, ctor.mods, vparamss, parents, selfdef, earlyDefs ::: body)) + def unapply(tree: Tree): Option[(Modifiers, TermName, List[Tree], List[Tree], ValDef, List[Tree])] = tree match { + case ModuleDef(mods, name, UnMkTemplate(parents, selfdef, _, _, earlyDefs, body)) => + Some((mods, name, earlyDefs, parents, selfdef, body)) case _ => None } diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index f4662296e2..936445f011 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -251,6 +251,7 @@ trait StdNames { final val QUASIQUOTE_TUPLE: NameType = "$quasiquote$tuple$" final val QUASIQUOTE_FUNCTION: NameType = "$quasiquote$function$" final val QUASIQUOTE_REFINE_STAT: NameType = "$quasiquote$refine$stat$" + final val QUASIQUOTE_EARLY_DEF: NameType = "$quasiquote$early$def$" // Annotation simple names, used in Namer final val BeanPropertyAnnot: NameType = "BeanProperty" @@ -592,6 +593,8 @@ trait StdNames { val SyntacticBlock: NameType = "SyntacticBlock" val SyntacticClassDef: NameType = "SyntacticClassDef" val SyntacticFunctionType: NameType= "SyntacticFunctionType" + val SyntacticModuleDef: NameType = "SyntacticModuleDef" + val SyntacticTraitDef: NameType = "SyntacticTraitDef" val SyntacticTypeApplied: NameType = "SyntacticTypeApplied" val This: NameType = "This" val ThisType: NameType = "ThisType" @@ -687,6 +690,7 @@ trait StdNames { val moduleClass : NameType = "moduleClass" val mkAnnotation: NameType = "mkAnnotation" val mkRefineStat: NameType = "mkRefineStat" + val mkEarlyDef: NameType = "mkEarlyDef" val ne: NameType = "ne" val newArray: NameType = "newArray" val newFreeTerm: NameType = "newFreeTerm" diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala index 648c921eb7..66345b7d0b 100644 --- a/src/reflect/scala/reflect/internal/TreeGen.scala +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -14,7 +14,8 @@ abstract class TreeGen extends macros.TreeBuilder { def rootScalaDot(name: Name) = Select(rootId(nme.scala_) setSymbol ScalaPackage, name) def scalaDot(name: Name) = Select(Ident(nme.scala_) setSymbol ScalaPackage, name) def scalaAnnotationDot(name: Name) = Select(scalaDot(nme.annotation), name) - def scalaAnyRefConstr = scalaDot(tpnme.AnyRef) setSymbol AnyRefClass // used in ide + def scalaAnyRefConstrRaw = scalaDot(tpnme.AnyRef) + def scalaAnyRefConstr = scalaAnyRefConstrRaw setSymbol AnyRefClass // used in ide def scalaFunctionConstr(argtpes: List[Tree], restpe: Tree, abstractFun: Boolean = false): Tree = { val cls = if (abstractFun) @@ -380,6 +381,17 @@ abstract class TreeGen extends macros.TreeBuilder { global.Template(parents, self, gvdefs ::: fieldDefs ::: constr ++: etdefs ::: rest) } + def mkParents(ownerMods: Modifiers, parents: List[Tree], parentPos: Position = NoPosition) = + if (ownerMods.isCase) parents ::: List(scalaDot(tpnme.Product), scalaDot(tpnme.Serializable)) + else if (parents.isEmpty) atPos(parentPos)(scalaAnyRefConstrRaw) :: Nil + else parents + + def mkClassDef(mods: Modifiers, name: TypeName, tparams: List[TypeDef], templ: Template): ClassDef = { + val isInterface = mods.isTrait && (templ.body forall treeInfo.isInterfaceMember) + val mods1 = if (isInterface) (mods | Flags.INTERFACE) else mods + ClassDef(mods1, name, tparams, templ) + } + /** Create positioned tree representing an object creation 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("splice term name into class") = forAll { (name: TypeName) => eqAst(q"class $name", "class " + name.toString) } @@ -42,10 +57,59 @@ trait ClassConstruction { self: QuasiquoteProperties => property("splice 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)") + } } trait TraitConstruction { self: QuasiquoteProperties => + property("splice name into trait def") = test { + val Foo = TypeName("Foo") + assert(q"trait $Foo" ≈ q"trait Foo") + } + + property("splice 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("splice 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("splice 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("splice 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("splice 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 => diff --git a/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala index 31d230d6fd..d6b60c4351 100644 --- a/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala @@ -15,11 +15,32 @@ object DefinitionDeconstructionProps with ValVarDeconstruction 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 => @@ -51,6 +72,26 @@ trait ClassDeconstruction { self: QuasiquoteProperties => 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 q"""$classMods class $name[..$targs] $ctorMods(...$argss) + extends { ..$early } with ..$parents { $self => ..$body }""" = parse(line) + } + 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)") + } } trait ModsDeconstruction { self: QuasiquoteProperties => -- cgit v1.2.3 From 546e94099d667c6395582fcba321ef95578121a5 Mon Sep 17 00:00:00 2001 From: Den Shabalin Date: Thu, 5 Sep 2013 20:41:48 +0200 Subject: SI-7803 support for matching of anonymous function literals --- .../scala/tools/nsc/ast/parser/Parsers.scala | 4 +++- .../scala/tools/reflect/quasiquotes/Parsers.scala | 5 +++++ .../scala/tools/reflect/quasiquotes/Reifiers.scala | 5 ++++- src/reflect/scala/reflect/api/BuildUtils.scala | 8 +++++++ .../scala/reflect/internal/BuildUtils.scala | 26 +++++++++++++++++----- src/reflect/scala/reflect/internal/StdNames.scala | 1 + .../quasiquotes/TermConstructionProps.scala | 10 +++++++++ .../quasiquotes/TermDeconstructionProps.scala | 9 +++++++- 8 files changed, 60 insertions(+), 8 deletions(-) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index f52ed60480..70b32f08ae 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -1445,7 +1445,7 @@ self => // The case still missed is unparenthesized single argument, like "x: Int => x + 1", which // may be impossible to distinguish from a self-type and so remains an error. (See #1564) def lhsIsTypedParamList() = t match { - case Parens(xs) if xs forall (_.isInstanceOf[Typed]) => true + case Parens(xs) if xs.forall(isTypedParam) => true case _ => false } if (in.token == ARROW && (location != InTemplate || lhsIsTypedParamList)) { @@ -1458,6 +1458,8 @@ self => parseOther } + def isTypedParam(t: Tree) = t.isInstanceOf[Typed] + /** {{{ * Expr ::= implicit Id => Expr * }}} diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala index 868c74ebcd..19888fa8d2 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala @@ -135,6 +135,11 @@ trait Parsers { self: Quasiquotes => case Ident(name: TermName) if isHole(name) => ValDef(NoMods | Flag.PRESUPER, name, Ident(tpnme.QUASIQUOTE_EARLY_DEF), EmptyTree) case _ => super.ensureEarlyDef(tree) } + + override def isTypedParam(tree: Tree) = super.isTypedParam(tree) || (tree match { + case Ident(name) if isHole(name) => true + case _ => false + }) } } diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala index 53b2e4cc96..fa305f3d07 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala @@ -8,7 +8,8 @@ import scala.reflect.internal.Flags._ trait Reifiers { self: Quasiquotes => import global._ import global.build.{SyntacticClassDef, SyntacticTraitDef, SyntacticModuleDef, - SyntacticBlock, SyntacticApplied, SyntacticTypeApplied} + SyntacticBlock, SyntacticApplied, SyntacticTypeApplied, + SyntacticFunction} import global.treeInfo._ import global.definitions._ import Cardinality._ @@ -67,6 +68,8 @@ trait Reifiers { self: Quasiquotes => reifyBuildCall(nme.SyntacticApplied, fun, argss) case SyntacticTypeApplied(fun, targs) if targs.nonEmpty => reifyBuildCall(nme.SyntacticTypeApplied, fun, targs) + case SyntacticFunction(args, body) => + reifyBuildCall(nme.SyntacticFunction, args, body) case Block(stats, last) => reifyBuildCall(nme.SyntacticBlock, stats :+ last) // parser emits trees with scala package symbol to ensure diff --git a/src/reflect/scala/reflect/api/BuildUtils.scala b/src/reflect/scala/reflect/api/BuildUtils.scala index 108efee37c..8b2e02c639 100644 --- a/src/reflect/scala/reflect/api/BuildUtils.scala +++ b/src/reflect/scala/reflect/api/BuildUtils.scala @@ -160,5 +160,13 @@ private[reflect] trait BuildUtils { self: Universe => def apply(argtpes: List[Tree], restpe: Tree): Tree def unapply(tree: Tree): Option[(List[Tree], Tree)] } + + val SyntacticFunction: SyntacticFunctionExtractor + + trait SyntacticFunctionExtractor { + def apply(params: List[ValDef], body: Tree): Tree + + def unapply(tree: Tree): Option[(List[ValDef], Tree)] + } } } diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala index 528b542361..936ca91c68 100644 --- a/src/reflect/scala/reflect/internal/BuildUtils.scala +++ b/src/reflect/scala/reflect/internal/BuildUtils.scala @@ -70,11 +70,12 @@ trait BuildUtils { self: SymbolTable => case _ => throw new IllegalArgumentException(s"Tree ${showRaw(tree)} isn't a correct representation of annotation, consider passing Ident as a first argument") } - def mkVparamss(argss: List[List[ValDef]]): List[List[ValDef]] = { - argss.map { _.map { - case vd @ ValDef(mods, _, _, EmptyTree) => copyValDef(vd)(mods = mods | PARAM) - case vd @ ValDef(mods, _, _, _) => copyValDef(vd)(mods = mods | PARAM | DEFAULTPARAM) - } } + def mkVparamss(argss: List[List[ValDef]]): List[List[ValDef]] = argss.map(_.map(mkParam)) + + def mkParam(vd: ValDef): ValDef = { + var newmods = (vd.mods | PARAM) & (~DEFERRED) + if (vd.rhs.nonEmpty) newmods |= DEFAULTPARAM + copyValDef(vd)(mods = newmods) } def mkTparams(tparams: List[Tree]): List[TypeDef] = @@ -327,6 +328,21 @@ trait BuildUtils { self: SymbolTable => case _ => None } } + + object SyntacticFunction extends SyntacticFunctionExtractor { + def apply(params: List[ValDef], body: Tree): Tree = { + val params0 = params.map { arg => + require(arg.rhs.isEmpty, "anonymous functions don't support default values") + mkParam(arg) + } + Function(params0, body) + } + + def unapply(tree: Tree): Option[(List[ValDef], Tree)] = tree match { + case Function(params, body) => Some((params, body)) + case _ => None + } + } } val build: BuildApi = new BuildImpl diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 936445f011..b9a32c481f 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -592,6 +592,7 @@ trait StdNames { val SyntacticApplied: NameType = "SyntacticApplied" val SyntacticBlock: NameType = "SyntacticBlock" val SyntacticClassDef: NameType = "SyntacticClassDef" + val SyntacticFunction: NameType = "SyntacticFunction" val SyntacticFunctionType: NameType= "SyntacticFunctionType" val SyntacticModuleDef: NameType = "SyntacticModuleDef" val SyntacticTraitDef: NameType = "SyntacticTraitDef" diff --git a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala index 599dfd442a..c6cca85c81 100644 --- a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala @@ -157,4 +157,14 @@ object TermConstructionProps extends QuasiquoteProperties("term construction") { val empty = List[Tree]() assert(q"(..$empty)" ≈ q"()") } + + property("function param flags are the same") = test { + val xy = q"val x: A" :: q"val y: B" :: Nil + assertEqAst(q"(..$xy) => x + y", "(x: A, y: B) => x + y") + } + + property("anonymous functions don't support default values") = test { + val x = q"val x: Int = 1" + assertThrows[IllegalArgumentException] { q"($x) => x" } + } } diff --git a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala index 6087bbdb74..762625a46a 100644 --- a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala @@ -67,4 +67,11 @@ object TermDeconstructionProps extends QuasiquoteProperties("term deconstruction val q"{ ..$xs }" = q"{ x1; x2; x3 }" assert(xs ≈ List(q"x1", q"x2", q"x3")) } -} \ No newline at end of file + + 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") + } +} -- cgit v1.2.3 From a455858fc19e47dca10f446df0f61297d0d60276 Mon Sep 17 00:00:00 2001 From: Den Shabalin Date: Thu, 5 Sep 2013 13:35:42 +0200 Subject: SI-7723 better support for deconstruction of new expressions --- .../scala/tools/reflect/quasiquotes/Reifiers.scala | 4 +++- src/reflect/scala/reflect/api/BuildUtils.scala | 7 +++++++ src/reflect/scala/reflect/internal/BuildUtils.scala | 15 +++++++++++++++ src/reflect/scala/reflect/internal/StdNames.scala | 1 + .../quasiquotes/TermDeconstructionProps.scala | 17 +++++++++++++++++ 5 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala index fa305f3d07..cb84b5d8c7 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala @@ -9,7 +9,7 @@ trait Reifiers { self: Quasiquotes => import global._ import global.build.{SyntacticClassDef, SyntacticTraitDef, SyntacticModuleDef, SyntacticBlock, SyntacticApplied, SyntacticTypeApplied, - SyntacticFunction} + SyntacticFunction, SyntacticNew} import global.treeInfo._ import global.definitions._ import Cardinality._ @@ -62,6 +62,8 @@ trait Reifiers { self: Quasiquotes => earlyDefs, parents, selfdef, body) case SyntacticModuleDef(mods, name, earlyDefs, parents, selfdef, body) => reifyBuildCall(nme.SyntacticModuleDef, mods, name, earlyDefs, parents, selfdef, body) + case SyntacticNew(earlyDefs, parents, selfdef, body) => + reifyBuildCall(nme.SyntacticNew, earlyDefs, parents, selfdef, body) case SyntacticApplied(fun, argss) if argss.length > 1 => reifyBuildCall(nme.SyntacticApplied, fun, argss) case SyntacticApplied(fun, argss @ (_ :+ (_ :+ Placeholder(_, _, DotDotDot)))) => diff --git a/src/reflect/scala/reflect/api/BuildUtils.scala b/src/reflect/scala/reflect/api/BuildUtils.scala index 8b2e02c639..7b3e287da7 100644 --- a/src/reflect/scala/reflect/api/BuildUtils.scala +++ b/src/reflect/scala/reflect/api/BuildUtils.scala @@ -154,6 +154,13 @@ private[reflect] trait BuildUtils { self: Universe => def unapply(tree: Tree): Option[List[Tree]] } + val SyntacticNew: SyntacticNewExtractor + + trait SyntacticNewExtractor { + def apply(earlyDefs: List[Tree], parents: List[Tree], selfdef: ValDef, body: List[Tree]): Tree + def unapply(tree: Tree): Option[(List[Tree], List[Tree], ValDef, List[Tree])] + } + val SyntacticFunctionType: SyntacticFunctionTypeExtractor trait SyntacticFunctionTypeExtractor { diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala index 936ca91c68..eaa4f7cba8 100644 --- a/src/reflect/scala/reflect/internal/BuildUtils.scala +++ b/src/reflect/scala/reflect/internal/BuildUtils.scala @@ -343,6 +343,21 @@ trait BuildUtils { self: SymbolTable => case _ => None } } + + object SyntacticNew extends SyntacticNewExtractor { + def apply(earlyDefs: List[Tree], parents: List[Tree], selfdef: ValDef, body: List[Tree]): Tree = + gen.mkNew(parents, selfdef, earlyDefs ::: body, NoPosition, NoPosition) + + def unapply(tree: Tree): Option[(List[Tree], List[Tree], ValDef, List[Tree])] = tree match { + case SyntacticApplied(Select(New(SyntacticTypeApplied(ident, targs)), nme.CONSTRUCTOR), argss) => + Some((Nil, SyntacticApplied(SyntacticTypeApplied(ident, targs), argss) :: Nil, emptyValDef, Nil)) + case SyntacticBlock(SyntacticClassDef(_, tpnme.ANON_CLASS_NAME, Nil, _, List(Nil), earlyDefs, parents, selfdef, body) :: + Apply(Select(New(Ident(tpnme.ANON_CLASS_NAME)), nme.CONSTRUCTOR), Nil) :: Nil) => + Some((earlyDefs, parents, selfdef, body)) + case _ => + None + } + } } val build: BuildApi = new BuildImpl diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index b9a32c481f..9ae86059f1 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -595,6 +595,7 @@ trait StdNames { val SyntacticFunction: NameType = "SyntacticFunction" val SyntacticFunctionType: NameType= "SyntacticFunctionType" val SyntacticModuleDef: NameType = "SyntacticModuleDef" + val SyntacticNew: NameType = "SyntacticNew" val SyntacticTraitDef: NameType = "SyntacticTraitDef" val SyntacticTypeApplied: NameType = "SyntacticTypeApplied" val This: NameType = "This" diff --git a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala index 762625a46a..45c7ee4bb7 100644 --- a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala @@ -74,4 +74,21 @@ object TermDeconstructionProps extends QuasiquoteProperties("term deconstruction 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 => }") + } } -- cgit v1.2.3 From 4ad10642e9dacfb50c6c0ac0faaf7e5839bdffb5 Mon Sep 17 00:00:00 2001 From: Den Shabalin Date: Thu, 5 Sep 2013 13:40:09 +0200 Subject: rename TupleN and TupleTypeN into SyntacticTuple and SyntacticTupleType --- src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala | 8 ++++---- src/reflect/scala/reflect/api/BuildUtils.scala | 6 +++--- src/reflect/scala/reflect/internal/BuildUtils.scala | 4 ++-- src/reflect/scala/reflect/internal/StdNames.scala | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala index cb84b5d8c7..2e134ddd34 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala @@ -100,20 +100,20 @@ trait Reifiers { self: Quasiquotes => def reifyTuple(args: List[Tree]) = args match { case Nil => reify(Literal(Constant(()))) case List(hole @ Placeholder(_, _, NoDot)) => reify(hole) - case List(Placeholder(_, _, _)) => reifyBuildCall(nme.TupleN, args) + case List(Placeholder(_, _, _)) => reifyBuildCall(nme.SyntacticTuple, args) // in a case we only have one element tuple without // any cardinality annotations this means that this is // just an expression wrapped in parentheses case List(other) => reify(other) - case _ => reifyBuildCall(nme.TupleN, args) + case _ => reifyBuildCall(nme.SyntacticTuple, args) } def reifyTupleType(args: List[Tree]) = args match { case Nil => reify(Select(Ident(nme.scala_), tpnme.Unit)) case List(hole @ Placeholder(_, _, NoDot)) => reify(hole) - case List(Placeholder(_, _, _)) => reifyBuildCall(nme.TupleTypeN, args) + case List(Placeholder(_, _, _)) => reifyBuildCall(nme.SyntacticTupleType, args) case List(other) => reify(other) - case _ => reifyBuildCall(nme.TupleTypeN, args) + case _ => reifyBuildCall(nme.SyntacticTupleType, args) } def reifyFunctionType(argtpes: List[Tree], restpe: Tree) = diff --git a/src/reflect/scala/reflect/api/BuildUtils.scala b/src/reflect/scala/reflect/api/BuildUtils.scala index 7b3e287da7..b80159d051 100644 --- a/src/reflect/scala/reflect/api/BuildUtils.scala +++ b/src/reflect/scala/reflect/api/BuildUtils.scala @@ -139,10 +139,10 @@ private[reflect] trait BuildUtils { self: Universe => def unapply(tree: Tree): Option[(Modifiers, TermName, List[Tree], List[Tree], ValDef, List[Tree])] } - val TupleN: TupleNExtractor - val TupleTypeN: TupleNExtractor + val SyntacticTuple: SyntacticTupleExtractor + val SyntacticTupleType: SyntacticTupleExtractor - trait TupleNExtractor { + trait SyntacticTupleExtractor { def apply(args: List[Tree]): Tree def unapply(tree: Tree): Option[List[Tree]] } diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala index eaa4f7cba8..0cbad0d641 100644 --- a/src/reflect/scala/reflect/internal/BuildUtils.scala +++ b/src/reflect/scala/reflect/internal/BuildUtils.scala @@ -255,7 +255,7 @@ trait BuildUtils { self: SymbolTable => } } - object TupleN extends TupleNExtractor { + object SyntacticTuple extends SyntacticTupleExtractor { def apply(args: List[Tree]): Tree = args match { case Nil => Literal(Constant(())) case _ => @@ -277,7 +277,7 @@ trait BuildUtils { self: SymbolTable => } } - object TupleTypeN extends TupleNExtractor { + object SyntacticTupleType extends SyntacticTupleExtractor { def apply(args: List[Tree]): Tree = args match { case Nil => self.Select(self.Ident(nme.scala_), tpnme.Unit) case _ => diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 9ae86059f1..e52f3140c3 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -597,13 +597,13 @@ trait StdNames { val SyntacticModuleDef: NameType = "SyntacticModuleDef" val SyntacticNew: NameType = "SyntacticNew" val SyntacticTraitDef: NameType = "SyntacticTraitDef" + val SyntacticTuple: NameType = "SyntacticTuple" + val SyntacticTupleType: NameType = "SyntacticTupleType" val SyntacticTypeApplied: NameType = "SyntacticTypeApplied" val This: NameType = "This" val ThisType: NameType = "ThisType" val True : NameType = "True" val Tuple2: NameType = "Tuple2" - val TupleN: NameType = "TupleN" - val TupleTypeN: NameType = "TupleTypeN" val TYPE_ : NameType = "TYPE" val TypeRef: NameType = "TypeRef" val TypeTree: NameType = "TypeTree" -- cgit v1.2.3 From cd07f9f9656ea1bdc6c27ef2be9192d52cd987a5 Mon Sep 17 00:00:00 2001 From: Den Shabalin Date: Thu, 5 Sep 2013 13:40:38 +0200 Subject: better support for ValDefs, VarDefs and DefDefs --- .../scala/tools/reflect/quasiquotes/Reifiers.scala | 7 ++++++ src/reflect/scala/reflect/api/BuildUtils.scala | 16 ++++++++++++ .../scala/reflect/internal/BuildUtils.scala | 29 ++++++++++++++++++++++ src/reflect/scala/reflect/internal/StdNames.scala | 3 +++ .../DefinitionDeconstructionProps.scala | 9 +++++++ 5 files changed, 64 insertions(+) diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala index 2e134ddd34..069ef09bc7 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala @@ -8,6 +8,7 @@ import scala.reflect.internal.Flags._ trait Reifiers { self: Quasiquotes => import global._ import global.build.{SyntacticClassDef, SyntacticTraitDef, SyntacticModuleDef, + SyntacticDefDef, SyntacticValDef, SyntacticVarDef, SyntacticBlock, SyntacticApplied, SyntacticTypeApplied, SyntacticFunction, SyntacticNew} import global.treeInfo._ @@ -64,6 +65,12 @@ trait Reifiers { self: Quasiquotes => reifyBuildCall(nme.SyntacticModuleDef, mods, name, earlyDefs, parents, selfdef, body) case SyntacticNew(earlyDefs, parents, selfdef, body) => reifyBuildCall(nme.SyntacticNew, earlyDefs, parents, selfdef, body) + case SyntacticDefDef(mods, name, tparams, vparamss, tpt, rhs) => + reifyBuildCall(nme.SyntacticDefDef, mods, name, tparams, vparamss, tpt, rhs) + case SyntacticValDef(mods, name, tpt, rhs) => + reifyBuildCall(nme.SyntacticValDef, mods, name, tpt, rhs) + case SyntacticVarDef(mods, name, tpt, rhs) => + reifyBuildCall(nme.SyntacticVarDef, mods, name, tpt, rhs) case SyntacticApplied(fun, argss) if argss.length > 1 => reifyBuildCall(nme.SyntacticApplied, fun, argss) case SyntacticApplied(fun, argss @ (_ :+ (_ :+ Placeholder(_, _, DotDotDot)))) => diff --git a/src/reflect/scala/reflect/api/BuildUtils.scala b/src/reflect/scala/reflect/api/BuildUtils.scala index b80159d051..ee6515b547 100644 --- a/src/reflect/scala/reflect/api/BuildUtils.scala +++ b/src/reflect/scala/reflect/api/BuildUtils.scala @@ -175,5 +175,21 @@ private[reflect] trait BuildUtils { self: Universe => def unapply(tree: Tree): Option[(List[ValDef], Tree)] } + + val SyntacticDefDef: SyntacticDefDefExtractor + + trait SyntacticDefDefExtractor { + def apply(mods: Modifiers, name: TermName, tparams: List[Tree], vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree): DefDef + + def unapply(tree: Tree): Option[(Modifiers, TermName, List[Tree], List[List[ValDef]], Tree, Tree)] + } + + val SyntacticValDef: SyntacticValDefExtractor + val SyntacticVarDef: SyntacticValDefExtractor + + trait SyntacticValDefExtractor { + def apply(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree): ValDef + def unapply(tree: Tree): Option[(Modifiers, TermName, Tree, Tree)] + } } } diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala index 0cbad0d641..7091b0e875 100644 --- a/src/reflect/scala/reflect/internal/BuildUtils.scala +++ b/src/reflect/scala/reflect/internal/BuildUtils.scala @@ -358,6 +358,35 @@ trait BuildUtils { self: SymbolTable => None } } + + object SyntacticDefDef extends SyntacticDefDefExtractor { + def apply(mods: Modifiers, name: TermName, tparams: List[Tree], vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree): DefDef = + DefDef(mods, name, mkTparams(tparams), mkVparamss(vparamss), tpt, rhs) + + def unapply(tree: Tree): Option[(Modifiers, TermName, List[Tree], List[List[ValDef]], Tree, Tree)] = tree match { + case DefDef(mods, name, tparams, vparamss, tpt, rhs) => Some((mods, name, tparams, vparamss, tpt, rhs)) + case _ => None + } + } + + trait SyntacticValDefBase extends SyntacticValDefExtractor { + val isMutable: Boolean + + def apply(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree) = { + val mods1 = if (isMutable) mods | MUTABLE else mods + ValDef(mods1, name, tpt, rhs) + } + + def unapply(tree: Tree): Option[(Modifiers, TermName, Tree, Tree)] = tree match { + case ValDef(mods, name, tpt, rhs) if mods.hasFlag(MUTABLE) == isMutable => + Some((mods, name, tpt, rhs)) + case _ => + None + } + } + + object SyntacticValDef extends SyntacticValDefBase { val isMutable = false } + object SyntacticVarDef extends SyntacticValDefBase { val isMutable = true } } val build: BuildApi = new BuildImpl diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index e52f3140c3..17a084f461 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -592,6 +592,7 @@ trait StdNames { val SyntacticApplied: NameType = "SyntacticApplied" val SyntacticBlock: NameType = "SyntacticBlock" val SyntacticClassDef: NameType = "SyntacticClassDef" + val SyntacticDefDef: NameType = "SyntacticDefDef" val SyntacticFunction: NameType = "SyntacticFunction" val SyntacticFunctionType: NameType= "SyntacticFunctionType" val SyntacticModuleDef: NameType = "SyntacticModuleDef" @@ -600,6 +601,8 @@ trait StdNames { val SyntacticTuple: NameType = "SyntacticTuple" val SyntacticTupleType: NameType = "SyntacticTupleType" val SyntacticTypeApplied: NameType = "SyntacticTypeApplied" + val SyntacticValDef: NameType = "SyntacticValDef" + val SyntacticVarDef: NameType = "SyntacticVarDef" val This: NameType = "This" val ThisType: NameType = "ThisType" val True : NameType = "True" diff --git a/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala index d6b60c4351..fdfbfe871c 100644 --- a/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala @@ -134,5 +134,14 @@ trait ValVarDeconstruction { self: QuasiquoteProperties => 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") } } } \ No newline at end of file -- cgit v1.2.3 From 71087fd13adb315b7f8a7ec7423cc49adbd55ecc Mon Sep 17 00:00:00 2001 From: Den Shabalin Date: Mon, 2 Sep 2013 16:25:42 +0200 Subject: make the postfixOps warning go away --- src/compiler/scala/tools/nsc/transform/Constructors.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala index cbe4f69d25..2ec7e97ac5 100644 --- a/src/compiler/scala/tools/nsc/transform/Constructors.scala +++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala @@ -174,7 +174,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { omittables ++= outerCandidatesForElision val bodyOfOuterAccessor: Map[Symbol, DefDef] = - defBuf collect { case dd: DefDef if outerCandidatesForElision(dd.symbol) => dd.symbol -> dd } toMap + defBuf.collect { case dd: DefDef if outerCandidatesForElision(dd.symbol) => dd.symbol -> dd }.toMap // no point traversing further once omittables is empty, all candidates ruled out already. object detectUsages extends Traverser { -- cgit v1.2.3 From 230f36d9ca99abe33f4379ffd573702b2671f83a Mon Sep 17 00:00:00 2001 From: Den Shabalin Date: Tue, 3 Sep 2013 16:10:54 +0200 Subject: unify handling of references to scala members for functions and tuples --- .../scala/reflect/internal/BuildUtils.scala | 54 ++++++++++++++-------- .../quasiquotes/TypeDeconstructionProps.scala | 2 +- 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala index 7091b0e875..2376787116 100644 --- a/src/reflect/scala/reflect/internal/BuildUtils.scala +++ b/src/reflect/scala/reflect/internal/BuildUtils.scala @@ -255,6 +255,33 @@ trait BuildUtils { self: SymbolTable => } } + private trait ScalaMemberRef { + val symbols: Seq[Symbol] + def result(name: Name): Option[Symbol] = + symbols.collect { case sym if sym.name == name => sym }.headOption + def unapply(tree: Tree): Option[Symbol] = tree match { + case id @ Ident(name) if symbols.contains(id.symbol) && name == id.symbol.name => + Some(id.symbol) + case Select(scalapkg @ Ident(nme.scala_), name) if scalapkg.symbol == ScalaPackage => + result(name) + case Select(Select(Ident(nme.ROOTPKG), nme.scala_), name) => + result(name) + case _ => None + } + } + private object TupleClassRef extends ScalaMemberRef { + val symbols = TupleClass.filter { _ != null }.toSeq + } + private object TupleCompanionRef extends ScalaMemberRef { + val symbols = TupleClassRef.symbols.map { _.companionModule } + } + private object UnitClassRef extends ScalaMemberRef { + val symbols = Seq(UnitClass) + } + private object FunctionClassRef extends ScalaMemberRef { + val symbols = FunctionClass.toSeq + } + object SyntacticTuple extends SyntacticTupleExtractor { def apply(args: List[Tree]): Tree = args match { case Nil => Literal(Constant(())) @@ -266,11 +293,9 @@ trait BuildUtils { self: SymbolTable => def unapply(tree: Tree): Option[List[Tree]] = tree match { case Literal(Constant(())) => Some(Nil) - case Apply(id: Ident, args) - if args.length <= MaxTupleArity && id.symbol == TupleClass(args.length).companionModule => - Some(args) - case Apply(Select(Ident(nme.scala_), TermName(tuple)), args) - if args.length <= MaxTupleArity && tuple == TupleClass(args.length).name => + case Apply(TupleCompanionRef(sym), args) + if args.length <= MaxTupleArity + && sym == TupleClass(args.length).companionModule => Some(args) case _ => None @@ -286,13 +311,10 @@ trait BuildUtils { self: SymbolTable => } def unapply(tree: Tree): Option[List[Tree]] = tree match { - case Select(Ident(nme.scala_), tpnme.Unit) => + case UnitClassRef(_) => Some(Nil) - case AppliedTypeTree(id: Ident, args) - if args.length <= MaxTupleArity && id.symbol == TupleClass(args.length) => - Some(args) - case AppliedTypeTree(Select(id @ Ident(nme.scala_), TermName(tuple)), args) - if args.length <= MaxTupleArity && id.symbol == ScalaPackage && tuple == TupleClass(args.length).name => + case AppliedTypeTree(TupleClassRef(sym), args) + if args.length <= MaxTupleArity && sym == TupleClass(args.length) => Some(args) case _ => None @@ -306,14 +328,8 @@ trait BuildUtils { self: SymbolTable => } def unapply(tree: Tree): Option[(List[Tree], Tree)] = tree match { - case AppliedTypeTree(id: Ident, args @ (argtpes :+ restpe)) - if args.length - 1 <= MaxFunctionArity && id.symbol == FunctionClass(args.length - 1) => - Some((argtpes, restpe)) - case AppliedTypeTree(Select(pack, fun), args @ (argtpes :+ restpe)) - if args.length - 1 <= MaxFunctionArity && pack.symbol == ScalaPackage && fun == FunctionClass(args.length - 1).name => - Some((argtpes, restpe)) - case AppliedTypeTree(Select(Select(Ident(nme.ROOTPKG), nme.scala_), fun), args @ (argtpes :+ restpe)) - if args.length - 1 <= MaxFunctionArity && fun == FunctionClass(args.length - 1).name => + case AppliedTypeTree(FunctionClassRef(sym), args @ (argtpes :+ restpe)) + if args.length - 1 <= MaxFunctionArity && sym == FunctionClass(args.length - 1) => Some((argtpes, restpe)) case _ => None } diff --git a/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala index 02787c551b..e1d5f4df96 100644 --- a/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TypeDeconstructionProps.scala @@ -19,7 +19,7 @@ object TypeDeconstructionProps extends QuasiquoteProperties("type deconstruction } property("tuple type") = test { - val tq"(..$empty)" = tq"scala.Unit" + val tq"(..$empty)" = tq"_root_.scala.Unit" assert(empty.isEmpty) val tq"(..$ts)" = tq"(t1, t2)" assert(ts ≈ List(tq"t1", tq"t2")) -- cgit v1.2.3 From cdac66f750acb6fbf000215b8534f27f51da00a3 Mon Sep 17 00:00:00 2001 From: Den Shabalin Date: Thu, 5 Sep 2013 14:38:31 +0200 Subject: streamline implementation of annotation splicing Syntax spec mislead me to believe that annotation can't have type parameters or multiple argument lists... I guess the lesson here is don't trust the spec. --- .../tools/reflect/quasiquotes/Placeholders.scala | 7 ++--- .../scala/tools/reflect/quasiquotes/Reifiers.scala | 35 +++++++--------------- src/reflect/scala/reflect/api/BuildUtils.scala | 4 ++- .../scala/reflect/internal/BuildUtils.scala | 15 +++++----- .../quasiquotes/DefinitionConstructionProps.scala | 10 +++++++ 5 files changed, 34 insertions(+), 37 deletions(-) diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala index c136e6d785..e20d98c0f1 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala @@ -92,10 +92,9 @@ trait Placeholders { self: Quasiquotes => } } - object AnnotPlaceholder { - def unapply(tree: Tree): Option[(Tree, Location, Cardinality, List[Tree])] = tree match { - case Apply(Select(New(Placeholder(tree, loc, card)), nme.CONSTRUCTOR), args) => Some(tree, loc, card, args) - case _ => None + object AnnotPlaceholder extends HolePlaceholder { + def matching = { + case Apply(Select(New(Ident(name)), nme.CONSTRUCTOR), Nil) => name } } diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala index 069ef09bc7..af4e34536c 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala @@ -130,6 +130,8 @@ trait Reifiers { self: Quasiquotes => def reifyEarlyDef(tree: Tree) = tree + def reifyAnnotation(tree: Tree) = tree + /** Splits list into a list of groups where subsequent elements are considered * similar by the corresponding function. * @@ -189,7 +191,12 @@ trait Reifiers { self: Quasiquotes => reify(_) } - def reifyAnnotList(annots: List[Tree]): Tree + def reifyAnnotList(annots: List[Tree]): Tree = reifyMultiCardinalityList(annots) { + case AnnotPlaceholder(tree, _, DotDot) => reifyAnnotation(tree) + } { + case AnnotPlaceholder(tree, UnknownLocation | TreeLocation(_), NoDot) => reifyAnnotation(tree) + case other => reify(other) + } // These are explicit flags except those that are used // to overload the same tree for two different concepts: @@ -236,19 +243,6 @@ trait Reifiers { self: Quasiquotes => tail.foldLeft[Tree](reifyGroup(head)) { (tree, lst) => Apply(Select(tree, nme.PLUSPLUS), List(reifyGroup(lst))) } } - override def reifyAnnotList(annots: List[Tree]): Tree = reifyMultiCardinalityList(annots) { - case AnnotPlaceholder(tree, _, DotDot, args) => - val x: TermName = c.freshName() - val xToAnnotationCtor = Function( - List(ValDef(Modifiers(PARAM), x, TypeTree(), EmptyTree)), - mirrorBuildCall(nme.mkAnnotation, Ident(x), reify(args))) - Apply(Select(tree, nme.map), List(xToAnnotationCtor)) - } { - case AnnotPlaceholder(tree, _: TreeLocation, _, args) => - mirrorBuildCall(nme.mkAnnotation, tree, reify(args)) - case other => reify(other) - } - override def reifyModifiers(m: Modifiers) = if (m == NoMods) super.reifyModifiers(m) else { @@ -283,6 +277,8 @@ trait Reifiers { self: Quasiquotes => override def reifyRefineStat(tree: Tree) = mirrorBuildCall(nme.mkRefineStat, tree) override def reifyEarlyDef(tree: Tree) = mirrorBuildCall(nme.mkEarlyDef, tree) + + override def reifyAnnotation(tree: Tree) = mirrorBuildCall(nme.mkAnnotation, tree) } class UnapplyReifier extends Reifier { @@ -301,17 +297,6 @@ trait Reifiers { self: Quasiquotes => mkList(xs.map(fallback)) } - override def reifyAnnotList(annots: List[Tree]): Tree = reifyMultiCardinalityList(annots) { - case AnnotPlaceholder(tree, _, DotDot, Nil) => tree - } { - case AnnotPlaceholder(tree, _, NoDot, Nil) => tree - case AnnotPlaceholder(tree, _, NoDot, args) => - val selectCONSTRUCTOR = Apply(Select(u, nme.Select), List(Apply(Select(u, nme.New), List(tree)), Select(Select(u, nme.nmeNme), nme.nmeCONSTRUCTOR))) - Apply(Select(u, nme.Apply), List(selectCONSTRUCTOR, reify(args))) - case other => - reify(other) - } - override def reifyModifiers(m: Modifiers) = if (m == NoMods) super.reifyModifiers(m) else { diff --git a/src/reflect/scala/reflect/api/BuildUtils.scala b/src/reflect/scala/reflect/api/BuildUtils.scala index ee6515b547..60c2a81947 100644 --- a/src/reflect/scala/reflect/api/BuildUtils.scala +++ b/src/reflect/scala/reflect/api/BuildUtils.scala @@ -72,7 +72,9 @@ private[reflect] trait BuildUtils { self: Universe => def setSymbol[T <: Tree](tree: T, sym: Symbol): T - def mkAnnotation(tree: Tree, args: List[Tree]): Tree + def mkAnnotation(tree: Tree): Tree + + def mkAnnotation(trees: List[Tree]): List[Tree] def mkRefineStat(stat: Tree): Tree diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala index 2376787116..06a6e10c30 100644 --- a/src/reflect/scala/reflect/internal/BuildUtils.scala +++ b/src/reflect/scala/reflect/internal/BuildUtils.scala @@ -61,15 +61,16 @@ trait BuildUtils { self: SymbolTable => def setSymbol[T <: Tree](tree: T, sym: Symbol): T = { tree.setSymbol(sym); tree } - def mkAnnotation(tree: Tree, args: List[Tree]): Tree = tree match { - case ident: Ident => Apply(self.Select(New(ident), nme.CONSTRUCTOR: TermName), args) - case call @ Apply(Select(New(ident: Ident), nme.CONSTRUCTOR), _) => - if (args.nonEmpty) - throw new IllegalArgumentException("Can't splice annotation that already contains args with extra args, consider merging these lists together") - call - case _ => throw new IllegalArgumentException(s"Tree ${showRaw(tree)} isn't a correct representation of annotation, consider passing Ident as a first argument") + def mkAnnotation(tree: Tree): Tree = tree match { + case SyntacticNew(Nil, SyntacticApplied(SyntacticTypeApplied(_, _), _) :: Nil, emptyValDef, Nil) => + tree + case _ => + throw new IllegalArgumentException(s"Tree ${showRaw(tree)} isn't a correct representation of annotation." + + """Consider reformatting it into a q"new $name[..$targs](...$argss)" shape""") } + def mkAnnotation(trees: List[Tree]): List[Tree] = trees.map(mkAnnotation) + def mkVparamss(argss: List[List[ValDef]]): List[List[ValDef]] = argss.map(_.map(mkParam)) def mkParam(vd: ValDef): ValDef = { diff --git a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala index 96105b9581..153e23d947 100644 --- a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala @@ -280,4 +280,14 @@ trait MethodConstruction { self: QuasiquoteProperties => q"@$a(y) def foo" } } + + property("splice annotation with targs") = test { + val a = q"new Foo[A, B]" + assertEqAst(q"@$a def foo", "@Foo[A,B] def foo") + } + + property("splice annotation with multiple argument lists") = test{ + val a = q"new Foo(a)(b)" + assertEqAst(q"@$a def foo", "@Foo(a)(b) def foo") + } } \ No newline at end of file -- cgit v1.2.3