KrishRVH/boringlint

GitHub: KrishRVH/boringlint

boringlint 是一个强制使用受限 Go 方言的静态分析器,通过禁止迭代器和方法局部泛型等特性来保持代码的过程化与简洁。

Stars: 0 | Forks: 0

# boringlint [![Go 参考](https://pkg.go.dev/badge/github.com/KrishRVH/boringlint.svg)](https://pkg.go.dev/github.com/KrishRVH/boringlint) `boringlint` 强制使用一种刻意受限的 Go 方言:推崇直接的数据和 控制流,而非掩盖这两者的语言机制。 其策略受 ThePrimeagen 的 [*I am done with Golang*](https://youtu.be/WqSWZuGS9pc) 启发:保持 Go 的过程化特性, 让控制流和开销可见,并保持代码库的熟悉感。 这是一种带有主观倾向的策略,并不意味着被拒绝的特性是无效的 Go。v0.9.5 是 stable 版本 Go 1.27 发布之前,针对 v1 规则范围和公共 analyzer API 的最终发布候选版本。v1.0 版本的发布仍然 取决于其能否通过 stable 版本 Go 1.27 toolchain 的兼容性测试。 ## 安装与运行 使用 Go 1.25 或更新版本安装此命令,且使用的 toolchain 版本不得低于 将要分析的代码版本: ``` go install github.com/KrishRVH/boringlint/cmd/boringlint@latest boringlint ./... ``` 该命令还实现了 `go vet` 工具协议: ``` go vet ./... go vet -vettool="$(command -v boringlint)" ./... ``` 运行这两个命令。`-vettool` 会使用 `boringlint` 替代标准的 vet analyzer;它不会在标准集合中添加检查。 ## 抑制 刻意去除了针对单个位置的抑制:`//nolint` 是 golangci-lint 的 约定,在 standalone 和 vettool 模式下会被忽略,而且一个带有 站点级逃生舱的策略 linter 将不再成其为策略。要仅运行一个 analyzer,可以在 调用时指定它: ``` boringlint -noiterator ./... go vet -vettool="$(command -v boringlint)" -noiterator ./... ``` 显式的 package 模式会在两种模式下限制分析范围。一个自定义的 [golangci-lint v2 module-plugin 适配器](https://golangci-lint.run/docs/plugins/module-plugins/) 可以根据规则名称注册导出的 analyzer,之后 golangci-lint 将 识别 `//nolint:noiterator`;`boringlint` 并不内置该适配器。 ## Go 版本支持 `boringlint` 的源码项目最低版本要求为 Go 1.23,该版本引入了 [range-over-function 和 `iter`](https://go.dev/doc/go1.23)。新的 Go 版本 只有在两种命令模式都通过兼容性测试后才会受支持; 当前的上限是 Go 1.27,已通过 Go 1.27rc2 toolchain 进行了测试。构建、 安装或运行 `boringlint` 需要 Go 1.25 或更新版本。将其 analyzer 导入到自定义 driver 中具有相同的最低版本限制,因为 module 的 [`go` 指令是强制性的最低版本要求](https://go.dev/ref/mod#go-mod-file-go)。 只要 standalone 或 vettool 二进制程序是使用受支持的、且版本不低于 所分析源码的 toolchain 构建的,它就可以分析声明使用 Go 1.23 或 1.24 的项目。 `nogenericmethod` 会分析 Go 1.27 语法,因此其二进制程序必须使用 Go 1.27 或更新版本构建。该代码仓库目前使用 Go 1.27rc2 测试该规则: ``` GOTOOLCHAIN=go1.27rc2 go install github.com/KrishRVH/boringlint/cmd/boringlint@latest ``` Go 1.27 目前仍是发布候选版本。v0.9.5 验证了其当前的语法和 工具行为,但在最终的 toolchain 通过相同的兼容性测试之前,v1.0 不会声明 提供稳定的 Go 1.27 支持。 设定 Go 1.25 toolchain 的最低版本要求是刻意为之。分析 driver 会消耗 [compiler 导出数据](https://github.com/golang/tools/blob/v0.48.0/go/gcexportdata/gcexportdata.go#L5-L23) 并实现 [`go vet -vettool` 协议](https://github.com/golang/tools/blob/v0.48.0/go/analysis/unitchecker/unitchecker.go#L82-L96), 因此普通的 Go 源码兼容性并不能使旧的 driver 具备向前兼容性。[`x/tools` v0.36.0](https://github.com/golang/tools/blob/v0.36.0/go.mod) 是在 [v0.37.0 提高该要求之前](https://github.com/golang/tools/blob/v0.37.0/go.mod),最后一个最低版本要求为 Go 1.23 的发布版本,但 Go 的 [子仓库通常仅支持之前的两个 Go 发布版本和 tip](https://go.dev/wiki/X-Repositories)。 将 driver 冻结在旧版本会为了更低的构建版本要求而牺牲掉不受支持的 compiler 集成。兼容的 [`x/tools` v0.48.0](https://github.com/golang/tools/blob/v0.48.0/go.mod) 要求 使用 Go 1.25。 只有当正确支持新发布的 Go 版本需要更新的分析 driver 时,最低版本要求才会 提高。每次提高都必须在 `go.mod` 和本节中明确说明, 并且在声明支持该源码版本之前,standalone 和 vettool 兼容性测试都必须通过。 ## 规则 ### `noiterator` 拒绝: - 直接导入 `iter`; - Go 1.23 的 range-over-function; - 项目类型、函数和方法声明中具有 iterator 形状的类型, 包括 constraint、字段、参数和结果,即使 允许的 iterator 类型具有不同的有效 yield 签名; - 项目类型声明中包含具有 iterator 形状 term 的 constraint 名称,包括 消除了这些 term 的混合 union 和 intersection。 每个独立命名的隐藏 constraint term 都会在该 term 处被报告。 针对整个类型参数的诊断不会取代对其 constraint 内部不同命名 term 的诊断。 ``` import "iter" // rejected type Sequence func(func(int) bool) // rejected type MaybeSequence = dependency.SequenceOrSlice // rejected func Values(yield func(int) bool) { // rejected // ... } for value := range dependency.Values() { // rejected // ... } ``` 对 array、slice、string、map、channel 和 integer 的 range 仍然是 允许的。依赖项返回的值也是允许的,这样就可以在 不命名 iterator 类型的情况下将它们实体化: ``` values := slices.Collect(dependency.Values()) ``` 该规则有意控制项目类型、函数和方法声明中的 import、range 语句和 具有 iterator 形状的类型。变量声明和函数字面量签名中 具有 iterator 形状的类型仍然是被允许的;该规则 并不试图在表达式层面上,禁止所有碰巧属于 iterator 形状类型的值。 #### 为什么 [`iter.Seq` 协议](https://pkg.go.dev/iter#Seq) 是基于推送的:生产者通过 调用调用者提供的 `yield` 函数来推进序列。 range-over-function 将该 callback 呈现为普通的循环语法; [`iter.Pull`](https://pkg.go.dev/iter#Pull) 通过 `next` 恢复了由调用者驱动的推进方式, 但放弃序列的调用者必须调用 `stop`。 在边界处将依赖项的 iterator 实体化,可以将具有 iterator 形状的类型排除在 项目类型、函数和方法声明之外,并恢复对具体集合的普通 range 操作。 ### `nogenericmethod` 拒绝在 Go 1.27 中引入的方法局部类型参数,无论是在方法 声明还是使用中。泛型函数仍然是被允许的,仅使用其 receiver 类型参数的 方法也同样被允许。 ``` func (box Box[T]) Map[U any](convert func(T) U) U { // rejected return convert(box.value) } func Map[T, U any](box Box[T], convert func(T) U) U { // allowed return convert(box.value) } ``` #### 为什么 [Go 1.27](https://go.dev/doc/go1.27#language) 既不允许 interface 方法带有类型参数, 也不允许将泛型方法作为 interface 方法的实现。在 泛型 receiver 上,方法局部的类型参数会在 receiver 实例化之上叠加方法实例化。 package 级别的泛型函数会将 receiver 作为普通的参数,并将泛型操作保留在单一的 package 级别命名空间中。 ## 作为 package 使用 analyzer 是普通的 `go/analysis` 值,可以被组合到 自定义 driver 中: ``` package main import ( "github.com/KrishRVH/boringlint" "golang.org/x/tools/go/analysis/multichecker" ) func main() { multichecker.Main(boringlint.NoIterator, boringlint.NoGenericMethod) } ``` ## 开发 [mise](https://mise.jdx.dev/) 是开发者接口: ``` mise run tasks mise run standards mise run standards:check ``` 完整的测试门会检查格式化、module 完整性、静态分析、 漏洞、密钥、竞态行为、命令集成、Go 1.25 兼容性以及 Go 1.27 行为。 ### 发布 仅发布确切通过了本地和远程测试门的 commit: 1. 检查已锁定的 mise 工具和 CI mise runtime。如果工具锁定发生变化,请运行 `mise run lock`;切勿手动编辑 lockfile。 2. 运行 `mise run standards:check`,检查完整的 diff,并使用 Conventional Commit 进行 commit。 3. 确认 `git status --short` 为空,推送 commit,并等待 CI 在 该确切 SHA 上通过。 4. 从通过 CI 的确切 SHA 创建 annotated tag,命令为 `git tag -a -m vX.Y.Z vX.Y.Z `,然后运行 `git push origin vX.Y.Z`。 5. 从该 tag 发布 GitHub Release,例如使用 `gh release create vX.Y.Z --verify-tag --generate-notes`。 在其对应 commit 的 CI 运行仍处于挂起状态时,请勿发布 tag 或 release。 ## 许可证 MIT
标签:EVTX分析, Go, Ruby工具, SOC Prime, 代码规范, 开发工具, 日志审计, 错误基检测, 静态代码分析