diff --git a/src/library/scala/collection/LinearSeq.scala b/src/library/scala/collection/LinearSeq.scala index 7dc67096fbfd..449d58c866e3 100644 --- a/src/library/scala/collection/LinearSeq.scala +++ b/src/library/scala/collection/LinearSeq.scala @@ -34,9 +34,23 @@ object LinearSeq extends SeqFactory.Delegate[LinearSeq](immutable.LinearSeq) /** Base trait for linear Seq operations */ trait LinearSeqOps[+A, +CC[X] <: LinearSeq[X], +C <: LinearSeq[A] with LinearSeqOps[A, CC, C]] extends Any with SeqOps[A, CC, C] { - // To be overridden in implementations: - def isEmpty: Boolean + /** @inheritdoc + * + * Note: *Must* be overridden in subclasses. The default implementation that is inherited from [[SeqOps]] + * uses `lengthCompare`, which is defined here to use `isEmpty`. + */ + override def isEmpty: Boolean + + /** @inheritdoc + * + * Note: *Must* be overridden in subclasses. The default implementation is inherited from [[IterableOps]]. + */ def head: A + + /** @inheritdoc + * + * Note: *Must* be overridden in subclasses. The default implementation is inherited from [[IterableOps]]. + */ def tail: C override def headOption: Option[A] = diff --git a/test/junit/scala/collection/LinearSeqTest.scala b/test/junit/scala/collection/LinearSeqTest.scala index c98a646a47db..61acac545278 100644 --- a/test/junit/scala/collection/LinearSeqTest.scala +++ b/test/junit/scala/collection/LinearSeqTest.scala @@ -1,10 +1,9 @@ package scala.collection import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 +import org.junit.Assert.{assertEquals, assertTrue} +import scala.tools.testkit.AssertUtil.assertThrows -@RunWith(classOf[JUnit4]) class LinearSeqTest { // Tests regression on issue 11262 @Test def extensionIteratorTest(): Unit = { @@ -17,6 +16,29 @@ class LinearSeqTest { val x = new ConstantLinearSeq(4, 7) val it = x.iterator // The main thing we want to test is that this does not throw an exception - assert(it.hasNext == true) // Call it at least once so that it won't be optimized away + assertTrue(it.hasNext) // Call it at least once so that it won't be optimized away + } + + // LinearSeqOps used isEmpty from SeqOps, which used lengthCompare, but LSO.lengthCompare used isEmpty + @Test def `linear seq is incoherent`: Unit = { + val ls = new LinearSeq[Int] { + var count = 0 + override def isEmpty = { + if (count > 5) throw new IllegalStateException("limit") + count += 1 + super.isEmpty + } + } + assertThrows[IllegalStateException](ls.toString, _ == "limit") + assertThrows[IllegalStateException](ls.head, _ == "limit") + assertThrows[IllegalStateException](ls.tail, _ == "limit") + } + @Test def `linear seq is semicoherent`: Unit = { + val ls = new LinearSeq[Int] { + override def isEmpty = true + } + assertEquals("LinearSeq()", ls.toString) + assertThrows[NoSuchElementException](ls.head) + assertThrows[UnsupportedOperationException](ls.tail) } }