diff options
16 files changed, 149 insertions, 58 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 3542fe5945..9e631febee 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -3096,10 +3096,6 @@ self => stats ++= importClause() acceptStatSepOpt() } - else if (isExprIntro) { - stats += statement(InBlock) - if (!isCaseDefEnd) acceptStatSep() - } else if (isDefIntro || isLocalModifier || isAnnotation) { if (in.token == IMPLICIT) { val start = in.skipToken() @@ -3110,6 +3106,10 @@ self => } acceptStatSepOpt() } + else if (isExprIntro) { + stats += statement(InBlock) + if (!isCaseDefEnd) acceptStatSep() + } else if (isStatSep) { in.nextToken() } diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala index 3b93a8933d..b68022afd9 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala @@ -22,7 +22,8 @@ trait Parsers { self: Quasiquotes => def parse(code: String): Tree = { try { val file = new BatchSourceFile(nme.QUASIQUOTE_FILE, code) - new QuasiquoteParser(file).parseRule(entryPoint) + val parser = new QuasiquoteParser(file) + parser.checkNoEscapingPlaceholders { parser.parseRule(entryPoint) } } catch { case mi: MalformedInput => c.abort(correspondingPosition(mi.offset), mi.msg) } @@ -30,15 +31,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) @@ -118,6 +119,8 @@ trait Parsers { self: Quasiquotes => override def isTemplateIntro: Boolean = super.isTemplateIntro || (isHole && lookingAhead { isTemplateIntro }) + override def isDefIntro: Boolean = super.isDefIntro || (isHole && lookingAhead { isDefIntro }) + override def isDclIntro: Boolean = super.isDclIntro || (isHole && lookingAhead { isDclIntro }) override def isStatSep(token: Int) = token == EOF || super.isStatSep(token) 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..5eae3b6e6f 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala @@ -151,21 +151,20 @@ trait Reifiers { self: Quasiquotes => mirrorCall(nme.This, tree) case SyntacticTraitDef(mods, name, tparams, earlyDefs, parents, selfdef, body) => reifyBuildCall(nme.SyntacticTraitDef, mods, name, tparams, earlyDefs, parents, selfdef, body) - case SyntacticClassDef(mods, name, tparams, constrmods, vparamss, earlyDefs, parents, selfdef, body) => - reifyBuildCall(nme.SyntacticClassDef, mods, name, tparams, constrmods, vparamss, - earlyDefs, parents, selfdef, body) + case SyntacticClassDef(mods, name, tparams, constrmods, vparamss, + earlyDefs, parents, selfdef, body) => + mirrorBuildCall(nme.SyntacticClassDef, reify(mods), reify(name), reify(tparams), reify(constrmods), + reifyVparamss(vparamss), reify(earlyDefs), reify(parents), + reify(selfdef), reify(body)) case SyntacticPackageObjectDef(name, earlyDefs, parents, selfdef, body) => reifyBuildCall(nme.SyntacticPackageObjectDef, name, earlyDefs, parents, selfdef, body) case SyntacticObjectDef(mods, name, earlyDefs, parents, selfdef, body) => 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, 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 SyntacticDefDef(mods, name, tparams, vparamss, tpt, rhs) => + mirrorBuildCall(nme.SyntacticDefDef, reify(mods), reify(name), reify(tparams), + reifyVparamss(vparamss), reify(tpt), reify(rhs)) case SyntacticValDef(mods, name, tpt, rhs) if tree != noSelfType => reifyBuildCall(nme.SyntacticValDef, mods, name, tpt, rhs) case SyntacticVarDef(mods, name, tpt, rhs) => @@ -202,10 +201,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 @@ -266,6 +269,12 @@ trait Reifiers { self: Quasiquotes => def reifyPackageStat(hole: Hole) = reifyConstructionCheck(nme.mkPackageStat, hole) + def reifyVparamss(vparamss: List[List[ValDef]]) = { + val build.ImplicitParams(paramss, implparams) = vparamss + if (implparams.isEmpty) reify(paramss) + else reifyBuildCall(nme.ImplicitParams, paramss, implparams) + } + /** Splits list into a list of groups where subsequent elements are considered * similar by the corresponding function. * diff --git a/src/reflect/scala/reflect/api/Internals.scala b/src/reflect/scala/reflect/api/Internals.scala index 01700345d1..01f928ed61 100644 --- a/src/reflect/scala/reflect/api/Internals.scala +++ b/src/reflect/scala/reflect/api/Internals.scala @@ -581,7 +581,7 @@ trait Internals { self: Universe => val ImplicitParams: ImplicitParamsExtractor trait ImplicitParamsExtractor { - def apply(paramss: List[List[ValDef]], implparams: List[ValDef]): List[List[ValDef]] + def apply(paramss: List[List[Tree]], implparams: List[Tree]): List[List[Tree]] def unapply(vparamss: List[List[ValDef]]): Some[(List[List[ValDef]], List[ValDef])] } diff --git a/src/reflect/scala/reflect/internal/ReificationSupport.scala b/src/reflect/scala/reflect/internal/ReificationSupport.scala index ea230a215b..66ac4bc751 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) => @@ -106,14 +110,14 @@ trait ReificationSupport { self: SymbolTable => def mkAnnotation(trees: List[Tree]): List[Tree] = trees.map(mkAnnotation) - def mkParam(argss: List[List[Tree]], extraFlags: FlagSet = NoFlags): List[List[ValDef]] = - argss.map { args => args.map { mkParam(_, extraFlags) } } + def mkParam(argss: List[List[Tree]], extraFlags: FlagSet = NoFlags, excludeFlags: FlagSet = DEFERRED): List[List[ValDef]] = + argss.map { args => args.map { mkParam(_, extraFlags, excludeFlags) } } - def mkParam(tree: Tree, extraFlags: FlagSet): ValDef = tree match { + def mkParam(tree: Tree, extraFlags: FlagSet, excludeFlags: FlagSet): ValDef = tree match { case Typed(Ident(name: TermName), tpt) => - mkParam(ValDef(NoMods, name, tpt, EmptyTree), extraFlags) + mkParam(ValDef(NoMods, name, tpt, EmptyTree), extraFlags, excludeFlags) case vd: ValDef => - var newmods = vd.mods & (~DEFERRED) + var newmods = vd.mods & (~excludeFlags) if (vd.rhs.nonEmpty) newmods |= DEFAULTPARAM copyValDef(vd)(mods = newmods | extraFlags) case _ => @@ -123,7 +127,7 @@ trait ReificationSupport { self: SymbolTable => def mkImplicitParam(args: List[Tree]): List[ValDef] = args.map(mkImplicitParam) - def mkImplicitParam(tree: Tree): ValDef = mkParam(tree, IMPLICIT | PARAM) + def mkImplicitParam(tree: Tree): ValDef = mkParam(tree, IMPLICIT | PARAM, NoFlags) def mkTparams(tparams: List[Tree]): List[TypeDef] = tparams.map { @@ -183,7 +187,7 @@ trait ReificationSupport { 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]] = + def apply(paramss: List[List[Tree]], implparams: List[Tree]): List[List[Tree]] = if (implparams.nonEmpty) paramss :+ mkImplicitParam(implparams) else paramss def unapply(vparamss: List[List[ValDef]]): Some[(List[List[ValDef]], List[ValDef])] = vparamss match { @@ -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]) = @@ -296,7 +300,7 @@ trait ReificationSupport { self: SymbolTable => constrMods: Modifiers, vparamss: List[List[Tree]], earlyDefs: List[Tree], parents: List[Tree], selfType: Tree, body: List[Tree]): ClassDef = { val extraFlags = PARAMACCESSOR | (if (mods.isCase) CASEACCESSOR else 0L) - val vparamss0 = mkParam(vparamss, extraFlags) + val vparamss0 = mkParam(vparamss, extraFlags, excludeFlags = DEFERRED | PARAM) val tparams0 = mkTparams(tparams) val parents0 = gen.mkParents(mods, if (mods.isCase) parents.filter { @@ -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/src/scaladoc/scala/tools/nsc/doc/DocFactory.scala b/src/scaladoc/scala/tools/nsc/doc/DocFactory.scala index 4607684c0d..dce52af56a 100644 --- a/src/scaladoc/scala/tools/nsc/doc/DocFactory.scala +++ b/src/scaladoc/scala/tools/nsc/doc/DocFactory.scala @@ -94,7 +94,7 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor val documentError: PartialFunction[Throwable, Unit] = { case NoCompilerRunException => - reporter.info(null, "No documentation generated with unsucessful compiler run", force = false) + reporter.info(null, "No documentation generated with unsuccessful compiler run", force = false) case _: ClassNotFoundException => () } 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/scalacheck/quasiquotes/DefinitionConstructionProps.scala b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala index fdb0d83277..69aef12668 100644 --- a/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/DefinitionConstructionProps.scala @@ -9,7 +9,7 @@ object DefinitionConstructionProps with ValDefConstruction with PatDefConstruction with DefConstruction - with PackageConstruction + with PackageConstruction with ImportConstruction { val x: Tree = q"val x: Int" @@ -81,6 +81,15 @@ trait ClassConstruction { self: QuasiquoteProperties => assertEqAst(q" class C($privx)", " class C(x: Int) ") assertEqAst(q"case class C($privx)", "case class C(private[this] val x: Int)") } + + property("SI-8333") = test { + assertEqAst(q"{ $NoMods class C }", "{ class C }") + } + + property("SI-8332") = test { + val args = q"val a: Int; val b: Int" + assertEqAst(q"class C(implicit ..$args)", "class C(implicit val a: Int, val b: Int)") + } } trait TraitConstruction { self: QuasiquoteProperties => diff --git a/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala index 996ac65b36..af7f2164a0 100644 --- a/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/DefinitionDeconstructionProps.scala @@ -73,8 +73,11 @@ trait ClassDeconstruction { self: QuasiquoteProperties => property("exhaustive class matcher") = test { def matches(line: String) { - val q"""$classMods class $name[..$targs] $ctorMods(...$argss) - extends { ..$early } with ..$parents { $self => ..$body }""" = parse(line) + val tree = parse(line) + val q"""$classMods0 class $name0[..$targs0] $ctorMods0(...$argss0) + extends { ..$early0 } with ..$parents0 { $self0 => ..$body0 }""" = tree + val q"""$classMods1 class $name1[..$targs1] $ctorMods1(...$argss1)(implicit ..$impl) + extends { ..$early1 } with ..$parents1 { $self1 => ..$body1 }""" = tree } matches("class Foo") matches("class Foo[T]") @@ -106,6 +109,13 @@ trait ClassDeconstruction { self: QuasiquoteProperties => Ident(TypeName("Int")), EmptyTree))), TypeTree(), Block(List(pendingSuperCall), Literal(Constant(()))))))) } } + + property("SI-8332") = test { + val q"class C(implicit ..$args)" = q"class C(implicit i: I, j: J)" + val q"$imods val i: I" :: q"$jmods val j: J" :: Nil = args + assert(imods.hasFlag(IMPLICIT)) + assert(jmods.hasFlag(IMPLICIT)) + } } trait ModsDeconstruction { self: QuasiquoteProperties => diff --git a/test/files/scalacheck/quasiquotes/ErrorProps.scala b/test/files/scalacheck/quasiquotes/ErrorProps.scala index d61119d98f..3d9b27de77 100644 --- a/test/files/scalacheck/quasiquotes/ErrorProps.scala +++ b/test/files/scalacheck/quasiquotes/ErrorProps.scala @@ -160,6 +160,18 @@ object ErrorProps extends QuasiquoteProperties("errors") { q"$n" """) + property("SI-8211: check unbound placeholder paremeters") = fails( + "unbound placeholder parameter", + """ + q"_" + """) + + property("SI-8211: check unbound wildcard types") = fails( + "unbound wildcard type", + """ + tq"_" + """) + // // Make sure a nice error is reported in this case // { import Flag._; val mods = NoMods; q"lazy $mods val x: Int" } } 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; ()" + } + } } |