Skip to content

Commit 9da5f2a

Browse files
committed
Add more tests and fix external rule dependencies forwarding to scalafix
1 parent bf73c8c commit 9da5f2a

File tree

2 files changed

+166
-37
lines changed

2 files changed

+166
-37
lines changed

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

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@ import caseapp.*
44
import caseapp.core.help.HelpFormat
55
import dependency.*
66
import scalafix.interfaces.ScalafixError.*
7-
import scalafix.interfaces.{Scalafix => ScalafixInterface, ScalafixError}
7+
import scalafix.interfaces.{ScalafixError, ScalafixException, ScalafixRule, Scalafix as ScalafixInterface}
88

99
import java.util.Optional
10-
1110
import scala.build.input.{Inputs, Script, SourceScalaFile}
1211
import scala.build.internal.{Constants, ExternalBinaryParams, FetchExternalBinary, Runner}
1312
import scala.build.options.{BuildOptions, Scope}
@@ -16,10 +15,12 @@ import scala.cli.CurrentParams
1615
import scala.cli.commands.compile.Compile.buildOptionsOrExit
1716
import scala.cli.commands.fmt.FmtUtil.*
1817
import scala.cli.commands.shared.{HelpCommandGroup, HelpGroup, SharedOptions}
19-
import scala.cli.commands.{ScalaCommand, SpecificationLevel, compile}
18+
import scala.cli.commands.{compile, ScalaCommand, SpecificationLevel}
2019
import scala.cli.config.Keys
2120
import scala.cli.util.ArgHelpers.*
2221
import scala.cli.util.ConfigDbUtils
22+
import scala.collection.mutable
23+
import scala.collection.mutable.Buffer
2324
import scala.jdk.CollectionConverters.*
2425
import scala.jdk.OptionConverters.*
2526

