From 2876430c0704fd3d575b915e230cee1c4cee0af0 Mon Sep 17 00:00:00 2001 From: Brice Jaglin Date: Tue, 30 Jan 2024 14:20:03 +0100 Subject: [PATCH 1/4] testkit: include last subpath of test input source directory This allows rules to inspect whether files are in `scala` or `scala-3` (like they would do with regular Input.File), at the cost of some verbosity in test names. --- .../scala/scalafix/testkit/TestkitPath.scala | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/scalafix-testkit/src/main/scala/scalafix/testkit/TestkitPath.scala b/scalafix-testkit/src/main/scala/scalafix/testkit/TestkitPath.scala index 098571f26..7c1da04e7 100644 --- a/scalafix-testkit/src/main/scala/scalafix/testkit/TestkitPath.scala +++ b/scalafix-testkit/src/main/scala/scalafix/testkit/TestkitPath.scala @@ -14,8 +14,8 @@ import scala.meta.io.RelativePath * @param input * the absolute path to the input file. * @param testPath - * the input file relativized by the input source directory. Used to compute - * the test name and the expected output file. + * the input file relativized by input source directory (but including its + * last subpath). Used to compute the test name and the expected output file. * @param semanticdbPath * the input file relativized by the SemanticDB sourceroot. Used to compute * the path to the SemanticDB payload. @@ -29,8 +29,12 @@ final class TestkitPath( def toInput: Input = Input.VirtualFile(testName, FileIO.slurp(input, StandardCharsets.UTF_8)) def resolveOutput(props: TestkitProperties): Either[String, AbsolutePath] = { + val testPathStripped = + RelativePath(testPath.toNIO.subpath(1, testPath.toNIO.getNameCount)) val candidates = - props.outputSourceDirectories.map(dir => dir.resolve(testPath)) + props.outputSourceDirectories.map(dir => dir.resolve(testPathStripped)) ++ + // for backwards compatibility, try the old output directory + props.outputSourceDirectories.map(dir => dir.resolve(testPath)) def tried: String = candidates.mkString("\n ", "\n ", "") candidates.filter(_.isFile) match { case head :: Nil => @@ -50,8 +54,10 @@ object TestkitPath { props.inputSourceDirectories.flatMap { sourceDirectory => val ls = FileIO.listAllFilesRecursively(sourceDirectory) val scalaFiles = ls.files.filter(path => isScalaFile.matches(path.toNIO)) - scalaFiles.map { testPath => - val input = sourceDirectory.resolve(testPath) + scalaFiles.map { scalaFile => + val input = sourceDirectory.resolve(scalaFile) + val testPath = + input.toRelative(AbsolutePath(sourceDirectory.toNIO.getParent)) val semanticdbPath = input.toRelative(props.sourceroot) new TestkitPath(input, testPath, semanticdbPath) } From f4fcb65d6bd1df7d7e3345b2cdfe646e9a1e841b Mon Sep 17 00:00:00 2001 From: Brice Jaglin Date: Sat, 20 Jan 2024 17:07:41 +0100 Subject: [PATCH 2/4] OrganizeImports: gain control over dialect used for .syntax --- .../internal/rule/OrganizeImports.scala | 170 +++++++++--------- 1 file changed, 88 insertions(+), 82 deletions(-) diff --git a/scalafix-rules/src/main/scala/scalafix/internal/rule/OrganizeImports.scala b/scalafix-rules/src/main/scala/scalafix/internal/rule/OrganizeImports.scala index f1ad5f7d0..923387ba1 100644 --- a/scalafix-rules/src/main/scala/scalafix/internal/rule/OrganizeImports.scala +++ b/scalafix-rules/src/main/scala/scalafix/internal/rule/OrganizeImports.scala @@ -5,6 +5,7 @@ import scala.collection.mutable import scala.collection.mutable.ArrayBuffer import scala.util.Try +import scala.meta.Dialect import scala.meta.Import import scala.meta.Importee import scala.meta.Importee.GivenAll @@ -43,8 +44,11 @@ import scalafix.v1.SymbolInformation import scalafix.v1.XtensionSeqPatch import scalafix.v1.XtensionTreeScalafix -class OrganizeImports(config: OrganizeImportsConfig) - extends SemanticRule("OrganizeImports") { +class OrganizeImports( + config: OrganizeImportsConfig, + // shadows the default implicit always on scope (Dialect.current, matching the runtime Scala version) + implicit val targetDialect: Dialect +) extends SemanticRule("OrganizeImports") { import OrganizeImports._ import ImportMatcher._ @@ -58,7 +62,7 @@ class OrganizeImports(config: OrganizeImportsConfig) private val diagnostics: ArrayBuffer[Diagnostic] = ArrayBuffer.empty[Diagnostic] - def this() = this(OrganizeImportsConfig()) + def this() = this(OrganizeImportsConfig(), Dialect.current) override def description: String = "Organize import statements" @@ -687,6 +691,84 @@ class OrganizeImports(config: OrganizeImportsConfig) Patch.addLeft(token, indented mkString "\n") } + + private def prettyPrintImportGroup(group: Seq[Importer]): String = + group + .map(i => "import " + importerSyntax(i)) + .mkString("\n") + + /** + * HACK: The Scalafix pretty-printer decides to add spaces after open and + * before close braces in imports with multiple importees, i.e., `import a.{ + * b, c }` instead of `import a.{b, c}`. On the other hand, renames are + * pretty-printed without the extra spaces, e.g., `import a.{b => c}`. This + * behavior is not customizable and makes ordering imports by ASCII order + * complicated. + * + * This function removes the unwanted spaces as a workaround. In cases where + * users do want the inserted spaces, Scalafmt should be used after running + * the `OrganizeImports` rule. + */ + private def importerSyntax(importer: Importer): String = + importer.pos match { + case pos: Position.Range => + // Position found, implies that `importer` was directly parsed from the source code. Returns + // the original parsed text to preserve the original source level formatting. + pos.text + + case Position.None => + // Position not found, implies that `importer` is derived from certain existing import + // statement(s). Pretty-prints it. + val syntax = importer.syntax + + // NOTE: We need to check whether the input importer is curly braced first and then replace + // the first "{ " and the last " }" if any. Naive string replacement is insufficient, e.g., + // a quoted-identifier like "`{ d }`" may cause broken output. + (importer.isCurlyBraced, syntax lastIndexOfSlice " }") match { + case (_, -1) => + syntax + case (true, index) => + syntax.patch(index, "}", 2).replaceFirst("\\{ ", "{") + case _ => + syntax + } + } + + implicit private class ImporterExtension(importer: Importer) { + + /** Checks whether the `Importer` is curly-braced when pretty-printed. */ + def isCurlyBraced: Boolean = { + val importees @ Importees(_, renames, unimports, _, _, _) = + importer.importees + renames.nonEmpty || unimports.nonEmpty || importees.length > 1 + } + + /** + * Returns an `Importer` with all the `Importee`s that are selected from the + * input `Importer` and satisfy a predicate. If all the `Importee`s are + * selected, the input `Importer` instance is returned to preserve the + * original source level formatting. If none of the `Importee`s are + * selected, returns a `None`. + */ + def filterImportees(f: Importee => Boolean): Option[Importer] = { + val filtered = importer.importees filter f + if (filtered.length == importer.importees.length) Some(importer) + else if (filtered.isEmpty) None + else Some(importer.copy(importees = filtered)) + } + + /** Returns true if the `Importer` contains a standalone wildcard. */ + def hasWildcard: Boolean = { + val Importees(_, _, unimports, _, _, wildcard) = importer.importees + unimports.isEmpty && wildcard.nonEmpty + } + + /** Returns true if the `Importer` contains a standalone given wildcard. */ + def hasGivenAll: Boolean = { + val Importees(_, _, unimports, _, givenAll, _) = importer.importees + unimports.isEmpty && givenAll.nonEmpty + } + } } object OrganizeImports { @@ -720,7 +802,9 @@ object OrganizeImports { } if (!conf.removeUnused || hasWarnUnused) - Configured.ok(new OrganizeImports(conf)) + Configured.ok( + new OrganizeImports(conf, Dialect.current) + ) else if (hasCompilerSupport) Configured.error( "The Scala compiler option \"-Ywarn-unused\" is required to use OrganizeImports with" @@ -778,48 +862,6 @@ object OrganizeImports { } } - private def prettyPrintImportGroup(group: Seq[Importer]): String = - group - .map(i => "import " + importerSyntax(i)) - .mkString("\n") - - /** - * HACK: The Scalafix pretty-printer decides to add spaces after open and - * before close braces in imports with multiple importees, i.e., `import a.{ - * b, c }` instead of `import a.{b, c}`. On the other hand, renames are - * pretty-printed without the extra spaces, e.g., `import a.{b => c}`. This - * behavior is not customizable and makes ordering imports by ASCII order - * complicated. - * - * This function removes the unwanted spaces as a workaround. In cases where - * users do want the inserted spaces, Scalafmt should be used after running - * the `OrganizeImports` rule. - */ - private def importerSyntax(importer: Importer): String = - importer.pos match { - case pos: Position.Range => - // Position found, implies that `importer` was directly parsed from the source code. Returns - // the original parsed text to preserve the original source level formatting. - pos.text - - case Position.None => - // Position not found, implies that `importer` is derived from certain existing import - // statement(s). Pretty-prints it. - val syntax = importer.syntax - - // NOTE: We need to check whether the input importer is curly braced first and then replace - // the first "{ " and the last " }" if any. Naive string replacement is insufficient, e.g., - // a quoted-identifier like "`{ d }`" may cause broken output. - (importer.isCurlyBraced, syntax lastIndexOfSlice " }") match { - case (_, -1) => - syntax - case (true, index) => - syntax.patch(index, "}", 2).replaceFirst("\\{ ", "{") - case _ => - syntax - } - } - @tailrec private def topQualifierOf(term: Term): Term.Name = term match { case Term.Select(qualifier, _) => topQualifierOf(qualifier) @@ -969,40 +1011,4 @@ object OrganizeImports { def infoNoThrow(implicit doc: SemanticDocument): Option[SymbolInformation] = Try(symbol.info).toOption.flatten } - - implicit private class ImporterExtension(importer: Importer) { - - /** Checks whether the `Importer` is curly-braced when pretty-printed. */ - def isCurlyBraced: Boolean = { - val importees @ Importees(_, renames, unimports, _, _, _) = - importer.importees - renames.nonEmpty || unimports.nonEmpty || importees.length > 1 - } - - /** - * Returns an `Importer` with all the `Importee`s that are selected from the - * input `Importer` and satisfy a predicate. If all the `Importee`s are - * selected, the input `Importer` instance is returned to preserve the - * original source level formatting. If none of the `Importee`s are - * selected, returns a `None`. - */ - def filterImportees(f: Importee => Boolean): Option[Importer] = { - val filtered = importer.importees filter f - if (filtered.length == importer.importees.length) Some(importer) - else if (filtered.isEmpty) None - else Some(importer.copy(importees = filtered)) - } - - /** Returns true if the `Importer` contains a standalone wildcard. */ - def hasWildcard: Boolean = { - val Importees(_, _, unimports, _, _, wildcard) = importer.importees - unimports.isEmpty && wildcard.nonEmpty - } - - /** Returns true if the `Importer` contains a standalone given wildcard. */ - def hasGivenAll: Boolean = { - val Importees(_, _, unimports, _, givenAll, _) = importer.importees - unimports.isEmpty && givenAll.nonEmpty - } - } } From 9ff33fbbc1f52535b6dd2c504ec26fe211967237 Mon Sep 17 00:00:00 2001 From: Brice Jaglin Date: Tue, 30 Jan 2024 22:20:55 +0100 Subject: [PATCH 3/4] OrganizeImports: add targetDialect for consistent syntax --- docs/rules/OrganizeImports.md | 146 ++++++++++++++ .../internal/rule/OrganizeImports.scala | 184 +++++++++++++----- .../internal/rule/OrganizeImportsConfig.scala | 16 +- .../organizeImports/TargetDialectAuto.scala | 13 ++ .../CurlyBracedSingleImportee.scala | 7 + .../CoalesceImporteesGivensAndNames.scala | 1 + .../CoalesceImporteesNoGivens.scala | 1 + .../CoalesceImporteesNoGivensNoNames.scala | 1 + .../CoalesceImporteesNoNames.scala | 1 + .../CurlyBracedSingleImportee.scala | 19 ++ .../test/organizeImports/ExpandGiven.scala | 3 +- .../organizeImports/ExpandUnimportGiven.scala | 1 + .../GroupedGivenImportsMergeUnimports.scala | 19 +- ...roupedImportsAggressiveMergeGivenAll.scala | 1 + .../test/organizeImports/MergeGiven.scala | 6 +- .../MergeImportsFormatPreservingGiven.scala | 13 ++ .../organizeImports/TargetDialectScala2.scala | 11 ++ .../organizeImports/AlreadyOrganized.scala | 3 +- .../organizeImports/CoalesceImportees.scala | 1 + .../ExpandRelativeRootPackage.scala | 1 + .../ExplodeImportsFormatPreserving.scala | 3 +- ...roupedImportsAggressiveMergeWildcard.scala | 1 + .../GroupedImportsExplodeMixed.scala | 1 + .../GroupedImportsExplodeUnimport.scala | 1 + .../GroupedImportsMergeDedup.scala | 1 + .../GroupedImportsMergeRenames.scala | 1 + .../GroupedImportsMergeUnimports.scala | 1 + .../GroupedImportsMergeWildcard.scala | 1 + .../organizeImports/ImportsOrderKeep.scala | 1 + .../ImportsOrderSymbolsFirst.scala | 4 + .../MergeImportsFormatPreserving.scala | 3 +- .../organizeImports/TargetDialectAuto.scala | 6 + .../organizeImports/AlreadyOrganized.scala | 2 +- .../organizeImports/CoalesceImportees.scala | 0 .../CurlyBracedSingleImportee.scala | 12 ++ .../ExplodeImportsFormatPreserving.scala | 0 ...roupedImportsAggressiveMergeWildcard.scala | 0 .../GroupedImportsExplodeMixed.scala | 0 .../GroupedImportsExplodeUnimport.scala | 0 .../GroupedImportsMergeDedup.scala | 0 .../GroupedImportsMergeRenames.scala | 0 .../GroupedImportsMergeUnimports.scala | 0 .../GroupedImportsMergeWildcard.scala | 0 .../organizeImports/ImportsOrderKeep.scala | 0 .../ImportsOrderSymbolsFirst.scala | 3 + .../MergeImportsFormatPreserving.scala | 2 +- .../organizeImports/AlreadyOrganized.scala | 10 + .../organizeImports/CoalesceImportees.scala | 8 + .../CoalesceImporteesGivensAndNames.scala | 4 +- .../CoalesceImporteesNoGivens.scala | 2 +- .../CoalesceImporteesNoGivensNoNames.scala | 2 +- .../CoalesceImporteesNoNames.scala | 4 +- .../CurlyBracedSingleImportee.scala | 14 ++ .../DeduplicateGivenImportees.scala | 2 +- .../test/organizeImports/ExpandGiven.scala | 1 + .../organizeImports/ExpandUnimportGiven.scala | 4 +- .../ExplodeImportsFormatPreserving.scala | 7 + .../GroupedGivenImportsMergeUnimports.scala | 6 +- ...roupedImportsAggressiveMergeGivenAll.scala | 8 +- ...roupedImportsAggressiveMergeWildcard.scala | 7 + .../GroupedImportsExplodeMixed.scala | 7 + .../GroupedImportsExplodeUnimport.scala | 5 + .../GroupedImportsMergeDedup.scala | 5 + .../GroupedImportsMergeRenames.scala | 7 + .../GroupedImportsMergeUnimports.scala | 6 + .../GroupedImportsMergeWildcard.scala | 7 + .../organizeImports/ImportsOrderKeep.scala | 12 ++ .../ImportsOrderSymbolsFirst.scala | 15 ++ .../test/organizeImports/MergeGiven.scala | 2 +- .../MergeImportsFormatPreserving.scala | 7 + .../MergeImportsFormatPreservingGiven.scala | 6 + .../organizeImports/TargetDialectScala2.scala | 6 + .../CurlyBracedSingleImportee.scala | 6 - 73 files changed, 569 insertions(+), 92 deletions(-) create mode 100644 scalafix-tests/input/src/main/scala-2-xsource3/test/organizeImports/TargetDialectAuto.scala rename scalafix-tests/input/src/main/{scala => scala-2}/test/organizeImports/CurlyBracedSingleImportee.scala (53%) create mode 100644 scalafix-tests/input/src/main/scala-3/test/organizeImports/CurlyBracedSingleImportee.scala create mode 100644 scalafix-tests/input/src/main/scala-3/test/organizeImports/MergeImportsFormatPreservingGiven.scala create mode 100644 scalafix-tests/input/src/main/scala-3/test/organizeImports/TargetDialectScala2.scala create mode 100644 scalafix-tests/output/src/main/scala-2-xsource3/test/organizeImports/TargetDialectAuto.scala rename scalafix-tests/output/src/main/{scala => scala-2}/test/organizeImports/AlreadyOrganized.scala (91%) rename scalafix-tests/output/src/main/{scala => scala-2}/test/organizeImports/CoalesceImportees.scala (100%) create mode 100644 scalafix-tests/output/src/main/scala-2/test/organizeImports/CurlyBracedSingleImportee.scala rename scalafix-tests/output/src/main/{scala => scala-2}/test/organizeImports/ExplodeImportsFormatPreserving.scala (100%) rename scalafix-tests/output/src/main/{scala => scala-2}/test/organizeImports/GroupedImportsAggressiveMergeWildcard.scala (100%) rename scalafix-tests/output/src/main/{scala => scala-2}/test/organizeImports/GroupedImportsExplodeMixed.scala (100%) rename scalafix-tests/output/src/main/{scala => scala-2}/test/organizeImports/GroupedImportsExplodeUnimport.scala (100%) rename scalafix-tests/output/src/main/{scala => scala-2}/test/organizeImports/GroupedImportsMergeDedup.scala (100%) rename scalafix-tests/output/src/main/{scala => scala-2}/test/organizeImports/GroupedImportsMergeRenames.scala (100%) rename scalafix-tests/output/src/main/{scala => scala-2}/test/organizeImports/GroupedImportsMergeUnimports.scala (100%) rename scalafix-tests/output/src/main/{scala => scala-2}/test/organizeImports/GroupedImportsMergeWildcard.scala (100%) rename scalafix-tests/output/src/main/{scala => scala-2}/test/organizeImports/ImportsOrderKeep.scala (100%) rename scalafix-tests/output/src/main/{scala => scala-2}/test/organizeImports/ImportsOrderSymbolsFirst.scala (69%) rename scalafix-tests/output/src/main/{scala => scala-2}/test/organizeImports/MergeImportsFormatPreserving.scala (96%) create mode 100644 scalafix-tests/output/src/main/scala-3/test/organizeImports/AlreadyOrganized.scala create mode 100644 scalafix-tests/output/src/main/scala-3/test/organizeImports/CoalesceImportees.scala create mode 100644 scalafix-tests/output/src/main/scala-3/test/organizeImports/CurlyBracedSingleImportee.scala create mode 100644 scalafix-tests/output/src/main/scala-3/test/organizeImports/ExplodeImportsFormatPreserving.scala create mode 100644 scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsAggressiveMergeWildcard.scala create mode 100644 scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsExplodeMixed.scala create mode 100644 scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsExplodeUnimport.scala create mode 100644 scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsMergeDedup.scala create mode 100644 scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsMergeRenames.scala create mode 100644 scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsMergeUnimports.scala create mode 100644 scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsMergeWildcard.scala create mode 100644 scalafix-tests/output/src/main/scala-3/test/organizeImports/ImportsOrderKeep.scala create mode 100644 scalafix-tests/output/src/main/scala-3/test/organizeImports/ImportsOrderSymbolsFirst.scala create mode 100644 scalafix-tests/output/src/main/scala-3/test/organizeImports/MergeImportsFormatPreserving.scala create mode 100644 scalafix-tests/output/src/main/scala-3/test/organizeImports/MergeImportsFormatPreservingGiven.scala create mode 100644 scalafix-tests/output/src/main/scala-3/test/organizeImports/TargetDialectScala2.scala delete mode 100644 scalafix-tests/output/src/main/scala/test/organizeImports/CurlyBracedSingleImportee.scala diff --git a/docs/rules/OrganizeImports.md b/docs/rules/OrganizeImports.md index 748de82cf..9ccc05d39 100644 --- a/docs/rules/OrganizeImports.md +++ b/docs/rules/OrganizeImports.md @@ -1328,3 +1328,149 @@ object RemoveUnused { val long: JLong = JLong.parseLong("0") } ``` + +`targetDialect` +-------------- + +Specify the [wildcards and renames](https://docs.scala-lang.org/scala3/reference/changed-features/imports.html) +syntax to use. + +### Value type + +Enum: `Auto | Scala2 | Scala3 | StandardLayout` + +#### `Auto` + +Infer the dialect from compilation settings (Scala version or `-Xsource` when +provided) and behave like `Scala2` or `Scala3`. This is safe only for sources +that are not cross-compiled and therefore it is NOT the default value (see +rationale below). + +#### `Scala2` + +For all files, +* use `_` as wildcard and `=>` for renames +* curly braces are stripped for importers with a single regular importee + +#### `Scala3` + +For all files, +* use `*` as wildcard and `as` for renames +* curly braces are stripped for importers with a single importee + +#### `StandardLayout` + +For files containing `scala-3` in their path, +* use `*` as wildcard and `as` for renames +* curly braces are stripped for importers with a single importee + +For others, +* use `_` as wildcard and `=>` for renames +* curly braces are stripped for importers with a single regular importee + +### Default value + +`StandardLayout` + +Rationale: `Auto` is not a safe default for projects cross-compiling with +Scala 3.x and Scala 2.x without `-Xsource:3`, as the Scala 3.x Scalafix run +would introduce Scala 2.x compilation errors. + +### Examples + +#### `Scala2` + +```conf +OrganizeImports { + targetDialect = Scala2 +} +``` + +Before: + +```scala +import scala.collection.immutable.{List => L} +import scala.collection.mutable.{Map} +import scala.collection.mutable.{Buffer => _, Seq => S, _} +``` + +After: + +```scala +import scala.collection.immutable.{List => L} +import scala.collection.mutable.Map +import scala.collection.mutable.{Buffer => _, Seq => S, _} +``` + +#### `Scala3` + +```conf +OrganizeImports { + targetDialect = Scala3 +} +``` + +Before: + +```scala +import scala.collection.immutable.{List => L} +import scala.collection.mutable.{Map} +import scala.collection.mutable.{Buffer => _, Seq => S, _} +import scala.concurrent.ExecutionContext.Implicits.{given scala.concurrent.ExecutionContext} +``` + +After: + +```scala +import scala.collection.immutable.List as L +import scala.collection.mutable.Map +import scala.collection.mutable.{Buffer as _, Seq as S, *} +import scala.concurrent.ExecutionContext.Implicits.given scala.concurrent.ExecutionContext +``` + +#### `StandardLayout` + +```conf +OrganizeImports { + targetDialect = StandardLayout +} +``` + +##### `**/scala-3/**` files + +Before: + +```scala +import scala.collection.immutable.{List => L} +import scala.collection.mutable.{Map} +import scala.collection.mutable.{Buffer => _, Seq => S, _} +import scala.concurrent.ExecutionContext.Implicits.{given scala.concurrent.ExecutionContext} +``` + +After: + +```scala +import scala.collection.immutable.List as L +import scala.collection.mutable.Map +import scala.collection.mutable.{Buffer as _, Seq as S, *} +import scala.concurrent.ExecutionContext.Implicits.given scala.concurrent.ExecutionContext +``` + +##### Other files + +Before: + +```scala +import scala.collection.immutable.{List => L} +import scala.collection.mutable.{Map} +import scala.collection.mutable.{Buffer => _, Seq => S, _} +``` + +After: + +```scala +import scala.collection.immutable.{List => L} +import scala.collection.mutable.Map +import scala.collection.mutable.{Buffer => _, Seq => S, _} +``` + diff --git a/scalafix-rules/src/main/scala/scalafix/internal/rule/OrganizeImports.scala b/scalafix-rules/src/main/scala/scalafix/internal/rule/OrganizeImports.scala index 923387ba1..4b2ee9091 100644 --- a/scalafix-rules/src/main/scala/scalafix/internal/rule/OrganizeImports.scala +++ b/scalafix-rules/src/main/scala/scalafix/internal/rule/OrganizeImports.scala @@ -20,6 +20,9 @@ import scala.meta.Tree import scala.meta.XtensionClassifiable import scala.meta.XtensionCollectionLikeUI import scala.meta.XtensionSyntax +import scala.meta.dialects +import scala.meta.inputs.Input.File +import scala.meta.inputs.Input.VirtualFile import scala.meta.inputs.Position import scala.meta.tokens.Token @@ -29,6 +32,7 @@ import metaconfig.ConfEncoder import metaconfig.ConfOps import metaconfig.Configured import metaconfig.internal.ConfGet +import scalafix.internal.config.ScalaVersion import scalafix.internal.rule.ImportMatcher.* import scalafix.internal.rule.ImportMatcher.--- import scalafix.internal.rule.ImportMatcher.parse @@ -47,11 +51,15 @@ import scalafix.v1.XtensionTreeScalafix class OrganizeImports( config: OrganizeImportsConfig, // shadows the default implicit always on scope (Dialect.current, matching the runtime Scala version) - implicit val targetDialect: Dialect + implicit val targetDialect: Dialect = Dialect.current, + scala3DialectForScala3Paths: Boolean = false ) extends SemanticRule("OrganizeImports") { import OrganizeImports._ import ImportMatcher._ + private lazy val scala3TargetDialect = + new OrganizeImports(config, dialects.Scala3) + private val matchers = buildImportMatchers(config) private val wildcardGroupIndex: Int = matchers indexOf * @@ -62,7 +70,7 @@ class OrganizeImports( private val diagnostics: ArrayBuffer[Diagnostic] = ArrayBuffer.empty[Diagnostic] - def this() = this(OrganizeImportsConfig(), Dialect.current) + def this() = this(OrganizeImportsConfig()) override def description: String = "Organize import statements" @@ -77,6 +85,18 @@ class OrganizeImports( .andThen(checkScalacOptions(_, config.scalacOptions, config.scalaVersion)) override def fix(implicit doc: SemanticDocument): Patch = { + (doc.input, scala3DialectForScala3Paths) match { + case (File(path, _), true) + if path.toFile.getAbsolutePath.contains("scala-3/") => + scala3TargetDialect.fixWithImplicitDialect(doc) + case (VirtualFile(path, _), true) if path.contains("scala-3/") => + scala3TargetDialect.fixWithImplicitDialect(doc) + case _ => + fixWithImplicitDialect(doc) + } + } + + private def fixWithImplicitDialect(implicit doc: SemanticDocument): Patch = { unusedImporteePositions ++= doc.diagnostics.collect { case d if d.message == "Unused import" => d.position } @@ -316,22 +336,16 @@ class OrganizeImports( } private def organizeImportGroup(importers: Seq[Importer]): Seq[Importer] = { - // https://github.com/liancheng/scalafix-organize-imports/issues/96: For - // importers with only a single `Importee.Name` importee, if the importee - // is curly-braced, remove the unneeded curly-braces. For example: - // `import p.{X}` should be rewritten into `import p.X`. - val noUnneededBraces = importers map removeRedundantBrances - val importeesSorted = locally { config.groupedImports match { case GroupedImports.Merge => - mergeImporters(noUnneededBraces, aggressive = false) + mergeImporters(importers, aggressive = false) case GroupedImports.AggressiveMerge => - mergeImporters(noUnneededBraces, aggressive = true) + mergeImporters(importers, aggressive = true) case GroupedImports.Explode => - explodeImportees(noUnneededBraces) + explodeImportees(importers) case GroupedImports.Keep => - noUnneededBraces + importers } } map (coalesceImportees _ andThen sortImportees) @@ -350,22 +364,6 @@ class OrganizeImports( } } - private def removeRedundantBrances(importer: Importer): Importer = - importer match { - case i @ Importer(_, Importee.Name(_) :: Nil) => - import Token.{Ident, LeftBrace, RightBrace} - - i.tokens.reverse.toList match { - // The `.copy()` call erases the source position information from the original importer, so - // that instead of returning the original source text, the pretty-printer will reformat - // `importer` without the unneeded curly-braces. - case RightBrace() :: Ident(_) :: LeftBrace() :: _ => i.copy() - case _ => i - } - - case _ => importer - } - private def mergeImporters( importers: Seq[Importer], aggressive: Boolean @@ -570,7 +568,12 @@ class OrganizeImports( importer match { case Importer(_, Importee.Wildcard() :: Nil) => - syntax.patch(syntax.lastIndexOfSlice("._"), ".\u0001", 2) + val wildcardSyntax = Importee.Wildcard().syntax + syntax.patch( + syntax.lastIndexOfSlice(s".$wildcardSyntax"), + ".\u0001", + 2 + ) case _ if importer.isCurlyBraced => syntax @@ -697,30 +700,73 @@ class OrganizeImports( .map(i => "import " + importerSyntax(i)) .mkString("\n") - /** - * HACK: The Scalafix pretty-printer decides to add spaces after open and - * before close braces in imports with multiple importees, i.e., `import a.{ - * b, c }` instead of `import a.{b, c}`. On the other hand, renames are - * pretty-printed without the extra spaces, e.g., `import a.{b => c}`. This - * behavior is not customizable and makes ordering imports by ASCII order - * complicated. - * - * This function removes the unwanted spaces as a workaround. In cases where - * users do want the inserted spaces, Scalafmt should be used after running - * the `OrganizeImports` rule. - */ private def importerSyntax(importer: Importer): String = importer.pos match { case pos: Position.Range => - // Position found, implies that `importer` was directly parsed from the source code. Returns - // the original parsed text to preserve the original source level formatting. - pos.text + // Position found, implies that `importer` was directly parsed from the source code. Rewrite + // importees to ensure they follow the target dialect. For importers with a single importee, + // strip enclosing braces if they exist (or add/preserve them for Rename & Unimport on Scala 2). + + val syntax = new StringBuilder(pos.text) + + def patchSyntax( + t: Tree, + newSyntax: String + ) = { + val start = t.pos.start - pos.start + syntax.replace(start, t.pos.end - pos.start, newSyntax) + + if (importer.importees.length == 1) { + val end = t.pos.start - pos.start + newSyntax.length + ( + syntax.take(start).lastIndexOf('{'), + syntax.indexOf('}', end), + importer.isCurlyBraced + ) match { + case (-1, -1, true) => + // braces required but not detected + syntax.append('}') + syntax.insert(start, '{') + case (opening, closing, false) + if opening != -1 && closing != -1 => + // braces detected but not required + syntax.delete(end, closing + 1) + syntax.delete(opening, start) + case _ => + } + } + } + + // traverse & patch backwards to avoid shifting indices + importer.importees.reverse.foreach { + case i @ Importee.Rename(_, _) => + patchSyntax(i, i.copy().syntax) + case i @ Importee.Unimport(_) => + patchSyntax(i, i.copy().syntax) + case i @ Importee.Wildcard() => + patchSyntax(i, i.copy().syntax) + case i => + patchSyntax(i, i.syntax) + } + + syntax.toString case Position.None => // Position not found, implies that `importer` is derived from certain existing import // statement(s). Pretty-prints it. val syntax = importer.syntax + // HACK: The Scalafix pretty-printer decides to add spaces after open and + // before close braces in imports with multiple importees, i.e., `import a.{ + // b, c }` instead of `import a.{b, c}`. On the other hand, renames are + // pretty-printed without the extra spaces, e.g., `import a.{b => c}`. This + // behavior is not customizable and makes ordering imports by ASCII order + // complicated. + // + // This function removes the unwanted spaces as a workaround. In cases where + // users do want the inserted spaces, Scalafmt should be used after running + // the `OrganizeImports` rule. + // NOTE: We need to check whether the input importer is curly braced first and then replace // the first "{ " and the last " }" if any. Naive string replacement is insufficient, e.g., // a quoted-identifier like "`{ d }`" may cause broken output. @@ -736,11 +782,15 @@ class OrganizeImports( implicit private class ImporterExtension(importer: Importer) { - /** Checks whether the `Importer` is curly-braced when pretty-printed. */ + /** + * Checks whether the `Importer` should be curly-braced when pretty-printed. + */ def isCurlyBraced: Boolean = { val importees @ Importees(_, renames, unimports, _, _, _) = importer.importees - renames.nonEmpty || unimports.nonEmpty || importees.length > 1 + + importees.length > 1 || + ((renames.length == 1 || unimports.length == 1) && !targetDialect.allowAsForImportRename) } /** @@ -801,9 +851,41 @@ object OrganizeImports { } } + val (targetDialect, scala3DialectForScala3Paths) = + conf.targetDialect match { + case TargetDialect.Auto => + val dialect = ScalaVersion + .from(scalaVersion) + .map { scalaVersion => + def extractSuffixForScalacOption(prefix: String) = { + scalacOptions + .filter(_.startsWith(prefix)) + .lastOption + .map(_.stripPrefix(prefix)) + } + + // We only lookup the Scala 2 option (Scala 3 is `-source`), as the latest Scala 3 + // dialect is used no matter what the actual minor version is anyway, and as of now, + // the pretty printer is just more permissive with the latest dialect. + val sourceScalaVersion = + extractSuffixForScalacOption("-Xsource:") + .flatMap(ScalaVersion.from(_).toOption) + + scalaVersion.dialect(sourceScalaVersion) + } + .getOrElse(Dialect.current) + (dialect, false) + case TargetDialect.Scala2 => + (dialects.Scala212, false) + case TargetDialect.Scala3 => + (dialects.Scala3, false) + case TargetDialect.StandardLayout => + (dialects.Scala212, true) + } + if (!conf.removeUnused || hasWarnUnused) Configured.ok( - new OrganizeImports(conf, Dialect.current) + new OrganizeImports(conf, targetDialect, scala3DialectForScala3Paths) ) else if (hasCompilerSupport) Configured.error( @@ -952,11 +1034,11 @@ object OrganizeImports { * Categorizes a list of `Importee`s into the following four groups: * * - Names, e.g., `Seq`, `Option`, etc. - * - Renames, e.g., `{Long => JLong}`, `{Duration => D}`, etc. - * - Unimports, e.g., `{Foo => _}`. - * - Givens, e.g., `{given Foo}`. + * - Renames, e.g., `{Long => JLong}`, `Duration as D`, etc. + * - Unimports, e.g., `{Foo => _}` or `Foo as _`. + * - Givens, e.g., `given Foo`. * - GivenAll, i.e., `given`. - * - Wildcard, i.e., `_`. + * - Wildcard, i.e., `_` or `*`. */ object Importees { def unapply(importees: Seq[Importee]): Option[ diff --git a/scalafix-rules/src/main/scala/scalafix/internal/rule/OrganizeImportsConfig.scala b/scalafix-rules/src/main/scala/scalafix/internal/rule/OrganizeImportsConfig.scala index d9703df4a..1ffab3153 100644 --- a/scalafix-rules/src/main/scala/scalafix/internal/rule/OrganizeImportsConfig.scala +++ b/scalafix-rules/src/main/scala/scalafix/internal/rule/OrganizeImportsConfig.scala @@ -75,6 +75,19 @@ object Preset { ConfEncoder.instance(v => Conf.Str(v.toString)) } +sealed trait TargetDialect +object TargetDialect { + case object Auto extends TargetDialect + case object Scala2 extends TargetDialect + case object Scala3 extends TargetDialect + case object StandardLayout extends TargetDialect + + implicit def reader: ConfDecoder[TargetDialect] = + ReaderUtil.oneOf(Auto, Scala2, Scala3, StandardLayout) + implicit def writer: ConfEncoder[TargetDialect] = + ConfEncoder.instance(v => Conf.Str(v.toString)) +} + final case class OrganizeImportsConfig( blankLines: BlankLines = BlankLines.Auto, coalesceToWildcardImportThreshold: Option[Int] = None, @@ -88,7 +101,8 @@ final case class OrganizeImportsConfig( importSelectorsOrder: ImportSelectorsOrder = ImportSelectorsOrder.Ascii, importsOrder: ImportsOrder = ImportsOrder.Ascii, preset: Preset = Preset.DEFAULT, - removeUnused: Boolean = true + removeUnused: Boolean = true, + targetDialect: TargetDialect = TargetDialect.StandardLayout ) object OrganizeImportsConfig { diff --git a/scalafix-tests/input/src/main/scala-2-xsource3/test/organizeImports/TargetDialectAuto.scala b/scalafix-tests/input/src/main/scala-2-xsource3/test/organizeImports/TargetDialectAuto.scala new file mode 100644 index 000000000..3c2e21066 --- /dev/null +++ b/scalafix-tests/input/src/main/scala-2-xsource3/test/organizeImports/TargetDialectAuto.scala @@ -0,0 +1,13 @@ +/* +rules = [OrganizeImports] +OrganizeImports { + removeUnused = false + targetDialect = Auto +} + */ +package test.organizeImports + +import scala.collection.immutable.{List => L} +import scala.collection.mutable._ + +object TargetDialectAuto diff --git a/scalafix-tests/input/src/main/scala/test/organizeImports/CurlyBracedSingleImportee.scala b/scalafix-tests/input/src/main/scala-2/test/organizeImports/CurlyBracedSingleImportee.scala similarity index 53% rename from scalafix-tests/input/src/main/scala/test/organizeImports/CurlyBracedSingleImportee.scala rename to scalafix-tests/input/src/main/scala-2/test/organizeImports/CurlyBracedSingleImportee.scala index 9f8255a67..c31390c95 100644 --- a/scalafix-tests/input/src/main/scala/test/organizeImports/CurlyBracedSingleImportee.scala +++ b/scalafix-tests/input/src/main/scala-2/test/organizeImports/CurlyBracedSingleImportee.scala @@ -1,10 +1,17 @@ /* rules = [OrganizeImports] OrganizeImports.removeUnused = false +OrganizeImports.expandRelative = true */ package test.organizeImports +import PackageObject.{a} +import PackageObject.{a => A} + +import scala.collection.{ BitSet } import scala.collection.{Map} +import scala.collection.{Seq => _} import scala.collection.{Set => ImmutableSet} +import scala.util.{_} object CurlyBracedSingleImportee diff --git a/scalafix-tests/input/src/main/scala-3/test/organizeImports/CoalesceImporteesGivensAndNames.scala b/scalafix-tests/input/src/main/scala-3/test/organizeImports/CoalesceImporteesGivensAndNames.scala index 0d6a6fe0f..0992e5f5f 100644 --- a/scalafix-tests/input/src/main/scala-3/test/organizeImports/CoalesceImporteesGivensAndNames.scala +++ b/scalafix-tests/input/src/main/scala-3/test/organizeImports/CoalesceImporteesGivensAndNames.scala @@ -4,6 +4,7 @@ OrganizeImports { groupedImports = Keep coalesceToWildcardImportThreshold = 2 removeUnused = false + targetDialect = Scala3 } */ package test.organizeImports diff --git a/scalafix-tests/input/src/main/scala-3/test/organizeImports/CoalesceImporteesNoGivens.scala b/scalafix-tests/input/src/main/scala-3/test/organizeImports/CoalesceImporteesNoGivens.scala index 82e3aa7a8..1213a38ee 100644 --- a/scalafix-tests/input/src/main/scala-3/test/organizeImports/CoalesceImporteesNoGivens.scala +++ b/scalafix-tests/input/src/main/scala-3/test/organizeImports/CoalesceImporteesNoGivens.scala @@ -4,6 +4,7 @@ OrganizeImports { groupedImports = Keep coalesceToWildcardImportThreshold = 2 removeUnused = false + targetDialect = Scala3 } */ package test.organizeImports diff --git a/scalafix-tests/input/src/main/scala-3/test/organizeImports/CoalesceImporteesNoGivensNoNames.scala b/scalafix-tests/input/src/main/scala-3/test/organizeImports/CoalesceImporteesNoGivensNoNames.scala index e91fedb27..d1ba21051 100644 --- a/scalafix-tests/input/src/main/scala-3/test/organizeImports/CoalesceImporteesNoGivensNoNames.scala +++ b/scalafix-tests/input/src/main/scala-3/test/organizeImports/CoalesceImporteesNoGivensNoNames.scala @@ -4,6 +4,7 @@ OrganizeImports { groupedImports = Keep coalesceToWildcardImportThreshold = 2 removeUnused = false + targetDialect = Scala3 } */ package test.organizeImports diff --git a/scalafix-tests/input/src/main/scala-3/test/organizeImports/CoalesceImporteesNoNames.scala b/scalafix-tests/input/src/main/scala-3/test/organizeImports/CoalesceImporteesNoNames.scala index 9e374ccff..6478767ca 100644 --- a/scalafix-tests/input/src/main/scala-3/test/organizeImports/CoalesceImporteesNoNames.scala +++ b/scalafix-tests/input/src/main/scala-3/test/organizeImports/CoalesceImporteesNoNames.scala @@ -4,6 +4,7 @@ OrganizeImports { groupedImports = Keep coalesceToWildcardImportThreshold = 2 removeUnused = false + targetDialect = Scala3 } */ package test.organizeImports diff --git a/scalafix-tests/input/src/main/scala-3/test/organizeImports/CurlyBracedSingleImportee.scala b/scalafix-tests/input/src/main/scala-3/test/organizeImports/CurlyBracedSingleImportee.scala new file mode 100644 index 000000000..c255739d3 --- /dev/null +++ b/scalafix-tests/input/src/main/scala-3/test/organizeImports/CurlyBracedSingleImportee.scala @@ -0,0 +1,19 @@ +/* +rules = [OrganizeImports] +OrganizeImports.removeUnused = false +OrganizeImports.expandRelative = true + */ +package test.organizeImports + +import GivenImports.{given GivenImports.Alpha} +import GivenImports.{given} +import PackageObject.{a} +import PackageObject.{a as A} + +import scala.collection.{ BitSet } +import scala.collection.{Map} +import scala.collection.{Seq as _} +import scala.collection.{Set as ImmutableSet} +import scala.util.{*} + +object CurlyBracedSingleImportee diff --git a/scalafix-tests/input/src/main/scala-3/test/organizeImports/ExpandGiven.scala b/scalafix-tests/input/src/main/scala-3/test/organizeImports/ExpandGiven.scala index f23f6848d..7d15ade39 100644 --- a/scalafix-tests/input/src/main/scala-3/test/organizeImports/ExpandGiven.scala +++ b/scalafix-tests/input/src/main/scala-3/test/organizeImports/ExpandGiven.scala @@ -1,12 +1,13 @@ /* rules = [OrganizeImports] OrganizeImports.removeUnused = false +OrganizeImports.targetDialect = Scala3 */ package test.organizeImports import test.organizeImports.GivenImports.Beta import test.organizeImports.GivenImports.Alpha -import test.organizeImports.GivenImports.{given Beta, given Alpha} +import test.organizeImports.GivenImports.{given Beta, given test.organizeImports.GivenImports.Gamma, given Alpha} import scala.util.Either object ExpandGiven diff --git a/scalafix-tests/input/src/main/scala-3/test/organizeImports/ExpandUnimportGiven.scala b/scalafix-tests/input/src/main/scala-3/test/organizeImports/ExpandUnimportGiven.scala index 457db872f..baf2545cd 100644 --- a/scalafix-tests/input/src/main/scala-3/test/organizeImports/ExpandUnimportGiven.scala +++ b/scalafix-tests/input/src/main/scala-3/test/organizeImports/ExpandUnimportGiven.scala @@ -1,6 +1,7 @@ /* rules = [OrganizeImports] OrganizeImports.removeUnused = false +OrganizeImports.targetDialect = Scala3 */ package test.organizeImports diff --git a/scalafix-tests/input/src/main/scala-3/test/organizeImports/GroupedGivenImportsMergeUnimports.scala b/scalafix-tests/input/src/main/scala-3/test/organizeImports/GroupedGivenImportsMergeUnimports.scala index f34b71897..3fe1a6e14 100644 --- a/scalafix-tests/input/src/main/scala-3/test/organizeImports/GroupedGivenImportsMergeUnimports.scala +++ b/scalafix-tests/input/src/main/scala-3/test/organizeImports/GroupedGivenImportsMergeUnimports.scala @@ -2,18 +2,19 @@ rules = [OrganizeImports] OrganizeImports.removeUnused = false OrganizeImports.groupedImports = Merge +OrganizeImports.targetDialect = Scala3 */ package test.organizeImports -import test.organizeImports.GivenImports._ -import test.organizeImports.GivenImports.{alpha => _, given} -import test.organizeImports.GivenImports.{given Beta} -import test.organizeImports.GivenImports.{gamma => _, given} -import test.organizeImports.GivenImports.{given Zeta} +import test.organizeImports.GivenImports.* +import test.organizeImports.GivenImports.{alpha as _, given} +import test.organizeImports.GivenImports.given Beta +import test.organizeImports.GivenImports.{gamma as _, given} +import test.organizeImports.GivenImports.given Zeta -import test.organizeImports.GivenImports2.{alpha => _} -import test.organizeImports.GivenImports2.{beta => _} -import test.organizeImports.GivenImports2.{given Gamma} -import test.organizeImports.GivenImports2.{given Zeta} +import test.organizeImports.GivenImports2.alpha as _ +import test.organizeImports.GivenImports2.beta as _ +import test.organizeImports.GivenImports2.given Gamma +import test.organizeImports.GivenImports2.given Zeta object GroupedGivenImportsMergeUnimports diff --git a/scalafix-tests/input/src/main/scala-3/test/organizeImports/GroupedImportsAggressiveMergeGivenAll.scala b/scalafix-tests/input/src/main/scala-3/test/organizeImports/GroupedImportsAggressiveMergeGivenAll.scala index bb0632fcc..7266c6556 100644 --- a/scalafix-tests/input/src/main/scala-3/test/organizeImports/GroupedImportsAggressiveMergeGivenAll.scala +++ b/scalafix-tests/input/src/main/scala-3/test/organizeImports/GroupedImportsAggressiveMergeGivenAll.scala @@ -2,6 +2,7 @@ rules = [OrganizeImports] OrganizeImports.removeUnused = false OrganizeImports.groupedImports = AggressiveMerge +OrganizeImports.targetDialect = Scala3 */ package test.organizeImports diff --git a/scalafix-tests/input/src/main/scala-3/test/organizeImports/MergeGiven.scala b/scalafix-tests/input/src/main/scala-3/test/organizeImports/MergeGiven.scala index a7d628d91..f45028d28 100644 --- a/scalafix-tests/input/src/main/scala-3/test/organizeImports/MergeGiven.scala +++ b/scalafix-tests/input/src/main/scala-3/test/organizeImports/MergeGiven.scala @@ -2,13 +2,15 @@ rules = [OrganizeImports] OrganizeImports.removeUnused = false OrganizeImports.groupedImports = Merge +OrganizeImports.targetDialect = Scala3 */ package test.organizeImports import test.organizeImports.GivenImports.Beta import test.organizeImports.GivenImports.Alpha -import test.organizeImports.GivenImports.{given Beta} -import test.organizeImports.GivenImports.{given Alpha} +import test.organizeImports.GivenImports.given Beta +import test.organizeImports.GivenImports.given test.organizeImports.GivenImports.Gamma +import test.organizeImports.GivenImports.given Alpha import scala.util.Either object MergeGiven diff --git a/scalafix-tests/input/src/main/scala-3/test/organizeImports/MergeImportsFormatPreservingGiven.scala b/scalafix-tests/input/src/main/scala-3/test/organizeImports/MergeImportsFormatPreservingGiven.scala new file mode 100644 index 000000000..27ba4a32f --- /dev/null +++ b/scalafix-tests/input/src/main/scala-3/test/organizeImports/MergeImportsFormatPreservingGiven.scala @@ -0,0 +1,13 @@ +/* +rules = [OrganizeImports] +OrganizeImports.removeUnused = false +OrganizeImports.groupedImports = Merge +OrganizeImports.targetDialect = Scala3 + */ + +package test.organizeImports + +import test.organizeImports.GivenImports.* +import test.organizeImports.GivenImports.{ given Alpha, given Beta } + +object MergeImportsFormatPreservingGiven diff --git a/scalafix-tests/input/src/main/scala-3/test/organizeImports/TargetDialectScala2.scala b/scalafix-tests/input/src/main/scala-3/test/organizeImports/TargetDialectScala2.scala new file mode 100644 index 000000000..df5e61c22 --- /dev/null +++ b/scalafix-tests/input/src/main/scala-3/test/organizeImports/TargetDialectScala2.scala @@ -0,0 +1,11 @@ +/* +rules = [OrganizeImports] +OrganizeImports.removeUnused = false +OrganizeImports.targetDialect = Scala2 + */ +package test.organizeImports + +import scala.util.Either as E +import scala.util.Try as _ + +object TargetDialectDowngrade diff --git a/scalafix-tests/input/src/main/scala/test/organizeImports/AlreadyOrganized.scala b/scalafix-tests/input/src/main/scala/test/organizeImports/AlreadyOrganized.scala index 392a2d280..fe95cf0b3 100644 --- a/scalafix-tests/input/src/main/scala/test/organizeImports/AlreadyOrganized.scala +++ b/scalafix-tests/input/src/main/scala/test/organizeImports/AlreadyOrganized.scala @@ -2,6 +2,7 @@ rules = [OrganizeImports] OrganizeImports.removeUnused = false OrganizeImports.groupedImports = Merge +OrganizeImports.targetDialect = Auto */ package test.organizeImports @@ -9,7 +10,7 @@ import scala.collection.mutable.{ ArrayBuffer, Map, Queue, - Set + Set => S } object AlreadyOrganized diff --git a/scalafix-tests/input/src/main/scala/test/organizeImports/CoalesceImportees.scala b/scalafix-tests/input/src/main/scala/test/organizeImports/CoalesceImportees.scala index 6a35ea974..619c09d81 100644 --- a/scalafix-tests/input/src/main/scala/test/organizeImports/CoalesceImportees.scala +++ b/scalafix-tests/input/src/main/scala/test/organizeImports/CoalesceImportees.scala @@ -4,6 +4,7 @@ OrganizeImports { groupedImports = Keep coalesceToWildcardImportThreshold = 3 removeUnused = false + targetDialect = Auto } */ package test.organizeImports diff --git a/scalafix-tests/input/src/main/scala/test/organizeImports/ExpandRelativeRootPackage.scala b/scalafix-tests/input/src/main/scala/test/organizeImports/ExpandRelativeRootPackage.scala index 0b096c22c..b4821e80c 100644 --- a/scalafix-tests/input/src/main/scala/test/organizeImports/ExpandRelativeRootPackage.scala +++ b/scalafix-tests/input/src/main/scala/test/organizeImports/ExpandRelativeRootPackage.scala @@ -2,6 +2,7 @@ rules = [OrganizeImports] OrganizeImports.removeUnused = false OrganizeImports.expandRelative = true +OrganizeImports.targetDialect = Auto */ package test.organizeImports diff --git a/scalafix-tests/input/src/main/scala/test/organizeImports/ExplodeImportsFormatPreserving.scala b/scalafix-tests/input/src/main/scala/test/organizeImports/ExplodeImportsFormatPreserving.scala index 179b9c68a..30bf63201 100644 --- a/scalafix-tests/input/src/main/scala/test/organizeImports/ExplodeImportsFormatPreserving.scala +++ b/scalafix-tests/input/src/main/scala/test/organizeImports/ExplodeImportsFormatPreserving.scala @@ -2,11 +2,12 @@ rules = [OrganizeImports] OrganizeImports.removeUnused = false OrganizeImports.groupedImports = Explode +OrganizeImports.targetDialect = Auto */ package test.organizeImports import test.organizeImports.ExplodeImports.FormatPreserving.g1.{ a, b } -import test.organizeImports.ExplodeImports.FormatPreserving.g2.{ c => C, _ } +import test.organizeImports.ExplodeImports.FormatPreserving.g2.{ c => C, _ } object ExplodeImportsFormatPreserving diff --git a/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsAggressiveMergeWildcard.scala b/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsAggressiveMergeWildcard.scala index e31c70827..fd905e9ec 100644 --- a/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsAggressiveMergeWildcard.scala +++ b/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsAggressiveMergeWildcard.scala @@ -2,6 +2,7 @@ rules = [OrganizeImports] OrganizeImports.removeUnused = false OrganizeImports.groupedImports = AggressiveMerge +OrganizeImports.targetDialect = Auto */ package test.organizeImports diff --git a/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsExplodeMixed.scala b/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsExplodeMixed.scala index 9d438645d..5eef1dac5 100644 --- a/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsExplodeMixed.scala +++ b/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsExplodeMixed.scala @@ -1,6 +1,7 @@ /* rules = [OrganizeImports] OrganizeImports.removeUnused = false +OrganizeImports.targetDialect = Auto */ package test.organizeImports diff --git a/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsExplodeUnimport.scala b/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsExplodeUnimport.scala index acd497cea..fa3c2d1ef 100644 --- a/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsExplodeUnimport.scala +++ b/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsExplodeUnimport.scala @@ -1,6 +1,7 @@ /* rules = [OrganizeImports] OrganizeImports.removeUnused = false +OrganizeImports.targetDialect = Auto */ package test.organizeImports diff --git a/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsMergeDedup.scala b/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsMergeDedup.scala index 15fe853fd..a3f134859 100644 --- a/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsMergeDedup.scala +++ b/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsMergeDedup.scala @@ -2,6 +2,7 @@ rules = [OrganizeImports] OrganizeImports.removeUnused = false OrganizeImports.groupedImports = Merge +OrganizeImports.targetDialect = Auto */ package test.organizeImports diff --git a/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsMergeRenames.scala b/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsMergeRenames.scala index 3a4c2cc60..283834f49 100644 --- a/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsMergeRenames.scala +++ b/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsMergeRenames.scala @@ -2,6 +2,7 @@ rules = [OrganizeImports] OrganizeImports.removeUnused = false OrganizeImports.groupedImports = Merge +OrganizeImports.targetDialect = Auto */ package test.organizeImports diff --git a/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsMergeUnimports.scala b/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsMergeUnimports.scala index 511213668..9b592134c 100644 --- a/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsMergeUnimports.scala +++ b/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsMergeUnimports.scala @@ -2,6 +2,7 @@ rules = [OrganizeImports] OrganizeImports.removeUnused = false OrganizeImports.groupedImports = Merge +OrganizeImports.targetDialect = Auto */ package test.organizeImports diff --git a/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsMergeWildcard.scala b/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsMergeWildcard.scala index b81148506..ff7961ab8 100644 --- a/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsMergeWildcard.scala +++ b/scalafix-tests/input/src/main/scala/test/organizeImports/GroupedImportsMergeWildcard.scala @@ -2,6 +2,7 @@ rules = [OrganizeImports] OrganizeImports.removeUnused = false OrganizeImports.groupedImports = Merge +OrganizeImports.targetDialect = Auto */ package test.organizeImports diff --git a/scalafix-tests/input/src/main/scala/test/organizeImports/ImportsOrderKeep.scala b/scalafix-tests/input/src/main/scala/test/organizeImports/ImportsOrderKeep.scala index 7e08de4d1..05bdb4ac6 100644 --- a/scalafix-tests/input/src/main/scala/test/organizeImports/ImportsOrderKeep.scala +++ b/scalafix-tests/input/src/main/scala/test/organizeImports/ImportsOrderKeep.scala @@ -5,6 +5,7 @@ OrganizeImports { groupedImports = Keep importSelectorsOrder = Keep importsOrder = Keep + targetDialect = Auto } */ package test.organizeImports diff --git a/scalafix-tests/input/src/main/scala/test/organizeImports/ImportsOrderSymbolsFirst.scala b/scalafix-tests/input/src/main/scala/test/organizeImports/ImportsOrderSymbolsFirst.scala index ee3b2db00..cbd4fb8ef 100644 --- a/scalafix-tests/input/src/main/scala/test/organizeImports/ImportsOrderSymbolsFirst.scala +++ b/scalafix-tests/input/src/main/scala/test/organizeImports/ImportsOrderSymbolsFirst.scala @@ -5,6 +5,7 @@ OrganizeImports { groupedImports = Keep importSelectorsOrder = Keep importsOrder = SymbolsFirst + targetDialect = Auto } */ package test.organizeImports @@ -15,6 +16,9 @@ import scala.concurrent.duration import scala.concurrent.{Promise, Future} import test.organizeImports.QuotedIdent.`a.b`.`{ d }`.e +import test.organizeImports.QuotedIdent.`a.b`.`{ d }`.{ e => E } +import test.organizeImports.QuotedIdent.`a.b` +import test.organizeImports.QuotedIdent.{`a.b` => ab} import test.organizeImports.QuotedIdent.`a.b`.{c => _, _} import test.organizeImports.QuotedIdent._ diff --git a/scalafix-tests/input/src/main/scala/test/organizeImports/MergeImportsFormatPreserving.scala b/scalafix-tests/input/src/main/scala/test/organizeImports/MergeImportsFormatPreserving.scala index 9be512c10..fa13febdc 100644 --- a/scalafix-tests/input/src/main/scala/test/organizeImports/MergeImportsFormatPreserving.scala +++ b/scalafix-tests/input/src/main/scala/test/organizeImports/MergeImportsFormatPreserving.scala @@ -2,11 +2,12 @@ rules = [OrganizeImports] OrganizeImports.removeUnused = false OrganizeImports.groupedImports = Merge +OrganizeImports.targetDialect = Auto */ package test.organizeImports -import test.organizeImports.MergeImports.FormatPreserving.g1.{ a, b } +import test.organizeImports.MergeImports.FormatPreserving.g1.{ a, b => B } import test.organizeImports.MergeImports.FormatPreserving.g2._ import test.organizeImports.MergeImports.FormatPreserving.g2.{ d => D } diff --git a/scalafix-tests/output/src/main/scala-2-xsource3/test/organizeImports/TargetDialectAuto.scala b/scalafix-tests/output/src/main/scala-2-xsource3/test/organizeImports/TargetDialectAuto.scala new file mode 100644 index 000000000..42e45c1f6 --- /dev/null +++ b/scalafix-tests/output/src/main/scala-2-xsource3/test/organizeImports/TargetDialectAuto.scala @@ -0,0 +1,6 @@ +package test.organizeImports + +import scala.collection.immutable.List as L +import scala.collection.mutable.* + +object TargetDialectAuto diff --git a/scalafix-tests/output/src/main/scala/test/organizeImports/AlreadyOrganized.scala b/scalafix-tests/output/src/main/scala-2/test/organizeImports/AlreadyOrganized.scala similarity index 91% rename from scalafix-tests/output/src/main/scala/test/organizeImports/AlreadyOrganized.scala rename to scalafix-tests/output/src/main/scala-2/test/organizeImports/AlreadyOrganized.scala index 8c4fbc1ed..9d3b4b245 100644 --- a/scalafix-tests/output/src/main/scala/test/organizeImports/AlreadyOrganized.scala +++ b/scalafix-tests/output/src/main/scala-2/test/organizeImports/AlreadyOrganized.scala @@ -4,7 +4,7 @@ import scala.collection.mutable.{ ArrayBuffer, Map, Queue, - Set + Set => S } object AlreadyOrganized diff --git a/scalafix-tests/output/src/main/scala/test/organizeImports/CoalesceImportees.scala b/scalafix-tests/output/src/main/scala-2/test/organizeImports/CoalesceImportees.scala similarity index 100% rename from scalafix-tests/output/src/main/scala/test/organizeImports/CoalesceImportees.scala rename to scalafix-tests/output/src/main/scala-2/test/organizeImports/CoalesceImportees.scala diff --git a/scalafix-tests/output/src/main/scala-2/test/organizeImports/CurlyBracedSingleImportee.scala b/scalafix-tests/output/src/main/scala-2/test/organizeImports/CurlyBracedSingleImportee.scala new file mode 100644 index 000000000..2e1f14587 --- /dev/null +++ b/scalafix-tests/output/src/main/scala-2/test/organizeImports/CurlyBracedSingleImportee.scala @@ -0,0 +1,12 @@ +package test.organizeImports + +import test.organizeImports.PackageObject.a +import test.organizeImports.PackageObject.{a => A} + +import scala.collection.BitSet +import scala.collection.Map +import scala.collection.{Seq => _} +import scala.collection.{Set => ImmutableSet} +import scala.util._ + +object CurlyBracedSingleImportee diff --git a/scalafix-tests/output/src/main/scala/test/organizeImports/ExplodeImportsFormatPreserving.scala b/scalafix-tests/output/src/main/scala-2/test/organizeImports/ExplodeImportsFormatPreserving.scala similarity index 100% rename from scalafix-tests/output/src/main/scala/test/organizeImports/ExplodeImportsFormatPreserving.scala rename to scalafix-tests/output/src/main/scala-2/test/organizeImports/ExplodeImportsFormatPreserving.scala diff --git a/scalafix-tests/output/src/main/scala/test/organizeImports/GroupedImportsAggressiveMergeWildcard.scala b/scalafix-tests/output/src/main/scala-2/test/organizeImports/GroupedImportsAggressiveMergeWildcard.scala similarity index 100% rename from scalafix-tests/output/src/main/scala/test/organizeImports/GroupedImportsAggressiveMergeWildcard.scala rename to scalafix-tests/output/src/main/scala-2/test/organizeImports/GroupedImportsAggressiveMergeWildcard.scala diff --git a/scalafix-tests/output/src/main/scala/test/organizeImports/GroupedImportsExplodeMixed.scala b/scalafix-tests/output/src/main/scala-2/test/organizeImports/GroupedImportsExplodeMixed.scala similarity index 100% rename from scalafix-tests/output/src/main/scala/test/organizeImports/GroupedImportsExplodeMixed.scala rename to scalafix-tests/output/src/main/scala-2/test/organizeImports/GroupedImportsExplodeMixed.scala diff --git a/scalafix-tests/output/src/main/scala/test/organizeImports/GroupedImportsExplodeUnimport.scala b/scalafix-tests/output/src/main/scala-2/test/organizeImports/GroupedImportsExplodeUnimport.scala similarity index 100% rename from scalafix-tests/output/src/main/scala/test/organizeImports/GroupedImportsExplodeUnimport.scala rename to scalafix-tests/output/src/main/scala-2/test/organizeImports/GroupedImportsExplodeUnimport.scala diff --git a/scalafix-tests/output/src/main/scala/test/organizeImports/GroupedImportsMergeDedup.scala b/scalafix-tests/output/src/main/scala-2/test/organizeImports/GroupedImportsMergeDedup.scala similarity index 100% rename from scalafix-tests/output/src/main/scala/test/organizeImports/GroupedImportsMergeDedup.scala rename to scalafix-tests/output/src/main/scala-2/test/organizeImports/GroupedImportsMergeDedup.scala diff --git a/scalafix-tests/output/src/main/scala/test/organizeImports/GroupedImportsMergeRenames.scala b/scalafix-tests/output/src/main/scala-2/test/organizeImports/GroupedImportsMergeRenames.scala similarity index 100% rename from scalafix-tests/output/src/main/scala/test/organizeImports/GroupedImportsMergeRenames.scala rename to scalafix-tests/output/src/main/scala-2/test/organizeImports/GroupedImportsMergeRenames.scala diff --git a/scalafix-tests/output/src/main/scala/test/organizeImports/GroupedImportsMergeUnimports.scala b/scalafix-tests/output/src/main/scala-2/test/organizeImports/GroupedImportsMergeUnimports.scala similarity index 100% rename from scalafix-tests/output/src/main/scala/test/organizeImports/GroupedImportsMergeUnimports.scala rename to scalafix-tests/output/src/main/scala-2/test/organizeImports/GroupedImportsMergeUnimports.scala diff --git a/scalafix-tests/output/src/main/scala/test/organizeImports/GroupedImportsMergeWildcard.scala b/scalafix-tests/output/src/main/scala-2/test/organizeImports/GroupedImportsMergeWildcard.scala similarity index 100% rename from scalafix-tests/output/src/main/scala/test/organizeImports/GroupedImportsMergeWildcard.scala rename to scalafix-tests/output/src/main/scala-2/test/organizeImports/GroupedImportsMergeWildcard.scala diff --git a/scalafix-tests/output/src/main/scala/test/organizeImports/ImportsOrderKeep.scala b/scalafix-tests/output/src/main/scala-2/test/organizeImports/ImportsOrderKeep.scala similarity index 100% rename from scalafix-tests/output/src/main/scala/test/organizeImports/ImportsOrderKeep.scala rename to scalafix-tests/output/src/main/scala-2/test/organizeImports/ImportsOrderKeep.scala diff --git a/scalafix-tests/output/src/main/scala/test/organizeImports/ImportsOrderSymbolsFirst.scala b/scalafix-tests/output/src/main/scala-2/test/organizeImports/ImportsOrderSymbolsFirst.scala similarity index 69% rename from scalafix-tests/output/src/main/scala/test/organizeImports/ImportsOrderSymbolsFirst.scala rename to scalafix-tests/output/src/main/scala-2/test/organizeImports/ImportsOrderSymbolsFirst.scala index 5bd3317c0..35b7e1d52 100644 --- a/scalafix-tests/output/src/main/scala/test/organizeImports/ImportsOrderSymbolsFirst.scala +++ b/scalafix-tests/output/src/main/scala-2/test/organizeImports/ImportsOrderSymbolsFirst.scala @@ -1,7 +1,10 @@ package test.organizeImports import test.organizeImports.QuotedIdent._ +import test.organizeImports.QuotedIdent.{`a.b` => ab} +import test.organizeImports.QuotedIdent.`a.b` import test.organizeImports.QuotedIdent.`a.b`.{c => _, _} +import test.organizeImports.QuotedIdent.`a.b`.`{ d }`.{ e => E } import test.organizeImports.QuotedIdent.`a.b`.`{ d }`.e import scala.concurrent._ diff --git a/scalafix-tests/output/src/main/scala/test/organizeImports/MergeImportsFormatPreserving.scala b/scalafix-tests/output/src/main/scala-2/test/organizeImports/MergeImportsFormatPreserving.scala similarity index 96% rename from scalafix-tests/output/src/main/scala/test/organizeImports/MergeImportsFormatPreserving.scala rename to scalafix-tests/output/src/main/scala-2/test/organizeImports/MergeImportsFormatPreserving.scala index dee1ea2a2..1e1219851 100644 --- a/scalafix-tests/output/src/main/scala/test/organizeImports/MergeImportsFormatPreserving.scala +++ b/scalafix-tests/output/src/main/scala-2/test/organizeImports/MergeImportsFormatPreserving.scala @@ -1,6 +1,6 @@ package test.organizeImports -import test.organizeImports.MergeImports.FormatPreserving.g1.{ a, b } +import test.organizeImports.MergeImports.FormatPreserving.g1.{ a, b => B } import test.organizeImports.MergeImports.FormatPreserving.g2._ import test.organizeImports.MergeImports.FormatPreserving.g2.{ d => D } diff --git a/scalafix-tests/output/src/main/scala-3/test/organizeImports/AlreadyOrganized.scala b/scalafix-tests/output/src/main/scala-3/test/organizeImports/AlreadyOrganized.scala new file mode 100644 index 000000000..325ab5d2a --- /dev/null +++ b/scalafix-tests/output/src/main/scala-3/test/organizeImports/AlreadyOrganized.scala @@ -0,0 +1,10 @@ +package test.organizeImports + +import scala.collection.mutable.{ + ArrayBuffer, + Map, + Queue, + Set as S +} + +object AlreadyOrganized diff --git a/scalafix-tests/output/src/main/scala-3/test/organizeImports/CoalesceImportees.scala b/scalafix-tests/output/src/main/scala-3/test/organizeImports/CoalesceImportees.scala new file mode 100644 index 000000000..51fb1f21b --- /dev/null +++ b/scalafix-tests/output/src/main/scala-3/test/organizeImports/CoalesceImportees.scala @@ -0,0 +1,8 @@ +package test.organizeImports + +import scala.collection.immutable.{Map, Seq, Vector} +import scala.collection.mutable.* +import scala.concurrent.{Channel as Ch, *} +import scala.util.{Random as _, *} + +object CoalesceImportees diff --git a/scalafix-tests/output/src/main/scala-3/test/organizeImports/CoalesceImporteesGivensAndNames.scala b/scalafix-tests/output/src/main/scala-3/test/organizeImports/CoalesceImporteesGivensAndNames.scala index e9530cea6..9cd087797 100644 --- a/scalafix-tests/output/src/main/scala-3/test/organizeImports/CoalesceImporteesGivensAndNames.scala +++ b/scalafix-tests/output/src/main/scala-3/test/organizeImports/CoalesceImporteesGivensAndNames.scala @@ -1,6 +1,6 @@ package test.organizeImports -import test.organizeImports.Givens._ -import test.organizeImports.Givens.{B => B1, C => _, _, given} +import test.organizeImports.Givens.* +import test.organizeImports.Givens.{B as B1, C as _, *, given} object CoalesceImporteesGivensAndNames diff --git a/scalafix-tests/output/src/main/scala-3/test/organizeImports/CoalesceImporteesNoGivens.scala b/scalafix-tests/output/src/main/scala-3/test/organizeImports/CoalesceImporteesNoGivens.scala index b5bbceec9..851e4a8d9 100644 --- a/scalafix-tests/output/src/main/scala-3/test/organizeImports/CoalesceImporteesNoGivens.scala +++ b/scalafix-tests/output/src/main/scala-3/test/organizeImports/CoalesceImporteesNoGivens.scala @@ -1,5 +1,5 @@ package test.organizeImports -import test.organizeImports.Givens.{C => C1, _} +import test.organizeImports.Givens.{C as C1, *} object CoalesceImporteesNoGivens diff --git a/scalafix-tests/output/src/main/scala-3/test/organizeImports/CoalesceImporteesNoGivensNoNames.scala b/scalafix-tests/output/src/main/scala-3/test/organizeImports/CoalesceImporteesNoGivensNoNames.scala index 7d4401969..e3085049c 100644 --- a/scalafix-tests/output/src/main/scala-3/test/organizeImports/CoalesceImporteesNoGivensNoNames.scala +++ b/scalafix-tests/output/src/main/scala-3/test/organizeImports/CoalesceImporteesNoGivensNoNames.scala @@ -1,5 +1,5 @@ package test.organizeImports -import test.organizeImports.Givens.{A => A1, B => _, _} +import test.organizeImports.Givens.{A as A1, B as _, *} object CoalesceImporteesNoGivensNoNames diff --git a/scalafix-tests/output/src/main/scala-3/test/organizeImports/CoalesceImporteesNoNames.scala b/scalafix-tests/output/src/main/scala-3/test/organizeImports/CoalesceImporteesNoNames.scala index c6a2d2940..147685d25 100644 --- a/scalafix-tests/output/src/main/scala-3/test/organizeImports/CoalesceImporteesNoNames.scala +++ b/scalafix-tests/output/src/main/scala-3/test/organizeImports/CoalesceImporteesNoNames.scala @@ -1,6 +1,6 @@ package test.organizeImports -import test.organizeImports.Givens._ -import test.organizeImports.Givens.{A => A1, given} +import test.organizeImports.Givens.* +import test.organizeImports.Givens.{A as A1, given} object CoalesceImporteesNoNames diff --git a/scalafix-tests/output/src/main/scala-3/test/organizeImports/CurlyBracedSingleImportee.scala b/scalafix-tests/output/src/main/scala-3/test/organizeImports/CurlyBracedSingleImportee.scala new file mode 100644 index 000000000..40ea9077c --- /dev/null +++ b/scalafix-tests/output/src/main/scala-3/test/organizeImports/CurlyBracedSingleImportee.scala @@ -0,0 +1,14 @@ +package test.organizeImports + +import test.organizeImports.GivenImports.given +import test.organizeImports.GivenImports.given GivenImports.Alpha +import test.organizeImports.PackageObject.a +import test.organizeImports.PackageObject.a as A + +import scala.collection.BitSet +import scala.collection.Map +import scala.collection.Seq as _ +import scala.collection.Set as ImmutableSet +import scala.util.* + +object CurlyBracedSingleImportee diff --git a/scalafix-tests/output/src/main/scala-3/test/organizeImports/DeduplicateGivenImportees.scala b/scalafix-tests/output/src/main/scala-3/test/organizeImports/DeduplicateGivenImportees.scala index 67b6cb5c4..7ce77bd26 100644 --- a/scalafix-tests/output/src/main/scala-3/test/organizeImports/DeduplicateGivenImportees.scala +++ b/scalafix-tests/output/src/main/scala-3/test/organizeImports/DeduplicateGivenImportees.scala @@ -1,6 +1,6 @@ package test.organizeImports -import test.organizeImports.Givens._ +import test.organizeImports.Givens.* import test.organizeImports.Givens.given A import test.organizeImports.Givens.given B import test.organizeImports.Givens.given C diff --git a/scalafix-tests/output/src/main/scala-3/test/organizeImports/ExpandGiven.scala b/scalafix-tests/output/src/main/scala-3/test/organizeImports/ExpandGiven.scala index e5e028ac8..d14eafbf1 100644 --- a/scalafix-tests/output/src/main/scala-3/test/organizeImports/ExpandGiven.scala +++ b/scalafix-tests/output/src/main/scala-3/test/organizeImports/ExpandGiven.scala @@ -4,6 +4,7 @@ import test.organizeImports.GivenImports.Alpha import test.organizeImports.GivenImports.Beta import test.organizeImports.GivenImports.given Alpha import test.organizeImports.GivenImports.given Beta +import test.organizeImports.GivenImports.given test.organizeImports.GivenImports.Gamma import scala.util.Either diff --git a/scalafix-tests/output/src/main/scala-3/test/organizeImports/ExpandUnimportGiven.scala b/scalafix-tests/output/src/main/scala-3/test/organizeImports/ExpandUnimportGiven.scala index 132be5915..df538e181 100644 --- a/scalafix-tests/output/src/main/scala-3/test/organizeImports/ExpandUnimportGiven.scala +++ b/scalafix-tests/output/src/main/scala-3/test/organizeImports/ExpandUnimportGiven.scala @@ -2,9 +2,9 @@ package test.organizeImports import test.organizeImports.GivenImports.Alpha import test.organizeImports.GivenImports.Beta +import test.organizeImports.GivenImports.alpha as _ import test.organizeImports.GivenImports.given Alpha -import test.organizeImports.GivenImports.{alpha => _} -import test.organizeImports.GivenImports.{beta => _, given} +import test.organizeImports.GivenImports.{beta as _, given} import scala.util.Either diff --git a/scalafix-tests/output/src/main/scala-3/test/organizeImports/ExplodeImportsFormatPreserving.scala b/scalafix-tests/output/src/main/scala-3/test/organizeImports/ExplodeImportsFormatPreserving.scala new file mode 100644 index 000000000..8d5d0fc47 --- /dev/null +++ b/scalafix-tests/output/src/main/scala-3/test/organizeImports/ExplodeImportsFormatPreserving.scala @@ -0,0 +1,7 @@ +package test.organizeImports + +import test.organizeImports.ExplodeImports.FormatPreserving.g1.a +import test.organizeImports.ExplodeImports.FormatPreserving.g1.b +import test.organizeImports.ExplodeImports.FormatPreserving.g2.{ c as C, * } + +object ExplodeImportsFormatPreserving diff --git a/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedGivenImportsMergeUnimports.scala b/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedGivenImportsMergeUnimports.scala index 4a1a27b26..a6856a8d8 100644 --- a/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedGivenImportsMergeUnimports.scala +++ b/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedGivenImportsMergeUnimports.scala @@ -1,8 +1,8 @@ package test.organizeImports -import test.organizeImports.GivenImports._ -import test.organizeImports.GivenImports.{gamma => _, given Beta, given Zeta, given} -import test.organizeImports.GivenImports2.{alpha => _, beta => _} +import test.organizeImports.GivenImports.* +import test.organizeImports.GivenImports.{gamma as _, given Beta, given Zeta, given} +import test.organizeImports.GivenImports2.{alpha as _, beta as _} import test.organizeImports.GivenImports2.{given Gamma, given Zeta} object GroupedGivenImportsMergeUnimports diff --git a/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsAggressiveMergeGivenAll.scala b/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsAggressiveMergeGivenAll.scala index 2c5436cc0..a6be3f3f7 100644 --- a/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsAggressiveMergeGivenAll.scala +++ b/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsAggressiveMergeGivenAll.scala @@ -1,10 +1,10 @@ package test.organizeImports -import test.organizeImports.GivenImports._ +import test.organizeImports.GivenImports.* import test.organizeImports.GivenImports.given -import test.organizeImports.MergeImports.Wildcard1._ -import test.organizeImports.MergeImports.Wildcard1.{b => B} -import test.organizeImports.MergeImports.Wildcard2._ +import test.organizeImports.MergeImports.Wildcard1.* +import test.organizeImports.MergeImports.Wildcard1.b as B +import test.organizeImports.MergeImports.Wildcard2.* object GroupedImportsAggressiveMergeGivenAll diff --git a/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsAggressiveMergeWildcard.scala b/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsAggressiveMergeWildcard.scala new file mode 100644 index 000000000..c8e490dd3 --- /dev/null +++ b/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsAggressiveMergeWildcard.scala @@ -0,0 +1,7 @@ +package test.organizeImports + +import test.organizeImports.MergeImports.Wildcard1.* +import test.organizeImports.MergeImports.Wildcard1.b as B +import test.organizeImports.MergeImports.Wildcard2.* + +object GroupedImportsAggressiveMergeWildcard diff --git a/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsExplodeMixed.scala b/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsExplodeMixed.scala new file mode 100644 index 000000000..8e46fb80a --- /dev/null +++ b/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsExplodeMixed.scala @@ -0,0 +1,7 @@ +package test.organizeImports + +import scala.collection.immutable.* +import scala.collection.mutable.Map +import scala.collection.mutable.{Buffer as _, Seq as S, *} + +object GroupedImportsExplodeMixed diff --git a/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsExplodeUnimport.scala b/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsExplodeUnimport.scala new file mode 100644 index 000000000..777a49fdd --- /dev/null +++ b/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsExplodeUnimport.scala @@ -0,0 +1,5 @@ +package test.organizeImports + +import scala.collection.{Seq as _, *} + +object GroupedImportExplodeUnimport diff --git a/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsMergeDedup.scala b/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsMergeDedup.scala new file mode 100644 index 000000000..ce51ff68f --- /dev/null +++ b/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsMergeDedup.scala @@ -0,0 +1,5 @@ +package test.organizeImports + +import test.organizeImports.MergeImports.Dedup.{a, b as b1, c as _} + +object GroupedImportsMergeDedup diff --git a/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsMergeRenames.scala b/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsMergeRenames.scala new file mode 100644 index 000000000..d50ab9199 --- /dev/null +++ b/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsMergeRenames.scala @@ -0,0 +1,7 @@ +package test.organizeImports + +import test.organizeImports.MergeImports.Rename1.{a as A, b as B, c, d} +import test.organizeImports.MergeImports.Rename2.{a as A, b as B, c} +import test.organizeImports.MergeImports.Rename2.{a, b} + +object GroupedImportsMergeRenames diff --git a/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsMergeUnimports.scala b/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsMergeUnimports.scala new file mode 100644 index 000000000..27cb850b0 --- /dev/null +++ b/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsMergeUnimports.scala @@ -0,0 +1,6 @@ +package test.organizeImports + +import test.organizeImports.MergeImports.Unimport1.{b as B, c as _, d, *} +import test.organizeImports.MergeImports.Unimport2.{a as _, b as _, c as C, d} + +object GroupedImportsMergeUnimports diff --git a/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsMergeWildcard.scala b/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsMergeWildcard.scala new file mode 100644 index 000000000..ddf279523 --- /dev/null +++ b/scalafix-tests/output/src/main/scala-3/test/organizeImports/GroupedImportsMergeWildcard.scala @@ -0,0 +1,7 @@ +package test.organizeImports + +import test.organizeImports.MergeImports.Wildcard1.b as B +import test.organizeImports.MergeImports.Wildcard1.{d, *} +import test.organizeImports.MergeImports.Wildcard2.{a, b, *} + +object GroupedImportsMergeWildcard diff --git a/scalafix-tests/output/src/main/scala-3/test/organizeImports/ImportsOrderKeep.scala b/scalafix-tests/output/src/main/scala-3/test/organizeImports/ImportsOrderKeep.scala new file mode 100644 index 000000000..ce414ea89 --- /dev/null +++ b/scalafix-tests/output/src/main/scala-3/test/organizeImports/ImportsOrderKeep.scala @@ -0,0 +1,12 @@ +package test.organizeImports + +import test.organizeImports.QuotedIdent.`a.b`.`{ d }`.e +import test.organizeImports.QuotedIdent.* +import test.organizeImports.QuotedIdent.`a.b`.{c as _, *} + +import scala.concurrent.ExecutionContext.Implicits.* +import scala.concurrent.duration +import scala.concurrent.* +import scala.concurrent.{Promise, Future} + +object ImportsOrderKeep diff --git a/scalafix-tests/output/src/main/scala-3/test/organizeImports/ImportsOrderSymbolsFirst.scala b/scalafix-tests/output/src/main/scala-3/test/organizeImports/ImportsOrderSymbolsFirst.scala new file mode 100644 index 000000000..fcb1f004b --- /dev/null +++ b/scalafix-tests/output/src/main/scala-3/test/organizeImports/ImportsOrderSymbolsFirst.scala @@ -0,0 +1,15 @@ +package test.organizeImports + +import test.organizeImports.QuotedIdent.* +import test.organizeImports.QuotedIdent.`a.b` +import test.organizeImports.QuotedIdent.`a.b` as ab +import test.organizeImports.QuotedIdent.`a.b`.{c as _, *} +import test.organizeImports.QuotedIdent.`a.b`.`{ d }`.e +import test.organizeImports.QuotedIdent.`a.b`.`{ d }`.e as E + +import scala.concurrent.* +import scala.concurrent.{Promise, Future} +import scala.concurrent.ExecutionContext.Implicits.* +import scala.concurrent.duration + +object ImportsOrderSymbolsFirst diff --git a/scalafix-tests/output/src/main/scala-3/test/organizeImports/MergeGiven.scala b/scalafix-tests/output/src/main/scala-3/test/organizeImports/MergeGiven.scala index b02b7e822..b5230e6fd 100644 --- a/scalafix-tests/output/src/main/scala-3/test/organizeImports/MergeGiven.scala +++ b/scalafix-tests/output/src/main/scala-3/test/organizeImports/MergeGiven.scala @@ -1,7 +1,7 @@ package test.organizeImports import test.organizeImports.GivenImports.{Alpha, Beta} -import test.organizeImports.GivenImports.{given Alpha, given Beta} +import test.organizeImports.GivenImports.{given Alpha, given Beta, given test.organizeImports.GivenImports.Gamma} import scala.util.Either diff --git a/scalafix-tests/output/src/main/scala-3/test/organizeImports/MergeImportsFormatPreserving.scala b/scalafix-tests/output/src/main/scala-3/test/organizeImports/MergeImportsFormatPreserving.scala new file mode 100644 index 000000000..2d5352f7f --- /dev/null +++ b/scalafix-tests/output/src/main/scala-3/test/organizeImports/MergeImportsFormatPreserving.scala @@ -0,0 +1,7 @@ +package test.organizeImports + +import test.organizeImports.MergeImports.FormatPreserving.g1.{ a, b as B } +import test.organizeImports.MergeImports.FormatPreserving.g2.* +import test.organizeImports.MergeImports.FormatPreserving.g2.d as D + +object MergeImportsFormatPreserving diff --git a/scalafix-tests/output/src/main/scala-3/test/organizeImports/MergeImportsFormatPreservingGiven.scala b/scalafix-tests/output/src/main/scala-3/test/organizeImports/MergeImportsFormatPreservingGiven.scala new file mode 100644 index 000000000..5e1ac6bf6 --- /dev/null +++ b/scalafix-tests/output/src/main/scala-3/test/organizeImports/MergeImportsFormatPreservingGiven.scala @@ -0,0 +1,6 @@ +package test.organizeImports + +import test.organizeImports.GivenImports.* +import test.organizeImports.GivenImports.{ given Alpha, given Beta } + +object MergeImportsFormatPreservingGiven diff --git a/scalafix-tests/output/src/main/scala-3/test/organizeImports/TargetDialectScala2.scala b/scalafix-tests/output/src/main/scala-3/test/organizeImports/TargetDialectScala2.scala new file mode 100644 index 000000000..966f82cea --- /dev/null +++ b/scalafix-tests/output/src/main/scala-3/test/organizeImports/TargetDialectScala2.scala @@ -0,0 +1,6 @@ +package test.organizeImports + +import scala.util.{Either => E} +import scala.util.{Try => _} + +object TargetDialectDowngrade diff --git a/scalafix-tests/output/src/main/scala/test/organizeImports/CurlyBracedSingleImportee.scala b/scalafix-tests/output/src/main/scala/test/organizeImports/CurlyBracedSingleImportee.scala deleted file mode 100644 index 4a2996246..000000000 --- a/scalafix-tests/output/src/main/scala/test/organizeImports/CurlyBracedSingleImportee.scala +++ /dev/null @@ -1,6 +0,0 @@ -package test.organizeImports - -import scala.collection.Map -import scala.collection.{Set => ImmutableSet} - -object CurlyBracedSingleImportee From 332901360f68b752f4e00aa6935a0741679a24f9 Mon Sep 17 00:00:00 2001 From: Brice Jaglin Date: Sat, 3 Feb 2024 10:28:44 +0100 Subject: [PATCH 4/4] OrganizeImports: document confusing behavior with star symbol Even when a * symbol exists, Scala 3 (and the Scalameta parser with the Scala 3 dialect) will treat imports with that syntax as wildcards. For projects cross-building in Scala 2 and 3 and explicitly referencing such a symbol in the importers, runs of OrganizeImports forcing Scala 2 syntax will yield inconsistent results, as * would confusingly (but not so incorrectly) be downgraded to _ in the Scala 3 run, would it will remain a reference to the symbol in the Scala 2 run. I attempted to align the Scala 3 run behavior with the Scala 2 run one, but after a while it became clear that it would require a big redesign of the flow (because the original syntax behind the wildcard gets lost across tree copies) and would add a lot of complexity for very little value. Also, I believe the current behavior is acceptable as the Scala 3 run output is stable against a Scala 2 run. That is why I decided to simply document the behavior via a test (which should hopefully prevent changes that would remove that property in the future) instead of fixing it. --- .../test/organizeImports/ImportStarType.scala | 17 +++++++++++++++++ .../test/organizeImports/ImportStarType.scala | 6 ++++++ .../test/organizeImports/ImportStarType.scala | 8 ++++++++ .../scala/test/organizeImports/StarType.scala | 6 ++++++ 4 files changed, 37 insertions(+) create mode 100644 scalafix-tests/input/src/main/scala/test/organizeImports/ImportStarType.scala create mode 100644 scalafix-tests/output/src/main/scala-2/test/organizeImports/ImportStarType.scala create mode 100644 scalafix-tests/output/src/main/scala-3/test/organizeImports/ImportStarType.scala create mode 100644 scalafix-tests/shared/src/main/scala/test/organizeImports/StarType.scala diff --git a/scalafix-tests/input/src/main/scala/test/organizeImports/ImportStarType.scala b/scalafix-tests/input/src/main/scala/test/organizeImports/ImportStarType.scala new file mode 100644 index 000000000..3b9b75ef4 --- /dev/null +++ b/scalafix-tests/input/src/main/scala/test/organizeImports/ImportStarType.scala @@ -0,0 +1,17 @@ +/* +rules = [OrganizeImports] +OrganizeImports.removeUnused = false +OrganizeImports.expandRelative = true +OrganizeImports.targetDialect = Scala2 + */ +package test.organizeImports + +import test.organizeImports.StarType.{---, *} + +import test.organizeImports.StarType.* +import test.organizeImports.StarType.--- + +import StarType.* +import StarType.--- + +object ImportStarType diff --git a/scalafix-tests/output/src/main/scala-2/test/organizeImports/ImportStarType.scala b/scalafix-tests/output/src/main/scala-2/test/organizeImports/ImportStarType.scala new file mode 100644 index 000000000..247afe1b4 --- /dev/null +++ b/scalafix-tests/output/src/main/scala-2/test/organizeImports/ImportStarType.scala @@ -0,0 +1,6 @@ +package test.organizeImports + +import test.organizeImports.StarType.* +import test.organizeImports.StarType.--- + +object ImportStarType diff --git a/scalafix-tests/output/src/main/scala-3/test/organizeImports/ImportStarType.scala b/scalafix-tests/output/src/main/scala-3/test/organizeImports/ImportStarType.scala new file mode 100644 index 000000000..4e3ba129c --- /dev/null +++ b/scalafix-tests/output/src/main/scala-3/test/organizeImports/ImportStarType.scala @@ -0,0 +1,8 @@ +package test.organizeImports + +import test.organizeImports.StarType.--- +import test.organizeImports.StarType._ +import test.organizeImports.StarType._ +import test.organizeImports.StarType._ + +object ImportStarType diff --git a/scalafix-tests/shared/src/main/scala/test/organizeImports/StarType.scala b/scalafix-tests/shared/src/main/scala/test/organizeImports/StarType.scala new file mode 100644 index 000000000..3091253aa --- /dev/null +++ b/scalafix-tests/shared/src/main/scala/test/organizeImports/StarType.scala @@ -0,0 +1,6 @@ +package test.organizeImports + +object StarType { + object * + object --- +}