Skip to content

Commit 02033a4

Browse files
tanishikingbjaglin
authored andcommitted
Support OrganizeImports.removeUnused in Scala 3
Once this PR scala/scala3#17835 has merged and released, scalafix-organize-imports should be able to run OrganizeImports.removeUnused based on the diagnostics information in SemanticDB emit from Scala3 compiler. In order to make OrganizeImports rule to work with Scala 3, this commit added a few adjustments to the rule.
1 parent e4eb15d commit 02033a4

File tree

7 files changed

+30
-24
lines changed

7 files changed

+30
-24
lines changed

docs/rules/OrganizeImports.md

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,11 @@ do not rewrite import statements in ways that conflict with
5252

5353
Known limitations:
5454

55-
1. The [`removeUnused`](OrganizeImports.md#removeunused) option must be
56-
explicitly set to `false` - the rule currently doesn’t remove unused
57-
imports as it is currently not supported by the compiler.
58-
59-
2. Usage of [deprecated package
55+
1. Usage of [deprecated package
6056
objects](http://dotty.epfl.ch/docs/reference/dropped-features/package-objects.html)
6157
may result in incorrect imports.
6258

63-
3. The
59+
2. The
6460
[`groupExplicitlyImportedImplicitsSeparately`](OrganizeImports.md#groupexplicitlyimportedimplicitsseparately)
6561
option has no effect.
6662

@@ -1279,12 +1275,9 @@ Remove unused imports.
12791275
> using Scala compilation diagnostics information, and the compilation phase
12801276
> happens before Scalafix rules get applied.
12811277
1282-
> The `removeUnused` option is currently not supported for source files
1283-
> compiled with Scala 3, as the [compiler cannot issue warnings for unused
1284-
> imports
1285-
> yet](https://docs.scala-lang.org/scala3/guides/migration/options-lookup.html#warning-settings).
1286-
> As a result, you must set `removeUnused` to `false` when running the
1287-
> rule on source files compiled with Scala 3.
1278+
> The `removeUnused` option is not supported for source files compiled with
1279+
> early versions of Scala 3 as these do not export SemanticDB diagnostics for
1280+
> unused imports. You must compile with Scala 3.4.0 or later to use it.
12881281
12891282
### Value type
12901283

@@ -1299,7 +1292,7 @@ Boolean
12991292
```conf
13001293
OrganizeImports {
13011294
groups = ["javax?\\.", "scala.", "*"]
1302-
removeUnused = true // not supported in Scala 3
1295+
removeUnused = true
13031296
}
13041297
```
13051298

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

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ class OrganizeImports(
9696

9797
private def fixWithImplicitDialect(implicit doc: SemanticDocument): Patch = {
9898
unusedImporteePositions ++= doc.diagnostics.collect {
99-
case d if d.message == "Unused import" => d.position
99+
// Scala2 says "Unused import" while Scala3 says "unused import"
100+
case d if d.message.toLowerCase == "unused import" => d.position
100101
}
101102

102103
val (globalImports, localImports) = collectImports(doc.tree)
@@ -112,8 +113,18 @@ class OrganizeImports(
112113
diagnostics.map(Patch.lint).asPatch + globalImportsPatch + localImportsPatch
113114
}
114115

115-
private def isUnused(importee: Importee): Boolean =
116-
unusedImporteePositions contains positionOf(importee)
116+
private def isUnused(importee: Importee): Boolean = {
117+
// positionOf returns the position of `bar` for `import foo.{bar => baz}`
118+
// this position matches with the diagnostics from Scala2,
119+
// but Scala3 diagnostics has a position for `bar => baz`, which doesn't match with
120+
// the return value of `positionOf`.
121+
// We could adjust the behavior of `positionOf` based on Scala version,
122+
// but this implementation just checking the unusedImporteePosition includes the importee pos, for simplicity.
123+
val pos = positionOf(importee)
124+
unusedImporteePositions.exists(unused =>
125+
unused.start <= pos.start && pos.end <= unused.end
126+
)
127+
}
117128

118129
private def organizeGlobalImports(
119130
imports: Seq[Import]
@@ -839,7 +850,9 @@ object OrganizeImports {
839850
scalacOptions: List[String],
840851
scalaVersion: String
841852
): Configured[Rule] = {
842-
val hasCompilerSupport = scalaVersion.startsWith("2")
853+
val hasCompilerSupport =
854+
Seq("3.0", "3.1", "3.2", "3.3")
855+
.forall(v => !scalaVersion.startsWith(v))
843856

844857
val hasWarnUnused = hasCompilerSupport && {
845858
val warnUnusedPrefix = Set("-Wunused", "-Ywarn-unused")
@@ -887,17 +900,17 @@ object OrganizeImports {
887900
)
888901
else if (hasCompilerSupport)
889902
Configured.error(
890-
"The Scala compiler option \"-Ywarn-unused\" is required to use OrganizeImports with"
903+
"A Scala compiler option is required to use OrganizeImports with"
891904
+ " \"OrganizeImports.removeUnused\" set to true. To fix this problem, update your"
892-
+ " build to use at least one Scala compiler option like -Ywarn-unused-import (2.11"
893-
+ " only), -Ywarn-unused, -Xlint:unused (2.12.2 or above) or -Wunused (2.13 only)."
905+
+ " build to add `-Ywarn-unused` (2.12), `-Wunused:imports` (2.13), or"
906+
+ " `-Wunused:import` (3.4+)."
894907
)
895908
else
896909
Configured.error(
897-
"\"OrganizeImports.removeUnused\" is not supported on Scala 3 as the compiler is"
898-
+ " not providing enough information. Run the rule with"
899-
+ " \"OrganizeImports.removeUnused\" set to false to organize imports while keeping"
900-
+ " potentially unused imports."
910+
"\"OrganizeImports.removeUnused\"" + s"is not supported on $scalaVersion as the compiler is"
911+
+ " not providing enough information. Please upgrade the Scala compiler to 3.4.0 or greater."
912+
+ " Otherwise, run the rule with \"OrganizeImports.removeUnused\" set to false"
913+
+ " to organize imports while keeping potentially unused imports."
901914
)
902915
}
903916

0 commit comments

Comments
 (0)