Skip to content

Commit 17a2b95

Browse files
authored
Fix RemoveUnused / OrganizeImports.removeUnused for Scala 3.10+ (#2504)
1 parent 0da2c45 commit 17a2b95

6 files changed

Lines changed: 126 additions & 4 deletions

File tree

scalafix-core/src/main/scala/scalafix/internal/config/ScalaVersion.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,17 @@ object ScalaVersion {
157157

158158
private val yyyyMMdd = DateTimeFormatter.ofPattern("yyyyMMdd")
159159

160+
implicit val ordering: Ordering[ScalaVersion] =
161+
Ordering.by(v =>
162+
(v.major.value, v.minor.getOrElse(0), v.patch.getOrElse(0))
163+
)
164+
165+
// unused warnings were only exposed to SemanticDB starting with Scala 3.3.4
166+
def unusedDiagnosticsInSemanticdb(version: String): Boolean =
167+
from(version).toOption.forall { v =>
168+
v.isScala2 || ordering.gteq(v, Patch(Major.Scala3, 3, 4))
169+
}
170+
160171
def from(version: String): Try[ScalaVersion] = {
161172
version match {
162173
case NightlyVersion(major, minor, patch, rc, date, shortSha1) =>

scalafix-rules/src/main/scala/scalafix/internal/rule/OrganizeImports.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -865,8 +865,7 @@ object OrganizeImports {
865865
scalaVersion: String
866866
): Configured[Rule] = {
867867
val hasCompilerSupport =
868-
Seq("3.0", "3.1", "3.2", "3.3.0", "3.3.1", "3.3.2", "3.3.3")
869-
.forall(v => !scalaVersion.startsWith(v))
868+
ScalaVersion.unusedDiagnosticsInSemanticdb(scalaVersion)
870869

871870
val hasWarnUnused = hasCompilerSupport && {
872871
val warnUnusedPrefix = Set("-Wunused", "-Ywarn-unused")

scalafix-rules/src/main/scala/scalafix/internal/rule/RemoveUnused.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import scala.meta._
66
import scala.meta.tokens.Token
77

88
import metaconfig.Configured
9+
import scalafix.internal.config.ScalaVersion
910
import scalafix.v1._
1011

1112
class RemoveUnused(config: RemoveUnusedConfig)
@@ -33,8 +34,7 @@ class RemoveUnused(config: RemoveUnusedConfig)
3334
private def warnUnusedString = List("-Wall", "-Xlint", "-Xlint:unused")
3435
override def withConfiguration(config: Configuration): Configured[Rule] = {
3536
val diagnosticsAvailableInSemanticdb =
36-
Seq("3.0", "3.1", "3.2", "3.3.0", "3.3.1", "3.3.2", "3.3.3")
37-
.forall(v => !config.scalaVersion.startsWith(v))
37+
ScalaVersion.unusedDiagnosticsInSemanticdb(config.scalaVersion)
3838

3939
val hasWarnUnused = config.scalacOptions.exists(option =>
4040
warnUnusedPrefix.exists(prefix => option.startsWith(prefix)) ||

scalafix-tests/unit/src/test/scala/scalafix/tests/config/OrganizeImportsConfigSuite.scala

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,55 @@ class OrganizeImportsConfigSuite extends AnyFunSuite {
8080
"Expected OrganizeImports to succeed when RemoveUnused is not configured"
8181
)
8282
}
83+
84+
private def checkRemoveUnusedScalaVersion(
85+
scalaVersion: String,
86+
expectedOk: Boolean
87+
): Unit = {
88+
val rawConfig =
89+
"""|rules = [OrganizeImports]
90+
|OrganizeImports.removeUnused = true
91+
|""".stripMargin
92+
val conf = Conf.parseString("test", rawConfig).get
93+
val config = Configuration()
94+
.withConf(conf)
95+
.withScalaVersion(scalaVersion)
96+
.withScalacOptions(List("-Wunused:all"))
97+
val result = new OrganizeImports().withConfiguration(config)
98+
assert(
99+
result.isOk == expectedOk,
100+
s"OrganizeImports.removeUnused with Scala $scalaVersion: expected isOk=$expectedOk, got $result"
101+
)
102+
if (!expectedOk) {
103+
result match {
104+
case Configured.NotOk(error) =>
105+
assert(
106+
error.msg.contains("not supported") &&
107+
error.msg.contains("3.3.4"),
108+
s"Error should mention unsupported compiler. Got: ${error.msg}"
109+
)
110+
case _ => fail("Expected Configured.NotOk")
111+
}
112+
}
113+
}
114+
115+
test("OrganizeImports.removeUnused accepts Scala 3.10.0-RC1") {
116+
checkRemoveUnusedScalaVersion("3.10.0-RC1", expectedOk = true)
117+
}
118+
119+
test("OrganizeImports.removeUnused accepts Scala 3.3.4") {
120+
checkRemoveUnusedScalaVersion("3.3.4", expectedOk = true)
121+
}
122+
123+
test("OrganizeImports.removeUnused accepts Scala 2.13.18") {
124+
checkRemoveUnusedScalaVersion("2.13.18", expectedOk = true)
125+
}
126+
127+
test("OrganizeImports.removeUnused rejects Scala 3.3.3") {
128+
checkRemoveUnusedScalaVersion("3.3.3", expectedOk = false)
129+
}
130+
131+
test("OrganizeImports.removeUnused rejects Scala 3.1.3") {
132+
checkRemoveUnusedScalaVersion("3.1.3", expectedOk = false)
133+
}
83134
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package scalafix.tests.config
2+
3+
import org.scalatest.funsuite.AnyFunSuite
4+
import scalafix.internal.rule.RemoveUnused
5+
import scalafix.v1.Configuration
6+
7+
class RemoveUnusedConfigSuite extends AnyFunSuite {
8+
9+
private def check(scalaVersion: String, expectedOk: Boolean): Unit = {
10+
val config = Configuration()
11+
.withScalaVersion(scalaVersion)
12+
.withScalacOptions(List("-Wunused:all"))
13+
val result = new RemoveUnused().withConfiguration(config)
14+
assert(
15+
result.isOk == expectedOk,
16+
s"RemoveUnused with Scala $scalaVersion: expected isOk=$expectedOk, got $result"
17+
)
18+
}
19+
20+
test("RemoveUnused accepts Scala 3.10.0-RC1") {
21+
check("3.10.0-RC1", expectedOk = true)
22+
}
23+
24+
test("RemoveUnused accepts Scala 3.3.4") {
25+
check("3.3.4", expectedOk = true)
26+
}
27+
28+
test("RemoveUnused accepts Scala 2.13.18") {
29+
check("2.13.18", expectedOk = true)
30+
}
31+
32+
test("RemoveUnused rejects Scala 3.3.3") {
33+
check("3.3.3", expectedOk = false)
34+
}
35+
36+
test("RemoveUnused rejects Scala 3.1.3") {
37+
check("3.1.3", expectedOk = false)
38+
}
39+
}

scalafix-tests/unit/src/test/scala/scalafix/tests/config/ScalaVersionSuite.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,26 @@ class ScalaVersionSuite extends munit.FunSuite {
5151
assert(scala3.isFailure)
5252
}
5353

54+
test("unusedDiagnosticsInSemanticdb") {
55+
val cases = List(
56+
"3.10.0-RC1" -> true,
57+
"3.3.4" -> true,
58+
"3.3.4-RC1" -> true,
59+
"2.13.18" -> true,
60+
"3.3.3" -> false,
61+
"3.1.3" -> false,
62+
"3.0.0" -> false,
63+
"3.2.2" -> false,
64+
"3.6.0-RC1-bin-20241008-3408ed7-NIGHTLY" -> true,
65+
"not-a-version" -> true // unparseable stays permissive
66+
)
67+
cases.foreach { case (version, expected) =>
68+
assertEquals(
69+
ScalaVersion.unusedDiagnosticsInSemanticdb(version),
70+
expected,
71+
clue = version
72+
)
73+
}
74+
}
75+
5476
}

0 commit comments

Comments
 (0)