scalafix - a Scala rewrite tool


0.3.1

Join the chat at https://gitter.im/scalacenter/scalafix

Scalafix is a Scala code rewriting tool and library. This effort follows the Scala Center Advisory Board proposal: Clarification of Scala to Dotty migration path. The long-term goal of the project is to automate migration from Scala 2.x to Dotty, a next-generation Scala compiler.

The name "scalafix" is inspired by gofix, a similar tool used to migrate code written in the Go programming language.

Installation


Scalafix can used either as a tool or a library. Integrations are currently limited to the command-line, but it may be possible to add IDE integrations in the future.

sbt-scalafix

sbt-scalafix is an integration to run pre-made scalafix rewrites as sbt commands.
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.3.2")

sbt-scalahost

sbt-scalahost is an integration to run custom scalafix rewrites using the scala.meta semantic API.
// project/plugins.sbt
addSbtPlugin("org.scalameta" % "sbt-scalahost" % "1.6.0")

// build.sbt
val rewrites = project
  .settings(
    scalaVersion := "2.11.8", // 2.12.x is also supported
    libraryDependencies += "ch.epfl.scala" %% "scalafix-core" % "0.3.2"
  )
  .dependsOn(
    projectToPerformRewritesOn % Scalameta
  )

// in rewrites/src/main/scala/Rewrites.scala
...
implicit mirror = scala.meta.Mirror()
val source = mirror.sources.head
val ctx = scalafix.rewrite.RewriteCtx(source,
  scalafix.config.ScalafixConfig(), mirror)
val patched = Patch(source, Seq(
  scalafix.util.TreePatch.AddGlobalImport(
    importer"scala.collection.immutable.Seq"))
)
...
For more information about sbt-scalahost, see sbt-semantic-example. For more information about using scalafix as a library, see scalafix-core.

scalafix-core

Scalafix can be used as a library to run custom rewrites.
libraryDependencies += "ch.epfl.scala" %% "scalafix-core" % "0.3.2"
By using scalafix as a library, you have full control of how/when/where rewrites are written.

When writing custom rewrites, you should decide what API you need. There currently available APIs are:

Scalafix as a library is still under heavy development. Feedback is very welcome. Be prepared for breaking changes.

scalafix-nsc

Scalafix can be used directly as a compiler plugin:
// download
https/repo1.maven.org/maven2/ch/epfl/scala/scalafix-nsc_2.12/0.3.2/scalafix-nsc_2.12-0.3.2.jar
// compile
scalac -Xplugin:/path/to/scalafix-nsc_2.12-0.3.2.jar mycode.scala

Configuration


Scalafix reads configuration from a file using HOCON syntax. I recommend you put a file .scalafix.conf into the root directory of your project.

rewrites

You can either run a pre-made that comes with scalafix or a custom rewrite that you write yourself.

Pre-made rewrites

Available rewrites are listed in Rewrites.

        rewrites = [ExplicitImplicit] # No rewrites are run if empty.

Custom rewrites


        rewrites = [
          "file:rewrites/MyRewrite.scala" // from local file
          // from url
          "https://gist.githubusercontent.com/olafurpg/fc6f43a695ac996bd02000f45ed02e63/raw/5c71c5444174130531021c2c39b0b3ccef30ef00/ExampleRewrite.scala"
          // from fully qualified name on classpath
          "scala:scalafix.rewrite.ExplicitImplicit"
        ]

imports

NOTE. This feature is new and may still have bugs that cause rewritten code to not compile. Please report back you bump into issues.

Scalafix can organize imports. If you use import patches such as Add/RemoveGlobalImport, you need to allow scalafix to organize you imports. To minimize the diff from import patches, it's best to run scalafix once with organize imports enabled and no zero patches.


        // Group and sort imports
        imports.organize = true
        // Example relative import: `import collection.immutable`
        // Expanded: `import scala.collection.immutable`
        imports.expandRelative = true
        // Removes global imports that -Ywarn-unused-import complains about
        // Requires sbt-scalafix/ScalafixMirror
        imports.removeUnused = true
        // Groups are separated by a blank line, example:
        // import com.a
        // import com.b
        //
        // import scala.collection.immutable._
        imports.groups = [
          "com.mycompany.*"
          "scala\\..*"
        ]

