You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Enforce-readiness: three clean, type-resolved linters (bytesbufferstring, mapdeletecheck, ioutildeprecated) have zero production
[Content truncated due to length] #45629
The registry has grown to 49 analyzers (cmd/linters/main.go), but the CI enforcement set in .github/workflows/cgo.yml (LINTER_FLAGS, lines 1208 and 1211) still lists only 20. Three of the newest analyzers are already built to the same enforcement bar as the linters that are enforced — type-resolved package identity, internal/nolint suppression, internal/filecheck.IsTestFile test-skip, and (for the two with autofix) analysistest.RunWithSuggestedFixes golden coverage — and each currently has zero production violations under the default build. They are ready to enforce:
Analyzer
main.go
Kind
Autofix
RWSF golden
Prod violations
bytesbufferstring
:73
simplification
yes
yes
0
ioutildeprecated
:90
deprecation
no (advisory)
n/a
0
mapdeletecheck
:95
simplification
yes
yes
0
Without CI enforcement these are dead weight in the registry — they can silently regress (e.g. a copied-in string(buf.Bytes()) or a resurrected io/ioutil import) with nothing to catch it. Adding them locks in the coverage the same way sprintfint, stringsindexcontains, stringscountcontains, tolowerequalfold, and fprintlnsprintf (all style/simplification linters) are already enforced.
Evidence per linter
bytesbufferstring — flags string(buf.Bytes()) on a bytes.Buffervalue receiver, suggesting buf.String().
Package/type identity is exact and type-resolved: isBytesBufferValue checks *types.Named with obj.Pkg().Path() == "bytes" && obj.Name() == "Buffer" (bytesbufferstring.go:117-124) — no syntactic name match.
Deliberately excludes *bytes.Buffer to keep the rewrite semantics-preserving (nil pointer would panic on .Bytes() but return "<nil>" on .String()), documented at :83-89. The value-only restriction is also self-consistent with addressability, since Bytes()/String() are pointer-receiver methods.
nolint (:33) + IsTestFile (:60); autofix verified by RunWithSuggestedFixes with a .golden (bytesbufferstring_test.go:15).
Grep of pkg/ (excluding _test.go/testdata) surfaces no production string(x.Bytes()) site.
ioutildeprecated — flags calls to deprecated io/ioutil functions (advisory only, no autofix).
nolint (:44) + IsTestFile (:58, :100). No autofix, so no compile-safety risk.
Grep confirms zeroio/ioutil imports in production pkg/ (only the linter's own doc comments match).
Note: the ReadDir → os.ReadDir mapping (:25) changes the return type ([]fs.FileInfo → []fs.DirEntry); since the linter only reports (no autofix) this matches Go's own deprecation advice and is safe to enforce.
mapdeletecheck — flags if _, ok := m[k]; ok { delete(m, k) }, suggesting bare delete(m, k).
Fully type-resolved: verifies the ok condition resolves to the same object as the init assignment (Uses/Defs, :71-74), the delete ident is the builtin (*types.Builtin, :93-94), the RHS is a *types.Map (:161-166), and the delete's map+key are the same entities as the index expression via a structural sameExpr (:102-106, :173-206). Function-call maps/keys are conservatively skipped (sameExpr has no *ast.CallExpr case), so no side-effect-reordering false positive.
The rewrite is unconditionally sound: delete is a no-op for absent keys and safe on nil maps, and the removed ok var is scoped to the if (no else, single-stmt body).
nolint (:33) + IsTestFile (:44); autofix verified by RunWithSuggestedFixes with a .golden (mapdeletecheck_test.go:15).
Of 24 if _, ok := ...]; ok { sites in production pkg/, none has a delete-only body; all 61 production delete( calls are unconditional or in unrelated contexts.
Why enforce now
All three sit at the same readiness bar as the 20 already-enforced analyzers (type-resolved identity, nolint, test-skip, golden autofix where applicable).
Zero production violations means enforcement is a pure guard — it changes no existing code, it only prevents regressions.
Add -bytesbufferstring -ioutildeprecated -mapdeletecheck to LINTER_FLAGS in .github/workflows/cgo.yml — the default run (:1208) at minimum; optionally the wasm run (:1211) too, though its package subset makes it lower-value here.
Validation checklist
make golint-custom LINTER_FLAGS="-bytesbufferstring -ioutildeprecated -mapdeletecheck -test=false" reports 0 diagnostics on the current tree.
Add the three flags to cgo.yml:1208 (and :1211 if desired); CI stays green.
A deliberately-introduced string(buf.Bytes()) / ioutil.ReadAll(...) / redundant if _,ok:=m[k];ok{delete(m,k)} in a pkg/ file fails CI, and a single //nolint:<name> suppresses it.
Effort: trivial — a 3-flag CI edit, no code changes, guarded by the checklist.
Summary
The registry has grown to 49 analyzers (
cmd/linters/main.go), but the CI enforcement set in.github/workflows/cgo.yml(LINTER_FLAGS, lines 1208 and 1211) still lists only 20. Three of the newest analyzers are already built to the same enforcement bar as the linters that are enforced — type-resolved package identity,internal/nolintsuppression,internal/filecheck.IsTestFiletest-skip, and (for the two with autofix)analysistest.RunWithSuggestedFixesgolden coverage — and each currently has zero production violations under the default build. They are ready to enforce:bytesbufferstringioutildeprecatedmapdeletecheckWithout CI enforcement these are dead weight in the registry — they can silently regress (e.g. a copied-in
string(buf.Bytes())or a resurrectedio/ioutilimport) with nothing to catch it. Adding them locks in the coverage the same waysprintfint,stringsindexcontains,stringscountcontains,tolowerequalfold, andfprintlnsprintf(all style/simplification linters) are already enforced.Evidence per linter
bytesbufferstring— flagsstring(buf.Bytes())on abytes.Buffervalue receiver, suggestingbuf.String().isBytesBufferValuechecks*types.Namedwithobj.Pkg().Path() == "bytes" && obj.Name() == "Buffer"(bytesbufferstring.go:117-124) — no syntactic name match.*bytes.Bufferto keep the rewrite semantics-preserving (nil pointer would panic on.Bytes()but return"<nil>"on.String()), documented at:83-89. The value-only restriction is also self-consistent with addressability, sinceBytes()/String()are pointer-receiver methods.nolint(:33) +IsTestFile(:60); autofix verified byRunWithSuggestedFixeswith a.golden(bytesbufferstring_test.go:15).pkg/(excluding_test.go/testdata) surfaces no productionstring(x.Bytes())site.ioutildeprecated— flags calls to deprecatedio/ioutilfunctions (advisory only, no autofix).pkgName.Imported().Path() == "io/ioutil",:73-76) and dot imports (obj.Pkg().Path() == "io/ioutil",:107-114) — correctly handles aliased imports (import iou "io/ioutil"), unlike the historic syntactic-name-match class (rawloginlib precision: isRawLog matches the identifier name "log" syntactically — shadowing FP and alias-import FN in a CI-enfor [Content truncated due to length] #39981, Linter precision: migrate the 3 remaining CI-enforced linters that match stdlib packages by identifier name to astutil.IsPkgSele [Content truncated due to length] #40243).nolint(:44) +IsTestFile(:58,:100). No autofix, so no compile-safety risk.io/ioutilimports in productionpkg/(only the linter's own doc comments match).ReadDir → os.ReadDirmapping (:25) changes the return type ([]fs.FileInfo→[]fs.DirEntry); since the linter only reports (no autofix) this matches Go's own deprecation advice and is safe to enforce.mapdeletecheck— flagsif _, ok := m[k]; ok { delete(m, k) }, suggesting baredelete(m, k).okcondition resolves to the same object as the init assignment (Uses/Defs,:71-74), thedeleteident is the builtin (*types.Builtin,:93-94), the RHS is a*types.Map(:161-166), and the delete's map+key are the same entities as the index expression via a structuralsameExpr(:102-106,:173-206). Function-call maps/keys are conservatively skipped (sameExprhas no*ast.CallExprcase), so no side-effect-reordering false positive.deleteis a no-op for absent keys and safe on nil maps, and the removedokvar is scoped to theif(noelse, single-stmt body).nolint(:33) +IsTestFile(:44); autofix verified byRunWithSuggestedFixeswith a.golden(mapdeletecheck_test.go:15).if _, ok := ...]; ok {sites in productionpkg/, none has adelete-only body; all 61 productiondelete(calls are unconditional or in unrelated contexts.Why enforce now
RunWithSuggestedFixesparity gap (Autofix verification-parity gap: 3 of 9 SuggestedFix linters (stringsindexcontains, sprintfint, stringreplaceminusone) have zero [Content truncated due to length] #43313) that bitwritebytestring/sprintfint, so their fixes are compile-verified against goldens.writebytestringandbytescomparestringare intentionally excluded here — they have open/known autofix-correctness caveats (writebytestring (new 43rd linter): autofix emits non-compiling io.WriteString(w, s) for named string types + missing RunWithSugg [Content truncated due to length] #44187, writebytestring autofix references io.WriteString but never adds the "io" import — non-compiling fix when the file doesn't alrea [Content truncated due to length] #44653, Autofix import robustness: sprintfint/writebytestring/bytescomparestring key import-presence on the path but emit a hardcoded pa [Content truncated due to length] #45037, bytescomparestring (new 44th linter): the "allocates" premise is inaccurate under gc — string(a) == string(b) does NOT allocate [Content truncated due to length] #44484) and should be enforced only after those land.Recommendation
Add
-bytesbufferstring -ioutildeprecated -mapdeletechecktoLINTER_FLAGSin.github/workflows/cgo.yml— the default run (:1208) at minimum; optionally the wasm run (:1211) too, though its package subset makes it lower-value here.Validation checklist
make golint-custom LINTER_FLAGS="-bytesbufferstring -ioutildeprecated -mapdeletecheck -test=false"reports 0 diagnostics on the current tree.GOOS=js GOARCH=wasmreports 0 (guards the wasm-only build-constrained files, cf. CI lint blind spot: build-constrained (*_wasm.go) files escape all 39 custom analyzers — proven by an un-migrated sprintfint sit [Content truncated due to length] #42645).cgo.yml:1208(and:1211if desired); CI stays green.string(buf.Bytes())/ioutil.ReadAll(...)/ redundantif _,ok:=m[k];ok{delete(m,k)}in apkg/file fails CI, and a single//nolint:<name>suppresses it.Effort: trivial — a 3-flag CI edit, no code changes, guarded by the checklist.