Skip to content

doc-sync guards never cross-check the analyzer registry — sprintfbool (55th, registered) is undocumented in doc.go/README/spec_t [Content truncated due to length] #46977

Description

@github-actions

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)

  1. 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.
  2. 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).
  3. 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

  • 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 caught sprintfbool).
  • Registry exposed via an importable exported slice; main.go consumes it.
  • doc.go (55 + bullet), README (bullet + table + examples), and documentedAnalyzers() all include sprintfbool.
  • New completeness test is bidirectional.

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 ·

  • expires on Jul 27, 2026, 9:02 PM UTC-08:00

Metadata

Metadata

Labels

cookieIssue Monster Loves Cookies!sergo

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions