Skip to content

Commit 826caab

Browse files
committed
Move ThicketTransformer to s.t.n.transform
It seems generally useful enough to move it out of the async utilities. I've also added a unit test.
1 parent 8ee8be7 commit 826caab

File tree

3 files changed

+137
-1
lines changed

3 files changed

+137
-1
lines changed

src/compiler/scala/tools/nsc/transform/TypingTransformers.scala

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
package scala.tools.nsc
1414
package transform
1515

16+
import scala.collection.mutable
17+
18+
1619
/** A base class for transforms.
1720
* A transform contains a compiler phase which applies a tree transformer.
1821
*/
@@ -56,5 +59,56 @@ trait TypingTransformers {
5659
}
5760
def transformAtOwner(owner: Symbol, tree: Tree): Tree = atOwner(tree, owner) { transform(tree) }
5861
}
62+
63+
private object ThicketAttachment
64+
/** A base class for typing transformers that need to perform "thicket expansion". A thicket is the output of a
65+
* transformation that is flattened into the enclosing block.
66+
*/
67+
abstract class ThicketTransformer(initLocalTyper: analyzer.Typer) extends TypingTransformer(initLocalTyper) {
68+
private def expandThicket(t: Tree): List[Tree] = t match {
69+
case Block(stats, expr) if t.attachments.containsElement(ThicketAttachment) =>
70+
stats :+ expr
71+
case _ => t :: Nil
72+
}
73+
74+
def apply(tree: Tree): List[Tree] = expandThicket(transform(tree))
75+
76+
protected def Thicket(stats: List[Tree], expr: Tree): Tree = {
77+
Block(stats, expr).updateAttachment(ThicketAttachment)
78+
}
79+
protected def Thicket(block: Block): Tree = {
80+
block.updateAttachment(ThicketAttachment)
81+
}
82+
83+
override def transform(tree: Tree): Tree = tree match {
84+
case Block(stats, expr) =>
85+
val transformedStats = transformTrees(stats)
86+
val transformedExpr = transform(expr)
87+
if ((stats eq transformedStats) && (expr eq transformedExpr)) tree
88+
else {
89+
val expanded = new mutable.ListBuffer[Tree]
90+
def expandStats(): Unit = transformedStats.foreach {
91+
case EmptyTree =>
92+
case blk @ Block(stats, expr) if blk.attachments.containsElement(ThicketAttachment) =>
93+
stats.foreach { s => if (s != EmptyTree) expanded += s }
94+
if (expr != EmptyTree) expanded += expr
95+
case t =>
96+
expanded += t
97+
}
98+
def expandExpr(): Tree = transformedExpr match {
99+
case blk @ Block(stats, expr) if blk.attachments.containsElement(ThicketAttachment) =>
100+
stats.foreach { s => if (s != EmptyTree) expanded += s }
101+
expr
102+
case t =>
103+
t
104+
}
105+
expandStats()
106+
val expr1 = expandExpr()
107+
treeCopy.Block(tree, expanded.toList, expr1)
108+
}
109+
case _ =>
110+
super.transform(tree)
111+
}
112+
}
59113
}
60114

src/compiler/scala/tools/nsc/transform/async/TransformUtils.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ private[async] trait TransformUtils extends AsyncTransformStates {
8989
}
9090

9191
private object ThicketAttachment
92-
class ThicketTransformer(initLocalTyper: analyzer.Typer) extends TypingTransformer(initLocalTyper) {
92+
abstract class ThicketTransformer(initLocalTyper: analyzer.Typer) extends TypingTransformer(initLocalTyper) {
9393
private def expandThicket(t: Tree): List[Tree] = t match {
9494
case Block(stats, expr) if t.attachments.containsElement(ThicketAttachment) =>
9595
stats :+ expr
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package scala.tools.nsc.transform
2+
3+
import org.junit.Assert.assertSame
4+
import org.junit.{Assert, Test}
5+
6+
import scala.tools.nsc.reporters.StoreReporter
7+
8+
class ThicketTransformerTest {
9+
@Test def thicketExpansion(): Unit = {
10+
val g = new scala.tools.nsc.Global(new StoreReporter)
11+
g.settings.usejavacp.value = true
12+
new g.Run
13+
import g._
14+
object testTransformers extends TypingTransformers {
15+
val global: g.type = g
16+
val dummyUnit = newCompilationUnit("")
17+
18+
object testTransformer extends ThicketTransformer(newRootLocalTyper(dummyUnit)) {
19+
override def transform(tree: Tree): Tree = tree match {
20+
case Literal(Constant(s: String)) if (s.toLowerCase() != s) => Literal(Constant(s.toLowerCase()))
21+
case Literal(Constant(s: String)) => s.toList.filterNot(_ == '-') match {
22+
case Nil => Thicket(Nil, EmptyTree)
23+
case a :: Nil => tree
24+
case as =>
25+
Thicket(Block(as.map(c => Literal(Constant(c.toString)).setType(definitions.StringTpe)): _*))
26+
}
27+
case t => super.transform(t)
28+
}
29+
}
30+
}
31+
def t(t: Tree): Tree = testTransformers.testTransformer.transform(t)
32+
def assertTreeEquals(expected: Tree, actual: Tree) = Assert.assertEquals(expected.toString, actual.toString)
33+
34+
def s(s: String) = Literal(Constant(s))
35+
def i(i: Int) = Literal(Constant(i))
36+
37+
locally {
38+
val noTransform = Block(s("a") :: Nil, s("b"))
39+
assertSame(noTransform, t(noTransform))
40+
}
41+
42+
locally {
43+
val noTransform = Block(s("a") :: s("b") :: Nil, s("c"))
44+
assertSame(noTransform, t(noTransform))
45+
}
46+
47+
locally {
48+
val transformStats = Block(s("ab") :: i(1) :: s("cd") :: Nil, s("e"))
49+
assertTreeEquals(Block(s("a") :: s("b") :: i(1) :: s("c") :: s("d") :: Nil, s("e")), t(transformStats))
50+
}
51+
52+
locally {
53+
val transformStats = Block(s("ab") :: s("cd") :: Nil, s("e"))
54+
assertTreeEquals(Block(s("a") :: s("b") :: s("c") :: s("d") :: Nil, s("e")), t(transformStats))
55+
}
56+
57+
locally {
58+
val transformExpr = Block(s("a") :: s("b") :: Nil, s("cd"))
59+
assertTreeEquals(Block(s("a") :: s("b") :: s("c") :: Nil, s("d")), t(transformExpr))
60+
}
61+
62+
locally {
63+
val transformStatsExpr = Block(s("ab") :: s("cd") :: Nil, s("ef"))
64+
assertTreeEquals(Block(s("a") :: s("b") :: s("c") :: s("d") :: s("e") :: Nil, s("f")), t(transformStatsExpr))
65+
}
66+
67+
locally {
68+
val transformStatsExpr = Block(s("A") :: s("B") :: Nil, s("cd"))
69+
assertTreeEquals(Block(s("a") :: s("b") :: s("c") :: Nil, s("d")), t(transformStatsExpr))
70+
}
71+
72+
locally {
73+
val transformStatsExpr = Block(s("A") :: s("B") :: Nil, s("C"))
74+
assertTreeEquals(Block(s("a") :: s("b") :: Nil, s("c")), t(transformStatsExpr))
75+
}
76+
77+
locally {
78+
val transformStatsExpr = Block(s("-") :: Nil, s("a"))
79+
assertTreeEquals(Block(Nil, s("a")), t(transformStatsExpr))
80+
}
81+
}
82+
}

0 commit comments

Comments
 (0)