logo
Kurashizu Blogwhere ideas flow
  • Home
  • News
  • Blog
  • About
  • Home
  • News
  • Blog
  • About

© 2026 Kurashizu. All rights reserved.

Admin·New Post·Service Status
Powered by Cloudflare·01.11.26
← Back to news

Go Analysis Framework: modular static analysis by go team

Original article
pkg.go.dev|124 points|12 comments|by AbuAssar|Jul 26, 2026

Go Analysis Framework: Modular Static Analysis by the Go Team

The analysis package, located at golang.org/x/tools/go/analysis, provides a standardized framework for building modular static analysis tools for the Go language.

📦 Package Metadata

AttributeDetail
Current Versionv0.48.0 (Note: Not the latest version in module)
Release DateJuly 9, 2026
LicenseBSD-3-Clause (Redistributable)
AdoptionImported by 6,564 projects
Repositorycs.opensource.google/go/x/tools

📖 Overview

The analysis package acts as the bridge between a modular static analysis (the logic) and an analysis driver (the program that executes that logic).

Background Concepts

At its core, a static analysis is a function that examines a Go package to identify diagnostics—usually bugs or stylistic errors—and may generate suggested fixes or "facts" about the code.

What is a "Checker"? An analysis specifically designed to find mistakes is informally known as a checker. For instance, the printf checker validates that format strings in fmt.Printf are used correctly.

What is "Modular" Analysis? A modular analysis processes one package at a time but can persist information from lower-level packages to be utilized when analyzing higher-level packages. This is conceptually similar to how separate compilation works in a traditional toolchain.

Example of Modularity: The printf checker is modular. If it detects that log.Fatalf internally calls fmt.Printf, it records this as a fact. Later, when analyzing other packages that call log.Fatalf, it knows to apply the same format string checks.

Ecosystem Integration

By utilizing a shared interface, checkers can be integrated into various environments:

  • CLI Tools: Such as go vet.
  • Editors/IDEs: Real-time linting in VS Code or GoLand.
  • Build Systems: Integration with go build, Bazel, or Buck.
  • Code Review & Indexing: Tools like SourceGraph or godoc.
  • CI/CD: Batch pipelines for massive monorepos.

🛠 The Analyzer

An Analyzer is a static description of an analysis function. It encapsulates the logic, documentation, configuration flags, and dependencies.

Defining an Analyzer

Users typically define an analysis by declaring a constant variable of type analysis.Analyzer.

package unusedresult

import "golang.org/x/tools/go/analysis"

var Analyzer = &analysis.Analyzer{
    Name: "unusedresult",
    Doc:  "check for unused results of calls to some functions",
    Run:  run,
    // ... other fields
}

The Analyzer Structure

The Analyzer struct contains the following fields:

FieldTypeDescription
NamestringThe unique identifier of the analysis.
DocstringA brief summary and optional detailed explanation.
Flagsflag.FlagSetGlobal flags to control behavior.
Runfunc(*Pass) (interface{}, error)The core logic executed per package.
RunDespiteErrorsboolIf true, runs even on ill-typed code.
ResultTypereflect.TypeThe type of value returned for other analyzers.
Requires[]*AnalyzerA list of dependencies that must run first.
FactTypes[]FactDefinitions for modular facts.

Key Technical Details

  • Flags: Unlike vet, flags aren't hardcoded into the CLI. The driver decides how to expose them (e.g., as -f, -analysis.f, or via a GUI/config file).
  • Dependencies: The Requires field creates a dependency graph. To ensure the driver can execute these, the graph must be a Directed Acyclic Graph (DAG), represented mathematically as: Graph G=(V,E) where E contains no cycles.\text{Graph } G = (V, E) \text{ where } E \text{ contains no cycles.}Graph G=(V,E) where E contains no cycles.
  • Validation: The analysis.Validate function performs sanity checks:
    • Verify the Requires graph is acyclic.
    • Ensure FactTypes and ResultType are unique.

🚀 The Driver

The driver is the execution engine (e.g., vet). It imports the necessary Analyzers and manages their lifecycle.

Adding analyzers to a driver:

import (
    "unusedresult"
    "nilness"
    "printf"
)

var analyses = []*analysis.Analyzer{
    unusedresult.Analyzer,
    nilness.Analyzer,
    printf.Analyzer,
}

The driver is responsible for parsing the Doc strings to provide help menus and mapping the Flags to the user's input method.


📦 The Pass

A Pass represents a single unit of work: one Analyzer applied to one package. It provides the Run function with the necessary context and a way to report findings.

Pass Structure

type Pass struct {
    Fset         *token.FileSet
    Files        []*ast.File
    OtherFiles   []string
    IgnoredFiles []string
    Pkg          *types.Package
    TypesInfo    *types.Info
    ResultOf     map[*Analyzer]interface{}
    Report       func(Diagnostic)
}

Workflow Visualization

Summary of Pass Fields

  • Code Access: Fset and Files provide the AST (Abstract Syntax Tree).
  • Type Information: Pkg and TypesInfo allow for deep type analysis.
  • Inter-Analyzer Communication: ResultOf allows an analyzer to access the output of its dependencies.
  • Feedback Loop: The Report function is used to send diagnostics back to the driver.

Static analysis is just a simple script →\rightarrow→ Static analysis is a sophisticated, modular framework.

Analysis Framework Concept