@@ -55,7 +56,7 @@ object Scalafix extends ScalaCommand[ScalafixOptions] {
5556
)
5657
val inputs = options.shared.inputs(args.all).orExit(logger)
5758
val threads = BuildThreads.create()
58-
val compilerMaker = options.shared.compilerMaker(threads).orExit(logger)
59+
val compilerMaker = options.shared.compilerMaker(threads)
5960
val configDb = ConfigDbUtils.configDb.orExit(logger)
6061
val actionableDiagnostics =
6162
options.shared.logging.verbosityOptions.actions.orElse(
@@ -86,7 +87,6 @@ object Scalafix extends ScalaCommand[ScalafixOptions] {
8687
sys.exit(1)
8788
val configFilePathOpt = options.scalafixConf.map(os.Path(_, os.pwd))
8889
val relPaths = sourcePaths.map(_.toNIO.getFileName)
89-
val toolClasspath = options.shared.dependencies.compileOnlyDependency
9090

9191
val scalafix = ScalafixInterface
9292
.fetchAndClassloadInstance(scalaBinaryVersion)
@@ -97,15 +97,20 @@ object Scalafix extends ScalaCommand[ScalafixOptions] {
9797
.withConfig(configFilePathOpt.map(_.toNIO).toJava)
9898
.withScalaVersion(scalaVersion)
9999

100-
val rulesThatWillRun = scalafix.rulesThatWillRun().asScala
101-
102100
logger.debug(
103-
s"Processing ${sourcePaths.size} Scala sources against ${rulesThatWillRun.size} rules"
101+
s"Processing ${sourcePaths.size} Scala sources"
104102
)
105103

106-
val isSemantic = rulesThatWillRun.exists(_.kind().isSemantic)
104+
val rulesThatWillRun: Either[ScalafixException, mutable.Buffer[ScalafixRule]] =
105+
try
106+
Right(scalafix.rulesThatWillRun().asScala)
107+
catch
108+
case e: ScalafixException => Left(e)
109+
val needToBuild: Boolean = rulesThatWillRun match
110+
case Right(rules) => rules.exists(_.kind().isSemantic)
111+
case Left(_) => true
107112

108-
val preparedScalafixInstance = if (isSemantic || toolClasspath.nonEmpty) {
113+
val preparedScalafixInstance = if (needToBuild) {
109114
val res = Build.build(
110115
inputs,
111116
buildOptionsWithSemanticDb,
@@ -127,10 +132,10 @@ object Scalafix extends ScalaCommand[ScalafixOptions] {
127132
val classPaths = successfulBuildOpt.map(_.fullClassPath).getOrElse(Seq.empty)
128133
val externalDeps =
129134
options.shared.dependencies.compileOnlyDependency ++ successfulBuildOpt.map(
130-
_.options.classPathOptions.extraCompileOnlyJars
131-
).getOrElse(Seq.empty).map(_.toNIO.toString)
135+
_.options.classPathOptions.extraCompileOnlyDependencies.values.flatten.map(_.value.render)
136+
).getOrElse(Seq.empty)
132137
val scalacOptions = options.shared.scalac.scalacOption ++ successfulBuildOpt.map(
133-
_.options.scalaOptions.scalacOptions.map(_.value.value).toSeq
138+
_.options.scalaOptions.scalacOptions.toSeq.map(_.value.value)
134139
).getOrElse(Seq.empty)
135140

136141
scalafix

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

Lines changed: 148 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,51 +9,175 @@ class ScalafixTests extends ScalaCliSuite {
99

1010
val emptyInputs: TestInputs = TestInputs(os.rel / ".placeholder" -> "")
1111

12-
val simpleInputsOriginalContent: String =
12+
val simpleInputsScala3OriginalContent: String =
1313
"""package foo
1414
|
15-
|final object Hello {
16-
| def main(args: Array[String]): Unit = {
15+
|final object Hello:
16+
| def main(args: Array[String]): Unit =
1717
| println("Hello")
18-
| }
19-
|}
2018
|""".stripMargin
21-
val simpleInputs: TestInputs = TestInputs(
19+
val simpleInputs3: TestInputs = TestInputs(
2220
os.rel / confFileName ->
2321
s"""|rules = [
2422
| RedundantSyntax
2523
|]
2624
|""".stripMargin,
27-
os.rel / "Hello.scala" -> simpleInputsOriginalContent
25+
os.rel / "Hello.scala" -> simpleInputsScala3OriginalContent
2826
)
29-
val expectedSimpleInputsRewrittenContent: String = noCrLf {
30-
"""package foo
31-
|
32-
|object Hello {
33-
| def main(args: Array[String]): Unit = {
34-
| println("Hello")
35-
| }
36-
|}
37-
|""".stripMargin
38-
}
3927

4028
private def noCrLf(input: String): String =
4129
input.replaceAll("\r\n", "\n")
4230

43-
test("simple") {
44-
simpleInputs.fromRoot { root =>
45-
os.proc(TestUtil.cli, "scalafix", ".").call(cwd = root)
31+
test("simple for scala2 code") {
32+
val simpleInputsScala2OriginalContent: String =
33+
"""package foo
34+
|
35+
|final object Hello {
36+
| def main(args: Array[String]): Unit = {
37+
| println("Hello")
38+
| }
39+
|}
40+
|""".stripMargin
41+
val simpleInputs2: TestInputs = TestInputs(
42+
os.rel / confFileName ->
43+
s"""|rules = [
44+
| RedundantSyntax
45+
|]
46+
|""".stripMargin,
47+
os.rel / "Hello.scala" -> simpleInputsScala2OriginalContent
48+
)
49+
val expectedContent: String = noCrLf {
50+
"""package foo
51+
|
52+
|object Hello {
53+
| def main(args: Array[String]): Unit = {
54+
| println("Hello")
55+
| }
56+
|}
57+
|""".stripMargin
58+
}
59+
60+
simpleInputs2.fromRoot { root =>
61+
os.proc(TestUtil.cli, "scalafix", ".", "-S", "2", "--power").call(cwd = root)
62+
val updatedContent = noCrLf(os.read(root / "Hello.scala"))
63+
expect(updatedContent == expectedContent)
64+
}
65+
}
66+
67+
test("simple for scala3 code") {
68+
val expectedContent: String = noCrLf {
69+
"""package foo
70+
|
71+
|object Hello:
72+
| def main(args: Array[String]): Unit =
73+
| println("Hello")
74+
|""".stripMargin
75+
}
76+
77+
simpleInputs3.fromRoot { root =>
78+
os.proc(TestUtil.cli, "scalafix", ".", "-S", "3", "--power").call(cwd = root)
4679
val updatedContent = noCrLf(os.read(root / "Hello.scala"))
47-
expect(updatedContent == expectedSimpleInputsRewrittenContent)
80+
expect(updatedContent == expectedContent)
4881
}
4982
}
5083

5184
test("with --check") {
52-
simpleInputs.fromRoot { root =>
53-
val res = os.proc(TestUtil.cli, "scalafix", "--check", ".").call(cwd = root, check = false)
85+
simpleInputs3.fromRoot { root =>
86+
val res = os.proc(TestUtil.cli, "scalafix", "--power", "--check", ".").call(cwd = root, check = false)
5487
expect(res.exitCode == 1)
5588
val updatedContent = noCrLf(os.read(root / "Hello.scala"))
56-
expect(updatedContent == noCrLf(simpleInputsOriginalContent))
89+
expect(updatedContent == noCrLf(simpleInputsScala3OriginalContent))
90+
}
91+
}
92+
93+
test("semantic rule") {
94+
val unusedValueInputsContent: String =
95+
"""//> using options -Wunused:all
96+
|package foo
97+
|
98+
|object Hello:
99+
| def main(args: Array[String]): Unit =
100+
| val name = "John"
101+
| println("Hello")
102+
|""".stripMargin
103+
val semanticRuleInputs: TestInputs = TestInputs(
104+
os.rel / confFileName ->
105+
s"""|rules = [
106+
| RemoveUnused
107+
|]
108+
|""".stripMargin,
109+
os.rel / "Hello.scala" -> unusedValueInputsContent
110+
)
111+
val expectedContent: String = noCrLf {
112+
"""//> using options -Wunused:all
113+
|package foo
114+
|
115+
|object Hello:
116+
| def main(args: Array[String]): Unit =
117+
|
118+
| println("Hello")
119+
|""".stripMargin
120+
}
121+
122+
semanticRuleInputs.fromRoot { root =>
123+
os.proc(TestUtil.cli, "scalafix", "--power", ".").call(cwd = root)
124+
val updatedContent = noCrLf(os.read(root / "Hello.scala"))
125+
expect(updatedContent == expectedContent)
126+
}
127+
}
128+
test("external rule") {
129+
val unnamedParamsInputsContent: String =
130+
"""//> using options -P:semanticdb:synthetics:on
131+
|//> using compileOnly.dep "com.github.jatcwang::scalafix-named-params:0.2.4"
132+
|
133+
|package foo
134+
|
135+
|object Hello {
136+
| def greetMany(name: String, times: Int) =
137+
| for {
138+
| i <- 0 to times
139+
| _ = println(s"Hello $name")
140+
| } yield ()
141+
|
142+
| def main(args: Array[String]): Unit =
143+
| greetMany("John", 42)
144+
|}
145+
|""".stripMargin
146+
val externalRuleInputs: TestInputs = TestInputs(
147+
os.rel / confFileName ->
148+
s"""|rules = [
149+
| UseNamedParameters
150+
|]
151+
|
152+
|UseNamedParameters.minParams = 2
153+
|""".stripMargin,
154+
os.rel / "Hello.scala" -> unnamedParamsInputsContent
155+
)
156+
val expectedContent: String = noCrLf {
157+
"""//> using options -P:semanticdb:synthetics:on
158+
|//> using compileOnly.dep "com.github.jatcwang::scalafix-named-params:0.2.4"
159+
|
160+
|package foo
161+
|
162+
|object Hello {
163+
| def greetMany(name: String, times: Int) =
164+
| for {
165+
| i <- 0 to times
166+
| _ = println(s"Hello $name")
167+
| } yield ()
168+
|
169+
| def main(args: Array[String]): Unit =
170+
| greetMany(name = "John", times = 42)
171+
|}
172+
|""".stripMargin
173+
}
174+
175+
externalRuleInputs.fromRoot { root =>
176+
os.proc(TestUtil.cli, "scalafix", "--power", ".", "-S", "2").call(cwd = root)
177+
val updatedContent = noCrLf(os.read(root / "Hello.scala"))
178+
println(updatedContent)
179+
println(expectedContent)
180+
expect(updatedContent == expectedContent)
57181
}
58182
}
59183
}

0 commit comments

Comments
 (0)