diff --git a/cmd/linters/main.go b/cmd/linters/main.go index ef212caea99..bba7bfb47aa 100644 --- a/cmd/linters/main.go +++ b/cmd/linters/main.go @@ -16,119 +16,9 @@ package main import ( "golang.org/x/tools/go/analysis/multichecker" - "github.com/github/gh-aw/pkg/linters/appendbytestring" - "github.com/github/gh-aw/pkg/linters/appendoneelement" - "github.com/github/gh-aw/pkg/linters/bytesbufferstring" - "github.com/github/gh-aw/pkg/linters/bytescomparestring" - "github.com/github/gh-aw/pkg/linters/contextcancelnotdeferred" - "github.com/github/gh-aw/pkg/linters/ctxbackground" - "github.com/github/gh-aw/pkg/linters/deferinloop" - "github.com/github/gh-aw/pkg/linters/errorfwrapv" - "github.com/github/gh-aw/pkg/linters/errormessage" - "github.com/github/gh-aw/pkg/linters/errortypeassertion" - "github.com/github/gh-aw/pkg/linters/errstringmatch" - "github.com/github/gh-aw/pkg/linters/excessivefuncparams" - "github.com/github/gh-aw/pkg/linters/execcommandwithoutcontext" - "github.com/github/gh-aw/pkg/linters/fileclosenotdeferred" - "github.com/github/gh-aw/pkg/linters/fmterrorfnoverbs" - "github.com/github/gh-aw/pkg/linters/fprintlnsprintf" - "github.com/github/gh-aw/pkg/linters/hardcodedfilepath" - "github.com/github/gh-aw/pkg/linters/httpnoctx" - "github.com/github/gh-aw/pkg/linters/httprespbodyclose" - "github.com/github/gh-aw/pkg/linters/httpstatuscode" - "github.com/github/gh-aw/pkg/linters/ioutildeprecated" - "github.com/github/gh-aw/pkg/linters/jsonmarshalignoredeerror" - "github.com/github/gh-aw/pkg/linters/largefunc" - "github.com/github/gh-aw/pkg/linters/lenstringsplit" - "github.com/github/gh-aw/pkg/linters/lenstringzero" - "github.com/github/gh-aw/pkg/linters/logfatallibrary" - "github.com/github/gh-aw/pkg/linters/manualmutexunlock" - "github.com/github/gh-aw/pkg/linters/mapclearloop" - "github.com/github/gh-aw/pkg/linters/mapdeletecheck" - "github.com/github/gh-aw/pkg/linters/nilctxpassed" - "github.com/github/gh-aw/pkg/linters/osexitinlibrary" - "github.com/github/gh-aw/pkg/linters/osgetenvlibrary" - "github.com/github/gh-aw/pkg/linters/ossetenvlibrary" - panicinlibrarycode "github.com/github/gh-aw/pkg/linters/panic-in-library-code" - "github.com/github/gh-aw/pkg/linters/rawloginlib" - "github.com/github/gh-aw/pkg/linters/regexpcompileinfunction" - "github.com/github/gh-aw/pkg/linters/seenmapbool" - "github.com/github/gh-aw/pkg/linters/sortslice" - "github.com/github/gh-aw/pkg/linters/sprintfbool" - "github.com/github/gh-aw/pkg/linters/sprintferrdot" - "github.com/github/gh-aw/pkg/linters/sprintferrorsnew" - "github.com/github/gh-aw/pkg/linters/sprintfint" - "github.com/github/gh-aw/pkg/linters/ssljson" - "github.com/github/gh-aw/pkg/linters/strconvparseignorederror" - "github.com/github/gh-aw/pkg/linters/stringreplaceminusone" - "github.com/github/gh-aw/pkg/linters/stringscountcontains" - "github.com/github/gh-aw/pkg/linters/stringsindexcontains" - "github.com/github/gh-aw/pkg/linters/timeafterleak" - "github.com/github/gh-aw/pkg/linters/timenowsub" - "github.com/github/gh-aw/pkg/linters/timesleepnocontext" - "github.com/github/gh-aw/pkg/linters/tolowerequalfold" - "github.com/github/gh-aw/pkg/linters/trimleftright" - "github.com/github/gh-aw/pkg/linters/uncheckedtypeassertion" - "github.com/github/gh-aw/pkg/linters/wgdonenotdeferred" - "github.com/github/gh-aw/pkg/linters/writebytestring" + "github.com/github/gh-aw/pkg/linters" ) func main() { - multichecker.Main( - appendbytestring.Analyzer, - appendoneelement.Analyzer, - bytesbufferstring.Analyzer, - bytescomparestring.Analyzer, - contextcancelnotdeferred.Analyzer, - ctxbackground.Analyzer, - deferinloop.Analyzer, - errormessage.Analyzer, - errortypeassertion.Analyzer, - fprintlnsprintf.Analyzer, - errstringmatch.Analyzer, - errorfwrapv.Analyzer, - execcommandwithoutcontext.Analyzer, - excessivefuncparams.Analyzer, - fileclosenotdeferred.Analyzer, - fmterrorfnoverbs.Analyzer, - hardcodedfilepath.Analyzer, - httpnoctx.Analyzer, - httprespbodyclose.Analyzer, - ioutildeprecated.Analyzer, - httpstatuscode.Analyzer, - largefunc.Analyzer, - logfatallibrary.Analyzer, - manualmutexunlock.Analyzer, - mapclearloop.Analyzer, - mapdeletecheck.Analyzer, - nilctxpassed.Analyzer, - osexitinlibrary.Analyzer, - osgetenvlibrary.Analyzer, - ossetenvlibrary.Analyzer, - panicinlibrarycode.Analyzer, - rawloginlib.Analyzer, - regexpcompileinfunction.Analyzer, - ssljson.Analyzer, - seenmapbool.Analyzer, - sortslice.Analyzer, - sprintferrdot.Analyzer, - sprintferrorsnew.Analyzer, - sprintfbool.Analyzer, - sprintfint.Analyzer, - strconvparseignorederror.Analyzer, - stringreplaceminusone.Analyzer, - stringsindexcontains.Analyzer, - stringscountcontains.Analyzer, - jsonmarshalignoredeerror.Analyzer, - lenstringzero.Analyzer, - lenstringsplit.Analyzer, - timeafterleak.Analyzer, - timesleepnocontext.Analyzer, - timenowsub.Analyzer, - tolowerequalfold.Analyzer, - trimleftright.Analyzer, - uncheckedtypeassertion.Analyzer, - wgdonenotdeferred.Analyzer, - writebytestring.Analyzer, - ) + multichecker.Main(linters.All()...) } diff --git a/linters b/linters index 67317959555..1a43d3b0037 100755 Binary files a/linters and b/linters differ diff --git a/pkg/linters/README.md b/pkg/linters/README.md index 230c3b5400d..a6e1f1d21f7 100644 --- a/pkg/linters/README.md +++ b/pkg/linters/README.md @@ -46,6 +46,7 @@ This package currently provides custom Go analyzers in the following subpackages - `sortslice` — reports `sort.Slice` / `sort.SliceStable` calls that should use `slices.SortFunc` / `slices.SortStableFunc`. - `sprintferrdot` — reports redundant `.Error()` calls on error values passed to `fmt` format functions where the fmt package calls `.Error()` automatically. - `sprintferrorsnew` — reports `errors.New(fmt.Sprintf(...))` calls that should use `fmt.Errorf` instead. +- `sprintfbool` — reports `fmt.Sprintf("%t", b)` calls where `b` is a single bool value and suggests using `strconv.FormatBool(b)` instead. - `sprintfint` — reports `fmt.Sprintf("%d", ...)` and related conversions that should use `strconv` helpers. - `ssljson` — validates `ssl.json` skill artifacts found in `.github/skills/` against the SSL spec (enum membership, graph integrity, transition targets, entry pointer validity). - `strconvparseignorederror` — reports `strconv` parsing calls (`Atoi`, `ParseInt`, etc.) where the error return is discarded with `_`. @@ -108,6 +109,7 @@ This package currently provides custom Go analyzers in the following subpackages | `sortslice` | Custom `go/analysis` analyzer that flags `sort.Slice` / `sort.SliceStable` calls that should use `slices.SortFunc` / `slices.SortStableFunc` | | `sprintferrdot` | Custom `go/analysis` analyzer that flags redundant `.Error()` calls on error values passed to `fmt` format functions | | `sprintferrorsnew` | Custom `go/analysis` analyzer that flags `errors.New(fmt.Sprintf(...))` calls that should use `fmt.Errorf` instead | +| `sprintfbool` | Custom `go/analysis` analyzer that flags `fmt.Sprintf("%t", b)` calls where `b` is a single bool value and suggests using `strconv.FormatBool(b)` instead | | `sprintfint` | Custom `go/analysis` analyzer that flags `fmt.Sprintf` integer conversions that should use `strconv` helpers | | `ssljson` | Custom `go/analysis` analyzer that validates SSL JSON skill artifacts in `.github/skills/` | | `strconvparseignorederror` | Custom `go/analysis` analyzer that flags `strconv` parsing calls where the error return is discarded with `_` | @@ -155,6 +157,7 @@ import ( "github.com/github/gh-aw/pkg/linters/rawloginlib" "github.com/github/gh-aw/pkg/linters/regexpcompileinfunction" "github.com/github/gh-aw/pkg/linters/sortslice" + "github.com/github/gh-aw/pkg/linters/sprintfbool" "github.com/github/gh-aw/pkg/linters/sprintfint" "github.com/github/gh-aw/pkg/linters/ssljson" "github.com/github/gh-aw/pkg/linters/timesleepnocontext" @@ -183,6 +186,7 @@ _ = panicinlibrarycode.Analyzer _ = rawloginlib.Analyzer _ = regexpcompileinfunction.Analyzer _ = sortslice.Analyzer +_ = sprintfbool.Analyzer _ = sprintfint.Analyzer _ = ssljson.Analyzer _ = timesleepnocontext.Analyzer @@ -231,6 +235,7 @@ _ = trimleftright.Analyzer - `github.com/github/gh-aw/pkg/linters/sortslice` — sort-slice analyzer subpackage - `github.com/github/gh-aw/pkg/linters/sprintferrdot` — sprintf-err-dot analyzer subpackage - `github.com/github/gh-aw/pkg/linters/sprintferrorsnew` — sprintf-errors-new analyzer subpackage +- `github.com/github/gh-aw/pkg/linters/sprintfbool` — sprintf-bool analyzer subpackage - `github.com/github/gh-aw/pkg/linters/sprintfint` — sprintf-int analyzer subpackage - `github.com/github/gh-aw/pkg/linters/ssljson` — ssl-json analyzer subpackage - `github.com/github/gh-aw/pkg/linters/strconvparseignorederror` — strconv-parse-ignored-error analyzer subpackage diff --git a/pkg/linters/doc.go b/pkg/linters/doc.go index 8e5e91dcc5a..7aa6294e9e6 100644 --- a/pkg/linters/doc.go +++ b/pkg/linters/doc.go @@ -1,6 +1,6 @@ // Package linters is a namespace for gh-aw's custom Go analysis linters. // -// All 54 active analyzers: +// All 55 active analyzers: // // - appendbytestring — flags append(b, []byte(s)...) calls where s is a string that can be simplified to append(b, s...) // - appendoneelement — flags append(s, []T{x}...) calls where a single-element slice literal is spread and can be simplified to append(s, x) @@ -42,6 +42,7 @@ // - sortslice — flags sort.Slice / sort.SliceStable calls that should use slices.SortFunc / slices.SortStableFunc // - sprintferrdot — flags redundant .Error() calls on error values passed to fmt format functions // - sprintferrorsnew — flags errors.New(fmt.Sprintf(...)) calls that should use fmt.Errorf instead +// - sprintfbool — flags fmt.Sprintf("%t", b) calls where b is a single bool value and suggests using strconv.FormatBool(b) instead // - sprintfint — flags fmt.Sprintf calls that format integers that should use strconv.Itoa // - ssljson — validates ssl.json skill artifacts in .github/skills/ against the SSL spec // - strconvparseignorederror — flags strconv parsing calls where the error is discarded with _ diff --git a/pkg/linters/registry.go b/pkg/linters/registry.go new file mode 100644 index 00000000000..9ab23f92ec6 --- /dev/null +++ b/pkg/linters/registry.go @@ -0,0 +1,126 @@ +package linters + +import ( + "golang.org/x/tools/go/analysis" + + "github.com/github/gh-aw/pkg/linters/appendbytestring" + "github.com/github/gh-aw/pkg/linters/appendoneelement" + "github.com/github/gh-aw/pkg/linters/bytesbufferstring" + "github.com/github/gh-aw/pkg/linters/bytescomparestring" + "github.com/github/gh-aw/pkg/linters/contextcancelnotdeferred" + "github.com/github/gh-aw/pkg/linters/ctxbackground" + "github.com/github/gh-aw/pkg/linters/deferinloop" + "github.com/github/gh-aw/pkg/linters/errorfwrapv" + "github.com/github/gh-aw/pkg/linters/errormessage" + "github.com/github/gh-aw/pkg/linters/errortypeassertion" + "github.com/github/gh-aw/pkg/linters/errstringmatch" + "github.com/github/gh-aw/pkg/linters/excessivefuncparams" + "github.com/github/gh-aw/pkg/linters/execcommandwithoutcontext" + "github.com/github/gh-aw/pkg/linters/fileclosenotdeferred" + "github.com/github/gh-aw/pkg/linters/fmterrorfnoverbs" + "github.com/github/gh-aw/pkg/linters/fprintlnsprintf" + "github.com/github/gh-aw/pkg/linters/hardcodedfilepath" + "github.com/github/gh-aw/pkg/linters/httpnoctx" + "github.com/github/gh-aw/pkg/linters/httprespbodyclose" + "github.com/github/gh-aw/pkg/linters/httpstatuscode" + "github.com/github/gh-aw/pkg/linters/ioutildeprecated" + "github.com/github/gh-aw/pkg/linters/jsonmarshalignoredeerror" + "github.com/github/gh-aw/pkg/linters/largefunc" + "github.com/github/gh-aw/pkg/linters/lenstringsplit" + "github.com/github/gh-aw/pkg/linters/lenstringzero" + "github.com/github/gh-aw/pkg/linters/logfatallibrary" + "github.com/github/gh-aw/pkg/linters/manualmutexunlock" + "github.com/github/gh-aw/pkg/linters/mapclearloop" + "github.com/github/gh-aw/pkg/linters/mapdeletecheck" + "github.com/github/gh-aw/pkg/linters/nilctxpassed" + "github.com/github/gh-aw/pkg/linters/osexitinlibrary" + "github.com/github/gh-aw/pkg/linters/osgetenvlibrary" + "github.com/github/gh-aw/pkg/linters/ossetenvlibrary" + panicinlibrarycode "github.com/github/gh-aw/pkg/linters/panic-in-library-code" + "github.com/github/gh-aw/pkg/linters/rawloginlib" + "github.com/github/gh-aw/pkg/linters/regexpcompileinfunction" + "github.com/github/gh-aw/pkg/linters/seenmapbool" + "github.com/github/gh-aw/pkg/linters/sortslice" + "github.com/github/gh-aw/pkg/linters/sprintfbool" + "github.com/github/gh-aw/pkg/linters/sprintferrdot" + "github.com/github/gh-aw/pkg/linters/sprintferrorsnew" + "github.com/github/gh-aw/pkg/linters/sprintfint" + "github.com/github/gh-aw/pkg/linters/ssljson" + "github.com/github/gh-aw/pkg/linters/strconvparseignorederror" + "github.com/github/gh-aw/pkg/linters/stringreplaceminusone" + "github.com/github/gh-aw/pkg/linters/stringscountcontains" + "github.com/github/gh-aw/pkg/linters/stringsindexcontains" + "github.com/github/gh-aw/pkg/linters/timeafterleak" + "github.com/github/gh-aw/pkg/linters/timenowsub" + "github.com/github/gh-aw/pkg/linters/timesleepnocontext" + "github.com/github/gh-aw/pkg/linters/tolowerequalfold" + "github.com/github/gh-aw/pkg/linters/trimleftright" + "github.com/github/gh-aw/pkg/linters/uncheckedtypeassertion" + "github.com/github/gh-aw/pkg/linters/wgdonenotdeferred" + "github.com/github/gh-aw/pkg/linters/writebytestring" +) + +// All returns all registered custom analysis linters. +// +// This is the canonical, importable source of truth for the full set of active +// analyzers. Use it to drive multichecker.Main, test assertions, and +// doc-completeness checks so that every consumer stays in sync automatically. +func All() []*analysis.Analyzer { + return []*analysis.Analyzer{ + appendbytestring.Analyzer, + appendoneelement.Analyzer, + bytesbufferstring.Analyzer, + bytescomparestring.Analyzer, + contextcancelnotdeferred.Analyzer, + ctxbackground.Analyzer, + deferinloop.Analyzer, + errormessage.Analyzer, + errortypeassertion.Analyzer, + fprintlnsprintf.Analyzer, + errstringmatch.Analyzer, + errorfwrapv.Analyzer, + execcommandwithoutcontext.Analyzer, + excessivefuncparams.Analyzer, + fileclosenotdeferred.Analyzer, + fmterrorfnoverbs.Analyzer, + hardcodedfilepath.Analyzer, + httpnoctx.Analyzer, + httprespbodyclose.Analyzer, + ioutildeprecated.Analyzer, + httpstatuscode.Analyzer, + largefunc.Analyzer, + logfatallibrary.Analyzer, + manualmutexunlock.Analyzer, + mapclearloop.Analyzer, + mapdeletecheck.Analyzer, + nilctxpassed.Analyzer, + osexitinlibrary.Analyzer, + osgetenvlibrary.Analyzer, + ossetenvlibrary.Analyzer, + panicinlibrarycode.Analyzer, + rawloginlib.Analyzer, + regexpcompileinfunction.Analyzer, + ssljson.Analyzer, + seenmapbool.Analyzer, + sortslice.Analyzer, + sprintferrdot.Analyzer, + sprintferrorsnew.Analyzer, + sprintfbool.Analyzer, + sprintfint.Analyzer, + strconvparseignorederror.Analyzer, + stringreplaceminusone.Analyzer, + stringsindexcontains.Analyzer, + stringscountcontains.Analyzer, + jsonmarshalignoredeerror.Analyzer, + lenstringzero.Analyzer, + lenstringsplit.Analyzer, + timeafterleak.Analyzer, + timesleepnocontext.Analyzer, + timenowsub.Analyzer, + tolowerequalfold.Analyzer, + trimleftright.Analyzer, + uncheckedtypeassertion.Analyzer, + wgdonenotdeferred.Analyzer, + writebytestring.Analyzer, + } +} diff --git a/pkg/linters/spec_test.go b/pkg/linters/spec_test.go index 6de999bb12d..18e79637a32 100644 --- a/pkg/linters/spec_test.go +++ b/pkg/linters/spec_test.go @@ -12,7 +12,9 @@ import ( "github.com/github/gh-aw/pkg/linters" "github.com/github/gh-aw/pkg/linters/appendbytestring" + "github.com/github/gh-aw/pkg/linters/appendoneelement" "github.com/github/gh-aw/pkg/linters/bytesbufferstring" + "github.com/github/gh-aw/pkg/linters/bytescomparestring" "github.com/github/gh-aw/pkg/linters/contextcancelnotdeferred" "github.com/github/gh-aw/pkg/linters/ctxbackground" "github.com/github/gh-aw/pkg/linters/deferinloop" @@ -27,6 +29,8 @@ import ( "github.com/github/gh-aw/pkg/linters/fprintlnsprintf" "github.com/github/gh-aw/pkg/linters/hardcodedfilepath" "github.com/github/gh-aw/pkg/linters/httpnoctx" + "github.com/github/gh-aw/pkg/linters/httprespbodyclose" + "github.com/github/gh-aw/pkg/linters/httpstatuscode" "github.com/github/gh-aw/pkg/linters/ioutildeprecated" "github.com/github/gh-aw/pkg/linters/jsonmarshalignoredeerror" "github.com/github/gh-aw/pkg/linters/largefunc" @@ -38,22 +42,27 @@ import ( "github.com/github/gh-aw/pkg/linters/mapdeletecheck" "github.com/github/gh-aw/pkg/linters/nilctxpassed" "github.com/github/gh-aw/pkg/linters/osexitinlibrary" + "github.com/github/gh-aw/pkg/linters/osgetenvlibrary" "github.com/github/gh-aw/pkg/linters/ossetenvlibrary" panicinlibrarycode "github.com/github/gh-aw/pkg/linters/panic-in-library-code" "github.com/github/gh-aw/pkg/linters/rawloginlib" "github.com/github/gh-aw/pkg/linters/regexpcompileinfunction" "github.com/github/gh-aw/pkg/linters/seenmapbool" "github.com/github/gh-aw/pkg/linters/sortslice" + "github.com/github/gh-aw/pkg/linters/sprintfbool" "github.com/github/gh-aw/pkg/linters/sprintferrdot" "github.com/github/gh-aw/pkg/linters/sprintferrorsnew" + "github.com/github/gh-aw/pkg/linters/sprintfint" "github.com/github/gh-aw/pkg/linters/ssljson" "github.com/github/gh-aw/pkg/linters/strconvparseignorederror" "github.com/github/gh-aw/pkg/linters/stringreplaceminusone" "github.com/github/gh-aw/pkg/linters/stringscountcontains" "github.com/github/gh-aw/pkg/linters/stringsindexcontains" "github.com/github/gh-aw/pkg/linters/timeafterleak" + "github.com/github/gh-aw/pkg/linters/timenowsub" "github.com/github/gh-aw/pkg/linters/timesleepnocontext" "github.com/github/gh-aw/pkg/linters/tolowerequalfold" + "github.com/github/gh-aw/pkg/linters/trimleftright" "github.com/github/gh-aw/pkg/linters/uncheckedtypeassertion" "github.com/github/gh-aw/pkg/linters/wgdonenotdeferred" "github.com/github/gh-aw/pkg/linters/writebytestring" @@ -72,23 +81,25 @@ type docAnalyzer struct { } // documentedAnalyzers returns the analyzer subpackages documented in the README -// "Public API > Subpackages" table. The README documents 46 analyzers +// "Public API > Subpackages" table. The README documents 55 analyzers // subpackages (the non-analyzer `internal` helper subpackage is excluded because // it exposes no Analyzer). // // Spec (README "Public API > Subpackages"): // -// appendbytestring, bytesbufferstring, contextcancelnotdeferred, ctxbackground, deferinloop, errorfwrapv, excessivefuncparams, errormessage, +// appendbytestring, appendoneelement, bytesbufferstring, bytescomparestring, contextcancelnotdeferred, ctxbackground, deferinloop, errorfwrapv, excessivefuncparams, errormessage, // errortypeassertion, errstringmatch, execcommandwithoutcontext, fileclosenotdeferred, fmterrorfnoverbs, fprintlnsprintf, -// hardcodedfilepath, httpnoctx, ioutildeprecated, jsonmarshalignoredeerror, largefunc, lenstringsplit, lenstringzero, -// logfatallibrary, manualmutexunlock, mapclearloop, mapdeletecheck, nilctxpassed, osexitinlibrary, ossetenvlibrary, panic-in-library-code, rawloginlib, -// regexpcompileinfunction, seenmapbool, sortslice, sprintferrdot, sprintferrorsnew, ssljson, -// strconvparseignorederror, stringreplaceminusone, stringscountcontains, stringsindexcontains, timeafterleak, timesleepnocontext, -// tolowerequalfold, uncheckedtypeassertion, wgdonenotdeferred, writebytestring +// hardcodedfilepath, httpnoctx, httprespbodyclose, httpstatuscode, ioutildeprecated, jsonmarshalignoredeerror, largefunc, lenstringsplit, lenstringzero, +// logfatallibrary, manualmutexunlock, mapclearloop, mapdeletecheck, nilctxpassed, osexitinlibrary, osgetenvlibrary, ossetenvlibrary, panic-in-library-code, rawloginlib, +// regexpcompileinfunction, seenmapbool, sortslice, sprintferrdot, sprintferrorsnew, sprintfbool, sprintfint, ssljson, +// strconvparseignorederror, stringreplaceminusone, stringscountcontains, stringsindexcontains, timeafterleak, timesleepnocontext, timenowsub, +// tolowerequalfold, trimleftright, uncheckedtypeassertion, wgdonenotdeferred, writebytestring func documentedAnalyzers() []docAnalyzer { return []docAnalyzer{ {"appendbytestring", appendbytestring.Analyzer}, + {"appendoneelement", appendoneelement.Analyzer}, {"bytesbufferstring", bytesbufferstring.Analyzer}, + {"bytescomparestring", bytescomparestring.Analyzer}, {"contextcancelnotdeferred", contextcancelnotdeferred.Analyzer}, {"ctxbackground", ctxbackground.Analyzer}, {"deferinloop", deferinloop.Analyzer}, @@ -103,6 +114,8 @@ func documentedAnalyzers() []docAnalyzer { {"fprintlnsprintf", fprintlnsprintf.Analyzer}, {"hardcodedfilepath", hardcodedfilepath.Analyzer}, {"httpnoctx", httpnoctx.Analyzer}, + {"httprespbodyclose", httprespbodyclose.Analyzer}, + {"httpstatuscode", httpstatuscode.Analyzer}, {"ioutildeprecated", ioutildeprecated.Analyzer}, {"jsonmarshalignoredeerror", jsonmarshalignoredeerror.Analyzer}, {"largefunc", largefunc.Analyzer}, @@ -114,6 +127,7 @@ func documentedAnalyzers() []docAnalyzer { {"mapdeletecheck", mapdeletecheck.Analyzer}, {"nilctxpassed", nilctxpassed.Analyzer}, {"osexitinlibrary", osexitinlibrary.Analyzer}, + {"osgetenvlibrary", osgetenvlibrary.Analyzer}, {"ossetenvlibrary", ossetenvlibrary.Analyzer}, {"panic-in-library-code", panicinlibrarycode.Analyzer}, {"rawloginlib", rawloginlib.Analyzer}, @@ -122,6 +136,8 @@ func documentedAnalyzers() []docAnalyzer { {"sortslice", sortslice.Analyzer}, {"sprintferrdot", sprintferrdot.Analyzer}, {"sprintferrorsnew", sprintferrorsnew.Analyzer}, + {"sprintfbool", sprintfbool.Analyzer}, + {"sprintfint", sprintfint.Analyzer}, {"ssljson", ssljson.Analyzer}, {"strconvparseignorederror", strconvparseignorederror.Analyzer}, {"stringreplaceminusone", stringreplaceminusone.Analyzer}, @@ -129,7 +145,9 @@ func documentedAnalyzers() []docAnalyzer { {"stringsindexcontains", stringsindexcontains.Analyzer}, {"timeafterleak", timeafterleak.Analyzer}, {"timesleepnocontext", timesleepnocontext.Analyzer}, + {"timenowsub", timenowsub.Analyzer}, {"tolowerequalfold", tolowerequalfold.Analyzer}, + {"trimleftright", trimleftright.Analyzer}, {"uncheckedtypeassertion", uncheckedtypeassertion.Analyzer}, {"wgdonenotdeferred", wgdonenotdeferred.Analyzer}, {"writebytestring", writebytestring.Analyzer}, @@ -219,3 +237,45 @@ func TestSpec_DesignDecision_UniqueAnalyzerNames(t *testing.T) { assert.Len(t, names, len(documented), "each documented subpackage should expose a distinct Analyzer.Name") } + +// TestRegistryMatchesDocumentation validates that linters.All() (the canonical, +// importable registry) and documentedAnalyzers() (the spec_test hand-list +// derived from the README Subpackages table) are equal sets — bidirectionally. +// +// A registered analyzer absent from any doc surface (e.g. sprintfbool added to +// cmd/linters/main.go without updating docs) causes this test to fail, closing +// the recurring doc-sync drift gap (gh-aw#40436, #45185, #46131, #46527, +// #46707, #46977). +func TestRegistryMatchesDocumentation(t *testing.T) { + allAnalyzers := linters.All() + documented := documentedAnalyzers() + + registryNames := make(map[string]struct{}, len(allAnalyzers)) + for _, a := range allAnalyzers { + registryNames[a.Name] = struct{}{} + } + + documentedNames := make(map[string]struct{}, len(documented)) + for _, d := range documented { + documentedNames[d.analyzer.Name] = struct{}{} + } + + // Every registered analyzer must appear in documentedAnalyzers(). + for name := range registryNames { + assert.Contains(t, documentedNames, name, + "analyzer %q is in linters.All() but missing from documentedAnalyzers() in spec_test.go; "+ + "add it to documentedAnalyzers(), doc.go, and README.md", name) + } + + // Every documented analyzer must appear in the registry. + for name := range documentedNames { + assert.Contains(t, registryNames, name, + "analyzer %q is in documentedAnalyzers() but missing from linters.All(); "+ + "add it to pkg/linters/registry.go and cmd/linters/main.go", name) + } + + assert.Len(t, allAnalyzers, len(documented), + "linters.All() has %d analyzers but documentedAnalyzers() has %d; "+ + "keep both in sync when adding or removing a linter", + len(allAnalyzers), len(documented)) +}