Skip to content

Commit fd67593

Browse files
committed
Mark relevant types as Nullable
1 parent d2b2266 commit fd67593

37 files changed

+276
-189
lines changed

build.sbt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,16 @@ lazy val xml = crossProject(JSPlatform, JVMPlatform, NativePlatform)
110110
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Null.get"),
111111
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Node.attribute"),
112112

113+
// Nullable types result in a raw Seq, is fixed in 3.8
114+
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Attribute.unapply"),
115+
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Attribute.apply"),
116+
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.Attribute.value"),
117+
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.MetaData.value"),
118+
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.PrefixedAttribute.unapply"),
119+
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.PrefixedAttribute.this"),
120+
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.UnprefixedAttribute.unapply"),
121+
ProblemFilters.exclude[IncompatibleSignatureProblem]("scala.xml.UnprefixedAttribute.this"),
122+
113123
// trait Attribute now extends trait ScalaVersionSpecificMetaData to ensure the previous signatures
114124
// with return type `collection.Seq` remain valid.
115125
// (trait Attribute extends MetaData, but that parent is not present in bytecode because it's a class.)

jvm/src/test/scala/scala/xml/XMLTest.scala

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import java.net.URL
88
import scala.xml.dtd.{DocType, PublicID}
99
import scala.xml.parsing.ConstructingParser
1010
import scala.xml.Utility.sort
11+
import xml.Nullables._
1112

