Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit c2c76c8

Browse files
authored
Merge pull request #760 from lrytz/imm-child
Change Node.child and Node.attribute return type to immutable.Seq on 2.13+
2 parents 3c4a4af + d04f3c3 commit c2c76c8

27 files changed

+194
-95
lines changed

‎build.sbt‎

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import sbtcrossproject.CrossPlugin.autoImport.{crossProject, CrossType}
1+
import com.typesafe.tools.mima.core._
2+
import sbtcrossproject.CrossPlugin.autoImport.{CrossType, crossProject}
23

34
publish / skip := true // root project
45

@@ -68,6 +69,51 @@ lazy val xml = crossProject(JSPlatform, JVMPlatform, NativePlatform)
6869
//import com.typesafe.tools.mima.core.{}
6970
//import com.typesafe.tools.mima.core.ProblemFilters
7071
Seq( // exclusions for all Scala versions
72+
// new method in `Node` with return type `immutable.Seq`
73+
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.xml.Node.child"),
74+
75+
// these used to be declared methods, but are now bridges without generic signature
76+
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Node.nonEmptyChildren"),
77+
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Group.child"),
78+
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.SpecialNode.child"),
79+
80+
// new methods with return type immutable.Seq
81+
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.xml.Attribute.apply"),
82+
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.xml.Attribute.value"),
83+
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.xml.MetaData.apply"),
84+
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.xml.MetaData.value"),
85+
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.xml.NodeSeq.theSeq"),
86+
87+
// Synthetic static accessors (for Java interop) have a changed return type
88+
ProblemFilters.exclude[IncompatibleResultTypeProblem]("scala.xml.Null.apply"),
89+
ProblemFilters.exclude[IncompatibleResultTypeProblem]("scala.xml.Null.value"),
90+
ProblemFilters.exclude[IncompatibleResultTypeProblem]("scala.xml.Utility.parseAttributeValue"),
91+
ProblemFilters.exclude[IncompatibleResultTypeProblem]("scala.xml.Utility.trimProper"),
92+
93+
// used to be a declared method, now a bridge without generic signature
94+
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.MetaData.apply"),
95+
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Null.apply"),
96+
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Null.value"),
97+
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.PrefixedAttribute.apply"),
98+
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.PrefixedAttribute.value"),
99+
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.UnprefixedAttribute.apply"),
100+
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.UnprefixedAttribute.value"),
101+
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Document.theSeq"),
102+
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Group.theSeq"),
103+
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Node.theSeq"),
104+
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.TextBuffer.toText"),
105+
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Utility.trimProper"),
106+
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Utility.parseAttributeValue"),
107+
108+
// Option[c.Seq] => Option[i.Seq] results in a changed generic signature
109+
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.MetaData.get"),
110+
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Null.get"),
111+
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Node.attribute"),
112+
113+
// trait Attribute now extends trait ScalaVersionSpecificMetaData to ensure the previous signatures
114+
// with return type `collection.Seq` remain valid.
115+
// (trait Attribute extends MetaData, but that parent is not present in bytecode because it's a class.)
116+
ProblemFilters.exclude[InheritedNewAbstractMethodProblem]("scala.xml.Attribute.apply"),
71117
) ++ (CrossVersion.partialVersion(scalaVersion.value) match {
72118
case Some((3, _)) => Seq( // Scala 3-specific exclusions
73119
)

‎jvm/src/test/scala/scala/xml/SerializationTest.scala‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class SerializationTest {
2525
def implicitConversion(): Unit = {
2626
val parent: Elem = <parent><child></child><child/></parent>
2727
val children: Seq[Node] = parent.child
28-
val asNodeSeq: NodeSeq = children
28+
val asNodeSeq: NodeSeq = children// implicit seqToNodeSeq
2929
assertEquals(asNodeSeq, JavaByteSerialization.roundTrip(asNodeSeq))
3030
}
3131
}

‎shared/src/main/scala-2.12/scala/xml/ScalaVersionSpecific.scala‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ private[xml] object ScalaVersionSpecific {
2222
override def apply(from: Coll): mutable.Builder[Node, NodeSeq] = NodeSeq.newBuilder
2323
override def apply(): mutable.Builder[Node, NodeSeq] = NodeSeq.newBuilder
2424
}
25-
type SeqNodeUnapplySeq = scala.collection.Seq[Node]
25+
type SeqOfNode = scala.collection.Seq[Node]
26+
type SeqOfText = scala.collection.Seq[Text]
2627
}
2728

2829
private[xml] trait ScalaVersionSpecificNodeSeq extends SeqLike[Node, NodeSeq] { self: NodeSeq =>
@@ -33,3 +34,11 @@ private[xml] trait ScalaVersionSpecificNodeSeq extends SeqLike[Node, NodeSeq] {
3334
private[xml] trait ScalaVersionSpecificNodeBuffer { self: NodeBuffer =>
3435
override def stringPrefix: String = "NodeBuffer"
3536
}
37+
38+
private[xml] trait ScalaVersionSpecificNode { self: Node => }
39+
40+
private[xml] trait ScalaVersionSpecificMetaData { self: MetaData => }
41+
42+
private[xml] trait ScalaVersionSpecificTextBuffer { self: TextBuffer => }
43+
44+
private[xml] trait ScalaVersionSpecificUtility { self: Utility.type => }

‎shared/src/main/scala-2.13+/scala/xml/ScalaVersionSpecific.scala‎

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ private[xml] object ScalaVersionSpecific {
2424
def newBuilder(from: Coll): Builder[Node, NodeSeq] = NodeSeq.newBuilder
2525
def fromSpecific(from: Coll)(it: IterableOnce[Node]): NodeSeq = (NodeSeq.newBuilder ++= from).result()
2626
}
27-
type SeqNodeUnapplySeq = scala.collection.immutable.Seq[Node]
27+
type SeqOfNode = scala.collection.immutable.Seq[Node]
28+
type SeqOfText = scala.collection.immutable.Seq[Text]
2829
}
2930

3031
private[xml] trait ScalaVersionSpecificNodeSeq
@@ -48,8 +49,34 @@ private[xml] trait ScalaVersionSpecificNodeSeq
4849
fromSpecific(new View.Map(this, f))
4950
def flatMap(f: Node => IterableOnce[Node]): NodeSeq =
5051
fromSpecific(new View.FlatMap(this, f))
52+
53+
def theSeq: scala.collection.Seq[Node]
5154
}
5255

5356
private[xml] trait ScalaVersionSpecificNodeBuffer { self: NodeBuffer =>
5457
override def className: String = "NodeBuffer"
5558
}
59+
60+
private[xml] trait ScalaVersionSpecificNode { self: Node =>
61+
// These methods are overridden in Node with return type `immutable.Seq`. The declarations here result
62+
// in a bridge method in `Node` with result type `collection.Seq` which is needed for binary compatibility.
63+
def child: scala.collection.Seq[Node]
64+
def nonEmptyChildren: scala.collection.Seq[Node]
65+
}
66+
67+
private[xml] trait ScalaVersionSpecificMetaData { self: MetaData =>
68+
def apply(key: String): scala.collection.Seq[Node]
69+
def apply(namespace_uri: String, owner: Node, key: String): scala.collection.Seq[Node]
70+
def apply(namespace_uri: String, scp: NamespaceBinding, k: String): scala.collection.Seq[Node]
71+
72+
def value: scala.collection.Seq[Node]
73+
}
74+
75+
private[xml] trait ScalaVersionSpecificTextBuffer { self: TextBuffer =>
76+
def toText: scala.collection.Seq[Text]
77+
}
78+
79+
private[xml] trait ScalaVersionSpecificUtility { self: Utility.type =>
80+
def trimProper(x: Node): scala.collection.Seq[Node]
81+
def parseAttributeValue(value: String): scala.collection.Seq[Node]
82+
}

‎shared/src/main/scala-2/scala/xml/ScalaVersionSpecificReturnTypes.scala‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ private[xml] object ScalaVersionSpecificReturnTypes { // should be
2828
type NullNext = scala.Null
2929
type NullKey = scala.Null
3030
type NullValue = scala.Null
31-
type NullApply1 = scala.collection.Seq[Node] // scala.Null
3231
type NullApply3 = scala.Null
3332
type NullRemove = Null.type
3433
type SpecialNodeChild = Nil.type

‎shared/src/main/scala-3/scala/xml/ScalaVersionSpecificReturnTypes.scala‎

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,17 @@ package scala.xml
1919
What should have been specified explicitly is given in the comments;
2020
next time we break binary compatibility the types should be changed in the code and this class removed.
2121
*/
22-
private[xml] object ScalaVersionSpecificReturnTypes { // should be
23-
type ExternalIDAttribute = MetaData // Null.type
24-
type NoExternalIDId = String // scala.Null
25-
type NodeNoAttributes = MetaData // Null.type
26-
type NullFilter = MetaData // Null.type
27-
type NullGetNamespace = String // scala.Null
28-
type NullNext = MetaData // scala.Null
29-
type NullKey = String // scala.Null
30-
type NullValue = scala.collection.Seq[Node] // scala.Null
31-
type NullApply1 = scala.collection.Seq[Node] // scala.Null
32-
type NullApply3 = scala.collection.Seq[Node] // scala.Null
33-
type NullRemove = MetaData // Null.type
34-
type SpecialNodeChild = scala.collection.Seq[Node] // Nil.type
35-
type GroupChild = scala.collection.Seq[Node] // Nothing
22+
private[xml] object ScalaVersionSpecificReturnTypes { // should be
23+
type ExternalIDAttribute = MetaData // Null.type
24+
type NoExternalIDId = String // scala.Null
25+
type NodeNoAttributes = MetaData // Null.type
26+
type NullFilter = MetaData // Null.type
27+
type NullGetNamespace = String // scala.Null
28+
type NullNext = MetaData // scala.Null
29+
type NullKey = String // scala.Null
30+
type NullValue = scala.collection.immutable.Seq[Node] // scala.Null
31+
type NullApply3 = scala.collection.immutable.Seq[Node] // scala.Null
32+
type NullRemove = MetaData // Null.type
33+
type SpecialNodeChild = scala.collection.immutable.Seq[Node] // Nil.type
34+
type GroupChild = scala.collection.immutable.Seq[Node] // Nothing
3635
}

‎shared/src/main/scala/scala/xml/Attribute.scala‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,14 @@ object Attribute {
5353
*
5454
* @author Burak Emir
5555
*/
56-
trait Attribute extends MetaData {
56+
trait Attribute extends MetaData withScalaVersionSpecificMetaData{
5757
def pre: String // will be null if unprefixed
5858
override val key: String
59-
override val value: Seq[Node]
59+
override val value: ScalaVersionSpecific.SeqOfNode
6060
override val next: MetaData
6161

62-
override def apply(key: String): Seq[Node]
63-
override def apply(namespace: String, scope: NamespaceBinding, key: String): Seq[Node]
62+
override def apply(key: String): ScalaVersionSpecific.SeqOfNode
63+
override def apply(namespace: String, scope: NamespaceBinding, key: String): ScalaVersionSpecific.SeqOfNode
6464
override def copy(next: MetaData): Attribute
6565

6666
override def remove(key: String): MetaData =

‎shared/src/main/scala/scala/xml/Document.scala‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class Document extends NodeSeq with Serializable {
3636
* excluded. If there is a document type declaration, the list also
3737
* contains a document type declaration information item.
3838
*/
39-
var children: Seq[Node] = _
39+
var children: Seq[Node] = _// effectively an `immutable.Seq`, not changed due to binary compatibility
4040

4141
/** The element information item corresponding to the document element. */
4242
var docElem: Node = _
@@ -96,7 +96,7 @@ class Document extends NodeSeq with Serializable {
9696

9797
// methods for NodeSeq
9898

99-
override def theSeq: Seq[Node] = this.docElem
99+
override def theSeq: ScalaVersionSpecific.SeqOfNode = this.docElem
100100

101101
override def canEqual(other: Any): Boolean = other match {
102102
case _: Document => true

‎shared/src/main/scala/scala/xml/Elem.scala‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ object Elem {
2727
def apply(prefix: String, label: String, attributes: MetaData, scope: NamespaceBinding, minimizeEmpty: Boolean, child: Node*): Elem =
2828
new Elem(prefix, label, attributes, scope, minimizeEmpty, child: _*)
2929

30-
def unapplySeq(n: Node): Option[(String, String, MetaData, NamespaceBinding, ScalaVersionSpecific.SeqNodeUnapplySeq)] =
30+
def unapplySeq(n: Node): Option[(String, String, MetaData, NamespaceBinding, ScalaVersionSpecific.SeqOfNode)] =
3131
n match {
3232
case _: SpecialNode | _: Group => None
33-
case _ => Some((n.prefix, n.label, n.attributes, n.scope, n.child.toSeq))
33+
case _ => Some((n.prefix, n.label, n.attributes, n.scope, n.child))
3434
}
3535
}
3636

@@ -104,7 +104,7 @@ class Elem(
104104
scope: NamespaceBinding = this.scope,
105105
minimizeEmpty: Boolean = this.minimizeEmpty,
106106
child: Seq[Node] = this.child
107-
): Elem = Elem(prefix, label, attributes, scope, minimizeEmpty, child: _*)
107+
): Elem = Elem(prefix, label, attributes, scope, minimizeEmpty, child.toSeq: _*)
108108

109109
/**
110110
* Returns concatenation of `text(n)` for each child `n`.

‎shared/src/main/scala/scala/xml/Group.scala‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ import scala.collection.Seq
2222
*/
2323
// Note: used by the Scala compiler.
2424
final case class Group(nodes: Seq[Node]) extends Node {
25-
override def theSeq: Seq[Node] = nodes
25+
// Ideally, the `immutable.Seq` would be stored as a field.
26+
// But evolving the case class and remaining binary compatible is very difficult
27+
// Since `Group` is used rarely, call `toSeq` on the field.
28+
// In practice, it should not matter - the `nodes` field anyway contains an `immutable.Seq`.
29+
override def theSeq: ScalaVersionSpecific.SeqOfNode = nodes.toSeq
2630

2731
override def canEqual(other: Any): Boolean = other match {
2832
case _: Group => true

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /