Skip to content

Fix #4291: Copy sticky attachments in TreeCopier #4292

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ object Trees {
@sharable var ntrees = 0

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

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

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

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

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

def Ident(tree: Tree)(name: Name): Ident = tree match {
case tree: BackquotedIdent =>
Expand Down
15 changes: 14 additions & 1 deletion compiler/src/dotty/tools/dotc/util/Attachment.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ package dotty.tools.dotc.util

/** A class inheriting from Attachment.Container supports
* adding, removing and lookup of attachments. Attachments are typed key/value pairs.
*
* Attachments whose key is an instance of `StickyKey` will be kept when the attachments
* are copied using `withAttachmentsFrom`.
*/
object Attachment {
import Property.Key
import Property.{Key, StickyKey}

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

/** Copy the sticky attachments from `container` to this container. */
final def withAttachmentsFrom(container: Container): this.type = {
var current: Link[_] = container.next
while (current != null) {
if (current.key.isInstanceOf[StickyKey[_]]) pushAttachment(current.key, current.value)
current = current.next
}
this
}

final def pushAttachment[V](key: Key[V], value: V): Unit = {
assert(!getAttachment(key).isDefined, s"duplicate attachment for key $key")
next = new Link(key, value, next)
Expand Down
10 changes: 9 additions & 1 deletion compiler/src/dotty/tools/dotc/util/Property.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,12 @@ object Property {

/** The class of keys for properties of type V */
class Key[+V]
}

/**
* The class of keys for sticky properties of type V
*
* Sticky properties are properties that should be copied over when their container
* is copied.
*/
class StickyKey[+V] extends Key[V]
}
63 changes: 63 additions & 0 deletions compiler/test/dotty/tools/dotc/ast/AttachmentsTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package dotty.tools.dotc.ast

import dotty.tools.DottyTest
import dotty.tools.dotc.ast.Trees._
import dotty.tools.dotc.util.Property
import dotty.tools.dotc.transform.PostTyper

import org.junit.Test
import org.junit.Assert.{assertEquals, assertTrue, fail}

class AttachmentsTests extends DottyTest {

private val TestKey = new Property.Key[String]
private val StickyTestKey = new Property.StickyKey[String]
private val StickyTestKey2 = new Property.StickyKey[String]

@Test
def attachmentsAreNotCopiedOver: Unit = {
checkCompile("frontend", "class A") {
case (PackageDef(_, (clazz: tpd.TypeDef) :: Nil), context) =>
assertTrue("Attachment shouldn't be present", clazz.getAttachment(TestKey).isEmpty)

val msg = "hello"
clazz.putAttachment(TestKey, msg)
assertEquals(Some(msg), clazz.getAttachment(TestKey))

val copy = tpd.cpy.TypeDef(clazz)(rhs = tpd.EmptyTree)
assertTrue("A copy should have been returned", clazz ne copy)
assertTrue("Attachment shouldn't be present", copy.getAttachment(TestKey).isEmpty)

case _ =>
fail
}
}

@Test
def stickyAttachmentsAreCopiedOver: Unit = {
checkCompile("frontend", "class A") {
case (PackageDef(_, (clazz: tpd.TypeDef) :: Nil), context) =>
assertTrue("Attachment shouldn't be present", clazz.getAttachment(StickyTestKey).isEmpty)
assertTrue("Attachment shouldn't be present", clazz.getAttachment(StickyTestKey2).isEmpty)
assertTrue("Attachment shouldn't be present", clazz.getAttachment(TestKey).isEmpty)

val msg = "hello"
clazz.putAttachment(StickyTestKey, msg)
clazz.putAttachment(TestKey, msg)
clazz.putAttachment(StickyTestKey2, msg)
assertEquals(Some(msg), clazz.getAttachment(StickyTestKey))
assertEquals(Some(msg), clazz.getAttachment(TestKey))
assertEquals(Some(msg), clazz.getAttachment(StickyTestKey))

val copy = tpd.cpy.TypeDef(clazz)(rhs = tpd.EmptyTree)
assertTrue("A copy should have been returned", clazz ne copy)
assertTrue("Attachment should be present", copy.getAttachment(StickyTestKey).isDefined)
assertTrue("Attachment shouldn't be present", copy.getAttachment(TestKey).isEmpty)
assertTrue("Attachment should be present", copy.getAttachment(StickyTestKey2).isDefined)

case _ =>
fail
}
}

}