diff --git a/build.sbt b/build.sbt index 809f062..fcbd44f 100644 --- a/build.sbt +++ b/build.sbt @@ -20,8 +20,23 @@ lazy val collectionContrib = crossProject(JVMPlatform, JSPlatform, NativePlatfor scalaModuleAutomaticModuleName := Some("scala.collection.contrib"), Compile / compile / scalacOptions ++= { CrossVersion.partialVersion(scalaVersion.value) match { - case Some((2, _)) => Seq("-opt-warnings", "-Werror", "-Wconf:origin=scala.collection.IterableOps.toIterable:s") - case _ => Seq("-Xfatal-warnings", "-Wconf:cat=deprecation:s") + case Some((2, _)) => Seq( + "-Wconf:origin=scala.collection.IterableOps.toIterable:s", // internal usage; annotating @nowarn is clutter + "-Werror", + "-Wnonunit-statement", + "-Wopt", + "-opt:inline:", + "-opt:inline:scala.util.package$chaining$,scala.util.ChainingSyntax,scala.util.ChainingOps$", + "-Wvalue-discard", + "-Xlint", + "-Xsource:3-cross", + ) + case _ => Seq( + "-Wconf:cat=deprecation:s", // Scala 3 lacks origin, src arrives in 3.3.4 & 3.5 + "-Wconf:id=E175:s", + "-Werror", + "-Wnonunit-statement", // warns on uni-limbed if + ) } }, Compile / doc / scalacOptions ++= { diff --git a/src/main/scala/scala/collection/MultiDict.scala b/src/main/scala/scala/collection/MultiDict.scala index 29cf304..7d254e5 100644 --- a/src/main/scala/scala/collection/MultiDict.scala +++ b/src/main/scala/scala/collection/MultiDict.scala @@ -13,7 +13,7 @@ trait MultiDict[K, V] with MultiDictOps[K, V, MultiDict, MultiDict[K, V]] with Equals { - override protected[this] def className: String = "MultiDict" + override protected def className: String = "MultiDict" def multiDictFactory: MapFactory[MultiDict] = MultiDict override protected def fromSpecific(coll: IterableOnce[(K, V)]): MultiDict[K, V] = multiDictFactory.from(coll) @@ -26,14 +26,11 @@ trait MultiDict[K, V] override def equals(o: Any): Boolean = o match { case that: MultiDict[K @unchecked, _] => (this eq that) || - (that canEqual this) && - (this.size == that.size) && { - try { - sets forall { case (k, vs) => that.sets.get(k).contains(vs) } - } catch { - case _: ClassCastException => false - } - } + that.canEqual(this) && + this.size == that.size && ( + try sets.forall { case (k, vs) => that.sets.get(k).contains(vs) } + catch { case _: ClassCastException => false } + ) case _ => false } @@ -205,7 +202,7 @@ trait MultiDictOps[K, V, +CC[X, Y] <: MultiDict[X, Y], +C <: MultiDict[K, V]] object MultiDictOps { class WithFilter[K, V, +IterableCC[_], +CC[X, Y] <: MultiDict[X, Y]]( - `this`: MultiDictOps[K, V, CC, _] with IterableOps[(K, V), IterableCC, _], + `this`: MultiDictOps[K, V, CC, ?] & IterableOps[(K, V), IterableCC, ?], p: ((K, V)) => Boolean ) extends IterableOps.WithFilter[(K, V), IterableCC](`this`, p) { diff --git a/src/main/scala/scala/collection/MultiSet.scala b/src/main/scala/scala/collection/MultiSet.scala index 0465c8b..fc50026 100644 --- a/src/main/scala/scala/collection/MultiSet.scala +++ b/src/main/scala/scala/collection/MultiSet.scala @@ -12,7 +12,7 @@ trait MultiSet[A] with MultiSetOps[A, MultiSet, MultiSet[A]] with Equals { - override protected[this] def className: String = "MultiSet" + override protected def className: String = "MultiSet" override def iterableFactory: IterableFactory[MultiSet] = MultiSet override protected def fromSpecific(coll: IterableOnce[A]): MultiSet[A] = iterableFactory.from(coll) @@ -24,14 +24,11 @@ trait MultiSet[A] override def equals(o: Any): Boolean = o match { case that: MultiSet[A @unchecked] => (this eq that) || - (that canEqual this) && - (this.size == that.size) && { - try { - occurrences forall { case (elem, n) => that.get(elem) == n } - } catch { - case _: ClassCastException => false - } - } + that.canEqual(this) && + this.size == that.size && ( + try occurrences.forall { case (elem, n) => that.get(elem) == n } + catch { case _: ClassCastException => false } + ) case _ => false } @@ -42,10 +39,10 @@ trait MultiSet[A] trait MultiSetOps[A, +CC[X] <: MultiSet[X], +C <: MultiSet[A]] extends IterableOps[A, CC, C] { - protected[this] def fromSpecificOccurrences(it: Iterable[(A, Int)]): C = + protected def fromSpecificOccurrences(it: Iterable[(A, Int)]): C = fromSpecific(it.view.flatMap { case (e, n) => new View.Fill(n)(e) }) - protected[this] def fromOccurrences[E](it: Iterable[(E, Int)]): CC[E] = + protected def fromOccurrences[E](it: Iterable[(E, Int)]): CC[E] = // Note new MultiSet(it.to(Map)) would be more efficient but would also loose duplicates iterableFactory.from(it.view.flatMap { case (e, n) => new View.Fill(n)(e) }) diff --git a/src/main/scala/scala/collection/SortedMultiDict.scala b/src/main/scala/scala/collection/SortedMultiDict.scala index 9351cc6..d6afee1 100644 --- a/src/main/scala/scala/collection/SortedMultiDict.scala +++ b/src/main/scala/scala/collection/SortedMultiDict.scala @@ -25,8 +25,8 @@ trait SortedMultiDictOps[K, V, +CC[X, Y] <: MultiDict[X, Y], +C <: MultiDict[K, def sortedMultiDictFactory: SortedMapFactory[CC] - protected[this] def sortedFromIterable[L : Ordering, W](it: Iterable[(L, W)]): CC[L, W] = sortedMultiDictFactory.from(it) - protected[this] def sortedFromSets[L : Ordering, W](it: Iterable[(L, Set[W])]): CC[L, W] = + protected def sortedFromIterable[L : Ordering, W](it: Iterable[(L, W)]): CC[L, W] = sortedMultiDictFactory.from(it) + protected def sortedFromSets[L : Ordering, W](it: Iterable[(L, Set[W])]): CC[L, W] = sortedFromIterable(it.view.flatMap { case (l, ws) => ws.map(w => (l, w)) }) /** `this` sorted multidict upcasted to an unsorted multidict */ @@ -126,7 +126,7 @@ trait SortedMultiDictOps[K, V, +CC[X, Y] <: MultiDict[X, Y], +C <: MultiDict[K, object SortedMultiDictOps { class WithFilter[K, V, +IterableCC[_], +MultiDictCC[X, Y] <: MultiDict[X, Y], +CC[X, Y] <: MultiDict[X, Y]]( - `this`: SortedMultiDictOps[K, V, CC, _] with MultiDictOps[K, V, MultiDictCC, _] with IterableOps[(K, V), IterableCC, _], + `this`: SortedMultiDictOps[K, V, CC, ?] & MultiDictOps[K, V, MultiDictCC, ?] & IterableOps[(K, V), IterableCC, ?], p: ((K, V)) => Boolean ) extends MultiDictOps.WithFilter[K, V, IterableCC, MultiDictCC](`this`, p) { diff --git a/src/main/scala/scala/collection/SortedMultiSet.scala b/src/main/scala/scala/collection/SortedMultiSet.scala index 0e532f7..3fd2613 100644 --- a/src/main/scala/scala/collection/SortedMultiSet.scala +++ b/src/main/scala/scala/collection/SortedMultiSet.scala @@ -118,7 +118,7 @@ trait SortedMultiSetOps[A, +CC[X] <: MultiSet[X], +C <: MultiSet[A]] * is the minimum of the lengths of `this` and `that` */ def zip[B](that: Iterable[B])(implicit ev: Ordering[B]): CC[(A @uncheckedVariance, B)] = // sound bcs of VarianceNote - sortedFromIterable(new View.Zip(toIterable, that))(Ordering.Tuple2(ordering, implicitly)) + sortedFromIterable(new View.Zip(toIterable, that))(using Ordering.Tuple2(ordering, implicitly)) /** * @return a new collection resulting from applying the given partial @@ -147,7 +147,7 @@ trait SortedMultiSetOps[A, +CC[X] <: MultiSet[X], +C <: MultiSet[A]] // --- Override return type of methods that returned an unsorted MultiSet override def zipWithIndex: CC[(A, Int)] = - sortedFromIterable(new View.ZipWithIndex(toIterable))(Ordering.Tuple2(ordering, implicitly)) + sortedFromIterable(new View.ZipWithIndex(toIterable))(using Ordering.Tuple2(ordering, implicitly)) } @@ -158,7 +158,7 @@ object SortedMultiSetOps { * @define coll sorted collection */ class WithFilter[A, +IterableCC[_], +CC[X] <: MultiSet[X]]( - `this`: SortedMultiSetOps[A, CC, _] with IterableOps[A, IterableCC, _], + `this`: SortedMultiSetOps[A, CC, ?] & IterableOps[A, IterableCC, ?], p: A => Boolean ) extends IterableOps.WithFilter[A, IterableCC](`this`, p) { diff --git a/src/main/scala/scala/collection/decorators/BitSetDecorator.scala b/src/main/scala/scala/collection/decorators/BitSetDecorator.scala index 0b5e294..663fcc5 100644 --- a/src/main/scala/scala/collection/decorators/BitSetDecorator.scala +++ b/src/main/scala/scala/collection/decorators/BitSetDecorator.scala @@ -2,7 +2,7 @@ package scala.collection.decorators import scala.collection.{BitSet, BitSetOps} -class BitSetDecorator[+C <: BitSet with BitSetOps[C]](protected val bs: C) { +class BitSetDecorator[+C <: BitSet & BitSetOps[C]](protected val bs: C) { import BitSetDecorator._ import BitSetOps._ diff --git a/src/main/scala/scala/collection/decorators/IteratorDecorator.scala b/src/main/scala/scala/collection/decorators/IteratorDecorator.scala index 79aa4bf..c4367ba 100644 --- a/src/main/scala/scala/collection/decorators/IteratorDecorator.scala +++ b/src/main/scala/scala/collection/decorators/IteratorDecorator.scala @@ -2,6 +2,7 @@ package scala.collection package decorators import scala.annotation.tailrec +import scala.util.chaining.* import scala.util.control.NonFatal /** Enriches Iterator with additional methods. @@ -188,8 +189,8 @@ class IteratorDecorator[A](val `this`: Iterator[A]) extends AnyVal { */ def splitBy[K](f: A => K): Iterator[immutable.Seq[A]] = new AbstractIterator[immutable.Seq[A]] { - private var hd: A = _ - private var hdKey: K = _ + private var hd: A = null.asInstanceOf[A] // todo uninitialized + private var hdKey: K = null.asInstanceOf[K] // todo uninitialized private var hdDefined: Boolean = false override def hasNext: Boolean = hdDefined || `this`.hasNext @@ -240,32 +241,26 @@ class IteratorDecorator[A](val `this`: Iterator[A]) extends AnyVal { } /** Gives elements from the source iterator until the source iterator ends or throws a NonFatal exception. - * - * @param exceptionCaught a callback invoked from `hasNext` when the source iterator throws a NonFatal exception - * @return an iterator that takes items until the wrapped iterator ends or throws a NonFatal exception - * @see scala.util.control.NonFatal - * @note Reuse: $consumesAndProducesIterator - */ - def takeUntilException(exceptionCaught: Throwable => Unit): Iterator[A] = { + * + * @param exceptionCaught a callback invoked from `hasNext` when the source iterator throws a NonFatal exception + * @return an iterator that takes items until the wrapped iterator ends or throws a NonFatal exception + * @see scala.util.control.NonFatal + * @note Reuse: $consumesAndProducesIterator + */ + def takeUntilException(exceptionCaught: Throwable => Unit): Iterator[A] = new AbstractIterator[A] { private val wrapped = `this`.buffered - override def hasNext: Boolean = { - try { - val n = wrapped.hasNext - // By already invoking `head` (and therefore also `next` on `this`), - // we are sure that `wrapped.next` will not throw when it is used from - // `next`. - if (n) wrapped.head - n - } catch { + override def hasNext: Boolean = + // By already invoking `head` (and therefore also `next` on `this`), + // we are sure that `wrapped.next` will not throw when it is used from `next`. + try wrapped.hasNext.tap(if(_) wrapped.head) + catch { case NonFatal(t) => exceptionCaught(t) false } - } override def next(): A = wrapped.next() } - } } diff --git a/src/main/scala/scala/collection/decorators/package.scala b/src/main/scala/scala/collection/decorators/package.scala index e13ccb9..3175d88 100644 --- a/src/main/scala/scala/collection/decorators/package.scala +++ b/src/main/scala/scala/collection/decorators/package.scala @@ -17,7 +17,7 @@ package object decorators { implicit def mapDecorator[C](coll: C)(implicit map: IsMap[C]): MapDecorator[C, map.type] = new MapDecorator(coll)(map) - implicit def bitSetDecorator[C <: BitSet with BitSetOps[C]](bs: C): BitSetDecorator[C] = + implicit def bitSetDecorator[C <: BitSet & BitSetOps[C]](bs: C): BitSetDecorator[C] = new BitSetDecorator(bs) implicit def mutableBitSetDecorator(bs: mutable.BitSet): MutableBitSetDecorator = diff --git a/src/main/scala/scala/collection/decorators/views.scala b/src/main/scala/scala/collection/decorators/views.scala index 050b4a1..1666372 100644 --- a/src/main/scala/scala/collection/decorators/views.scala +++ b/src/main/scala/scala/collection/decorators/views.scala @@ -4,7 +4,7 @@ package decorators /** Views used by decorators */ object View { - type SomeIterableOps[+A] = IterableOps[A, AnyConstr, _] + type SomeIterableOps[+A] = IterableOps[A, AnyConstr, ?] class Intersperse[A](underlying: SomeIterableOps[A], sep: A) extends View[A] { def iterator: Iterator[A] = underlying.iterator.intersperse(sep) @@ -21,4 +21,4 @@ object View { else underlying.knownSize } -} \ No newline at end of file +} diff --git a/src/main/scala/scala/collection/mutable/MultiDict.scala b/src/main/scala/scala/collection/mutable/MultiDict.scala index c364538..c7190b0 100644 --- a/src/main/scala/scala/collection/mutable/MultiDict.scala +++ b/src/main/scala/scala/collection/mutable/MultiDict.scala @@ -2,11 +2,10 @@ package scala package collection package mutable -/** - * A mutable multidict - * @tparam K the type of keys - * @tparam V the type of values - */ +/** A mutable multidict. + * @tparam K the type of keys + * @tparam V the type of values + */ class MultiDict[K, V] private (elems: Map[K, Set[V]]) extends collection.MultiDict[K, V] with Iterable[(K, V)] @@ -27,20 +26,18 @@ class MultiDict[K, V] private (elems: Map[K, Set[V]]) def addOne(elem: (K, V)): this.type = { val (k, v) = elem - elems.updateWith(k) { - case None => Some(Set(v)) - case Some(vs) => Some(vs += v) - } + val vs = elems.getOrElseUpdate(k, Set.empty[V]) + vs.addOne(v) this } def subtractOne(elem: (K, V)): this.type = { val (k, v) = elem - elems.updateWith(k) { + elems.get(k) match { case Some(vs) => - vs -= v - if (vs.nonEmpty) Some(vs) else None - case None => None + vs.subtractOne(v) + if (vs.isEmpty) elems.subtractOne(k) + case _ => } this } @@ -50,7 +47,7 @@ class MultiDict[K, V] private (elems: Map[K, Set[V]]) * @return the collection itself */ def removeKey(key: K): this.type = { - elems -= key + elems.subtractOne(key) this } @@ -69,4 +66,4 @@ object MultiDict extends MapFactory[MultiDict] { def newBuilder[K, V]: Builder[(K, V), MultiDict[K, V]] = new GrowableBuilder[(K, V), MultiDict[K, V]](empty) -} \ No newline at end of file +} diff --git a/src/main/scala/scala/collection/mutable/MultiSet.scala b/src/main/scala/scala/collection/mutable/MultiSet.scala index a387e90..1c344da 100644 --- a/src/main/scala/scala/collection/mutable/MultiSet.scala +++ b/src/main/scala/scala/collection/mutable/MultiSet.scala @@ -2,6 +2,8 @@ package scala package collection package mutable +import java.util.concurrent.atomic.AtomicInteger + /** * A mutable multiset. */ @@ -19,22 +21,19 @@ trait MultiSet[A] override def knownSize: Int = super[Growable].knownSize } -class MultiSetImpl[A] private[mutable] (val elems: Map[A, Int]) extends MultiSet[A] { +class MultiSetImpl[A] private[mutable] (elems: Map[A, AtomicInteger]) extends MultiSet[A] { - def occurrences: collection.Map[A, Int] = elems + def occurrences: collection.Map[A, Int] = elems.map { case (k, v) => (k, v.get) } def addOne(elem: A): this.type = { - elems.updateWith(elem) { - case None => Some(1) - case Some(n) => Some(n + 1) - } + elems.getOrElseUpdate(elem, new AtomicInteger).getAndIncrement this } def subtractOne(elem: A): this.type = { - elems.updateWith(elem) { - case Some(n) => if (n > 1) Some(n - 1) else None - case None => None + elems.get(elem) match { + case Some(n) => if (n.decrementAndGet <= 0) elems.subtractOne(elem) + case _ => } this } @@ -50,4 +49,4 @@ object MultiSet extends IterableFactory[MultiSet] { def newBuilder[A]: Builder[A, MultiSet[A]] = new GrowableBuilder[A, MultiSet[A]](empty) -} \ No newline at end of file +} diff --git a/src/main/scala/scala/collection/mutable/SortedMultiDict.scala b/src/main/scala/scala/collection/mutable/SortedMultiDict.scala index 1d4dcef..e5ff3ae 100644 --- a/src/main/scala/scala/collection/mutable/SortedMultiDict.scala +++ b/src/main/scala/scala/collection/mutable/SortedMultiDict.scala @@ -31,20 +31,18 @@ class SortedMultiDict[K, V] private (elems: SortedMap[K, Set[V]])(implicit val o def addOne(elem: (K, V)): this.type = { val (k, v) = elem - elems.updateWith(k) { - case None => Some(Set(v)) - case Some(vs) => Some(vs += v) - } + val vs = elems.getOrElseUpdate(k, Set.empty[V]) + vs.addOne(v) this } def subtractOne(elem: (K, V)): this.type = { val (k, v) = elem - elems.updateWith(k) { + elems.get(k) match { case Some(vs) => - vs -= v - if (vs.nonEmpty) Some(vs) else None - case None => None + vs.subtractOne(v) + if (vs.isEmpty) elems.subtractOne(k) + case _ => } this } @@ -54,7 +52,7 @@ class SortedMultiDict[K, V] private (elems: SortedMap[K, Set[V]])(implicit val o * @return the collection itself */ def removeKey(key: K): this.type = { - elems -= key + elems.subtractOne(key) this } @@ -76,4 +74,4 @@ object SortedMultiDict extends SortedMapFactory[SortedMultiDict] { def newBuilder[K: Ordering, V]: Builder[(K, V), SortedMultiDict[K, V]] = new GrowableBuilder[(K, V), SortedMultiDict[K, V]](empty) -} \ No newline at end of file +} diff --git a/src/main/scala/scala/collection/mutable/SortedMultiSet.scala b/src/main/scala/scala/collection/mutable/SortedMultiSet.scala index 1bc1667..525902b 100644 --- a/src/main/scala/scala/collection/mutable/SortedMultiSet.scala +++ b/src/main/scala/scala/collection/mutable/SortedMultiSet.scala @@ -2,12 +2,14 @@ package scala package collection package mutable +import java.util.concurrent.atomic.AtomicInteger + /** * A mutable multiset whose elements are sorted according to a given ordering. * * @tparam A Type of elements */ -class SortedMultiSet[A] private (elems: SortedMap[A, Int])(implicit val ordering: Ordering[A]) +class SortedMultiSet[A] private (elems: SortedMap[A, AtomicInteger])(implicit val ordering: Ordering[A]) extends MultiSet[A] with collection.SortedMultiSet[A] with collection.SortedMultiSetOps[A, SortedMultiSet, SortedMultiSet[A]] @@ -15,7 +17,7 @@ class SortedMultiSet[A] private (elems: SortedMap[A, Int])(implicit val ordering with Growable[A] with Shrinkable[A] { - def occurrences: collection.SortedMap[A, Int] = elems + def occurrences: collection.SortedMap[A, Int] = elems.map { case (k, v) => (k, v.get) } override def sortedIterableFactory: SortedIterableFactory[SortedMultiSet] = SortedMultiSet override protected def fromSpecific(coll: IterableOnce[A]): SortedMultiSet[A] = sortedIterableFactory.from(coll) @@ -28,17 +30,14 @@ class SortedMultiSet[A] private (elems: SortedMap[A, Int])(implicit val ordering new SortedMultiSet(elems.rangeImpl(from, until)) def addOne(elem: A): this.type = { - elems.updateWith(elem) { - case None => Some(1) - case Some(n) => Some(n + 1) - } + elems.getOrElseUpdate(elem, new AtomicInteger).getAndIncrement this } def subtractOne(elem: A): this.type = { - elems.updateWith(elem) { - case Some(n) => if (n > 1) Some(n - 1) else None - case None => None + elems.get(elem) match { + case Some(n) => if (n.decrementAndGet <= 0) elems.subtractOne(elem) + case _ => } this } @@ -50,8 +49,8 @@ object SortedMultiSet extends SortedIterableFactory[SortedMultiSet] { def from[E: Ordering](it: IterableOnce[E]): SortedMultiSet[E] = (newBuilder[E] ++= it).result() - def empty[A: Ordering]: SortedMultiSet[A] = new SortedMultiSet[A](SortedMap.empty[A, Int]) + def empty[A: Ordering]: SortedMultiSet[A] = new SortedMultiSet[A](SortedMap.empty[A, AtomicInteger]) def newBuilder[A: Ordering]: Builder[A, SortedMultiSet[A]] = new GrowableBuilder[A, SortedMultiSet[A]](empty) -} \ No newline at end of file +}