diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e9494bc..3f68071 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -10,9 +10,9 @@ jobs: - uses: olafurpg/setup-scala@v11 - uses: coursier/cache-action@v6 - name: Lint - run: sbt scalafmtCheckAll "rules/scalafix --check" + run: sbt scalafmtCheckAll "rules2_12/scalafix --check" - name: Test - run: sbt coverage +tests/test +rules/coverageReport + run: sbt coverage tests/test coverageAggregate - uses: codecov/codecov-action@v1 with: token: ${{ secrets.CODECOV_TOKEN }} diff --git a/README.adoc b/README.adoc index e0a5885..cc44b94 100644 --- a/README.adoc +++ b/README.adoc @@ -81,6 +81,18 @@ Assuming that the first two commands run successfully, the last `scalafixAll --c However, you should make sure that the source-formatting tools you use do not rewrite import statements in ways that conflict with `OrganizeImports`. For example, when using Scalafmt together with `OrganizeImports`, the `ExpandImportSelectors`, `SortImports`, and `AsciiSortImports` rewriting rules should not be used. +=== Scala 3 + +Running the rule on source files compiled with Scala 3 is still experimental. + +Known limitations: + +. You must use Scalafix 0.9.28 or later +. The <> option must be explicitly set to `false` +. Source files using new syntax introduced in Scala 3 such as https://docs.scala-lang.org/scala3/book/ca-given-imports.html[`given` imports] may not work properly +. Usage of http://dotty.epfl.ch/docs/reference/dropped-features/package-objects.html[deprecated package objects] may result in incorrect imports +. The <> option has no effect + == Configuration === Default Configuration values @@ -444,6 +456,13 @@ Unfortunately, Scalafix is not able to surgically identify conflicting implicit CAUTION: In general, order-sensitive imports are fragile, and can easily be broken by either human collaborators or tools (e.g., the IntelliJ IDEA Scala import optimizer does not handle this case correctly). They should be eliminated whenever possible. This option is mostly useful when you are dealing with a large trunk of legacy codebase, and you want to minimize manual intervention and guarantee correctness in all cases. + +[IMPORTANT] +==== +The `groupExplicitlyImportedImplicitsSeparately` option has currently no effect on source files compiled with Scala 3, as the https://github.com/lampepfl/dotty/issues/12766[compiler does not expose full signature information], preventing the rule to identify imported implicits. +==== + + ==== Value type Boolean @@ -472,7 +491,7 @@ Configuration: ---- OrganizeImports { groups = ["scala.", "*"] - groupExplicitlyImportedImplicitsSeparately = true + groupExplicitlyImportedImplicitsSeparately = true // not supported in Scala 3 } ---- @@ -1319,6 +1338,11 @@ Remove unused imports. As mentioned in <>, the `removeUnused` option doesn't play perfectly with the `expandRelative` option. Setting `expandRelative` to `true` might introduce new unused imports (see <>). These newly introduced unused imports cannot be removed by setting `removeUnused` to `true`. This is because unused imports are identified using Scala compilation diagnostics information, and the compilation phase happens before Scalafix rules get applied. ==== +[IMPORTANT] +==== +The `removeUnused` option is currently not supported for source files compiled with Scala 3, as the https://docs.scala-lang.org/scala3/guides/migration/options-lookup.html#warning-settings[compiler cannot issue warnings for unused imports yet]. As a result, you must set `removeUnused` to `false` when running the rule on source files compiled with Scala 3. +==== + ==== Value type Boolean @@ -1335,7 +1359,7 @@ Configuration: ---- OrganizeImports { groups = ["javax?\\.", "scala.", "*"] - removeUnused = true + removeUnused = true // not supported in Scala 3 } ---- diff --git a/build.sbt b/build.sbt index 0410955..e3d7f29 100644 --- a/build.sbt +++ b/build.sbt @@ -1,5 +1,8 @@ lazy val v = _root_.scalafix.sbt.BuildInfo +lazy val rulesCrossVersions = Seq(v.scala213, v.scala212, v.scala211) +lazy val scala3Version = "3.0.0" + inThisBuild( List( organization := "com.github.liancheng", @@ -13,52 +16,76 @@ inThisBuild( url("https://github.com/liancheng") ) ), - scalaVersion := v.scala212, - crossScalaVersions := List(v.scala211, v.scala212, v.scala213), scalacOptions ++= List( - "-deprecation", - "-Yrangepos", - "-P:semanticdb:synthetics:on" - ), - conflictManager := ConflictManager.strict, - dependencyOverrides ++= List( - "org.scala-lang.modules" %% "scala-xml" % "1.2.0", - "org.slf4j" % "slf4j-api" % "1.7.25", - "com.lihaoyi" %% "sourcecode" % "0.2.1", - "org.scala-lang.modules" %% "scala-collection-compat" % "2.1.6" + "-deprecation" ), - addCompilerPlugin(scalafixSemanticdb), + semanticdbEnabled := true, + // semanticdbTargetRoot makes it hard to have several input modules + semanticdbIncludeInJar := true, + semanticdbVersion := scalafixSemanticdb.revision, scalafixDependencies += "com.github.liancheng" %% "organize-imports" % "0.5.0", // Super shell output often messes up Scalafix test output. useSuperShell := false ) ) -skip in publish := true - -lazy val rules = project +lazy val `scalafix-organize-imports` = project + .in(file(".")) + .aggregate( + rules.projectRefs ++ + input.projectRefs ++ + output.projectRefs ++ + tests.projectRefs: _* + ) + .settings( + publish / skip := true + ) +lazy val rules = projectMatrix .settings( moduleName := "organize-imports", - dependencyOverrides += "com.lihaoyi" %% "sourcecode" % "0.2.1", + conflictManager := ConflictManager.strict, + dependencyOverrides ++= List( + "org.scala-lang.modules" %% "scala-collection-compat" % "2.1.6", + "com.lihaoyi" %% "sourcecode" % "0.2.1" + ), libraryDependencies += "ch.epfl.scala" %% "scalafix-core" % v.scalafixVersion, scalacOptions ++= List("-Ywarn-unused"), scalafixOnCompile := true ) + .defaultAxes(VirtualAxis.jvm) + .jvmPlatform(rulesCrossVersions) -lazy val shared = project.settings(skip in publish := true) +lazy val shared = projectMatrix + .settings( + publish / skip := true, + coverageEnabled := false + ) + .defaultAxes(VirtualAxis.jvm) + .jvmPlatform(scalaVersions = rulesCrossVersions :+ scala3Version) -lazy val input = project +lazy val input = projectMatrix .dependsOn(shared) - .settings(skip in publish := true) + .settings( + publish / skip := true, + coverageEnabled := false + ) + .defaultAxes(VirtualAxis.jvm) + .jvmPlatform(scalaVersions = rulesCrossVersions :+ scala3Version) -lazy val output = project +lazy val output = projectMatrix .dependsOn(shared) - .settings(skip in publish := true) + .settings( + publish / skip := true, + coverageEnabled := false + ) + .defaultAxes(VirtualAxis.jvm) + .jvmPlatform(scalaVersions = rulesCrossVersions :+ scala3Version) -lazy val inputUnusedImports = project +lazy val inputUnusedImports = projectMatrix .dependsOn(shared) .settings( - skip in publish := true, + publish / skip := true, + coverageEnabled := false, scalacOptions += { if (scalaVersion.value.startsWith("2.11.")) "-Ywarn-unused-import" @@ -66,28 +93,69 @@ lazy val inputUnusedImports = project "-Ywarn-unused" } ) + .defaultAxes(VirtualAxis.jvm) + .jvmPlatform(scalaVersions = rulesCrossVersions :+ scala3Version) -lazy val tests = project +lazy val testsAggregate = Project("tests", file("target/testsAggregate")) + .aggregate(tests.projectRefs: _*) + +lazy val tests = projectMatrix .dependsOn(rules) .enablePlugins(ScalafixTestkitPlugin) .settings( - skip in publish := true, - scalacOptions ++= List("-Ywarn-unused"), + publish / skip := true, + coverageEnabled := false, libraryDependencies += "ch.epfl.scala" % "scalafix-testkit" % v.scalafixVersion % Test cross CrossVersion.full, - (compile in Compile) := (compile in Compile) - .dependsOn( - compile in (input, Compile), - compile in (inputUnusedImports, Compile) - ) - .value, - scalafixTestkitOutputSourceDirectories := sourceDirectories.in(output, Compile).value, - scalafixTestkitInputSourceDirectories := ( - sourceDirectories.in(input, Compile).value ++ - sourceDirectories.in(inputUnusedImports, Compile).value - ), - scalafixTestkitInputClasspath := ( - fullClasspath.in(input, Compile).value ++ - fullClasspath.in(inputUnusedImports, Compile).value - ).distinct + scalafixTestkitOutputSourceDirectories := + TargetAxis.resolve(output, Compile / unmanagedSourceDirectories).value, + scalafixTestkitInputSourceDirectories := { + val inputSrc = TargetAxis.resolve( + input, + Compile / unmanagedSourceDirectories + ).value + val inputUnusedImportsSrc = TargetAxis.resolve( + inputUnusedImports, + Compile / unmanagedSourceDirectories + ).value + inputSrc ++ inputUnusedImportsSrc + }, + scalafixTestkitInputClasspath := { + val inputClasspath = TargetAxis.resolve( + input, + Compile / fullClasspath + ).value + val inputUnusedImportsClasspath = TargetAxis.resolve( + inputUnusedImports, + Compile / fullClasspath + ).value + inputClasspath ++ inputUnusedImportsClasspath + }, + scalafixTestkitInputScalacOptions := + TargetAxis.resolve(inputUnusedImports, Compile / scalacOptions).value, + scalafixTestkitInputScalaVersion := + TargetAxis.resolve(inputUnusedImports, Compile / scalaVersion).value + ) + .defaultAxes( + rulesCrossVersions.map(VirtualAxis.scalaABIVersion) :+ VirtualAxis.jvm: _* + ) + .customRow( + scalaVersions = Seq(v.scala212), + axisValues = Seq(TargetAxis(scala3Version), VirtualAxis.jvm), + settings = Seq() + ) + .customRow( + scalaVersions = Seq(v.scala213), + axisValues = Seq(TargetAxis(v.scala213), VirtualAxis.jvm), + settings = Seq() + ) + .customRow( + scalaVersions = Seq(v.scala212), + axisValues = Seq(TargetAxis(v.scala212), VirtualAxis.jvm), + settings = Seq() + ) + .customRow( + scalaVersions = Seq(v.scala211), + axisValues = Seq(TargetAxis(v.scala211), VirtualAxis.jvm), + settings = Seq() ) diff --git a/input/src/main/scala/fix/ExpandRelativePackageObject.scala b/input/src/main/scala-2/fix/ExpandRelativePackageObject.scala similarity index 79% rename from input/src/main/scala/fix/ExpandRelativePackageObject.scala rename to input/src/main/scala-2/fix/ExpandRelativePackageObject.scala index bb76292..fa8019b 100644 --- a/input/src/main/scala/fix/ExpandRelativePackageObject.scala +++ b/input/src/main/scala-2/fix/ExpandRelativePackageObject.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] +OrganizeImports.removeUnused = false OrganizeImports.expandRelative = true */ package fix diff --git a/input/src/main/scala/fix/ExplicitlyImportedImplicits.scala b/input/src/main/scala-2/fix/ExplicitlyImportedImplicits.scala similarity index 91% rename from input/src/main/scala/fix/ExplicitlyImportedImplicits.scala rename to input/src/main/scala-2/fix/ExplicitlyImportedImplicits.scala index 73ad704..18b1c80 100644 --- a/input/src/main/scala/fix/ExplicitlyImportedImplicits.scala +++ b/input/src/main/scala-2/fix/ExplicitlyImportedImplicits.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] +OrganizeImports.removeUnused = false OrganizeImports.groupExplicitlyImportedImplicitsSeparately = true */ package fix diff --git a/input/src/main/scala/fix/PresetDefault.scala b/input/src/main/scala-2/fix/PresetDefault.scala similarity index 100% rename from input/src/main/scala/fix/PresetDefault.scala rename to input/src/main/scala-2/fix/PresetDefault.scala diff --git a/input/src/main/scala/fix/PresetIntelliJ_2020_3.scala b/input/src/main/scala-2/fix/PresetIntelliJ_2020_3.scala similarity index 100% rename from input/src/main/scala/fix/PresetIntelliJ_2020_3.scala rename to input/src/main/scala-2/fix/PresetIntelliJ_2020_3.scala diff --git a/input/src/main/scala/ExpandRelativeRootPackage.scala b/input/src/main/scala/ExpandRelativeRootPackage.scala index d1ab6f6..1ffe6ba 100644 --- a/input/src/main/scala/ExpandRelativeRootPackage.scala +++ b/input/src/main/scala/ExpandRelativeRootPackage.scala @@ -3,6 +3,7 @@ rules = [OrganizeImports] OrganizeImports { expandRelative = true groupedImports = Explode + removeUnused = false } */ import P._ diff --git a/input/src/main/scala/OrganizeImportsRootPackage.scala b/input/src/main/scala/OrganizeImportsRootPackage.scala index edf32d0..7d30938 100644 --- a/input/src/main/scala/OrganizeImportsRootPackage.scala +++ b/input/src/main/scala/OrganizeImportsRootPackage.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] +OrganizeImports.removeUnused = false OrganizeImports.groups = ["re:javax?\\.", "*", "scala."] */ import java.time.Clock diff --git a/input/src/main/scala/fix/AlreadyOrganized.scala b/input/src/main/scala/fix/AlreadyOrganized.scala index 6e7cf4f..6488ed4 100644 --- a/input/src/main/scala/fix/AlreadyOrganized.scala +++ b/input/src/main/scala/fix/AlreadyOrganized.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] +OrganizeImports.removeUnused = false OrganizeImports.groupedImports = Merge */ package fix diff --git a/input/src/main/scala/fix/BlankLinesManual.scala b/input/src/main/scala/fix/BlankLinesManual.scala index bcdd84f..060d4a1 100644 --- a/input/src/main/scala/fix/BlankLinesManual.scala +++ b/input/src/main/scala/fix/BlankLinesManual.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] + OrganizeImports { blankLines = Manual groups = [ @@ -8,6 +9,7 @@ OrganizeImports { "---" "*" ] + removeUnused = false } */ package fix diff --git a/input/src/main/scala/fix/CoalesceImportees.scala b/input/src/main/scala/fix/CoalesceImportees.scala index e061c5b..8c1e9f7 100644 --- a/input/src/main/scala/fix/CoalesceImportees.scala +++ b/input/src/main/scala/fix/CoalesceImportees.scala @@ -3,6 +3,7 @@ rules = [OrganizeImports] OrganizeImports { groupedImports = Keep coalesceToWildcardImportThreshold = 3 + removeUnused = false } */ package fix diff --git a/input/src/main/scala/fix/CurlyBracedSingleImportee.scala b/input/src/main/scala/fix/CurlyBracedSingleImportee.scala index f4771b4..66eb557 100644 --- a/input/src/main/scala/fix/CurlyBracedSingleImportee.scala +++ b/input/src/main/scala/fix/CurlyBracedSingleImportee.scala @@ -1,4 +1,7 @@ -/* rules = [OrganizeImports] */ +/* +rules = [OrganizeImports] +OrganizeImports.removeUnused = false + */ package fix import scala.collection.{Map} diff --git a/input/src/main/scala/fix/DeduplicateImportees.scala b/input/src/main/scala/fix/DeduplicateImportees.scala index fa0421a..8b7fde2 100644 --- a/input/src/main/scala/fix/DeduplicateImportees.scala +++ b/input/src/main/scala/fix/DeduplicateImportees.scala @@ -1,4 +1,7 @@ -/* rules = [OrganizeImports] */ +/* +rules = [OrganizeImports] +OrganizeImports.removeUnused = false + */ package fix import scala.collection.immutable.Vector diff --git a/input/src/main/scala/fix/ExpandRelative.scala b/input/src/main/scala/fix/ExpandRelative.scala index 8fe5ebb..4f0081d 100644 --- a/input/src/main/scala/fix/ExpandRelative.scala +++ b/input/src/main/scala/fix/ExpandRelative.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] +OrganizeImports.removeUnused = false OrganizeImports.expandRelative = true */ package fix diff --git a/input/src/main/scala/fix/ExpandRelativeMultiGroups.scala b/input/src/main/scala/fix/ExpandRelativeMultiGroups.scala index 5c4cab0..e6e5a1c 100644 --- a/input/src/main/scala/fix/ExpandRelativeMultiGroups.scala +++ b/input/src/main/scala/fix/ExpandRelativeMultiGroups.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] +OrganizeImports.removeUnused = false OrganizeImports.expandRelative = true */ package fix diff --git a/input/src/main/scala/fix/ExpandRelativeQuotedIdent.scala b/input/src/main/scala/fix/ExpandRelativeQuotedIdent.scala index 59ed1ee..1ce6c7d 100644 --- a/input/src/main/scala/fix/ExpandRelativeQuotedIdent.scala +++ b/input/src/main/scala/fix/ExpandRelativeQuotedIdent.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] +OrganizeImports.removeUnused = false OrganizeImports.expandRelative = true */ package fix diff --git a/input/src/main/scala/fix/ExpandRelativeRootPackage.scala b/input/src/main/scala/fix/ExpandRelativeRootPackage.scala index 8591344..241c3e7 100644 --- a/input/src/main/scala/fix/ExpandRelativeRootPackage.scala +++ b/input/src/main/scala/fix/ExpandRelativeRootPackage.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] +OrganizeImports.removeUnused = false OrganizeImports.expandRelative = true */ package fix diff --git a/input/src/main/scala/fix/GlobalImportsOnly.scala b/input/src/main/scala/fix/GlobalImportsOnly.scala index e53d15f..58dc463 100644 --- a/input/src/main/scala/fix/GlobalImportsOnly.scala +++ b/input/src/main/scala/fix/GlobalImportsOnly.scala @@ -1,4 +1,7 @@ -/* rules = [OrganizeImports] */ +/* +rules = [OrganizeImports] +OrganizeImports.removeUnused = false + */ package fix import scala.collection.mutable diff --git a/input/src/main/scala/fix/GroupedImportsAggressiveMergeWildcard.scala b/input/src/main/scala/fix/GroupedImportsAggressiveMergeWildcard.scala index 56e442d..6cfa22d 100644 --- a/input/src/main/scala/fix/GroupedImportsAggressiveMergeWildcard.scala +++ b/input/src/main/scala/fix/GroupedImportsAggressiveMergeWildcard.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] +OrganizeImports.removeUnused = false OrganizeImports.groupedImports = AggressiveMerge */ package fix diff --git a/input/src/main/scala/fix/GroupedImportsExplode.scala b/input/src/main/scala/fix/GroupedImportsExplode.scala index a6e4c61..798752d 100644 --- a/input/src/main/scala/fix/GroupedImportsExplode.scala +++ b/input/src/main/scala/fix/GroupedImportsExplode.scala @@ -1,4 +1,7 @@ -/* rules = [OrganizeImports] */ +/* +rules = [OrganizeImports] +OrganizeImports.removeUnused = false + */ package fix import scala.collection.mutable.{ArrayBuffer, Buffer, StringBuilder} diff --git a/input/src/main/scala/fix/GroupedImportsExplodeMixed.scala b/input/src/main/scala/fix/GroupedImportsExplodeMixed.scala index 393df3a..9c20806 100644 --- a/input/src/main/scala/fix/GroupedImportsExplodeMixed.scala +++ b/input/src/main/scala/fix/GroupedImportsExplodeMixed.scala @@ -1,4 +1,7 @@ -/* rules = [OrganizeImports] */ +/* +rules = [OrganizeImports] +OrganizeImports.removeUnused = false + */ package fix import scala.collection.immutable._ diff --git a/input/src/main/scala/fix/GroupedImportsExplodeUnimport.scala b/input/src/main/scala/fix/GroupedImportsExplodeUnimport.scala index 7839816..83dfdb0 100644 --- a/input/src/main/scala/fix/GroupedImportsExplodeUnimport.scala +++ b/input/src/main/scala/fix/GroupedImportsExplodeUnimport.scala @@ -1,4 +1,7 @@ -/* rules = [OrganizeImports] */ +/* +rules = [OrganizeImports] +OrganizeImports.removeUnused = false + */ package fix import scala.collection.{Seq => _, _} diff --git a/input/src/main/scala/fix/GroupedImportsKeep.scala b/input/src/main/scala/fix/GroupedImportsKeep.scala index 694c536..dfcbb19 100644 --- a/input/src/main/scala/fix/GroupedImportsKeep.scala +++ b/input/src/main/scala/fix/GroupedImportsKeep.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] +OrganizeImports.removeUnused = false OrganizeImports.groupedImports = Keep */ package fix diff --git a/input/src/main/scala/fix/GroupedImportsMerge.scala b/input/src/main/scala/fix/GroupedImportsMerge.scala index 4c4ca03..ebb06c1 100644 --- a/input/src/main/scala/fix/GroupedImportsMerge.scala +++ b/input/src/main/scala/fix/GroupedImportsMerge.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] +OrganizeImports.removeUnused = false OrganizeImports.groupedImports = Merge */ package fix diff --git a/input/src/main/scala/fix/GroupedImportsMergeDedup.scala b/input/src/main/scala/fix/GroupedImportsMergeDedup.scala index ceed9a4..c7fcd5e 100644 --- a/input/src/main/scala/fix/GroupedImportsMergeDedup.scala +++ b/input/src/main/scala/fix/GroupedImportsMergeDedup.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] +OrganizeImports.removeUnused = false OrganizeImports.groupedImports = Merge */ package fix diff --git a/input/src/main/scala/fix/GroupedImportsMergeRenames.scala b/input/src/main/scala/fix/GroupedImportsMergeRenames.scala index 8be206f..3718c2a 100644 --- a/input/src/main/scala/fix/GroupedImportsMergeRenames.scala +++ b/input/src/main/scala/fix/GroupedImportsMergeRenames.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] +OrganizeImports.removeUnused = false OrganizeImports.groupedImports = Merge */ package fix diff --git a/input/src/main/scala/fix/GroupedImportsMergeUnimports.scala b/input/src/main/scala/fix/GroupedImportsMergeUnimports.scala index a85dd88..b1275c7 100644 --- a/input/src/main/scala/fix/GroupedImportsMergeUnimports.scala +++ b/input/src/main/scala/fix/GroupedImportsMergeUnimports.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] +OrganizeImports.removeUnused = false OrganizeImports.groupedImports = Merge */ package fix diff --git a/input/src/main/scala/fix/GroupedImportsMergeWildcard.scala b/input/src/main/scala/fix/GroupedImportsMergeWildcard.scala index 5f89bb3..0133646 100644 --- a/input/src/main/scala/fix/GroupedImportsMergeWildcard.scala +++ b/input/src/main/scala/fix/GroupedImportsMergeWildcard.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] +OrganizeImports.removeUnused = false OrganizeImports.groupedImports = Merge */ package fix diff --git a/input/src/main/scala/fix/Groups.scala b/input/src/main/scala/fix/Groups.scala index 71043e0..49050eb 100644 --- a/input/src/main/scala/fix/Groups.scala +++ b/input/src/main/scala/fix/Groups.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] +OrganizeImports.removeUnused = false OrganizeImports.groups = ["re:javax?\\.", "*", "scala."] */ package fix diff --git a/input/src/main/scala/fix/GroupsLongestMatch.scala b/input/src/main/scala/fix/GroupsLongestMatch.scala index 3025b16..a556a29 100644 --- a/input/src/main/scala/fix/GroupsLongestMatch.scala +++ b/input/src/main/scala/fix/GroupsLongestMatch.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] +OrganizeImports.removeUnused = false OrganizeImports.groups = ["re:javax?\\.", "scala.", "scala.util.", "*"] */ package fix diff --git a/input/src/main/scala/fix/ImportsOrderAsciiPreformatted.scala b/input/src/main/scala/fix/ImportsOrderAsciiPreformatted.scala index 62e0fef..d427384 100644 --- a/input/src/main/scala/fix/ImportsOrderAsciiPreformatted.scala +++ b/input/src/main/scala/fix/ImportsOrderAsciiPreformatted.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] +OrganizeImports.removeUnused = false OrganizeImports.groupedImports = Keep */ package fix diff --git a/input/src/main/scala/fix/ImportsOrderKeep.scala b/input/src/main/scala/fix/ImportsOrderKeep.scala index 0b560c4..ca93394 100644 --- a/input/src/main/scala/fix/ImportsOrderKeep.scala +++ b/input/src/main/scala/fix/ImportsOrderKeep.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] +OrganizeImports.removeUnused = false OrganizeImports { groupedImports = Keep importSelectorsOrder = Keep diff --git a/input/src/main/scala/fix/ImportsOrderSymbolsFirst.scala b/input/src/main/scala/fix/ImportsOrderSymbolsFirst.scala index 7d6caae..cf2e42d 100644 --- a/input/src/main/scala/fix/ImportsOrderSymbolsFirst.scala +++ b/input/src/main/scala/fix/ImportsOrderSymbolsFirst.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] +OrganizeImports.removeUnused = false OrganizeImports { groupedImports = Keep importSelectorsOrder = Keep diff --git a/input/src/main/scala/fix/ImportsOrderSymbolsFirstPreformatted.scala b/input/src/main/scala/fix/ImportsOrderSymbolsFirstPreformatted.scala index 1c8800d..0c1d9a3 100644 --- a/input/src/main/scala/fix/ImportsOrderSymbolsFirstPreformatted.scala +++ b/input/src/main/scala/fix/ImportsOrderSymbolsFirstPreformatted.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] +OrganizeImports.removeUnused = false OrganizeImports { groupedImports = Keep importsOrder = SymbolsFirst diff --git a/input/src/main/scala/fix/Inheritance.scala b/input/src/main/scala/fix/Inheritance.scala index 818a6fb..97cfeaf 100644 --- a/input/src/main/scala/fix/Inheritance.scala +++ b/input/src/main/scala/fix/Inheritance.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] +OrganizeImports.removeUnused = false OrganizeImports.expandRelative = true */ package fix diff --git a/input/src/main/scala/fix/MergeImportsFormatPreserving.scala b/input/src/main/scala/fix/MergeImportsFormatPreserving.scala index 4c4958f..2ebc986 100644 --- a/input/src/main/scala/fix/MergeImportsFormatPreserving.scala +++ b/input/src/main/scala/fix/MergeImportsFormatPreserving.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] +OrganizeImports.removeUnused = false OrganizeImports.groupedImports = Merge */ diff --git a/input/src/main/scala/fix/NoImports.scala b/input/src/main/scala/fix/NoImports.scala index d1b549d..240aeaa 100644 --- a/input/src/main/scala/fix/NoImports.scala +++ b/input/src/main/scala/fix/NoImports.scala @@ -1,4 +1,7 @@ -/* rules = OrganizeImports */ +/* +rules = [OrganizeImports] +OrganizeImports.removeUnused = false + */ package fix object NoImports \ No newline at end of file diff --git a/input/src/main/scala/fix/RelativeImports.scala b/input/src/main/scala/fix/RelativeImports.scala index 00569bf..b49fa35 100644 --- a/input/src/main/scala/fix/RelativeImports.scala +++ b/input/src/main/scala/fix/RelativeImports.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] +OrganizeImports.removeUnused = false OrganizeImports.groups = ["scala.", "*"] */ package fix diff --git a/input/src/main/scala/fix/SortImportSelectorsAscii.scala b/input/src/main/scala/fix/SortImportSelectorsAscii.scala index 5bb7c14..b6e22d8 100644 --- a/input/src/main/scala/fix/SortImportSelectorsAscii.scala +++ b/input/src/main/scala/fix/SortImportSelectorsAscii.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] +OrganizeImports.removeUnused = false OrganizeImports.groupedImports = Keep */ package fix diff --git a/input/src/main/scala/fix/SortImportSelectorsKeep.scala b/input/src/main/scala/fix/SortImportSelectorsKeep.scala index b556aa8..b2d1546 100644 --- a/input/src/main/scala/fix/SortImportSelectorsKeep.scala +++ b/input/src/main/scala/fix/SortImportSelectorsKeep.scala @@ -3,6 +3,7 @@ rules = OrganizeImports OrganizeImports { importSelectorsOrder = Keep groupedImports = Keep + removeUnused = false } */ package fix diff --git a/input/src/main/scala/fix/SortImportSelectorsSymbolsFirst.scala b/input/src/main/scala/fix/SortImportSelectorsSymbolsFirst.scala index 04e1843..97a04fa 100644 --- a/input/src/main/scala/fix/SortImportSelectorsSymbolsFirst.scala +++ b/input/src/main/scala/fix/SortImportSelectorsSymbolsFirst.scala @@ -3,6 +3,7 @@ rules = [OrganizeImports] OrganizeImports { importSelectorsOrder = SymbolsFirst groupedImports = Keep + removeUnused = false } */ package fix diff --git a/input/src/main/scala/fix/Suppression.scala b/input/src/main/scala/fix/Suppression.scala index b2aaa11..fce30cf 100644 --- a/input/src/main/scala/fix/Suppression.scala +++ b/input/src/main/scala/fix/Suppression.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] +OrganizeImports.removeUnused = false OrganizeImports.groups = ["re:javax?\\.", "*", "scala."] */ package fix diff --git a/input/src/main/scala/fix/nested/NestedPackage.scala b/input/src/main/scala/fix/nested/NestedPackage.scala index 14e35a0..fd29e06 100644 --- a/input/src/main/scala/fix/nested/NestedPackage.scala +++ b/input/src/main/scala/fix/nested/NestedPackage.scala @@ -1,5 +1,6 @@ /* rules = [OrganizeImports] +OrganizeImports.removeUnused = false OrganizeImports.groups = ["re:javax?\\.", "*", "scala."] */ package fix diff --git a/input/src/main/scala/fix/nested/NestedPackageWithBraces.scala b/input/src/main/scala/fix/nested/NestedPackageWithBraces.scala index 416f114..6bffeec 100644 --- a/input/src/main/scala/fix/nested/NestedPackageWithBraces.scala +++ b/input/src/main/scala/fix/nested/NestedPackageWithBraces.scala @@ -1,4 +1,7 @@ -/* rules = [OrganizeImports] */ +/* +rules = [OrganizeImports] +OrganizeImports.removeUnused = false + */ package fix { package nested { import java.time.Clock diff --git a/inputUnusedImports/src/main/scala/fix/RemoveUnused.scala b/inputUnusedImports/src/main/scala-2/fix/RemoveUnused.scala similarity index 100% rename from inputUnusedImports/src/main/scala/fix/RemoveUnused.scala rename to inputUnusedImports/src/main/scala-2/fix/RemoveUnused.scala diff --git a/inputUnusedImports/src/main/scala/fix/RemoveUnusedDisabled.scala b/inputUnusedImports/src/main/scala-2/fix/RemoveUnusedDisabled.scala similarity index 100% rename from inputUnusedImports/src/main/scala/fix/RemoveUnusedDisabled.scala rename to inputUnusedImports/src/main/scala-2/fix/RemoveUnusedDisabled.scala diff --git a/inputUnusedImports/src/main/scala/fix/RemoveUnusedLocal.scala b/inputUnusedImports/src/main/scala-2/fix/RemoveUnusedLocal.scala similarity index 100% rename from inputUnusedImports/src/main/scala/fix/RemoveUnusedLocal.scala rename to inputUnusedImports/src/main/scala-2/fix/RemoveUnusedLocal.scala diff --git a/inputUnusedImports/src/main/scala/fix/RemoveUnusedMixed.scala b/inputUnusedImports/src/main/scala-2/fix/RemoveUnusedMixed.scala similarity index 100% rename from inputUnusedImports/src/main/scala/fix/RemoveUnusedMixed.scala rename to inputUnusedImports/src/main/scala-2/fix/RemoveUnusedMixed.scala diff --git a/inputUnusedImports/src/main/scala/fix/RemoveUnusedRelative.scala b/inputUnusedImports/src/main/scala-2/fix/RemoveUnusedRelative.scala similarity index 100% rename from inputUnusedImports/src/main/scala/fix/RemoveUnusedRelative.scala rename to inputUnusedImports/src/main/scala-2/fix/RemoveUnusedRelative.scala diff --git a/project/TargetAxis.scala b/project/TargetAxis.scala new file mode 100644 index 0000000..ee36836 --- /dev/null +++ b/project/TargetAxis.scala @@ -0,0 +1,47 @@ +import sbt._ +import sbt.internal.ProjectMatrix +import sbtprojectmatrix.ProjectMatrixPlugin.autoImport._ + +/** Use on ProjectMatrix rows to tag an affinity to a custom scalaVersion */ +case class TargetAxis(scalaVersion: String) extends VirtualAxis.WeakAxis { + + private val scalaBinaryVersion = CrossVersion.binaryScalaVersion(scalaVersion) + + override val idSuffix = s"Target${scalaBinaryVersion.replace('.', '_')}" + override val directorySuffix = s"target$scalaBinaryVersion" +} + +object TargetAxis { + + private def targetScalaVersion(virtualAxes: Seq[VirtualAxis]): String = + virtualAxes.collectFirst { case a: TargetAxis => a.scalaVersion }.get + + /** When invoked on a ProjectMatrix with a TargetAxis, lookup the project + * generated by `matrix` with a scalaVersion matching the one declared in + * that TargetAxis, and resolve `key`. + */ + def resolve[T]( + matrix: ProjectMatrix, + key: TaskKey[T] + ): Def.Initialize[Task[T]] = + Def.taskDyn { + val sv = targetScalaVersion(virtualAxes.value) + val project = matrix.finder().apply(sv) + Def.task((project / key).value) + } + + /** When invoked on a ProjectMatrix with a TargetAxis, lookup the project + * generated by `matrix` with a scalaVersion matching the one declared in + * that TargetAxis, and resolve `key`. + */ + def resolve[T]( + matrix: ProjectMatrix, + key: SettingKey[T] + ): Def.Initialize[T] = + Def.settingDyn { + val sv = targetScalaVersion(virtualAxes.value) + val project = matrix.finder().apply(sv) + Def.setting((project / key).value) + } + +} diff --git a/project/plugins.sbt b/project/plugins.sbt index 06aac12..6d5b884 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -4,3 +4,4 @@ addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.29") addSbtPlugin("com.geirsson" % "sbt-ci-release" % "1.5.7") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.2") addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.8.2") +addSbtPlugin("com.eed3si9n" % "sbt-projectmatrix" % "0.8.0") diff --git a/rules/src/main/scala/fix/OrganizeImports.scala b/rules/src/main/scala/fix/OrganizeImports.scala index 2ab606f..9b10712 100644 --- a/rules/src/main/scala/fix/OrganizeImports.scala +++ b/rules/src/main/scala/fix/OrganizeImports.scala @@ -60,7 +60,7 @@ class OrganizeImports(config: OrganizeImportsConfig) extends SemanticRule("Organ config.conf .getOrElse("OrganizeImports")(OrganizeImportsConfig()) .andThen(patchPreset(_, config.conf)) - .andThen(checkScalacOptions(_, config.scalacOptions)) + .andThen(checkScalacOptions(_, config.scalacOptions, config.scalaVersion)) override def fix(implicit doc: SemanticDocument): Patch = { unusedImporteePositions ++= doc.diagnostics.collect { @@ -181,6 +181,9 @@ class OrganizeImports(config: OrganizeImportsConfig) extends SemanticRule("Organ )(implicit doc: SemanticDocument): (Seq[Importer], Seq[Importer]) = { val (implicits, implicitPositions) = importers.flatMap { case importer @ Importer(_, importees) => + importees.foreach { i => + println(i.symbol.info) + } importees collect { case i: Importee.Name if i.symbol.infoNoThrow exists (_.isImplicit) => importer.copy(importees = i :: Nil) -> i.pos @@ -614,9 +617,12 @@ object OrganizeImports { private def checkScalacOptions( conf: OrganizeImportsConfig, - scalacOptions: List[String] + scalacOptions: List[String], + scalaVersion: String ): Configured[Rule] = { - val hasWarnUnused = { + val hasCompilerSupport = scalaVersion.startsWith("2") + + val hasWarnUnused = hasCompilerSupport && { val warnUnusedPrefix = Set("-Wunused", "-Ywarn-unused") val warnUnusedString = Set("-Xlint", "-Xlint:unused") scalacOptions exists { option => @@ -626,13 +632,20 @@ object OrganizeImports { if (!conf.removeUnused || hasWarnUnused) Configured.ok(new OrganizeImports(conf)) - else + else if (hasCompilerSupport) Configured.error( "The Scala compiler option \"-Ywarn-unused\" is required to use OrganizeImports with" + " \"OrganizeImports.removeUnused\" set to true. To fix this problem, update your" + " build to use at least one Scala compiler option like -Ywarn-unused-import (2.11" + " only), -Ywarn-unused, -Xlint:unused (2.12.2 or above) or -Wunused (2.13 only)." ) + else + Configured.error( + "\"OrganizeImports.removeUnused\" is not supported on Scala 3 as the compiler is" + + " not providing enough information. Run the rule with" + + " \"OrganizeImports.removeUnused\" set to false to organize imports while keeping" + + " potentially unused imports." + ) } private def buildImportMatchers(config: OrganizeImportsConfig): Seq[ImportMatcher] = {