Map

go/analysis Framework

Wiki conceptgolangstatic-analysistooling โ†ณ show in map Markdown
title
go/analysis Framework
type
concept
summary
Standard Go framework for building static analyzers that integrate with go vet and golangci-lint
tags
golang, static-analysis, tooling
created
2026-04-15
updated
2026-04-15

The golang.org/x/tools/go/analysis package is Go's standard framework for writing static analyzers. Every analyzer built with it can run standalone, integrate with go vet, or plug into golangci-lint.

Structure

An analyzer is a struct with:

var Analyzer = &analysis.Analyzer{
    Name:     "mycheck",
    Doc:      "checks for X",
    Requires: []*analysis.Analyzer{inspect.Analyzer},
    Run:      run,
}

The Requires field declares dependencies on other analyzers. The inspect.Analyzer is almost always required โ€” it provides optimized AST traversal.

The Run function receives an analysis.Pass containing:

  • Fset โ€” file set for position information
  • Files โ€” parsed AST of the package
  • TypesInfo โ€” type checker results
  • ResultOf โ€” results from required analyzers
  • Report โ€” function to emit diagnostics

AST walking

Rather than walking every node, use the inspector from inspect.Analyzer:

inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
nodeFilter := []ast.Node{(*ast.CallExpr)(nil)}
inspect.Preorder(nodeFilter, func(n ast.Node) {
    call := n.(*ast.CallExpr)
    // analyze the call
})

The filter makes traversal efficient โ€” you only visit node types you care about.

Type information

The pass.TypesInfo field gives access to Go's type checker results:

  • TypesInfo.Types[expr] โ€” type of any expression
  • TypesInfo.Uses[ident] โ€” what a name refers to
  • TypesInfo.Defs[ident] โ€” what a name defines
  • TypesInfo.ObjectOf(ident) โ€” the types.Object for an identifier

The types.Object is key for tracking variables. Two identifiers pointing to the same variable share the same object, so pointer equality works even across shadowing.

Testing

The analysistest package makes testing straightforward:

func TestMyCheck(t *testing.T) {
    testdata := analysistest.TestData()
    analysistest.Run(t, testdata, mycheck.Analyzer, "mypackage")
}

Test files use // want comments for expected diagnostics:

x := badThing() // want "badThing is deprecated"

Running

Standalone:

func main() {
    singlechecker.Main(mycheck.Analyzer)
}

With go vet (requires registration in the Go toolchain).

With golangci-lint: add to .golangci.yml under linters-settings.

Examples

  • transactioncheck โ€” detects database operations leaking outside transactions
  • errcheck โ€” detects unchecked errors
  • staticcheck โ€” comprehensive static analysis suite
  • nilness โ€” detects redundant nil checks and guaranteed nil dereferences