From 60893389fd7d6bf0d529a28d52ea89853dfac096 Mon Sep 17 00:00:00 2001 From: Tomasz Godzik Date: Fri, 22 Sep 2023 16:33:44 +0200 Subject: [PATCH] improvement: Automatically calculate last supported versions for semanticdb Previously, we would fail if our version of semanticdb was not supported for a sepcific scala version. Now, we calculate last supported version for each scala version when compiling. Potentially this means we could get a new semanticdb version automatically, but that should not be an issue as semanticdb is well tested in scalameta itself --- build.sbt | 23 +++++--- .../scala/meta/internal/metals/Embedded.scala | 3 +- project/SemanticDbSupport.scala | 32 ++++++++++ project/V.scala | 6 +- .../scala/meta/metals/MetalsPlugin.scala | 5 +- .../sbt-test/sbt-metals/semanticdb/build.sbt | 6 +- .../src/main/scala/tests/QuickBuild.scala | 13 ++++- .../test/scala/tests/FormattingLspSuite.scala | 58 +++++++++---------- 8 files changed, 100 insertions(+), 46 deletions(-) create mode 100644 project/SemanticDbSupport.scala diff --git a/build.sbt b/build.sbt index 4b1fcd5b6f6..08fb02d6aaf 100644 --- a/build.sbt +++ b/build.sbt @@ -216,7 +216,9 @@ val sharedSettings = sharedJavacOptions ++ sharedScalacOptions ++ List( scalaVersion.value, if2 = List( compilerPlugin( - "org.scalameta" % "semanticdb-scalac" % V.scalameta cross CrossVersion.full + "org.scalameta" % "semanticdb-scalac" % V.semanticdb( + scalaVersion.value + ) cross CrossVersion.full ) ), ), @@ -323,7 +325,9 @@ val mtagsSettings = List( if2 = List( // for token edit-distance used by goto definition "com.googlecode.java-diff-utils" % "diffutils" % "1.3.0", - "org.scalameta" % "semanticdb-scalac-core" % V.scalameta cross CrossVersion.full, + "org.scalameta" % "semanticdb-scalac-core" % V.semanticdb( + scalaVersion.value + ) cross CrossVersion.full, ), if3 = List( "org.scala-lang" %% "scala3-compiler" % scalaVersion.value, @@ -489,9 +493,11 @@ lazy val metals = project "com.lihaoyi" %% "ujson" % "3.1.3", // For remote language server "com.lihaoyi" %% "requests" % "0.8.0", - // for producing SemanticDB from Scala source files - "org.scalameta" %% "scalameta" % V.scalameta, - "org.scalameta" % "semanticdb-scalac-core" % V.scalameta cross CrossVersion.full, + // for producing SemanticDB from Scala source files, to be sure we want the same version of scalameta + "org.scalameta" %% "scalameta" % V.semanticdb(scalaVersion.value), + "org.scalameta" % "semanticdb-scalac-core" % V.semanticdb( + scalaVersion.value + ) cross CrossVersion.full, // For starting Ammonite "io.github.alexarchambault.ammonite" %% "ammonite-runner" % "0.4.0", "org.scala-lang.modules" %% "scala-xml" % "2.2.0", @@ -514,7 +520,7 @@ lazy val metals = project "mavenBloopVersion" -> V.mavenBloop, "gradleBloopVersion" -> V.gradleBloop, "scalametaVersion" -> V.scalameta, - "semanticdbVersion" -> V.semanticdb, + "semanticdbVersion" -> V.semanticdb(scalaVersion.value), "javaSemanticdbVersion" -> V.javaSemanticdb, "scalafmtVersion" -> V.scalafmt, "ammoniteVersion" -> V.ammonite, @@ -536,6 +542,7 @@ lazy val metals = project "ammonite3" -> V.ammonite3Version, "scala213" -> V.scala213, "scala3" -> V.scala3, + "lastSupportedSemanticdb" -> SemanticDbSupport.last, ), ) .dependsOn(mtags, `mtags-java`) @@ -545,9 +552,10 @@ lazy val `sbt-metals` = project .settings( buildInfoPackage := "scala.meta.internal.sbtmetals", buildInfoKeys := Seq[BuildInfoKey]( - "semanticdbVersion" -> V.semanticdb, + "semanticdbVersion" -> V.semanticdb(scalaVersion.value), "supportedScala2Versions" -> V.scala2Versions, "javaSemanticdbVersion" -> V.javaSemanticdb, + "lastSupportedSemanticdb" -> SemanticDbSupport.last, ), scalaVersion := V.scala212, scriptedLaunchOpts ++= Seq(s"-Dplugin.version=${version.value}"), @@ -683,6 +691,7 @@ lazy val mtest = project "scalaVersion" -> scalaVersion.value, "kindProjector" -> V.kindProjector, "betterMonadicFor" -> V.betterMonadicFor, + "lastSupportedSemanticdb" -> SemanticDbSupport.last, ), crossScalaVersions := V.nonDeprecatedScalaVersions, Compile / unmanagedSourceDirectories ++= multiScalaDirectories( diff --git a/metals/src/main/scala/scala/meta/internal/metals/Embedded.scala b/metals/src/main/scala/scala/meta/internal/metals/Embedded.scala index 0378d2647b1..eb97d3ead0c 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/Embedded.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/Embedded.scala @@ -278,7 +278,8 @@ object Embedded { Dependency.of( "org.scalameta", s"semanticdb-scalac_$scalaVersion", - BuildInfo.scalametaVersion, + BuildInfo.lastSupportedSemanticdb + .getOrElse(scalaVersion, BuildInfo.scalametaVersion), ) def downloadDependency( diff --git a/project/SemanticDbSupport.scala b/project/SemanticDbSupport.scala new file mode 100644 index 00000000000..75fc897972d --- /dev/null +++ b/project/SemanticDbSupport.scala @@ -0,0 +1,32 @@ +import scala.jdk.CollectionConverters._ + +object SemanticDbSupport { + + private val Scala211Versions = getVersions(2, 11, 12 to 12) + private val last212 = V.scala212.split('.').last.toInt + private val Scala212Versions = getVersions(2, 12, 9 to last212) + private val last213 = V.scala213.split('.').last.toInt + private val Scala213Versions = getVersions(2, 13, 1 to last213) + + private val AllScalaVersions = + Scala213Versions ++ Scala212Versions ++ Scala211Versions + + val last: Map[String, String] = AllScalaVersions.flatMap { scalaVersion => + coursierapi.Complete + .create() + .withScalaVersion(scalaVersion) + .withScalaBinaryVersion(scalaVersion.split('.').take(2).mkString(".")) + .withInput(s"org.scalameta:semanticdb-scalac_$scalaVersion:") + .complete() + .getCompletions() + .asScala + .lastOption + .map(scalaVersion -> _) + }.toMap + + // returns versions from newest to oldest + private def getVersions(major: Int, minor: Int, range: Range) = { + val desc = if (range.step > 0) range.reverse else range + desc.map { x => s"$major.$minor.$x" } + } +} diff --git a/project/V.scala b/project/V.scala index aa51b4e6b46..b855fec30e3 100644 --- a/project/V.scala +++ b/project/V.scala @@ -41,15 +41,17 @@ object V { val scalaCli = "1.0.4" val scalafix = "0.11.1" val scalafmt = "3.7.14" - val scalameta = "4.8.3" + val scalameta = "4.8.10" val scribe = "3.12.2" - val semanticdb = scalameta val qdox = "2.0.3" val guava = "com.google.guava" % "guava" % "32.1.2-jre" val lsp4j = "org.eclipse.lsp4j" % "org.eclipse.lsp4j" % lsp4jV val dap4j = "org.eclipse.lsp4j" % "org.eclipse.lsp4j.debug" % lsp4jV + def semanticdb(scalaVersion: String) = + SemanticDbSupport.last.getOrElse(scalaVersion, scalameta) + def isNightliesEnabled: Boolean = sys.env.get("CI").isDefined || sys.env.get("NIGHTLIES").isDefined diff --git a/sbt-metals/src/main/scala/scala/meta/metals/MetalsPlugin.scala b/sbt-metals/src/main/scala/scala/meta/metals/MetalsPlugin.scala index 3a341d5bc2a..f10255546ee 100644 --- a/sbt-metals/src/main/scala/scala/meta/metals/MetalsPlugin.scala +++ b/sbt-metals/src/main/scala/scala/meta/metals/MetalsPlugin.scala @@ -30,7 +30,10 @@ object MetalsPlugin extends AutoPlugin { override lazy val projectSettings: Seq[Def.Setting[_]] = Def.settings( Keys.semanticdbVersion := { if (requiresSemanticdb.value && !isScala3.value) - BuildInfo.semanticdbVersion + BuildInfo.lastSupportedSemanticdb.getOrElse( + scalaVersion.value, + BuildInfo.semanticdbVersion, + ) else Keys.semanticdbVersion.value }, semanticdbEnabled := { diff --git a/sbt-metals/src/sbt-test/sbt-metals/semanticdb/build.sbt b/sbt-metals/src/sbt-test/sbt-metals/semanticdb/build.sbt index 0c50c79a4a6..efe25dd0d4e 100644 --- a/sbt-metals/src/sbt-test/sbt-metals/semanticdb/build.sbt +++ b/sbt-metals/src/sbt-test/sbt-metals/semanticdb/build.sbt @@ -7,7 +7,7 @@ val checkSemanticdb = lazy val a = project .in(file("a")) .settings( - scalaVersion := "2.13.5", + scalaVersion := "2.13.12", inConfig(Compile) { checkSemanticdb := { assertSemanticdbForScala2.value @@ -21,7 +21,7 @@ lazy val a = project lazy val b = project .in(file("b")) .settings( - scalaVersion := "3.0.1", + scalaVersion := "3.3.1", inConfig(Compile) { checkSemanticdb := assertSemanticdbForScala3.value }, inConfig(Test) { checkSemanticdb := assertSemanticdbForScala3.value }, ) @@ -30,7 +30,7 @@ lazy val b = project lazy val c = project .in(file("c")) .settings( - scalaVersion := "2.12.17", + scalaVersion := "2.12.18", inConfig(Compile) { checkSemanticdb := { assertSemanticdbForScala2.value diff --git a/tests/unit/src/main/scala/tests/QuickBuild.scala b/tests/unit/src/main/scala/tests/QuickBuild.scala index ebe1aa15b52..5c4875310b8 100644 --- a/tests/unit/src/main/scala/tests/QuickBuild.scala +++ b/tests/unit/src/main/scala/tests/QuickBuild.scala @@ -141,9 +141,16 @@ case class QuickBuild( } val classpath = classDirectory :: allJars.filterNot(isSourceJar) val allPlugins = - if (ScalaVersions.isSupportedAtReleaseMomentScalaVersion(scalaVersion)) - s"org.scalameta:::semanticdb-scalac:${V.semanticdbVersion}" :: compilerPlugins.toList - else compilerPlugins.toList + if ( + ScalaVersions.isSupportedAtReleaseMomentScalaVersion( + scalaVersion + ) && !ScalaVersions.isScala3Version(scalaVersion) + ) { + val semanticdbVersion = BuildInfoVersions.lastSupportedSemanticdb( + scalaVersion + ) + s"org.scalameta:::semanticdb-scalac:$semanticdbVersion" :: compilerPlugins.toList + } else compilerPlugins.toList val pluginDependencies = allPlugins.map(plugin => QuickBuild .toDependency(plugin, scalaVersion, binaryVersion) diff --git a/tests/unit/src/test/scala/tests/FormattingLspSuite.scala b/tests/unit/src/test/scala/tests/FormattingLspSuite.scala index 122b74d358b..444ed808250 100644 --- a/tests/unit/src/test/scala/tests/FormattingLspSuite.scala +++ b/tests/unit/src/test/scala/tests/FormattingLspSuite.scala @@ -378,18 +378,18 @@ class FormattingLspSuite extends BaseLspSuite("formatting") { } for { _ <- initialize( - """|/metals.json - |{ - | "a": { - | "scalaVersion": "2.13.6", - | "scalacOptions": ["-Xsource:3"] - | } - |} - |/.scalafmt.conf - |version = "2.7.5" - |/a/src/main/scala/A.scala - |object A - |""".stripMargin + s"""|/metals.json + |{ + | "a": { + | "scalaVersion": "${V.scala213}", + | "scalacOptions": ["-Xsource:3"] + | } + |} + |/.scalafmt.conf + |version = "2.7.5" + |/a/src/main/scala/A.scala + |object A + |""".stripMargin ) _ = assertNoDiff( server.textContents(".scalafmt.conf"), @@ -447,23 +447,23 @@ class FormattingLspSuite extends BaseLspSuite("formatting") { } for { _ <- initialize( - """|/metals.json - |{ - | "a": { - | "scalaVersion": "3.0.0" - | }, - | "b" : { - | "scalaVersion": "2.13.5", - | "scalacOptions": ["-Xsource:3"] - | } - |} - |/.scalafmt.conf - |version = "2.7.5" - |/a/src/main/scala/A.scala - |object A - |/b/src/main/scala/B.scala - |object B - |""".stripMargin + s"""|/metals.json + |{ + | "a": { + | "scalaVersion": "3.0.0" + | }, + | "b" : { + | "scalaVersion": "${V.scala213}", + | "scalacOptions": ["-Xsource:3"] + | } + |} + |/.scalafmt.conf + |version = "2.7.5" + |/a/src/main/scala/A.scala + |object A + |/b/src/main/scala/B.scala + |object B + |""".stripMargin ) _ = assertNoDiff( server.textContents(".scalafmt.conf"),