golangci-lint 默认不检查错误信息首字母大小写,需通过 revive 自定义规则实现,或用 pre-commit 脚本简单兜底,但维护例外列表(如"HTTP 404")才是实际难点。

golangci-lint 本身不检查错误信息首字母大小写
它没有内置 linter 负责校验 errors.New("Something wrong") 或 fmt.Errorf("Something wrong") 中字符串的首字母是否小写。这类风格规则不属于 staticcheck、govet、errcheck 等核心检查器的职责范围,官方 linter 集合里也不存在叫 errorcase 或 errormsgcase 的组件。
要用 revive 自定义 rule 实现该检查
revive 是 golangci-lint 推荐替代 golint 的可扩展 linter,支持通过配置文件定义正则匹配式规则。想强制错误消息首字母小写,需在 .golangci.yml 中启用 revive 并添加自定义 rule:
linters-settings:
revive:
rules:
- name: error-message-lowercase
disabled: false
severity: error
lint: "^(?i)([A-Z][a-z]+.*|.*[A-Z][a-z]+)$"
message: "error message must start with lowercase letter"
action: "replace"
replacement: "{{.LowerFirst}}"
source: |
// match errors.New("XXX") or fmt.Errorf("XXX")
// and check first non-space char of string literal
注意:revive 的 lint 字段是正则,实际生效需配合其解析 AST 的能力——上面示例是示意逻辑,真实 rule 需用 revive 支持的 rule DSL(如 string-literal 类型节点 + 正则提取),不能只靠纯文本正则。
- 必须先确保
revive在linters.enable列表中:- revive - rule 文件需放在项目内(如
.revive.toml),并在linters-settings.revive.rules中引用路径,或直接内联(如上) - 该规则对
log.Printf("ERROR: ...")无效——它只匹配明确构造error值的调用,如errors.New、fmt.Errorf、errors.Unwrap等
为什么不用 go/analysis 写原生 linter?
自己用 go/analysis 实现看似精准,但落地成本高、维护难:
- 需识别所有可能返回
error的函数调用,包括第三方封装(如github.com/pkg/errors.Wrapf) - 要跳过测试文件、生成代码(如
mock_*.go)、文档注释里的字符串字面量 - VSCode 和 CI 中必须统一注册 analyzer 名称、输出格式,否则 IDE 不显示、CI 不报错
- 升级 Go 版本后,
types.Info解析行为可能变化,导致误报突增
多数团队卡在这一步就放弃了——不是不会写,而是没人持续跟进误报反馈和兼容性适配。
更务实的做法:用 pre-commit + shell 脚本兜底
如果只是想拦截明显违规(比如 errors.New("Invalid input")),且不想折腾 revive 配置,可用简单脚本在提交前扫描:
grep -rE 'errors\.New\("[A-Z]|fmt\.Errorf\("[A-Z]' --include="*.go" . | grep -v "testdata/" | grep -v "vendor/"
把它加入 .pre-commit-config.yaml,比定制 linter 快得多。缺点是无法定位到具体行号、不和 IDE 联动、不能自动修复。
真正难的从来不是“怎么写规则”,而是“谁来维护这条规则的例外列表”——比如某些错误消息必须大写("HTTP 404"、"TLS handshake"),这些边界 case 很少写进文档,全靠口口相传。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











