Scalafix
相关工具推荐
| 工具 | 说明 |
|---|---|
| Scalafmt | Scala 代码格式化工具,与 Scalafix 互补 |
| Spotless | 多语言格式化调度插件,可集成 Scalafmt 做 Scala 格式化 |
| Pre-commit | 多语言 Git 钩子管理框架,可通过 local hook 调用 Scalafix |
| Checkstyle | Java 代码风格检查工具,Scalafix 是 Scala 对应的代码质量工具 |
1. 简介
Scalafix 是由 Scala Center 开发维护的 Scala 代码质量和重构工具,是 Scala 生态中的主流 Linter。它支持语法规则(无需编译)和语义规则(需 SemanticDB 编译器输出)两类规则,内置 9 条规则,并支持自定义规则。
- 主要检查语言:Scala(Scala 2.12、2.13、Scala 3)
- 主要检查能力:检测代码异味、废弃 API、不合规语法;涵盖代码质量检查
- 核心检查原理:基于 Scalameta 解析器,语法规则直接分析 AST,语义规则通过 SemanticDB 获取类型信息
- 检查规则/选项:9 个内置规则(4 个语义规则如 ExplicitResultTypes、NoAutoTupling、OrganizeImports、RemoveUnused;5 个语法规则如 DisableSyntax、ProcedureSyntax 等),全量规则列表
- GitHub 仓库:https://github.com/scalacenter/scalafix(874 stars)
- 开源协议:BSD-3-Clause
- 最新稳定版本:0.14.7
- 运行环境要求:需 JDK 11+;通过 sbt-scalafix 插件集成
- 误报率:低
2. 官方文档
3. 社区优秀实践
3.1 ZIO
ZIO 是 Scala 函数式编程框架,使用 Scalafmt 统一格式,并通过 Scalafix 管理代码质量规则。
ZIO 的实践特点:
- 通过
.scalafmt.conf中project.excludeFilters排除 Scalafix 测试规则目录 - 使用 Scalafmt 的
rewrite.rules完成部分代码重写,与 Scalafix 规则互补
3.2 Cats
Cats 是 Typelevel 生态核心库,同时使用 Scalafmt 和 Scalafix 管理代码质量。
Cats 的实践特点:
.scalafmt.conf中通过project.excludeFilters = ["scalafix/*"]排除 Scalafix 规则测试目录- 将 Scalafix 自定义规则测试放在独立目录中管理
- 使用 Scalafmt 的
rewrite.scala3.convertToNewSyntax = true统一 Scala 3 新语法
3.3 Play Framework
Play Framework 是 Scala/Web 领域主流 Web 框架,使用 Scalafmt 统一格式,配合 Scalafix 做代码质量检查。
Play Framework 的实践特点:
- 通过 Scalafmt 的
rewrite.rules自动整理 import 和修饰符顺序,减少 Scalafix 的OrganizeImports规则负担 - 使用
fileOverride为 Scala 3 目录切换 dialect,确保 Scalafix 规则在不同 Scala 版本下兼容
4. 工具配置说明
4.1 配置文件说明
Scalafix 涉及以下配置文件:
| 配置文件 | 用途 | 使用场景 |
|---|---|---|
.scalafix.conf |
Scalafix 主配置文件(HOCON 格式),声明启用的规则及规则参数 | 所有项目,控制规则集与规则行为 |
配置文件默认位置:仓库根目录 .scalafix.conf(HOCON 格式)。
4.2 .scalafix.conf 配置详解
.scalafix.conf 是 Scalafix 的主配置文件,通过 rules 字段声明启用的规则列表,并可为每个规则配置参数。
配置项说明:
| 配置项 | 类型 | 说明 |
|---|---|---|
rules |
list[string] | 启用的规则列表,按顺序执行 |
<RuleName>.<param> |
任意 | 规则专属参数,键名为规则名加参数名(如 DisableSyntax.noVars) |
推荐配置示例:
新项目推荐规则集:全部为语法规则,无需 SemanticDB。
# .scalafix.conf
rules = [
DisableSyntax
OrganizeImports
RedundantSyntax
NoAutoTupling
ProcedureSyntax
NoValInForComprehension
]
| 规则 | 类型 | 作用 |
|---|---|---|
DisableSyntax |
语法 | 禁用 var、return、null、while 等不推荐语法 |
OrganizeImports |
语法 | 整理和排序 import 语句 |
RedundantSyntax |
语法 | 移除冗余语法(如多余的 Unit 返回类型) |
NoAutoTupling |
语法 | 禁用自动元组化,避免隐含参数被包装为元组 |
ProcedureSyntax |
语法 | 将过程语法 def foo { } 改写为 def foo: Unit = { } |
NoValInForComprehension |
语法 | 移除 for 推导中多余的 val |
进阶规则集(需 SemanticDB):团队熟悉语法规则后,可逐步引入语义规则。
# .scalafix.conf
rules = [
DisableSyntax
OrganizeImports
RedundantSyntax
NoAutoTupling
ProcedureSyntax
ExplicitResultTypes
RemoveUnused
]
| 规则 | 类型 | 作用 | 前置条件 |
|---|---|---|---|
ExplicitResultTypes |
语义 | 为公共成员补全显式返回类型 | 需 SemanticDB |
RemoveUnused |
语义 | 移除未使用的 import 和局部变量 | 需 SemanticDB + -Wunused:imports 编译选项 |
引入语义规则时需在
build.sbt中配置 SemanticDB 启用选项,详见第 5 章集成方式说明。
5. 主流集成方式
5.1 sbt 集成(生态主流方式)
sbt 是 Scala 生态事实标准构建工具,Scalafix 通过官方 sbt-scalafix 插件集成(需 sbt 1.4+)。
在 project/plugins.sbt 中添加插件:
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.14.7")
常用 sbt 任务:
| 任务 | 作用 |
|---|---|
sbt scalafix |
运行所有启用的规则(自动修复) |
sbt scalafixAll |
运行所有配置(含 Test)的规则 |
sbt 'scalafix --check' |
检查模式(不修改,CI 用,有 diff 即失败) |
sbt 'scalafixAll --check' |
检查所有配置(不修改) |
sbt scalafix <规则名> |
运行指定规则 |
语义规则前置条件:在 build.sbt 中启用 SemanticDB:
ThisBuild / semanticdbEnabled := true
ThisBuild / semanticdbVersion := scalafixSemanticdb.revision
增量检查:sbt-scalafix 插件可通过传递文件路径限定作用范围:
sbt "scalafix src/main/scala/com/example/Foo.scala"
注意:语义规则需要完整编译上下文(SemanticDB),仅检查变更文件可能导致语义规则结果不完整。语法规则不受此限制。
通过 pre-commit local hook 集成 Scalafix 时(hook 通过 sbt scalafix 调用 sbt 任务),pre-commit 框架默认只对暂存区中变更的 .scala 文件触发 hook。由于 Scalafix 的 local hook 使用 pass_filenames: false(让 sbt 自行管理检查范围),实际增量由 sbt 的构建范围决定。
# .pre-commit-config.yaml
- repo: local
hooks:
- id: scalafix
name: Scalafix
entry: sbt scalafix
language: system
files: \.scala$
pass_filenames: false # 由 sbt 控制检查范围
注意:语义规则(如
ExplicitResultTypes、RemoveUnused)需要完整编译上下文(SemanticDB),仅检查变更文件可能导致语义规则结果不完整。语法规则不受此限制。pre-commit 负责"何时触发",sbt 负责"检查什么"。CI 中可通过pre-commit run --all-files做全量检查。
5.2 IDE 集成
| IDE | 集成方式 |
|---|---|
| IntelliJ IDEA | 安装 Scala 插件,Scalafix 通过 sbt 任务运行,IDE 不直接集成 |
| VS Code | 安装 Metals 扩展,Metals 支持 Scalafix 规则运行和快速修复 |
| Neovim | 通过 nvim-metals LSP 集成 Scalafix |
5.3 CLI 集成
通过 Coursier 安装 CLI:
cs install scalafix
常用 CLI 命令:
# 运行 .scalafix.conf 中配置的规则
scalafix
# 检查模式(不修改,CI 用)
scalafix --check
# 运行指定规则
scalafix OrganizeImports
# 指定 SemanticDB 路径(语义规则需要)
scalafix --sourceroot .
增量检查:结合 git diff 只检查暂存区中变更的 .scala 文件:
# 只检查 git 暂存区中变更的 .scala 文件
CHANGED=$(git diff --name-only --diff-filter=ACMR --cached -- '*.scala')
if [ -n "$CHANGED" ]; then
sbt "scalafix --check $CHANGED"
fi
CI 脚本调用:在 CI 环境中通过脚本调用 Scalafix(或 sbt 任务)进行格式化检查。
GitHub Actions 示例:
name: Scalafix Check
on: [pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "21"
- uses: sbt/setup-sbt@v1
- run: sbt 'scalafixAll --check'
增量检查:GitHub Actions 中按 PR 变更文件触发增量检查:
- name: Check changed files only
run: |
CHANGED=$(git diff --name-only --diff-filter=ACMR ${{ github.event.pull_request.base.sha }} -- '*.scala' | tr '\n' ' ')
if [ -n "$CHANGED" ]; then
sbt "scalafix --check $CHANGED"
fi
6. 告警抑制(屏蔽)方法
6.1 通过工具配置文件屏蔽
在 .scalafix.conf 中移除或注释不需要的规则:
# .scalafix.conf:移除 DisableSyntax 规则
rules = [
OrganizeImports
RedundantSyntax
NoAutoTupling
ProcedureSyntax
]
对特定规则配置参数以放宽限制:
# .scalafix.conf:DisableSyntax 规则中允许 var
rules = [
DisableSyntax {
noVar = false
noReturn = true
noNull = true
}
OrganizeImports
]
官方文档:规则配置
6.2 通过代码屏蔽
Scalafix 支持行级和块级注释屏蔽:
// 行级屏蔽:抑制当前行
val x = 1 // scalafix:ok
// 块级屏蔽:抑制一段代码
// scalafix:off DisableSyntax
var mutableState = 0
// scalafix:on DisableSyntax
官方文档:抑制语法
6.3 通过构建配置屏蔽
sbt 项目中可通过 scalafixConfig 指定不同配置文件,或通过 scalafixRules 动态调整规则:
// build.sbt:为 Test 配置使用不同规则集
Test / scalafixConfig := Some(file(".scalafix.test.conf"))
6.4 通过 sbt 任务范围屏蔽
通过 sbt 的 Configuration 作用域,只为特定配置运行 Scalafix:
# 只对 Compile 配置运行 Scalafix
sbt Compile/scalafix