Skip to content

[go-fan] Go Module Review: golang.org/x/toolsΒ #38313

@github-actions

Description

@github-actions

🐹 Go Fan Report: golang.org/x/tools

Module Overview

golang.org/x/tools is the Go team's collection of supplementary tooling packages β€” most notably the go/analysis static-analysis framework, the AST inspector, the multichecker driver, and analysistest. In gh-aw it's used for exactly one purpose: building the project's suite of 23 custom linters that enforce internal code conventions, wired into a single binary via cmd/linters/main.go.

It's also our most recently updated direct dependency (repo pushed_at 2026-06-09), and the go/ast/inspector package is under active development right now β€” making this a timely review. πŸš€

Summary

  • Module: golang.org/x/tools v0.45.0 (latest)
  • Files using it: 26 non-test .go files (+ 24 test files via analysistest)
  • Role: powers all 23 go/analysis linters
  • Status: ⚠️ well-used, but three divergent traversal idioms and low autofix coverage

Current Usage in gh-aw

  • Files: 26 non-test files (one per linter + cmd/linters/main.go + shared internal/astutil)
  • Import Count: go/analysis (28), analysistest (24, tests), go/ast/inspector (23), passes/inspect (23), multichecker (1)
  • Key APIs Used: analysis.Analyzer / analysis.Pass / analysis.Diagnostic, multichecker.Main, inspector.Inspector (Preorder, WithStack, and the modern Root()/Cursor API), passes/inspect.Analyzer as a shared dependency.
The 23 linters in the suite

contextcancelnotdeferred, ctxbackground, errormessage, errstringmatch, excessivefuncparams, execcommandwithoutcontext, fileclosenotdeferred, fmterrorfnoverbs, fprintlnsprintf, jsonmarshalignoredeerror, largefunc, lenstringzero, manualmutexunlock, osexitinlibrary, ossetenvlibrary, panic-in-library-code, rawloginlib, regexpcompileinfunction, seenmapbool, sortslice, ssljson, strconvparseignorederror, tolowerequalfold, uncheckedtypeassertion

Research Findings

The go/analysis framework is the canonical, stable foundation for Go static analysis, and gh-aw uses it idiomatically (one Analyzer per rule, inspect.Analyzer as a Requires dependency, multichecker.Main as the driver, analysistest golden tests).

Recent Updates

The go/ast/inspector package is actively evolving toward the Cursor API. Recent upstream commits add Cursor.ParentEdge{Kind,Index} methods β€” the commit message explicitly notes they're "obviating astutil.IsChildOf." The Cursor model (insp.Root(), cur.Enclosing, cur.Parent, cur.Children, cur.Preorder) is the maintainers' recommended modern replacement for WithStack and manual stack handling.

Best Practices

  • One focused Analyzer per rule; declare Requires: []*analysis.Analyzer{inspect.Analyzer} and read the shared *inspector.Inspector from pass.ResultOf.
  • Prefer the Cursor API over WithStack/manual stacks for enclosing-node queries.
  • Emit analysis.SuggestedFix where a fix is mechanical β€” multichecker exposes these via its built-in -fix flag for free.

Improvement Opportunities

πŸƒ Quick Wins

  • De-duplicate inspector-extraction boilerplate. All 23 linters repeat the identical block:
    insp, ok := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
    if !ok {
        return nil, fmt.Errorf("inspect analyzer result has unexpected type %T", pass.ResultOf[inspect.Analyzer])
    }
    Extract one helper β€” e.g. astutil.Inspector(pass) (*inspector.Inspector, error) in pkg/linters/internal/astutil β€” and collapse 23 copies to a single call. Pure mechanical win, no behavior change.

✨ Feature Opportunities

  • Add SuggestedFixes (autofix) to more linters. Only 2 of 23 analyzers (ctxbackground, execcommandwithoutcontext) currently emit analysis.SuggestedFix/analysis.TextEdit. Since the suite runs through multichecker.Main, adding fixes makes them available via the built-in -fix flag and editor integrations at no extra wiring cost. Good mechanical candidates: fprintlnsprintf, tolowerequalfold, lenstringzero. Pair each with analysistest.RunWithSuggestedFixes golden tests (already the established pattern in the 2 existing fixers).

πŸ“ Best Practice Alignment

  • Standardize on the Cursor API. The suite currently mixes three idioms for the same task:

    Idiom Files Notes
    Modern insp.Root().Preorder(...) + cur.Enclosing(...) ctxbackground, execcommandwithoutcontext newest, no manual stack
    Legacy insp.WithStack(...) + manual stack []ast.Node scan regexpcompileinfunction, panic-in-library-code hand-rolled enclosing-node logic
    Classic insp.Preorder(filter, func) the other ~19 no parent/enclosing context

    In regexpcompileinfunction, the hand-written isInsideFunction(stack []ast.Node) loop is exactly what cur.Enclosing((*ast.FuncDecl)(nil), (*ast.FuncLit)(nil)) provides natively β€” migrating the two WithStack linters removes the manual push/pop boolean handling and aligns with where x/tools is heading.

πŸ”§ General Improvements

  • A short CONTRIBUTING/pkg/linters note pinning the canonical traversal idiom would stop new linters from reintroducing Preorder/WithStack divergence.
  • Centralizing the extraction helper makes any future inspect/inspector API shift a one-file change instead of a 23-file sweep.

Recommendations

  1. (Low effort, high consistency) Extract the inspector-extraction helper into internal/astutil; refactor all 23 linters to use it.
  2. (Medium effort, user-facing value) Add SuggestedFixes + RunWithSuggestedFixes tests to the mechanically-fixable linters, starting with fprintlnsprintf / tolowerequalfold / lenstringzero.
  3. (Low effort, future-proofing) Migrate the two WithStack linters to the Cursor API and document it as the canonical idiom.

Next Steps

  • Open a focused PR for recommendation rejig docsΒ #1 (mechanical, easily reviewable).
  • Prototype an autofix on one linter to validate the RunWithSuggestedFixes workflow before fanning out.

Generated by Go Fan
Module summary saved to: scratchpad/mods/golang-x-tools.md

References: Β§27265473810

Generated by 🐹 Go Fan Β· 197.9 AIC Β· βŒ– 13.6 AIC Β· ⊞ 8K Β· β—·

  • expires on Jun 11, 2026, 1:15 AM UTC-08:00

Metadata

Metadata

Labels

automationcookieIssue Monster Loves Cookies!dependenciesPull requests that update a dependency filego-fan

Type

No type
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