summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala2
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala8
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala8
-rw-r--r--src/reflect/scala/reflect/api/BuildUtils.scala7
-rw-r--r--src/reflect/scala/reflect/internal/BuildUtils.scala12
-rw-r--r--src/reflect/scala/reflect/internal/StdNames.scala1
-rw-r--r--src/reflect/scala/reflect/internal/TreeGen.scala8
-rw-r--r--test/files/neg/macro-quasiquotes.check2
-rw-r--r--test/files/scalacheck/quasiquotes/TermConstructionProps.scala24
-rw-r--r--test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala20
10 files changed, 77 insertions, 15 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index 07e24900e9..a894e38ab4 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -1403,7 +1403,7 @@ self =>
if (in.token == EQUALS) {
t match {
case Ident(_) | Select(_, _) | Apply(_, _) =>
- t = atPos(t.pos.startOrPoint, in.skipToken()) { makeAssign(t, expr()) }
+ t = atPos(t.pos.startOrPoint, in.skipToken()) { gen.mkAssign(t, expr()) }
case _ =>
}
} else if (in.token == COLON) {
diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
index 91ff530e05..0d93a1b427 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
@@ -191,14 +191,6 @@ abstract class TreeBuilder {
}
}
- /** Create a tree representing an assignment <lhs = rhs> */
- def makeAssign(lhs: Tree, rhs: Tree): Tree = lhs match {
- case Apply(fn, args) =>
- Apply(atPos(fn.pos) { Select(fn, nme.update) }, args ::: List(rhs))
- case _ =>
- Assign(lhs, rhs)
- }
-
/** Tree for `od op`, start is start0 if od.pos is borked. */
def makePostfixSelect(start0: Int, end: Int, od: Tree, op: Name): Tree = {
val start = if (od.pos.isDefined) od.pos.startOrPoint else start0
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
index c2d8bcdcd6..0d1fb6be07 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
@@ -10,7 +10,7 @@ trait Reifiers { self: Quasiquotes =>
import global.build.{SyntacticClassDef, SyntacticTraitDef, SyntacticModuleDef,
SyntacticDefDef, SyntacticValDef, SyntacticVarDef,
SyntacticBlock, SyntacticApplied, SyntacticTypeApplied,
- SyntacticFunction, SyntacticNew}
+ SyntacticFunction, SyntacticNew, SyntacticAssign}
import global.treeInfo._
import global.definitions._
import Cardinality._
@@ -71,9 +71,9 @@ trait Reifiers { self: Quasiquotes =>
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)))) =>
+ case SyntacticAssign(lhs, rhs) =>
+ reifyBuildCall(nme.SyntacticAssign, lhs, rhs)
+ case SyntacticApplied(fun, argss) if argss.nonEmpty =>
reifyBuildCall(nme.SyntacticApplied, fun, argss)
case SyntacticTypeApplied(fun, targs) if targs.nonEmpty =>
reifyBuildCall(nme.SyntacticTypeApplied, fun, targs)
diff --git a/src/reflect/scala/reflect/api/BuildUtils.scala b/src/reflect/scala/reflect/api/BuildUtils.scala
index 60c2a81947..551c27bf9c 100644
--- a/src/reflect/scala/reflect/api/BuildUtils.scala
+++ b/src/reflect/scala/reflect/api/BuildUtils.scala
@@ -193,5 +193,12 @@ private[reflect] trait BuildUtils { self: Universe =>
def apply(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree): ValDef
def unapply(tree: Tree): Option[(Modifiers, TermName, Tree, Tree)]
}
+
+ val SyntacticAssign: SyntacticAssignExtractor
+
+ trait SyntacticAssignExtractor {
+ def apply(lhs: Tree, rhs: Tree): Tree
+ def unapply(tree: Tree): Option[(Tree, Tree)]
+ }
}
}
diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala
index 2584dcb117..1049f83d46 100644
--- a/src/reflect/scala/reflect/internal/BuildUtils.scala
+++ b/src/reflect/scala/reflect/internal/BuildUtils.scala
@@ -139,7 +139,7 @@ trait BuildUtils { self: SymbolTable =>
object SyntacticApplied extends SyntacticAppliedExtractor {
def apply(tree: Tree, argss: List[List[Tree]]): Tree =
- argss.foldLeft(tree) { Apply(_, _) }
+ argss.foldLeft(tree) { (f, args) => Apply(f, args.map(treeInfo.assignmentToMaybeNamedArg)) }
def unapply(tree: Tree): Some[(Tree, List[List[Tree]])] = {
val treeInfo.Applied(fun, targs, argss) = tree
@@ -400,6 +400,16 @@ trait BuildUtils { self: SymbolTable =>
object SyntacticValDef extends SyntacticValDefBase { val isMutable = false }
object SyntacticVarDef extends SyntacticValDefBase { val isMutable = true }
+
+ object SyntacticAssign extends SyntacticAssignExtractor {
+ def apply(lhs: Tree, rhs: Tree): Tree = gen.mkAssign(lhs, rhs)
+ def unapply(tree: Tree): Option[(Tree, Tree)] = tree match {
+ case Assign(lhs, rhs) => Some((lhs, rhs))
+ case AssignOrNamedArg(lhs, rhs) => Some((lhs, rhs))
+ case Apply(Select(fn, nme.update), args :+ rhs) => Some((atPos(fn.pos)(Apply(fn, args)), rhs))
+ 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 6407a3979c..f4eae5590a 100644
--- a/src/reflect/scala/reflect/internal/StdNames.scala
+++ b/src/reflect/scala/reflect/internal/StdNames.scala
@@ -602,6 +602,7 @@ trait StdNames {
val SelectFromTypeTree: NameType = "SelectFromTypeTree"
val StringContext: NameType = "StringContext"
val SyntacticApplied: NameType = "SyntacticApplied"
+ val SyntacticAssign: NameType = "SyntacticAssign"
val SyntacticBlock: NameType = "SyntacticBlock"
val SyntacticClassDef: NameType = "SyntacticClassDef"
val SyntacticDefDef: NameType = "SyntacticDefDef"
diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala
index 26adf20c52..0050df67d8 100644
--- a/src/reflect/scala/reflect/internal/TreeGen.scala
+++ b/src/reflect/scala/reflect/internal/TreeGen.scala
@@ -443,4 +443,12 @@ abstract class TreeGen extends macros.TreeBuilder {
case head :: Nil => head
case _ => gen.mkBlock(stats)
}
+
+ /** Create a tree representing an assignment <lhs = rhs> */
+ def mkAssign(lhs: Tree, rhs: Tree): Tree = lhs match {
+ case Apply(fn, args) =>
+ Apply(atPos(fn.pos)(Select(fn, nme.update)), args :+ rhs)
+ case _ =>
+ Assign(lhs, rhs)
+ }
}
diff --git a/test/files/neg/macro-quasiquotes.check b/test/files/neg/macro-quasiquotes.check
index a2d48723b5..96ef75dd32 100644
--- a/test/files/neg/macro-quasiquotes.check
+++ b/test/files/neg/macro-quasiquotes.check
@@ -1,6 +1,6 @@
Macros_1.scala:14: error: macro implementation has wrong shape:
required: (x: Impls.this.c.Expr[Int]): Impls.this.c.Expr[Any]
- found : (x: Impls.this.c.universe.Block): Impls.this.c.universe.Apply
+ found : (x: Impls.this.c.universe.Block): Impls.this.c.universe.Tree
type mismatch for parameter x: Impls.this.c.Expr[Int] does not conform to Impls.this.c.universe.Block
def m3(x: Int) = macro Impls.impl3
^
diff --git a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala
index c6cca85c81..753ad1aa59 100644
--- a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala
+++ b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala
@@ -167,4 +167,28 @@ object TermConstructionProps extends QuasiquoteProperties("term construction") {
val x = q"val x: Int = 1"
assertThrows[IllegalArgumentException] { q"($x) => x" }
}
+
+ property("assign variable") = test {
+ val v = q"v"
+ val value = q"foo"
+ assertEqAst(q"$v = $value", "v = foo")
+ }
+
+ property("assign update 1") = test {
+ val v = q"v"
+ val args = q"1" :: q"2" :: Nil
+ val value = q"foo"
+ assertEqAst(q"$v(..$args) = $value", "v(1, 2) = foo")
+ }
+
+ property("assign update 2") = test {
+ val a = q"v(0)"
+ val value = q"foo"
+ assertEqAst(q"$a = $value", "v(0) = foo")
+ }
+
+ property("assign or named arg") = test {
+ val assignx = q"x = 1"
+ assertEqAst(q"f($assignx)", "f(x = 1)")
+ }
}
diff --git a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala
index 45c7ee4bb7..22d4b1ce4f 100644
--- a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala
+++ b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala
@@ -91,4 +91,24 @@ object TermDeconstructionProps extends QuasiquoteProperties("term deconstruction
matches("new { val early = 1} with Parent[Int] { body }")
matches("new Foo { selfie => }")
}
+
+ property("exhaustive assign pattern") = test {
+ def matches(tree: Tree) { val q"$rhs = $lhs" = tree }
+ matches(parse("left = right"))
+ matches(parse("arr(1) = 2"))
+ matches(AssignOrNamedArg(EmptyTree, EmptyTree))
+ }
+
+ property("deconstruct update 1") = test {
+ val q"$obj(..$args) = $value" = q"foo(bar) = baz"
+ assert(obj ≈ q"foo")
+ assert(args ≈ List(q"bar"))
+ assert(value ≈ q"baz")
+ }
+
+ property("deconstruct update 2") = test {
+ val q"$left = $value" = q"foo(bar) = baz"
+ assert(left ≈ q"foo(bar)")
+ assert(value ≈ q"baz")
+ }
}