aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2016-03-04 11:43:57 +0100
committerMartin Odersky <odersky@gmail.com>2016-03-12 16:08:36 +0100
commitc43ae4a31cac6363050ab07aa6ec1a9f0e9213b4 (patch)
treea919e11248ea3f62155a70b6aa12ab701a82a083 /src
parente68d68414fa346fad9dee204746d826ee172e861 (diff)
downloaddotty-c43ae4a31cac6363050ab07aa6ec1a9f0e9213b4.tar.gz
dotty-c43ae4a31cac6363050ab07aa6ec1a9f0e9213b4.tar.bz2
dotty-c43ae4a31cac6363050ab07aa6ec1a9f0e9213b4.zip
Add patching functionality for migration
Firs version of patching that can be invoked by dotty compiler itself.
Diffstat (limited to 'src')
-rw-r--r--src/dotty/tools/dotc/Driver.scala2
-rw-r--r--src/dotty/tools/dotc/Run.scala2
-rw-r--r--src/dotty/tools/dotc/config/ScalaSettings.scala1
-rw-r--r--src/dotty/tools/dotc/core/Types.scala2
-rw-r--r--src/dotty/tools/dotc/parsing/Parsers.scala7
-rw-r--r--src/dotty/tools/dotc/rewrite/Patches.scala97
6 files changed, 108 insertions, 3 deletions
diff --git a/src/dotty/tools/dotc/Driver.scala b/src/dotty/tools/dotc/Driver.scala
index 887274fa8..fd82934ec 100644
--- a/src/dotty/tools/dotc/Driver.scala
+++ b/src/dotty/tools/dotc/Driver.scala
@@ -5,6 +5,7 @@ import config.CompilerCommand
import core.Contexts.{Context, ContextBase}
import util.DotClass
import reporting._
+import rewrite.Patches
import scala.util.control.NonFatal
/** Run the Dotty compiler.
@@ -43,6 +44,7 @@ abstract class Driver extends DotClass {
val ctx = rootCtx.fresh
val summary = CompilerCommand.distill(args)(ctx)
ctx.setSettings(summary.sstate)
+ Patches.setup(ctx)
val fileNames = CompilerCommand.checkUsage(summary, sourcesRequired)(ctx)
(fileNames, ctx)
}
diff --git a/src/dotty/tools/dotc/Run.scala b/src/dotty/tools/dotc/Run.scala
index 9972e3e64..aba7a002d 100644
--- a/src/dotty/tools/dotc/Run.scala
+++ b/src/dotty/tools/dotc/Run.scala
@@ -8,6 +8,7 @@ import io.PlainFile
import util.{SourceFile, NoSource, Stats, SimpleMap}
import reporting.Reporter
import transform.TreeChecker
+import rewrite.Patches
import java.io.{BufferedWriter, OutputStreamWriter}
import scala.reflect.io.VirtualFile
import scala.util.control.NonFatal
@@ -64,6 +65,7 @@ class Run(comp: Compiler)(implicit ctx: Context) {
foreachUnit(printTree)
ctx.informTime(s"$phase ", start)
}
+ if (!ctx.reporter.hasErrors) Patches.writeBack()
}
private def printTree(ctx: Context) = {
diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala
index aa264329c..17da1f8d1 100644
--- a/src/dotty/tools/dotc/config/ScalaSettings.scala
+++ b/src/dotty/tools/dotc/config/ScalaSettings.scala
@@ -48,6 +48,7 @@ class ScalaSettings extends Settings.SettingGroup {
val d = StringSetting("-d", "directory|jar", "destination for generated classfiles.", ".")
val nospecialization = BooleanSetting("-no-specialization", "Ignore @specialize annotations.")
val language = MultiStringSetting("-language", "feature", "Enable one or more language features.")
+ val rewrite = BooleanSetting("-rewrite", "When used in conjunction with -language:Scala2 rewrites sources to migrate to new syntax")
/** -X "Advanced" settings
*/
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index 17c2ec4ca..3801f1914 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -2424,8 +2424,6 @@ object Types {
x => paramBounds mapConserve (_.subst(this, x).bounds),
x => resType.subst(this, x))
- // need to override hashCode and equals to be object identity
- // because paramNames by itself is not discriminatory enough
override def equals(other: Any) = other match {
case other: PolyType =>
other.paramNames == this.paramNames && other.paramBounds == this.paramBounds && other.resType == this.resType
diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala
index bb8fbe08b..47b0ae22d 100644
--- a/src/dotty/tools/dotc/parsing/Parsers.scala
+++ b/src/dotty/tools/dotc/parsing/Parsers.scala
@@ -21,6 +21,7 @@ import Constants._
import ScriptParsers._
import annotation.switch
import util.DotClass
+import rewrite.Patches.patch
object Parsers {
@@ -1762,7 +1763,11 @@ object Parsers {
*/
def defDefOrDcl(mods: Modifiers): Tree = atPos(tokenRange) {
def scala2ProcedureSyntax =
- testScala2Mode("Procedure syntax no longer supported; `: Unit =' should be inserted here")
+ testScala2Mode("Procedure syntax no longer supported; `: Unit =' should be inserted here") && {
+ patch(source, Position(in.lastOffset),
+ if (in.token == LBRACE) ": Unit =" else ": Unit ")
+ true
+ }
if (in.token == THIS) {
in.nextToken()
val vparamss = paramClauses(nme.CONSTRUCTOR)
diff --git a/src/dotty/tools/dotc/rewrite/Patches.scala b/src/dotty/tools/dotc/rewrite/Patches.scala
new file mode 100644
index 000000000..85532dad9
--- /dev/null
+++ b/src/dotty/tools/dotc/rewrite/Patches.scala
@@ -0,0 +1,97 @@
+package dotty.tools.dotc
+package rewrite
+
+import util.{SourceFile, Positions}
+import Positions.Position
+import core.Contexts.{Context, FreshContext}
+import collection.mutable
+
+object Patches {
+
+ private val PropertyTag = "rewrite-patches"
+
+ private case class Patch(pos: Position, replacement: String) {
+ def delta = replacement.length - (pos.end - pos.start)
+ }
+
+ private class PatchedFiles extends mutable.HashMap[SourceFile, Patches]
+
+ /** If `-rewrite` is set, enable patches in `ctx`. This means
+ * setting up a property in `ctx` that allows to record patches.
+ */
+ def setup(ctx: FreshContext): Unit =
+ if (ctx.settings.rewrite.value(ctx))
+ ctx.setProperty(PropertyTag, new PatchedFiles)
+
+ /** If patches are enabled in `ctx`, record a patch that replaces the range
+ * given by `pos` in `source` by `replacement`
+ */
+ def patch(source: SourceFile, pos: Position, replacement: String)(implicit ctx: Context): Unit =
+ ctx.moreProperties.get(PropertyTag) match {
+ case Some(pfs: PatchedFiles) =>
+ pfs.get(source) match {
+ case Some(ps) =>
+ ps.addPatch(pos, replacement)
+ case None =>
+ pfs(source) = new Patches(source)
+ patch(source, pos, replacement)
+ }
+ case _ =>
+ }
+
+ /** If patches are enabled in `ctx`, apply all patches
+ * and overwrite patched source files
+ */
+ def writeBack()(implicit ctx: Context) =
+ ctx.moreProperties.get(PropertyTag) match {
+ case Some(pfs: PatchedFiles) =>
+ for (source <- pfs.keys) {
+ ctx.println(s"[patched file ${source.file.path}]")
+ pfs(source).writeBack()
+ }
+ case _ =>
+ }
+}
+
+private class Patches(source: SourceFile) {
+ import Patches._
+
+ private val pbuf = new mutable.ListBuffer[Patch]()
+
+ def addPatch(pos: Position, replacement: String): Unit =
+ pbuf += Patch(pos, replacement)
+
+ def apply(cs: Array[Char]): Array[Char] = {
+ val delta = pbuf.map(_.delta).sum
+ val patches = pbuf.toList.sortBy(_.pos.start)
+ patches.iterator.sliding(2, 1).foreach(ps =>
+ assert(ps(0).pos.end <= ps(1).pos.start, s"overlapping patches: ${ps(0)} and ${ps(1)}"))
+ val ds = new Array[Char](cs.length + delta)
+ def loop(ps: List[Patch], inIdx: Int, outIdx: Int): Unit = {
+ def copy(upTo: Int): Int = {
+ val untouched = upTo - inIdx
+ Array.copy(cs, inIdx, ds, outIdx, untouched)
+ outIdx + untouched
+ }
+ ps match {
+ case patch @ Patch(pos, replacement) :: ps1 =>
+ val outNew = copy(pos.start)
+ replacement.copyToArray(ds, outNew)
+ loop(ps1, pos.end, outNew + replacement.length)
+ case Nil =>
+ val outNew = copy(cs.length)
+ assert(outNew == ds.length, s"$outNew != ${ds.length}")
+ }
+ }
+ loop(patches, 0, 0)
+ ds
+ }
+
+ def writeBack(): Unit = {
+ val out = source.file.output
+ val chars = apply(source.content)
+ val bytes = new String(chars).getBytes
+ out.write(bytes)
+ out.close()
+ }
+} \ No newline at end of file