diff --git a/scalafix-interfaces/src/main/java/scalafix/interfaces/Scalafix.java b/scalafix-interfaces/src/main/java/scalafix/interfaces/Scalafix.java index 8ebc23582..a8059e6ed 100644 --- a/scalafix-interfaces/src/main/java/scalafix/interfaces/Scalafix.java +++ b/scalafix-interfaces/src/main/java/scalafix/interfaces/Scalafix.java @@ -86,12 +86,13 @@ public interface Scalafix { *

* The custom classloader optionally provided with {@link ScalafixArguments#withToolClasspath} to compile and * classload external rules must have the classloader of the returned instance as ancestor to share a common - * loaded instance of `scalafix-core`, and therefore have been compiled against the requested Scala binary version. + * loaded instance of `scalafix-core`, and therefore have been compiled against the requested Scala version. * - * @param requestedScalaVersion The Scala version ("3.3.4" for example) available in the classloader of the + * @param requestedScalaVersion A full Scala version (i.e. "3.3.4") or a major.minor one (i.e. "3.3") to infer + * the major.minor Scala version that should be available in the classloader of the * returned instance. To be able to run advanced semantic rules using the Scala - * Presentation Compiler such as ExplicitResultTypes, this must match the version - * that the target classpath was built with, as provided with + * Presentation Compiler such as ExplicitResultTypes, this must be source-compatible + * with the version that the target classpath is built with, as provided with * {@link ScalafixArguments#withScalaVersion}. * @return An implementation of the {@link Scalafix} interface. * @throws ScalafixException in case of errors during artifact resolution/fetching. @@ -106,12 +107,13 @@ static Scalafix fetchAndClassloadInstance(String scalaBinaryVersion) throws Scal *

* The custom classloader optionally provided with {@link ScalafixArguments#withToolClasspath} to compile and * classload external rules must have the classloader of the returned instance as ancestor to share a common - * loaded instance of `scalafix-core`, and therefore have been compiled against the requested Scala binary version. + * loaded instance of `scalafix-core`, and therefore have been compiled against the requested Scala version. * - * @param requestedScalaVersion The Scala version ("3.3.4" for example) available in the classloader of the + * @param requestedScalaVersion A full Scala version (i.e. "3.3.4") or a major.minor one (i.e. "3.3") to infer + * the major.minor Scala version that should be available in the classloader of the * returned instance. To be able to run advanced semantic rules using the Scala - * Presentation Compiler such as ExplicitResultTypes, this must match the version - * that the target classpath was built with, as provided with + * Presentation Compiler such as ExplicitResultTypes, this must be source-compatible + * with the version that the target classpath is built with, as provided with * {@link ScalafixArguments#withScalaVersion}. * @param repositories Maven/Ivy repositories to fetch the JARs from. * @return An implementation of the {@link Scalafix} interface. @@ -120,17 +122,21 @@ static Scalafix fetchAndClassloadInstance(String scalaBinaryVersion) throws Scal static Scalafix fetchAndClassloadInstance(String requestedScalaVersion, List repositories) throws ScalafixException { + String requestedScalaMajorMinorOrMajorVersion = + requestedScalaVersion.replaceAll("^(\\d+\\.\\d+).*", "$1"); + String scalaVersionKey; - if (requestedScalaVersion.startsWith("2.12")) { + if (requestedScalaMajorMinorOrMajorVersion.equals("2.12")) { scalaVersionKey = "scala212"; - } else if (requestedScalaVersion.startsWith("2.13")) { + } else if (requestedScalaMajorMinorOrMajorVersion.equals("2.13") || + requestedScalaMajorMinorOrMajorVersion.equals("2")) { scalaVersionKey = "scala213"; - } else if (requestedScalaVersion.startsWith("3.0") || - requestedScalaVersion.startsWith("3.1") || - requestedScalaVersion.startsWith("3.2") || - requestedScalaVersion.startsWith("3.3")) { + } else if (requestedScalaMajorMinorOrMajorVersion.equals("3.0") || + requestedScalaMajorMinorOrMajorVersion.equals("3.1") || + requestedScalaMajorMinorOrMajorVersion.equals("3.2") || + requestedScalaMajorMinorOrMajorVersion.equals("3.3")) { scalaVersionKey = "scala3LTS"; - } else if (requestedScalaVersion.startsWith("3")) { + } else if (requestedScalaMajorMinorOrMajorVersion.startsWith("3")) { scalaVersionKey = "scala3Next"; } else { throw new IllegalArgumentException("Unsupported scala version " + requestedScalaVersion); diff --git a/scalafix-tests/integration/src/test/scala/scalafix/tests/interfaces/ScalafixSuite.scala b/scalafix-tests/integration/src/test/scala/scalafix/tests/interfaces/ScalafixSuite.scala index 4a5f6c4b8..e189de24b 100644 --- a/scalafix-tests/integration/src/test/scala/scalafix/tests/interfaces/ScalafixSuite.scala +++ b/scalafix-tests/integration/src/test/scala/scalafix/tests/interfaces/ScalafixSuite.scala @@ -54,16 +54,80 @@ class ScalafixSuite extends AnyFunSuite { assert(help.contains("Usage: scalafix")) } - test("classload Scala 3 LTS as a fallback for pre-LTS versions") { + test("fail to classload Scala 2.11 with full version") { + assertThrows[IllegalArgumentException]( + Scalafix.fetchAndClassloadInstance("2.11.0", repositories) + ) + } + + test("fail to classload Scala 2.11 with minor version") { + assertThrows[IllegalArgumentException]( + Scalafix.fetchAndClassloadInstance("2.11", repositories) + ) + } + + test("classload Scala 2.12 with full version") { + val scalafixAPI = + Scalafix.fetchAndClassloadInstance("2.12.20", repositories) + assert(scalafixAPI.scalaVersion() == Versions.scala212) + } + + test("classload Scala 2.12 with major.minor version") { + val scalafixAPI = Scalafix.fetchAndClassloadInstance("2.12", repositories) + assert(scalafixAPI.scalaVersion() == Versions.scala212) + } + + test("classload Scala 2.13 with full version") { + val scalafixAPI = + Scalafix.fetchAndClassloadInstance("2.13.15", repositories) + assert(scalafixAPI.scalaVersion() == Versions.scala213) + } + + test("classload Scala 2.13 with major.minor version") { + val scalafixAPI = Scalafix.fetchAndClassloadInstance("2.13", repositories) + assert(scalafixAPI.scalaVersion() == Versions.scala213) + } + + test("classload Scala 2.13 with major version") { + val scalafixAPI = Scalafix.fetchAndClassloadInstance("2", repositories) + assert(scalafixAPI.scalaVersion() == Versions.scala213) + } + + test("classload Scala 3 LTS with full pre-LTS version") { val scalafixAPI = Scalafix.fetchAndClassloadInstance("3.0.0", repositories) assert(scalafixAPI.scalaVersion() == Versions.scala3LTS) } - test("classload Scala 3 Next as a fallback for post-LTS versions") { + test("classload Scala 3 LTS with major.minor pre-LTS version") { + val scalafixAPI = Scalafix.fetchAndClassloadInstance("3.2", repositories) + assert(scalafixAPI.scalaVersion() == Versions.scala3LTS) + } + + test("classload Scala 3 LTS with full LTS version") { + val scalafixAPI = Scalafix.fetchAndClassloadInstance("3.3.4", repositories) + assert(scalafixAPI.scalaVersion() == Versions.scala3LTS) + } + + test("classload Scala 3 LTS with major.minor LTS version") { + val scalafixAPI = Scalafix.fetchAndClassloadInstance("3.3", repositories) + assert(scalafixAPI.scalaVersion() == Versions.scala3LTS) + } + + test("classload Scala 3 Next with full post-LTS version") { val scalafixAPI = Scalafix.fetchAndClassloadInstance("3.4.0", repositories) assert(scalafixAPI.scalaVersion() == Versions.scala3Next) } + test("classload Scala 3 Next with major.minor post-LTS version") { + val scalafixAPI = Scalafix.fetchAndClassloadInstance("3.5", repositories) + assert(scalafixAPI.scalaVersion() == Versions.scala3Next) + } + + test("classload Scala 3 Next with major version") { + val scalafixAPI = Scalafix.fetchAndClassloadInstance("3", repositories) + assert(scalafixAPI.scalaVersion() == Versions.scala3Next) + } + test("invalid class loader") { val cl = new URLClassLoader(Array(), null) val ex = intercept[ScalafixException] {