Week 14 - go/ast, go/parser, go/types: Static Analysis¶
14.1 Conceptual Core¶
- The
go/astpackage represents Go source as a syntax tree. Thego/parserpackage parses source files intoast.Files. Thego/typespackage performs type checking and resolves identifiers to declarations. - The triad (
ast+parser+types) is the foundation for every serious Go tool:gofmt,goimports,gopls,golangci-lint,staticcheck,mockgen,sqlc. golang.org/x/tools/go/packagesis the modern entry point for loading a Go program for analysis. It handles modules, build tags, and CGO transparently. Use this; do not callparser.ParseFiledirectly except for single-file tools.golang.org/x/tools/go/analysisis the framework for writing analyzers-small, composable passes consumed bygo vet,golangci-lint, and standalone drivers.
14.2 Mechanical Detail¶
- Loading a package:
The
cfg := &packages.Config{Mode: packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo} pkgs, _ := packages.Load(cfg, "./...")Modeflags determine cost; load only what you need. - Walking AST:
- Type information:
pkg.TypesInfo.Types[expr]→ the type of an expression.pkg.TypesInfo.Defs[ident]/Uses[ident]→ the object an identifier defines or uses.pkg.TypesInfo.ObjectOf(ident)→ the resolved object (can be a*types.Var,*types.Func,*types.TypeName, etc.).- Writing an analyzer:
Compile as a binary using
var Analyzer = &analysis.Analyzer{ Name: "noprintln", Doc: "disallow fmt.Println in production code", Run: func(pass *analysis.Pass) (any, error) { /* walk pass.Files */ }, }unitcheckeror load via thegolangci-lintplugin system. - Common pitfalls: position information (
token.Pos) is meaningless without thetoken.FileSetit was created from; always pass them together. Comment groups are a separate field onast.File, not attached to AST nodes by default-ast.CommentMapbridges them.
14.3 Lab-"Build a Custom Analyzer"¶
Write an analyzer that flags:
1. context.Background() calls outside main and *_test.go files.
2. time.After inside a select body (the classic timer-leak pattern).
3. Goroutines launched with closures capturing a context.Context parameter named ctx of an enclosing HTTP handler (heuristic; document the false-positive risk).
Wire as a unitchecker binary. Run on a real codebase and triage findings. Document each false positive in ANALYZER_NOTES.md.
14.4 Idiomatic & golangci-lint Drill¶
- Read
staticcheck's source for two of its analyzers (e.g.,SA1015andSA4006). Internalize the analyzer-author idioms.
14.5 Production Hardening Slice¶
- Publish your analyzer as a module. Add a
golangci-lintcustomplugin entry so it runs alongside the standard suite. CI now enforces your project's idioms automatically.