Skip to content

Commit

Permalink
Add more tests and fix external rule dependencies forwarding to scalafix
Browse files Browse the repository at this point in the history
  • Loading branch information
Vigorge committed Aug 12, 2024
1 parent bf73c8c commit 9da5f2a
Show file tree
Hide file tree
Showing 2 changed files with 166 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ import caseapp.*
import caseapp.core.help.HelpFormat
import dependency.*
import scalafix.interfaces.ScalafixError.*
import scalafix.interfaces.{Scalafix => ScalafixInterface, ScalafixError}
import scalafix.interfaces.{ScalafixError, ScalafixException, ScalafixRule, Scalafix as ScalafixInterface}

import java.util.Optional

import scala.build.input.{Inputs, Script, SourceScalaFile}
import scala.build.internal.{Constants, ExternalBinaryParams, FetchExternalBinary, Runner}
import scala.build.options.{BuildOptions, Scope}
Expand All @@ -16,10 +15,12 @@ import scala.cli.CurrentParams
import scala.cli.commands.compile.Compile.buildOptionsOrExit
import scala.cli.commands.fmt.FmtUtil.*
import scala.cli.commands.shared.{HelpCommandGroup, HelpGroup, SharedOptions}
import scala.cli.commands.{ScalaCommand, SpecificationLevel, compile}
import scala.cli.commands.{compile, ScalaCommand, SpecificationLevel}
import scala.cli.config.Keys
import scala.cli.util.ArgHelpers.*
import scala.cli.util.ConfigDbUtils
import scala.collection.mutable
import scala.collection.mutable.Buffer
import scala.jdk.CollectionConverters.*
import scala.jdk.OptionConverters.*

Expand Down Expand Up @@ -55,7 +56,7 @@ object Scalafix extends ScalaCommand[ScalafixOptions] {
)
val inputs = options.shared.inputs(args.all).orExit(logger)
val threads = BuildThreads.create()
val compilerMaker = options.shared.compilerMaker(threads).orExit(logger)
val compilerMaker = options.shared.compilerMaker(threads)
val configDb = ConfigDbUtils.configDb.orExit(logger)
val actionableDiagnostics =
options.shared.logging.verbosityOptions.actions.orElse(
Expand Down Expand Up @@ -86,7 +87,6 @@ object Scalafix extends ScalaCommand[ScalafixOptions] {
sys.exit(1)
val configFilePathOpt = options.scalafixConf.map(os.Path(_, os.pwd))
val relPaths = sourcePaths.map(_.toNIO.getFileName)
val toolClasspath = options.shared.dependencies.compileOnlyDependency

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

val rulesThatWillRun = scalafix.rulesThatWillRun().asScala

logger.debug(
s"Processing ${sourcePaths.size} Scala sources against ${rulesThatWillRun.size} rules"
s"Processing ${sourcePaths.size} Scala sources"
)

val isSemantic = rulesThatWillRun.exists(_.kind().isSemantic)
val rulesThatWillRun: Either[ScalafixException, mutable.Buffer[ScalafixRule]] =
try
Right(scalafix.rulesThatWillRun().asScala)
catch
case e: ScalafixException => Left(e)
val needToBuild: Boolean = rulesThatWillRun match
case Right(rules) => rules.exists(_.kind().isSemantic)
case Left(_) => true

val preparedScalafixInstance = if (isSemantic || toolClasspath.nonEmpty) {
val preparedScalafixInstance = if (needToBuild) {
val res = Build.build(
inputs,
buildOptionsWithSemanticDb,
Expand All @@ -127,10 +132,10 @@ object Scalafix extends ScalaCommand[ScalafixOptions] {
val classPaths = successfulBuildOpt.map(_.fullClassPath).getOrElse(Seq.empty)
val externalDeps =
options.shared.dependencies.compileOnlyDependency ++ successfulBuildOpt.map(
_.options.classPathOptions.extraCompileOnlyJars
).getOrElse(Seq.empty).map(_.toNIO.toString)
_.options.classPathOptions.extraCompileOnlyDependencies.values.flatten.map(_.value.render)
).getOrElse(Seq.empty)
val scalacOptions = options.shared.scalac.scalacOption ++ successfulBuildOpt.map(
_.options.scalaOptions.scalacOptions.map(_.value.value).toSeq
_.options.scalaOptions.scalacOptions.toSeq.map(_.value.value)
).getOrElse(Seq.empty)

scalafix
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,51 +9,175 @@ class ScalafixTests extends ScalaCliSuite {

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

val simpleInputsOriginalContent: String =
val simpleInputsScala3OriginalContent: String =
"""package foo
|
|final object Hello {
| def main(args: Array[String]): Unit = {
|final object Hello:
| def main(args: Array[String]): Unit =
| println("Hello")
| }
|}
|""".stripMargin
val simpleInputs: TestInputs = TestInputs(
val simpleInputs3: TestInputs = TestInputs(
os.rel / confFileName ->
s"""|rules = [
| RedundantSyntax
|]
|""".stripMargin,
os.rel / "Hello.scala" -> simpleInputsOriginalContent
os.rel / "Hello.scala" -> simpleInputsScala3OriginalContent
)
val expectedSimpleInputsRewrittenContent: String = noCrLf {
"""package foo
|
|object Hello {
| def main(args: Array[String]): Unit = {
| println("Hello")
| }
|}
|""".stripMargin
}

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

test("simple") {
simpleInputs.fromRoot { root =>
os.proc(TestUtil.cli, "scalafix", ".").call(cwd = root)
test("simple for scala2 code") {
val simpleInputsScala2OriginalContent: String =
"""package foo
|
|final object Hello {
| def main(args: Array[String]): Unit = {
| println("Hello")
| }
|}
|""".stripMargin
val simpleInputs2: TestInputs = TestInputs(
os.rel / confFileName ->
s"""|rules = [
| RedundantSyntax
|]
|""".stripMargin,
os.rel / "Hello.scala" -> simpleInputsScala2OriginalContent
)
val expectedContent: String = noCrLf {
"""package foo
|
|object Hello {
| def main(args: Array[String]): Unit = {
| println("Hello")
| }
|}
|""".stripMargin
}

simpleInputs2.fromRoot { root =>
os.proc(TestUtil.cli, "scalafix", ".", "-S", "2", "--power").call(cwd = root)
val updatedContent = noCrLf(os.read(root / "Hello.scala"))
expect(updatedContent == expectedContent)
}
}

test("simple for scala3 code") {
val expectedContent: String = noCrLf {
"""package foo
|
|object Hello:
| def main(args: Array[String]): Unit =
| println("Hello")
|""".stripMargin
}

simpleInputs3.fromRoot { root =>
os.proc(TestUtil.cli, "scalafix", ".", "-S", "3", "--power").call(cwd = root)
val updatedContent = noCrLf(os.read(root / "Hello.scala"))
expect(updatedContent == expectedSimpleInputsRewrittenContent)
expect(updatedContent == expectedContent)
}
}

test("with --check") {
simpleInputs.fromRoot { root =>
val res = os.proc(TestUtil.cli, "scalafix", "--check", ".").call(cwd = root, check = false)
simpleInputs3.fromRoot { root =>
val res = os.proc(TestUtil.cli, "scalafix", "--power", "--check", ".").call(cwd = root, check = false)
expect(res.exitCode == 1)
val updatedContent = noCrLf(os.read(root / "Hello.scala"))
expect(updatedContent == noCrLf(simpleInputsOriginalContent))
expect(updatedContent == noCrLf(simpleInputsScala3OriginalContent))
}
}

test("semantic rule") {
val unusedValueInputsContent: String =
"""//> using options -Wunused:all
|package foo
|
|object Hello:
| def main(args: Array[String]): Unit =
| val name = "John"
| println("Hello")
|""".stripMargin
val semanticRuleInputs: TestInputs = TestInputs(
os.rel / confFileName ->
s"""|rules = [
| RemoveUnused
|]
|""".stripMargin,
os.rel / "Hello.scala" -> unusedValueInputsContent
)
val expectedContent: String = noCrLf {
"""//> using options -Wunused:all
|package foo
|
|object Hello:
| def main(args: Array[String]): Unit =
|
| println("Hello")
|""".stripMargin
}

semanticRuleInputs.fromRoot { root =>
os.proc(TestUtil.cli, "scalafix", "--power", ".").call(cwd = root)
val updatedContent = noCrLf(os.read(root / "Hello.scala"))
expect(updatedContent == expectedContent)
}
}
test("external rule") {
val unnamedParamsInputsContent: String =
"""//> using options -P:semanticdb:synthetics:on
|//> using compileOnly.dep "com.github.jatcwang::scalafix-named-params:0.2.4"
|
|package foo
|
|object Hello {
| def greetMany(name: String, times: Int) =
| for {
| i <- 0 to times
| _ = println(s"Hello $name")
| } yield ()
|
| def main(args: Array[String]): Unit =
| greetMany("John", 42)
|}
|""".stripMargin
val externalRuleInputs: TestInputs = TestInputs(
os.rel / confFileName ->
s"""|rules = [
| UseNamedParameters
|]
|
|UseNamedParameters.minParams = 2
|""".stripMargin,
os.rel / "Hello.scala" -> unnamedParamsInputsContent
)
val expectedContent: String = noCrLf {
"""//> using options -P:semanticdb:synthetics:on
|//> using compileOnly.dep "com.github.jatcwang::scalafix-named-params:0.2.4"
|
|package foo
|
|object Hello {
| def greetMany(name: String, times: Int) =
| for {
| i <- 0 to times
| _ = println(s"Hello $name")
| } yield ()
|
| def main(args: Array[String]): Unit =
| greetMany(name = "John", times = 42)
|}
|""".stripMargin
}

externalRuleInputs.fromRoot { root =>
os.proc(TestUtil.cli, "scalafix", "--power", ".", "-S", "2").call(cwd = root)
val updatedContent = noCrLf(os.read(root / "Hello.scala"))
println(updatedContent)
println(expectedContent)
expect(updatedContent == expectedContent)
}
}
}

0 comments on commit 9da5f2a

Please sign in to comment.