Skip to content

Commit 4062073

Browse files
committed
Fix #4291: Copy sticky attachments in TreeCopier
Sticky attachments are attachments whose key is a subclass of `StickyKey`. Those attachments should be copied when a tree is copied using a `TreeCopier`. Fixes #4291
1 parent 2762567 commit 4062073

File tree

4 files changed

+89
-5
lines changed

4 files changed

+89
-5
lines changed

compiler/src/dotty/tools/dotc/ast/Trees.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ object Trees {
3131
@sharable var ntrees = 0
3232

3333
/** Property key for trees with documentation strings attached */
34-
val DocComment = new Property.Key[Comment]
34+
val DocComment = new Property.StickyKey[Comment]
3535

3636
@sharable private[this] var nextId = 0 // for debugging
3737

@@ -913,10 +913,10 @@ object Trees {
913913
def postProcess(tree: Tree, copied: untpd.MemberDef): copied.ThisTree[T]
914914

915915
def finalize(tree: Tree, copied: untpd.Tree): copied.ThisTree[T] =
916-
postProcess(tree, copied withPos tree.pos)
916+
postProcess(tree, copied.withPos(tree.pos).withAttachmentsFrom(tree))
917917

918918
def finalize(tree: Tree, copied: untpd.MemberDef): copied.ThisTree[T] =
919-
postProcess(tree, copied withPos tree.pos)
919+
postProcess(tree, copied.withPos(tree.pos).withAttachmentsFrom(tree))
920920

921921
def Ident(tree: Tree)(name: Name): Ident = tree match {
922922
case tree: BackquotedIdent =>

compiler/src/dotty/tools/dotc/util/Attachment.scala

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ package dotty.tools.dotc.util
22

33
/** A class inheriting from Attachment.Container supports
44
* adding, removing and lookup of attachments. Attachments are typed key/value pairs.
5+
*
6+
* Attachments whose key is an instance of `StickyKey` will be kept when the attachments
7+
* are copied using `withAttachmentsFrom`.
58
*/
69
object Attachment {
7-
import Property.Key
10+
import Property.{Key, StickyKey}
811

912
/** An implementation trait for attachments.
1013
* Clients should inherit from Container instead.
@@ -88,6 +91,16 @@ object Attachment {
8891
trait Container extends LinkSource {
8992
private[Attachment] var next: Link[_] = null
9093

94+
/** Copy the sticky attachments from `container` to this container. */
95+
final def withAttachmentsFrom(container: Container): this.type = {
96+
var current: Link[_] = container.next
97+
while (current != null) {
98+
if (current.key.isInstanceOf[StickyKey[_]]) pushAttachment(current.key, current.value)
99+
current = current.next
100+
}
101+
this
102+
}
103+
91104
final def pushAttachment[V](key: Key[V], value: V): Unit = {
92105
assert(!getAttachment(key).isDefined, s"duplicate attachment for key $key")
93106
next = new Link(key, value, next)

compiler/src/dotty/tools/dotc/util/Property.scala

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,12 @@ object Property {
77

88
/** The class of keys for properties of type V */
99
class Key[+V]
10-
}
10+
11+
/**
12+
* The class of keys for sticky properties of type V
13+
*
14+
* Sticky properties are properties that should be copied over when their container
15+
* is copied.
16+
*/
17+
class StickyKey[+V] extends Key[V]
18+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package dotty.tools.dotc.ast
2+
3+
import dotty.tools.DottyTest
4+
import dotty.tools.dotc.ast.Trees._
5+
import dotty.tools.dotc.util.Property
6+
import dotty.tools.dotc.transform.PostTyper
7+
8+
import org.junit.Test
9+
import org.junit.Assert.{assertEquals, assertTrue, fail}
10+
11+
class AttachmentsTests extends DottyTest {
12+
13+
private val TestKey = new Property.Key[String]
14+
private val StickyTestKey = new Property.StickyKey[String]
15+
private val StickyTestKey2 = new Property.StickyKey[String]
16+
17+
@Test
18+
def attachmentsAreNotCopiedOver: Unit = {
19+
checkCompile("frontend", "class A") {
20+
case (PackageDef(_, (clazz: tpd.TypeDef) :: Nil), context) =>
21+
assertTrue("Attachment shouldn't be present", clazz.getAttachment(TestKey).isEmpty)
22+
23+
val msg = "hello"
24+
clazz.putAttachment(TestKey, msg)
25+
assertEquals(Some(msg), clazz.getAttachment(TestKey))
26+
27+
val copy = tpd.cpy.TypeDef(clazz)(rhs = tpd.EmptyTree)
28+
assertTrue("A copy should have been returned", clazz ne copy)
29+
assertTrue("Attachment shouldn't be present", copy.getAttachment(TestKey).isEmpty)
30+
31+
case _ =>
32+
fail
33+
}
34+
}
35+
36+
@Test
37+
def stickyAttachmentsAreCopiedOver: Unit = {
38+
checkCompile("frontend", "class A") {
39+
case (PackageDef(_, (clazz: tpd.TypeDef) :: Nil), context) =>
40+
assertTrue("Attachment shouldn't be present", clazz.getAttachment(StickyTestKey).isEmpty)
41+
assertTrue("Attachment shouldn't be present", clazz.getAttachment(StickyTestKey2).isEmpty)
42+
assertTrue("Attachment shouldn't be present", clazz.getAttachment(TestKey).isEmpty)
43+
44+
val msg = "hello"
45+
clazz.putAttachment(StickyTestKey, msg)
46+
clazz.putAttachment(TestKey, msg)
47+
clazz.putAttachment(StickyTestKey2, msg)
48+
assertEquals(Some(msg), clazz.getAttachment(StickyTestKey))
49+
assertEquals(Some(msg), clazz.getAttachment(TestKey))
50+
assertEquals(Some(msg), clazz.getAttachment(StickyTestKey))
51+
52+
val copy = tpd.cpy.TypeDef(clazz)(rhs = tpd.EmptyTree)
53+
assertTrue("A copy should have been returned", clazz ne copy)
54+
assertTrue("Attachment should be present", copy.getAttachment(StickyTestKey).isDefined)
55+
assertTrue("Attachment shouldn't be present", copy.getAttachment(TestKey).isEmpty)
56+
assertTrue("Attachment should be present", copy.getAttachment(StickyTestKey2).isDefined)
57+
58+
case _ =>
59+
fail
60+
}
61+
}
62+
63+
}

0 commit comments

Comments
 (0)