summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Suereth <joshua.suereth@gmail.com>2011-12-18 10:54:49 -0500
committerJosh Suereth <joshua.suereth@gmail.com>2011-12-18 10:54:49 -0500
commit56a366c854457f842522a481701be2f1f1b318c7 (patch)
tree3819bdcfe76a07be7027694299f86d7ec7aad5d4
parent971536593f33f357356b4c58d8a66742755801b8 (diff)
parenta332a39d316f0223f00a31999b76a369f9e6fee4 (diff)
downloadscala-56a366c854457f842522a481701be2f1f1b318c7.tar.gz
scala-56a366c854457f842522a481701be2f1f1b318c7.tar.bz2
scala-56a366c854457f842522a481701be2f1f1b318c7.zip
Merge branch 'master' into xsbt
-rwxr-xr-xsrc/compiler/scala/tools/nsc/ast/DocComments.scala10
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala34
-rw-r--r--src/library/scala/collection/immutable/List.scala247
-rw-r--r--src/library/scala/collection/immutable/Range.scala185
-rw-r--r--src/partest/scala/tools/partest/nest/CompileManager.scala3
-rw-r--r--test/files/scalacheck/CheckEither.scala8
-rw-r--r--test/files/scalacheck/range.scala81
-rw-r--r--test/scaladoc/resources/SI_5054_q7.scala8
-rw-r--r--test/scaladoc/resources/SI_5287.scala17
-rw-r--r--test/scaladoc/scala/html.flags1
-rw-r--r--test/scaladoc/scala/html/HtmlFactoryTest.flags1
-rw-r--r--test/scaladoc/scala/html/HtmlFactoryTest.scala183
12 files changed, 511 insertions, 267 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/DocComments.scala b/src/compiler/scala/tools/nsc/ast/DocComments.scala
index 8d52a7bd2c..f9c818daf0 100755
--- a/src/compiler/scala/tools/nsc/ast/DocComments.scala
+++ b/src/compiler/scala/tools/nsc/ast/DocComments.scala
@@ -99,7 +99,7 @@ trait DocComments { self: Global =>
*/
def useCases(sym: Symbol, site: Symbol): List[(Symbol, String, Position)] = {
def getUseCases(dc: DocComment) = {
- for (uc <- dc.useCases; defn <- uc.expandedDefs(site)) yield
+ for (uc <- dc.useCases; defn <- uc.expandedDefs(sym, site)) yield
(defn,
expandVariables(merge(cookedDocComment(sym), uc.comment.raw, defn), sym, site),
uc.pos)
@@ -346,7 +346,7 @@ trait DocComments { self: Global =>
var defined: List[Symbol] = List() // initialized by Typer
var aliases: List[Symbol] = List() // initialized by Typer
- def expandedDefs(site: Symbol): List[Symbol] = {
+ def expandedDefs(sym: Symbol, site: Symbol): List[Symbol] = {
def select(site: Type, name: Name, orElse: => Type): Type = {
val member = site.nonPrivateMember(name)
@@ -424,8 +424,10 @@ trait DocComments { self: Global =>
}
for (defn <- defined) yield {
- defn.cloneSymbol.setFlag(Flags.SYNTHETIC).setInfo(
- substAliases(defn.info).asSeenFrom(site.thisType, defn.owner))
+ val useCase = defn.cloneSymbol
+ useCase.owner = sym.owner
+ useCase.flags = sym.flags
+ useCase.setFlag(Flags.SYNTHETIC).setInfo(substAliases(defn.info).asSeenFrom(site.thisType, sym.owner))
}
}
}
diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala
index 1fe96ed447..7eb8c393f3 100644
--- a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala
@@ -100,11 +100,15 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
if (inTpl == null) None else thisFactory.comment(sym, inTpl)
override def inTemplate = inTpl
override def toRoot: List[MemberImpl] = this :: inTpl.toRoot
- def inDefinitionTemplates =
- if (inTpl == null)
- makeRootPackage.toList
- else
- makeTemplate(sym.owner) :: (sym.allOverriddenSymbols map { inhSym => makeTemplate(inhSym.owner) })
+ def inDefinitionTemplates = this match {
+ case mb: NonTemplateMemberEntity if (mb.useCaseOf.isDefined) =>
+ mb.useCaseOf.get.inDefinitionTemplates
+ case _ =>
+ if (inTpl == null)
+ makeRootPackage.toList
+ else
+ makeTemplate(sym.owner) :: (sym.allOverriddenSymbols map { inhSym => makeTemplate(inhSym.owner) })
+ }
def visibility = {
if (sym.isPrivateLocal) PrivateInInstance()
else if (sym.isProtectedLocal) ProtectedInInstance()
@@ -119,18 +123,14 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
else Public()
}
}
- def flags = this match {
- // workaround for uninitialized flags in use cases - see SI-5054
- case m: NonTemplateMemberEntity if (m.useCaseOf.isDefined) =>
- m.useCaseOf.get.flags
- case _ =>
- val fgs = mutable.ListBuffer.empty[Paragraph]
- if (sym.isImplicit) fgs += Paragraph(Text("implicit"))
- if (sym.isSealed) fgs += Paragraph(Text("sealed"))
- if (!sym.isTrait && (sym hasFlag Flags.ABSTRACT)) fgs += Paragraph(Text("abstract"))
- if (!sym.isTrait && (sym hasFlag Flags.DEFERRED)) fgs += Paragraph(Text("abstract"))
- if (!sym.isModule && (sym hasFlag Flags.FINAL)) fgs += Paragraph(Text("final"))
- fgs.toList
+ def flags = {
+ val fgs = mutable.ListBuffer.empty[Paragraph]
+ if (sym.isImplicit) fgs += Paragraph(Text("implicit"))
+ if (sym.isSealed) fgs += Paragraph(Text("sealed"))
+ if (!sym.isTrait && (sym hasFlag Flags.ABSTRACT)) fgs += Paragraph(Text("abstract"))
+ if (!sym.isTrait && (sym hasFlag Flags.DEFERRED)) fgs += Paragraph(Text("abstract"))
+ if (!sym.isModule && (sym hasFlag Flags.FINAL)) fgs += Paragraph(Text("final"))
+ fgs.toList
}
def deprecation =
if (sym.isDeprecated)
diff --git a/src/library/scala/collection/immutable/List.scala b/src/library/scala/collection/immutable/List.scala
index 531eac6c01..c6f056bd81 100644
--- a/src/library/scala/collection/immutable/List.scala
+++ b/src/library/scala/collection/immutable/List.scala
@@ -277,6 +277,9 @@ sealed abstract class List[+A] extends AbstractSeq[A]
override def toStream : Stream[A] =
if (isEmpty) Stream.Empty
else new Stream.Cons(head, tail.toStream)
+
+ @deprecated("use `distinct` instead", "2.8.0")
+ def removeDuplicates: List[A] = distinct
}
/** The empty list.
@@ -343,6 +346,8 @@ final case class ::[B](private var hd: B, private[scala] var tl: List[B]) extend
*/
object List extends SeqFactory[List] {
+ import scala.collection.{Iterable, Seq, IndexedSeq}
+
/** $genericCanBuildFromInfo */
implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, List[A]] =
ReusableCBF.asInstanceOf[GenericCanBuildFrom[A]]
@@ -352,6 +357,248 @@ object List extends SeqFactory[List] {
override def empty[A]: List[A] = Nil
override def apply[A](xs: A*): List[A] = xs.toList
+
+ /** Create a sorted list with element values `v,,>n+1,, = step(v,,n,,)`
+ * where `v,,0,, = start` and elements are in the range between `start`
+ * (inclusive) and `end` (exclusive).
+ *
+ * @param start the start value of the list
+ * @param end the end value of the list
+ * @param step the increment function of the list, which given `v,,n,,`,
+ * computes `v,,n+1,,`. Must be monotonically increasing
+ * or decreasing.
+ * @return the sorted list of all integers in range `[start;end)`.
+ */
+ @deprecated("use `iterate` instead", "2.8.0")
+ def range(start: Int, end: Int, step: Int => Int): List[Int] = {
+ val up = step(start) > start
+ val down = step(start) < start
+ val b = new ListBuffer[Int]
+ var i = start
+ while ((!up || i < end) && (!down || i > end)) {
+ b += i
+ val next = step(i)
+ if (i == next)
+ throw new IllegalArgumentException("the step function did not make any progress on "+ i)
+ i = next
+ }
+ b.toList
+ }
+
+ /** Create a list containing several copies of an element.
+ *
+ * @param n the length of the resulting list
+ * @param elem the element composing the resulting list
+ * @return a list composed of `n` elements all equal to `elem`
+ */
+ @deprecated("use `fill` instead", "2.8.0")
+ def make[A](n: Int, elem: A): List[A] = {
+ val b = new ListBuffer[A]
+ var i = 0
+ while (i < n) {
+ b += elem
+ i += 1
+ }
+ b.toList
+ }
+
+ /** Concatenate all the elements of a given list of lists.
+ *
+ * @param xss the list of lists that are to be concatenated
+ * @return the concatenation of all the lists
+ */
+ @deprecated("use `xss.flatten` instead of `List.flatten(xss)`", "2.8.0")
+ def flatten[A](xss: List[List[A]]): List[A] = {
+ val b = new ListBuffer[A]
+ for (xs <- xss) {
+ var xc = xs
+ while (!xc.isEmpty) {
+ b += xc.head
+ xc = xc.tail
+ }
+ }
+ b.toList
+ }
+
+ /** Transforms a list of pairs into a pair of lists.
+ *
+ * @param xs the list of pairs to unzip
+ * @return a pair of lists.
+ */
+ @deprecated("use `xs.unzip` instead of `List.unzip(xs)`", "2.8.0")
+ def unzip[A,B](xs: List[(A,B)]): (List[A], List[B]) = {
+ val b1 = new ListBuffer[A]
+ val b2 = new ListBuffer[B]
+ var xc = xs
+ while (!xc.isEmpty) {
+ b1 += xc.head._1
+ b2 += xc.head._2
+ xc = xc.tail
+ }
+ (b1.toList, b2.toList)
+ }
+
+ /** Transforms an iterable of pairs into a pair of lists.
+ *
+ * @param xs the iterable of pairs to unzip
+ * @return a pair of lists.
+ */
+ @deprecated("use `xs.unzip` instead of `List.unzip(xs)`", "2.8.0")
+ def unzip[A,B](xs: Iterable[(A,B)]): (List[A], List[B]) =
+ xs.foldRight[(List[A], List[B])]((Nil, Nil)) {
+ case ((x, y), (xs, ys)) => (x :: xs, y :: ys)
+ }
+
+ /**
+ * Returns the `Left` values in the given `Iterable` of `Either`s.
+ */
+ @deprecated("use `xs collect { case Left(x: A) => x }` instead of `List.lefts(xs)`", "2.8.0")
+ def lefts[A, B](es: Iterable[Either[A, B]]) =
+ es.foldRight[List[A]](Nil)((e, as) => e match {
+ case Left(a) => a :: as
+ case Right(_) => as
+ })
+
+ /**
+ * Returns the `Right` values in the given `Iterable` of `Either`s.
+ */
+ @deprecated("use `xs collect { case Right(x: B) => x }` instead of `List.rights(xs)`", "2.8.0")
+ def rights[A, B](es: Iterable[Either[A, B]]) =
+ es.foldRight[List[B]](Nil)((e, bs) => e match {
+ case Left(_) => bs
+ case Right(b) => b :: bs
+ })
+
+ /** Transforms an Iterable of Eithers into a pair of lists.
+ *
+ * @param xs the iterable of Eithers to separate
+ * @return a pair of lists.
+ */
+ @deprecated("use `(for (Left(x) <- es) yield x, for (Right(x) <- es) yield x)` instead", "2.8.0")
+ def separate[A,B](es: Iterable[Either[A, B]]): (List[A], List[B]) =
+ es.foldRight[(List[A], List[B])]((Nil, Nil)) {
+ case (Left(a), (lefts, rights)) => (a :: lefts, rights)
+ case (Right(b), (lefts, rights)) => (lefts, b :: rights)
+ }
+
+ /** Converts an iterator to a list.
+ *
+ * @param it the iterator to convert
+ * @return a list that contains the elements returned by successive
+ * calls to `it.next`
+ */
+ @deprecated("use `it.toList` instead of `List.toList(it)`", "2.8.0")
+ def fromIterator[A](it: Iterator[A]): List[A] = it.toList
+
+ /** Converts an array into a list.
+ *
+ * @param arr the array to convert
+ * @return a list that contains the same elements than `arr`
+ * in the same order
+ */
+ @deprecated("use `array.toList` instead of `List.fromArray(array)`", "2.8.0")
+ def fromArray[A](arr: Array[A]): List[A] = fromArray(arr, 0, arr.length)
+
+ /** Converts a range of an array into a list.
+ *
+ * @param arr the array to convert
+ * @param start the first index to consider
+ * @param len the length of the range to convert
+ * @return a list that contains the same elements than `arr`
+ * in the same order
+ */
+ @deprecated("use `array.view(start, end).toList` instead of `List.fromArray(array, start, end)`", "2.8.0")
+ def fromArray[A](arr: Array[A], start: Int, len: Int): List[A] = {
+ var res: List[A] = Nil
+ var i = start + len
+ while (i > start) {
+ i -= 1
+ res = arr(i) :: res
+ }
+ res
+ }
+
+ /** Returns the list resulting from applying the given function `f`
+ * to corresponding elements of the argument lists.
+ *
+ * @param f function to apply to each pair of elements.
+ * @return `[f(a,,0,,,b,,0,,), ..., f(a,,n,,,b,,n,,)]` if the lists are
+ * `[a,,0,,, ..., a,,k,,]`, `[b,,0,,, ..., b,,l,,]` and
+ * `n = min(k,l)`
+ */
+ @deprecated("use `(xs, ys).zipped.map(f)` instead of `List.map2(xs, ys)(f)`", "2.8.0")
+ def map2[A,B,C](xs: List[A], ys: List[B])(f: (A, B) => C): List[C] = {
+ val b = new ListBuffer[C]
+ var xc = xs
+ var yc = ys
+ while (!xc.isEmpty && !yc.isEmpty) {
+ b += f(xc.head, yc.head)
+ xc = xc.tail
+ yc = yc.tail
+ }
+ b.toList
+ }
+
+ /** Tests whether the given predicate `p` holds
+ * for all corresponding elements of the argument lists.
+ *
+ * @param p function to apply to each pair of elements.
+ * @return `(p(a<sub>0</sub>,b<sub>0</sub>) &amp;&amp;
+ * ... &amp;&amp; p(a<sub>n</sub>,b<sub>n</sub>))]`
+ * if the lists are `[a<sub>0</sub>, ..., a<sub>k</sub>]`;
+ * `[b<sub>0</sub>, ..., b<sub>l</sub>]`
+ * and `n = min(k,l)`
+ */
+ @deprecated("use `(xs, ys).zipped.forall(f)` instead of `List.forall2(xs, ys)(f)`", "2.8.0")
+ def forall2[A,B](xs: List[A], ys: List[B])(f: (A, B) => Boolean): Boolean = {
+ var xc = xs
+ var yc = ys
+ while (!xc.isEmpty && !yc.isEmpty) {
+ if (!f(xc.head, yc.head)) return false
+ xc = xc.tail
+ yc = yc.tail
+ }
+ true
+ }
+
+ /** Tests whether the given predicate `p` holds
+ * for some corresponding elements of the argument lists.
+ *
+ * @param p function to apply to each pair of elements.
+ * @return `n != 0 &amp;&amp; (p(a<sub>0</sub>,b<sub>0</sub>) ||
+ * ... || p(a<sub>n</sub>,b<sub>n</sub>))]` if the lists are
+ * `[a<sub>0</sub>, ..., a<sub>k</sub>]`,
+ * `[b<sub>0</sub>, ..., b<sub>l</sub>]` and
+ * `n = min(k,l)`
+ */
+ @deprecated("use `(xs, ys).zipped.exists(f)` instead of `List.exists2(xs, ys)(f)`", "2.8.0")
+ def exists2[A,B](xs: List[A], ys: List[B])(f: (A, B) => Boolean): Boolean = {
+ var xc = xs
+ var yc = ys
+ while (!xc.isEmpty && !yc.isEmpty) {
+ if (f(xc.head, yc.head)) return true
+ xc = xc.tail
+ yc = yc.tail
+ }
+ false
+ }
+
+ /** Transposes a list of lists.
+ * pre: All element lists have the same length.
+ *
+ * @param xss the list of lists
+ * @return the transposed list of lists
+ */
+ @deprecated("use `xss.transpose` instead of `List.transpose(xss)`", "2.8.0")
+ def transpose[A](xss: List[List[A]]): List[List[A]] = {
+ val buf = new ListBuffer[List[A]]
+ var yss = xss
+ while (!yss.head.isEmpty) {
+ buf += (yss map (_.head))
+ yss = (yss map (_.tail))
+ }
+ buf.toList
+ }
}
/** Only used for list serialization */
diff --git a/src/library/scala/collection/immutable/Range.scala b/src/library/scala/collection/immutable/Range.scala
index 16d7e68dee..c92c0268b6 100644
--- a/src/library/scala/collection/immutable/Range.scala
+++ b/src/library/scala/collection/immutable/Range.scala
@@ -51,15 +51,35 @@ extends collection.AbstractSeq[Int]
{
override def par = new ParRange(this)
- // This member is designed to enforce conditions:
- // (step != 0) && (length <= Int.MaxValue),
- // but cannot be evaluated eagerly because we have a pattern where ranges
- // are constructed like: "x to y by z"
- // The "x to y" piece should not trigger an exception. So the calculation
- // is delayed, which means it will not fail fast for those cases where failing
- // was correct.
- private lazy val numRangeElements: Int = Range.count(start, end, step, isInclusive)
-
+ private def gap = end.toLong - start.toLong
+ private def isExact = gap % step == 0
+ private def hasStub = isInclusive || !isExact
+ private def longLength = gap / step + ( if (hasStub) 1 else 0 )
+
+ // Check cannot be evaluated eagerly because we have a pattern where
+ // ranges are constructed like: "x to y by z" The "x to y" piece
+ // should not trigger an exception. So the calculation is delayed,
+ // which means it will not fail fast for those cases where failing was
+ // correct.
+ override final val isEmpty = (
+ (start > end && step > 0)
+ || (start < end && step < 0)
+ || (start == end && !isInclusive)
+ )
+ final val numRangeElements: Int = {
+ if (step == 0) throw new IllegalArgumentException("step cannot be 0.")
+ else if (isEmpty) 0
+ else {
+ val len = longLength
+ if (len > scala.Int.MaxValue) -1
+ else len.toInt
+ }
+ }
+ final val lastElement = start + (numRangeElements - 1) * step
+ final val terminalElement = start + numRangeElements * step
+
+ override def last = if (isEmpty) Nil.last else lastElement
+
protected def copy(start: Int, end: Int, step: Int): Range = new Range(start, end, step)
/** Create a new range with the `start` and `end` values of this range and
@@ -71,93 +91,46 @@ extends collection.AbstractSeq[Int]
def isInclusive = false
- override def length: Int = numRangeElements
- override lazy val last: Int =
- if (length == 0) Nil.last
- else locationAfterN(length - 1)
-
- final override def isEmpty = length == 0
-
- @inline
- final def apply(idx: Int): Int = {
- if (idx < 0 || idx >= length) throw new IndexOutOfBoundsException(idx.toString)
- locationAfterN(idx)
+ override def size = length
+ override def length = if (numRangeElements < 0) fail() else numRangeElements
+
+ private def description = "%d %s %d by %s".format(start, if (isInclusive) "to" else "until", end, step)
+ private def fail() = throw new IllegalArgumentException(description + ": seqs cannot contain more than Int.MaxValue elements.")
+ private def validateMaxLength() {
+ if (numRangeElements < 0)
+ fail()
}
- /** @note Making foreach run as fast as a while loop is a challenge.
- * The key elements which I can observe making a difference are:
- *
- * - the inner loop should be as small as possible
- * - the inner loop should be monomorphic
- * - the inner loop should perform no boxing and no avoidable tests
- *
- * This is achieved by:
- *
- * - keeping initialization logic out of the inner loop
- * - dispatching to custom variations based on initial conditions
- * - tricking the compiler into always calling Function1#apply$mcVI$sp
- *
- * The last one is important and less than obvious. Even when foreach
- * was specialized on Unit, only Int => Unit arguments benefited from it.
- * Other function types would be accepted, but in the absence of full
- * specialization the integer argument was boxed on every call. For example:
- *
- class A {
- final def f(x: Int): Int = x + 1
- // Calls Range.foreach, which calls Function1.apply
- def g1 = 1 until 100 foreach { x => f(x) }
- // Calls Range.foreach$mVc$sp, which calls Function1.apply$mcVI$sp
- def g2 = 1 until 100 foreach { x => f(x) ; () }
- }
- *
- * However! Since the result of the closure is always discarded, we
- * simply cast it to Int => Unit, thereby executing the fast version.
- * The seemingly looming ClassCastException can never arrive.
- */
- @inline final override def foreach[U](f: Int => U) {
- if (step < 0) {
- if (isInclusive) foreachDownIn(f.asInstanceOf[Int => Unit])
- else foreachDownEx(f.asInstanceOf[Int => Unit])
- }
- else {
- if (isInclusive) foreachUpIn(f.asInstanceOf[Int => Unit])
- else foreachUpEx(f.asInstanceOf[Int => Unit])
+ def validateRangeBoundaries(f: Int => Any): Boolean = {
+ validateMaxLength()
+
+ start != Int.MinValue || end != Int.MinValue || {
+ var count = 0
+ var num = start
+ while (count < numRangeElements) {
+ f(num)
+ count += 1
+ num += step
+ }
+ false
}
}
- /** !!! These methods must be public or they will not be inlined.
- * But they are certainly not intended to be part of the API.
- * This collision between inlining requirements and access semantics
- * is highly unfortunate and must be resolved.
- *
- * Proposed band-aid: an @internal annotation.
- */
- @inline final def foreachDownIn(f: Int => Unit) {
- var i = start
- while (i >= end) {
- f(i)
- i += step
- }
- }
- @inline final def foreachUpIn(f: Int => Unit) {
- var i = start
- while (i <= end) {
- f(i)
- i += step
- }
+ @inline final def apply(idx: Int): Int = {
+ validateMaxLength()
+ if (idx < 0 || idx >= numRangeElements) throw new IndexOutOfBoundsException(idx.toString)
+ else start + (step * idx)
}
- @inline final def foreachDownEx(f: Int => Unit) {
- var i = start
- while (i > end) {
- f(i)
- i += step
- }
- }
- @inline final def foreachUpEx(f: Int => Unit) {
- var i = start
- while (i < end) {
- f(i)
- i += step
+
+ @inline final override def foreach[@specialized(Unit) U](f: Int => U) {
+ if (validateRangeBoundaries(f)) {
+ var i = start
+ val terminal = terminalElement
+ val step = this.step
+ while (i != terminal) {
+ f(i)
+ i += step
+ }
}
}
@@ -169,8 +142,8 @@ extends collection.AbstractSeq[Int]
* @return a new range consisting of `n` first elements.
*/
final override def take(n: Int): Range = (
- if (n <= 0 || length == 0) newEmptyRange(start)
- else if (n >= length) this
+ if (n <= 0 || isEmpty) newEmptyRange(start)
+ else if (n >= numRangeElements) this
else new Range.Inclusive(start, locationAfterN(n - 1), step)
)
@@ -182,8 +155,8 @@ extends collection.AbstractSeq[Int]
* @return a new range consisting of all the elements of this range except `n` first elements.
*/
final override def drop(n: Int): Range = (
- if (n <= 0 || length == 0) this
- else if (n >= length) newEmptyRange(end)
+ if (n <= 0 || isEmpty) this
+ else if (n >= numRangeElements) newEmptyRange(end)
else copy(locationAfterN(n), end, step)
)
@@ -218,7 +191,7 @@ extends collection.AbstractSeq[Int]
var current = start
var counted = 0
- while (counted < length && p(current)) {
+ while (counted < numRangeElements && p(current)) {
counted += 1
current += step
}
@@ -226,7 +199,7 @@ extends collection.AbstractSeq[Int]
}
// Tests whether a number is within the endpoints, without testing
// whether it is a member of the sequence (i.e. when step > 1.)
- private def isWithinBoundaries(elem: Int) = (length > 0) && (
+ private def isWithinBoundaries(elem: Int) = !isEmpty && (
(step > 0 && start <= elem && elem <= last ) ||
(step < 0 && last <= elem && elem <= start)
)
@@ -255,21 +228,21 @@ extends collection.AbstractSeq[Int]
*
* $doesNotUseBuilders
*/
- final override def takeRight(n: Int): Range = drop(length - n)
+ final override def takeRight(n: Int): Range = drop(numRangeElements - n)
/** Creates a new range consisting of the initial `length - n` elements of the range.
*
* $doesNotUseBuilders
*/
- final override def dropRight(n: Int): Range = take(length - n)
+ final override def dropRight(n: Int): Range = take(numRangeElements - n)
/** Returns the reverse of this range.
*
* $doesNotUseBuilders
*/
final override def reverse: Range =
- if (length > 0) new Range.Inclusive(last, start, -step)
- else this
+ if (isEmpty) this
+ else new Range.Inclusive(last, start, -step)
/** Make range inclusive.
*/
@@ -280,10 +253,9 @@ extends collection.AbstractSeq[Int]
final def contains(x: Int) = isWithinBoundaries(x) && ((x - start) % step == 0)
final override def sum[B >: Int](implicit num: Numeric[B]): Int = {
- val len = length
- if (len == 0) 0
- else if (len == 1) head
- else (len.toLong * (head + last) / 2).toInt
+ if (isEmpty) 0
+ else if (numRangeElements == 1) head
+ else (numRangeElements.toLong * (head + last) / 2).toInt
}
override def toIterable = this
@@ -293,7 +265,7 @@ extends collection.AbstractSeq[Int]
override def equals(other: Any) = other match {
case x: Range =>
(x canEqual this) && (length == x.length) && (
- (length == 0) || // all empty sequences are equal
+ isEmpty || // all empty sequences are equal
(start == x.start && last == x.last) // same length and same endpoints implies equality
)
case _ =>
@@ -304,7 +276,7 @@ extends collection.AbstractSeq[Int]
*/
override def toString() = {
- val endStr = if (length > Range.MAX_PRINT) ", ... )" else ")"
+ val endStr = if (numRangeElements > Range.MAX_PRINT) ", ... )" else ")"
take(Range.MAX_PRINT).mkString("Range(", ", ", endStr)
}
}
@@ -415,3 +387,4 @@ object Range {
// super.foreach(f)
}
}
+ \ No newline at end of file
diff --git a/src/partest/scala/tools/partest/nest/CompileManager.scala b/src/partest/scala/tools/partest/nest/CompileManager.scala
index f4ebfb7e7d..68688ff949 100644
--- a/src/partest/scala/tools/partest/nest/CompileManager.scala
+++ b/src/partest/scala/tools/partest/nest/CompileManager.scala
@@ -75,7 +75,8 @@ class DirectCompiler(val fileManager: FileManager) extends SimpleCompiler {
val logWriter = new FileWriter(log)
// check whether there is a ".flags" file
- val flagsFileName = "%s.flags" format (basename(log.getName) dropRight 4) // 4 is "-run" or similar
+ val logFile = basename(log.getName)
+ val flagsFileName = "%s.flags" format (logFile.substring(0, logFile.lastIndexOf("-")))
val argString = (io.File(log).parent / flagsFileName) ifFile (x => updatePluginPath(x.slurp())) getOrElse ""
val allOpts = fileManager.SCALAC_OPTS.toList ::: argString.split(' ').toList.filter(_.length > 0)
val args = allOpts.toList
diff --git a/test/files/scalacheck/CheckEither.scala b/test/files/scalacheck/CheckEither.scala
index a7e50877a7..0145d3321f 100644
--- a/test/files/scalacheck/CheckEither.scala
+++ b/test/files/scalacheck/CheckEither.scala
@@ -8,7 +8,7 @@ import org.scalacheck.Test.{Params, check}
import org.scalacheck.ConsoleReporter.testStatsEx
import Function.tupled
-object CheckEither extends Properties("Either") {
+object Test extends Properties("Either") {
implicit def arbitraryEither[X, Y](implicit xa: Arbitrary[X], ya: Arbitrary[Y]): Arbitrary[Either[X, Y]] =
Arbitrary[Either[X, Y]](oneOf(arbitrary[X].map(Left(_)), arbitrary[Y].map(Right(_))))
@@ -186,9 +186,3 @@ object CheckEither extends Properties("Either") {
STest.checkProperties(STest.Params(testCallback = ConsoleReporter(0)), this)
}
}
-
-object Test {
- def main(args: Array[String]): Unit = {
- CheckEither.runTests()
- }
-}
diff --git a/test/files/scalacheck/range.scala b/test/files/scalacheck/range.scala
index 56295f204c..72979115be 100644
--- a/test/files/scalacheck/range.scala
+++ b/test/files/scalacheck/range.scala
@@ -12,10 +12,16 @@ class Counter(r: Range) {
if (cnt % 500000000L == 0L) {
println("Working: %s %d %d" format (str, cnt, x))
}
- if (cnt > (Int.MaxValue.toLong + 1) * 2)
- error("Count exceeds maximum possible for an Int Range")
- if ((r.step > 0 && last.exists(_ > x)) || (r.step < 0 && last.exists(_ < x)))
- error("Range wrapped: %d %s" format (x, last.toString))
+ if (cnt > (Int.MaxValue.toLong + 1) * 2) {
+ val msg = "Count exceeds maximum possible for an Int Range: %s" format str
+ println(msg) // exception is likely to be eaten by an out of memory error
+ sys error msg
+ }
+ if ((r.step > 0 && last.exists(_ > x)) || (r.step < 0 && last.exists(_ < x))) {
+ val msg = "Range %s wrapped: %d %s" format (str, x, last.toString)
+ println(msg) // exception is likely to be eaten by an out of memory error
+ sys error msg
+ }
last = Some(x)
}
}
@@ -23,29 +29,40 @@ class Counter(r: Range) {
abstract class RangeTest(kind: String) extends Properties("Range "+kind) {
def myGen: Gen[Range]
- val genRange = for {
- start <- arbitrary[Int]
- end <- arbitrary[Int]
- step <- Gen.choose(1, (start - end).abs + 1)
- } yield if (start < end) Range(start, end, step) else Range(start, end, -step)
-
- val genReasonableSizeRange = for {
- start <- choose(-Int.MinValue, Int.MaxValue)
- end <- choose(-Int.MinValue, Int.MaxValue)
+ def genReasonableSizeRange = oneOf(genArbitraryRange, genBoundaryRange)
+
+ def genArbitraryRange = for {
+ start <- choose(Int.MinValue, Int.MaxValue)
+ end <- choose(Int.MinValue, Int.MaxValue)
step <- choose(-Int.MaxValue, Int.MaxValue)
} yield Range(start, end, if (step == 0) 100 else step)
- val genSmallRange = for {
+ def genBoundaryRange = for {
+ boundary <- oneOf(Int.MinValue, -1, 0, 1, Int.MaxValue)
+ isStart <- arbitrary[Boolean]
+ size <- choose(1, 100)
+ step <- choose(1, 101)
+ } yield {
+ val signum = if (boundary == 0) 1 else boundary.signum
+ if (isStart) Range(boundary, boundary - size * boundary.signum, - step * signum)
+ else Range(boundary - size * boundary.signum, boundary, step * signum)
+ }
+
+
+ def genSmallRange = for {
start <- choose(-100, 100)
end <- choose(-100, 100)
step <- choose(1, 1)
} yield if (start < end) Range(start, end, step) else Range(start, end, -step)
- val genRangeByOne = for {
- start <- arbitrary[Int]
- end <- arbitrary[Int]
- if (end.toLong - start.toLong).abs <= 10000000L
- } yield if (start < end) Range(start, end) else Range(end, start)
+ def genRangeByOne = oneOf(genRangeOpenByOne, genRangeClosedByOne)
+
+ def genRangeOpenByOne = for {
+ r <- oneOf(genSmallRange, genBoundaryRange)
+ if (r.end.toLong - r.start.toLong).abs <= 10000000L
+ } yield if (r.start < r.end) Range(r.start, r.end) else Range(r.end, r.start)
+
+ def genRangeClosedByOne = for (r <- genRangeOpenByOne) yield r.start to r.end
def str(r: Range) = "Range["+r.start+", "+r.end+", "+r.step+(if (r.isInclusive) "]" else ")")
@@ -71,7 +88,8 @@ abstract class RangeTest(kind: String) extends Properties("Range "+kind) {
def multiple(r: Range, x: Int) = (x.toLong - r.start) % r.step == 0
- property("foreach.step") = forAll(myGen) { r =>
+ property("foreach.step") = forAllNoShrink(myGen) { r =>
+// println("foreach.step "+str(r))
var allValid = true
val cnt = new Counter(r)
// println("--------------------")
@@ -84,6 +102,7 @@ abstract class RangeTest(kind: String) extends Properties("Range "+kind) {
}
property("foreach.inside.range") = forAll(myGen) { r =>
+// println("foreach.inside.range "+str(r))
var allValid = true
var last: Option[Int] = None
val cnt = new Counter(r)
@@ -94,6 +113,7 @@ abstract class RangeTest(kind: String) extends Properties("Range "+kind) {
}
property("foreach.visited.size") = forAll(myGen) { r =>
+// println("foreach.visited.size "+str(r))
var visited = 0L
val cnt = new Counter(r)
r foreach { x => cnt(x)
@@ -108,14 +128,17 @@ abstract class RangeTest(kind: String) extends Properties("Range "+kind) {
}
property("length") = forAll(myGen suchThat (r => expectedSize(r).toInt == expectedSize(r))) { r =>
+// println("length "+str(r))
(r.length == expectedSize(r)) :| str(r)
}
property("isEmpty") = forAll(myGen suchThat (r => expectedSize(r).toInt == expectedSize(r))) { r =>
+// println("isEmpty "+str(r))
(r.isEmpty == (expectedSize(r) == 0L)) :| str(r)
}
property("contains") = forAll(myGen, arbInt.arbitrary) { (r, x) =>
+// println("contains "+str(r))
// println("----------------")
// println(str(r))
// println(x)
@@ -126,11 +149,13 @@ abstract class RangeTest(kind: String) extends Properties("Range "+kind) {
}
property("take") = forAll(myGen suchThat (r => expectedSize(r).toInt == expectedSize(r)), arbInt.arbitrary) { (r, x) =>
+// println("take "+str(r))
val t = r take x
(t.size == (0 max x min r.size) && t.start == r.start && t.step == r.step) :| str(r)+" / "+str(t)+": "+x
}
property("init") = forAll(myGen suchThat (r => expectedSize(r).toInt == expectedSize(r))) { r =>
+// println("init "+str(r))
(r.size == 0) || {
val t = r.init
(t.size + 1 == r.size) && (t.isEmpty || t.head == r.head)
@@ -138,6 +163,7 @@ abstract class RangeTest(kind: String) extends Properties("Range "+kind) {
}
property("takeWhile") = forAll(myGen suchThat (r => expectedSize(r).toInt == expectedSize(r)), arbInt.arbitrary) { (r, x) =>
+// println("takeWhile "+str(r))
val t = (if (r.step > 0) r takeWhile (_ <= x) else r takeWhile(_ >= x))
if (r.size == 0) {
(t.size == 0) :| str(r)+" / "+str(t)+": "+x
@@ -148,6 +174,7 @@ abstract class RangeTest(kind: String) extends Properties("Range "+kind) {
}
property("reverse.toSet.equal") = forAll(myGen) { r =>
+// println("reverse.toSet.equal "+str(r))
val reversed = r.reverse
val aresame = r.toSet == reversed.toSet
if (!aresame) {
@@ -157,7 +184,7 @@ abstract class RangeTest(kind: String) extends Properties("Range "+kind) {
println(r.toSet)
println(reversed.toSet)
}
- aresame
+ aresame :| str(r)
}
}
@@ -178,11 +205,11 @@ object InclusiveRangeTest extends RangeTest("inclusive") {
}
object ByOneRangeTest extends RangeTest("byOne") {
- override def myGen = genSmallRange
+ override def myGen = genRangeByOne
}
object InclusiveByOneRangeTest extends RangeTest("inclusiveByOne") {
- override def myGen = for (r <- genSmallRange) yield r.inclusive
+ override def myGen = for (r <- genRangeByOne) yield r.inclusive
}
object SmallValuesRange extends RangeTest("smallValues") {
@@ -207,9 +234,11 @@ object TooLargeRange extends Properties("Too Large Range") {
object Test extends Properties("Range") {
import org.scalacheck.{ Test => STest }
- List(NormalRangeTest, InclusiveRangeTest, ByOneRangeTest, InclusiveByOneRangeTest, TooLargeRange) foreach { ps =>
- STest.checkProperties(STest.Params(testCallback = ConsoleReporter(0)), ps)
- }
+ include(NormalRangeTest)
+ include(InclusiveRangeTest)
+ include(ByOneRangeTest)
+ include(InclusiveByOneRangeTest)
+ include(TooLargeRange)
}
/* Mini-benchmark
diff --git a/test/scaladoc/resources/SI_5054_q7.scala b/test/scaladoc/resources/SI_5054_q7.scala
index 26d4b5fcf4..1bd120e30c 100644
--- a/test/scaladoc/resources/SI_5054_q7.scala
+++ b/test/scaladoc/resources/SI_5054_q7.scala
@@ -6,17 +6,17 @@ trait SI_5054_q7 {
*
* @param lost a lost parameter
* @return some integer
- * @usecase def test(): Int
+ * @usecase def test1(): Int
*
* This takes the implicit value in scope.
*
- * Example: `test()`
+ * Example: `test1()`
*
- * @usecase def test(explicit: Int): Int
+ * @usecase def test2(explicit: Int): Int
*
* This takes the explicit value passed.
*
- * Example: `test(3)`
+ * Example: `test2(3)`
*/
def test(implicit lost: Int): Int
}
diff --git a/test/scaladoc/resources/SI_5287.scala b/test/scaladoc/resources/SI_5287.scala
new file mode 100644
index 0000000000..141ab15325
--- /dev/null
+++ b/test/scaladoc/resources/SI_5287.scala
@@ -0,0 +1,17 @@
+trait SI_5287_A {
+ def method(implicit a: Int): Int = a
+}
+
+trait SI_5287_B extends SI_5287_A {
+ override def method(implicit a: Int): Int = a + 1
+}
+
+trait SI_5287 extends SI_5287_B{
+ /**
+ * Some explanation
+ *
+ * @usecase def method(): Int
+ * The usecase explanation
+ */
+ override def method(implicit a: Int): Int = a + 3
+} \ No newline at end of file
diff --git a/test/scaladoc/scala/html.flags b/test/scaladoc/scala/html.flags
new file mode 100644
index 0000000000..b2264ec4f4
--- /dev/null
+++ b/test/scaladoc/scala/html.flags
@@ -0,0 +1 @@
+-encoding UTF-8 \ No newline at end of file
diff --git a/test/scaladoc/scala/html/HtmlFactoryTest.flags b/test/scaladoc/scala/html/HtmlFactoryTest.flags
new file mode 100644
index 0000000000..b2264ec4f4
--- /dev/null
+++ b/test/scaladoc/scala/html/HtmlFactoryTest.flags
@@ -0,0 +1 @@
+-encoding UTF-8 \ No newline at end of file
diff --git a/test/scaladoc/scala/html/HtmlFactoryTest.scala b/test/scaladoc/scala/html/HtmlFactoryTest.scala
index d1bfbb023f..5b17affbf0 100644
--- a/test/scaladoc/scala/html/HtmlFactoryTest.scala
+++ b/test/scaladoc/scala/html/HtmlFactoryTest.scala
@@ -21,6 +21,9 @@ object XMLUtil {
}
object Test extends Properties("HtmlFactory") {
+
+ final val RESOURCES = "test/scaladoc/resources/"
+
import scala.tools.nsc.doc.{DocFactory, Settings}
import scala.tools.nsc.doc.model.IndexModelFactory
import scala.tools.nsc.doc.html.HtmlFactory
@@ -47,7 +50,7 @@ object Test extends Properties("HtmlFactory") {
def createTemplates(basename: String) = {
val result = scala.collection.mutable.Map[String, scala.xml.NodeSeq]()
- createFactory.makeUniverse(List("test/scaladoc/resources/"+basename)) match {
+ createFactory.makeUniverse(List(RESOURCES+basename)) match {
case Some(universe) => {
val index = IndexModelFactory.makeIndex(universe)
(new HtmlFactory(universe, index)).writeTemplates((page) => {
@@ -61,7 +64,7 @@ object Test extends Properties("HtmlFactory") {
}
def createReferenceIndex(basename: String) = {
- createFactory.makeUniverse(List("test/scaladoc/resources/"+basename)) match {
+ createFactory.makeUniverse(List(RESOURCES+basename)) match {
case Some(universe) => {
val index = IndexModelFactory.makeIndex(universe)
val pages = index.firstLetterIndex.map({
@@ -81,6 +84,52 @@ object Test extends Properties("HtmlFactory") {
val html = scala.stripSuffix(".scala") + ".html"
createTemplates(scala)(html)
}
+
+ /**
+ * See checkTextOnly(scalaFile: String, checks: List[String])
+ */
+ def checkText1(scalaFile: String, check: String, debug: Boolean = true): Boolean = checkText(scalaFile, List(check), debug)
+
+ /**
+ * This tests the text without the markup - ex:
+ *
+ * <h4 class="signature">
+ * <span class="modifier_kind">
+ * <span class="modifier">implicit</span>
+ * <span class="kind">def</span>
+ * </span>
+ * <span class="symbol">
+ * <span class="name">test</span><span class="params">()</span><span class="result">: <span name="scala.Int" class="extype">Int</span></span>
+ * </span>
+ * </h4>
+ *
+ * becomes:
+ *
+ * implicit def test(): Int
+ *
+ * and is required to contain the text in the given checks
+ *
+ * NOTE: Comparison is done ignoring all whitespace
+ */
+ def checkText(scalaFile: String, checks: List[String], debug: Boolean = true): Boolean = {
+ val htmlFile = scalaFile.stripSuffix(".scala") + ".html"
+ val htmlText = createTemplates(scalaFile)(htmlFile).text.replace('→',' ').replaceAll("\\s+","")
+ var result = true
+
+ for (check <- checks) {
+ val checkText = check.replace('→',' ').replaceAll("\\s+","")
+ val checkValue = htmlText.contains(checkText)
+ if (debug && (!checkValue)) {
+ Console.err.println("Check failed: ")
+ Console.err.println("HTML: " + htmlText)
+ Console.err.println("Check: " + checkText)
+ }
+ result &&= checkValue
+ }
+
+ result
+ }
+
def shortComments(root: scala.xml.Node) =
XMLUtil.stripGroup(root).descendant.flatMap {
@@ -377,113 +426,43 @@ object Test extends Properties("HtmlFactory") {
createTemplate("SI_4898.scala")
true
}
-
- // A piece of the signature - corresponding to the use case
- def signature(no: Int, modifier: String) = ("""
- <li visbl="pub" name="SI_5054_q""" + no + """#test" data-isabs="false">
- <a id="test():Int"></a>
- <h4 class="signature">
- <span class="modifier_kind">
- <span class="modifier">""" + modifier + """</span>
- <span class="kind">def</span>
- </span>
- <span class="symbol">
- <span class="name">test</span><span class="params">()</span><span class="result">: <span name="scala.Int" class="extype">Int</span></span>
- </span>
- </h4>
- <p class="shortcomment cmt">[use case]
- </p>
- </li>""").replaceAll("\\s+", "")
- property("Use cases should override their original members") = {
- createTemplate("SI_5054_q1.scala") match {
- case node: scala.xml.Node =>
- node.toString.replaceAll("\\s+","").contains(signature(1, ""))
- case _ => false
- }
- }
-
- property("Use cases should keep their flags - final should not be lost") = {
- createTemplate("SI_5054_q2.scala") match {
- case node: scala.xml.Node =>
- node.toString.replaceAll("\\s+","").contains(signature(2, "final"))
- case _ => false
- }
- }
+ property("Use cases should override their original members") =
+ checkText1("SI_5054_q1.scala", """def test(): Int""") &&
+ !checkText1("SI_5054_q1.scala", """def test(implicit lost: Int): Int""")
- property("Use cases should keep their flags - implicit should not be lost") = {
- createTemplate("SI_5054_q3.scala") match {
- case node: scala.xml.Node =>
- node.toString.replaceAll("\\s+","").contains(signature(3, "implicit"))
- case _ => false
- }
- }
- property("Use cases should keep their flags - real abstract should not be lost") = {
- createTemplate("SI_5054_q4.scala") match {
- case node: scala.xml.Node =>
- node.toString.replaceAll("\\s+","").contains(signature(4, "abstract"))
- case _ => false
- }
- }
-
- property("Use cases should keep their flags - traits should not be affected") = {
- createTemplate("SI_5054_q5.scala") match {
- case node: scala.xml.Node =>
- node.toString.replaceAll("\\s+","").contains(signature(5, ""))
- case _ => false
- }
- }
-
- property("Use cases should keep their flags - traits should not be affected") = {
- createTemplate("SI_5054_q6.scala") match {
- case node: scala.xml.Node =>
- node.toString.replaceAll("\\s+","").contains(signature(6, "abstract"))
- case _ => false
- }
- }
+ property("Use cases should keep their flags - final should not be lost") =
+ checkText1("SI_5054_q2.scala", """final def test(): Int""")
- val useCaseExplanation = """
- </li><li visbl="pub" name="SI_5054_q7#test" data-isabs="false">
- <a id="test():Int"></a>
- <h4 class="signature">
- <span class="modifier_kind">
- <span class="modifier">abstract </span>
- <span class="kind">def</span>
- </span>
- <span class="symbol">
- <span class="name">test</span><span class="params">()</span><span class="result">: <span name="scala.Int" class="extype">Int</span></span>
- </span>
- </h4>
- <p class="shortcomment cmt">[use case] This takes the implicit value in scope.</p><div class="fullcomment">[use case] <div class="comment cmt"><p>This takes the implicit value in scope.</p><p>Example: <code>test()</code></p></div><dl class="paramcmts block"><dt>returns</dt><dd class="cmt"><p>some integer
- </p></dd></dl></div>
- </li><li visbl="pub" name="SI_5054_q7#test" data-isabs="false">
- <a id="test(Int):Int"></a>
- <h4 class="signature">
- <span class="modifier_kind">
- <span class="modifier">abstract </span>
- <span class="kind">def</span>
- </span>
- <span class="symbol">
- <span class="name">test</span><span class="params">(<span name="explicit">explicit: <span name="scala.Int" class="extype">Int</span></span>)</span><span class="result">: <span name="scala.Int" class="extype">Int</span></span>
- </span>
- </h4>
- <p class="shortcomment cmt">[use case] This takes the explicit value passed.</p><div class="fullcomment">[use case] <div class="comment cmt"><p>This takes the explicit value passed.</p><p>Example: <code>test(3)</code></p></div><dl class="paramcmts block"><dt>returns</dt><dd class="cmt"><p>some integer
- </p></dd></dl></div>
- </li>
- """.replaceAll("\\s+","")
-
- property("Use case individual signature test") = {
- createTemplate("SI_5054_q7.scala") match {
- case node: scala.xml.Node =>
- node.toString.replaceAll("\\s+","").contains(useCaseExplanation)
- case _ => false
- }
- }
+ property("Use cases should keep their flags - implicit should not be lost") =
+ checkText1("SI_5054_q3.scala", """implicit def test(): Int""")
+
+ property("Use cases should keep their flags - real abstract should not be lost") =
+ checkText1("SI_5054_q4.scala", """abstract def test(): Int""")
+
+ property("Use cases should keep their flags - traits should not be affected") =
+ checkText1("SI_5054_q5.scala", """def test(): Int""")
+
+ property("Use cases should keep their flags - traits should not be affected") =
+ checkText1("SI_5054_q6.scala", """abstract def test(): Int""")
+
+ property("Use case individual signature test") =
+ checkText("SI_5054_q7.scala", List(
+ """abstract def test2(explicit: Int): Int [use case] This takes the explicit value passed.""",
+ """abstract def test1(): Int [use case] This takes the implicit value in scope."""))
+
+ property("Display correct \"Definition classes\"") =
+ checkText1("SI_5287.scala",
+ """def method(): Int
+ [use case] The usecase explanation
+ [use case] The usecase explanation
+ Definition Classes SI_5287 SI_5287_B SI_5287_A""", debug=true)
+ // explanation appears twice, as small comment and full comment
{
val files = createTemplates("basic.scala")
- println(files)
+ //println(files)
property("class") = files.get("com/example/p1/Clazz.html") match {
case Some(node: scala.xml.Node) => {