Skip to content

Commit

Permalink
SI-8466 fix quasiquote crash on recursively iterable unlifting
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
densh committed Apr 2, 2014
1 parent 8489be1 commit 9fbac09
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 5 deletions.
10 changes: 5 additions & 5 deletions src/compiler/scala/tools/reflect/quasiquotes/Holes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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()
Expand Down Expand Up @@ -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 {
Expand Down
7 changes: 7 additions & 0 deletions test/files/scalacheck/quasiquotes/LiftableProps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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"<!--foo-->")
}
}
8 changes: 8 additions & 0 deletions test/files/scalacheck/quasiquotes/UnliftableProps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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"<!--foo-->"
assert(comment.commentText == "foo")
}
}

0 comments on commit 9fbac09

Please sign in to comment.