exhaustive检查器静态扫描switch是否显式覆盖具名枚举类型的所有已知值,要求枚举定义清晰、集中、可导出;v2.15起配置项更新为ignore-types和ignore-constants切片形式,default默认不视为穷尽需显式启用check-default。

golangci-lint 的 exhaustive 检查器能做什么
它不检查编译期穷尽性(Go 编译器本身不提供该能力),而是静态扫描 switch 语句是否显式覆盖了某个枚举类型的所有已知值。前提是:变量类型必须是具名的 enum 类型(即 type Status int 或 type Phase string 这类定义),且所有枚举值在同一个包内可被 linter 解析到。
它不能识别 int 或 string 字面量拼凑的“伪枚举”,也不会跨模块分析未导出的常量。所以你得先确保枚举定义清晰、集中、可导出(至少对 linter 可见)。
启用 exhaustive 并配置 ignore-types 和 ignore-constants
从 golangci-lint v2.15 开始,旧的 explicit-exhaustive-switch 已被合并为统一的 exhaustive 检查器,配置项也变了:
-
ignore-types是切片,不是单个字符串;要忽略某个枚举类型,得写成- Status,而不是ignore-enum-types: Status -
ignore-constants替代了ignore-enum-members,同样必须是切片形式,例如- StatusUnknown - 如果还沿用旧 key,linter 会静默忽略——不会报错,但配置失效,容易误以为“已禁用”
典型配置片段:
linters-settings:
exhaustive:
check: switch
ignore-types:
- ErrorCode
ignore-constants:
- ErrorCodeUnknown
为什么 switch 表达式里写了 default 还是报 exhaustive 错误
因为 exhaustive 默认把 default 当作“未穷尽”的兜底分支,而不是合法覆盖。它鼓励你显式列出所有已知值,而非依赖 default 掩盖新增枚举后漏处理的风险。
要让 default 被接受,必须显式开启 check-default:
linters-settings:
exhaustive:
check: switch
check-default: true
但注意:开启后,default 分支将被视为覆盖全部剩余值,此时新增枚举值不会触发告警——这削弱了它的核心价值。只在确实需要兼容外部不可控输入时才开。
常见误报和绕过陷阱
exhaustive 在以下情况容易漏报或误报:
- 枚举值定义在另一个 module,且未被
go mod vendor或本地 replace 覆盖 → linter 解析不到,认为“只有当前看到的几个值”,实际运行时却有更多 - 用了
const ( A = iota; B )但没导出A和B→ linter 无法访问,判定为“无值可覆盖” - switch 变量是接口类型,再做
.(*MyEnum)断言后使用 → exhaustive 不分析断言后的值,只看原始表达式类型 - 在 CI 中启用了
--fast模式 → exhaustive 被归入slow分组,默认被跳过
真正关键的不是“怎么关掉警告”,而是确认:枚举定义是否稳定、可见、完整;switch 是否真的只处理内部可控值;default 是否真有必要,还是只是偷懒。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