patches

For simple use-cases, it's possible to write custom rewrites directly .scalafix.conf.

      patches.removeGlobalImports = [
        "scala.collection.mutable" # scala.meta.Importee
      ]
      patches.addGlobalImports = [
        "scala.collection.immutable"
      ]
      patches.replacements = [
        {
          from = "_root_.scala.Seq." // scala.meta.Symbol
          to = "Seq"
          additionalImports = [ "scala.collection.immutable.Seq" ]
        }
      ]
      // Helper to see which symbols appear in your source files
      debug.printSymbols = true
For more advanced use-cases, I recommend you use scalafix-core as a library.

All options

The following is the list of all available options along with their default values.
rewrites = [
  ProcedureSyntax
  DemandJSGlobal
  ExplicitImplicit
]
patches.removeGlobalImports = []
patches.addGlobalImports = []
patches.replacements = []
parser = scala.meta.internal.parsers.ScalametaParser$$anon$202@4e11044e
debug.printSymbols = false
fatalWarnings = true
dialect = Scala211
imports.organize = true
imports.alwaysUsed = []
imports.expandRelative = false
imports.groups = [
  "scala.language.*"
  "(scala|scala\..*)$"
  "(java|java\..*)$"
]
imports.removeUnused = true
imports.groupByPrefix = false
imports.spaceAroundCurlyBrace = false

Rewrites


Scalafix comes with a few rewrites out-of-the-box. These rewrites have been chosen to meet the long-term goal of scalafix to clarify the Scala to Dotty migration path. To create custom rewrites, see scalafix-core.

ExplicitImplicit

Dotty requires implicit vals and defs to explicitly annotate return types. The ExplicitImplicit rewrite inserts the inferred type from the compiler for implicit definitions that are missing an explicit return type. For example,
// before
implicit val tt = liftedType
// after
implicit val tt: TypedType[R] = liftedType

ProcedureSyntax

"Procedure syntax" is not supported in Dotty. Methods that use procedure syntax should use regular method syntax instead. For example,
// before: procedure syntax
def main(args: Seq[String]) {
  println("Hello world!")
}
// after: regular syntax
def main(args: Seq[String]): Unit = {
  println("Hello world!")
}

VolatileLazyVal

Adds a @volatile annotation to lazy vals. The @volatile annotation is needed to maintain thread-safe behaviour of lazy vals in Dotty.

// before
lazy val x = ...
// after
@volatile lazy val x = ...

With @volatile, Dotty uses a deadlock free scheme that is comparable-if-not-faster than the scheme used in scalac.

Planned rewrites...

See here

FAQ / Troubleshooting


If you have any questions, don't hesitate to ask on Gitter. Join the chat at https://gitter.im/scalacenter/scalafix

Enclosing tree [2873] does not include tree [2872]

Scalafix requires code to compile with the scalac option -Yrangepos. A macro that emits invalid tree positions is usually the cause of compiler errors triggered by -Yrangepos. Other tools like the presentation compiler (ENSIME) or scala.meta semantic api also require -Yrangepos to work properly.

sbt.Init$RuntimeUndefined: References to undefined settings at runtime.

You might be using an old version of sbt. sbt-scalafix requires sbt 0.13.13 or higher.

(root/compile:compileIncremental) java.lang.reflect.InvocationTargetException

You might have a typo in .scalafix.conf, run last root/compile:compileIncremental to see the full stack trace.

Changelog


0.3.2

See merged PRs.

0.3.1

0.3.0

0.2.1

0.2.0

0.1.0