Skip to content
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

Rework migrate types #428

Merged
merged 5 commits into from
Aug 31, 2023
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
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
Loading