1213
object XMLTestJVM {
1314
val e: MetaData = Null //Node.NoAttributes
@@ -17,7 +18,7 @@ object XMLTestJVM {
1718
class XMLTestJVM {
1819
import XMLTestJVM.{e, sc}
1920

20-
def Elem(prefix: String, label: String, attributes: MetaData, scope: NamespaceBinding, child: Node*): Elem =
21+
def Elem(prefix: Nullable[String], label: String, attributes: MetaData, scope: NamespaceBinding, child: Node*): Elem =
2122
scala.xml.Elem.apply(prefix, label, attributes, scope, minimizeEmpty = true, child: _*)
2223

2324
lazy val parsedxml1: Elem = XML.loadString("<hello><world/></hello>")
@@ -385,9 +386,9 @@ class XMLTestJVM {
385386

386387
@UnitTest
387388
def t5052(): Unit = {
388-
assertTrue(<elem attr={ null: String }/> xml_== <elem/>)
389+
assertTrue(<elem attr={ null: Nullable[String] }/> xml_== <elem/>)
389390
assertTrue(<elem attr={ None }/> xml_== <elem/>)
390-
assertTrue(<elem/> xml_== <elem attr={ null: String }/>)
391+
assertTrue(<elem/> xml_== <elem attr={ null: Nullable[String] }/>)
391392
assertTrue(<elem/> xml_== <elem attr={ None }/>)
392393
}
393394

@@ -400,9 +401,9 @@ class XMLTestJVM {
400401
assertHonorsIterableContract(<a y={ None }/>.attributes)
401402
assertHonorsIterableContract(<a y={ None } x=""/>.attributes)
402403
assertHonorsIterableContract(<a a="" y={ None }/>.attributes)
403-
assertHonorsIterableContract(<a y={ null: String }/>.attributes)
404-
assertHonorsIterableContract(<a y={ null: String } x=""/>.attributes)
405-
assertHonorsIterableContract(<a a="" y={ null: String }/>.attributes)
404+
assertHonorsIterableContract(<a y={ null: Nullable[String] }/>.attributes)
405+
assertHonorsIterableContract(<a y={ null: Nullable[String] } x=""/>.attributes)
406+
assertHonorsIterableContract(<a a="" y={ null: Nullable[String] }/>.attributes)
406407
}
407408

408409
@UnitTest
@@ -459,18 +460,18 @@ class XMLTestJVM {
459460
@UnitTest
460461
def attributes(): Unit = {
461462
val noAttr: Elem = <t/>
462-
val attrNull: Elem = <t a={ null: String }/>
463+
val attrNull: Elem = <t a={ null: Nullable[String] }/>
463464
val attrNone: Elem = <t a={ None: Option[Seq[Node]] }/>
464-
val preAttrNull: Elem = <t p:a={ null: String }/>
465+
val preAttrNull: Elem = <t p:a={ null: Nullable[String] }/>
465466
val preAttrNone: Elem = <t p:a={ None: Option[Seq[Node]] }/>
466467
assertEquals(noAttr, attrNull)
467468
assertEquals(noAttr, attrNone)
468469
assertEquals(noAttr, preAttrNull)
469470
assertEquals(noAttr, preAttrNone)
470471

471472
val xml1: Elem = <t b="1" d="2"/>
472-
val xml2: Elem = <t a={ null: String } p:a={ null: String } b="1" c={ null: String } d="2"/>
473-
val xml3: Elem = <t b="1" c={ null: String } d="2" a={ null: String } p:a={ null: String }/>
473+
val xml2: Elem = <t a={ null: Nullable[String] } p:a={ null: Nullable[String] } b="1" c={ null: Nullable[String] } d="2"/>
474+
val xml3: Elem = <t b="1" c={ null: Nullable[String] } d="2" a={ null: Nullable[String] } p:a={ null: Nullable[String] }/>
474475
assertEquals(xml1, xml2)
475476
assertEquals(xml1, xml3)
476477

@@ -759,8 +760,8 @@ class XMLTestJVM {
759760
def documentBaseURI(): Unit = {
760761
val url: URL = resourceUrl("site")
761762
// XMLLoader returns the document's baseURI:
762-
assert(XML.withSAXParser(xercesInternal.newSAXParser).loadDocument(url).baseURI.endsWith("/test-classes/scala/xml/site.xml"))
763-
assert(XML.withSAXParser(xercesExternal.newSAXParser).loadDocument(url).baseURI.endsWith("/test-classes/scala/xml/site.xml"))
763+
assert(XML.withSAXParser(xercesInternal.newSAXParser).loadDocument(url).baseURI.nn.endsWith("/test-classes/scala/xml/site.xml"))
764+
assert(XML.withSAXParser(xercesExternal.newSAXParser).loadDocument(url).baseURI.nn.endsWith("/test-classes/scala/xml/site.xml"))
764765
// ConstructingParser does not return it of course: since it uses scala.io.Source it has no idea where is the XML coming from:
765766
assertNull(ConstructingParser.fromSource(scala.io.Source.fromURI(url.toURI), preserveWS = false).document().baseURI)
766767
}

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
package scala.xml
1414

15+
16+
import xml.Nullables._
1517
import scala.collection.immutable.StrictOptimizedSeqOps
1618
import scala.collection.{View, SeqOps, IterableOnce, immutable, mutable}
1719
import scala.collection.BuildFrom
@@ -65,11 +67,11 @@ private[xml] trait ScalaVersionSpecificNode { self: Node =>
6567
}
6668

6769
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]
70+
def apply(key: String): Nullable[scala.collection.Seq[Node]]
71+
def apply(namespace_uri: String, owner: Node, key: Nullable[String]): Nullable[scala.collection.Seq[Node]]
72+
def apply(namespace_uri: Nullable[String], scp: NamespaceBinding, k: Nullable[String]): Nullable[scala.collection.Seq[Node]]
7173

72-
def value: scala.collection.Seq[Node]
74+
def value: Nullable[scala.collection.Seq[Node]]
7375
}
7476

7577
private[xml] trait ScalaVersionSpecificTextBuffer { self: TextBuffer =>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Scala (https://www.scala-lang.org)
3+
*
4+
* Copyright EPFL and Lightbend, Inc.
5+
*
6+
* Licensed under Apache License 2.0
7+
* (http://www.apache.org/licenses/LICENSE-2.0).
8+
*
9+
* See the NOTICE file distributed with this work for
10+
* additional information regarding copyright ownership.
11+
*/
12+
13+
package scala.xml
14+
object Nullables {
15+
type Nullable[T] = T
16+
implicit class NonNullOps[T](private val x: T) extends AnyVal {
17+
def nn: T = {
18+
x
19+
}
20+
}
21+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Scala (https://www.scala-lang.org)
3+
*
4+
* Copyright EPFL and Lightbend, Inc.
5+
*
6+
* Licensed under Apache License 2.0
7+
* (http://www.apache.org/licenses/LICENSE-2.0).
8+
*
9+
* See the NOTICE file distributed with this work for
10+
* additional information regarding copyright ownership.
11+
*/
12+
13+
package scala.xml
14+
object Nullables {
15+
type Nullable[T] = T | Null
16+
}

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

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
package scala
1414
package xml
1515

16+
import xml.Nullables._
1617
import scala.collection.Seq
1718

1819
/**
@@ -22,21 +23,21 @@ import scala.collection.Seq
2223
* @author Burak Emir
2324
*/
2425
object Attribute {
25-
def unapply(x: Attribute): Option[(String, Seq[Node], MetaData)] = x match {
26+
def unapply(x: Attribute): Option[(Nullable[String], Nullable[Seq[Node]], MetaData)] = x match {
2627
case PrefixedAttribute(_, key, value, next) => Some((key, value, next))
2728
case UnprefixedAttribute(key, value, next) => Some((key, value, next))
2829
case _ => None
2930
}
3031

3132
/** Convenience functions which choose Un/Prefixedness appropriately */
32-
def apply(key: String, value: Seq[Node], next: MetaData): Attribute =
33+
def apply(key: Nullable[String], value: Seq[Node], next: MetaData): Attribute =
3334
new UnprefixedAttribute(key, value, next)
3435

35-
def apply(pre: String, key: String, value: String, next: MetaData): Attribute =
36+
def apply(pre: Nullable[String], key: String, value: String, next: MetaData): Attribute =
3637
if (pre == null || pre == "") new UnprefixedAttribute(key, value, next)
3738
else new PrefixedAttribute(pre, key, value, next)
3839

39-
def apply(pre: String, key: String, value: Seq[Node], next: MetaData): Attribute =
40+
def apply(pre: Nullable[String], key: String, value: Seq[Node], next: MetaData): Attribute =
4041
if (pre == null || pre == "") new UnprefixedAttribute(key, value, next)
4142
else new PrefixedAttribute(pre, key, value, next)
4243

@@ -54,29 +55,29 @@ object Attribute {
5455
* @author Burak Emir
5556
*/
5657
trait Attribute extends MetaData with ScalaVersionSpecificMetaData {
57-
def pre: String // will be null if unprefixed
58-
override val key: String
59-
override val value: ScalaVersionSpecific.SeqOfNode
58+
def pre: Nullable[String] // will be null if unprefixed
59+
override val key: Nullable[String]
60+
override val value: Nullable[ScalaVersionSpecific.SeqOfNode]
6061
override val next: MetaData
6162

62-
override def apply(key: String): ScalaVersionSpecific.SeqOfNode
63-
override def apply(namespace: String, scope: NamespaceBinding, key: String): ScalaVersionSpecific.SeqOfNode
63+
override def apply(key: String): Nullable[ScalaVersionSpecific.SeqOfNode]
64+
override def apply(namespace: Nullable[String], scope: NamespaceBinding, key: Nullable[String]): Nullable[ScalaVersionSpecific.SeqOfNode]
6465
override def copy(next: MetaData): Attribute
6566

66-
override def remove(key: String): MetaData =
67+
override def remove(key: Nullable[String]): MetaData =
6768
if (!isPrefixed && this.key == key) next
6869
else copy(next.remove(key))
6970

70-
override def remove(namespace: String, scope: NamespaceBinding, key: String): MetaData =
71+
override def remove(namespace: Nullable[String], scope: NamespaceBinding, key: String): MetaData =
7172
if (this.key == key && scope.getURI(pre) == namespace) next
7273
else copy(next.remove(namespace, scope, key))
7374

7475
override def isPrefixed: Boolean = pre != null
7576

76-
override def getNamespace(owner: Node): String
77+
override def getNamespace(owner: Node): Nullable[String]
7778

7879
override def wellformed(scope: NamespaceBinding): Boolean = {
79-
val arg: String = if (isPrefixed) scope.getURI(pre) else null
80+
val arg: Nullable[String] = if (isPrefixed) scope.getURI(pre) else null
8081
(next(arg, scope, key) == null) && next.wellformed(scope)
8182
}
8283

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ package scala
1414
package xml
1515

1616
import scala.collection.Seq
17+
import xml.Nullables._
1718

1819
/**
1920
* A document information item (according to InfoSet spec). The comments
@@ -42,25 +43,25 @@ class Document extends NodeSeq with Serializable {
4243
var docElem: Node = _
4344

4445
/** The dtd that comes with the document, if any */
45-
var dtd: scala.xml.dtd.DTD = _
46+
var dtd: Nullable[scala.xml.dtd.DTD] = _
4647

4748
/**
4849
* An unordered set of notation information items, one for each notation
4950
* declared in the DTD. If any notation is multiply declared, this property
5051
* has no value.
5152
*/
5253
def notations: Seq[scala.xml.dtd.NotationDecl] =
53-
dtd.notations
54+
dtd.nn.notations
5455

5556
/**
5657
* An unordered set of unparsed entity information items, one for each
5758
* unparsed entity declared in the DTD.
5859
*/
5960
def unparsedEntities: Seq[scala.xml.dtd.EntityDecl] =
60-
dtd.unparsedEntities
61+
dtd.nn.unparsedEntities
6162

6263
/** The base URI of the document entity. */
63-
var baseURI: String = _
64+
var baseURI: Nullable[String] = _
6465

6566
/**
6667
* The name of the character encoding scheme in which the document entity

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ package scala
1414
package xml
1515

1616
import scala.collection.Seq
17+
import xml.Nullables._
1718

1819
/**
1920
* This singleton object contains the `apply` and `unapplySeq` methods for
@@ -24,10 +25,10 @@ import scala.collection.Seq
2425
// Note: used by the Scala compiler.
2526
object Elem {
2627

27-
def apply(prefix: String, label: String, attributes: MetaData, scope: NamespaceBinding, minimizeEmpty: Boolean, child: Node*): Elem =
28+
def apply(prefix: Nullable[String], label: Nullable[String], attributes: MetaData, scope: NamespaceBinding, minimizeEmpty: Boolean, child: Node*): Elem =
2829
new Elem(prefix, label, attributes, scope, minimizeEmpty, child: _*)
2930

30-
def unapplySeq(n: Node): Option[(String, String, MetaData, NamespaceBinding, ScalaVersionSpecific.SeqOfNode)] =
31+
def unapplySeq(n: Node): Option[(Nullable[String], Nullable[String], MetaData, NamespaceBinding, ScalaVersionSpecific.SeqOfNode)] =
3132
n match {
3233
case _: SpecialNode | _: Group => None
3334
case _ => Some((n.prefix, n.label, n.attributes, n.scope, n.child))
@@ -55,8 +56,8 @@ object Elem {
5556
*/
5657
// Note: used by the Scala compiler.
5758
class Elem(
58-
override val prefix: String,
59-
override val label: String,
59+
override val prefix: Nullable[String],
60+
override val label: Nullable[String],
6061
attributes1: MetaData,
6162
override val scope: NamespaceBinding,
6263
val minimizeEmpty: Boolean,
@@ -98,8 +99,8 @@ class Elem(
9899
* @return a new symbol with updated attributes
99100
*/
100101
def copy(
101-
prefix: String = this.prefix,
102-
label: String = this.label,
102+
prefix: Nullable[String] = this.prefix,
103+
label: Nullable[String] = this.label,
103104
attributes: MetaData = this.attributes,
104105
scope: NamespaceBinding = this.scope,
105106
minimizeEmpty: Boolean = this.minimizeEmpty,

0 commit comments

Comments
 (0)