From a506548bc377efe7ac04bc83d5ef1695538a37a3 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 8 Dec 2020 13:37:32 +0000 Subject: [PATCH] Add Scala 3 to MiMa's version matrix --- .travis.yml | 1 + .../lib/analyze/method/MethodChecker.scala | 15 +++--- .../typesafe/tools/mima/lib/AppRunTest.scala | 6 ++- .../tools/mima/lib/CollectProblemsTest.scala | 29 ++++++------ .../tools/mima/lib/ScalaCompiler.scala | 11 +++-- .../com/typesafe/tools/mima/lib/Test.scala | 46 ++++++++++++------- .../typesafe/tools/mima/lib/TestCase.scala | 28 +++++++++-- .../com/typesafe/tools/mima/lib/TestCli.scala | 24 +++++----- .../app/App.scala | 3 +- .../v2/A.scala | 2 +- .../app/App.scala | 3 +- .../problems-3.txt | 7 +++ .../app/App.scala | 1 + .../problems-3.txt | 4 ++ .../class-becomes-final-nok/app/App.scala | 3 +- .../app/App.scala | 2 +- .../problems-3.txt | 7 +++ .../problems-3.txt | 6 +++ .../class-method-generics-nok/app/App.scala | 4 +- .../v1/Foo.scala | 2 +- .../v2/Foo.scala | 2 +- .../problems-3.txt | 5 ++ .../problems-3.txt | 3 ++ .../testAppRun-3.pending | 1 + .../testAppRun-3.insane | 4 ++ .../problems-3.txt | 4 ++ .../problems-3.txt | 7 +++ .../problems-3.txt | 4 ++ .../app/App.scala | 2 +- .../v1/A.scala | 2 +- .../app/App.scala | 2 +- .../v1/A.scala | 8 ++-- .../v2/A.scala | 5 +- .../app/App.scala | 2 +- .../v1/A.scala | 8 ++-- .../v2/A.scala | 5 +- .../problems-3.txt | 4 ++ .../problems-3.txt | 4 ++ .../v1/A.scala | 4 +- .../problems-3.txt | 4 ++ .../problems-3.txt | 1 + .../testAppRun-3.pending | 4 ++ .../typesafe/tools/mima/plugin/SbtMima.scala | 9 ++-- 43 files changed, 214 insertions(+), 84 deletions(-) create mode 100644 functional-tests/src/test/case-class-concrete-becomes-abstract-nok/problems-3.txt create mode 100644 functional-tests/src/test/case-class-constructor-becomes-private-ok/problems-3.txt create mode 100644 functional-tests/src/test/class-constructor-generics-nok/problems-3.txt create mode 100644 functional-tests/src/test/class-method-abstract-override-of-concrete-supertrait-method-nok/problems-3.txt create mode 100644 functional-tests/src/test/class-var-becomes-final-nok/problems-3.txt create mode 100644 functional-tests/src/test/java-class-static-varargs-to-scala-object-annotated-varargs-ok/problems-3.txt create mode 100644 functional-tests/src/test/java-class-static-varargs-to-scala-object-annotated-varargs-ok/testAppRun-3.pending create mode 100644 functional-tests/src/test/java-class-virtual-to-static-method-nok/testAppRun-3.insane create mode 100644 functional-tests/src/test/moving-method-upward-from-trait-to-class-nok/problems-3.txt create mode 100644 functional-tests/src/test/object-removing-inner-object-nok/problems-3.txt create mode 100644 functional-tests/src/test/public-method-becomes-package-private-nok/problems-3.txt create mode 100644 functional-tests/src/test/trait-deleting-concrete-methods-is-nok/problems-3.txt create mode 100644 functional-tests/src/test/trait-pushing-up-concrete-methods-is-nok/problems-3.txt create mode 100644 functional-tests/src/test/type-parameters-change-breaks-method-signature-nok/problems-3.txt create mode 100644 functional-tests/src/test/value-class-generic-replacement-nok/problems-3.txt create mode 100644 functional-tests/src/test/value-class-generic-replacement-nok/testAppRun-3.pending diff --git a/.travis.yml b/.travis.yml index d2ce4685..1ccf011d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ jobs: - { name: testFunctional 2.11, script: sbt "functional-tests/runMain com.typesafe.tools.mima.lib.UnitTests -211" } - { name: testFunctional 2.12, script: sbt "functional-tests/runMain com.typesafe.tools.mima.lib.UnitTests -212" } - { name: testFunctional 2.13, script: sbt "functional-tests/runMain com.typesafe.tools.mima.lib.UnitTests -213" } + - { name: testFunctional 3, script: sbt "functional-tests/runMain com.typesafe.tools.mima.lib.UnitTests -3" } - { name: scripted 1/2, script: sbt "scripted sbt-mima-plugin/*1of2" } - { name: scripted 2/2, script: sbt "scripted sbt-mima-plugin/*2of2" } diff --git a/core/src/main/scala/com/typesafe/tools/mima/lib/analyze/method/MethodChecker.scala b/core/src/main/scala/com/typesafe/tools/mima/lib/analyze/method/MethodChecker.scala index 47e91906..09b22dcb 100644 --- a/core/src/main/scala/com/typesafe/tools/mima/lib/analyze/method/MethodChecker.scala +++ b/core/src/main/scala/com/typesafe/tools/mima/lib/analyze/method/MethodChecker.scala @@ -13,9 +13,10 @@ private[analyze] object MethodChecker { /** Analyze incompatibilities that may derive from new methods in `newclazz`. */ private def checkNew(oldclazz: ClassInfo, newclazz: ClassInfo): List[Problem] = { - (if (newclazz.isClass) Nil else checkEmulatedConcreteMethodsProblems(oldclazz, newclazz)) ::: - checkDeferredMethodsProblems(oldclazz, newclazz) ::: - checkInheritedNewAbstractMethodProblems(oldclazz, newclazz) + val problems1 = if (newclazz.isClass) Nil else checkEmulatedConcreteMethodsProblems(oldclazz, newclazz) + val problems2 = checkDeferredMethodsProblems(oldclazz, newclazz) + val problems3 = checkInheritedNewAbstractMethodProblems(oldclazz, newclazz) + problems1 ::: problems2 ::: problems3 } private def checkExisting1(oldmeth: MethodInfo, newclazz: ClassInfo): Option[Problem] = { @@ -138,11 +139,9 @@ private[analyze] object MethodChecker { for { newmeth <- newclazz.deferredMethods.iterator problem <- oldclazz.lookupMethods(newmeth).find(_.descriptor == newmeth.descriptor) match { - case None => Some(ReversedMissingMethodProblem(newmeth)) - case Some(oldmeth) => - if (newclazz.isClass && oldmeth.isConcrete) - Some(ReversedAbstractMethodProblem(newmeth)) - else None + case None => Some(ReversedMissingMethodProblem(newmeth)) + case Some(oldmeth) if newclazz.isClass && oldmeth.isConcrete => Some(ReversedAbstractMethodProblem(newmeth)) + case Some(_) => None } } yield problem }.toList diff --git a/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/AppRunTest.scala b/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/AppRunTest.scala index 35dd5199..b6b5f73b 100644 --- a/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/AppRunTest.scala +++ b/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/AppRunTest.scala @@ -13,11 +13,15 @@ object AppRunTest { def testAppRun1(testCase: TestCase, v1: Directory, v2: Directory, oracleFile: Path): Try[Unit] = for { () <- testCase.compileBoth pending = testCase.versionedFile("testAppRun.pending").exists + insane = testCase.versionedFile("testAppRun.insane").exists expectOk = testCase.blankFile(testCase.versionedFile(oracleFile)) //() <- testCase.compileApp(v2) // compile app with v2 //() <- testCase.runMain(v2) // sanity check 1: run app with v2 () <- testCase.compileApp(v1) // recompile app with v1 - () <- testCase.runMain(v1) // sanity check 2: run app with v1 + () <- testCase.runMain(v1) match { // sanity check 2: run app with v1 + case Failure(t) if !insane => Failure(new Exception("Sanity runMain check failed", t, true, false) {}) + case _ => Success(()) + } () <- testCase.runMain(v2) match { // test: run app, compiled with v1, with v2 case Failure(t) if !pending && expectOk => Failure(t) case Success(()) if !pending && !expectOk => Failure(new Exception("expected running App to fail")) diff --git a/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/CollectProblemsTest.scala b/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/CollectProblemsTest.scala index b5309732..d4fd3aa4 100644 --- a/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/CollectProblemsTest.scala +++ b/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/CollectProblemsTest.scala @@ -31,27 +31,24 @@ object CollectProblemsTest { case Forwards => "other" } - // diff between the oracle and the collected problems - val unexpected = problems.filter(p => !expected.contains(p.description(affectedVersion))) - val unreported = expected.diff(problems.map(_.description(affectedVersion))) + val reported = problems.map(_.description(affectedVersion)) val msg = new StringBuilder("\n") - def pp(start: String, lines: List[String]) = { - if (lines.isEmpty) () - else lines.sorted.distinct.addString(msg, s"$start (${lines.size}):\n - ", "\n - ", "\n") - } - pp("The following problem(s) were expected but not reported", unreported) - pp("The following problem(s) were reported but not expected", unexpected.map(_.description(affectedVersion))) - pp("Filter with:", unexpected.flatMap(_.howToFilter)) - pp("Or filter with:", unexpected.flatMap(p => p.matchName.map { matchName => - s"{ matchName=$dq$matchName$dq , problemName=${p.getClass.getSimpleName} }" - })) + def pp(start: String, lines: List[String]) = + if (lines.nonEmpty) { + msg.append(s"$start (${lines.size}):") + lines.sorted.distinct.map("\n - " + _).foreach(msg.append(_)) + msg.append("\n") + } + + pp("The following problem(s) were expected but not reported", expected.diff(reported)) + pp("The following problem(s) were reported but not expected", reported.diff(expected)) msg.mkString match { case "\n" => Success(()) - case msg => Failure(new Exception(msg)) + case msg => + Console.err.println(msg) + Failure(new Exception("CollectProblemsTest failure")) } } - - private final val dq = '"' // scala/bug#6476 -.- } diff --git a/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/ScalaCompiler.scala b/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/ScalaCompiler.scala index d75b18cc..4793359a 100644 --- a/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/ScalaCompiler.scala +++ b/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/ScalaCompiler.scala @@ -7,7 +7,10 @@ import scala.util.{ Failure, Success, Try } import coursier._ final class ScalaCompiler(val version: String) { - val jars = Coursier.fetch(Dependency(mod"org.scala-lang:scala-compiler", version)) + val isScala3 = version.startsWith("3.") + + val name = if (isScala3) ModuleName(s"scala3-compiler_$version") else name"scala-compiler" + val jars = Coursier.fetch(Dependency(Module(org"org.scala-lang", name), version)) val classLoader = new URLClassLoader(jars.toArray.map(_.toURI.toURL), parentClassLoader()) @@ -17,7 +20,8 @@ final class ScalaCompiler(val version: String) { def compile(args: Seq[String]): Try[Unit] = { import scala.language.reflectiveCalls - val cls = classLoader.loadClass("scala.tools.nsc.Main$") + val clsName = if (isScala3) "dotty.tools.dotc.Main$" else "scala.tools.nsc.Main$" + val cls = classLoader.loadClass(clsName) type Main = { def process(args: Array[String]): Any; def reporter: Reporter } type Reporter = { def hasErrors: Boolean } val m = cls.getField("MODULE$").get(null).asInstanceOf[Main] @@ -25,8 +29,9 @@ final class ScalaCompiler(val version: String) { val success = m.process(args.toArray) match { case b: Boolean => b case null => !m.reporter.hasErrors // nsc 2.11 + case x => !x.asInstanceOf[Reporter].hasErrors // dotc } if (success) Success(()) else Failure(new Exception("scalac failed")) - } + }.flatten } } diff --git a/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/Test.scala b/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/Test.scala index a0775838..005a845d 100644 --- a/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/Test.scala +++ b/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/Test.scala @@ -10,11 +10,19 @@ object Test { def pass = s"${Console.GREEN}\u2713${Console.RESET}" // check mark (green) def fail = s"${Console.RED}\u2717${Console.RESET}" // cross mark (red) - def testAll(tests: List[Test]): Try[Unit] = { - tests.iterator.map(_.run()).foldLeft(Try(())) { - case (res @ Failure(e1), Failure(e2)) => e1.addSuppressed(e2); res - case (res @ Failure(_), _) => res - case (_, res) => res + def testAll(tests: List[Test1]): Try[Unit] = { + val (successes, failures) = tests.map(t => t -> Test.run1(t.label, t.action)).partition(_._2.isSuccess) + println(s"${tests.size} tests, ${successes.size} successes, ${failures.size} failures") + if (failures.nonEmpty) { + val failureNames = failures.map { case ((t1, _)) => t1.name } + println("Failures:") + failureNames.foreach(name => println(s"* $name")) + println(s"functional-tests/Test/run ${failureNames.mkString(" ")}") + } + failures.foldLeft(Try(())) { + case (res @ Failure(e1), (_, Failure(e2))) => e1.addSuppressed(e2); res + case (res @ Failure(_), _) => res + case (_, (_, res)) => res } } @@ -24,17 +32,6 @@ object Test { case res @ Failure(ex) => println(s"- $fail $label: $ex"); res } } - - implicit class TestOps(private val t: Test) extends AnyVal { - def tests: List[Test1] = t match { - case t1: Test1 => List(t1) - case Tests(tests) => tests - } - - def munitTests: List[GenericTest[Unit]] = for { - test <- t.tests - } yield new GenericTest(test.label, () => test.unsafeRunTest(), Set.empty, Location.empty) - } } sealed trait Test { @@ -51,5 +48,22 @@ sealed trait Test { } } +object Test1 { + implicit class Ops(private val t: Test1) extends AnyVal { + def name: String = t.label.indexOf(" / ") match { + case -1 => t.label + case idx => t.label.drop(idx + 3) + } + } +} + +object Tests { + implicit class Ops(private val t: Tests) extends AnyVal { + def munitTests: List[GenericTest[Unit]] = for { + test <- t.tests + } yield new GenericTest(test.label, () => test.unsafeRunTest(), Set.empty, Location.empty) + } +} + case class Test1(label: String, action: () => Try[Unit]) extends Test case class Tests(tests: List[Test1]) extends Test diff --git a/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/TestCase.scala b/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/TestCase.scala index 5c11fb26..ca286b0a 100644 --- a/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/TestCase.scala +++ b/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/TestCase.scala @@ -4,6 +4,7 @@ import java.io.{ ByteArrayOutputStream, PrintStream } import java.net.{ URI, URLClassLoader } import javax.tools._ +import scala.annotation.tailrec import scala.collection.JavaConverters._ import scala.collection.mutable import scala.reflect.internal.util.BatchSourceFile @@ -14,7 +15,7 @@ import com.typesafe.tools.mima.core.ClassPath final class TestCase(val baseDir: Directory, val scalaCompiler: ScalaCompiler, val javaCompiler: JavaCompiler) { def name = baseDir.name - def scalaBinaryVersion = scalaCompiler.version.take(4) + def scalaBinaryVersion = if (scalaCompiler.isScala3) "3" else scalaCompiler.version.take(4) def scalaJars = scalaCompiler.jars val srcV1 = (baseDir / "v1").toDirectory @@ -42,7 +43,7 @@ final class TestCase(val baseDir: Directory, val scalaCompiler: ScalaCompiler, v val sourceFiles = lsSrcs(srcDir) if (sourceFiles.forall(_.isJava)) return Success(()) val bootcp = ClassPath.join(scalaJars.map(_.getPath)) - val cpOpt = if (cp.isEmpty) Nil else List("-cp", ClassPath.join(cp.map(_.path))) + val cpOpt = if (cp.isEmpty) Nil else List("-classpath", ClassPath.join(cp.map(_.path))) val paths = sourceFiles.map(_.path) val args = "-bootclasspath" :: bootcp :: cpOpt ::: "-d" :: s"$out" :: paths scalaCompiler.compile(args) @@ -78,7 +79,16 @@ final class TestCase(val baseDir: Directory, val scalaCompiler: ScalaCompiler, v System.setErr(printStream) Console.withErr(printStream) { Console.withOut(printStream) { - Try(meth.invoke(null, new Array[String](0)): Unit) + try { + meth.invoke(null, new Array[String](0)) + Success(()) + } catch { + case e: VirtualMachineError => throw e + case e: ThreadDeath => throw e + case e: InterruptedException => throw e + case e: scala.util.control.ControlThrowable => throw e // don't rethrow LinkageError + case e: Throwable => Failure(rootCause(e)) + } } } } finally { @@ -98,9 +108,11 @@ final class TestCase(val baseDir: Directory, val scalaCompiler: ScalaCompiler, v val p = baseDir.resolve(path).toFile val p211 = (p.parent / (s"${p.stripExtension}-2.11")).addExtension(p.extension).toFile val p212 = (p.parent / (s"${p.stripExtension}-2.12")).addExtension(p.extension).toFile + val p3 = (p.parent / (s"${p.stripExtension}-3" )).addExtension(p.extension).toFile scalaBinaryVersion match { case "2.11" => if (p211.exists) p211 else if (p212.exists) p212 else p case "2.12" => if (p212.exists) p212 else p + case "3" => if (p3.exists) p3 else p case _ => p } } @@ -112,5 +124,15 @@ final class TestCase(val baseDir: Directory, val scalaCompiler: ScalaCompiler, v () } + @tailrec private def rootCause(x: Throwable): Throwable = x match { + case _: ExceptionInInitializerError | + _: java.lang.reflect.InvocationTargetException | + _: java.lang.reflect.UndeclaredThrowableException | + _: java.util.concurrent.ExecutionException + if x.getCause != null => + rootCause(x.getCause) + case _ => x + } + override def toString = s"TestCase(baseDir=${baseDir.name}, scalaVersion=${scalaCompiler.version})" } diff --git a/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/TestCli.scala b/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/TestCli.scala index b5dce188..41c2991f 100644 --- a/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/TestCli.scala +++ b/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/TestCli.scala @@ -9,10 +9,11 @@ import scala.util.{ Properties => StdLibProps } object TestCli { val scala211 = "2.11.12" - val scala212 = "2.12.11" - val scala213 = "2.13.2" + val scala212 = "2.12.12" + val scala213 = "2.13.4" + val scala3 = "3.0.0-M3" val hostScalaVersion = StdLibProps.scalaPropOrNone("maven.version.number").get - val allScalaVersions = List(scala211, scala212, scala213) + val allScalaVersions = List(scala211, scala212, scala213, scala3) val testsDir = Directory("functional-tests/src/test") def argsToTests(args: List[String], runTestCase: TestCase => Try[Unit]): Tests = @@ -24,7 +25,7 @@ object TestCli { def testCaseToTest1(tc: TestCase, runTestCase: TestCase => Try[Unit]): Test1 = Test(s"${tc.scalaBinaryVersion} / ${tc.name}", runTestCase(tc)) - def fromArgs(args: List[String]): List[TestCase] = fromConf(go(args, Conf(Nil, Nil))) + def fromArgs(args: List[String]): List[TestCase] = fromConf(readArgs(args, Conf(Nil, Nil))) @tailrec def postProcessConf(conf: Conf): Conf = conf match { case Conf(Nil, _) => postProcessConf(conf.copy(scalaVersions = List(hostScalaVersion))) @@ -49,13 +50,14 @@ object TestCli { final case class Conf(scalaVersions: List[String], dirs: List[Directory]) - @tailrec private def go(argv: List[String], conf: Conf): Conf = argv match { - case "-213" :: xs => go(xs, conf.copy(scalaVersions = scala213 :: conf.scalaVersions)) - case "-212" :: xs => go(xs, conf.copy(scalaVersions = scala212 :: conf.scalaVersions)) - case "-211" :: xs => go(xs, conf.copy(scalaVersions = scala211 :: conf.scalaVersions)) - case "--scala-version" :: sv :: xs => go(xs, conf.copy(scalaVersions = sv :: conf.scalaVersions)) - case "--cross" :: xs => go(xs, conf.copy(scalaVersions = List(scala211, scala212, scala213))) - case s :: xs => go(xs, conf.copy(dirs = testDirs(s) ::: conf.dirs)) + @tailrec private def readArgs(args: List[String], conf: Conf): Conf = args match { + case "-3" :: xs => readArgs(xs, conf.copy(scalaVersions = scala3 :: conf.scalaVersions)) + case "-213" :: xs => readArgs(xs, conf.copy(scalaVersions = scala213 :: conf.scalaVersions)) + case "-212" :: xs => readArgs(xs, conf.copy(scalaVersions = scala212 :: conf.scalaVersions)) + case "-211" :: xs => readArgs(xs, conf.copy(scalaVersions = scala211 :: conf.scalaVersions)) + case "--scala-version" :: sv :: xs => readArgs(xs, conf.copy(scalaVersions = sv :: conf.scalaVersions)) + case "--cross" :: xs => readArgs(xs, conf.copy(scalaVersions = allScalaVersions)) + case s :: xs => readArgs(xs, conf.copy(dirs = testDirs(s) ::: conf.dirs)) case Nil => conf } diff --git a/functional-tests/src/test/abstract-class-extending-new-trait-with-abstract-method-ok/app/App.scala b/functional-tests/src/test/abstract-class-extending-new-trait-with-abstract-method-ok/app/App.scala index 9b7d946d..5a466d39 100644 --- a/functional-tests/src/test/abstract-class-extending-new-trait-with-abstract-method-ok/app/App.scala +++ b/functional-tests/src/test/abstract-class-extending-new-trait-with-abstract-method-ok/app/App.scala @@ -1,5 +1,6 @@ object App { def main(args: Array[String]): Unit = { - println(new A { def foo = () }.foo) + object a extends A { def foo() = () } + println(a.foo()) } } diff --git a/functional-tests/src/test/abstract-class-extending-new-trait-with-abstract-method-ok/v2/A.scala b/functional-tests/src/test/abstract-class-extending-new-trait-with-abstract-method-ok/v2/A.scala index a1638dbb..4a1e6252 100644 --- a/functional-tests/src/test/abstract-class-extending-new-trait-with-abstract-method-ok/v2/A.scala +++ b/functional-tests/src/test/abstract-class-extending-new-trait-with-abstract-method-ok/v2/A.scala @@ -1,5 +1,5 @@ abstract class A extends B { - def foo: Unit + def foo(): Unit } trait B { diff --git a/functional-tests/src/test/case-class-becomes-final-nok/app/App.scala b/functional-tests/src/test/case-class-becomes-final-nok/app/App.scala index 4181cf4a..c5c2d66b 100644 --- a/functional-tests/src/test/case-class-becomes-final-nok/app/App.scala +++ b/functional-tests/src/test/case-class-becomes-final-nok/app/App.scala @@ -1,5 +1,6 @@ object App { def main(args: Array[String]): Unit = { - println(new A { def baz = 2 }.baz) + object a extends A { def baz = 2 } + println(a.baz) } } diff --git a/functional-tests/src/test/case-class-concrete-becomes-abstract-nok/problems-3.txt b/functional-tests/src/test/case-class-concrete-becomes-abstract-nok/problems-3.txt new file mode 100644 index 00000000..8eab776e --- /dev/null +++ b/functional-tests/src/test/case-class-concrete-becomes-abstract-nok/problems-3.txt @@ -0,0 +1,7 @@ +class A was concrete; is declared abstract in new version +method apply()A in object A does not have a correspondent in new version +static method apply()A in class A does not have a correspondent in new version +the type hierarchy of object A is different in new version. Missing types {scala.deriving.Mirror$Product} +method fromProduct(scala.Product)A in object A does not have a correspondent in new version +static method fromProduct(scala.Product)A in class A does not have a correspondent in new version +# the last 2 are not present in Scala 2 diff --git a/functional-tests/src/test/case-class-constructor-becomes-private-ok/app/App.scala b/functional-tests/src/test/case-class-constructor-becomes-private-ok/app/App.scala index 30ff850c..b74bfbea 100644 --- a/functional-tests/src/test/case-class-constructor-becomes-private-ok/app/App.scala +++ b/functional-tests/src/test/case-class-constructor-becomes-private-ok/app/App.scala @@ -1,5 +1,6 @@ object App { def main(args: Array[String]): Unit = { println(new A) + println(A()) } } diff --git a/functional-tests/src/test/case-class-constructor-becomes-private-ok/problems-3.txt b/functional-tests/src/test/case-class-constructor-becomes-private-ok/problems-3.txt new file mode 100644 index 00000000..4bca3932 --- /dev/null +++ b/functional-tests/src/test/case-class-constructor-becomes-private-ok/problems-3.txt @@ -0,0 +1,4 @@ +# In Scala 2 none of these changes are present. Positive progress IMO. +method apply()A in object A does not have a correspondent in new version +static method apply()A in class A does not have a correspondent in new version +method copy()A in class A does not have a correspondent in new version diff --git a/functional-tests/src/test/class-becomes-final-nok/app/App.scala b/functional-tests/src/test/class-becomes-final-nok/app/App.scala index 4181cf4a..c5c2d66b 100644 --- a/functional-tests/src/test/class-becomes-final-nok/app/App.scala +++ b/functional-tests/src/test/class-becomes-final-nok/app/App.scala @@ -1,5 +1,6 @@ object App { def main(args: Array[String]): Unit = { - println(new A { def baz = 2 }.baz) + object a extends A { def baz = 2 } + println(a.baz) } } diff --git a/functional-tests/src/test/class-constructor-generics-nok/app/App.scala b/functional-tests/src/test/class-constructor-generics-nok/app/App.scala index b47e8e97..0692de6b 100644 --- a/functional-tests/src/test/class-constructor-generics-nok/app/App.scala +++ b/functional-tests/src/test/class-constructor-generics-nok/app/App.scala @@ -1,6 +1,6 @@ object App { def main(args: Array[String]): Unit = { - val result: String = new OptionPane(("foo", "bar")).show + val result: String = new OptionPane(("foo", "bar")).show() println("result: " + result) } } diff --git a/functional-tests/src/test/class-constructor-generics-nok/problems-3.txt b/functional-tests/src/test/class-constructor-generics-nok/problems-3.txt new file mode 100644 index 00000000..b701b973 --- /dev/null +++ b/functional-tests/src/test/class-constructor-generics-nok/problems-3.txt @@ -0,0 +1,7 @@ +method source()scala.Tuple2 in class OptionPane has a different generic signature in new version, where it is ()Lscala/Tuple2;Ljava/lang/String;>; rather than ()Lscala/Tuple2;. See https://github.com/lightbend/mima#incompatiblesignatureproblem +method show()java.lang.String in class OptionPane does not have a correspondent in new version +method this(scala.Tuple2)Unit in class OptionPane has a different generic signature in new version, where it is (Lscala/Tuple2;Ljava/lang/String;>;)V rather than (Lscala/Tuple2;)V. See https://github.com/lightbend/mima#incompatiblesignatureproblem +# In Scala 2 it's: +# ... where it is (Lscala/Tuple2;Ljava/lang/String;>;)V rather than ... +# note there's no new type parameter +# https://github.com/lampepfl/dotty/issues/10834 diff --git a/functional-tests/src/test/class-method-abstract-override-of-concrete-supertrait-method-nok/problems-3.txt b/functional-tests/src/test/class-method-abstract-override-of-concrete-supertrait-method-nok/problems-3.txt new file mode 100644 index 00000000..be25cee7 --- /dev/null +++ b/functional-tests/src/test/class-method-abstract-override-of-concrete-supertrait-method-nok/problems-3.txt @@ -0,0 +1,6 @@ +in new version there is abstract method foo()Int in class B, which does not have a correspondent +# what's missing is: +# abstract method foo()Int in class B does not have a correspondent in new version +# which is a `DirectAbstractMethodProblem`, rather than the above `ReversedAbstractMethodProblem` +# not sure exactly what that means... ¯\_(ツ)_/¯ +# https://github.com/lightbend/mima/issues/590 diff --git a/functional-tests/src/test/class-method-generics-nok/app/App.scala b/functional-tests/src/test/class-method-generics-nok/app/App.scala index f4b6269e..15fcfe2f 100644 --- a/functional-tests/src/test/class-method-generics-nok/app/App.scala +++ b/functional-tests/src/test/class-method-generics-nok/app/App.scala @@ -1,7 +1,7 @@ object App { def main(args: Array[String]): Unit = { val api = new Api - val cov2 = api.cov2[String] + val cov2 = api.cov2[String]() println(cov2) println(api.con1(App)) @@ -12,7 +12,7 @@ object App { def con1(x: Any) = () def con2[T](x: T) = () } - val cov1: Any = abi.cov1 + val cov1: Any = abi.cov1() println(cov1) println(abi.con2[App.type](App)) } diff --git a/functional-tests/src/test/class-method-implementation-result-type-changed-nok/v1/Foo.scala b/functional-tests/src/test/class-method-implementation-result-type-changed-nok/v1/Foo.scala index 21753c6d..46f1bebc 100644 --- a/functional-tests/src/test/class-method-implementation-result-type-changed-nok/v1/Foo.scala +++ b/functional-tests/src/test/class-method-implementation-result-type-changed-nok/v1/Foo.scala @@ -2,5 +2,5 @@ abstract class Super { def bar: Number } class Foo extends Super { - override def bar = 42 + override def bar: Integer = 42 } diff --git a/functional-tests/src/test/class-method-implementation-result-type-changed-nok/v2/Foo.scala b/functional-tests/src/test/class-method-implementation-result-type-changed-nok/v2/Foo.scala index 3847ff0e..98f7e359 100644 --- a/functional-tests/src/test/class-method-implementation-result-type-changed-nok/v2/Foo.scala +++ b/functional-tests/src/test/class-method-implementation-result-type-changed-nok/v2/Foo.scala @@ -2,5 +2,5 @@ abstract class Super { def bar: Number } class Foo extends Super { - override def bar = 42L + override def bar: java.lang.Long = 42L } diff --git a/functional-tests/src/test/class-var-becomes-final-nok/problems-3.txt b/functional-tests/src/test/class-var-becomes-final-nok/problems-3.txt new file mode 100644 index 00000000..5a080cb4 --- /dev/null +++ b/functional-tests/src/test/class-var-becomes-final-nok/problems-3.txt @@ -0,0 +1,5 @@ +# missing: +# method foo()Int in class A is declared final in new version +# method foo_=(Int)Unit in class A is declared final in new version +# not a critical case IMO +# https://github.com/lampepfl/dotty/issues/10835 diff --git a/functional-tests/src/test/java-class-static-varargs-to-scala-object-annotated-varargs-ok/problems-3.txt b/functional-tests/src/test/java-class-static-varargs-to-scala-object-annotated-varargs-ok/problems-3.txt new file mode 100644 index 00000000..9dbf144e --- /dev/null +++ b/functional-tests/src/test/java-class-static-varargs-to-scala-object-annotated-varargs-ok/problems-3.txt @@ -0,0 +1,3 @@ +static method create(Array[java.lang.String])java.lang.String in class A has a different generic signature in new version, where it is ([Ljava/lang/String;)Ljava/lang/String; rather than . See https://github.com/lightbend/mima#incompatiblesignatureproblem +# Scala 3 adds a method signature when it isn't required +# https://github.com/lampepfl/dotty/issues/10837 diff --git a/functional-tests/src/test/java-class-static-varargs-to-scala-object-annotated-varargs-ok/testAppRun-3.pending b/functional-tests/src/test/java-class-static-varargs-to-scala-object-annotated-varargs-ok/testAppRun-3.pending new file mode 100644 index 00000000..3d182a1e --- /dev/null +++ b/functional-tests/src/test/java-class-static-varargs-to-scala-object-annotated-varargs-ok/testAppRun-3.pending @@ -0,0 +1 @@ +# The method signature problem (the redundant new Signature) isn't fatal to the App running successfully. diff --git a/functional-tests/src/test/java-class-virtual-to-static-method-nok/testAppRun-3.insane b/functional-tests/src/test/java-class-virtual-to-static-method-nok/testAppRun-3.insane new file mode 100644 index 00000000..670d4f9b --- /dev/null +++ b/functional-tests/src/test/java-class-virtual-to-static-method-nok/testAppRun-3.insane @@ -0,0 +1,4 @@ +# In Scala 3 `new a.Nested` throws: +# Caused by: java.lang.NoSuchMethodError: A$Nested: method 'void ()' not found +# at App$.main(App.scala:7) +# https://github.com/lampepfl/dotty/issues/10838 diff --git a/functional-tests/src/test/moving-method-upward-from-trait-to-class-nok/problems-3.txt b/functional-tests/src/test/moving-method-upward-from-trait-to-class-nok/problems-3.txt new file mode 100644 index 00000000..b4fc1332 --- /dev/null +++ b/functional-tests/src/test/moving-method-upward-from-trait-to-class-nok/problems-3.txt @@ -0,0 +1,4 @@ +method foo()Int in interface B does not have a correspondent in new version +# missing: +# synthetic static method $init$(B)Unit in interface B does not have a correspondent in new version +# https://github.com/lampepfl/dotty/issues/10839 diff --git a/functional-tests/src/test/object-removing-inner-object-nok/problems-3.txt b/functional-tests/src/test/object-removing-inner-object-nok/problems-3.txt new file mode 100644 index 00000000..7c738278 --- /dev/null +++ b/functional-tests/src/test/object-removing-inner-object-nok/problems-3.txt @@ -0,0 +1,7 @@ +object A#B does not have a correspondent in new version +object A#B#C does not have a correspondent in new version +static field B in object A does not have a correspondent in new version +# in Scala 2 that field problem isn't emitted +# because the field never existed in the first place +# put different: Scala 3 adds a static final A$.B field +# which LGTM as it allows easier access for Java users to nested objects diff --git a/functional-tests/src/test/public-method-becomes-package-private-nok/problems-3.txt b/functional-tests/src/test/public-method-becomes-package-private-nok/problems-3.txt new file mode 100644 index 00000000..88cdddce --- /dev/null +++ b/functional-tests/src/test/public-method-becomes-package-private-nok/problems-3.txt @@ -0,0 +1,4 @@ +# missing: +# static method foo(java.lang.Object,java.lang.Object)java.lang.Object in class bar.A does not have a correspondent in new version +# I guess privateWithin isn't honoured in static method emission in Scala 3 +# https://github.com/lampepfl/dotty/issues/10842 diff --git a/functional-tests/src/test/trait-abstract-var-becomes-concrete-ok/app/App.scala b/functional-tests/src/test/trait-abstract-var-becomes-concrete-ok/app/App.scala index f309ce5d..2586f0ad 100644 --- a/functional-tests/src/test/trait-abstract-var-becomes-concrete-ok/app/App.scala +++ b/functional-tests/src/test/trait-abstract-var-becomes-concrete-ok/app/App.scala @@ -1,6 +1,6 @@ object App { def main(args: Array[String]): Unit = { - val a = new A { var foo = 1 } + object a extends A { var foo = 1 } println(a.foo) a.foo = 10 println(a.foo) diff --git a/functional-tests/src/test/trait-abstract-var-becomes-concrete-ok/v1/A.scala b/functional-tests/src/test/trait-abstract-var-becomes-concrete-ok/v1/A.scala index b6d366e2..325bfb85 100644 --- a/functional-tests/src/test/trait-abstract-var-becomes-concrete-ok/v1/A.scala +++ b/functional-tests/src/test/trait-abstract-var-becomes-concrete-ok/v1/A.scala @@ -3,5 +3,5 @@ trait A { } abstract class B extends A { - override var foo = 2 + var foo = 2 } diff --git a/functional-tests/src/test/trait-added-val-in-new-version-nok/app/App.scala b/functional-tests/src/test/trait-added-val-in-new-version-nok/app/App.scala index 69cc04ac..4b9a66c1 100644 --- a/functional-tests/src/test/trait-added-val-in-new-version-nok/app/App.scala +++ b/functional-tests/src/test/trait-added-val-in-new-version-nok/app/App.scala @@ -1,5 +1,5 @@ object App { def main(args: Array[String]): Unit = { - println(new A {}.foo + 1) + UseA.useA(new A {}) } } diff --git a/functional-tests/src/test/trait-added-val-in-new-version-nok/v1/A.scala b/functional-tests/src/test/trait-added-val-in-new-version-nok/v1/A.scala index 5bb0b7ae..fffe213f 100644 --- a/functional-tests/src/test/trait-added-val-in-new-version-nok/v1/A.scala +++ b/functional-tests/src/test/trait-added-val-in-new-version-nok/v1/A.scala @@ -1,3 +1,5 @@ -trait A { - def foo = 3 -} \ No newline at end of file +trait A + +object UseA { + def useA(a: A): Unit = () +} diff --git a/functional-tests/src/test/trait-added-val-in-new-version-nok/v2/A.scala b/functional-tests/src/test/trait-added-val-in-new-version-nok/v2/A.scala index d33d227f..ff9ad142 100644 --- a/functional-tests/src/test/trait-added-val-in-new-version-nok/v2/A.scala +++ b/functional-tests/src/test/trait-added-val-in-new-version-nok/v2/A.scala @@ -1,4 +1,7 @@ trait A { - def foo = 2 val bar = 3 } + +object UseA { + def useA(a: A): Unit = println(a.bar + 1) +} diff --git a/functional-tests/src/test/trait-added-var-in-new-version-nok/app/App.scala b/functional-tests/src/test/trait-added-var-in-new-version-nok/app/App.scala index 69cc04ac..4b9a66c1 100644 --- a/functional-tests/src/test/trait-added-var-in-new-version-nok/app/App.scala +++ b/functional-tests/src/test/trait-added-var-in-new-version-nok/app/App.scala @@ -1,5 +1,5 @@ object App { def main(args: Array[String]): Unit = { - println(new A {}.foo + 1) + UseA.useA(new A {}) } } diff --git a/functional-tests/src/test/trait-added-var-in-new-version-nok/v1/A.scala b/functional-tests/src/test/trait-added-var-in-new-version-nok/v1/A.scala index 5bb0b7ae..fffe213f 100644 --- a/functional-tests/src/test/trait-added-var-in-new-version-nok/v1/A.scala +++ b/functional-tests/src/test/trait-added-var-in-new-version-nok/v1/A.scala @@ -1,3 +1,5 @@ -trait A { - def foo = 3 -} \ No newline at end of file +trait A + +object UseA { + def useA(a: A): Unit = () +} diff --git a/functional-tests/src/test/trait-added-var-in-new-version-nok/v2/A.scala b/functional-tests/src/test/trait-added-var-in-new-version-nok/v2/A.scala index 6cf5995d..c961a2ae 100644 --- a/functional-tests/src/test/trait-added-var-in-new-version-nok/v2/A.scala +++ b/functional-tests/src/test/trait-added-var-in-new-version-nok/v2/A.scala @@ -1,4 +1,7 @@ trait A { - def foo = 2 var bar = 3 } + +object UseA { + def useA(a: A): Unit = println(a.bar + 1) +} diff --git a/functional-tests/src/test/trait-deleting-concrete-methods-is-nok/problems-3.txt b/functional-tests/src/test/trait-deleting-concrete-methods-is-nok/problems-3.txt new file mode 100644 index 00000000..de6a2241 --- /dev/null +++ b/functional-tests/src/test/trait-deleting-concrete-methods-is-nok/problems-3.txt @@ -0,0 +1,4 @@ +in new version, classes mixing B need be recompiled to wire to the new static mixin forwarder method all super calls to method foo()Int +# missing: +# synthetic static method $init$(B)Unit in interface B does not have a correspondent in new version +# https://github.com/lampepfl/dotty/issues/10839 diff --git a/functional-tests/src/test/trait-pushing-up-concrete-methods-is-nok/problems-3.txt b/functional-tests/src/test/trait-pushing-up-concrete-methods-is-nok/problems-3.txt new file mode 100644 index 00000000..de6a2241 --- /dev/null +++ b/functional-tests/src/test/trait-pushing-up-concrete-methods-is-nok/problems-3.txt @@ -0,0 +1,4 @@ +in new version, classes mixing B need be recompiled to wire to the new static mixin forwarder method all super calls to method foo()Int +# missing: +# synthetic static method $init$(B)Unit in interface B does not have a correspondent in new version +# https://github.com/lampepfl/dotty/issues/10839 diff --git a/functional-tests/src/test/tuple-parametric-type-change-nok/v1/A.scala b/functional-tests/src/test/tuple-parametric-type-change-nok/v1/A.scala index 46bb47a3..832b3407 100644 --- a/functional-tests/src/test/tuple-parametric-type-change-nok/v1/A.scala +++ b/functional-tests/src/test/tuple-parametric-type-change-nok/v1/A.scala @@ -1,3 +1,3 @@ class A { - def foo(): (String,Int) = ("",0) -} \ No newline at end of file + def foo: (String,Int) = ("",0) +} diff --git a/functional-tests/src/test/type-parameters-change-breaks-method-signature-nok/problems-3.txt b/functional-tests/src/test/type-parameters-change-breaks-method-signature-nok/problems-3.txt new file mode 100644 index 00000000..2c918300 --- /dev/null +++ b/functional-tests/src/test/type-parameters-change-breaks-method-signature-nok/problems-3.txt @@ -0,0 +1,4 @@ +method contains(NodeImpl)Boolean in class Tree's type is different in new version, where it is (Node)Boolean instead of (NodeImpl)Boolean +method this()Unit in class Tree has a different generic signature in new version, where it is ()V rather than ()V. See https://github.com/lightbend/mima#incompatiblesignatureproblem +# that last one doesn't emit for Scala 2 +# https://github.com/lampepfl/dotty/issues/10834 diff --git a/functional-tests/src/test/value-class-generic-replacement-nok/problems-3.txt b/functional-tests/src/test/value-class-generic-replacement-nok/problems-3.txt new file mode 100644 index 00000000..8337ffdc --- /dev/null +++ b/functional-tests/src/test/value-class-generic-replacement-nok/problems-3.txt @@ -0,0 +1 @@ +# (see testAppRun-3.pending) diff --git a/functional-tests/src/test/value-class-generic-replacement-nok/testAppRun-3.pending b/functional-tests/src/test/value-class-generic-replacement-nok/testAppRun-3.pending new file mode 100644 index 00000000..b3e59675 --- /dev/null +++ b/functional-tests/src/test/value-class-generic-replacement-nok/testAppRun-3.pending @@ -0,0 +1,4 @@ +# Scalac 3 doesn't have the generic signature fix. +# So MiMa can't spot the problem, which is why problems-3.txt is empty. +# The App correctly fails but the empty problems-3.txt implies it should succeed. So mark pending. +# https://github.com/lampepfl/dotty/issues/10846 diff --git a/sbtplugin/src/main/scala/com/typesafe/tools/mima/plugin/SbtMima.scala b/sbtplugin/src/main/scala/com/typesafe/tools/mima/plugin/SbtMima.scala index a97c294c..e542c825 100644 --- a/sbtplugin/src/main/scala/com/typesafe/tools/mima/plugin/SbtMima.scala +++ b/sbtplugin/src/main/scala/com/typesafe/tools/mima/plugin/SbtMima.scala @@ -32,12 +32,15 @@ object SbtMima { } private def sanityCheckScalaVersion(scalaVersion: String) = { - scalaVersion.take(5) match { - case "2.11." | "2.12." | "2.13." => () // ok - case _ => throw new IllegalArgumentException(s"MiMa supports Scala 2.11-2.13, not $scalaVersion") + scalaBinaryVersion(scalaVersion) match { + case "2.11" | "2.12" | "2.13" | "3" => () // ok + case _ => throw new IllegalArgumentException(s"MiMa supports Scala 2.11, 2.12, 2.13 and 3, not $scalaVersion") } } + private def scalaBinaryVersion(version: String) = + if (version.startsWith("3.")) "3" else version.take(4) + /** Reports binary compatibility errors for a module. */ def reportModuleErrors( module: ModuleID,