Skip to content

Commit 9d908a2

Browse files
committed
scalafix: add scalafix.dep, more tests
1 parent bb38e9c commit 9d908a2

File tree

9 files changed

+249
-85
lines changed

9 files changed

+249
-85
lines changed

modules/build/src/main/scala/scala/build/ScalafixArtifacts.scala

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ object ScalafixArtifacts {
2525

2626
def artifacts(
2727
scalaVersion: String,
28-
compileOnlyDeps: Seq[Dependency],
28+
externalRulesDeps: Seq[Positioned[AnyDependency]],
2929
extraRepositories: Seq[Repository],
3030
logger: Logger,
3131
cache: FileCache[Task]
@@ -39,6 +39,7 @@ object ScalafixArtifacts {
3939

4040
val scalafixDeps =
4141
Seq(dep"ch.epfl.scala:scalafix-cli_$fetchScalaVersion:${Constants.scalafixVersion}")
42+
4243
val scalafix =
4344
value(
4445
Artifacts.artifacts(
@@ -50,16 +51,23 @@ object ScalafixArtifacts {
5051
)
5152
)
5253

54+
val scalaParameters =
55+
// Scalafix for scala 3 uses 2.13-published community rules
56+
// https://github.com/scalacenter/scalafix/issues/2041
57+
if (scalaVersion.startsWith("3")) ScalaParameters(Constants.defaultScala213Version)
58+
else ScalaParameters(scalaVersion)
59+
5360
val tools =
5461
value(
5562
Artifacts.artifacts(
56-
compileOnlyDeps.map(Positioned.none),
63+
externalRulesDeps,
5764
extraRepositories,
58-
None,
65+
Some(scalaParameters),
5966
logger,
60-
cache.withMessage(s"Downloading tools classpath for scalafix")
67+
cache.withMessage(s"Downloading scalafix.deps")
6168
)
6269
)
70+
6371
ScalafixArtifacts(scalafix.map(_._2), tools.map(_._2))
6472
}
6573

@@ -81,7 +89,6 @@ object ScalafixArtifacts {
8189
propsData
8290
}
8391
else os.read(cachePath)
84-
8592
val props = new Properties()
8693
val stream = new ByteArrayInputStream(content.getBytes())
8794
props.load(stream)

modules/cli/src/main/scala/scala/cli/commands/scalafix/Scalafix.scala

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@ import caseapp.core.help.HelpFormat
55
import coursier.cache.FileCache
66
import dependency.*
77
import scalafix.interfaces.ScalafixError.*
8-
import scalafix.interfaces.{Scalafix => ScalafixInterface, ScalafixError, ScalafixException, ScalafixRule}
8+
import scalafix.interfaces.{
9+
Scalafix => ScalafixInterface,
10+
ScalafixError,
11+
ScalafixException,
12+
ScalafixRule
13+
}
914

1015
import java.io.File
1116
import java.util.Optional
@@ -96,12 +101,6 @@ object Scalafix extends ScalaCommand[ScalafixOptions] {
96101
case None => sys.exit(1)
97102
case Some(build) =>
98103
val classPaths = build.fullClassPath
99-
val compileOnlyDeps = {
100-
val params = ScalaParameters(scalaVersion)
101-
build.options.classPathOptions.extraCompileOnlyDependencies.values.flatten.map(
102-
_.value.applyParams(params)
103-
)
104-
}
105104

106105
val scalacOptions = options.shared.scalac.scalacOption ++
107106
build.options.scalaOptions.scalacOptions.toSeq.map(_.value.value)
@@ -111,18 +110,17 @@ object Scalafix extends ScalaCommand[ScalafixOptions] {
111110
value(
112111
ScalafixArtifacts.artifacts(
113112
scalaVersion,
114-
compileOnlyDeps,
113+
build.options.classPathOptions.scalafixDependencies.values.flatten,
115114
value(buildOptions.finalRepositories),
116115
logger,
117116
buildOptions.internal.cache.getOrElse(FileCache())
118117
)
119118
)
120119

121120
val scalafixOptions =
122-
configFilePathOpt.map(file => Seq("-c", file.toString)).getOrElse(Nil) ++
121+
options.scalafixConf.toList.flatMap(scalafixConf => List("--config", scalafixConf)) ++
123122
Seq("--sourceroot", workspace.toString) ++
124123
Seq("--classpath", classPaths.mkString(java.io.File.pathSeparator)) ++
125-
options.scalafixConf.toList.flatMap(scalafixConf => List("--config", scalafixConf)) ++
126124
(if (options.check) Seq("--test") else Nil) ++
127125
(if (scalacOptions.nonEmpty) scalacOptions.flatMap(Seq("--scalac-options", _))
128126
else Nil) ++

modules/cli/src/main/scala/scala/cli/commands/scalafix/ScalafixOptions.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ final case class ScalafixOptions(
3434
@Tag(tags.experimental)
3535
@HelpMessage("Run rule(s) explicitly, overriding the configuration file default.")
3636
@Tag(tags.inShortHelp)
37-
@Name("r")
3837
rules: List[String] = Nil,
3938

4039
@Group(HelpGroup.Format.toString)

modules/directives/src/main/scala/scala/build/preprocessing/directives/Dependency.scala

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import scala.build.options.{
1414
ShadowingSeq,
1515
WithBuildRequirements
1616
}
17+
import scala.build.preprocessing.directives.Dependency.DependencyType
1718
import scala.build.preprocessing.directives.DirectiveUtil.*
1819
import scala.cli.commands.SpecificationLevel
1920

@@ -46,27 +47,48 @@ final case class Dependency(
4647
@DirectiveName("compileOnly.dep")
4748
@DirectiveName("compileOnly.deps")
4849
@DirectiveName("compileOnly.dependencies")
49-
compileOnlyDependency: List[Positioned[String]] = Nil
50+
compileOnlyDependency: List[Positioned[String]] = Nil,
51+
@DirectiveName("scalafix.dep")
52+
@DirectiveName("scalafix.deps")
53+
@DirectiveName("scalafix.dependencies")
54+
scalafixDependency: List[Positioned[String]] = Nil
5055
) extends HasBuildOptionsWithRequirements {
5156
def buildOptionsList: List[Either[BuildException, WithBuildRequirements[BuildOptions]]] = List(
52-
Dependency.buildOptions(dependency).map(_.withEmptyRequirements),
53-
Dependency.buildOptions(testDependency).map(_.withScopeRequirement(Scope.Test)),
54-
Dependency.buildOptions(compileOnlyDependency, isCompileOnly = true)
57+
Dependency.buildOptions(dependency, DependencyType.Runtime).map(_.withEmptyRequirements),
58+
Dependency.buildOptions(testDependency, DependencyType.Runtime).map(
59+
_.withScopeRequirement(Scope.Test)
60+
),
61+
Dependency.buildOptions(compileOnlyDependency, DependencyType.CompileOnly)
62+
.map(_.withEmptyRequirements),
63+
Dependency.buildOptions(scalafixDependency, DependencyType.Scalafix)
5564
.map(_.withEmptyRequirements)
5665
)
5766
}
5867

5968
object Dependency {
6069
val handler: DirectiveHandler[Dependency] = DirectiveHandler.derive
70+
71+
sealed trait DependencyType
72+
object DependencyType {
73+
case object Runtime extends DependencyType
74+
case object CompileOnly extends DependencyType
75+
case object Scalafix extends DependencyType
76+
}
77+
6178
def buildOptions(
6279
ds: List[Positioned[String]],
63-
isCompileOnly: Boolean = false
80+
tpe: DependencyType
6481
): Either[BuildException, BuildOptions] = either {
6582
val dependencies: ShadowingSeq[Positioned[AnyDependency]] =
6683
value(ds.asDependencies.map(ShadowingSeq.from))
6784
val classPathOptions =
68-
if (isCompileOnly) ClassPathOptions(extraCompileOnlyDependencies = dependencies)
69-
else ClassPathOptions(extraDependencies = dependencies)
85+
tpe match {
86+
case DependencyType.Runtime => ClassPathOptions(extraDependencies = dependencies)
87+
case DependencyType.CompileOnly =>
88+
ClassPathOptions(extraCompileOnlyDependencies = dependencies)
89+
case DependencyType.Scalafix => ClassPathOptions(scalafixDependencies = dependencies)
90+
}
91+
7092
BuildOptions(classPathOptions = classPathOptions)
7193
}
7294
}

modules/integration/src/test/scala/scala/cli/integration/ScalafixTestDefinitions.scala

Lines changed: 152 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ abstract class ScalafixTestDefinitions extends ScalaCliSuite with TestScalaVersi
101101
}
102102
}
103103

104-
test("rule args") {
104+
test("--rules args") {
105105
val input = TestInputs(
106106
os.rel / confFileName ->
107107
s"""|rules = [
@@ -149,4 +149,155 @@ abstract class ScalafixTestDefinitions extends ScalaCliSuite with TestScalaVersi
149149

150150
}
151151
}
152+
153+
test("--scalafix-arg arg") {
154+
val original: String =
155+
"""|package foo
156+
|
157+
|final object Hello { // keep `final` beucase of parameter finalObject=false
158+
| s"Foo"
159+
|}
160+
|""".stripMargin
161+
val inputs: TestInputs = TestInputs(
162+
os.rel / confFileName ->
163+
s"""|rules = [
164+
| RedundantSyntax
165+
|]
166+
|""".stripMargin,
167+
os.rel / "Hello.scala" -> original
168+
)
169+
val expectedContent: String = noCrLf {
170+
"""|package foo
171+
|
172+
|final object Hello { // keep `final` beucase of parameter finalObject=false
173+
| "Foo"
174+
|}
175+
|""".stripMargin
176+
}
177+
178+
inputs.fromRoot { root =>
179+
os.proc(
180+
TestUtil.cli,
181+
"scalafix",
182+
".",
183+
"--scalafix-arg=--settings.RedundantSyntax.finalObject=false",
184+
"--power",
185+
scalaVersionArgs
186+
).call(cwd = root)
187+
val updatedContent = noCrLf(os.read(root / "Hello.scala"))
188+
expect(updatedContent == expectedContent)
189+
}
190+
}
191+
192+
test("--scalafix-conf arg") {
193+
val original: String =
194+
"""|package foo
195+
|
196+
|final object Hello {
197+
| s"Foo"
198+
|}
199+
|""".stripMargin
200+
201+
val confFileName = "unusual-scalafix-filename"
202+
val inputs: TestInputs = TestInputs(
203+
os.rel / confFileName ->
204+
s"""|rules = [
205+
| RedundantSyntax
206+
|]
207+
|""".stripMargin,
208+
os.rel / "Hello.scala" -> original
209+
)
210+
val expectedContent: String = noCrLf {
211+
"""|package foo
212+
|
213+
|object Hello {
214+
| "Foo"
215+
|}
216+
|""".stripMargin
217+
}
218+
219+
inputs.fromRoot { root =>
220+
os.proc(
221+
TestUtil.cli,
222+
"scalafix",
223+
".",
224+
s"--scalafix-conf=$confFileName",
225+
"--power",
226+
scalaVersionArgs
227+
).call(cwd = root)
228+
val updatedContent = noCrLf(os.read(root / "Hello.scala"))
229+
expect(updatedContent == expectedContent)
230+
}
231+
}
232+
233+
test("external rule") {
234+
val original: String =
235+
"""|//> using scalafix.dep "io.github.ghostbuster91.scalafix-unified::unified:0.0.8"
236+
|
237+
|package foo
238+
|
239+
|object Hello {
240+
| val a = List[Int]()
241+
|}
242+
|""".stripMargin
243+
val inputs: TestInputs = TestInputs(
244+
os.rel / confFileName ->
245+
s"""|rules = [
246+
| EmptyCollectionsUnified
247+
|]
248+
|""".stripMargin,
249+
os.rel / "Hello.scala" -> original
250+
)
251+
val expectedContent: String = noCrLf {
252+
"""|//> using scalafix.dep "io.github.ghostbuster91.scalafix-unified::unified:0.0.8"
253+
|
254+
|package foo
255+
|
256+
|object Hello {
257+
| val a = List.empty[Int]
258+
|}
259+
|""".stripMargin
260+
}
261+
262+
inputs.fromRoot { root =>
263+
os.proc(TestUtil.cli, "scalafix", ".", "--power", scalaVersionArgs).call(cwd = root)
264+
val updatedContent = noCrLf(os.read(root / "Hello.scala"))
265+
expect(updatedContent == expectedContent)
266+
}
267+
}
268+
269+
test {
270+
val name = "explicit-result-types"
271+
if (actualScalaVersion.startsWith("3")) name.ignore else munit.TestOptions(name)
272+
} {
273+
val original: String =
274+
"""|package foo
275+
|
276+
|object Hello {
277+
| def a(a: Int) = "asdasd" + a.toString
278+
|}
279+
|""".stripMargin
280+
val inputs: TestInputs = TestInputs(
281+
os.rel / confFileName ->
282+
s"""|rules = [
283+
| ExplicitResultTypes
284+
|]
285+
|""".stripMargin,
286+
os.rel / "Hello.scala" -> original
287+
)
288+
val expectedContent: String = noCrLf {
289+
"""|package foo
290+
|
291+
|object Hello {
292+
| def a(a: Int): String = "asdasd" + a.toString
293+
|}
294+
|""".stripMargin
295+
}
296+
297+
inputs.fromRoot { root =>
298+
os.proc(TestUtil.cli, "scalafix", ".", "--power", scalaVersionArgs).call(cwd = root)
299+
val updatedContent = noCrLf(os.read(root / "Hello.scala"))
300+
expect(updatedContent == expectedContent)
301+
}
302+
}
152303
}
Lines changed: 0 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,6 @@
11
package scala.cli.integration
22

3-
import com.eed3si9n.expecty.Expecty.expect
4-
53
class ScalafixTests213 extends ScalafixTestDefinitions with Test213 {
64
override val unusedRuleOption: String = "-Wunused"
75

8-
test("external rule") {
9-
val unnamedParamsInputsContent: String =
10-
"""//> using options -P:semanticdb:synthetics:on
11-
|//> using compileOnly.dep "com.github.jatcwang::scalafix-named-params:0.2.5"
12-
|
13-
|package foo
14-
|
15-
|object Hello {
16-
| def greetMany(name: String, times: Int) =
17-
| for {
18-
| i <- 0 to times
19-
| _ = println(s"Hello $name")
20-
| } yield ()
21-
|
22-
| def main(args: Array[String]): Unit =
23-
| greetMany("John", 42)
24-
|}
25-
|""".stripMargin
26-
val externalRuleInputs: TestInputs = TestInputs(
27-
os.rel / confFileName ->
28-
s"""|rules = [
29-
| UseNamedParameters
30-
|]
31-
|
32-
|UseNamedParameters.minParams = 2
33-
|""".stripMargin,
34-
os.rel / "Hello.scala" -> unnamedParamsInputsContent
35-
)
36-
val expectedContent: String = noCrLf {
37-
"""//> using options -P:semanticdb:synthetics:on
38-
|//> using compileOnly.dep "com.github.jatcwang::scalafix-named-params:0.2.5"
39-
|
40-
|package foo
41-
|
42-
|object Hello {
43-
| def greetMany(name: String, times: Int) =
44-
| for {
45-
| i <- 0 to times
46-
| _ = println(s"Hello $name")
47-
| } yield ()
48-
|
49-
| def main(args: Array[String]): Unit =
50-
| greetMany(name = "John", times = 42)
51-
|}
52-
|""".stripMargin
53-
}
54-
55-
externalRuleInputs.fromRoot { root =>
56-
os.proc(TestUtil.cli, "scalafix", "--power", ".", "-S", "2").call(cwd = root)
57-
val updatedContent = noCrLf(os.read(root / "Hello.scala"))
58-
expect(updatedContent == expectedContent)
59-
}
60-
}
616
}

0 commit comments

Comments
 (0)