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