From 393829489a1e352c2ed659b16b6bea24069f4f9a Mon Sep 17 00:00:00 2001 From: Denys Shabalin Date: Thu, 16 Jan 2014 11:58:38 +0100 Subject: SI-6844 restrict splicing in parameter position MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously were a bit too permissive on how splicing in function parameter position worked. This made confusing things like possible: val x = TermName(“x”) q”def foo($x)” Now you can either splice trees in that position (ValDefs) or you have to provide type if you splice a name. --- test/files/neg/quasiquotes-syntax-error-position.check | 5 ++++- test/files/neg/quasiquotes-syntax-error-position.scala | 3 ++- test/files/neg/t6844.check | 6 ++++++ test/files/neg/t6844.scala | 5 +++++ 4 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 test/files/neg/t6844.check create mode 100644 test/files/neg/t6844.scala (limited to 'test/files') diff --git a/test/files/neg/quasiquotes-syntax-error-position.check b/test/files/neg/quasiquotes-syntax-error-position.check index 25e5b8d75a..14fef16e01 100644 --- a/test/files/neg/quasiquotes-syntax-error-position.check +++ b/test/files/neg/quasiquotes-syntax-error-position.check @@ -29,4 +29,7 @@ quasiquotes-syntax-error-position.scala:13: error: end of quote expected but 'ca quasiquotes-syntax-error-position.scala:14: error: ')' expected but end of quote found. pq"$a(bar" ^ -10 errors found +quasiquotes-syntax-error-position.scala:15: error: ':' expected but ')' found. + q"def foo(x)" + ^ +11 errors found diff --git a/test/files/neg/quasiquotes-syntax-error-position.scala b/test/files/neg/quasiquotes-syntax-error-position.scala index b97af52cfc..7b1d66ba00 100644 --- a/test/files/neg/quasiquotes-syntax-error-position.scala +++ b/test/files/neg/quasiquotes-syntax-error-position.scala @@ -12,4 +12,5 @@ object test extends App { tq"$t => $t $t]" cq"pattern => body ; case pattern2 =>" pq"$a(bar" -} \ No newline at end of file + q"def foo(x)" +} diff --git a/test/files/neg/t6844.check b/test/files/neg/t6844.check new file mode 100644 index 0000000000..1fc2485520 --- /dev/null +++ b/test/files/neg/t6844.check @@ -0,0 +1,6 @@ +t6844.scala:4: error: type mismatch; + found : reflect.runtime.universe.TermName + required: reflect.runtime.universe.Tree + q"def foo($x)" + ^ +one error found diff --git a/test/files/neg/t6844.scala b/test/files/neg/t6844.scala new file mode 100644 index 0000000000..809d9d0f98 --- /dev/null +++ b/test/files/neg/t6844.scala @@ -0,0 +1,5 @@ +import scala.reflect.runtime.universe._ +object Test extends App { + val x = TermName("x") + q"def foo($x)" +} -- cgit v1.2.3 From 8e9862473abd03bded2d3afa60c777099f7872c5 Mon Sep 17 00:00:00 2001 From: Denys Shabalin Date: Thu, 16 Jan 2014 12:01:30 +0100 Subject: SI-8076 improve support for implicit argument list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds support for construction and deconstruction of implicit argument list which was originally suggested by @cvogt. 1. Splicing vale into implicit argument list automatically adds implicit flag to them: val x = q"val x: Int" q"def foo(implicit $x)" // <=> q"def foo(implicit x: Int)" 2. One might extract implicit argument list separately from other argument lists: val q”def foo(...$argss)(implicit ..$impl)" = q"def foo(implicit x: Int) // argss is Nil, impl contains valdef for x But this doesn't require you to always extract it separatly: val q”def foo(...$argss)" = q"def foo(implicit x: Int) // argss contains valdef for x --- .../scala/tools/reflect/quasiquotes/Reifiers.scala | 8 ++++-- src/reflect/scala/reflect/api/BuildUtils.scala | 7 +++++ .../scala/reflect/internal/BuildUtils.scala | 14 ++++++++++ src/reflect/scala/reflect/internal/StdNames.scala | 1 + .../quasiquotes/DefinitionConstructionProps.scala | 15 ++++++++++- .../DefinitionDeconstructionProps.scala | 31 +++++++++++++++++++++- 6 files changed, 72 insertions(+), 4 deletions(-) (limited to 'test/files') diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala index 58baad01a1..9c0a036541 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala @@ -160,8 +160,12 @@ trait Reifiers { self: Quasiquotes => reifyBuildCall(nme.SyntacticObjectDef, 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 SyntacticDefDef(mods, name, tparams, build.ImplicitParams(vparamss, implparams), tpt, rhs) => + if (implparams.nonEmpty) + mirrorBuildCall(nme.SyntacticDefDef, reify(mods), reify(name), reify(tparams), + reifyBuildCall(nme.ImplicitParams, vparamss, implparams), reify(tpt), reify(rhs)) + else + reifyBuildCall(nme.SyntacticDefDef, mods, name, tparams, vparamss, tpt, rhs) case SyntacticValDef(mods, name, tpt, rhs) if tree != noSelfType => reifyBuildCall(nme.SyntacticValDef, mods, name, tpt, rhs) case SyntacticVarDef(mods, name, tpt, rhs) => diff --git a/src/reflect/scala/reflect/api/BuildUtils.scala b/src/reflect/scala/reflect/api/BuildUtils.scala index 89492e75c4..d70fa01ae6 100644 --- a/src/reflect/scala/reflect/api/BuildUtils.scala +++ b/src/reflect/scala/reflect/api/BuildUtils.scala @@ -94,6 +94,13 @@ private[reflect] trait BuildUtils { self: Universe => def freshTypeName(prefix: String): TypeName + val ImplicitParams: ImplicitParamsExtractor + + trait ImplicitParamsExtractor { + def apply(paramss: List[List[ValDef]], implparams: List[ValDef]): List[List[ValDef]] + def unapply(vparamss: List[List[ValDef]]): Some[(List[List[ValDef]], List[ValDef])] + } + val ScalaDot: ScalaDotExtractor trait ScalaDotExtractor { diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala index 657b098952..292413674b 100644 --- a/src/reflect/scala/reflect/internal/BuildUtils.scala +++ b/src/reflect/scala/reflect/internal/BuildUtils.scala @@ -86,6 +86,10 @@ trait BuildUtils { self: SymbolTable => """consider reformatting it into q"val $name: $T = $default" shape""") } + def mkImplicitParam(args: List[Tree]): List[ValDef] = args.map(mkImplicitParam) + + def mkImplicitParam(tree: Tree): ValDef = mkParam(tree, IMPLICIT | PARAM) + def mkTparams(tparams: List[Tree]): List[TypeDef] = tparams.map { case td: TypeDef => copyTypeDef(td)(mods = (td.mods | PARAM) & (~DEFERRED)) @@ -143,6 +147,16 @@ trait BuildUtils { self: SymbolTable => protected implicit def fresh: FreshNameCreator = self.currentFreshNameCreator + object ImplicitParams extends ImplicitParamsExtractor { + def apply(paramss: List[List[ValDef]], implparams: List[ValDef]): List[List[ValDef]] = + if (implparams.nonEmpty) paramss :+ mkImplicitParam(implparams) else paramss + + def unapply(vparamss: List[List[ValDef]]): Some[(List[List[ValDef]], List[ValDef])] = vparamss match { + case init :+ (last @ (initlast :: _)) if initlast.mods.isImplicit => Some((init, last)) + case _ => Some((vparamss, Nil)) + } + } + 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 73ad9def73..49c26ca58d 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -576,6 +576,7 @@ trait StdNames { val Flag : NameType = "Flag" val FlagsRepr: NameType = "FlagsRepr" val Ident: NameType = "Ident" + val ImplicitParams: NameType = "ImplicitParams" val Import: NameType = "Import" val Literal: NameType = "Literal" val LiteralAnnotArg: NameType = "LiteralAnnotArg" diff --git a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala index 2af656c7c9..3166eb7a99 100644 --- a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala @@ -7,6 +7,7 @@ object DefinitionConstructionProps with TraitConstruction with TypeDefConstruction with ValDefConstruction + with DefConstruction with PackageConstruction { property("SI-6842") = test { val x: Tree = q"val x: Int" @@ -349,4 +350,16 @@ trait PackageConstruction { self: QuasiquoteProperties => assertEqAst(q"package object foo extends { ..$edefs } with Any", "package object foo extends { val x = 1; type I = Int } with Any") } -} \ No newline at end of file +} + +trait DefConstruction { self: QuasiquoteProperties => + property("construct implicit args (1)") = test { + val x = q"val x: Int" + assertEqAst(q"def foo(implicit $x) = x", "def foo(implicit x: Int) = x") + } + + property("construct implicit args (2)") = test { + val xs = q"val x1: Int" :: q"val x2: Long" :: Nil + assertEqAst(q"def foo(implicit ..$xs) = x1 + x2", "def foo(implicit x1: Int, x2: Long) = x1 + x2") + } +} diff --git a/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala index 94465930ed..209fe9bbeb 100644 --- a/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala @@ -8,6 +8,7 @@ object DefinitionDeconstructionProps with ObjectDeconstruction with ModsDeconstruction with ValVarDeconstruction + with DefDeconstruction with PackageDeconstruction trait TraitDeconstruction { self: QuasiquoteProperties => @@ -179,4 +180,32 @@ trait PackageDeconstruction { self: QuasiquoteProperties => matches("package object foo extends { val early = 1 } with daddy") assertThrows[MatchError] { matches("object foo") } } -} \ No newline at end of file +} + +trait DefDeconstruction { self: QuasiquoteProperties => + property("exhaustive def matcher") = test { + def matches(line: String) = { + val t = parse(line) + val q"$mods0 def $name0[..$targs0](...$argss0): $restpe0 = $body0" = t + val q"$mods1 def $name1[..$targs1](...$argss1)(implicit ..$impl1): $restpe1 = $body1" = t + } + matches("def foo = foo") + matches("implicit def foo: Int = 2") + matches("def foo[T](x: T): T = x") + matches("def foo[A: B] = implicitly[B[A]]") + matches("private def foo = 0") + matches("def foo[A <% B] = null") + matches("def foo(one: One)(two: Two) = (one, two)") + matches("def foo[T](args: T*) = args.toList") + } + + property("extract implicit arg list (1)") = test { + val q"def foo(...$argss)(implicit ..$impl)" = q"def foo(x: Int)(implicit y: Int)" + assert(impl ≈ List(q"${Modifiers(IMPLICIT | PARAM)} val y: Int")) + } + + property("extract implicit arg list (2)") = test { + val q"def foo(...$argss)(implicit ..$impl)" = q"def foo(x: Int)" + assert(impl.isEmpty) + } +} -- cgit v1.2.3