Summary
The sprintfbool analyzer is registered in cmd/linters/main.go (it is the 55th analyzer) but is absent from every documentation surface — and every doc-sync guard is green anyway. This is the recurring doc-sync drift (#40436, #45185, #46131, #46527, #46707) reappearing, and it keeps reappearing because no test compares the docs against the authoritative registry. The three existing guards only check the docs against each other or check a hand-maintained subset, so a linter added to main.go without touching the docs slips through undetected.
Evidence — four doc surfaces, three different counts, all green
| Surface |
Location |
Count |
sprintfbool listed? |
| Registry (source of truth) |
cmd/linters/main.go:78-132 (multichecker.Main(...)) |
55 |
✅ line 116 |
| doc.go header + bullets |
pkg/linters/doc.go:3 (// All 54 active analyzers:) + 54 bullets |
54 |
❌ omitted |
| README bullets / Subpackages table / import example |
pkg/linters/README.md |
54 / — |
❌ absent everywhere |
| spec_test hand-list |
pkg/linters/spec_test.go:88-137 documentedAnalyzers() |
46 |
❌ omitted |
Verification:
$ grep -c Analyzer cmd/linters/main.go # 55 registered
$ grep -c '^// - ' pkg/linters/doc.go # 54 documented bullets
$ grep -n sprintfbool pkg/linters/README.md # (no output)
Why every guard passes
The three doc-sync tests and their blind spots
TestDocGo_CountMatchesBullets (doc_sync_test.go:22-52) — compares the header count to doc.go's own bullet count. Both are 54, so it passes. It never opens main.go, so it cannot see that 55 are registered.
assert.Equal(t, headerCount, bulletCount, ...) // 54 == 54 ✅ (registry is 55)
TestDocGo_AnalyzersMatchREADME (doc_sync_test.go:54-88) — compares doc.go bullets to the README table. sprintfbool is omitted from both, so the two sets are equal and it passes.
TestSpec_PublicAPI_SubpackageAnalyzers / ..._AnalyzersUsable / ..._UniqueAnalyzerNames (spec_test.go:143-221) — all iterate the hand-maintained documentedAnalyzers() slice (46 entries) and only assert each listed analyzer is non-nil / uniquely named. This is a listed ⊆ real check; it can never catch real \ listed omissions (sprintfbool, appendoneelement, bytescomparestring, httprespbodyclose, httpstatuscode, osgetenvlibrary, sprintfint, timenowsub, trimleftright, ... are all missing from the slice).
Root cause
The registry is inlined as positional args to multichecker.Main(...) inside func main() in package main (cmd/linters/main.go:77-133). Being a main package, it is not importable, so no test can enumerate it. Every doc artifact is therefore maintained by hand and drifts independently. The symptom ("header says N, M registered") has been fixed ≥5 times without ever closing the gap that lets it recur.
Recommendation (permanent fix)
- Extract the analyzer list into a single exported, importable source of truth, e.g.
func All() []*analysis.Analyzer in an importable package (pkg/linters or a new pkg/linters/registry). Have cmd/linters/main.go call multichecker.Main(registry.All()...) so the CLI and the tests consume the same slice.
- Add a completeness test asserting the registry set equals (a) the doc.go
// All N active analyzers: header count and bullet set, (b) the README Subpackages table, and (c) the spec_test.go documentedAnalyzers() labels — bidirectionally (registry == docs, not docs ⊆ registry).
- As the immediate patch: bump doc.go to
55, add the sprintfbool bullet, add it to the README bullet list + Subpackages table + import/usage examples, and add {"sprintfbool", sprintfbool.Analyzer} to documentedAnalyzers().
Validation checklist
Effort: small-to-medium (refactor registry to an exported slice + one guard test + mechanical doc updates).
Generated by 🤖 Sergo - Serena Go Expert · 419.4 AIC · ⌖ 11.5 AIC · ⊞ 5.8K · ◷
Summary
The
sprintfboolanalyzer is registered incmd/linters/main.go(it is the 55th analyzer) but is absent from every documentation surface — and every doc-sync guard is green anyway. This is the recurring doc-sync drift (#40436, #45185, #46131, #46527, #46707) reappearing, and it keeps reappearing because no test compares the docs against the authoritative registry. The three existing guards only check the docs against each other or check a hand-maintained subset, so a linter added tomain.gowithout touching the docs slips through undetected.Evidence — four doc surfaces, three different counts, all green
cmd/linters/main.go:78-132(multichecker.Main(...))pkg/linters/doc.go:3(// All 54 active analyzers:) + 54 bulletspkg/linters/README.mdpkg/linters/spec_test.go:88-137documentedAnalyzers()Verification:
Why every guard passes
The three doc-sync tests and their blind spots
TestDocGo_CountMatchesBullets(doc_sync_test.go:22-52) — compares the header count to doc.go's own bullet count. Both are 54, so it passes. It never opensmain.go, so it cannot see that 55 are registered.TestDocGo_AnalyzersMatchREADME(doc_sync_test.go:54-88) — compares doc.go bullets to the README table.sprintfboolis omitted from both, so the two sets are equal and it passes.TestSpec_PublicAPI_SubpackageAnalyzers/..._AnalyzersUsable/..._UniqueAnalyzerNames(spec_test.go:143-221) — all iterate the hand-maintaineddocumentedAnalyzers()slice (46 entries) and only assert each listed analyzer is non-nil / uniquely named. This is alisted ⊆ realcheck; it can never catchreal \ listedomissions (sprintfbool,appendoneelement,bytescomparestring,httprespbodyclose,httpstatuscode,osgetenvlibrary,sprintfint,timenowsub,trimleftright, ... are all missing from the slice).Root cause
The registry is inlined as positional args to
multichecker.Main(...)insidefunc main()in packagemain(cmd/linters/main.go:77-133). Being amainpackage, it is not importable, so no test can enumerate it. Every doc artifact is therefore maintained by hand and drifts independently. The symptom ("header says N, M registered") has been fixed ≥5 times without ever closing the gap that lets it recur.Recommendation (permanent fix)
func All() []*analysis.Analyzerin an importable package (pkg/lintersor a newpkg/linters/registry). Havecmd/linters/main.gocallmultichecker.Main(registry.All()...)so the CLI and the tests consume the same slice.// All N active analyzers:header count and bullet set, (b) the README Subpackages table, and (c) thespec_test.godocumentedAnalyzers()labels — bidirectionally (registry == docs, notdocs ⊆ registry).55, add thesprintfboolbullet, add it to the README bullet list + Subpackages table + import/usage examples, and add{"sprintfbool", sprintfbool.Analyzer}todocumentedAnalyzers().Validation checklist
go test ./pkg/linters/...fails before the fix when a registered analyzer is missing from any doc surface (add a guard test and confirm it would have caughtsprintfbool).main.goconsumes it.documentedAnalyzers()all includesprintfbool.Effort: small-to-medium (refactor registry to an exported slice + one guard test + mechanical doc updates).