diff options
-rwxr-xr-x | build.xml | 34 | ||||
-rw-r--r-- | src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala | 10 | ||||
-rw-r--r-- | src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala | 6 | ||||
-rw-r--r-- | src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala | 4 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/ReificationSupport.scala | 29 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/StdNames.scala | 1 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/TreeGen.scala | 2 | ||||
-rw-r--r-- | test/files/neg/quasiquotes-syntax-error-position.check | 14 | ||||
-rw-r--r-- | test/files/neg/quasiquotes-syntax-error-position.scala | 5 | ||||
-rw-r--r-- | test/files/pos/t8352.check | 0 | ||||
-rw-r--r-- | test/files/pos/t8352/Macros_1.scala | 7 | ||||
-rw-r--r-- | test/files/pos/t8352/Test_2.scala | 5 | ||||
-rw-r--r-- | test/files/scalacheck/quasiquotes/TermConstructionProps.scala | 19 | ||||
-rw-r--r-- | test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala | 24 |
14 files changed, 117 insertions, 43 deletions
@@ -529,20 +529,30 @@ TODO: <!-- used during releases to bump versions in versions.properties --> <if><isset property="update.versions"/><then> <echo message="Updating `versions.properties`:"/> - <echo message=" starr.version = ${starr.version}"/> - <echo message=" scala.binary.version = ${scala.binary.version}"/> - <echo message=" partest.version.number = ${partest.version.number}"/> - <echo message=" scala-xml.version.number = ${scala-xml.version.number}"/> - <echo message=" scala-parser-combinators.version.number = ${scala-parser-combinators.version.number}"/> - <echo message=" scalacheck.version.number = ${scalacheck.version.number}"/> + <echo message="starr.version = ${starr.version}"/> + <echo message="scala.binary.version = ${scala.binary.version}"/> + <echo message="scala-xml.version.number = ${scala-xml.version.number}"/> + <echo message="scala-parser-combinators.version.number = ${scala-parser-combinators.version.number}"/> + <echo message="scala-continuations-plugin.version.number = ${scala-continuations-plugin.version.number}"/> + <echo message="scala-continuations-library.version.number = ${scala-continuations-library.version.number}"/> + <echo message="scala-swing.version.number = ${scala-swing.version.number}"/> + <echo message="akka-actor.version.number = ${akka-actor.version.number}"/> + <echo message="actors-migration.version.number = ${actors-migration.version.number}"/> + <echo message="partest.version.number = ${partest.version.number}"/> + <echo message="scalacheck.version.number = ${scalacheck.version.number}"/> <propertyfile file="versions.properties"> - <entry key="starr.version" value="${starr.version}"/> - <entry key="scala.binary.version" value="${scala.binary.version}"/> - <entry key="partest.version.number" value="${partest.version.number}"/> - <entry key="scala-xml.version.number" value="${scala-xml.version.number}"/> - <entry key="scala-parser-combinators.version.number" value="${scala-parser-combinators.version.number}"/> - <entry key="scalacheck.version.number" value="${scalacheck.version.number}"/> + <entry key="starr.version" value="${starr.version}"/> + <entry key="scala.binary.version" value="${scala.binary.version}"/> + <entry key="scala-xml.version.number" value="${scala-xml.version.number}"/> + <entry key="scala-parser-combinators.version.number" value="${scala-parser-combinators.version.number}"/> + <entry key="scala-continuations-plugin.version.number" value="${scala-continuations-plugin.version.number}"/> + <entry key="scala-continuations-library.version.number" value="${scala-continuations-library.version.number}"/> + <entry key="scala-swing.version.number" value="${scala-swing.version.number}"/> + <entry key="akka-actor.version.number" value="${akka-actor.version.number}"/> + <entry key="actors-migration.version.number" value="${actors-migration.version.number}"/> + <entry key="partest.version.number" value="${partest.version.number}"/> + <entry key="scalacheck.version.number" value="${scalacheck.version.number}"/> </propertyfile> </then></if> diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala index a788a5edc2..af17ae9ac0 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala @@ -30,15 +30,15 @@ trait Parsers { self: Quasiquotes => def correspondingPosition(offset: Int): Position = { val posMapList = posMap.toList - def containsOffset(start: Int, end: Int) = start <= offset && offset <= end + def containsOffset(start: Int, end: Int) = start <= offset && offset < end def fallbackPosition = posMapList match { case (pos1, (start1, end1)) :: _ if start1 > offset => pos1 - case _ :+ ((pos2, (start2, end2))) if offset > end2 => pos2.withPoint(pos2.point + (end2 - start2)) + case _ :+ ((pos2, (start2, end2))) if end2 <= offset => pos2.withPoint(pos2.point + (end2 - start2)) } posMapList.sliding(2).collect { - case (pos1, (start1, end1)) :: _ if containsOffset(start1, end1) => (pos1, offset - start1) - case (pos1, (_, end1)) :: (_, (start2, _)) :: _ if containsOffset(end1, start2) => (pos1, end1) - case _ :: (pos2, (start2, end2)) :: _ if containsOffset(start2, end2) => (pos2, offset - start2) + case (pos1, (start1, end1)) :: _ if containsOffset(start1, end1) => (pos1, offset - start1) + case (pos1, (start1, end1)) :: (pos2, (start2, _)) :: _ if containsOffset(end1, start2) => (pos1, end1 - start1) + case _ :: (pos2, (start2, end2)) :: _ if containsOffset(start2, end2) => (pos2, offset - start2) }.map { case (pos, offset) => pos.withPoint(pos.point + offset) }.toList.headOption.getOrElse(fallbackPosition) diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala index 5986758c2b..b287971815 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala @@ -17,7 +17,7 @@ trait Placeholders { self: Quasiquotes => // Step 1: Transform Scala source with holes into vanilla Scala source - lazy val posMap = mutable.ListMap[Position, (Int, Int)]() + lazy val posMap = mutable.LinkedHashMap[Position, (Int, Int)]() lazy val code = { val sb = new StringBuilder() val sessionSuffix = randomUUID().toString.replace("-", "").substring(0, 8) + "$" @@ -40,9 +40,7 @@ trait Placeholders { self: Quasiquotes => val iargs = method match { case nme.apply => args - case nme.unapply => - val (dummy @ Ident(nme.SELECTOR_DUMMY)) :: Nil = args - internal.subpatterns(dummy).get + case nme.unapply => internal.subpatterns(args.head).get case _ => global.abort("unreachable") } diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala index 61fb22bc73..481897d0ec 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala @@ -202,10 +202,14 @@ trait Reifiers { self: Quasiquotes => // not to cause infinite recursion. case block @ SyntacticBlock(stats) if block.isInstanceOf[Block] => reifyBuildCall(nme.SyntacticBlock, stats) + case SyntheticUnit() => + reifyBuildCall(nme.SyntacticBlock, Nil) case Try(block, catches, finalizer) => reifyBuildCall(nme.SyntacticTry, block, catches, finalizer) case Match(selector, cases) => reifyBuildCall(nme.SyntacticMatch, selector, cases) + case CaseDef(pat, guard, body) if fillListHole.isDefinedAt(body) => + mirrorCall(nme.CaseDef, reify(pat), reify(guard), mirrorBuildCall(nme.SyntacticBlock, fillListHole(body))) // 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 diff --git a/src/reflect/scala/reflect/internal/ReificationSupport.scala b/src/reflect/scala/reflect/internal/ReificationSupport.scala index ea230a215b..7f345845fe 100644 --- a/src/reflect/scala/reflect/internal/ReificationSupport.scala +++ b/src/reflect/scala/reflect/internal/ReificationSupport.scala @@ -94,7 +94,11 @@ trait ReificationSupport { self: SymbolTable => def setSymbol[T <: Tree](tree: T, sym: Symbol): T = { tree.setSymbol(sym); tree } - def toStats(tree: Tree): List[Tree] = SyntacticBlock.unapply(tree).get + def toStats(tree: Tree): List[Tree] = tree match { + case EmptyTree => Nil + case SyntacticBlock(stats) => stats + case _ => throw new IllegalArgumentException(s"can't flatten $tree") + } def mkAnnotation(tree: Tree): Tree = tree match { case SyntacticNew(Nil, SyntacticApplied(SyntacticTypeApplied(_, _), _) :: Nil, noSelfType, Nil) => @@ -239,7 +243,7 @@ trait ReificationSupport { self: SymbolTable => def unapply(templ: Template): Option[(List[Tree], ValDef, Modifiers, List[List[ValDef]], List[Tree], List[Tree])] = { val Template(parents, selfType, _) = templ val tbody = treeInfo.untypecheckedTemplBody(templ) - + def result(ctorMods: Modifiers, vparamss: List[List[ValDef]], edefs: List[Tree], body: List[Tree]) = Some((parents, selfType, ctorMods, vparamss, edefs, body)) def indexOfCtor(trees: List[Tree]) = @@ -448,28 +452,25 @@ trait ReificationSupport { self: SymbolTable => * block as a list of elements rather than (stats, expr) pair * it also: * - * 1. Treats of q"" (empty tree) as zero-element block. - * - * 2. Strips trailing synthetic units which are inserted by the + * 1. Strips trailing synthetic units which are inserted by the * compiler if the block ends with a definition rather - * than an expression. + * than an expression or is empty. * - * 3. Matches non-block term trees and recognizes them as + * 2. Matches non-block term trees and recognizes them as * single-element blocks for sake of consistency with * compiler's default to treat single-element blocks with - * expressions as just expressions. + * expressions as just expressions. The only exception is q"" + * which is not considered to be a block. */ object SyntacticBlock extends SyntacticBlockExtractor { - def apply(stats: List[Tree]): Tree = - if (stats.isEmpty) EmptyTree - else gen.mkBlock(stats) + def apply(stats: List[Tree]): Tree = gen.mkBlock(stats) def unapply(tree: Tree): Option[List[Tree]] = tree match { case bl @ self.Block(stats, SyntheticUnit()) => Some(treeInfo.untypecheckedBlockBody(bl)) case bl @ self.Block(stats, expr) => Some(treeInfo.untypecheckedBlockBody(bl) :+ expr) - case EmptyTree => Some(Nil) - case _ if tree.isTerm => Some(tree :: Nil) - case _ => None + case SyntheticUnit() => Some(Nil) + case _ if tree.isTerm && tree.nonEmpty => Some(tree :: Nil) + case _ => None } } diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index f3467ff9f4..339923a061 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -580,6 +580,7 @@ trait StdNames { val AnyVal: NameType = "AnyVal" val Apply: NameType = "Apply" val ArrayAnnotArg: NameType = "ArrayAnnotArg" + val CaseDef: NameType = "CaseDef" val ClassInfoType: NameType = "ClassInfoType" val ConstantType: NameType = "ConstantType" val EmptyPackage: NameType = "EmptyPackage" diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala index 6011289baf..9066c73393 100644 --- a/src/reflect/scala/reflect/internal/TreeGen.scala +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -452,7 +452,7 @@ abstract class TreeGen { /** Create block of statements `stats` */ def mkBlock(stats: List[Tree]): Tree = - if (stats.isEmpty) Literal(Constant(())) + if (stats.isEmpty) mkSyntheticUnit() else if (!stats.last.isTerm) Block(stats, mkSyntheticUnit()) else if (stats.length == 1) stats.head else Block(stats.init, stats.last) diff --git a/test/files/neg/quasiquotes-syntax-error-position.check b/test/files/neg/quasiquotes-syntax-error-position.check index fd55bd25b5..9fd6ce0417 100644 --- a/test/files/neg/quasiquotes-syntax-error-position.check +++ b/test/files/neg/quasiquotes-syntax-error-position.check @@ -32,4 +32,16 @@ quasiquotes-syntax-error-position.scala:14: error: ')' expected but end of quote quasiquotes-syntax-error-position.scala:15: error: ':' expected but ')' found. q"def foo(x)" ^ -11 errors found +quasiquotes-syntax-error-position.scala:16: error: illegal start of simple expression + q"$a(])" + ^ +quasiquotes-syntax-error-position.scala:17: error: in XML literal: '>' expected instead of '$' + q"foo bar <xml$a>" + ^ +quasiquotes-syntax-error-position.scala:19: error: ';' expected but '<:' found. + q"val $x: $x <: $x" + ^ +quasiquotes-syntax-error-position.scala:20: error: '=' expected but '.' found. + q"def f ( $x ) . $x" + ^ +15 errors found diff --git a/test/files/neg/quasiquotes-syntax-error-position.scala b/test/files/neg/quasiquotes-syntax-error-position.scala index 7b1d66ba00..823fe9a551 100644 --- a/test/files/neg/quasiquotes-syntax-error-position.scala +++ b/test/files/neg/quasiquotes-syntax-error-position.scala @@ -13,4 +13,9 @@ object test extends App { cq"pattern => body ; case pattern2 =>" pq"$a(bar" q"def foo(x)" + q"$a(])" + q"foo bar <xml$a>" + val x = q"x" + q"val $x: $x <: $x" + q"def f ( $x ) . $x" } diff --git a/test/files/pos/t8352.check b/test/files/pos/t8352.check new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/files/pos/t8352.check diff --git a/test/files/pos/t8352/Macros_1.scala b/test/files/pos/t8352/Macros_1.scala new file mode 100644 index 0000000000..f5c8ce578f --- /dev/null +++ b/test/files/pos/t8352/Macros_1.scala @@ -0,0 +1,7 @@ +import scala.reflect.macros.whitebox._ +import scala.language.experimental.macros + +object Macros { + def impl(c: Context)(x: c.Expr[Boolean]): c.Expr[Boolean] = x + def foo(x: Boolean): Boolean = macro impl +}
\ No newline at end of file diff --git a/test/files/pos/t8352/Test_2.scala b/test/files/pos/t8352/Test_2.scala new file mode 100644 index 0000000000..b5bfe92ffb --- /dev/null +++ b/test/files/pos/t8352/Test_2.scala @@ -0,0 +1,5 @@ +object Test extends App { + def expectUnit() { + Macros.foo(true) + } +}
\ No newline at end of file diff --git a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala index 10ce1604b1..fd4d2e9c4b 100644 --- a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala @@ -103,7 +103,7 @@ object TermConstructionProps extends QuasiquoteProperties("term construction") { def blockInvariant(quote: Tree, trees: List[Tree]) = quote ≈ (trees match { - case Nil => q"" + case Nil => q"{}" case _ :+ last if !last.isTerm => Block(trees, q"()") case head :: Nil => head case init :+ last => Block(init, last) @@ -277,11 +277,18 @@ object TermConstructionProps extends QuasiquoteProperties("term construction") { assert(stats ≈ List(q"def x = 2", q"()")) } - property("empty-tree as block") = test { - val q"{ ..$stats1 }" = q" " - assert(stats1.isEmpty) - val stats2 = List.empty[Tree] - assert(q"{ ..$stats2 }" ≈ q"") + property("empty-tree is not a block") = test { + assertThrows[MatchError] { + val q"{ ..$stats1 }" = q" " + } + } + + property("empty block is synthetic unit") = test { + val q"()" = q"{}" + val q"{..$stats}" = q"{}" + assert(stats.isEmpty) + assertEqAst(q"{..$stats}", "{}") + assertEqAst(q"{..$stats}", "()") } property("consistent variable order") = test { diff --git a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala index 7c9b5ead20..e96d1186f7 100644 --- a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala @@ -175,4 +175,28 @@ object TermDeconstructionProps extends QuasiquoteProperties("term deconstruction assert(x ≈ q"x") val q"{ _ * _ }" = q"{ _ * _ }" } + + property("si-8275 a") = test { + val cq"_ => ..$stats" = cq"_ => foo; bar" + assert(stats ≈ List(q"foo", q"bar")) + } + + property("si-8275 b") = test { + val cq"_ => ..$init; $last" = cq"_ => a; b; c" + assert(init ≈ List(q"a", q"b")) + assert(last ≈ q"c") + } + + property("si-8275 c") = test { + val cq"_ => ..$stats" = cq"_ =>" + assert(stats.isEmpty) + assertEqAst(q"{ case _ => ..$stats }", "{ case _ => }") + } + + property("can't flatten type into block") = test { + assertThrows[IllegalArgumentException] { + val tpt = tq"List[Int]" + q"..$tpt; ()" + } + } } |