diff --git a/compiler/src/dotty/tools/backend/jvm/CodeGen.scala b/compiler/src/dotty/tools/backend/jvm/CodeGen.scala index c9f9e4e23d90..4e00597d5b96 100644 --- a/compiler/src/dotty/tools/backend/jvm/CodeGen.scala +++ b/compiler/src/dotty/tools/backend/jvm/CodeGen.scala @@ -30,6 +30,7 @@ import scala.tools.asm import scala.tools.asm.tree._ import tpd._ import dotty.tools.io.AbstractFile +import dotty.tools.dotc.util import dotty.tools.dotc.util.NoSourcePosition @@ -106,7 +107,7 @@ class CodeGen(val int: DottyBackendInterface, val primitives: DottyPrimitives)( } // Creates a callback that will be evaluated in PostProcessor after creating a file - private def onFileCreated(cls: ClassNode, claszSymbol: Symbol, sourceFile: interfaces.SourceFile): AbstractFile => Unit = clsFile => { + private def onFileCreated(cls: ClassNode, claszSymbol: Symbol, sourceFile: util.SourceFile): AbstractFile => Unit = clsFile => { val (fullClassName, isLocal) = atPhase(sbtExtractDependenciesPhase) { (ExtractDependencies.classNameAsString(claszSymbol), claszSymbol.isLocal) } @@ -115,12 +116,9 @@ class CodeGen(val int: DottyBackendInterface, val primitives: DottyPrimitives)( if (ctx.compilerCallback != null) ctx.compilerCallback.onClassGenerated(sourceFile, convertAbstractFile(clsFile), className) - if (ctx.sbtCallback != null) { - val jSourceFile = sourceFile.jfile.orElse(null) - val cb = ctx.sbtCallback - if (isLocal) cb.generatedLocalClass(jSourceFile, clsFile.file) - else cb.generatedNonLocalClass(jSourceFile, clsFile.file, className, fullClassName) - } + ctx.withIncCallback: cb => + if (isLocal) cb.generatedLocalClass(sourceFile, clsFile.jpath) + else cb.generatedNonLocalClass(sourceFile, clsFile.jpath, className, fullClassName) } /** Convert a `dotty.tools.io.AbstractFile` into a diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index e0e43169820a..8a7f2ff4e051 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -34,25 +34,25 @@ import scala.annotation.internal.sharable import DenotTransformers.DenotTransformer import dotty.tools.dotc.profile.Profiler +import dotty.tools.dotc.sbt.interfaces.IncrementalCallback import util.Property.Key import util.Store -import xsbti.AnalysisCallback import plugins._ import java.util.concurrent.atomic.AtomicInteger import java.nio.file.InvalidPathException object Contexts { - private val (compilerCallbackLoc, store1) = Store.empty.newLocation[CompilerCallback]() - private val (sbtCallbackLoc, store2) = store1.newLocation[AnalysisCallback]() - private val (printerFnLoc, store3) = store2.newLocation[Context => Printer](new RefinedPrinter(_)) - private val (settingsStateLoc, store4) = store3.newLocation[SettingsState]() - private val (compilationUnitLoc, store5) = store4.newLocation[CompilationUnit]() - private val (runLoc, store6) = store5.newLocation[Run | Null]() - private val (profilerLoc, store7) = store6.newLocation[Profiler]() - private val (notNullInfosLoc, store8) = store7.newLocation[List[NotNullInfo]]() - private val (importInfoLoc, store9) = store8.newLocation[ImportInfo | Null]() - private val (typeAssignerLoc, store10) = store9.newLocation[TypeAssigner](TypeAssigner) + private val (compilerCallbackLoc, store1) = Store.empty.newLocation[CompilerCallback]() + private val (incCallbackLoc, store2) = store1.newLocation[IncrementalCallback | Null]() + private val (printerFnLoc, store3) = store2.newLocation[Context => Printer](new RefinedPrinter(_)) + private val (settingsStateLoc, store4) = store3.newLocation[SettingsState]() + private val (compilationUnitLoc, store5) = store4.newLocation[CompilationUnit]() + private val (runLoc, store6) = store5.newLocation[Run | Null]() + private val (profilerLoc, store7) = store6.newLocation[Profiler]() + private val (notNullInfosLoc, store8) = store7.newLocation[List[NotNullInfo]]() + private val (importInfoLoc, store9) = store8.newLocation[ImportInfo | Null]() + private val (typeAssignerLoc, store10) = store9.newLocation[TypeAssigner](TypeAssigner) private val initialStore = store10 @@ -164,8 +164,18 @@ object Contexts { /** The compiler callback implementation, or null if no callback will be called. */ def compilerCallback: CompilerCallback = store(compilerCallbackLoc) - /** The sbt callback implementation if we are run from sbt, null otherwise */ - def sbtCallback: AnalysisCallback = store(sbtCallbackLoc) + /** The Zinc callback implementation if we are run from Zinc, null otherwise */ + def incCallback: IncrementalCallback | Null = store(incCallbackLoc) + + /** Run `op` if there exists an incremental callback */ + inline def withIncCallback(inline op: IncrementalCallback => Unit): Unit = + val local = incCallback + if local != null then op(local) + + def runZincPhases: Boolean = + def forceRun = settings.YdumpSbtInc.value || settings.YforceSbtPhases.value + val local = incCallback + local != null && local.enabled || forceRun /** The current plain printer */ def printerFn: Context => Printer = store(printerFnLoc) @@ -664,7 +674,7 @@ object Contexts { } def setCompilerCallback(callback: CompilerCallback): this.type = updateStore(compilerCallbackLoc, callback) - def setSbtCallback(callback: AnalysisCallback): this.type = updateStore(sbtCallbackLoc, callback) + def setIncCallback(callback: IncrementalCallback): this.type = updateStore(incCallbackLoc, callback) def setPrinterFn(printer: Context => Printer): this.type = updateStore(printerFnLoc, printer) def setSettings(settingsState: SettingsState): this.type = updateStore(settingsStateLoc, settingsState) def setRun(run: Run | Null): this.type = updateStore(runLoc, run) diff --git a/compiler/src/dotty/tools/dotc/sbt/APIUtils.scala b/compiler/src/dotty/tools/dotc/sbt/APIUtils.scala index aa98f79c8e3b..833cf7f2e0ff 100644 --- a/compiler/src/dotty/tools/dotc/sbt/APIUtils.scala +++ b/compiler/src/dotty/tools/dotc/sbt/APIUtils.scala @@ -35,9 +35,9 @@ object APIUtils { * a dummy empty class can be registered instead, using this method. */ def registerDummyClass(classSym: ClassSymbol)(using Context): Unit = { - if (ctx.sbtCallback != null) { + ctx.withIncCallback { cb => val classLike = emptyClassLike(classSym) - ctx.sbtCallback.api(ctx.compilationUnit.source.file.file, classLike) + cb.api(ctx.compilationUnit.source, classLike) } } diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala index f54baeb7256c..5ecf17be32a9 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala @@ -49,8 +49,7 @@ class ExtractAPI extends Phase { override def description: String = ExtractAPI.description override def isRunnable(using Context): Boolean = { - def forceRun = ctx.settings.YdumpSbtInc.value || ctx.settings.YforceSbtPhases.value - super.isRunnable && (ctx.sbtCallback != null || forceRun) + super.isRunnable && ctx.runZincPhases } // Check no needed. Does not transform trees @@ -65,9 +64,9 @@ class ExtractAPI extends Phase { override def run(using Context): Unit = { val unit = ctx.compilationUnit - val sourceFile = unit.source.file - if (ctx.sbtCallback != null) - ctx.sbtCallback.startSource(sourceFile.file) + val sourceFile = unit.source + ctx.withIncCallback: cb => + cb.startSource(sourceFile) val apiTraverser = new ExtractAPICollector val classes = apiTraverser.apiSource(unit.tpdTree) @@ -75,18 +74,17 @@ class ExtractAPI extends Phase { if (ctx.settings.YdumpSbtInc.value) { // Append to existing file that should have been created by ExtractDependencies - val pw = new PrintWriter(File(sourceFile.jpath).changeExtension("inc").toFile + val pw = new PrintWriter(File(sourceFile.file.jpath).changeExtension("inc").toFile .bufferedWriter(append = true), true) try { classes.foreach(source => pw.println(DefaultShowAPI(source))) } finally pw.close() } - if ctx.sbtCallback != null && - !ctx.compilationUnit.suspendedAtInliningPhase // already registered before this unit was suspended - then - classes.foreach(ctx.sbtCallback.api(sourceFile.file, _)) - mainClasses.foreach(ctx.sbtCallback.mainClass(sourceFile.file, _)) + ctx.withIncCallback: cb => + if !ctx.compilationUnit.suspendedAtInliningPhase then // already registered before this unit was suspended + classes.foreach(cb.api(sourceFile, _)) + mainClasses.foreach(cb.mainClass(sourceFile, _)) } } diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala index ddd89b156fd4..ac3136340625 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala @@ -4,6 +4,7 @@ package sbt import scala.language.unsafeNulls import java.io.File +import java.nio.file.Path import java.util.{Arrays, EnumSet} import dotty.tools.dotc.ast.tpd @@ -54,8 +55,7 @@ class ExtractDependencies extends Phase { override def description: String = ExtractDependencies.description override def isRunnable(using Context): Boolean = { - def forceRun = ctx.settings.YdumpSbtInc.value || ctx.settings.YforceSbtPhases.value - super.isRunnable && (ctx.sbtCallback != null || forceRun) + super.isRunnable && ctx.runZincPhases } // Check no needed. Does not transform trees @@ -90,18 +90,16 @@ class ExtractDependencies extends Phase { } finally pw.close() } - if (ctx.sbtCallback != null) { + ctx.withIncCallback: cb => collector.usedNames.foreach { case (clazz, usedNames) => val className = classNameAsString(clazz) usedNames.names.foreach { case (usedName, scopes) => - ctx.sbtCallback.usedName(className, usedName.toString, scopes) + cb.usedName(className, usedName.toString, scopes) } } - collector.dependencies.foreach(recordDependency) - } } /* @@ -111,27 +109,20 @@ class ExtractDependencies extends Phase { */ def recordDependency(dep: ClassDependency)(using Context): Unit = { val fromClassName = classNameAsString(dep.from) - val sourceFile = ctx.compilationUnit.source.file.file + val sourceFile = ctx.compilationUnit.source - def binaryDependency(file: File, binaryClassName: String) = - ctx.sbtCallback.binaryDependency(file, binaryClassName, fromClassName, sourceFile, dep.context) + def binaryDependency(file: Path, binaryClassName: String) = + ctx.withIncCallback(_.binaryDependency(file, binaryClassName, fromClassName, sourceFile, dep.context)) def processExternalDependency(depFile: AbstractFile, binaryClassName: String) = { depFile match { case ze: ZipArchive#Entry => // The dependency comes from a JAR ze.underlyingSource match - case Some(zip) if zip.file != null => - binaryDependency(zip.file, binaryClassName) + case Some(zip) if zip.jpath != null => + binaryDependency(zip.jpath, binaryClassName) case _ => - case pf: PlainFile => // The dependency comes from a class file - // FIXME: pf.file is null for classfiles coming from the modulepath - // (handled by JrtClassPath) because they cannot be represented as - // java.io.File, since the `binaryDependency` callback must take a - // java.io.File, this means that we cannot record dependencies coming - // from the modulepath. For now this isn't a big deal since we only - // support having the standard Java library on the modulepath. - if pf.file != null then - binaryDependency(pf.file, binaryClassName) + case pf: PlainFile => // The dependency comes from a class file, Zinc handles JRT filesystem + binaryDependency(pf.jpath, binaryClassName) case _ => internalError(s"Ignoring dependency $depFile of unknown class ${depFile.getClass}}", dep.from.srcPos) } @@ -144,11 +135,11 @@ class ExtractDependencies extends Phase { if (depFile.extension == "class") { // Dependency is external -- source is undefined processExternalDependency(depFile, dep.to.binaryClassName) - } else if (allowLocal || depFile.file != sourceFile) { + } else if (allowLocal || depFile != sourceFile.file) { // We cannot ignore dependencies coming from the same source file because // the dependency info needs to propagate. See source-dependencies/trait-trait-211. val toClassName = classNameAsString(dep.to) - ctx.sbtCallback.classDependency(toClassName, fromClassName, dep.context) + ctx.withIncCallback(_.classDependency(toClassName, fromClassName, dep.context)) } } } diff --git a/compiler/src/dotty/tools/dotc/sbt/interfaces/IncrementalCallback.java b/compiler/src/dotty/tools/dotc/sbt/interfaces/IncrementalCallback.java new file mode 100644 index 000000000000..4c6afa113f4f --- /dev/null +++ b/compiler/src/dotty/tools/dotc/sbt/interfaces/IncrementalCallback.java @@ -0,0 +1,39 @@ +package dotty.tools.dotc.sbt.interfaces; + +import dotty.tools.dotc.util.SourceFile; + +import java.util.EnumSet; +import java.nio.file.Path; + +/* User code should not implement this interface, it is intended to be a wrapper around xsbti.AnalysisCallback. */ +public interface IncrementalCallback { + default void api(SourceFile sourceFile, xsbti.api.ClassLike classApi) { + } + + default void startSource(SourceFile sourceFile) { + } + + default void mainClass(SourceFile sourceFile, String className) { + } + + default boolean enabled() { + return false; + } + + default void usedName(String className, String name, EnumSet useScopes) { + } + + default void binaryDependency(Path onBinaryEntry, String onBinaryClassName, String fromClassName, + SourceFile fromSourceFile, xsbti.api.DependencyContext context) { + } + + default void classDependency(String onClassName, String sourceClassName, xsbti.api.DependencyContext context) { + } + + default void generatedLocalClass(SourceFile source, Path classFile) { + } + + default void generatedNonLocalClass(SourceFile source, Path classFile, String binaryClassName, + String srcClassName) { + } +} diff --git a/project/Build.scala b/project/Build.scala index 0e7535c1b83e..107a7ed25eb6 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -525,6 +525,33 @@ object Build { recur(lines, false) } + /** replace imports of `com.google.protobuf.*` with compiler implemented version */ + def replaceProtobuf(lines: List[String]): List[String] = { + def recur(ls: List[String]): List[String] = ls match { + case l :: rest => + val lt = l.trim() + if (lt.isEmpty || lt.startsWith("package ") || lt.startsWith("import ")) { + val newLine = + if (lt.startsWith("import com.google.protobuf.")) { + if (lt == "import com.google.protobuf.CodedInputStream") { + "import dotty.tools.dotc.semanticdb.internal.SemanticdbInputStream as CodedInputStream" + } else if (lt == "import com.google.protobuf.CodedOutputStream") { + "import dotty.tools.dotc.semanticdb.internal.SemanticdbOutputStream as CodedOutputStream" + } else { + l + } + } else { + l + } + newLine :: recur(rest) + } else { + ls // don't check rest of file + } + case _ => ls + } + recur(lines) + } + // Settings shared between scala3-compiler and scala3-compiler-bootstrapped lazy val commonDottyCompilerSettings = Seq( // Generate compiler.properties, used by sbt @@ -551,7 +578,7 @@ object Build { // get libraries onboard libraryDependencies ++= Seq( "org.scala-lang.modules" % "scala-asm" % "9.5.0-scala-1", // used by the backend - Dependencies.oldCompilerInterface, // we stick to the old version to avoid deprecation warnings + Dependencies.compilerInterface, "org.jline" % "jline-reader" % "3.19.0", // used by the REPL "org.jline" % "jline-terminal" % "3.19.0", "org.jline" % "jline-terminal-jna" % "3.19.0", // needed for Windows @@ -668,7 +695,8 @@ object Build { val dottyTastyInspector = jars("scala3-tasty-inspector") val dottyInterfaces = jars("scala3-interfaces") val tastyCore = jars("tasty-core") - run(insertClasspathInArgs(args1, List(dottyCompiler, dottyInterfaces, asm, dottyStaging, dottyTastyInspector, tastyCore).mkString(File.pathSeparator))) + val compilerInterface = findArtifactPath(externalDeps, "compiler-interface") + run(insertClasspathInArgs(args1, List(dottyCompiler, dottyInterfaces, asm, dottyStaging, dottyTastyInspector, tastyCore, compilerInterface).mkString(File.pathSeparator))) } else run(args) }, @@ -707,7 +735,8 @@ object Build { val dottyTastyInspector = jars("scala3-tasty-inspector") val tastyCore = jars("tasty-core") val asm = findArtifactPath(externalDeps, "scala-asm") - extraClasspath ++= Seq(dottyCompiler, dottyInterfaces, asm, dottyStaging, dottyTastyInspector, tastyCore) + val compilerInterface = findArtifactPath(externalDeps, "compiler-interface") + extraClasspath ++= Seq(dottyCompiler, dottyInterfaces, asm, dottyStaging, dottyTastyInspector, tastyCore, compilerInterface) } val fullArgs = main :: (if (printTasty) args else insertClasspathInArgs(args, extraClasspath.mkString(File.pathSeparator))) @@ -1051,8 +1080,7 @@ object Build { // when sbt reads the settings. Test / test := (LocalProject("scala3-sbt-bridge-tests") / Test / test).value, - // The `newCompilerInterface` is backward compatible with the `oldCompilerInterface` - libraryDependencies += Dependencies.newCompilerInterface % Provided + libraryDependencies += Dependencies.compilerInterface % Provided ) // We use a separate project for the bridge tests since they can only be run @@ -1134,7 +1162,8 @@ object Build { val mtagsSharedSources = (targetDir ** "*.scala").get.toSet mtagsSharedSources.foreach(f => { val lines = IO.readLines(f) - IO.writeLines(f, insertUnsafeNullsImport(lines)) + val substitutions = (replaceProtobuf(_)) andThen (insertUnsafeNullsImport(_)) + IO.writeLines(f, substitutions(lines)) }) mtagsSharedSources } (Set(mtagsSharedSourceJar)).toSeq diff --git a/project/Dependencies.scala b/project/Dependencies.scala index f22601346803..4b2a9e2815be 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -28,6 +28,5 @@ object Dependencies { "com.vladsch.flexmark" % "flexmark-ext-yaml-front-matter" % flexmarkVersion, ) - val newCompilerInterface = "org.scala-sbt" % "compiler-interface" % "1.9.0" - val oldCompilerInterface = "org.scala-sbt" % "compiler-interface" % "1.3.5" + val compilerInterface = "org.scala-sbt" % "compiler-interface" % "1.9.0" } diff --git a/sbt-bridge/src/dotty/tools/xsbt/CompilerBridgeDriver.java b/sbt-bridge/src/dotty/tools/xsbt/CompilerBridgeDriver.java index 12291120b157..6a018287ea53 100644 --- a/sbt-bridge/src/dotty/tools/xsbt/CompilerBridgeDriver.java +++ b/sbt-bridge/src/dotty/tools/xsbt/CompilerBridgeDriver.java @@ -10,15 +10,25 @@ import dotty.tools.dotc.ScalacCommand; import dotty.tools.dotc.config.Properties; import dotty.tools.dotc.core.Contexts; +import dotty.tools.dotc.util.SourceFile; import dotty.tools.io.AbstractFile; +import dotty.tools.io.PlainFile; +import dotty.tools.io.Path; +import dotty.tools.io.Streamable; import scala.collection.mutable.ListBuffer; +import scala.jdk.javaapi.CollectionConverters; import scala.io.Codec; import xsbti.Problem; import xsbti.*; import xsbti.compile.Output; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.Comparator; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.Arrays; public class CompilerBridgeDriver extends Driver { @@ -50,15 +60,68 @@ public boolean sourcesRequired() { return false; } + private static VirtualFile asVirtualFile(SourceFile sourceFile, DelegatingReporter reporter, + HashMap lookup) { + return lookup.computeIfAbsent(sourceFile.file(), path -> { + reportMissingFile(reporter, sourceFile); + if (sourceFile.file().jpath() != null) + return new FallbackPathBasedFile(sourceFile); + else + return new FallbackVirtualFile(sourceFile); + }); + } + + private static void reportMissingFile(DelegatingReporter reporter, SourceFile sourceFile) { + String underline = String.join("", Collections.nCopies(sourceFile.path().length(), "^")); + String message = + sourceFile.path() + ": Missing Zinc virtual file\n" + + underline + "\n" + + " Falling back to placeholder for the given source file (of class " + sourceFile.getClass().getName() + ")\n" + + " This is likely a bug in incremental compilation for the Scala 3 compiler.\n" + + " Please report it to the Scala 3 maintainers at https://github.com/lampepfl/dotty/issues."; + reporter.reportBasicWarning(message); + } + synchronized public void run(VirtualFile[] sources, AnalysisCallback callback, Logger log, Reporter delegate) { - DelegatingReporter reporter = new DelegatingReporter(delegate); + VirtualFile[] sortedSources = new VirtualFile[sources.length]; + System.arraycopy(sources, 0, sortedSources, 0, sources.length); + Arrays.sort(sortedSources, (x0, x1) -> x0.id().compareTo(x1.id())); + + ListBuffer sourcesBuffer = new ListBuffer<>(); + HashMap lookup = new HashMap<>(sources.length, 0.25f); + + for (int i = 0; i < sources.length; i++) { + VirtualFile source = sortedSources[i]; + AbstractFile abstractFile = asDottyFile(source); + sourcesBuffer.append(abstractFile); + lookup.put(abstractFile, source); + } + + DelegatingReporter reporter = new DelegatingReporter(delegate, sourceFile -> { + // TODO: possible situation here where we use -from-tasty and TASTy source files but + // the reporter log is associated to a Scala source file? + + // Zinc will use the output of this function to possibly lookup a mapped virtual file, + // e.g. convert `${ROOT}/Foo.scala` to `/path/to/Foo.scala` if it exists in the lookup map. + VirtualFile vf = lookup.get(sourceFile.file()); + if (vf != null) + return vf.id(); + else + // follow Zinc, which uses the path of the source file as a fallback. + return sourceFile.path(); + }); + + IncrementalCallback incCallback = new IncrementalCallback(callback, sourceFile -> + asVirtualFile(sourceFile, reporter, lookup) + ); + try { log.debug(this::infoOnCachedCompiler); Contexts.Context initialCtx = initCtx() .fresh() .setReporter(reporter) - .setSbtCallback(callback); + .setIncCallback(incCallback); Contexts.Context context = setup(args, initialCtx).map(t -> t._2).getOrElse(() -> initialCtx); @@ -70,28 +133,13 @@ synchronized public void run(VirtualFile[] sources, AnalysisCallback callback, L log.debug(this::prettyPrintCompilationArguments); Compiler compiler = newCompiler(context); - VirtualFile[] sortedSources = new VirtualFile[sources.length]; - System.arraycopy(sources, 0, sortedSources, 0, sources.length); - Arrays.sort( - sortedSources, - new Comparator() { - @Override - public int compare(VirtualFile x0, VirtualFile x1) { - return x0.id().compareTo(x1.id()); - } - } - ); - - ListBuffer sourcesBuffer = new ListBuffer<>(); - for (VirtualFile file: sortedSources) - sourcesBuffer.append(asDottyFile(file)); doCompile(compiler, sourcesBuffer.toList(), context); for (xsbti.Problem problem: delegate.problems()) { callback.problem(problem.category(), problem.position(), problem.message(), problem.severity(), true); } - } else { + } else { delegate.printSummary(); } @@ -105,11 +153,28 @@ public int compare(VirtualFile x0, VirtualFile x1) { } private static AbstractFile asDottyFile(VirtualFile virtualFile) { - if (virtualFile instanceof PathBasedFile) - return new ZincPlainFile((PathBasedFile) virtualFile); + if (virtualFile instanceof PathBasedFile) { + java.nio.file.Path path = ((PathBasedFile) virtualFile).toPath(); + return new PlainFile(new Path(path)); + } try { - return new ZincVirtualFile(virtualFile); + return new dotty.tools.io.VirtualFile(virtualFile.name(), virtualFile.id()) { + { + // fill in the content + try (OutputStream output = output()) { + try (InputStream input = virtualFile.input()) { + Streamable.Bytes bytes = new Streamable.Bytes() { + @Override + public InputStream inputStream() { + return input; + } + }; + output.write(bytes.toByteArray()); + } + } + } + }; } catch (IOException e) { throw new IllegalArgumentException("invalid file " + virtualFile.name(), e); } diff --git a/sbt-bridge/src/dotty/tools/xsbt/DelegatingReporter.java b/sbt-bridge/src/dotty/tools/xsbt/DelegatingReporter.java index dba1889b393f..e6ddbc51ea32 100644 --- a/sbt-bridge/src/dotty/tools/xsbt/DelegatingReporter.java +++ b/sbt-bridge/src/dotty/tools/xsbt/DelegatingReporter.java @@ -19,12 +19,21 @@ import xsbti.Position; import xsbti.Severity; +import java.util.Collections; +import java.util.function.*; + final public class DelegatingReporter extends AbstractReporter { private xsbti.Reporter delegate; - public DelegatingReporter(xsbti.Reporter delegate) { + // A function that can lookup the `id` of the VirtualFile + // associated with a SourceFile. If there is not an associated virtual file, + // then it is the path of the SourceFile as a String. + private final Function lookupVirtualFileId; + + public DelegatingReporter(xsbti.Reporter delegate, Function lookupVirtualFileId) { super(); this.delegate = delegate; + this.lookupVirtualFileId = lookupVirtualFileId; } public void dropDelegate() { @@ -53,7 +62,16 @@ public void doReport(Diagnostic dia, Context ctx) { messageBuilder.append(System.lineSeparator()).append(explanation(message, ctx)); } - delegate.log(new Problem(position, messageBuilder.toString(), severity, rendered.toString(), diagnosticCode, actions)); + delegate.log(new Problem(position, messageBuilder.toString(), severity, rendered.toString(), diagnosticCode, actions, + lookupVirtualFileId)); + } + + public void reportBasicWarning(String message) { + Position position = PositionBridge.noPosition; + Severity severity = Severity.Warn; + String diagnosticCode = "-1"; // no error code + List actions = Collections.emptyList(); + delegate.log(new Problem(position, message, severity, message, diagnosticCode, actions, lookupVirtualFileId)); } private static Severity severityOf(int level) { @@ -68,9 +86,9 @@ private static Severity severityOf(int level) { return severity; } - private static Position positionOf(SourcePosition pos) { - if (pos.exists()){ - return new PositionBridge(pos, pos.source()); + private Position positionOf(SourcePosition pos) { + if (pos.exists()) { + return new PositionBridge(pos, lookupVirtualFileId.apply(pos.source())); } else { return PositionBridge.noPosition; } diff --git a/sbt-bridge/src/dotty/tools/xsbt/FallbackPathBasedFile.java b/sbt-bridge/src/dotty/tools/xsbt/FallbackPathBasedFile.java new file mode 100644 index 000000000000..28c2170d2b50 --- /dev/null +++ b/sbt-bridge/src/dotty/tools/xsbt/FallbackPathBasedFile.java @@ -0,0 +1,20 @@ +package dotty.tools.xsbt; + +import dotty.tools.dotc.util.SourceFile; + +/**A basic implementation of PathBasedFile that is only used when + * the real virtual file can not be found. + * + * See FallbackVirtualFile for more details. + */ +public class FallbackPathBasedFile extends FallbackVirtualFile implements xsbti.PathBasedFile { + + public FallbackPathBasedFile(SourceFile sourceFile) { + super(sourceFile); + } + + public java.nio.file.Path toPath() { + return sourceFile.file().jpath(); + } + +} diff --git a/sbt-bridge/src/dotty/tools/xsbt/FallbackVirtualFile.java b/sbt-bridge/src/dotty/tools/xsbt/FallbackVirtualFile.java new file mode 100644 index 000000000000..6fcb6ef73e1f --- /dev/null +++ b/sbt-bridge/src/dotty/tools/xsbt/FallbackVirtualFile.java @@ -0,0 +1,36 @@ +package dotty.tools.xsbt; + +import dotty.tools.dotc.util.SourceFile; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +/**A basic implementation of VirtualFile that is only used when + * the real virtual file can not be found. + * + * This has a very basic implementation of contentHash that is almost certainly colliding more than the implementation + * in Zinc. It does not matter anyway as Zinc will recompile the associated source file, because it did not recieve the + * same virtual file back. + */ +public class FallbackVirtualFile extends xsbti.BasicVirtualFileRef implements xsbti.VirtualFile { + + protected final SourceFile sourceFile; + + public FallbackVirtualFile(SourceFile sourceFile) { + super(sourceFile.path()); + this.sourceFile = sourceFile; + } + + private static byte[] toBytes(char[] chars) { + return new String(chars).getBytes(StandardCharsets.UTF_8); + } + + public InputStream input() { + return new java.io.ByteArrayInputStream(toBytes(sourceFile.content())); + } + + public long contentHash() { + int murmurHash3 = scala.util.hashing.MurmurHash3$.MODULE$.bytesHash(toBytes(sourceFile.content())); + return (long) murmurHash3; + } + +} diff --git a/sbt-bridge/src/dotty/tools/xsbt/IncrementalCallback.java b/sbt-bridge/src/dotty/tools/xsbt/IncrementalCallback.java new file mode 100644 index 000000000000..3c3d33c1c1fe --- /dev/null +++ b/sbt-bridge/src/dotty/tools/xsbt/IncrementalCallback.java @@ -0,0 +1,60 @@ +package dotty.tools.xsbt; + +import dotty.tools.dotc.util.SourceFile; +import java.util.function.Function; + +public final class IncrementalCallback implements dotty.tools.dotc.sbt.interfaces.IncrementalCallback { + + private final xsbti.AnalysisCallback delegate; + private final Function asVirtualFile; + + public IncrementalCallback(xsbti.AnalysisCallback delegate, Function asVirtualFile) { + this.delegate = delegate; + this.asVirtualFile = asVirtualFile; + } + + @Override + public void api(SourceFile sourceFile, xsbti.api.ClassLike classApi) { + delegate.api(asVirtualFile.apply(sourceFile), classApi); + } + + @Override + public void startSource(SourceFile sourceFile) { + delegate.startSource(asVirtualFile.apply(sourceFile)); + } + + @Override + public void mainClass(SourceFile sourceFile, String className) { + delegate.mainClass(asVirtualFile.apply(sourceFile), className); + } + + @Override + public boolean enabled() { + return delegate.enabled(); + } + + @Override + public void usedName(String className, String name, java.util.EnumSet useScopes) { + delegate.usedName(className, name, useScopes); + } + + @Override + public void binaryDependency(java.nio.file.Path onBinaryEntry, String onBinaryClassName, String fromClassName, SourceFile fromSourceFile, xsbti.api.DependencyContext context) { + delegate.binaryDependency(onBinaryEntry, onBinaryClassName, fromClassName, asVirtualFile.apply(fromSourceFile), context); + } + + @Override + public void classDependency(String onClassName, String sourceClassName, xsbti.api.DependencyContext context) { + delegate.classDependency(onClassName, sourceClassName, context); + } + + @Override + public void generatedLocalClass(SourceFile source, java.nio.file.Path classFile) { + delegate.generatedLocalClass(asVirtualFile.apply(source), classFile); + } + + @Override + public void generatedNonLocalClass(SourceFile source, java.nio.file.Path classFile, String binaryClassName, String srcClassName) { + delegate.generatedNonLocalClass(asVirtualFile.apply(source), classFile, binaryClassName, srcClassName); + } +} diff --git a/sbt-bridge/src/dotty/tools/xsbt/OldIncrementalCallback.java b/sbt-bridge/src/dotty/tools/xsbt/OldIncrementalCallback.java new file mode 100644 index 000000000000..597a964eb944 --- /dev/null +++ b/sbt-bridge/src/dotty/tools/xsbt/OldIncrementalCallback.java @@ -0,0 +1,74 @@ +package dotty.tools.xsbt; + +import dotty.tools.dotc.util.SourceFile; +import java.util.function.Function; +import java.util.Optional; + +import java.io.File; + +/** To be compatible with the Zinc 1.3 API */ +public final class OldIncrementalCallback implements dotty.tools.dotc.sbt.interfaces.IncrementalCallback { + + private final xsbti.AnalysisCallback delegate; + + public OldIncrementalCallback(xsbti.AnalysisCallback delegate) { + this.delegate = delegate; + } + + private static File asJavaFile(SourceFile sourceFile) { + File jfileOrNull = sourceFile.file().file(); + if (jfileOrNull != null) return jfileOrNull; + throw new IllegalArgumentException("SourceFile " + sourceFile + " is not backed by a java.io.File"); + } + + @SuppressWarnings("deprecation") + @Override + public void api(SourceFile sourceFile, xsbti.api.ClassLike classApi) { + delegate.api(asJavaFile(sourceFile), classApi); + } + + @SuppressWarnings("deprecation") + @Override + public void startSource(SourceFile sourceFile) { + delegate.startSource(asJavaFile(sourceFile)); + } + + @SuppressWarnings("deprecation") + @Override + public void mainClass(SourceFile sourceFile, String className) { + delegate.mainClass(asJavaFile(sourceFile), className); + } + + @Override + public boolean enabled() { + return delegate.enabled(); + } + + @Override + public void usedName(String className, String name, java.util.EnumSet useScopes) { + delegate.usedName(className, name, useScopes); + } + + @SuppressWarnings("deprecation") + @Override + public void binaryDependency(java.nio.file.Path onBinaryEntry, String onBinaryClassName, String fromClassName, SourceFile fromSourceFile, xsbti.api.DependencyContext context) { + delegate.binaryDependency(onBinaryEntry.toFile(), onBinaryClassName, fromClassName, asJavaFile(fromSourceFile), context); + } + + @Override + public void classDependency(String onClassName, String sourceClassName, xsbti.api.DependencyContext context) { + delegate.classDependency(onClassName, sourceClassName, context); + } + + @SuppressWarnings("deprecation") + @Override + public void generatedLocalClass(SourceFile source, java.nio.file.Path classFile) { + delegate.generatedLocalClass(asJavaFile(source), classFile.toFile()); + } + + @SuppressWarnings("deprecation") + @Override + public void generatedNonLocalClass(SourceFile source, java.nio.file.Path classFile, String binaryClassName, String srcClassName) { + delegate.generatedNonLocalClass(asJavaFile(source), classFile.toFile(), binaryClassName, srcClassName); + } +} diff --git a/sbt-bridge/src/dotty/tools/xsbt/PositionBridge.java b/sbt-bridge/src/dotty/tools/xsbt/PositionBridge.java index 6b3c25e2e27c..eb01da25ba1c 100644 --- a/sbt-bridge/src/dotty/tools/xsbt/PositionBridge.java +++ b/sbt-bridge/src/dotty/tools/xsbt/PositionBridge.java @@ -12,10 +12,12 @@ import java.io.File; import java.util.Optional; +import java.util.function.Function; public class PositionBridge implements Position { private final SourcePosition pos; private final SourceFile src; + private final String pathId; public static final Position noPosition = new Position() { public Optional sourceFile() { @@ -45,9 +47,10 @@ public String toString() { } }; - public PositionBridge(SourcePosition pos, SourceFile src) { + public PositionBridge(SourcePosition pos, String path) { this.pos = pos; - this.src = src; + this.src = pos.source(); + this.pathId = path; } @Override @@ -82,17 +85,7 @@ public Optional offset() { @Override public Optional sourcePath() { - if (!src.exists()) - return Optional.empty(); - - AbstractFile sourceFile = pos.source().file(); - if (sourceFile instanceof ZincPlainFile) { - return Optional.of(((ZincPlainFile) sourceFile).underlying().id()); - } else if (sourceFile instanceof ZincVirtualFile) { - return Optional.of(((ZincVirtualFile) sourceFile).underlying().id()); - } else { - return Optional.of(sourceFile.path()); - } + return Optional.of(pathId); } @Override @@ -131,7 +124,7 @@ public String toString() { else return path; } - + @Override public Optional startOffset() { if (src.content().length == 0) diff --git a/sbt-bridge/src/dotty/tools/xsbt/Problem.java b/sbt-bridge/src/dotty/tools/xsbt/Problem.java index 9bde5ce76ebb..532bb35786c4 100644 --- a/sbt-bridge/src/dotty/tools/xsbt/Problem.java +++ b/sbt-bridge/src/dotty/tools/xsbt/Problem.java @@ -2,17 +2,22 @@ import java.util.List; import java.util.Optional; +import java.util.function.Function; + import static java.util.stream.Collectors.toList; import dotty.tools.dotc.reporting.CodeAction; import dotty.tools.dotc.rewrites.Rewrites.ActionPatch; import dotty.tools.dotc.util.SourcePosition; +import dotty.tools.dotc.util.SourceFile; import scala.jdk.javaapi.CollectionConverters; import scala.jdk.javaapi.OptionConverters; import xsbti.Position; import xsbti.Severity; +import xsbti.VirtualFile; + final public class Problem implements xsbti.Problem { private final Position _position; @@ -22,7 +27,13 @@ final public class Problem implements xsbti.Problem { private final String _diagnosticCode; private final List _actions; - public Problem(Position position, String message, Severity severity, String rendered, String diagnosticCode, List actions) { + // A function that can lookup the `id` of the VirtualFile + // associated with a SourceFile. If there is not an associated virtual file, + // then it is the path of the SourceFile as a String. + private final Function _lookupVirtualFileId; + + public Problem(Position position, String message, Severity severity, String rendered, String diagnosticCode, List actions, + Function lookupVirtualFileId) { super(); this._position = position; this._message = message; @@ -30,6 +41,7 @@ public Problem(Position position, String message, Severity severity, String rend this._rendered = Optional.of(rendered); this._diagnosticCode = diagnosticCode; this._actions = actions; + this._lookupVirtualFileId = lookupVirtualFileId; } public String category() { @@ -78,23 +90,23 @@ public List actions() { // never getting called. return _actions .stream() - .map(action -> new Action(action.title(), OptionConverters.toJava(action.description()), toWorkspaceEdit(CollectionConverters.asJava(action.patches())))) + .map(action -> new Action(action.title(), OptionConverters.toJava(action.description()), toWorkspaceEdit(CollectionConverters.asJava(action.patches()), _lookupVirtualFileId))) .collect(toList()); } } - private static WorkspaceEdit toWorkspaceEdit(List patches) { + private static WorkspaceEdit toWorkspaceEdit(List patches, Function lookupVirtualFileId) { return new WorkspaceEdit( patches .stream() - .map(patch -> new TextEdit(positionOf(patch.srcPos()), patch.replacement())) + .map(patch -> new TextEdit(positionOf(patch.srcPos(), lookupVirtualFileId), patch.replacement())) .collect(toList()) ); } - private static Position positionOf(SourcePosition pos) { + private static Position positionOf(SourcePosition pos, Function lookupVirtualFileId) { if (pos.exists()){ - return new PositionBridge(pos, pos.source()); + return new PositionBridge(pos, lookupVirtualFileId.apply(pos.source())); } else { return PositionBridge.noPosition; } diff --git a/sbt-bridge/src/dotty/tools/xsbt/ZincPlainFile.java b/sbt-bridge/src/dotty/tools/xsbt/ZincPlainFile.java deleted file mode 100644 index 68b3494cb84b..000000000000 --- a/sbt-bridge/src/dotty/tools/xsbt/ZincPlainFile.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Zinc - The incremental compiler for Scala. - * Copyright Lightbend, Inc. and Mark Harrah - */ - -package dotty.tools.xsbt; - -import xsbti.PathBasedFile; - -public class ZincPlainFile extends dotty.tools.io.PlainFile { - private final PathBasedFile _underlying; - - public ZincPlainFile(PathBasedFile underlying) { - super(new dotty.tools.io.Path(underlying.toPath())); - this._underlying = underlying; - } - - public PathBasedFile underlying() { - return _underlying; - } -} \ No newline at end of file diff --git a/sbt-bridge/src/dotty/tools/xsbt/ZincVirtualFile.java b/sbt-bridge/src/dotty/tools/xsbt/ZincVirtualFile.java deleted file mode 100644 index a79686270f34..000000000000 --- a/sbt-bridge/src/dotty/tools/xsbt/ZincVirtualFile.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Zinc - The incremental compiler for Scala. - * Copyright Lightbend, Inc. and Mark Harrah - */ - -package dotty.tools.xsbt; - -import dotty.tools.io.Streamable; -import xsbti.VirtualFile; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -public class ZincVirtualFile extends dotty.tools.io.VirtualFile { - private final VirtualFile _underlying; - - public ZincVirtualFile(VirtualFile underlying) throws IOException { - super(underlying.name(), underlying.id()); - this._underlying = underlying; - - // fill in the content - OutputStream output = output(); - try { - Streamable.Bytes bytes = new Streamable.Bytes() { - @Override - public InputStream inputStream() { - return underlying.input(); - } - }; - output.write(bytes.toByteArray()); - } finally { - output.close(); - } - } - - public VirtualFile underlying() { - return _underlying; - } -} diff --git a/sbt-bridge/src/xsbt/CachedCompilerImpl.java b/sbt-bridge/src/xsbt/CachedCompilerImpl.java index 0b876475e51e..8b7779f9c9cb 100644 --- a/sbt-bridge/src/xsbt/CachedCompilerImpl.java +++ b/sbt-bridge/src/xsbt/CachedCompilerImpl.java @@ -13,6 +13,9 @@ import dotty.tools.dotc.Main; import dotty.tools.xsbt.InterfaceCompileFailed; import dotty.tools.xsbt.DelegatingReporter; +import dotty.tools.xsbt.OldIncrementalCallback; + +import dotty.tools.dotc.sbt.interfaces.IncrementalCallback; // deprecation warnings are suppressed because scala3-sbt-bridge must stay compatible with Zinc 1.3 // see https://github.com/lampepfl/dotty/issues/10816 @@ -60,9 +63,11 @@ synchronized public void run(File[] sources, DependencyChanges changes, Analysis return msg; }); + IncrementalCallback incCallback = new OldIncrementalCallback(callback); + Context ctx = new ContextBase().initialCtx().fresh() - .setSbtCallback(callback) - .setReporter(new DelegatingReporter(delegate)); + .setIncCallback(incCallback) + .setReporter(new DelegatingReporter(delegate, source -> source.file().absolutePath())); dotty.tools.dotc.reporting.Reporter reporter = Main.process(commandArguments(sources), ctx); if (reporter.hasErrors()) { diff --git a/sbt-bridge/src/xsbt/CompilerInterface.java b/sbt-bridge/src/xsbt/CompilerInterface.java index 3f26036eee6d..c48ee4c9d909 100644 --- a/sbt-bridge/src/xsbt/CompilerInterface.java +++ b/sbt-bridge/src/xsbt/CompilerInterface.java @@ -54,6 +54,7 @@ private boolean isClassLoaderValid() { } } + @SuppressWarnings("deprecation") public void run(File[] sources, DependencyChanges changes, AnalysisCallback callback, Logger log, Reporter delegate, CompileProgress progress, CachedCompiler cached) { cached.run(sources, changes, callback, log, delegate, progress); diff --git a/sbt-bridge/src/xsbt/DottydocRunner.java b/sbt-bridge/src/xsbt/DottydocRunner.java index e4c35a317e71..a91ff087cea9 100644 --- a/sbt-bridge/src/xsbt/DottydocRunner.java +++ b/sbt-bridge/src/xsbt/DottydocRunner.java @@ -53,7 +53,7 @@ public void run() { args = retained.toArray(new String[retained.size()]); Context ctx = new ContextBase().initialCtx().fresh() - .setReporter(new DelegatingReporter(delegate)); + .setReporter(new DelegatingReporter(delegate, source -> source.file().absolutePath())); try { Class dottydocMainClass = Class.forName("dotty.tools.dottydoc.Main"); diff --git a/sbt-bridge/test/xsbt/ScalaCompilerForUnitTesting.scala b/sbt-bridge/test/xsbt/ScalaCompilerForUnitTesting.scala index e58f9fefd92d..51f10e90f932 100644 --- a/sbt-bridge/test/xsbt/ScalaCompilerForUnitTesting.scala +++ b/sbt-bridge/test/xsbt/ScalaCompilerForUnitTesting.scala @@ -19,7 +19,6 @@ import TestCallback.ExtractedClassDependencies * source code using Scala compiler. */ class ScalaCompilerForUnitTesting { - import scala.language.reflectiveCalls /** * Compiles given source code using Scala compiler and returns API representation @@ -122,7 +121,7 @@ class ScalaCompilerForUnitTesting { * The sequence of temporary files corresponding to passed snippets and analysis * callback is returned as a result. */ - def compileSrcs(groupedSrcs: List[List[String]]): (Seq[File], TestCallback) = { + def compileSrcs(groupedSrcs: List[List[String]]): (Seq[VirtualFile], TestCallback) = { val temp = IO.createTemporaryDirectory val analysisCallback = new TestCallback val classesDir = new File(temp, "classes") @@ -137,13 +136,13 @@ class ScalaCompilerForUnitTesting { prepareSrcFile(temp, fileName, src) } - val virtualSrcFiles = srcFiles.map(file => TestVirtualFile(file.toPath)).toArray + val virtualSrcFiles = srcFiles.toArray val classesDirPath = classesDir.getAbsolutePath.toString val output = new SingleOutput: def getOutputDirectory() = classesDir bridge.run( - virtualSrcFiles.toArray, + virtualSrcFiles, new TestDependencyChanges, Array("-Yforce-sbt-phases", "-classpath", classesDirPath, "-usejavacp", "-d", classesDirPath), output, @@ -158,14 +157,14 @@ class ScalaCompilerForUnitTesting { (files.flatten.toSeq, analysisCallback) } - def compileSrcs(srcs: String*): (Seq[File], TestCallback) = { + def compileSrcs(srcs: String*): (Seq[VirtualFile], TestCallback) = { compileSrcs(List(srcs.toList)) } - private def prepareSrcFile(baseDir: File, fileName: String, src: String): File = { + private def prepareSrcFile(baseDir: File, fileName: String, src: String): VirtualFile = { val srcFile = new File(baseDir, fileName) IO.write(srcFile, src) - srcFile + new TestVirtualFile(srcFile.toPath) } } diff --git a/sbt-bridge/test/xsbt/TestVirtualFile.scala b/sbt-bridge/test/xsbt/TestVirtualFile.scala index db00038272a8..2c7729d0a6cd 100644 --- a/sbt-bridge/test/xsbt/TestVirtualFile.scala +++ b/sbt-bridge/test/xsbt/TestVirtualFile.scala @@ -1,6 +1,6 @@ package xsbt -import xsbti.PathBasedFile +import xsbti.{PathBasedFile, VirtualFileRef} import java.nio.file.{Files, Path} import scala.io.Source import scala.io.Codec @@ -8,7 +8,17 @@ import scala.io.Codec class TestVirtualFile(path: Path) extends PathBasedFile: override def contentHash(): Long = ??? override def input(): java.io.InputStream = Files.newInputStream(path) - override def id(): String = name() + lazy val absolutePath: String = path.toAbsolutePath.toString() + override def id(): String = absolutePath override def name(): String = path.toFile.getName override def names(): Array[String] = ??? override def toPath(): Path = path + + + override def hashCode(): Int = absolutePath.hashCode() + + override def equals(x: Any): Boolean = this.eq(x.asInstanceOf[AnyRef]) || x.match { + case vf: VirtualFileRef => vf.id() == id() + } + + diff --git a/sbt-bridge/test/xsbti/TestCallback.scala b/sbt-bridge/test/xsbti/TestCallback.scala index a0919dc69bc4..0f48ad9a18ef 100644 --- a/sbt-bridge/test/xsbti/TestCallback.scala +++ b/sbt-bridge/test/xsbti/TestCallback.scala @@ -14,40 +14,37 @@ class TestCallback extends AnalysisCallback { case class TestUsedName(name: String, scopes: EnumSet[UseScope]) val classDependencies = new ArrayBuffer[(String, String, DependencyContext)] - val binaryDependencies = new ArrayBuffer[(File, String, String, File, DependencyContext)] - val products = new ArrayBuffer[(File, File)] + val binaryDependencies = new ArrayBuffer[(Path, String, String, VirtualFileRef, DependencyContext)] + val products = new ArrayBuffer[(VirtualFileRef, Path)] val usedNamesAndScopes = scala.collection.mutable.Map.empty[String, Set[TestUsedName]].withDefaultValue(Set.empty) - val classNames = scala.collection.mutable.Map.empty[File, Set[(String, String)]].withDefaultValue(Set.empty) - val apis: scala.collection.mutable.Map[File, Seq[ClassLike]] = scala.collection.mutable.Map.empty + val classNames = scala.collection.mutable.Map.empty[VirtualFileRef, Set[(String, String)]].withDefaultValue(Set.empty) + val apis: scala.collection.mutable.Map[VirtualFileRef, Seq[ClassLike]] = scala.collection.mutable.Map.empty def usedNames = usedNamesAndScopes.view.mapValues(_.map(_.name)).toMap - override def startSource(source: File): Unit = { + override def startSource(source: File): Unit = ??? + override def startSource(source: VirtualFile): Unit = { assert(!apis.contains(source), s"startSource can be called only once per source file: $source") apis(source) = Seq.empty } - override def startSource(source: VirtualFile): Unit = ??? - override def binaryDependency(binary: File, name: String, fromClassName: String, source: File, context: DependencyContext): Unit = { + override def binaryDependency(binary: File, name: String, fromClassName: String, source: File, context: DependencyContext): Unit = ??? + override def binaryDependency(binary: Path, name: String, fromClassName: String, source: VirtualFileRef, context: DependencyContext): Unit = { binaryDependencies += ((binary, name, fromClassName, source, context)) } - override def binaryDependency(binary: Path, name: String, fromClassName: String, source: VirtualFileRef, context: DependencyContext): Unit = ??? - override def generatedNonLocalClass(source: File, - module: File, - binaryClassName: String, - srcClassName: String): Unit = { + override def generatedNonLocalClass(source: File, module: File, binaryClassName: String, srcClassName: String): Unit = ??? + override def generatedNonLocalClass(source: VirtualFileRef, module: Path, binaryClassName: String, srcClassName: String): Unit = { products += ((source, module)) classNames(source) += ((srcClassName, binaryClassName)) () } - override def generatedNonLocalClass(source: VirtualFileRef, module: Path, binaryClassName: String, srcClassName: String): Unit = ??? - override def generatedLocalClass(source: File, module: File): Unit = { + override def generatedLocalClass(source: File, module: File): Unit = ??? + override def generatedLocalClass(source: VirtualFileRef, module: Path): Unit = { products += ((source, module)) () } - override def generatedLocalClass(source: VirtualFileRef, module: Path): Unit = ??? override def classDependency(onClassName: String, sourceClassName: String, context: DependencyContext): Unit = { if (onClassName != sourceClassName) classDependencies += ((onClassName, sourceClassName, context)) @@ -57,10 +54,10 @@ class TestCallback extends AnalysisCallback usedNamesAndScopes(className) += TestUsedName(name, scopes) } - override def api(source: File, classApi: ClassLike): Unit = { + override def api(source: File, classApi: ClassLike): Unit = ??? + override def api(source: VirtualFileRef, classApi: ClassLike): Unit = { apis(source) = classApi +: apis(source) } - override def api(source: VirtualFileRef, classApi: ClassLike): Unit = ??? override def problem(category: String, pos: xsbti.Position, message: String, severity: xsbti.Severity, reported: Boolean): Unit = () override def dependencyPhaseCompleted(): Unit = () diff --git a/sbt-test/sbt-dotty/compiler-plugin/plugin/DivideZero.scala b/sbt-test/sbt-dotty/compiler-plugin/plugin/DivideZero.scala index b7f1b1007420..c6fac6b796c0 100644 --- a/sbt-test/sbt-dotty/compiler-plugin/plugin/DivideZero.scala +++ b/sbt-test/sbt-dotty/compiler-plugin/plugin/DivideZero.scala @@ -19,8 +19,8 @@ class DivideZero extends PluginPhase with StandardPlugin { val phaseName = name - override val runsAfter = Set(Staging.name) - override val runsBefore = Set(Pickler.name) + override val runsAfter = Set(Pickler.name) + override val runsBefore = Set(Staging.name) def init(options: List[String]): List[PluginPhase] = this :: Nil diff --git a/sbt-test/source-dependencies/compactify/src/main/scala/Nested.scala b/sbt-test/source-dependencies/compactify/src/main/scala/Nested.scala index 798868d72640..4b1597d287d4 100644 --- a/sbt-test/source-dependencies/compactify/src/main/scala/Nested.scala +++ b/sbt-test/source-dependencies/compactify/src/main/scala/Nested.scala @@ -38,4 +38,6 @@ class TopLevel2 object TopLevel3 -class TopLevel4 \ No newline at end of file +class TopLevel4 + +object TopLevelModuleSuffix$ diff --git a/sbt-test/source-dependencies/compactify/test b/sbt-test/source-dependencies/compactify/test index b56be3e5d4aa..64be9af369b5 100644 --- a/sbt-test/source-dependencies/compactify/test +++ b/sbt-test/source-dependencies/compactify/test @@ -5,4 +5,4 @@ -> outputEmpty $ delete src/main/scala/For.scala src/main/scala/Nested.scala > compile -> outputEmpty \ No newline at end of file +> outputEmpty diff --git a/sbt-test/tasty-compat/add-overload/build.sbt b/sbt-test/tasty-compat/add-overload/build.sbt index 82dc596134c8..52a04f07148e 100644 --- a/sbt-test/tasty-compat/add-overload/build.sbt +++ b/sbt-test/tasty-compat/add-overload/build.sbt @@ -16,7 +16,7 @@ lazy val `a-changes` = project.in(file("a-changes")) lazy val c = project.in(file(".")) .settings( - scalacOptions ++= Seq("-from-tasty", "-Ycheck:readTasty"), + scalacOptions ++= Seq("-from-tasty", "-Ycheck:readTasty", "-Xfatal-warnings"), Compile / sources := Seq(new java.io.File("c-input/B.tasty")), Compile / unmanagedClasspath += (ThisBuild / baseDirectory).value / "c-input", Compile / classDirectory := (ThisBuild / baseDirectory).value / "c-output"