From 9fbac09b6ec7fcb1b2df75fcbc04bc795eccd669 Mon Sep 17 00:00:00 2001 From: Denys Shabalin Date: Tue, 1 Apr 2014 22:26:55 +0200 Subject: SI-8466 fix quasiquote crash on recursively iterable unlifting In order to handle unquoting quasiquotes needs to know if type is iterable and whats the depth of the iterable nesting which is called rank. (e.g. List[List[Tree]] is rank 2 iterable of Tree) The logic that checks depth of iterable nesting didn't take a situation where T is in fact Iterable[T] which caused infinite recursion in stripIterable function. In order to fix it stripIterable now always recurs no more than non-optional limit times. --- src/compiler/scala/tools/reflect/quasiquotes/Holes.scala | 10 +++++----- test/files/scalacheck/quasiquotes/LiftableProps.scala | 7 +++++++ test/files/scalacheck/quasiquotes/UnliftableProps.scala | 8 ++++++++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala b/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala index 55a28f9f20..68cc728eb3 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala @@ -43,13 +43,13 @@ trait Holes { self: Quasiquotes => tpe <:< NothingClass.tpe || tpe <:< NullClass.tpe private def extractIterableTParam(tpe: Type) = IterableTParam.asSeenFrom(tpe, IterableClass) - private def stripIterable(tpe: Type, limit: Option[Rank] = None): (Rank, Type) = - if (limit.map { _ == NoDot }.getOrElse { false }) (NoDot, tpe) + private def stripIterable(tpe: Type, limit: Rank = DotDotDot): (Rank, Type) = + if (limit == NoDot) (NoDot, tpe) else if (tpe != null && !isIterableType(tpe)) (NoDot, tpe) else if (isBottomType(tpe)) (NoDot, tpe) else { val targ = extractIterableTParam(tpe) - val (rank, innerTpe) = stripIterable(targ, limit.map { _.pred }) + val (rank, innerTpe) = stripIterable(targ, limit.pred) (rank.succ, innerTpe) } private def iterableTypeFromRank(n: Rank, tpe: Type): Type = { @@ -76,7 +76,7 @@ trait Holes { self: Quasiquotes => class ApplyHole(annotatedRank: Rank, unquotee: Tree) extends Hole { val (strippedTpe, tpe): (Type, Type) = { - val (strippedRank, strippedTpe) = stripIterable(unquotee.tpe, limit = Some(annotatedRank)) + val (strippedRank, strippedTpe) = stripIterable(unquotee.tpe, limit = annotatedRank) if (isBottomType(strippedTpe)) cantSplice() else if (isNativeType(strippedTpe)) { if (strippedRank != NoDot && !(strippedTpe <:< treeType) && !isLiftableType(strippedTpe)) cantSplice() @@ -193,7 +193,7 @@ trait Holes { self: Quasiquotes => val (iterableRank, _) = stripIterable(tpe) if (iterableRank.value < rank.value) c.abort(pat.pos, s"Can't extract $tpe with $rank, consider using $iterableRank") - val (_, strippedTpe) = stripIterable(tpe, limit = Some(rank)) + val (_, strippedTpe) = stripIterable(tpe, limit = rank) if (strippedTpe <:< treeType) treeNoUnlift else unlifters.spawn(strippedTpe, rank).map { diff --git a/test/files/scalacheck/quasiquotes/LiftableProps.scala b/test/files/scalacheck/quasiquotes/LiftableProps.scala index 29fdea5c89..a4c57ac359 100644 --- a/test/files/scalacheck/quasiquotes/LiftableProps.scala +++ b/test/files/scalacheck/quasiquotes/LiftableProps.scala @@ -164,4 +164,11 @@ object LiftableProps extends QuasiquoteProperties("liftable") { val right3: Either[Int, Int] = Right(1) assert(q"$right3" ≈ q"scala.util.Right(1)") } + + property("lift xml comment") = test { + implicit val liftXmlComment = Liftable[xml.Comment] { comment => + q"new _root_.scala.xml.Comment(${comment.commentText})" + } + assert(q"${xml.Comment("foo")}" ≈ q"") + } } diff --git a/test/files/scalacheck/quasiquotes/UnliftableProps.scala b/test/files/scalacheck/quasiquotes/UnliftableProps.scala index 1d7629aa29..659b18edab 100644 --- a/test/files/scalacheck/quasiquotes/UnliftableProps.scala +++ b/test/files/scalacheck/quasiquotes/UnliftableProps.scala @@ -155,4 +155,12 @@ object UnliftableProps extends QuasiquoteProperties("unliftable") { assert(t21 == (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21)) assert(t22 == (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22)) } + + property("unlift xml comment") = test { + implicit val unliftXmlComment = Unliftable[xml.Comment] { + case q"new _root_.scala.xml.Comment(${value: String})" => xml.Comment(value) + } + val q"${comment: xml.Comment}" = q"" + assert(comment.commentText == "foo") + } } -- cgit v1.2.3