Installation
Support
Scalafix is tested to work with:
- macOS, Linux or Windows
- Java LTS (8, 11, 17 or 21)
- Scala 2.12.20, 2.13.15, 3.3.4 LTS or 3.5.1
Note that other setups may work, but could result in unexpected behavior.
sbt
Start by installing the sbt 1.4+ plugin in project/plugins.sbt
// project/plugins.sbt
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.13.0")
Scalafix is no longer published for Scala 2.11. You can run the final version of Scalafix supporting 2.11, but all features documented below might not be supported.
// project/plugins.sbt addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.10.4") // final Scala 2.11 version
sbt-scalafix is no longer published for sbt 0.13.x. You should be able to run the latest version of Scalafix with the final sbt-scalafix version published for sbt 0.13.x, but all features documented below might not be supported.
// project/plugins.sbt addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.29") // final sbt 0.13.x version dependencyOverrides += "ch.epfl.scala" % "scalafix-interfaces" % "0.13.0"
From the sbt shell, let's run the rule ProcedureSyntax
> myproject / scalafix ProcedureSyntax
It's normal that the first invocation of scalafix
takes a while to download
Scalafix artifacts from Maven Central.
If all went well and your project uses the deprecated "procedure syntax", you should have a diff in your sources like this
- def myProcedure {
+ def myProcedure: Unit = {
Next, if we run another rule like RemoveUnused
then we get an error
> myproject / scalafix RemoveUnused
[error] (Compile / scalafix) scalafix.sbt.InvalidArgument: 2 errors
[E1] The scalac compiler should produce semanticdb files to run semantic
rules like RemoveUnused ...
[E2] A Scala compiler option is required to use RemoveUnused. To fix this
problem, update your build to add -Ywarn-unused-import (with 2.12) ...
The first error message means the
SemanticDB compiler plugin
is not enabled for this project. The second error says RemoveUnused
requires
the Scala compiler option -Ywarn-unused-import
(or -Wunused:imports
in
2.13.x or 3.x). To fix both problems, add the following settings to build.sbt
/*
* build.sbt
* SemanticDB is enabled for all sub-projects via ThisBuild scope.
* https://www.scala-sbt.org/1.x/docs/sbt-1.3-Release-Notes.html#SemanticDB+support
*/
inThisBuild(
List(
scalaVersion := "2.12.20", // 2.13.15, 3.3.4 or 3.5.1
+ semanticdbEnabled := true, // enable SemanticDB
+ semanticdbVersion := scalafixSemanticdb.revision // only required for Scala 2.x
)
)
lazy val myproject = project.settings(
+ scalacOptions += {
+ if (scalaVersion.value.startsWith("2.12"))
+ "-Ywarn-unused-import"
+ else
+ "-Wunused:imports"
+ }
)
/*
* build.sbt
* SemanticDB is enabled only for a sub-project.
* https://www.scala-sbt.org/1.x/docs/sbt-1.3-Release-Notes.html#SemanticDB+support
*/
lazy val myproject = project.settings(
scalaVersion := "2.12.20", // 2.13.15, 3.3.4 or 3.5.1
+ semanticdbEnabled := true, // enable SemanticDB
+ semanticdbVersion := scalafixSemanticdb.revision, // only required for Scala 2.x
+ scalacOptions += {
+ if (scalaVersion.value.startsWith("2.12"))
+ "-Ywarn-unused-import"
+ else
+ "-Wunused:imports"
+ }
)
For project/*.scala
files, add
import scalafix.sbt.ScalafixPlugin.autoImport._
to the top of the file to
resolve scalafixSemanticdb
.
We run RemoveUnused
again and the error is now gone
> myproject / scalafix RemoveUnused
[info] Compiling 15 Scala sources to ...
[info] Running scalafix on 15 Scala sources
If your project has unused imports, you should see a diff like this
- import scala.util.{ Success, Failure }
+ import scala.util.Success
See example project for a repository that demonstrates
ProcedureSyntax
and RemoveUnused
.
Great! You are all set to use Scalafix with sbt :)
Beware that the SemanticDB compiler plugin in combination with
-Yrangepos
adds overhead to compilation time. The exact compilation overhead depends on the codebase being compiled and compiler options used. It's recommended to provide generous JVM memory and stack settings in the file.jvmopts
:-Xss8m -Xms1G -Xmx8G
You can also use project scoped settings if you don't want to mix SemanticDB settings with your sub-projects which don't use it, rather than using ThisBuild scoped settings.
Settings and tasks
Name | Type | Description |
---|---|---|
scalafix <args> | InputKey[Unit] | Invoke scalafix command line interface directly. Use tab completion to explore supported arguments or consult --help. |
scalafixAll <args> | InputKey[Unit] | Invoke scalafix across all configurations where scalafix is enabled. |
scalafixCaching | SettingKey[Boolean] | Controls whether 2 successive invocations of the scalafix InputTask with the same arguments & configuration should be incremental. Defaults to true . When enabled, use the --no-cache argument to force an exhaustive run. |
scalafixConfig | SettingKey[Option[File]] | .scalafix.conf file to specify which scalafix rules should run, together with their potential options. Defaults to .scalafix.conf in the root directory, if it exists. |
scalafixDependencies | SettingKey[Seq[ModuleID]] | Dependencies making custom rules available via their simple name. Can be set in ThisBuild or at project-level. Defaults to Nil . |
scalafixOnCompile | SettingKey[Boolean] | When true , Scalafix rule(s) declared in scalafixConfig are run on compilation, applying rewrites and failing on lint errors. Defaults to false . |
scalafixResolvers | SettingKey[Seq[Repository]] | Custom resolvers where scalafixDependencies are resolved from, in addition to the user-defined sbt ThisBuild / resolvers . Must be set in ThisBuild . Defaults to: Ivy2 local, Maven Central, Sonatype releases & Sonatype snapshots. |
Main and test sources
The task myproject / scalafix
runs for main sources in the project
myproject
. To run Scalafix on test sources, execute
myproject / Test / scalafix
instead. To run on both main and test sources, execute
myproject / scalafixAll
.
Integration tests
By default, the scalafix
command is enabled for the Compile
and Test
configurations, and scalafixAll
will run on both of them. To enable
Scalafix for other configuration like IntegrationTest
, add the following
to your project settings
lazy val myproject = project
.configs(IntegrationTest)
.settings(
Defaults.itSettings,
+ scalafixConfigSettings(IntegrationTest)
// ...
)
Multi-module builds
Both the scalafix
& the scalafixAll
task aggregate like the compile
and test
tasks. To run Scalafix on all projects for both main and test
sources you can execute scalafixAll
.
Enforce in CI
To automatically enforce that Scalafix has been run on all sources, use
scalafix --check
instead of scalafix
. This task fails the build if running
scalafix
would produce a diff or a linter error message is reported.
Use scalafixAll --check
to enforce Scalafix on your entire project.
Cache in CI
To avoid binary compatibility conflicts with the sbt classpath
(example issue),
the Scalafix plugin uses Coursier to
fetch Scalafix artifacts from Maven Central. These artifacts are by default
cached inside the home directory.
To avoid redundant downloads on every pull request, it's recommended to configure
your CI enviroment to cache this directory. The location can be customized with
the environment variable COURSIER_CACHE
export COURSIER_CACHE=$HOME/.custom-cache
Run Scalafix automatically on compile
If you set scalafixOnCompile
to true
, the rules declared in .scalafix.conf
(or in the file located by scalafixConfig
) will run automatically each time
compile
is invoked either explicitly or implicitly (for example when
executing test
or publishLocal
). Lint errors will fail that invocation,
while rewrites will be applied.
Although this looks like an easy way to use Scalafix as a linter, use this feature with care as it has several shortcomings, for example:
scalafixOnCompile := true
is dangerous on CI as a rewrite might be applied before a call toscalafix[All] --check
, causing this one to run on dirty sources and thus pass while it should not. Make sure thatscalafixOnCompile
is disabled on CI or, if that is impossible, thatscalafix[All] --check
is the first task executed, without any other concurrently.- Some rules such as
RemoveUnused
can be counter-productive if applied too often/early, as the work-in-progress code that was just added might disappear after a simpletest
. To make such invocations less intrusive, you can change the rules and rules configuration used in that case by defining in.scalafix.conf
custom values for them. - If you run many semantic rules by default, the last one(s) to run might see
stale information and fail the invocation, which needs to be re-run manually.
This is not specific to
scalafixOnCompile
, but the problem becomes much more visible with it. - To keep the overhead minimal,
scalafixCaching
is automatically enabled whenscalafixOnCompile
is, which can cause unexpected behaviors if you run into false positive cache hits.scalafixCaching
can explicitly be set tofalse
in that case. - Non-idempotent rewrite rules might get you in an infinite loop where sources
never converge - not specific to
scalafixOnCompile
either, but rather confusing when triggered automatically. - Bugs in rule implementations can prevent you from getting a successful
compile
, blocking testing or publishing for example
Run custom rules
It's possible to run custom Scalafix rules that have been published to Maven
Central. To install a custom rule, add it to scalafixDependencies
(scalafix.sbt.ScalafixPlugin.autoImport.scalafixDependencies
):
// at the top of build.sbt
ThisBuild / scalafixDependencies +=
"ch.epfl.scala" %% "example-scalafix-rule" % "4.0.0"
Start sbt and type scalafix <TAB>
, once the example-scalafix-rule
dependency
has been downloaded the rules SemanticRule
and SyntacticRule
should appear
as tab completion suggestions.
$ sbt
> scalafix Syn<TAB>
> scalafix SyntacticRule
If all went well, you should see a diff adding the comment
// v1 SyntacticRule!
to all Scala source files.
+// v1 SyntacticRule!
Exclude files from SemanticDB (Scala 2.x only)
By default, the SemanticDB compiler plugin will process all files in a project.
Use -P:semanticdb:exclude:<regex>
to exclude files from the SemanticDB
compiler plugin.
scalacOptions += "-P:semanticdb:exclude:Macros.scala"
Separate multiple patterns with pipe |
to exclude multiple files.
scalacOptions += "-P:semanticdb:exclude:Macros.scala|Schema.scala"
To learn more about SemanticDB compiler options visit https://scalameta.org/docs/semanticdb/guide.html#scalac-compiler-plugin
Avoid using slashes like
/
in-P:semanticdb:exclude
since that will not work on Windows. The argument is compiled to a regular expression and gets matched against thejava.io.File.getAbsolutePath
representation of each file.
Exclude files from Scalafix
By default, the scalafix
task processes all files in a project. If you use
Scala 2.x, the scalafix
task also respects
-P:semanticdb:exclude
.
Use Compile / scalafix / unmanagedSources
to optionally exclude files from
the scalafix
task.
Compile / scalafix / unmanagedSources :=
(Compile / unmanagedSources).value
.filterNot(file => file.getName == "Macros.scala")
Replace Compile
with Test
to customize which test sources should be
processed.
Customize SemanticDB output directory
The *.semanticdb
files are available in the directory referenced by the
semanticdbTargetRoot
key, which defaults to target/scala-x/meta
.
You can override this default to emit *.semanticdb
files in a custom
location. For example:
semanticdbTargetRoot := target.value / "semanticdb"
Alternatively, you can set the semanticdbIncludeInJar
key to request
the compiler to emit these files into the classDirectory
so that they
are available in packaged JARs.
semanticdbIncludeInJar := true
Disable Scalafix for specific project
Use .disablePlugins(ScalafixPlugin)
to disable Scalafix for a particular
project.
lazy val myproject = project
.settings(...)
+ .disablePlugins(ScalafixPlugin)
When using Scala.js or Scala Native, use .jsConfigure
or .nativeConfigure
to
disable Scalafix for only the Scala.js or Scala Native project. For example:
lazy val myproject = crossProject(JVMPlatform, JSPlatform)
.settings(...)
+ .jsConfigure(_.disablePlugins(ScalafixPlugin))
Enable SemanticDB for current shell session
Instead of permanently enabling SemanticDB in build.sbt, use the
scalafixEnable
command to enable SemanticDB the current active sbt shell
session.
> scalafixEnable
...
> scalafix RemoveUnused
The scalafixEnable
command automatically enables semanticdb output and adds
scalacOptions += "-Yrangepos"
for all eligible projects in the builds. The
change in Scala compiler options means the project may need to be re-built on
the next compile
.
The
scalafixEnable
command must be re-executed after everyreload
and when sbt shell is exited.
Example project
For a minimal example project using sbt-scalafix, see the scalacenter/sbt-scalafix-example repository.
git clone https://github.com/scalacenter/sbt-scalafix-example
cd sbt-scalafix-example
sbt "scalafix RemoveUnused"
git diff // should produce a diff
Command line
First, install the Coursier command-line interface.
Then, download the Scalafix runner built with the latest version of Scala (3.5.1)
cs install scalafix
./scalafix --version # Should say 0.13.0
If you plan to use advanced semantic rules like
ExplicitResultTypes
, you must use the version of Scalafix built with a Scala version matching your target source files.If your target files are not built with the latest minor version of Scala, use one of the following commands to create a custom runner
cs bootstrap ch.epfl.scala:scalafix-cli_2.12.20:0.13.0 --main scalafix.cli.Cli -o scalafix_2.12 cs bootstrap ch.epfl.scala:scalafix-cli_2.13.15:0.13.0 --main scalafix.cli.Cli -o scalafix_2.13 cs bootstrap ch.epfl.scala:scalafix-cli_3.3.4:0.13.0 --main scalafix.cli.Cli -o scalafix_3-lts
Help
Scalafix 0.13.0+57-2efb89dd-SNAPSHOT
Usage: scalafix [options] [<path> ...]
Scalafix is a refactoring and linting tool. Scalafix
supports both syntactic and semantic linter and rewrite
rules. Syntactic rules can run on source code without
compilation. Semantic rules can run on source code that has
been compiled with the SemanticDB compiler plugin.
Common options:
--rules | -r [String ...] (default: [])
Scalafix rules to run, for example ExplicitResultTypes. The
syntax for rules is documented in
https://scalacenter.github.io/scalafix/docs/users/configuration#rules
--files | -f [<path> ...] (default: [])
Files or directories (recursively visited) to fix.
--config <path> (default: null)
File path to a .scalafix.conf configuration file. Defaults
to .scalafix.conf in the current working directory, if
any.
--check
Check that all files have been fixed with scalafix, exiting
with non-zero code on violations. Won't write to files.
--stdout
Print fixed output to stdout instead of writing in-place.
--diff
If set, only apply scalafix to added and edited files in git
diff against the master branch.
--diff-base String (default: null)
If set, only apply scalafix to added and edited files in git
diff against a provided branch, commit or tag.
--syntactic
Run only syntactic rules, ignore semantic rules even if they
are explicitly configured in .scalafix.conf or via
--rules
--triggered
Overlay the default rules & rule settings in .scalafix.conf
with the `triggered` section
--verbose
Print out additional diagnostics while running scalafix.
--help | -h
Print out this help message and exit
--version | -v
Print out version number and exit
Semantic options:
--classpath Classpath (default: "<classpath>")
Full classpath of the files to fix, required for semantic
rules. The source files that should be fixed must be
compiled with semanticdb-scalac. Dependencies are
required by rules like ExplicitResultTypes, but the
dependencies do not need to be compiled with
semanticdb-scalac.
--sourceroot <path> (default: null)
Absolute path passed to semanticdb with
-P:semanticdb:sourceroot:<path>. Relative filenames
persisted in the Semantic DB are absolutized by the
sourceroot. Defaults to current working directory if not
provided.
--semanticdb-targetroots [<path> ...] (default: [])
Absolute paths passed to semanticdb with
-P:semanticdb:targetroot:<path>. Used to locate
semanticdb files. By default, Scalafix will try to
locate semanticdb files in the classpath
--auto-classpath
If set, automatically infer the --classpath flag by scanning
for directories with META-INF/semanticdb
--auto-classpath-roots [<path> ...] (default: [])
Additional directories to scan for --auto-classpath
--scalac-options [String ...] (default: [])
The scala compiler options used to compile this --classpath,
for example -Ywarn-unused-import
--scala-version ScalaVersion (default: "2.13.15")
The major or binary Scala version that the provided files
are targeting, or the full version that was used to
compile them when a classpath is provided.
Tab completions:
--bash
Print out bash tab completions. To install:
```
# macOS, requires "brew install bash-completion"
scalafix --bash > /usr/local/etc/bash_completion.d/scalafix
# Linux
scalafix --bash > /etc/bash_completion.d/scalafix
```
--zsh
Print out zsh tab completions. To install:
```
scalafix --zsh > /usr/local/share/zsh/site-functions/_scalafix
unfunction _scalafix
autoload -U _scalafix
```
Less common options:
--exclude [<glob> ...] (default: [])
Unix-style glob for files to exclude from fixing. The glob
syntax is defined by `nio.FileSystem.getPathMatcher`.
--tool-classpath URLClassLoader (default: "<classloader>")
Additional classpath for compiling and classloading custom
rules, as a set of filesystem paths, separated by ':' on
Unix or ';' on Windows.
--charset Charset (default: "UTF-8")
The encoding to use for reading/writing files
--no-sys-exit
If set, throw exception in the end instead of System.exit
--no-stale-semanticdb
Don't error on stale semanticdb files.
--settings ScalafixConfig (default: {})
Custom settings to override .scalafix.conf
--out-from String (default: null)
Write fixed output to custom location instead of in-place.
Regex is passed as first argument to
file.replaceAll(--out-from, --out-to), requires
--out-to.
--out-to String (default: null)
Companion of --out-from, string that is passed as second
argument to fileToFix.replaceAll(--out-from, --out-to)
--auto-suppress-linter-errors
Insert /* scalafix:ok */ suppressions instead of reporting
linter errors.
--cwd <path> (default: "/home/runner/work/scalafix/scalafix")
The current working directory
Plugins for other build tools
Scalafix is supported in other build tools via externally maintained plugins:
- Mill: mill-scalafix
- Gradle: gradle-scalafix
- Maven: scalafix-maven-plugin
- Mega-Linter (only built-in syntactic rules are supported)
SNAPSHOT
Our CI publishes a snapshot release to Sonatype on every merge into main. Each snapshot release has a unique version number, jars don't get overwritten.
If using the sbt plugin
// project/plugins.sbt
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.13.0")
+resolvers += Resolver.sonatypeRepo("snapshots")
+dependencyOverrides += "ch.epfl.scala" % "scalafix-interfaces" % "0.13.0+57-2efb89dd-SNAPSHOT"
If using the command-line interface
cs launch ch.epfl.scala:scalafix-cli_2.13.15:0.13.0+57-2efb89dd-SNAPSHOT -r sonatype:snapshots --main scalafix.cli.Cli -- --help