8000 SI-6963 Add version to -Xmigration by JamesIry · Pull Request #1998 · scala/scala · GitHub
[go: up one dir, main page]

Skip to content

SI-6963 Add version to -Xmigration #1998

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/compiler/scala/tools/nsc/matching/ParallelMatching.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import symtab.Flags
import mutable.ListBuffer
import scala.annotation.elidable
import scala.language.postfixOps
import scala.tools.nsc.settings.ScalaVersion

trait ParallelMatching extends ast.TreeDSL
with MatchSupport
Expand Down Expand Up @@ -821,7 +822,7 @@ trait ParallelMatching extends ast.TreeDSL
// match that's unimportant; so we add an instance check only if there
// is a binding.
def bindingWarning() = {
if (isBound && settings.Xmigration28.value) {
if (isBound && settings.Xmigration.value < ScalaVersion.twoDotEight) {
cunit.warning(scrutTree.pos,
"A bound pattern such as 'x @ Pattern' now matches fewer cases than the same pattern with no binding.")
}
Expand Down
30 changes: 30 additions & 0 deletions src/compiler/scala/tools/nsc/settings/MutableSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ class MutableSettings(val errorFn: String => Unit)
def OutputSetting(outputDirs: OutputDirs, default: String) = add(new OutputSetting(outputDirs, default))
def PhasesSetting(name: String, descr: String, default: String = "") = add(new PhasesSetting(name, descr, default))
def StringSetting(name: String, arg: String, descr: String, default: String) = add(new StringSetting(name, arg, descr, default))
def ScalaVersionSetting(name: String, arg: String, descr: String, default: ScalaVersion) = add(new ScalaVersionSetting(name, arg, descr, default))
def PathSetting(name: String, descr: String, default: String): PathSetting = {
val prepend = StringSetting(name + "/p", "", "", "").internalOnly()
val append = StringSetting(name + "/a", "", "", "").internalOnly()
Expand Down Expand Up @@ -486,6 +487,35 @@ class MutableSettings(val errorFn: String => Unit)
withHelpSyntax(name + " <" + arg + ">")
}

/** A setting represented by a Scala version, (`default` unless set) */
class ScalaVersionSetting private[nsc](
name: String,
val arg: String,
descr: String,
default: ScalaVersion)
extends Setting(name, descr) {
import ScalaVersion._

type T = ScalaVersion
protected var v: T = NoScalaVersion

override def tryToSet(args: List[String]) = {
value = default
Some(args)
}

override def tryToSetColon(args: List[String]) = args match {
case Nil => value = default; Some(Nil)
case x :: xs => value = ScalaVersion(x, errorFn) ; Some(xs)
}

override def tryToSetFromPropertyValue(s: String) = tryToSet(List(s))

def unparse: List[String] = if (value == NoScalaVersion) Nil else List(s"${name}:${value.unparse}")

withHelpSyntax(s"${name}:<${arg}>")
}

class PathSetting private[nsc](
name: String,
descr: String,
Expand Down
3 changes: 1 addition & 2 deletions src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,7 @@ trait ScalaSettings extends AbsScalaSettings
val logFreeTerms = BooleanSetting ("-Xlog-free-terms", "Print a message when reification creates a free term.")
val logFreeTypes = BooleanSetting ("-Xlog-free-types", "Print a message when reification resorts to generating a free type.")
val maxClassfileName = IntSetting ("-Xmax-classfile-name", "Maximum filename length for generated classes", 255, Some((72, 255)), _ => None)
val Xmigration28 = BooleanSetting ("-Xmigration", "Warn about constructs whose behavior may have changed between 2.7 and 2.8.").
withDeprecationMessage("This setting is no longer useful and will be removed. Please remove it from your build.")
val Xmigration = ScalaVersionSetting("-Xmigration", "version", "Warn about constructs whose behavior may have changed since version.", AnyScalaVersion)
val nouescape = BooleanSetting ("-Xno-uescape", "Disable handling of \\u unicode escapes.")
val Xnojline = BooleanSetting ("-Xnojline", "Do not use JLine for editing.")
val Xverify = BooleanSetting ("-Xverify", "Verify generic signatures in generated bytecode (asm backend only.)")
Expand Down
194 changes: 194 additions & 0 deletions src/compiler/scala/tools/nsc/settings/ScalaVersion.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/* NSC -- new Scala compiler
* Copyright 2005-2013 LAMP/EPFL
* @author James Iry
*/
// $Id$

package scala.tools.nsc.settings

/**
* Represents a single Scala version in a manner that
* supports easy comparison and sorting.
*/
abstract class ScalaVersion extends Ordered[ScalaVersion] {
def unparse: String
}

/**
* A scala version that sorts higher than all actual versions
*/
case object NoScalaVersion extends ScalaVersion {
def unparse = "none"

def compare(that: ScalaVersion): Int = that match {
case NoScalaVersion => 0
case _ => 1
}
}

/**
* A specific Scala version, not one of the magic min/max versions. An SpecificScalaVersion
* may or may not be a released version - i.e. this same class is used to represent
* final, release candidate, milestone, and development builds. The build argument is used
* to segregate builds
*/
case class SpecificScalaVersion(major: Int, minor: Int, rev: Int, build: ScalaBuild) extends ScalaVersion {
def unparse = s"${major}.${minor}.${rev}.${build.unparse}"

def compare(that: ScalaVersion): Int = that match {
case SpecificScalaVersion(thatMajor, thatMinor, thatRev, thatBuild) =>
// this could be done more cleanly by importing scala.math.Ordering.Implicits, but we have to do these
// comparisons a lot so I'm using brute force direct style code
if (major < thatMajor) -1
else if (major > thatMajor) 1
else if (minor < thatMinor) -1
else if (minor > thatMinor) 1
else if (rev < thatRev) -1
else if (rev > thatRev) 1
else build compare thatBuild
case AnyScalaVersion => 1
case NoScalaVersion => -1
}
}

/**
* A Scala version that sorts lower than all actual versions
*/
case object AnyScalaVersion extends ScalaVersion {
def unparse = "any"

def compare(that: ScalaVersion): Int = that match {
case AnyScalaVersion => 0
case _ => -1
}
}

/**
* Factory methods for producing ScalaVersions
*/
object ScalaVersion {
private val dot = "\\."
private val dash = "\\-"
private def not(s:String) = s"[^${s}]"
private val R = s"((${not(dot)}*)(${dot}(${not(dot)}*)(${dot}(${not(dash)}*)(${dash}(.*))?)?)?)".r

def apply(versionString : String, errorHandler: String => Unit): ScalaVersion = {
def errorAndValue() = {
errorHandler(
s"There was a problem parsing ${versionString}. " +
"Versions should be in the form major[.minor[.revision]] " +
"where each part is a positive number, as in 2.10.1. " +
"The minor and revision parts are optional."
)
AnyScalaVersion
}

def toInt(s: String) = s match {
case null | "" => 0
case _ => s.toInt
}

def isInt(s: String) = util.Try(toInt(s)).isSuccess

def toBuild(s: String) = s match {
case null | "FINAL" => Final
case s if (s.toUpperCase.startsWith("RC") && isInt(s.substring(2))) => RC(toInt(s.substring(2)))
case s if (s.toUpperCase.startsWith("M") && isInt(s.substring(1))) => Milestone(toInt(s.substring(1)))
case _ => Development(s)
}

try versionString match {
case "none" => NoScalaVersion
case "any" => AnyScalaVersion
case R(_, majorS, _, minorS, _, revS, _, buildS) =>
SpecificScalaVersion(toInt(majorS), toInt(minorS), toInt(revS), toBuild(buildS))
case _ =>
errorAndValue()
} catch {
case e: NumberFormatException => errorAndValue()
}
}

def apply(versionString: String): ScalaVersion =
apply(versionString, msg => throw new NumberFormatException(msg))

/**
* The version of the compiler running now
*/
val current = apply(util.Properties.versionNumberString)

/**
* The 2.8.0 version.
*/
val twoDotEight = SpecificScalaVersion(2, 8, 0, Final)
}

/**
* Represents the data after the dash in major.minor.rev-build
*/
abstract class ScalaBuild extends Ordered[ScalaBuild] {
/**
* Return a version of this build information that can be parsed back into the
* same ScalaBuild
*/
def unparse: String
}
/**
* A development, test, nightly, snapshot or other "unofficial" build
*/
case class Development(id: String) extends ScalaBuild {
F438 def unparse = s"-${id}"

def compare(that: ScalaBuild) = that match {
// sorting two development builds based on id is reasonably valid for two versions created with the same schema
// otherwise it's not correct, but since it's impossible to put a total ordering on development build versions
// this is a pragmatic compromise
case Development(thatId) => id compare thatId
// assume a development build is newer than anything else, that's not really true, but good luck
// mapping development build versions to other build types
case _ => 1
}
}
/**
* A final final
*/
case object Final extends ScalaBuild {
def unparse = ""

def compare(that: ScalaBuild) = that match {
case Final => 0
// a final is newer than anything other than a development build or another final
case Development(_) => -1
case _ => 1
}
}

/**
* A candidate for final release
*/
case class RC(n: Int) extends ScalaBuild {
def unparse = s"-RC${n}"

def compare(that: ScalaBuild) = that match {
// compare two rcs based on their RC numbers
case RC(thatN) => n - thatN
// an rc is older than anything other than a milestone or another rc
case Milestone(_) => 1
case _ => -1
}
}

/**
* An intermediate release
*/
case class Milestone(n: Int) extends ScalaBuild {
def unparse = s"-M${n}"

def compare(that: ScalaBuild) = that match {
// compare two milestones based on their milestone numbers
case Milestone(thatN) => n - thatN
// a milestone is older than anything other than another milestone
case _ => -1

}
}
3 changes: 2 additions & 1 deletion src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import symtab._
import Flags.{ CASE => _, _ }
import scala.collection.mutable.ListBuffer
import matching.{ Patterns, ParallelMatching }
import scala.tools.nsc.settings.ScalaVersion

/** This class ...
*
Expand Down Expand Up @@ -553,7 +554,7 @@ abstract class ExplicitOuter extends InfoTransform
}

case _ =>
if (settings.Xmigration28.value) tree match {
if (settings.Xmigration.value < ScalaVersion.twoDotEight) tree match {
case TypeApply(fn @ Select(qual, _), args) if fn.symbol == Object_isInstanceOf || fn.symbol == Any_isInstanceOf =>
if (isArraySeqTest(qual.tpe, args.head.tpe))
unit.warning(tree.pos, "An Array will no longer match as Seq[_].")
Expand Down
21 changes: 16 additions & 5 deletions src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import scala.collection.{ mutable, immutable }
import transform.InfoTransform
import scala.collection.mutable.ListBuffer
import scala.language.postfixOps
import scala.tools.nsc.settings.ScalaVersion
import scala.tools.nsc.settings.AnyScalaVersion
import scala.tools.nsc.settings.NoScalaVersion

/** <p>
* Post-attribution checking and transformation.
Expand Down Expand Up @@ -1378,10 +1381,18 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
* indicating it has changed semantics between versions.
*/
private def checkMigration(sym: Symbol, pos: Position) = {
if (sym.hasMigrationAnnotation)
unit.warning(pos, "%s has changed semantics in version %s:\n%s".format(
sym.fullLocationString, sym.migrationVersion.get, sym.migrationMessage.get)
)
if (sym.hasMigrationAnnotation) {
val changed = try
settings.Xmigration.value < ScalaVersion(sym.migrationVersion.get)
catch {
case e : NumberFormatException =>
unit.warning(pos, s"${sym.fullLocationString} has an unparsable version number: ${e.getMessage()}")
// if we can't parse the format on the migration annotation just conservatively assume it changed
true
}
if (changed)
unit.warning(pos, s"${sym.fullLocationString} has changed semantics in version ${sym.migrationVersion.get}:\n${sym.migrationMessage.get}")
}
}

private def checkCompileTimeOnly(sym: Symbol, pos: Position) = {
Expand Down Expand Up @@ -1587,7 +1598,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
* arbitrarily choose one as more important than the other.
*/
checkDeprecated(sym, tree.pos)
if (settings.Xmigration28.value)
if(settings.Xmigration.value != NoScalaVersion)
checkMigration(sym, tree.pos)
checkCompileTimeOnly(sym, tree.pos)

Expand Down
3 changes: 2 additions & 1 deletion src/library/scala/annotation/migration.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ package scala.annotation
* order between Scala 2.7 and 2.8.
*
* @param message A message describing the change, which is emitted
* by the compiler if the flag `-Xmigration` is set.
* by the compiler if the flag `-Xmigration` indicates a version
* prior to the changedIn version.
*
* @param changedIn The version, in which the behaviour change was
* introduced.
Expand Down
2 changes: 0 additions & 2 deletions test/files/neg/t6963.check

This file was deleted.

1 change: 0 additions & 1 deletion test/files/neg/t6963.flags

This file was deleted.

3 changes: 0 additions & 3 deletions test/files/neg/t6963.scala

This file was deleted.

5 changes: 5 additions & 0 deletions test/files/neg/t6963a.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
t6963a.scala:4: error: method scanRight in trait TraversableLike has changed semantics in version 2.9.0:
The behavior of `scanRight` has changed. The previous behavior can be reproduced with scanRight.reverse.
List(1,2,3,4,5).scanRight(0)(_+_)
^
one error found
1 change: 1 addition & 0 deletions test/files/neg/t6963a.flags
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-Xfatal-warnings -Xmigration:2.7
5 changes: 5 additions & 0 deletions test/files/neg/t6963a.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
object Test {
import scala.collection.mutable._

List(1,2,3,4,5).scanRight(0)(_+_)
}
13 changes: 13 additions & 0 deletions test/files/neg/t6963b.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
t6963b.scala:2: error: An Array will no longer match as Seq[_].
def f1(x: Any) = x.isInstanceOf[Seq[_]]
^
t6963b.scala:4: error: An Array will no longer match as Seq[_].
case _: Seq[_] => true
^
t6963b.scala:16: error: An Array will no longer match as Seq[_].
case (Some(_: Seq[_]), Nil, _) => 1
^
t6963b.scala:17: error: An Array will no longer match as Seq[_].
case (None, List(_: List[_], _), _) => 2
^
four errors found
1 change: 1 addition & 0 deletions test/files/neg/t6963b.flags
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-Xmigration:2.7 -Xfatal-warnings
20 changes: 20 additions & 0 deletions test/files/neg/t6963b.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
object Test {
def f1(x: Any) = x.isInstanceOf[Seq[_]]
def f2(x: Any) = x match {
case _: Seq[_] => true
case _ => false
}

def f3(x: Any) = x match {
case _: Array[_] => true
case _ => false
}

def f4(x: Any) = x.isInstanceOf[Traversable[_]]

def f5(x1: Any, x2: Any, x3: AnyRef) = (x1, x2, x3) match {
case (Some(_: Seq[_]), Nil, _) => 1
case (None, List(_: List[_], _), _) => 2
case _ => 3
}
}
1 change: 1 addition & 0 deletions test/files/pos/t6963c.flags
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-Xmigration:2.9 -Xfatal-warnings
Loading
0