diff options
Diffstat (limited to 'src')
24 files changed, 588 insertions, 448 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 819887f959..56ad4738d9 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -86,6 +86,19 @@ class Global(var currentSettings: Settings, var reporter: Reporter) def erasurePhase: Phase = if (currentRun.isDefined) currentRun.erasurePhase else NoPhase + /* Override `newStubSymbol` defined in `SymbolTable` to provide us access + * to the last tree to typer, whose position is the trigger of stub errors. */ + override def newStubSymbol(owner: Symbol, + name: Name, + missingMessage: String): Symbol = { + val stubSymbol = super.newStubSymbol(owner, name, missingMessage) + val stubErrorPosition = { + val lastTreeToTyper = analyzer.lastTreeToTyper + if (lastTreeToTyper != EmptyTree) lastTreeToTyper.pos else stubSymbol.pos + } + stubSymbol.setPos(stubErrorPosition) + } + // platform specific elements protected class GlobalPlatform extends { diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 9129478b41..f146419a73 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -553,6 +553,7 @@ abstract class ClassfileParser { val name = readName() val sym = ownerForFlags(jflags).newMethod(name.toTermName, NoPosition, sflags) var info = pool.getType(sym, u2) + var removedOuterParameter = false if (name == nme.CONSTRUCTOR) info match { case MethodType(params, restpe) => @@ -567,6 +568,7 @@ abstract class ClassfileParser { * ClassfileParser for 1 executes, and clazz.owner is the package. */ assert(params.head.tpe.typeSymbol == clazz.owner || clazz.owner.hasPackageFlag, params.head.tpe.typeSymbol + ": " + clazz.owner) + removedOuterParameter = true params.tail case _ => params @@ -586,7 +588,7 @@ abstract class ClassfileParser { // parsed from SignatureATTR sym setInfo info propagatePackageBoundary(jflags, sym) - parseAttributes(sym, info) + parseAttributes(sym, info, removedOuterParameter) if (jflags.isVarargs) sym modifyInfo arrayToRepeated @@ -769,7 +771,7 @@ abstract class ClassfileParser { GenPolyType(ownTypeParams, tpe) } // sigToType - def parseAttributes(sym: Symbol, symtype: Type) { + def parseAttributes(sym: Symbol, symtype: Type, removedOuterParameter: Boolean = false) { def convertTo(c: Constant, pt: Type): Constant = { if (pt.typeSymbol == BooleanClass && c.tag == IntTag) Constant(c.value != 0) @@ -804,16 +806,24 @@ abstract class ClassfileParser { else devWarning(s"failure to convert $c to $symtype") case tpnme.MethodParametersATTR => def readParamNames(): Unit = { - import tools.asm.Opcodes.ACC_SYNTHETIC + import scala.tools.asm.Opcodes.ACC_SYNTHETIC val paramCount = u1 var i = 0 + if (removedOuterParameter && i < paramCount) { + in.skip(4) + i += 1 + } + var remainingParams = sym.paramss.head // Java only has exactly one parameter list while (i < paramCount) { val name = pool.getName(u2) val access = u2 - if ((access & ACC_SYNTHETIC) != ACC_SYNTHETIC) { // name not synthetic - val params = sym.paramss.head // Java only has exactly one parameter list - params(i).name = name.encode - params(i).resetFlag(SYNTHETIC) + if (remainingParams.nonEmpty) { + val param = remainingParams.head + remainingParams = remainingParams.tail + if ((access & ACC_SYNTHETIC) != ACC_SYNTHETIC) { // name not synthetic + param.name = name.encode + param.resetFlag(SYNTHETIC) + } } i += 1 } @@ -1051,8 +1061,11 @@ abstract class ClassfileParser { val sflags = jflags.toScalaFlags val owner = ownerForFlags(jflags) val scope = getScope(jflags) - def newStub(name: Name) = - owner.newStubSymbol(name, s"Class file for ${entry.externalName} not found").setFlag(JAVA) + def newStub(name: Name) = { + val stub = owner.newStubSymbol(name, s"Class file for ${entry.externalName} not found") + stub.setPos(owner.pos) + stub.setFlag(JAVA) + } val (innerClass, innerModule) = if (file == NoAbstractFile) { (newStub(name.toTypeName), newStub(name.toTermName)) @@ -1174,7 +1187,11 @@ abstract class ClassfileParser { if (enclosing == clazz) entry.scope lookup name else lookupMemberAtTyperPhaseIfPossible(enclosing, name) } - def newStub = enclosing.newStubSymbol(name, s"Unable to locate class corresponding to inner class entry for $name in owner ${entry.outerName}") + def newStub = { + enclosing + .newStubSymbol(name, s"Unable to locate class corresponding to inner class entry for $name in owner ${entry.outerName}") + .setPos(enclosing.pos) + } member.orElse(newStub) } } diff --git a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala deleted file mode 100644 index e69de29bb2..0000000000 --- a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala +++ /dev/null diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala index 744b9c8a8e..9e3e8ff455 100644 --- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala +++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala @@ -84,7 +84,7 @@ abstract class TailCalls extends Transform { * </p> * <p> * Assumes: `Uncurry` has been run already, and no multiple - * parameter lists exit. + * parameter lists exist. * </p> */ class TailCallElimination(unit: CompilationUnit) extends Transformer { diff --git a/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala b/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala index 57de44a038..b1901c04bb 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala @@ -80,7 +80,9 @@ trait Adaptations { context.deprecationWarning(t.pos, t.symbol, adaptWarningMessage(msg), "2.11.0") } } else if (settings.warnAdaptedArgs) - context.warning(t.pos, adaptWarningMessage(s"Adapting argument list by creating a ${args.size}-tuple: this may not be what you want.")) + context.warning(t.pos, adaptWarningMessage( + s"Adapting argument list by creating a ${args.size}-tuple: this may not be what you want.") + ) // return `true` if the adaptation should be kept !(settings.noAdaptedArgs || (args.isEmpty && settings.future)) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 8333d5d295..2cbd9475fc 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3426,29 +3426,29 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // repeat vararg as often as needed, remove by-name val formals = formalTypes(paramTypes, argslen) - /* Try packing all arguments into a Tuple and apply `fun` - * to that. This is the last thing which is tried (after - * default arguments) + /* Try packing all arguments into a Tuple and apply `fun` to that. + * This is the last thing which is tried (after default arguments). */ - def tryTupleApply: Tree = { - if (eligibleForTupleConversion(paramTypes, argslen) && !phase.erasedTypes) { + def tryTupleApply: Tree = + if (phase.erasedTypes || !eligibleForTupleConversion(paramTypes, argslen)) EmptyTree + else { val tupleArgs = List(atPos(tree.pos.makeTransparent)(gen.mkTuple(args))) // expected one argument, but got 0 or >1 ==> try applying to tuple // the inner "doTypedApply" does "extractUndetparams" => restore when it fails val savedUndetparams = context.undetparams - silent(_.doTypedApply(tree, fun, tupleArgs, mode, pt)) map { t => - // Depending on user options, may warn or error here if - // a Unit or tuple was inserted. - val keepTree = ( - !mode.typingExprNotFun // why? introduced in 4e488a60, doc welcome - || t.symbol == null // ditto - || checkValidAdaptation(t, args) - ) - if (keepTree) t else EmptyTree - } orElse { _ => context.undetparams = savedUndetparams ; EmptyTree } + // May warn or error if a Unit or tuple was inserted. + def validate(t: Tree): Tree = { + // regardless of typer's mode + val invalidAdaptation = t.symbol != null && !checkValidAdaptation(t, args) + // only bail if we're typing an expression (and not inside another application) + if (invalidAdaptation && mode.typingExprNotFun) EmptyTree else t + } + def reset(errors: Seq[AbsTypeError]): Tree = { + context.undetparams = savedUndetparams + EmptyTree + } + silent(_.doTypedApply(tree, fun, tupleArgs, mode, pt)).map(validate).orElse(reset) } - else EmptyTree - } /* Treats an application which uses named or default arguments. * Also works if names + a vararg used: when names are used, the vararg diff --git a/src/library/scala/collection/JavaConverters.scala b/src/library/scala/collection/JavaConverters.scala index d48a1764e9..2337f0ef84 100644 --- a/src/library/scala/collection/JavaConverters.scala +++ b/src/library/scala/collection/JavaConverters.scala @@ -18,12 +18,12 @@ import convert._ * * The following conversions are supported via `asScala` and `asJava`: *{{{ - * scala.collection.Iterable <=> java.lang.Iterable - * scala.collection.Iterator <=> java.util.Iterator - * scala.collection.mutable.Buffer <=> java.util.List - * scala.collection.mutable.Set <=> java.util.Set - * scala.collection.mutable.Map <=> java.util.Map - * scala.collection.mutable.concurrent.Map <=> java.util.concurrent.ConcurrentMap + * scala.collection.Iterable <=> java.lang.Iterable + * scala.collection.Iterator <=> java.util.Iterator + * scala.collection.mutable.Buffer <=> java.util.List + * scala.collection.mutable.Set <=> java.util.Set + * scala.collection.mutable.Map <=> java.util.Map + * scala.collection.concurrent.Map <=> java.util.concurrent.ConcurrentMap *}}} * The following conversions are supported via `asScala` and through * specially-named extension methods to convert to Java collections, as shown: diff --git a/src/library/scala/collection/immutable/Map.scala b/src/library/scala/collection/immutable/Map.scala index cbdf7b39f5..4107b6414d 100644 --- a/src/library/scala/collection/immutable/Map.scala +++ b/src/library/scala/collection/immutable/Map.scala @@ -198,7 +198,7 @@ object Map extends ImmutableMapFactory[Map] { else if (key == key2) new Map4(key1, value1, key2, value, key3, value3, key4, value4) else if (key == key3) new Map4(key1, value1, key2, value2, key3, value, key4, value4) else if (key == key4) new Map4(key1, value1, key2, value2, key3, value3, key4, value) - else new HashMap + ((key1, value1), (key2, value2), (key3, value3), (key4, value4), (key, value)) + else (new HashMap).updated(key1,value1).updated(key2, value2).updated(key3, value3).updated(key4, value4).updated(key, value) def + [V1 >: V](kv: (K, V1)): Map[K, V1] = updated(kv._1, kv._2) def - (key: K): Map[K, V] = if (key == key1) new Map3(key2, value2, key3, value3, key4, value4) diff --git a/src/library/scala/collection/immutable/Set.scala b/src/library/scala/collection/immutable/Set.scala index 047ea736bd..0f16f97cb0 100644 --- a/src/library/scala/collection/immutable/Set.scala +++ b/src/library/scala/collection/immutable/Set.scala @@ -193,7 +193,7 @@ object Set extends ImmutableSetFactory[Set] { elem == elem1 || elem == elem2 || elem == elem3 || elem == elem4 def + (elem: A): Set[A] = if (contains(elem)) this - else new HashSet[A] + (elem1, elem2, elem3, elem4, elem) + else new HashSet[A] + elem1 + elem2 + elem3 + elem4 + elem def - (elem: A): Set[A] = if (elem == elem1) new Set3(elem2, elem3, elem4) else if (elem == elem2) new Set3(elem1, elem3, elem4) diff --git a/src/library/scala/collection/mutable/ListBuffer.scala b/src/library/scala/collection/mutable/ListBuffer.scala index 3bb7004184..aa79e972d5 100644 --- a/src/library/scala/collection/mutable/ListBuffer.scala +++ b/src/library/scala/collection/mutable/ListBuffer.scala @@ -119,6 +119,10 @@ final class ListBuffer[A] // Don't use the inherited size, which forwards to a List and is O(n). override def size = length + // Override with efficient implementations using the extra size information available to ListBuffer. + override def isEmpty: Boolean = len == 0 + override def nonEmpty: Boolean = len > 0 + // Implementations of abstract methods in Buffer override def apply(n: Int): A = diff --git a/src/library/scala/concurrent/BatchingExecutor.scala b/src/library/scala/concurrent/BatchingExecutor.scala index a0d7aaea47..fd31f3470e 100644 --- a/src/library/scala/concurrent/BatchingExecutor.scala +++ b/src/library/scala/concurrent/BatchingExecutor.scala @@ -103,7 +103,7 @@ private[concurrent] trait BatchingExecutor extends Executor { override def execute(runnable: Runnable): Unit = { if (batchable(runnable)) { // If we can batch the runnable _tasksLocal.get match { - case null => unbatchedExecute(new Batch(List(runnable))) // If we aren't in batching mode yet, enqueue batch + case null => unbatchedExecute(new Batch(runnable :: Nil)) // If we aren't in batching mode yet, enqueue batch case some => _tasksLocal.set(runnable :: some) // If we are already in batching mode, add to batch } } else unbatchedExecute(runnable) // If not batchable, just delegate to underlying diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index fe684e4d46..f46f294387 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -25,21 +25,20 @@ import scala.annotation.implicitNotFound * and an implicit `ExecutionContext`. The implicit `ExecutionContext` * will be used to execute the callback. * - * It is possible to simply import + * While it is possible to simply import * `scala.concurrent.ExecutionContext.Implicits.global` to obtain an - * implicit `ExecutionContext`. This global context is a reasonable - * default thread pool. - * - * However, application developers should carefully consider where they - * want to set policy; ideally, one place per application (or per - * logically-related section of code) will make a decision about - * which `ExecutionContext` to use. That is, you might want to avoid - * hardcoding `scala.concurrent.ExecutionContext.Implicits.global` all - * over the place in your code. - * One approach is to add `(implicit ec: ExecutionContext)` - * to methods which need an `ExecutionContext`. Then import a specific - * context in one place for the entire application or module, - * passing it implicitly to individual methods. + * implicit `ExecutionContext`, application developers should carefully + * consider where they want to set execution policy; + * ideally, one place per application—or per logically related section of code— + * will make a decision about which `ExecutionContext` to use. + * That is, you will mostly want to avoid hardcoding, especially via an import, + * `scala.concurrent.ExecutionContext.Implicits.global`. + * The recommended approach is to add `(implicit ec: ExecutionContext)` to methods, + * or class constructor parameters, which need an `ExecutionContext`. + * + * Then locally import a specific `ExecutionContext` in one place for the entire + * application or module, passing it implicitly to individual methods. + * Alternatively define a local implicit val with the required `ExecutionContext`. * * A custom `ExecutionContext` may be appropriate to execute code * which blocks on IO or performs long-running computations. @@ -111,9 +110,18 @@ object ExecutionContext { * The explicit global `ExecutionContext`. Invoke `global` when you want to provide the global * `ExecutionContext` explicitly. * - * The default `ExecutionContext` implementation is backed by a work-stealing thread pool. By default, - * the thread pool uses a target number of worker threads equal to the number of - * [[https://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#availableProcessors-- available processors]]. + * The default `ExecutionContext` implementation is backed by a work-stealing thread pool. + * It can be configured via the following [[scala.sys.SystemProperties]]: + * + * `scala.concurrent.context.minThreads` = defaults to "1" + * `scala.concurrent.context.numThreads` = defaults to "x1" (i.e. the current number of available processors * 1) + * `scala.concurrent.context.maxThreads` = defaults to "x1" (i.e. the current number of available processors * 1) + * `scala.concurrent.context.maxExtraThreads` = defaults to "256" + * + * The pool size of threads is then `numThreads` bounded by `minThreads` on the lower end and `maxThreads` on the high end. + * + * The `maxExtraThreads` is the maximum number of extra threads to have at any given time to evade deadlock, + * see [[scala.concurrent.BlockContext]]. * * @return the global `ExecutionContext` */ diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index 667a7547ac..0695ee3351 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -22,12 +22,31 @@ import scala.annotation.implicitNotFound * == Common Imports == * * When working with Futures, you will often find that importing the whole concurrent - * package is convenient, furthermore you are likely to need an implicit ExecutionContext - * in scope for many operations involving Futures and Promises: + * package is convenient: * * {{{ * import scala.concurrent._ - * import ExecutionContext.Implicits.global + * }}} + * + * When using things like `Future`s, it is often required to have an implicit `ExecutionContext` + * in scope. The general advice for these implicits are as follows. + * + * If the code in question is a class or method definition, and no `ExecutionContext` is available, + * request one from the caller by adding an implicit parameter list: + * + * {{{ + * def myMethod(myParam: MyType)(implicit ec: ExecutionContext) = … + * //Or + * class MyClass(myParam: MyType)(implicit ec: ExecutionContext) { … } + * }}} + * + * This allows the caller of the method, or creator of the instance of the class, to decide which + * `ExecutionContext` should be used. + * + * For typical REPL usage and experimentation, importing the global `ExecutionContext` is often desired. + * + * {{{ + * import scala.concurrent.ExcutionContext.Implicits.global * }}} * * == Specifying Durations == @@ -140,17 +159,20 @@ package concurrent { /** * `Await` is what is used to ensure proper handling of blocking for `Awaitable` instances. * - * While occasionally useful, e.g. for testing, it is recommended that you avoid Await - * when possible in favor of callbacks and combinators like onComplete and use in - * for comprehensions. Await will block the thread on which it runs, and could cause - * performance and deadlock issues. + * While occasionally useful, e.g. for testing, it is recommended that you avoid Await whenever possible— + * instead favoring combinators and/or callbacks. + * Await's `result` and `ready` methods will block the calling thread's execution until they return, + * which will cause performance degradation, and possibly, deadlock issues. */ object Await { /** * Await the "completed" state of an `Awaitable`. * * Although this method is blocking, the internal use of [[scala.concurrent.blocking blocking]] ensures that - * the underlying [[ExecutionContext]] is prepared to properly manage the blocking. + * the underlying [[ExecutionContext]] is given an opportunity to properly manage the blocking. + * + * WARNING: It is strongly discouraged to supply lengthy timeouts since the progress of the calling thread will be + * suspended—blocked—until either the `Awaitable` becomes ready or the timeout expires. * * @param awaitable * the `Awaitable` to be awaited @@ -172,7 +194,10 @@ package concurrent { * Await and return the result (of type `T`) of an `Awaitable`. * * Although this method is blocking, the internal use of [[scala.concurrent.blocking blocking]] ensures that - * the underlying [[ExecutionContext]] to properly detect blocking and ensure that there are no deadlocks. + * the underlying [[ExecutionContext]] is given an opportunity to properly manage the blocking. + * + * WARNING: It is strongly discouraged to supply lengthy timeouts since the progress of the calling thread will be + * suspended—blocked—until either the `Awaitable` has a result or the timeout expires. * * @param awaitable * the `Awaitable` to be awaited diff --git a/src/library/scala/sys/process/ProcessBuilder.scala b/src/library/scala/sys/process/ProcessBuilder.scala index b7966b0341..d0745e5833 100644 --- a/src/library/scala/sys/process/ProcessBuilder.scala +++ b/src/library/scala/sys/process/ProcessBuilder.scala @@ -257,10 +257,9 @@ trait ProcessBuilder extends Source with Sink { */ def run(connectInput: Boolean): Process - /** Starts the process represented by this builder, blocks until it exits, and - * returns the exit code. Standard output and error are sent to the given - * ProcessLogger. The newly started process reads from standard input of the - * current process if `connectInput` is true. + /** Starts the process represented by this builder. Standard output and error + * are sent to the given ProcessLogger. The newly started process reads from + * standard input of the current process if `connectInput` is true. */ def run(log: ProcessLogger, connectInput: Boolean): Process diff --git a/src/library/scala/util/Either.scala b/src/library/scala/util/Either.scala index 523c10c483..d295478698 100644 --- a/src/library/scala/util/Either.scala +++ b/src/library/scala/util/Either.scala @@ -6,54 +6,52 @@ ** |/ ** \* */ - - package scala package util /** Represents a value of one of two possible types (a disjoint union.) - * An instance of Either is either an instance of [[scala.util.Left]] or [[scala.util.Right]]. + * An instance of `Either` is an instance of either [[scala.util.Left]] or [[scala.util.Right]]. * - * A common use of Either is as an alternative to [[scala.Option]] for dealing - * with possible missing values. In this usage, [[scala.None]] is replaced + * A common use of `Either` is as an alternative to [[scala.Option]] for dealing + * with possibly missing values. In this usage, [[scala.None]] is replaced * with a [[scala.util.Left]] which can contain useful information. * [[scala.util.Right]] takes the place of [[scala.Some]]. Convention dictates - * that Left is used for failure and Right is used for success. + * that `Left` is used for failure and `Right` is used for success. * - * For example, you could use `Either[String, Int]` to detect whether a - * received input is a String or an Int. + * For example, you could use `Either[String, Int]` to indicate whether a + * received input is a `String` or an `Int`. * * {{{ - * val in = Console.readLine("Type Either a string or an Int: ") - * val result: Either[String,Int] = try { - * Right(in.toInt) - * } catch { - * case e: Exception => - * Left(in) - * } + * import scala.io.StdIn._ + * val in = readLine("Type Either a string or an Int: ") + * val result: Either[String,Int] = + * try Right(in.toInt) + * catch { + * case e: NumberFormatException => Left(in) + * } * - * println(result match { + * result match { * case Right(x) => s"You passed me the Int: $x, which I will increment. $x + 1 = ${x+1}" * case Left(x) => s"You passed me the String: $x" - * }) + * } * }}} * - * Either is right-biased, which means that Right is assumed to be the default case to - * operate on. If it is Left, operations like map, flatMap, ... return the Left value - * unchanged: + * `Either` is right-biased, which means that `Right` is assumed to be the default case to + * operate on. If it is `Left`, operations like `map` and `flatMap` return the `Left` value unchanged: * * {{{ - * Right(12).map(_ * 2) // Right(24) - * Left(23).map(_ * 2) // Left(23) + * def doubled(i: Int) = i * 2 + * Right(42).map(doubled) // Right(84) + * Left(42).map(doubled) // Left(42) * }}} * - * As Either defines the methods `map` and `flatMap`, it can also be used in for comprehensions: + * Since `Either` defines the methods `map` and `flatMap`, it can also be used in for comprehensions: * {{{ - * val right1: Right[Double, Int] = Right(1) - * val right2 = Right(2) - * val right3 = Right(3) - * val left23: Left[Double, Int] = Left(23.0) - * val left42 = Left(42.0) + * val right1 = Right(1) : Right[Double, Int] + * val right2 = Right(2) + * val right3 = Right(3) + * val left23 = Left(23.0) : Left[Double, Int] + * val left42 = Left(42.0) * * for { * a <- right1 @@ -73,8 +71,9 @@ package util * c <- right2 * } yield a + b + c // Left(23.0) * - * // It is advisable to provide the type of the “missing” value (especially the right value for `Left`) - * // as otherwise that type might be inferred as `Nothing` without context: + * // It may be necessary to provide the type of the “missing” value, especially the type + * // of the right value for `Left`. Otherwise, without any context that constrains the type, + * // it might be inferred as `Nothing`: * for { * a <- left23 * b <- right1 @@ -92,77 +91,69 @@ package util * @since 2.7 */ sealed abstract class Either[+A, +B] extends Product with Serializable { - /** - * Projects this `Either` as a `Left`. - */ + /** Projects this `Either` as a `Left`. */ def left = Either.LeftProjection(this) - /** - * Projects this `Either` as a `Right`. + /** Projects this `Either` as a `Right`. * - * Because `Either` is right-biased, this method is not normally needed. - * (It is retained in the API for now for easy cross-compilation between Scala - * 2.11 and 2.12.) + * Because `Either` is right-biased, this method is not normally needed. */ def right = Either.RightProjection(this) - /** - * Applies `fa` if this is a `Left` or `fb` if this is a `Right`. + /** Applies `fa` if this is a `Left` or `fb` if this is a `Right`. * - * @example {{{ - * val result: Either[Exception, Value] = possiblyFailingOperation() - * log(result.fold( - * ex => s"Operation failed with $ex", - * v => s"Operation produced value: $v" - * )) - * }}} + * @example {{{ + * val result = util.Try("42".toInt).toEither + * result.fold( + * e => s"Operation failed with $e", + * v => s"Operation produced value: $v" + * ) + * }}} * - * @param fa the function to apply if this is a `Left` - * @param fb the function to apply if this is a `Right` - * @return the results of applying the function + * @param fa the function to apply if this is a `Left` + * @param fb the function to apply if this is a `Right` + * @return the results of applying the function */ def fold[C](fa: A => C, fb: B => C): C = this match { case Right(b) => fb(b) case Left(a) => fa(a) } - /** - * If this is a `Left`, then return the left value in `Right` or vice versa. + /** If this is a `Left`, then return the left value in `Right` or vice versa. * - * @example {{{ - * val left: Either[String, Int] = Left("left") - * val right: Either[Int, String] = l.swap // Result: Right("left") - * }}} - * @example {{{ - * val right = Right(2) - * val left = Left(3) - * for { - * r1 <- right - * r2 <- left.swap - * } yield r1 * r2 // Right(6) - * }}} + * @example {{{ + * val left: Either[String, Int] = Left("left") + * val right: Either[Int, String] = left.swap // Result: Right("left") + * }}} + * @example {{{ + * val right = Right(2) + * val left = Left(3) + * for { + * r1 <- right + * r2 <- left.swap + * } yield r1 * r2 // Right(6) + * }}} */ def swap: Either[B, A] = this match { case Left(a) => Right(a) case Right(b) => Left(b) } - /** - * Joins an `Either` through `Right`. + /** Joins an `Either` through `Right`. * - * This method requires that the right side of this Either is itself an - * Either type. That is, this must be some type like: {{{ - * Either[A, Either[A, C]] - * }}} (which respects the type parameter bounds, shown below.) + * This method requires that the right side of this `Either` is itself + * an `Either` type. That is, this must be some type like: {{{ + * Either[A, Either[A, C]] + * }}} (which respects the type parameter bounds, shown below.) * - * If this instance is a Right[Either[A, C]] then the contained Either[A, C] - * will be returned, otherwise this value will be returned unmodified. + * If this instance is a `Right[Either[A, C]]` then the contained `Either[A, C]` + * will be returned, otherwise this value will be returned unmodified. * - * @example {{{ - * Right[String, Either[String, Int]](Right(12)).joinRight // Result: Right(12) - * Right[String, Either[String, Int]](Left("flower")).joinRight // Result: Left("flower") - * Left[String, Either[String, Int]]("flower").joinRight // Result: Left("flower") - * }}} + * @example {{{ + * Right[String, Either[String, Int]](Right(12)).joinRight // Result: Right(12) + * Right[String, Either[String, Int]](Left("flower")).joinRight // Result: Left("flower") + * Left[String, Either[String, Int]]("flower").joinRight // Result: Left("flower") + * }}} * * This method, and `joinLeft`, are analogous to `Option#flatten` */ @@ -172,51 +163,48 @@ sealed abstract class Either[+A, +B] extends Product with Serializable { } - /** - * Joins an `Either` through `Left`. + /** Joins an `Either` through `Left`. * - * This method requires that the left side of this Either is itself an - * Either type. That is, this must be some type like: {{{ - * Either[Either[C, B], B] - * }}} (which respects the type parameter bounds, shown below.) + * This method requires that the left side of this `Either` is itself an + * `Either` type. That is, this must be some type like: {{{ + * Either[Either[C, B], B] + * }}} (which respects the type parameter bounds, shown below.) * - * If this instance is a Left[Either[C, B]] then the contained Either[C, B] - * will be returned, otherwise this value will be returned unmodified. + * If this instance is a `Left[Either[C, B]]` then the contained `Either[C, B]` + * will be returned, otherwise this value will be returned unmodified. * - * {{{ - * Left[Either[Int, String], String](Right("flower")).joinLeft // Result: Right("flower") - * Left[Either[Int, String], String](Left(12)).joinLeft // Result: Left(12) - * Right[Either[Int, String], String]("daisy").joinLeft // Result: Right("daisy") - * }}} + * {{{ + * Left[Either[Int, String], String](Right("flower")).joinLeft // Result: Right("flower") + * Left[Either[Int, String], String](Left(12)).joinLeft // Result: Left(12) + * Right[Either[Int, String], String]("daisy").joinLeft // Result: Right("daisy") + * }}} * - * This method, and `joinRight`, are analogous to `Option#flatten` + * This method, and `joinRight`, are analogous to `Option#flatten`. */ def joinLeft[A1 >: A, B1 >: B, C](implicit ev: A1 <:< Either[C, B1]): Either[C, B1] = this match { case Left(a) => a case Right(b) => this.asInstanceOf[Either[C, B1]] } - /** - * Executes the given side-effecting function if this is a `Right`. + /** Executes the given side-effecting function if this is a `Right`. * - * {{{ - * Right(12).foreach(x => println(x)) // prints "12" - * Left(12).foreach(x => println(x)) // doesn't print - * }}} - * @param f The side-effecting function to execute. + * {{{ + * Right(12).foreach(println) // prints "12" + * Left(12).foreach(println) // doesn't print + * }}} + * @param f The side-effecting function to execute. */ def foreach[U](f: B => U): Unit = this match { case Right(b) => f(b) case Left(_) => } - /** - * Returns the value from this `Right` or the given argument if this is a `Left`. + /** Returns the value from this `Right` or the given argument if this is a `Left`. * - * {{{ - * Right(12).getOrElse(17) // 12 - * Left(12).getOrElse(17) // 17 - * }}} + * {{{ + * Right(12).getOrElse(17) // 12 + * Left(12).getOrElse(17) // 17 + * }}} */ def getOrElse[BB >: B](or: => BB): BB = this match { case Right(b) => b @@ -233,7 +221,7 @@ sealed abstract class Either[+A, +B] extends Product with Serializable { * // Returns false because value of Right is "something" which does not equal "anything". * Right("something") contains "anything" * - * // Returns false because there is no value for Right. + * // Returns false because it's not a Right value. * Left("something") contains "something" * }}} * @@ -245,53 +233,49 @@ sealed abstract class Either[+A, +B] extends Product with Serializable { case Left(_) => false } - /** - * Returns `true` if `Left` or returns the result of the application of - * the given predicate to the `Right` value. + /** Returns `true` if `Left` or returns the result of the application of + * the given predicate to the `Right` value. * - * {{{ - * Right(12).forall(_ > 10) // true - * Right(7).forall(_ > 10) // false - * Left(12).forall(_ > 10) // true - * }}} + * {{{ + * Right(12).forall(_ > 10) // true + * Right(7).forall(_ > 10) // false + * Left(12).forall(_ => false) // true + * }}} */ def forall(f: B => Boolean): Boolean = this match { case Right(b) => f(b) case Left(_) => true } - /** - * Returns `false` if `Left` or returns the result of the application of - * the given predicate to the `Right` value. + /** Returns `false` if `Left` or returns the result of the application of + * the given predicate to the `Right` value. * - * {{{ - * Right(12).exists(_ > 10) // true - * Right(7).exists(_ > 10) // false - * Left(12).exists(_ > 10) // false - * }}} + * {{{ + * Right(12).exists(_ > 10) // true + * Right(7).exists(_ > 10) // false + * Left(12).exists(_ => true) // false + * }}} */ def exists(p: B => Boolean): Boolean = this match { case Right(b) => p(b) case Left(_) => false } - /** - * Binds the given function across `Right`. + /** Binds the given function across `Right`. * - * @param f The function to bind across `Right`. + * @param f The function to bind across `Right`. */ def flatMap[AA >: A, Y](f: B => Either[AA, Y]): Either[AA, Y] = this match { case Right(b) => f(b) case Left(a) => this.asInstanceOf[Either[AA, Y]] } - /** - * The given function is applied if this is a `Right`. + /** The given function is applied if this is a `Right`. * - * {{{ - * Right(12).map(x => "flower") // Result: Right("flower") - * Left(12).map(x => "flower") // Result: Left(12) - * }}} + * {{{ + * Right(12).map(x => "flower") // Result: Right("flower") + * Left(12).map(x => "flower") // Result: Left(12) + * }}} */ def map[Y](f: B => Y): Either[A, Y] = this match { case Right(b) => Right(f(b)) @@ -303,9 +287,9 @@ sealed abstract class Either[+A, +B] extends Product with Serializable { * returns `Left` with the existing value of `Left` if this is a `Left`. * * {{{ - * Right(12).filterOrElse(_ > 10, -1) // Right(12) - * Right(7).filterOrElse(_ > 10, -1) // Left(-1) - * Left(12).filterOrElse(_ > 10, -1) // Left(12) + * Right(12).filterOrElse(_ > 10, -1) // Right(12) + * Right(7).filterOrElse(_ > 10, -1) // Left(-1) + * Left(7).filterOrElse(_ => false, -1) // Left(7) * }}} */ def filterOrElse[AA >: A](p: B => Boolean, zero: => AA): Either[AA, B] = this match { @@ -344,32 +328,29 @@ sealed abstract class Either[+A, +B] extends Product with Serializable { case Left(a) => Failure(a) } - /** - * Returns `true` if this is a `Left`, `false` otherwise. + /** Returns `true` if this is a `Left`, `false` otherwise. * - * {{{ - * Left("tulip").isLeft // true - * Right("venus fly-trap").isLeft // false - * }}} + * {{{ + * Left("tulip").isLeft // true + * Right("venus fly-trap").isLeft // false + * }}} */ def isLeft: Boolean - /** - * Returns `true` if this is a `Right`, `false` otherwise. + /** Returns `true` if this is a `Right`, `false` otherwise. * - * {{{ - * Left("tulip").isRight // false - * Right("venus fly-trap").isRight // true - * }}} + * {{{ + * Left("tulip").isRight // false + * Right("venus fly-trap").isRight // true + * }}} */ def isRight: Boolean } -/** - * The left side of the disjoint union, as opposed to the [[scala.util.Right]] side. +/** The left side of the disjoint union, as opposed to the [[scala.util.Right]] side. * - * @author <a href="mailto:research@workingmouse.com">Tony Morris</a>, Workingmouse - * @version 1.0, 11/10/2008 + * @author <a href="mailto:research@workingmouse.com">Tony Morris</a>, Workingmouse + * @version 1.0, 11/10/2008 */ final case class Left[+A, +B](@deprecatedName('a, "2.12.0") value: A) extends Either[A, B] { def isLeft = true @@ -378,11 +359,10 @@ final case class Left[+A, +B](@deprecatedName('a, "2.12.0") value: A) extends Ei @deprecated("Use .value instead.", "2.12.0") def a: A = value } -/** - * The right side of the disjoint union, as opposed to the [[scala.util.Left]] side. +/** The right side of the disjoint union, as opposed to the [[scala.util.Left]] side. * - * @author <a href="mailto:research@workingmouse.com">Tony Morris</a>, Workingmouse - * @version 1.0, 11/10/2008 + * @author <a href="mailto:research@workingmouse.com">Tony Morris</a>, Workingmouse + * @version 1.0, 11/10/2008 */ final case class Right[+A, +B](@deprecatedName('b, "2.12.0") value: B) extends Either[A, B] { def isLeft = false @@ -396,27 +376,26 @@ object Either { /** If the condition is satisfied, return the given `B` in `Right`, * otherwise, return the given `A` in `Left`. * - * {{{ - * val userInput: String = ... - * Either.cond( - * userInput.forall(_.isDigit) && userInput.size == 10, - * PhoneNumber(userInput), - * "The input (%s) does not look like a phone number".format(userInput) - * }}} + * {{{ + * val userInput: String = ... + * Either.cond( + * userInput.forall(_.isDigit) && userInput.size == 10, + * PhoneNumber(userInput), + * "The input (%s) does not look like a phone number".format(userInput) + * }}} */ def cond[X, Y](test: Boolean, right: => Y, left: => X): Either[X, Y] = if (test) Right(right) else Left(left) - /** - * Allows use of a `merge` method to extract values from Either instances - * regardless of whether they are Left or Right. + /** Allows use of a `merge` method to extract values from Either instances + * regardless of whether they are Left or Right. * - * {{{ - * val l = Left(List(1)): Either[List[Int], Vector[Int]] - * val r = Right(Vector(1)): Either[List[Int], Vector[Int]] - * l.merge: Seq[Int] // List(1) - * r.merge: Seq[Int] // Vector(1) - * }}} + * {{{ + * val l = Left(List(1)): Either[List[Int], Vector[Int]] + * val r = Right(Vector(1)): Either[List[Int], Vector[Int]] + * l.merge: Seq[Int] // List(1) + * r.merge: Seq[Int] // Vector(1) + * }}} */ implicit class MergeableEither[A](private val x: Either[A, A]) extends AnyVal { def merge: A = x match { @@ -425,65 +404,58 @@ object Either { } } - /** - * Projects an `Either` into a `Left`. - * - * This allows for-comprehensions over the left side of Either instances, - * reversing Either's usual right-bias. + /** Projects an `Either` into a `Left`. * - * For example {{{ - * for (s <- Left("flower").left) yield s.length // Left(6) - * }}} + * This allows for-comprehensions over the left side of Either instances, + * reversing Either's usual right-bias. * - * Continuing the analogy with [[scala.Option]], a `LeftProjection` declares - * that `Left` should be analogous to `Some` in some code. + * For example {{{ + * for (s <- Left("flower").left) yield s.length // Left(6) + * }}} * - * {{{ - * // using Option: - * def interactWithDB(x: Query): Option[Result] = - * try { - * Some(getResultFromDatabase(x)) - * } catch { - * case ex => None - * } + * Continuing the analogy with [[scala.Option]], a `LeftProjection` declares + * that `Left` should be analogous to `Some` in some code. * - * // this will only be executed if interactWithDB returns a Some - * val report = - * for (r <- interactWithDB(someQuery)) yield generateReport(r) - * if (report.isDefined) - * send(report) - * else - * log("report not generated, not sure why...") - * }}} + * {{{ + * // using Option: + * def interactWithDB(x: Query): Option[Result] = + * try Some(getResultFromDatabase(x)) + * catch { + * case _: SQLException => None + * } + * + * // this will only be executed if interactWithDB returns a Some + * val report = for (result <- interactWithDB(someQuery)) yield generateReport(result) + * report match { + * case Some(r) => send(r) + * case None => log("report not generated, not sure why...") + * }}} * - * {{{ - * // using Either - * def interactWithDB(x: Query): Either[Exception, Result] = - * try { - * Right(getResultFromDatabase(x)) - * } catch { - * case ex => Left(ex) + * {{{ + * // using Either + * def interactWithDB(x: Query): Either[Exception, Result] = + * try Right(getResultFromDatabase(x)) + * catch { + * case e: SQLException => Left(e) + * } + * + * // this will only be executed if interactWithDB returns a Right + * val report = for (result <- interactWithDB(someQuery).right) yield generateReport(result) + * report match { + * case Right(r) => send(r) + * case Left(e) => log(s"report not generated, reason was $e") * } + * }}} * - * // this will only be executed if interactWithDB returns a Right - * val report = - * for (r <- interactWithDB(someQuery).right) yield generateReport(r) - * if (report.isRight) - * send(report) - * else - * log("report not generated, reason was " + report.left.get) - * }}} - * - * @author <a href="mailto:research@workingmouse.com">Tony Morris</a>, Workingmouse - * @version 1.0, 11/10/2008 + * @author <a href="mailto:research@workingmouse.com">Tony Morris</a>, Workingmouse + * @version 1.0, 11/10/2008 */ final case class LeftProjection[+A, +B](e: Either[A, B]) { - /** - * Returns the value from this `Left` or throws `java.util.NoSuchElementException` - * if this is a `Right`. + /** Returns the value from this `Left` or throws `java.util.NoSuchElementException` + * if this is a `Right`. * * {{{ - * Left(12).left.get // 12 + * Left(12).left.get // 12 * Right(12).left.get // NoSuchElementException * }}} * @@ -494,8 +466,7 @@ object Either { case Right(_) => throw new NoSuchElementException("Either.left.get on Right") } - /** - * Executes the given side-effecting function if this is a `Left`. + /** Executes the given side-effecting function if this is a `Left`. * * {{{ * Left(12).left.foreach(x => println(x)) // prints "12" @@ -508,116 +479,105 @@ object Either { case Right(_) => } - /** - * Returns the value from this `Left` or the given argument if this is a `Right`. - * - * {{{ - * Left(12).left.getOrElse(17) // 12 - * Right(12).left.getOrElse(17) // 17 - * }}} + /** Returns the value from this `Left` or the given argument if this is a `Right`. * + * {{{ + * Left(12).left.getOrElse(17) // 12 + * Right(12).left.getOrElse(17) // 17 + * }}} */ def getOrElse[AA >: A](or: => AA): AA = e match { case Left(a) => a case Right(_) => or } - /** - * Returns `true` if `Right` or returns the result of the application of - * the given function to the `Left` value. - * - * {{{ - * Left(12).left.forall(_ > 10) // true - * Left(7).left.forall(_ > 10) // false - * Right(12).left.forall(_ > 10) // true - * }}} + /** Returns `true` if `Right` or returns the result of the application of + * the given function to the `Left` value. * + * {{{ + * Left(12).left.forall(_ > 10) // true + * Left(7).left.forall(_ > 10) // false + * Right(12).left.forall(_ > 10) // true + * }}} */ def forall(@deprecatedName('f) p: A => Boolean): Boolean = e match { case Left(a) => p(a) case Right(_) => true } - /** - * Returns `false` if `Right` or returns the result of the application of - * the given function to the `Left` value. - * - * {{{ - * Left(12).left.exists(_ > 10) // true - * Left(7).left.exists(_ > 10) // false - * Right(12).left.exists(_ > 10) // false - * }}} + /** Returns `false` if `Right` or returns the result of the application of + * the given function to the `Left` value. * + * {{{ + * Left(12).left.exists(_ > 10) // true + * Left(7).left.exists(_ > 10) // false + * Right(12).left.exists(_ > 10) // false + * }}} */ def exists(@deprecatedName('f) p: A => Boolean): Boolean = e match { case Left(a) => p(a) case Right(_) => false } - /** - * Binds the given function across `Left`. + /** Binds the given function across `Left`. * - * {{{ - * Left(12).left.flatMap(x => Left("scala")) // Left("scala") - * Right(12).left.flatMap(x => Left("scala") // Right(12) - * }}} - * @param f The function to bind across `Left`. + * {{{ + * Left(12).left.flatMap(x => Left("scala")) // Left("scala") + * Right(12).left.flatMap(x => Left("scala")) // Right(12) + * }}} + * @param f The function to bind across `Left`. */ def flatMap[BB >: B, X](f: A => Either[X, BB]): Either[X, BB] = e match { case Left(a) => f(a) case Right(b) => e.asInstanceOf[Either[X, BB]] } - /** - * Maps the function argument through `Left`. + /** Maps the function argument through `Left`. * - * {{{ - * Left(12).left.map(_ + 2) // Left(14) - * Right[Int, Int](12).left.map(_ + 2) // Right(12) - * }}} + * {{{ + * Left(12).left.map(_ + 2) // Left(14) + * Right[Int, Int](12).left.map(_ + 2) // Right(12) + * }}} */ def map[X](f: A => X): Either[X, B] = e match { case Left(a) => Left(f(a)) case Right(b) => e.asInstanceOf[Either[X, B]] } - /** - * Returns `None` if this is a `Right` or if the given predicate - * `p` does not hold for the left value, otherwise, returns a `Left`. + /** Returns `None` if this is a `Right` or if the given predicate + * `p` does not hold for the left value, otherwise, returns a `Left`. * - * {{{ - * Left(12).left.filter(_ > 10) // Some(Left(12)) - * Left(7).left.filter(_ > 10) // None - * Right(12).left.filter(_ > 10) // None - * }}} + * {{{ + * Left(12).left.filter(_ > 10) // Some(Left(12)) + * Left(7).left.filter(_ > 10) // None + * Right(12).left.filter(_ > 10) // None + * }}} */ def filter[Y](p: A => Boolean): Option[Either[A, Y]] = e match { case Left(a) => if(p(a)) Some(Left(a)) else None case Right(b) => None } - /** - * Returns a `Seq` containing the `Left` value if it exists or an empty - * `Seq` if this is a `Right`. + /** Returns a `Seq` containing the `Left` value if it exists or an empty + * `Seq` if this is a `Right`. * - * {{{ - * Left(12).left.toSeq // Seq(12) - * Right(12).left.toSeq // Seq() - * }}} + * {{{ + * Left(12).left.toSeq // Seq(12) + * Right(12).left.toSeq // Seq() + * }}} */ def toSeq: Seq[A] = e match { case Left(a) => Seq(a) case Right(_) => Seq.empty } - /** - * Returns a `Some` containing the `Left` value if it exists or a - * `None` if this is a `Right`. + /** Returns a `Some` containing the `Left` value if it exists or a + * `None` if this is a `Right`. * - * {{{ - * Left(12).left.toOption // Some(12) - * Right(12).left.toOption // None - * }}} + * {{{ + * Left(12).left.toOption // Some(12) + * Right(12).left.toOption // None + * }}} */ def toOption: Option[A] = e match { case Left(a) => Some(a) @@ -625,26 +585,24 @@ object Either { } } - /** - * Projects an `Either` into a `Right`. + /** Projects an `Either` into a `Right`. * - * Because `Either` is already right-biased, this class is not normally needed. - * (It is retained in the library for now for easy cross-compilation between Scala - * 2.11 and 2.12.) + * Because `Either` is already right-biased, this class is not normally needed. + * (It is retained in the library for now for easy cross-compilation between Scala + * 2.11 and 2.12.) * - * @author <a href="mailto:research@workingmouse.com">Tony Morris</a>, Workingmouse - * @version 1.0, 11/10/2008 + * @author <a href="mailto:research@workingmouse.com">Tony Morris</a>, Workingmouse + * @version 1.0, 11/10/2008 */ final case class RightProjection[+A, +B](e: Either[A, B]) { - /** - * Returns the value from this `Right` or throws - * `java.util.NoSuchElementException` if this is a `Left`. + /** Returns the value from this `Right` or throws + * `java.util.NoSuchElementException` if this is a `Left`. * - * {{{ - * Right(12).right.get // 12 - * Left(12).right.get // NoSuchElementException - * }}} + * {{{ + * Right(12).right.get // 12 + * Left(12).right.get // NoSuchElementException + * }}} * * @throws java.util.NoSuchElementException if the projection is `Left`. */ @@ -653,80 +611,74 @@ object Either { case Left(_) => throw new NoSuchElementException("Either.right.get on Left") } - /** - * Executes the given side-effecting function if this is a `Right`. + /** Executes the given side-effecting function if this is a `Right`. * - * {{{ - * Right(12).right.foreach(x => println(x)) // prints "12" - * Left(12).right.foreach(x => println(x)) // doesn't print - * }}} - * @param f The side-effecting function to execute. + * {{{ + * Right(12).right.foreach(x => println(x)) // prints "12" + * Left(12).right.foreach(x => println(x)) // doesn't print + * }}} + * @param f The side-effecting function to execute. */ def foreach[U](f: B => U): Unit = e match { case Right(b) => f(b) case Left(_) => } - /** - * Returns the value from this `Right` or the given argument if this is a `Left`. + /** Returns the value from this `Right` or the given argument if this is a `Left`. * - * {{{ - * Right(12).right.getOrElse(17) // 12 - * Left(12).right.getOrElse(17) // 17 - * }}} + * {{{ + * Right(12).right.getOrElse(17) // 12 + * Left(12).right.getOrElse(17) // 17 + * }}} */ def getOrElse[BB >: B](or: => BB): BB = e match { case Right(b) => b case Left(_) => or } - /** - * Returns `true` if `Left` or returns the result of the application of - * the given function to the `Right` value. + /** Returns `true` if `Left` or returns the result of the application of + * the given function to the `Right` value. * - * {{{ - * Right(12).right.forall(_ > 10) // true - * Right(7).right.forall(_ > 10) // false - * Left(12).right.forall(_ > 10) // true - * }}} + * {{{ + * Right(12).right.forall(_ > 10) // true + * Right(7).right.forall(_ > 10) // false + * Left(12).right.forall(_ > 10) // true + * }}} */ def forall(f: B => Boolean): Boolean = e match { case Right(b) => f(b) case Left(_) => true } - /** - * Returns `false` if `Left` or returns the result of the application of - * the given function to the `Right` value. + /** Returns `false` if `Left` or returns the result of the application of + * the given function to the `Right` value. * - * {{{ - * Right(12).right.exists(_ > 10) // true - * Right(7).right.exists(_ > 10) // false - * Left(12).right.exists(_ > 10) // false - * }}} + * {{{ + * Right(12).right.exists(_ > 10) // true + * Right(7).right.exists(_ > 10) // false + * Left(12).right.exists(_ > 10) // false + * }}} */ def exists(@deprecatedName('f) p: B => Boolean): Boolean = e match { case Right(b) => p(b) case Left(_) => false } - /** - * Binds the given function across `Right`. + /** Binds the given function across `Right`. * - * @param f The function to bind across `Right`. + * @param f The function to bind across `Right`. */ def flatMap[AA >: A, Y](f: B => Either[AA, Y]): Either[AA, Y] = e match { case Right(b) => f(b) case Left(a) => e.asInstanceOf[Either[AA, Y]] } - /** - * The given function is applied if this is a `Right`. + /** The given function is applied if this is a `Right`. * - * {{{ - * Right(12).right.map(x => "flower") // Result: Right("flower") - * Left(12).right.map(x => "flower") // Result: Left(12) - * }}} + * {{{ + * Right(12).right.map(x => "flower") // Result: Right("flower") + * Left(12).right.map(x => "flower") // Result: Left(12) + * }}} */ def map[Y](f: B => Y): Either[A, Y] = e match { case Right(b) => Right(f(b)) diff --git a/src/library/scala/util/matching/Regex.scala b/src/library/scala/util/matching/Regex.scala index ea9f02f85b..4822fe02b4 100644 --- a/src/library/scala/util/matching/Regex.scala +++ b/src/library/scala/util/matching/Regex.scala @@ -41,11 +41,13 @@ import java.util.regex.{ Pattern, Matcher } * implicitly for strings: * * {{{ - * val date = """(\d\d\d\d)-(\d\d)-(\d\d)""".r + * val date = raw"(\d{4})-(\d{2})-(\d{2})".r * }}} * * Since escapes are not processed in multi-line string literals, using triple quotes * avoids having to escape the backslash character, so that `"\\d"` can be written `"""\d"""`. + * The same result is achieved with certain interpolators, such as `raw"\d".r` or + * a custom interpolator `r"\d"` that also compiles the `Regex`. * * === Extraction === * To extract the capturing groups when a `Regex` is matched, use it as @@ -116,29 +118,41 @@ import java.util.regex.{ Pattern, Matcher } * while (mi.hasNext) { * val d = mi.next * if (mi.group(1).toInt < 1960) println(s"$d: An oldie but goodie.") + * } * }}} * - * Note that `findAllIn` finds matches that don't overlap. (See [[findAllIn]] for more examples.) + * Although the `MatchIterator` returned by `findAllIn` is used like any `Iterator`, + * with alternating calls to `hasNext` and `next`, `hasNext` has the additional + * side effect of advancing the underlying matcher to the next unconsumed match. + * This effect is visible in the `MatchData` representing the "current match". * * {{{ - * val num = """(\d+)""".r - * val all = num.findAllIn("123").toList // List("123"), not List("123", "23", "3") + * val r = "(ab+c)".r + * val s = "xxxabcyyyabbczzz" + * r.findAllIn(s).start // 3 + * val mi = r.findAllIn(s) + * mi.hasNext // true + * mi.start // 3 + * mi.next() // "abc" + * mi.start // 3 + * mi.hasNext // true + * mi.start // 9 + * mi.next() // "abbc" * }}} * - * Also, the "current match" of a `MatchIterator` may be advanced by either `hasNext` or `next`. - * By comparison, the `Iterator[Match]` returned by `findAllMatchIn` or `findAllIn.matchData` - * produces `Match` objects that remain valid after the iterator is advanced. + * The example shows that methods on `MatchData` such as `start` will advance to + * the first match, if necessary. It also shows that `hasNext` will advance to + * the next unconsumed match, if `next` has already returned the current match. + * + * The current `MatchData` can be captured using the `matchData` method. + * Alternatively, `findAllMatchIn` returns an `Iterator[Match]`, where there + * is no interaction between the iterator and `Match` objects it has already produced. + * + * Note that `findAllIn` finds matches that don't overlap. (See [[findAllIn]] for more examples.) * * {{{ - * val ns = num.findAllIn("1 2 3") - * ns.start // 0 - * ns.hasNext // true - * ns.start // 2 - * val ms = num.findAllMatchIn("1 2 3") - * val m = ms.next() - * m.start // 0 - * ms.hasNext // true - * m.start // still 0 + * val num = raw"(\d+)".r + * val all = num.findAllIn("123").toList // List("123"), not List("123", "23", "3") * }}} * * === Replace Text === diff --git a/src/partest-extras/scala/tools/partest/StubErrorMessageTest.scala b/src/partest-extras/scala/tools/partest/StubErrorMessageTest.scala new file mode 100644 index 0000000000..f713b79e75 --- /dev/null +++ b/src/partest-extras/scala/tools/partest/StubErrorMessageTest.scala @@ -0,0 +1,47 @@ +package scala.tools.partest + +trait StubErrorMessageTest extends StoreReporterDirectTest { + // Stub to feed to partest, unused + def code = throw new Error("Use `userCode` instead of `code`.") + + val classpath = List(sys.props("partest.lib"), testOutput.path) + .mkString(sys.props("path.separator")) + + def compileCode(codes: String*) = { + val global = newCompiler("-cp", classpath, "-d", testOutput.path) + val sourceFiles = newSources(codes: _*) + withRun(global)(_ compileSources sourceFiles) + } + + def removeClasses(inPackage: String, classNames: Seq[String]): Unit = { + val pkg = new File(testOutput.path, inPackage) + classNames.foreach { className => + val classFile = new File(pkg, s"$className.class") + assert(classFile.exists) + assert(classFile.delete()) + } + } + + def removeFromClasspath(): Unit + def codeA: String + def codeB: String + def userCode: String + def extraUserCode: String = "" + + def show(): Unit = { + compileCode(codeA) + assert(filteredInfos.isEmpty, filteredInfos) + + compileCode(codeB) + assert(filteredInfos.isEmpty, filteredInfos) + removeFromClasspath() + + if (extraUserCode == "") compileCode(userCode) + else compileCode(userCode, extraUserCode) + import scala.reflect.internal.util.Position + filteredInfos.map { report => + print(if (report.severity == storeReporter.ERROR) "error: " else "") + println(Position.formatMessage(report.pos, report.msg, true)) + } + } +} diff --git a/src/reflect/scala/reflect/internal/Depth.scala b/src/reflect/scala/reflect/internal/Depth.scala index a330e0accb..5e7202f8bf 100644 --- a/src/reflect/scala/reflect/internal/Depth.scala +++ b/src/reflect/scala/reflect/internal/Depth.scala @@ -5,7 +5,7 @@ package internal import Depth._ final class Depth private (val depth: Int) extends AnyVal with Ordered[Depth] { - def max(that: Depth): Depth = if (this < that) that else this + def max(that: Depth): Depth = if (this.depth < that.depth) that else this def decr(n: Int): Depth = if (isAnyDepth) this else Depth(depth - n) def incr(n: Int): Depth = if (isAnyDepth) this else Depth(depth + n) def decr: Depth = decr(1) diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 9d71136fc5..854849d27c 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -193,6 +193,15 @@ trait Symbols extends api.Symbols { self: SymbolTable => private[reflect] case class SymbolKind(accurate: String, sanitized: String, abbreviation: String) + protected def newStubSymbol(owner: Symbol, + name: Name, + missingMessage: String): Symbol = { + name match { + case n: TypeName => new StubClassSymbol(owner, n, missingMessage) + case _ => new StubTermSymbol(owner, name.toTermName, missingMessage) + } + } + /** The class for all symbols */ abstract class Symbol protected[Symbols] (initOwner: Symbol, initPos: Position, initName: Name) extends SymbolContextApiImpl @@ -504,9 +513,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => * failure to the point when that name is used for something, which is * often to the point of never. */ - def newStubSymbol(name: Name, missingMessage: String, isPackage: Boolean = false): Symbol = name match { - case n: TypeName => new StubClassSymbol(this, n, missingMessage) - case _ => new StubTermSymbol(this, name.toTermName, missingMessage) + def newStubSymbol(name: Name, missingMessage: String): Symbol = { + // Invoke the overriden `newStubSymbol` in Global that gives us access to typer + Symbols.this.newStubSymbol(this, name, missingMessage) } /** Given a field, construct a term symbol that represents the source construct that gave rise the field */ @@ -3427,7 +3436,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => private def fail[T](alt: T): T = { // Avoid issuing lots of redundant errors if (!hasFlag(IS_ERROR)) { - globalError(missingMessage) + globalError(pos, missingMessage) if (settings.debug.value) (new Throwable).printStackTrace diff --git a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala index b4152c9b8c..16fbab7103 100644 --- a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala +++ b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala @@ -246,14 +246,15 @@ abstract class UnPickler { adjust(mirrorThatLoaded(owner).missingHook(owner, name)) orElse { // (4) Create a stub symbol to defer hard failure a little longer. val advice = moduleAdvice(s"${owner.fullName}.$name") + val lazyCompletingSymbol = completingStack.headOption.getOrElse(NoSymbol) val missingMessage = - s"""|missing or invalid dependency detected while loading class file '$filename'. - |Could not access ${name.longString} in ${owner.kindString} ${owner.fullName}, - |because it (or its dependencies) are missing. Check your build definition for - |missing or conflicting dependencies. (Re-run with `-Ylog-classpath` to see the problematic classpath.) + s"""|Symbol '${name.nameKind} ${owner.fullName}.$name' is missing from the classpath. + |This symbol is required by '${lazyCompletingSymbol.kindString} ${lazyCompletingSymbol.fullName}'. + |Make sure that ${name.longString} is in your classpath and check for conflicting dependencies with `-Ylog-classpath`. |A full rebuild may help if '$filename' was compiled against an incompatible version of ${owner.fullName}.$advice""".stripMargin val stubName = if (tag == EXTref) name else name.toTypeName - owner.newStubSymbol(stubName, missingMessage) + // The position of the error message is set by `newStubSymbol` + NoSymbol.newStubSymbol(stubName, missingMessage) } } } @@ -696,11 +697,18 @@ abstract class UnPickler { new TypeError(e.msg) } + /** Keep track of the symbols pending to be initialized. + * + * Useful for reporting on stub errors and cyclic errors. + */ + private var completingStack = List.empty[Symbol] + /** A lazy type which when completed returns type at index `i`. */ private class LazyTypeRef(i: Int) extends LazyType with FlagAgnosticCompleter { private val definedAtRunId = currentRunId private val p = phase protected def completeInternal(sym: Symbol) : Unit = try { + completingStack = sym :: completingStack val tp = at(i, () => readType(sym.isTerm)) // after NMT_TRANSITION, revert `() => readType(sym.isTerm)` to `readType` // This is a temporary fix allowing to read classes generated by an older, buggy pickler. @@ -723,7 +731,10 @@ abstract class UnPickler { } catch { case e: MissingRequirementError => throw toTypeError(e) + } finally { + completingStack = completingStack.tail } + override def complete(sym: Symbol) : Unit = { completeInternal(sym) if (!isCompilerUniverse) markAllCompleted(sym) diff --git a/src/reflect/scala/reflect/internal/transform/RefChecks.scala b/src/reflect/scala/reflect/internal/transform/RefChecks.scala deleted file mode 100644 index e69de29bb2..0000000000 --- a/src/reflect/scala/reflect/internal/transform/RefChecks.scala +++ /dev/null diff --git a/src/reflect/scala/reflect/io/ZipArchive.scala b/src/reflect/scala/reflect/io/ZipArchive.scala index 262ab22ce9..f4e1633af4 100644 --- a/src/reflect/scala/reflect/io/ZipArchive.scala +++ b/src/reflect/scala/reflect/io/ZipArchive.scala @@ -27,6 +27,8 @@ import scala.annotation.tailrec * ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ object ZipArchive { + private[io] val closeZipFile = sys.props.get("scala.classpath.closeZip").map(_.toBoolean).getOrElse(false) + /** * @param file a File * @return A ZipArchive if `file` is a readable zip file, otherwise null. @@ -120,31 +122,69 @@ abstract class ZipArchive(override val file: JFile) extends AbstractFile with Eq } /** ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */ final class FileZipArchive(file: JFile) extends ZipArchive(file) { + private[this] def openZipFile(): ZipFile = try { + new ZipFile(file) + } catch { + case ioe: IOException => throw new IOException("Error accessing " + file.getPath, ioe) + } + + private[this] class LazyEntry( + name: String, + time: Long, + size: Int + ) extends Entry(name) { + override def lastModified: Long = time // could be stale + override def input: InputStream = { + val zipFile = openZipFile() + val entry = zipFile.getEntry(name) + val delegate = zipFile.getInputStream(entry) + new FilterInputStream(delegate) { + override def close(): Unit = { zipFile.close() } + } + } + override def sizeOption: Option[Int] = Some(size) // could be stale + } + + // keeps a file handle open to ZipFile, which forbids file mutation + // on Windows, and leaks memory on all OS (typically by stopping + // classloaders from being garbage collected). But is slightly + // faster than LazyEntry. + private[this] class LeakyEntry( + zipFile: ZipFile, + zipEntry: ZipEntry + ) extends Entry(zipEntry.getName) { + override def lastModified: Long = zipEntry.getTime + override def input: InputStream = zipFile.getInputStream(zipEntry) + override def sizeOption: Option[Int] = Some(zipEntry.getSize.toInt) + } + lazy val (root, allDirs) = { val root = new DirEntry("/") val dirs = mutable.HashMap[String, DirEntry]("/" -> root) - val zipFile = try { - new ZipFile(file) - } catch { - case ioe: IOException => throw new IOException("Error accessing " + file.getPath, ioe) - } - + val zipFile = openZipFile() val enum = zipFile.entries() - while (enum.hasMoreElements) { - val zipEntry = enum.nextElement - val dir = getDir(dirs, zipEntry) - if (zipEntry.isDirectory) dir - else { - class FileEntry() extends Entry(zipEntry.getName) { - override def getArchive = zipFile - override def lastModified = zipEntry.getTime() - override def input = getArchive getInputStream zipEntry - override def sizeOption = Some(zipEntry.getSize().toInt) + try { + while (enum.hasMoreElements) { + val zipEntry = enum.nextElement + val dir = getDir(dirs, zipEntry) + if (zipEntry.isDirectory) dir + else { + val f = + if (ZipArchive.closeZipFile) + new LazyEntry( + zipEntry.getName(), + zipEntry.getTime(), + zipEntry.getSize().toInt + ) + else + new LeakyEntry(zipFile, zipEntry) + + dir.entries(f.name) = f } - val f = new FileEntry() - dir.entries(f.name) = f } + } finally { + if (ZipArchive.closeZipFile) zipFile.close() } (root, dirs) } diff --git a/src/repl/scala/tools/nsc/interpreter/ILoop.scala b/src/repl/scala/tools/nsc/interpreter/ILoop.scala index 9635f320fe..8be4d159f1 100644 --- a/src/repl/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/repl/scala/tools/nsc/interpreter/ILoop.scala @@ -768,7 +768,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) result } - private object paste extends Pasted(prompt) { + private object paste extends Pasted(replProps.promptText) { def interpret(line: String) = intp interpret line def echo(message: String) = ILoop.this echo message diff --git a/src/repl/scala/tools/nsc/interpreter/Pasted.scala b/src/repl/scala/tools/nsc/interpreter/Pasted.scala index 3a7eda1b77..7ab5e5bb42 100644 --- a/src/repl/scala/tools/nsc/interpreter/Pasted.scala +++ b/src/repl/scala/tools/nsc/interpreter/Pasted.scala @@ -38,10 +38,9 @@ abstract class Pasted(prompt: String) { def matchesContinue(line: String) = matchesString(line, ContinueString) def running = isRunning - private def matchesString(line: String, target: String): Boolean = ( - (line startsWith target) || - (line.nonEmpty && spacey(line.head) && matchesString(line.tail, target)) - ) + private def matchesString(line: String, target: String): Boolean = + line.startsWith(target) || (line.nonEmpty && spacey(line.head) && matchesString(line.tail, target)) + private def stripString(line: String, target: String) = line indexOf target match { case -1 => line case idx => line drop (idx + target.length) |