Skip to content

Linter precision: two newest linters (fileclosenotdeferred, contextcancelnotdeferred) match stdlib packages by identifier name — [Content truncated due to length] #40435

Description

@github-actions

Summary

The codebase has been systematically eliminating the syntactic package match antipattern — identifying a stdlib call by ident.Name == "<pkg>" instead of by package identity via pass.TypesInfo. The canonical helper astutil.IsPkgSelector(pass, sel, pkgPath) exists (pkg/linters/internal/astutil/astutil.go:75-94) and is proven in production (sortslice, rawloginlib, regexpcompileinfunction), with #40243 currently consolidating the last three CI-enforced holdouts (os/fmt/strings).

The two most recently added linters re-introduce the same antipattern:

Linter Site Syntactic check
fileclosenotdeferred pkg/linters/fileclosenotdeferred/fileclosenotdeferred.go:172 ident.Name != "os" (gates os.Open/Create/OpenFile)
contextcancelnotdeferred pkg/linters/contextcancelnotdeferred/contextcancelnotdeferred.go:138 pkgIdent.Name != "context" (gates context.WithCancel/WithTimeout/WithDeadline)

Defect class (same as the landed precedents)

  • False positive (shadowing): a local variable named os or context — e.g. os := newObjectStore(); os.Open(path) — is matched as the stdlib package, producing a spurious diagnostic.
  • False negative (alias import): import osx "os"osx.Open(...), or import ctx "context"ctx.WithCancel(...), escapes detection entirely.

Note contextcancelnotdeferred's "context" syntactic match is the exact class flagged and fixed for the sibling ctxbackground linter in #38789 (closed) — the same file family detected context.Background() syntactically there.

Why a separate issue from #40243

#40243 is explicitly scoped to the three remaining CI-enforced linters (os/fmt/strings holdouts: osexitinlibrary, fprintlnsprintf, errstringmatch). These two linters are newly landed and not in that scope, so they would otherwise slip through. Closing them completes the codebase-wide consolidation #40243 began and prevents the antipattern from regressing on every new linter.

Evidence

  • pkg/linters/fileclosenotdeferred/fileclosenotdeferred.go:166-176isFileOpenCall matches sel.X.(*ast.Ident) then ident.Name != "os"
  • pkg/linters/contextcancelnotdeferred/contextcancelnotdeferred.go:132-147isContextWithCancelCall matches sel.X.(*ast.Ident) then pkgIdent.Name != "context"
  • pkg/linters/internal/astutil/astutil.go:75-94IsPkgSelector resolves ObjectOf*types.PkgNameImported().Path()
  • Precedent: pkg/linters/rawloginlib/rawloginlib.go:58 (migrated), and sprintferrdot.go:109 — the newest format linter already uses IsPkgSelector(pass, sel, "fmt") correctly, showing the helper is the established convention these two missed.

Recommendation

Replace the ident.Name == "<pkg>" checks with astutil.IsPkgSelector(pass, sel, "os") / astutil.IsPkgSelector(pass, sel, "context") respectively (one-line change each; the *ast.SelectorExpr is already in hand at both sites).

Validation checklist

  • Shadowing testdata: local os/context variable does not trigger a diagnostic
  • Alias-import testdata: osx "os" / ctx "context" calls are detected
  • Existing positive/negative cases unchanged
  • No production violations introduced (grep shows no aliased os/context imports in pkg/ non-test today, so this is precision-hardening with zero churn)

Effort: small (two single-line changes + shadowing/alias testdata).

References: §27860925409

Generated by 🤖 Sergo - Serena Go Expert · 276.8 AIC · ⌖ 11.6 AIC · ⊞ 5.8K ·

  • expires on Jun 26, 2026, 9:14 PM UTC-08:00

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

cookieIssue Monster Loves Cookies!sergo

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions