Skip to content

Commit

Permalink
Add support for Scalafix (pantsbuild#20394)
Browse files Browse the repository at this point in the history
Implemented for `fix` and `lint` goals.
  • Loading branch information
alonsodomin authored Feb 23, 2024
1 parent 548ef90 commit e4532cb
Show file tree
Hide file tree
Showing 27 changed files with 5,179 additions and 11 deletions.
10 changes: 10 additions & 0 deletions .scalafix.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
rules = [
NoAutoTupling,
DisableSyntax,
NoValInForComprehension,
RedundantSyntax,
RemoveUnused,
ProcedureSyntax
]

DisableSyntax.noThrows = true
15 changes: 13 additions & 2 deletions 3rdparty/jvm/org/scalameta/BUILD
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

scala_artifact(
name="semanticdb-jar",
group="org.scalameta",
artifact="semanticdb-scalac",
version="4.8.4",
resolve="jvm_testprojects",
crossversion="full",
)

scalac_plugin(name="semanticdb", artifact=":semanticdb-jar")

# Note: This target must be kept in sync with the requirements in `generate_scala_parser_lockfile_request`.
jvm_artifact(
scala_artifact(
name="scalameta",
group="org.scalameta",
artifact="scalameta_2.13",
artifact="scalameta",
version="4.8.7",
packages=["scala.meta.**"],
resolve="scala_parser_dev",
Expand Down
28 changes: 26 additions & 2 deletions 3rdparty/jvm/testprojects.lockfile
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# This lockfile was autogenerated by Pants. To regenerate, run:
#
# ./pants generate-lockfiles
# pants generate-lockfiles
#
# --- BEGIN PANTS LOCKFILE METADATA: DO NOT EDIT OR REMOVE ---
# {
# "version": 1,
# "generated_with_requirements": [
# "org.scala-lang:scala-library:2.13.6,url=not_provided,jar=not_provided"
# "org.scala-lang:scala-library:2.13.6,url=not_provided,jar=not_provided",
# "org.scalameta:semanticdb-scalac_2.13.6:4.8.4,url=not_provided,jar=not_provided"
# ]
# }
# --- END PANTS LOCKFILE METADATA ---
Expand All @@ -24,3 +25,26 @@ packaging = "jar"
[entries.file_digest]
fingerprint = "f19ed732e150d3537794fd3fe42ee18470a3f707efd499ecd05a99e727ff6c8a"
serialized_bytes_length = 5955737
[[entries]]
file_name = "org.scalameta_semanticdb-scalac_2.13.6_4.8.4.jar"
[[entries.directDependencies]]
group = "org.scala-lang"
artifact = "scala-library"
version = "2.13.6"
packaging = "jar"

[[entries.dependencies]]
group = "org.scala-lang"
artifact = "scala-library"
version = "2.13.6"
packaging = "jar"


[entries.coord]
group = "org.scalameta"
artifact = "semanticdb-scalac_2.13.6"
version = "4.8.4"
packaging = "jar"
[entries.file_digest]
fingerprint = "acc1f667d4e98b42ba851309feb391cf301afeeeeddb8c172874a2184b18b057"
serialized_bytes_length = 17073222
37 changes: 37 additions & 0 deletions docs/docs/jvm/java-and-scala.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,43 @@ Once enabled, `lint` and `fmt` will check and automatically reformat your code:
❯ pants --changed-since=HEAD fmt
```

### Fix Scala code

Additionally to the previously mentioned tools, `scalafix` can also be enabled by adding the `pants.backend.experimental.scala.lint.scalafix` backend to `backend_packages` in the `[GLOBAL]` section of `pants.toml`. However to take full advantage of it additional settings are required.

If we want to use Scalafix's semantic rules, Scalafix needs to be able to find `.semanticdb` compiled files in our classpath. In versions prior to Scala 3 this is achieved by adding the `semanticdb` scalac plugin to our build. Find which is the right version of it for the Scala version you are using and add the following targets:

```python
scala_artifact(
name="semanticdb-jar",
group="org.scalameta",
artifact="semanticdb-scalac",
version="<SEMANTICDB_VERSION>",
crossversion="full",
)

scalac_plugin(name="semanticdb", artifact=":semanticdb-jar")
```

Now you will need to add the `scalac_plugins` field to your scala targets like in the following:

```python
scala_sources(scalac_plugins=["semanticdb"])
```

Alternatively, you could add `semanticdb` to the `[scalac].plugins_for_resolve` setting:

```toml pants.toml
[scalac.plugins_for_resolve]
jvm-default = "semanticdb"
```

:::note Scalafix and Scala 3
At the moment the support for Scalac 3 in Scalafix is limited, most of the syntactic rules work but not that many in the semantic front.

Despite those raugh edges, Scalafix is a great linting tool for Scala 3, just note that the setup is different than from prior versions: Instead of adding a scalac plugin to our build, we only need to add the `-Xsemanticdb` flag to our `[scalac].args` settings to enable the generation of `.semanticdb` compiled files.
:::

## Working in an IDE

Pants supports loading Java and Scala projects in IntelliJ via the [BSP protocol](https://build-server-protocol.github.io/) (which should ease VSCode support [via Metals](https://scalameta.org/metals/docs/editors/vscode), although it is not yet supported).
Expand Down
4 changes: 4 additions & 0 deletions pants.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ backend_packages.add = [
"pants.backend.experimental.python.packaging.pyoxidizer",
"pants.backend.experimental.scala",
"pants.backend.experimental.scala.lint.scalafmt",
"pants.backend.experimental.scala.lint.scalafix",
"pants.backend.experimental.scala.debug_goals",
"pants.backend.experimental.tools.workunit_logger",
"pants.backend.experimental.visibility",
Expand Down Expand Up @@ -234,5 +235,8 @@ jar_tool_dev = "src/python/pants/jvm/jar_tool/jar_tool.lock"
[scala]
version_for_resolve = { "scala_parser_dev" = "2.13.8" }

[scalac]
args = ["-Yrangepos", "-Xlint:unused"]

[scala-infer]
force_add_siblings_as_dependencies = false
4 changes: 4 additions & 0 deletions src/python/pants/backend/experimental/scala/lint/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright 2024 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

python_sources()
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright 2023 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

python_sources()
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright 2024 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

from pants.backend.experimental.scala.register import rules as all_scala_rules
from pants.backend.scala.lint.scalafix import extra_fields
from pants.backend.scala.lint.scalafix import rules as scalafix_rules


def rules():
return [
*all_scala_rules(),
*scalafix_rules.rules(),
*extra_fields.rules(),
]
3 changes: 2 additions & 1 deletion src/python/pants/backend/scala/dependency_inference/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ python_tests(name="tests", timeout=240)
scala_sources(
name="scala_parser",
resolve="scala_parser_dev",
# TODO: Allow the parser files to be formatted.
# TODO: Allow the parser files to be formatted and linted.
skip_scalafmt=True,
skip_scalafix=True,
)

jvm_artifact(
Expand Down
10 changes: 10 additions & 0 deletions src/python/pants/backend/scala/lint/scalafix/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright 2023 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

python_sources(dependencies=[":lockfile"])

python_tests(name="tests", dependencies=[":test-lockfiles"])

resource(name="lockfile", source="scalafix.default.lockfile.txt")

resources(name="test-lockfiles", sources=["*.test.lock"])
Empty file.
15 changes: 15 additions & 0 deletions src/python/pants/backend/scala/lint/scalafix/extra_fields.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright 2023 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

from pants.backend.scala.target_types import SCALA_SOURCES_TARGET_TYPES
from pants.engine.target import BoolField


class SkipScalafixField(BoolField):
alias = "skip_scalafix"
default = False
help = "If true, don't run `scalafix` on this target's code."


def rules():
return [tgt.register_plugin_field(SkipScalafixField) for tgt in SCALA_SOURCES_TARGET_TYPES]
Loading

0 comments on commit e4532cb

Please sign in to comment.