Skip to content

Commit

Permalink
Merge pull request #428 from adpi2/rework-migrate-types
Browse files Browse the repository at this point in the history
Rework migrate types
  • Loading branch information
adpi2 authored Aug 31, 2023
2 parents a46c61f + d1b9681 commit 6ffd3a6
Show file tree
Hide file tree
Showing 39 changed files with 284 additions and 238 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package migrate.compiler.interfaces;

import java.nio.file.Path;

public class CompilationUnit {
public String content;
public String name;
public Path path;

public CompilationUnit(String name, String content, Path path) {
this.name = name;
this.content = content;
this.path = path;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package migrate.interfaces;
package migrate.compiler.interfaces;

import dotty.tools.dotc.core.Contexts.Context;
import dotty.tools.dotc.reporting.Diagnostic;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package migrate.interfaces;
package migrate.compiler.interfaces;

import dotty.tools.dotc.reporting.*;
import dotty.tools.dotc.core.Contexts.*;

import migrate.interfaces.Logger;

// a copy of DelegatingReporter
// https://github.com/lampepfl/dotty/blob/b9b1f2a083c4b3ef130fbb70c7da1956b144e2a1/sbt-bridge/src/xsbt/DelegatingReporter.java
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package migrate.compiler.interfaces;

import dotty.tools.io.VirtualFile;
import java.nio.file.Path;

class MigrationFile extends VirtualFile {
private Path _jpath;

public MigrationFile(String name, Path path) {
super(name, path.toString());
this._jpath = path;
}

@Override
public Path jpath() {
return _jpath;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package migrate.compiler.interfaces;

import dotty.tools.dotc.util.SourceFile;
import java.nio.file.Path;

class MigrationSourceFile extends SourceFile {
private char[] _content;

public MigrationSourceFile(String content, String name, Path path) {
super(new MigrationFile(name, path), null);
this._content = content.toCharArray();
}

@Override
public char[] content() {
return _content;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package migrate.interfaces;
package migrate.compiler.interfaces;

import dotty.tools.dotc.core.Contexts.Context;
import dotty.tools.dotc.reporting.Diagnostic;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package migrate.interfaces;
package migrate.compiler.interfaces;

import dotty.tools.dotc.Compiler;
import dotty.tools.dotc.Run;
Expand Down Expand Up @@ -26,7 +26,7 @@ public class Scala3Compiler {

private Function1<CompilationUnit, SourceFile> toSourceFile = new AbstractFunction1<CompilationUnit, SourceFile>() {
public SourceFile apply(CompilationUnit unit) {
return SourceFile$.MODULE$.virtual(unit.name, unit.content, false);
return new MigrationSourceFile(unit.content, unit.name, unit.path);
}
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package migrate.interfaces;
package migrate.compiler.interfaces;

import dotty.tools.dotc.Driver;
import dotty.tools.dotc.core.Contexts.Context;
Expand Down

This file was deleted.

16 changes: 7 additions & 9 deletions interfaces/migrate/src/main/java/migrate/interfaces/Migrate.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,16 @@ void migrateSyntax(List<Path> unmanagedSources,
List<String> scala2CompilerOptions,
Path baseDirectory);


// Todo: Maybe using ServiceLoader could simplify this code a bit:
// https://www.baeldung.com/java-spi
static Migrate fetchAndClassloadInstance(String migrateVersion, String scalaVersion, Logger logger) throws Exception {
static ClassLoader getClassLoader(String migrateVersion, String scalaVersion) throws Exception {
List<URL> jars = getJars(migrateVersion, scalaVersion);

ClassLoader parent = new MigrateClassloader(Migrate.class.getClassLoader());
URLClassLoader classLoader = new URLClassLoader(jars.stream().toArray(URL[]::new), parent);

return classloadInstance(classLoader, logger);
return classLoader;
}

static Migrate classloadInstance(URLClassLoader classLoader, Logger logger) throws Exception {
// Todo: Maybe using ServiceLoader could simplify this code a bit:
// https://www.baeldung.com/java-spi
static Migrate getInstance(ClassLoader classLoader, Logger logger) throws Exception {
Class<?> cls = classLoader.loadClass("migrate.interfaces.MigrateImpl");
Constructor<?> ctor = cls.getDeclaredConstructor(Logger.class);
ctor.setAccessible(true);
Expand All @@ -59,7 +56,8 @@ static Migrate classloadInstance(URLClassLoader classLoader, Logger logger) thro
static List<URL> getJars(String migrateVersion, String scalaVersion) throws Exception {
ScalaVersion scalaV = ScalaVersion.of(scalaVersion);
Dependency migrate = Dependency.parse("ch.epfl.scala::scala3-migrate-core:" + migrateVersion, scalaV);
return fetch(Collections.singletonList(migrate), ResolutionParams.create());
List<URL> jars = fetch(Collections.singletonList(migrate), ResolutionParams.create());
return jars;
}

static List<URL> fetch(List<Dependency> dependencies, ResolutionParams resolutionParams) throws Exception {
Expand Down
74 changes: 34 additions & 40 deletions migrate/src/main/scala/migrate/Scala3Migrate.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import scala.util.Failure
import scala.util.Success
import scala.util.Try

import migrate.interfaces.CompilationUnit
import migrate.compiler.interfaces.CompilationUnit
import migrate.compiler.interfaces.Scala3Compiler
import migrate.interfaces.Logger
import migrate.interfaces.Scala3Compiler
import migrate.internal._
import migrate.utils.FileUtils
import migrate.utils.Format._
Expand All @@ -15,28 +15,26 @@ import migrate.utils.ScalafixService
import migrate.utils.Timer._
import scalafix.interfaces.ScalafixEvaluation

class Scala3Migrate(scalafixSrv: ScalafixService, logger: Logger) {
class Scala3Migrate(scalafixSrv: ScalafixService, baseDirectory: AbsolutePath, logger: Logger) {

def previewMigration(
unmanagedSources: Seq[AbsolutePath],
managedSources: Seq[AbsolutePath],
compiler: Scala3Compiler
): Try[Map[AbsolutePath, FileMigrationState.FinalState]] = {
unmanagedSources.foreach(f => logger.info(s"Migrating $f"))
val (scalaFiles, javaFiles) = unmanagedSources.partition(_.value.endsWith("scala"))
// first try to compile without adding any patch
val filesWithErr = compileInScala3AndGetFilesWithErrors(unmanagedSources ++ managedSources, compiler)
if (filesWithErr.isEmpty) {
logger.info("The project compiles successfully in Scala 3")
Success(Map[AbsolutePath, FileMigrationState.FinalState]())
} else {
if (filesWithErr.isEmpty) { Success(Map[AbsolutePath, FileMigrationState.FinalState]()) }
else {
val filesWithoutErrors = scalaFiles.diff(filesWithErr)
(for {
for {
initialFileToMigrate <- buildMigrationFiles(filesWithErr)
_ <- compileInScala3(initialFileToMigrate, filesWithoutErrors ++ javaFiles ++ managedSources, compiler)
migratedFiles <-
initialFileToMigrate.map(f => f.migrate(compiler, logger).map(success => (f.source, success))).sequence
} yield migratedFiles.toMap)
migratedFiles <- initialFileToMigrate
.map(f => f.migrate(compiler, logger).map(success => (f.source, success)))
.sequence
} yield migratedFiles.toMap
}
}

Expand All @@ -48,13 +46,11 @@ class Scala3Migrate(scalafixSrv: ScalafixService, logger: Logger) {
scala3ClassDirectory: AbsolutePath
): Try[Unit] =
for {
compiler <- setupScala3Compiler(scala3Classpath, scala3ClassDirectory, scala3CompilerOptions)
migratedFiles <-
previewMigration(unmanagedSources, managedSources, compiler)
compiler <- setupScala3Compiler(scala3Classpath, scala3ClassDirectory, scala3CompilerOptions)
migratedFiles <- previewMigration(unmanagedSources, managedSources, compiler)
_ <- migratedFiles.map { case (file, migrated) =>
migrated.newFileContent.flatMap(FileUtils.writeFile(file, _))
}.sequence
_ = migratedFiles.keys.map(file => logger.info(s"${file.value} has been successfully migrated"))
_ <- compileWithRewrite(
scala3Classpath,
scala3ClassDirectory,
Expand All @@ -67,11 +63,11 @@ class Scala3Migrate(scalafixSrv: ScalafixService, logger: Logger) {
def previewSyntaxMigration(unmanagedSources: Seq[AbsolutePath]): Try[ScalafixEvaluation] =
for {
scalafixEval <- timeAndLog(scalafixSrv.fixSyntaxForScala3(unmanagedSources)) {
case (finiteDuration, Success(_)) =>
case (_, Success(_)) =>
logger.info(
s"Run syntactic rules in ${plural(unmanagedSources.size, "file")} successfully after $finiteDuration")
s"Run syntactic rules in ${plural(unmanagedSources.size, "Scala source")} successfully")
case (_, Failure(e)) =>
logger.info(s"Failed running syntactic rules because: ${e.getMessage}")
logger.error(s"Failed running syntactic rules because: ${e.getMessage}")
}
} yield scalafixEval

Expand Down Expand Up @@ -101,7 +97,7 @@ class Scala3Migrate(scalafixSrv: ScalafixService, logger: Logger) {
unmanaged: Seq[AbsolutePath],
managed: Seq[AbsolutePath]
): Try[Unit] = {
logger.info(s"Compiling in scala 3 with -rewrite option")
logger.info(s"Compiling to Scala 3 with -source:3.0-migration -rewrite")
for {
compilerWithRewrite <- setupScala3Compiler(classpath3, classDir3, settings3 :+ "-rewrite")
_ <- Try(compilerWithRewrite.compileWithRewrite((unmanaged ++ managed).map(_.value).toList))
Expand All @@ -115,20 +111,23 @@ class Scala3Migrate(scalafixSrv: ScalafixService, logger: Logger) {
): Try[Unit] =
for {
cuUnmanagedSources <- migrationFiles.map(_.previewAllPatches()).sequence
cuManagedSources = managedSources.map(path => new CompilationUnit(path.value, FileUtils.read(path)))
cuManagedSources = managedSources.map(path => new CompilationUnit(path.value, FileUtils.read(path), path.toNio))
_ <- timeAndLog(Try(compiler.compileAndReport((cuUnmanagedSources ++ cuManagedSources).toList, logger))) {
case (finiteDuration, Success(_)) =>
logger.info(s"Scala 3 compilation succeeded after $finiteDuration")
case (_, Failure(_)) =>
logger.error("Scala 3 compilation failed. Try to fix the error(s) manually")
logger.error("Failed inferring meaningful types because: Scala 3 compilation error")
case _ =>
val count = migrationFiles.map(_.patches.size).sum
val message =
s"Found ${plural(count, "patch", "patches")} in ${plural(migrationFiles.size, "Scala source")}"
logger.info(message)
}
} yield ()

private def compileInScala3AndGetFilesWithErrors(
files: Seq[AbsolutePath],
compiler: Scala3Compiler
): Seq[AbsolutePath] = {
val compilationUnits = files.map(path => new CompilationUnit(path.value, FileUtils.read(path)))
val compilationUnits = files.map(path => new CompilationUnit(path.value, FileUtils.read(path), path.toNio))
val res = compiler.compileAndReportFilesWithErrors(compilationUnits.toList).toSeq
res.map(AbsolutePath.from(_)).sequence.getOrElse(Nil)
}
Expand All @@ -137,23 +136,18 @@ class Scala3Migrate(scalafixSrv: ScalafixService, logger: Logger) {
if (unmanagedSources.isEmpty) Success(Seq())
else
for {
fileEvaluations <-
timeAndLog(scalafixSrv.inferTypesAndImplicits(unmanagedSources)) {
case (duration, Success(files)) =>
val fileEvaluationsSeq = files.getFileEvaluations().toSeq
val count = fileEvaluationsSeq.map(_.getPatches().size).sum
logger.info(
s"Found ${plural(count, "patch", "patches")} in ${plural(unmanagedSources.size, "file")} after $duration")
case (_, Failure(e)) =>
logger.error(s"Failed inferring types because: ${e.getMessage()}.")
}
fileEvaluationMap <- fileEvaluations
.getFileEvaluations()
.toSeq
.map(e => AbsolutePath.from(e.getEvaluatedFile()).map(file => file -> e))
fileEvaluations <- timeAndLog(scalafixSrv.inferTypesAndImplicits(unmanagedSources)) {
case (_, Failure(e)) =>
logger.error(s"Failed inferring types because: ${e.getMessage}")
case _ =>
}
fileEvaluationMap <- fileEvaluations.getFileEvaluations.toSeq
.map(e => AbsolutePath.from(e.getEvaluatedFile).map(file => file -> e))
.sequence
.map(_.toMap)
fileToMigrate <-
unmanagedSources.map(src => fileEvaluationMap.get(src).map(FileMigrationState.Initial).toTry).sequence
unmanagedSources
.map(src => fileEvaluationMap.get(src).map(FileMigrationState.Initial(_, baseDirectory)).toTry)
.sequence
} yield fileToMigrate
}
4 changes: 2 additions & 2 deletions migrate/src/main/scala/migrate/interfaces/MigrateImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ final class MigrateImpl(logger: Logger) extends Migrate {
targetRootAbs,
baseDirectory,
logger)
scalaMigrate = new Scala3Migrate(configuredScalafixSrv, logger)
scalaMigrate = new Scala3Migrate(configuredScalafixSrv, baseDirectory, logger)
_ <- scalaMigrate
.migrate(
unmanagedSources = unmanagedSourcesAbs,
Expand Down Expand Up @@ -83,7 +83,7 @@ final class MigrateImpl(logger: Logger) extends Migrate {
targetRootAbs,
baseDirectory,
logger)
scalaMigrate = new Scala3Migrate(configuredScalafixSrv, logger)
scalaMigrate = new Scala3Migrate(configuredScalafixSrv, baseDirectory, logger)
_ <- scalaMigrate.migrateSyntax(unmanagedSourcesAbs)
} yield ()).get
}
17 changes: 8 additions & 9 deletions migrate/src/main/scala/migrate/internal/FileMigration.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import scala.util.Success
import scala.util.Try
import scala.util.control.NonFatal

import migrate.compiler.interfaces.Scala3Compiler
import migrate.interfaces.Logger
import migrate.interfaces.Scala3Compiler
import migrate.utils.Format
import migrate.utils.Format._
import migrate.utils.Timer._
import scalafix.interfaces.ScalafixPatch
Expand All @@ -22,17 +23,15 @@ private[migrate] class FileMigration(
logger: Logger) {

def migrate(): Try[FileMigrationState.FinalState] = {
logger.info(s"Starting migration of ${fileToMigrate.relativePath}")
val initialState = CompilingState(fileToMigrate.patches, Seq.empty)

timeAndLog(loopUntilNoCandidates(Success(initialState))) {
case (timeMs, Success(finalState)) =>
logger.info(
s"Found ${finalState.necessaryPatches.size} required patch(es) in ${fileToMigrate.source} after $timeMs ms"
)
case (timeMs, Failure(e)) =>
logger.info(
s"Failed finding the required patches in ${fileToMigrate.source} after $timeMs ms because ${e.getMessage()}"
)
case (_, Success(finalState)) =>
val formattedPatches = Format.plural(finalState.necessaryPatches.size, "required patch", "required patches")
logger.info(s"Found $formattedPatches in ${fileToMigrate.relativePath}")
case (_, Failure(e)) =>
logger.error(s"Failed to reduce the patches in ${fileToMigrate.relativePath} because: ${e.getMessage}")
}.map(finalState => fileToMigrate.success(finalState.necessaryPatches))
}

Expand Down
Loading

0 comments on commit 6ffd3a6

Please sign in to comment.