summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2010-03-05 06:01:19 +0000
committerPaul Phillips <paulp@improving.org>2010-03-05 06:01:19 +0000
commit4e7fd5ce080a42fb4c6eeba5f8a005bd973d6c8e (patch)
treea44c5ed902c1abc93303c9cdfb162d2c9b97e364 /src
parent98c87462f7ffcc14dc4fbab9df586a200b77428b (diff)
downloadscala-4e7fd5ce080a42fb4c6eeba5f8a005bd973d6c8e.tar.gz
scala-4e7fd5ce080a42fb4c6eeba5f8a005bd973d6c8e.tar.bz2
scala-4e7fd5ce080a42fb4c6eeba5f8a005bd973d6c8e.zip
Added -Xmigration option and @migration annotat...
Added -Xmigration option and @migration annotation. At present it will warn about the following changes from 2.7 to 2.8: Stack iterator order reversed mutable.Set.map returns a Set and thus discards duplicates A case 'x @ Pattern' matches differently than 'Pattern' Review by odersky.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/Settings.scala1
-rw-r--r--src/compiler/scala/tools/nsc/matching/Matrix.scala2
-rw-r--r--src/compiler/scala/tools/nsc/matching/ParallelMatching.scala9
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Definitions.scala1
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Symbols.scala32
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala16
-rw-r--r--src/library/scala/annotation/migration.scala28
-rw-r--r--src/library/scala/collection/mutable/SetLike.scala4
-rw-r--r--src/library/scala/collection/mutable/Stack.scala15
9 files changed, 83 insertions, 25 deletions
diff --git a/src/compiler/scala/tools/nsc/Settings.scala b/src/compiler/scala/tools/nsc/Settings.scala
index 2d88b38777..1740844110 100644
--- a/src/compiler/scala/tools/nsc/Settings.scala
+++ b/src/compiler/scala/tools/nsc/Settings.scala
@@ -877,6 +877,7 @@ trait ScalacSettings {
val future = BooleanSetting ("-Xfuture", "Turn on future language features")
val genPhaseGraph = StringSetting ("-Xgenerate-phase-graph", "file", "Generate the phase graphs (outputs .dot files) to fileX.dot", "")
val XlogImplicits = BooleanSetting ("-Xlog-implicits", "Show more info on why some implicits are not applicable")
+ val Xmigration28 = BooleanSetting ("-Xmigration", "Warn about constructs whose behavior may have changed between 2.7 and 2.8")
val nouescape = BooleanSetting ("-Xno-uescape", "Disables handling of \\u unicode escapes")
val Xnojline = BooleanSetting ("-Xnojline", "Do not use JLine for editing")
val plugin = MultiStringSetting("-Xplugin", "file", "Load a plugin from a file")
diff --git a/src/compiler/scala/tools/nsc/matching/Matrix.scala b/src/compiler/scala/tools/nsc/matching/Matrix.scala
index 8464ecc46e..88507efa2f 100644
--- a/src/compiler/scala/tools/nsc/matching/Matrix.scala
+++ b/src/compiler/scala/tools/nsc/matching/Matrix.scala
@@ -156,7 +156,7 @@ trait Matrix extends MatrixAdditions {
def tpe = valsym.tpe
lazy val ident = ID(lhs)
- lazy val valDef = tracing("typedVal", typer typedValDef (VAL(lhs) === rhs))
+ lazy val valDef = tracing("typedVal", typer typedValDef (VAL(lhs) === rhs) setPos lhs.pos)
override def toString() = "%s: %s = %s".format(lhs, lhs.info, rhs)
}
diff --git a/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala b/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala
index 79812474ee..4a000b1901 100644
--- a/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala
+++ b/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala
@@ -95,7 +95,7 @@ trait ParallelMatching extends ast.TreeDSL
def sym = pv.sym
def tpe = sym.tpe
def pos = sym.pos
- def id = ID(sym) // attributed ident
+ def id = ID(sym) setPos pos // attributed ident
def accessors = if (isCaseClass) sym.caseFieldAccessors else Nil
def accessorTypes = accessors map (x => (tpe memberType x).resultType)
@@ -830,7 +830,12 @@ trait ParallelMatching extends ast.TreeDSL
// type, but if the value doesn't appear on the right hand side of the
// match that's unimportant; so we add an instance check only if there
// is a binding.
- if (isBound) eqTest AND (scrutTree IS tpe.widen)
+ if (isBound) {
+ if (settings.Xmigration28.value) {
+ cunit.warning(scrutTree.pos, "A bound pattern such as 'x @ Pattern' now matches fewer cases than the same pattern with no binding.")
+ }
+ eqTest AND (scrutTree IS tpe.widen)
+ }
else eqTest
case _ if scrutTree.tpe <:< tpe && tpe.isAnyRef => scrutTree OBJ_!= NULL
diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
index edf2e49f5a..d540b67c82 100644
--- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
@@ -437,6 +437,7 @@ trait Definitions extends reflect.generic.StandardDefinitions {
// special attributes
lazy val SerializableAttr: Symbol = getClass("scala.serializable")
lazy val DeprecatedAttr: Symbol = getClass("scala.deprecated")
+ lazy val MigrationAnnotationClass: Symbol = getClass("scala.annotation.migration")
lazy val BeanPropertyAttr: Symbol = getClass(sn.BeanProperty)
lazy val BooleanBeanPropertyAttr: Symbol = getClass(sn.BooleanBeanProperty)
diff --git a/src/compiler/scala/tools/nsc/symtab/Symbols.scala b/src/compiler/scala/tools/nsc/symtab/Symbols.scala
index 021478a23d..dfdab4189f 100644
--- a/src/compiler/scala/tools/nsc/symtab/Symbols.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Symbols.scala
@@ -133,6 +133,14 @@ trait Symbols extends reflect.generic.Symbols { self: SymbolTable =>
def getAnnotation(cls: Symbol): Option[AnnotationInfo] =
annotations find (_.atp.typeSymbol == cls)
+ /** Finds the requested annotation and returns Some(Tree) containing
+ * the argument at position 'index', or None if either the annotation
+ * or the index does not exist.
+ */
+ private def getAnnotationArg(cls: Symbol, index: Int) =
+ for (AnnotationInfo(_, args, _) <- getAnnotation(cls) ; if args.size > index) yield
+ args(index)
+
/** Remove all annotations matching the given class. */
def removeAnnotation(cls: Symbol): Unit =
setAnnotations(annotations filterNot (_.atp.typeSymbol == cls))
@@ -446,26 +454,10 @@ trait Symbols extends reflect.generic.Symbols { self: SymbolTable =>
}
}
- def isDeprecated = hasAnnotation(DeprecatedAttr)
- def deprecationMessage: Option[String] =
- annotations find (_.atp.typeSymbol == DeprecatedAttr) flatMap { annot =>
- annot.args match {
- case Literal(const) :: Nil =>
- Some(const.stringValue)
- case _ =>
- None
- }
- }
- def elisionLevel: Option[Int] = {
- if (!hasAnnotation(ElidableMethodClass)) None
- else annotations find (_.atp.typeSymbol == ElidableMethodClass) flatMap { annot =>
- // since we default to enabled by default, only look hard for falsity
- annot.args match {
- case Literal(Constant(x: Int)) :: Nil => Some(x)
- case _ => None
- }
- }
- }
+ def isDeprecated = hasAnnotation(DeprecatedAttr)
+ def deprecationMessage = getAnnotationArg(DeprecatedAttr, 0) partialMap { case Literal(const) => const.stringValue }
+ def migrationMessage = getAnnotationArg(MigrationAnnotationClass, 2) partialMap { case Literal(const) => const.stringValue }
+ def elisionLevel = getAnnotationArg(ElidableMethodClass, 0) partialMap { case Literal(Constant(x: Int)) => x }
/** Does this symbol denote a wrapper object of the interpreter or its class? */
final def isInterpreterWrapper =
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index 55500c7f17..28eebdc033 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -927,6 +927,13 @@ abstract class RefChecks extends InfoTransform {
unit.deprecationWarning(pos, msg)
}
}
+ /** Similar to deprecation: check if the symbol is marked with @migration
+ * indicating it has changed semantics between versions.
+ */
+ private def checkMigration(sym: Symbol, pos: Position) =
+ for (msg <- sym.migrationMessage)
+ unit.warning(pos, "%s%s has changed semantics:\n %s".format(sym, sym.locationString, msg))
+
/** Check that a deprecated val or def does not override a
* concrete, non-deprecated method. If it does, then
* deprecation is meaningless.
@@ -1027,7 +1034,16 @@ abstract class RefChecks extends InfoTransform {
private def transformSelect(tree: Select): Tree = {
val Select(qual, name) = tree
val sym = tree.symbol
+
+ /** Note: if a symbol has both @deprecated and @migration annotations and both
+ * warnings are enabled, only the first one checked here will be emitted.
+ * I assume that's a consequence of some code trying to avoid noise by suppressing
+ * warnings after the first, but I think it'd be better if we didn't have to
+ * arbitrarily choose one as more important than the other.
+ */
checkDeprecated(sym, tree.pos)
+ if (settings.Xmigration28.value)
+ checkMigration(sym, tree.pos)
if (currentClass != sym.owner && (sym hasFlag LOCAL)) {
var o = currentClass
diff --git a/src/library/scala/annotation/migration.scala b/src/library/scala/annotation/migration.scala
new file mode 100644
index 0000000000..b0915cde34
--- /dev/null
+++ b/src/library/scala/annotation/migration.scala
@@ -0,0 +1,28 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2002-2010, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.annotation
+
+/**
+ * An annotation that marks a member as having changed semantics
+ * between versions. This is intended for methods which for one
+ * reason or another retain the same name and type signature,
+ * but some aspect of their behavior is different. An illustrative
+ * examples is Stack.iterator, which reversed from LIFO to FIFO
+ * order between scala 2.7 and 2.8.
+ *
+ * The version numbers are to mark the scala major/minor release
+ * version where the change took place.
+ *
+ * @since 2.8
+ */
+private[scala] final class migration(
+ majorVersion: Int,
+ minorVersion: Int,
+ message: String)
+extends StaticAnnotation {}
diff --git a/src/library/scala/collection/mutable/SetLike.scala b/src/library/scala/collection/mutable/SetLike.scala
index cb6eb293c1..c7ea037f49 100644
--- a/src/library/scala/collection/mutable/SetLike.scala
+++ b/src/library/scala/collection/mutable/SetLike.scala
@@ -14,6 +14,7 @@ package mutable
import generic._
import script._
+import scala.annotation.migration
/** A template trait for mutable sets of type `mutable.Set[A]`.
* @tparam A the type of the elements of the set
@@ -63,6 +64,9 @@ trait SetLike[A, +This <: SetLike[A, This] with Set[A]]
*/
override protected[this] def newBuilder: Builder[A, This] = empty
+ @migration(2, 8, "Set.map now returns a Set, so it will discard duplicate values.")
+ override def map[B, That](f: A => B)(implicit bf: CanBuildFrom[This, B, That]): That = super.map(f)(bf)
+
/** Adds an element to this $coll.
*
* @param elem the element to be added
diff --git a/src/library/scala/collection/mutable/Stack.scala b/src/library/scala/collection/mutable/Stack.scala
index bbb4189dc3..8f626e7f33 100644
--- a/src/library/scala/collection/mutable/Stack.scala
+++ b/src/library/scala/collection/mutable/Stack.scala
@@ -15,6 +15,7 @@ package mutable
import generic._
import collection.immutable.{List, Nil}
import collection.Iterator
+import annotation.migration
/** A stack implements a data structure which allows to store and retrieve
* objects in a last-in-first-out (LIFO) fashion.
@@ -74,8 +75,13 @@ class Stack[A] private (var elems: List[A]) extends scala.collection.Seq[A] with
*/
def pushAll(elems: scala.collection.Traversable[A]): this.type = { for (elem <- elems) { push(elem); () }; this }
- @deprecated("use pushAll") def ++=(it: Iterator[A]): this.type = pushAll(it)
- @deprecated("use pushAll") def ++=(it: scala.collection.Iterable[A]): this.type = pushAll(it)
+ @deprecated("use pushAll")
+ @migration(2, 8, "Stack ++= now pushes arguments on the stack from left to right.")
+ def ++=(it: Iterator[A]): this.type = pushAll(it)
+
+ @deprecated("use pushAll")
+ @migration(2, 8, "Stack ++= now pushes arguments on the stack from left to right.")
+ def ++=(it: scala.collection.Iterable[A]): this.type = pushAll(it)
/** Returns the top element of the stack. This method will not remove
* the element from the stack. An error is signaled if there is no
@@ -112,14 +118,19 @@ class Stack[A] private (var elems: List[A]) extends scala.collection.Seq[A] with
*
* @return an iterator over all stack elements.
*/
+ @migration(2, 8, "Stack iterator and foreach now traverse in FIFO order.")
override def iterator: Iterator[A] = elems.iterator
/** Creates a list of all stack elements in LIFO order.
*
* @return the created list.
*/
+ @migration(2, 8, "Stack iterator and foreach now traverse in FIFO order.")
override def toList: List[A] = elems
+ @migration(2, 8, "Stack iterator and foreach now traverse in FIFO order.")
+ override def foreach[U](f: A => U): Unit = super.foreach(f)
+
/** This method clones the stack.
*
* @return a stack with the same elements.