Summary
Five custom linters still identify standard-library calls by the syntactic identifier name (ident.Name == "<pkg>" / != "<pkg>") instead of by package identity via pass.TypesInfo. The canonical helper astutil.IsPkgSelector(pass, sel, pkgPath) (pkg/linters/internal/astutil/astutil.go:161-178) already exists and is proven in production (rawloginlib.go:58, plus regexpcompileinfunction, sortslice, tolowerequalfold, ctxbackground all migrated).
Two earlier issues tracked exactly these holdouts — #40243 (os/fmt/strings) and #40435 (os/context) — but both were closed via tracker auto-expiry with zero migrations applied. Verified at current HEAD (branch main): all five call sites are still syntactic, and three of them are CI-enforced (.github/workflows/cgo.yml:1208 LINTER_FLAGS), where a false positive is a build-blocker.
The remaining holdouts (verified at HEAD)
| Linter |
Location |
Current code |
CI-enforced? |
errstringmatch |
pkg/linters/errstringmatch/errstringmatch.go:100 |
if ident.Name != "strings" { |
✅ yes |
fprintlnsprintf |
pkg/linters/fprintlnsprintf/fprintlnsprintf.go:132 |
return ident.Name == "fmt" && sel.Sel.Name == name |
✅ yes |
osexitinlibrary |
pkg/linters/osexitinlibrary/osexitinlibrary.go:59 |
if ident.Name == "os" && sel.Sel.Name == "Exit" { |
✅ yes |
fileclosenotdeferred |
pkg/linters/fileclosenotdeferred/fileclosenotdeferred.go:174 |
`if !ok |
|
contextcancelnotdeferred |
pkg/linters/contextcancelnotdeferred/contextcancelnotdeferred.go:140 |
`if !ok |
|
Why syntactic matching is wrong (two failure modes)
- Alias-import false negative.
import str "strings" then str.Contains(err.Error(), "x") — ident.Name is "str", so the call escapes the linter. Same for import xos "os" / import xfmt "fmt" / import ctx "context". For the three enforced linters this silently lets the targeted anti-pattern ship past CI.
- Shadowing false positive (build-blocker for the 3 enforced ones). A local variable/parameter/field named
os/fmt/strings/context with a matching method or field name matches by name even though it is not the stdlib package — a spurious diagnostic that fails the build.
Current impact
Latent today — pkg/ currently has no aliased os/fmt/strings/context imports and does not shadow those identifiers, so no live FP/FN exists right now. But this is a correctness gap in enforced tooling that will bite the first time someone adds an alias import or a shadowing local. The fix is cheap, the helper is proven, and the sibling migration is already accepted — the two tracking issues simply expired before the work was done.
Correct approach (already used by the migrated linters)
astutil.IsPkgSelector resolves the selector base to a *types.PkgName and compares the imported path:
func IsPkgSelector(pass *analysis.Pass, sel *ast.SelectorExpr, pkgPath string) bool {
pkgIdent, ok := sel.X.(*ast.Ident)
if !ok { return false }
obj := pass.TypesInfo.ObjectOf(pkgIdent)
pkgName, ok := obj.(*types.PkgName)
if !ok || pkgName.Imported() == nil { return false }
return pkgName.Imported().Path() == pkgPath
}
Reference migration — rawloginlib.go:58: if !astutil.IsPkgSelector(pass, sel, "log") {
Recommendation
Replace each syntactic check with the existing helper (each is a one-line change against the already-extracted *ast.SelectorExpr):
errstringmatch.go:100 → if !astutil.IsPkgSelector(pass, sel, "strings") {
fprintlnsprintf.go:132 → return astutil.IsPkgSelector(pass, sel, "fmt") && sel.Sel.Name == name
osexitinlibrary.go:59 → if astutil.IsPkgSelector(pass, sel, "os") && sel.Sel.Name == "Exit" {
fileclosenotdeferred.go:174 → if !astutil.IsPkgSelector(pass, sel, "os") {
contextcancelnotdeferred.go:140 → resolve sel via astutil.IsPkgSelector(pass, sel, "context") (this file constructs sel/pkgIdent inline in isContextWithCancelCall; pass the *ast.SelectorExpr to the helper).
Validation checklist
Effort
Small — one-line change per linter plus two testdata cases each; the helper already exists. Single PR. This finishes the migration that #40243 and #40435 both started and that already landed for rawloginlib/regexpcompileinfunction/sortslice/tolowerequalfold/ctxbackground.
Related
#40243 (os/fmt/strings holdouts — expired unfixed), #40435 (os/context holdouts — expired unfixed), #39981 (rawloginlib, landed), #39733 (regexpcompileinfunction), #38789 (ctxbackground). Same syntactic-package-identity class.
References:
Generated by 🤖 Sergo - Serena Go Expert · 386 AIC · ⌖ 14.1 AIC · ⊞ 5.9K · ◷
Summary
Five custom linters still identify standard-library calls by the syntactic identifier name (
ident.Name == "<pkg>"/!= "<pkg>") instead of by package identity viapass.TypesInfo. The canonical helperastutil.IsPkgSelector(pass, sel, pkgPath)(pkg/linters/internal/astutil/astutil.go:161-178) already exists and is proven in production (rawloginlib.go:58, plusregexpcompileinfunction,sortslice,tolowerequalfold,ctxbackgroundall migrated).Two earlier issues tracked exactly these holdouts — #40243 (os/fmt/strings) and #40435 (os/context) — but both were closed via tracker auto-expiry with zero migrations applied. Verified at current
HEAD(branchmain): all five call sites are still syntactic, and three of them are CI-enforced (.github/workflows/cgo.yml:1208LINTER_FLAGS), where a false positive is a build-blocker.The remaining holdouts (verified at HEAD)
errstringmatchpkg/linters/errstringmatch/errstringmatch.go:100if ident.Name != "strings" {fprintlnsprintfpkg/linters/fprintlnsprintf/fprintlnsprintf.go:132return ident.Name == "fmt" && sel.Sel.Name == nameosexitinlibrarypkg/linters/osexitinlibrary/osexitinlibrary.go:59if ident.Name == "os" && sel.Sel.Name == "Exit" {fileclosenotdeferredpkg/linters/fileclosenotdeferred/fileclosenotdeferred.go:174contextcancelnotdeferredpkg/linters/contextcancelnotdeferred/contextcancelnotdeferred.go:140Why syntactic matching is wrong (two failure modes)
import str "strings"thenstr.Contains(err.Error(), "x")—ident.Nameis"str", so the call escapes the linter. Same forimport xos "os"/import xfmt "fmt"/import ctx "context". For the three enforced linters this silently lets the targeted anti-pattern ship past CI.os/fmt/strings/contextwith a matching method or field name matches by name even though it is not the stdlib package — a spurious diagnostic that fails the build.Current impact
Latent today —
pkg/currently has no aliasedos/fmt/strings/contextimports and does not shadow those identifiers, so no live FP/FN exists right now. But this is a correctness gap in enforced tooling that will bite the first time someone adds an alias import or a shadowing local. The fix is cheap, the helper is proven, and the sibling migration is already accepted — the two tracking issues simply expired before the work was done.Correct approach (already used by the migrated linters)
astutil.IsPkgSelectorresolves the selector base to a*types.PkgNameand compares the imported path:Reference migration —
rawloginlib.go:58:if !astutil.IsPkgSelector(pass, sel, "log") {Recommendation
Replace each syntactic check with the existing helper (each is a one-line change against the already-extracted
*ast.SelectorExpr):errstringmatch.go:100→if !astutil.IsPkgSelector(pass, sel, "strings") {fprintlnsprintf.go:132→return astutil.IsPkgSelector(pass, sel, "fmt") && sel.Sel.Name == nameosexitinlibrary.go:59→if astutil.IsPkgSelector(pass, sel, "os") && sel.Sel.Name == "Exit" {fileclosenotdeferred.go:174→if !astutil.IsPkgSelector(pass, sel, "os") {contextcancelnotdeferred.go:140→ resolveselviaastutil.IsPkgSelector(pass, sel, "context")(this file constructssel/pkgIdentinline inisContextWithCancelCall; pass the*ast.SelectorExprto the helper).Validation checklist
str "strings") — confirm it is now flagged (FN closed) for the 3 enforced linters, and detected for the 2 not-enforced.os/fmt/strings— confirm it is NOT flagged (FP / build-blocker closed).strings.Contains/fmt.Fprintln/os.Exit/os.Open+.Close/context.WithCancelcases unchanged.go test ./pkg/linters/...green;make golint-customoverpkg/produces no new diagnostics.Effort
Small — one-line change per linter plus two testdata cases each; the helper already exists. Single PR. This finishes the migration that #40243 and #40435 both started and that already landed for
rawloginlib/regexpcompileinfunction/sortslice/tolowerequalfold/ctxbackground.Related
#40243 (os/fmt/strings holdouts — expired unfixed), #40435 (os/context holdouts — expired unfixed), #39981 (rawloginlib, landed), #39733 (regexpcompileinfunction), #38789 (ctxbackground). Same syntactic-package-identity class.
References: