summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDen Shabalin <den.shabalin@gmail.com>2013-09-05 20:41:48 +0200
committerDen Shabalin <den.shabalin@gmail.com>2013-09-05 20:42:10 +0200
commit546e94099d667c6395582fcba321ef95578121a5 (patch)
tree6c250294e163c0aa008d117b5e994003174857fe
parent1352fea1c4ecfa0fd66ff1d5ad6e0ee437b1a59f (diff)
downloadscala-546e94099d667c6395582fcba321ef95578121a5.tar.gz
scala-546e94099d667c6395582fcba321ef95578121a5.tar.bz2
scala-546e94099d667c6395582fcba321ef95578121a5.zip
SI-7803 support for matching of anonymous function literals
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala4
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala5
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala5
-rw-r--r--src/reflect/scala/reflect/api/BuildUtils.scala8
-rw-r--r--src/reflect/scala/reflect/internal/BuildUtils.scala26
-rw-r--r--src/reflect/scala/reflect/internal/StdNames.scala1
-rw-r--r--test/files/scalacheck/quasiquotes/TermConstructionProps.scala10
-rw-r--r--test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala9
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")
+ }
+}