Safeguard
Developer Tools•

How do you write a custom Go analyzer and test it with analysistest?

Define a Go analysis.Analyzer with a Run function, report diagnostics, and test it with analysistest using want comments in testdata files. Steps shown.

Safeguard Security Team
3 min read

To write a custom Go check, declare an analysis.Analyzer value with a name, documentation and a Run function that inspects a package and reports diagnostics. Test it with the analysistest package, which runs your analyzer on testdata files and checks the results against want comments in the code.

Definitions

The Go analysis package describes an analysis as a variable of type Analyzer. Its fields include Name, Doc, Run, and Requires, a list of other analyzers whose results yours depends on. An analysis driver is a program, such as vet, that runs a set of analyzers and prints what they report. Your analyzer is a package that a driver imports.

Skeleton

package nosleep

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

var Analyzer = &analysis.Analyzer{
    Name: "nosleep",
    Doc:  "report calls to time.Sleep outside test files",
    Run:  run,
}

func run(pass *analysis.Pass) (interface{}, error) {
    // walk pass.Files, inspect call expressions,
    // and call pass.Reportf(pos, "message") on a match
    return nil, nil
}

The body is where your logic lives. The structure is the part worth copying: a named, documented variable and a single Run function.

Steps

  1. Decide the one pattern to report and write it in the Doc string.
  2. Create the Analyzer value and the run function.
  3. Use the Requires field if you rely on another analyzer's output, so work is not repeated.
  4. Report with a position and a message a developer can act on.
  5. Create a testdata directory with Go files that contain both flagged and safe code.
  6. Add a test that calls analysistest.Run on that directory.
  7. Run the analyzer through a driver on your real packages.

How want comments work

The analysistest package runs an analyzer on a package of testdata files and checks that it reported all the expected diagnostics and facts, and no more. Expectations are written as want comments in the input code, on the line where a report should appear. That gives you a useful property: a line without a want comment is an assertion that nothing is reported there. Safe code in your testdata is therefore a test for false positives without any extra work.

Layout of a test case

File contentWhat it proves
Flagged call with a want commentThe analyzer finds the pattern
Similar safe call with no commentThe analyzer does not over-report
Call inside a helper or closureNested cases are reached
Aliased importWhether you chose to support it

Where to run it

Because analyzers are packages, a team can bundle several into one small command and run it in CI. Keep that command in version control with its testdata so a rule change is reviewed like any other change.

Quick check

What is the difference between an analyzer and a driver?

The analyzer holds the logic for one check. The driver is the program that loads packages, runs a set of analyzers and prints their diagnostics.

How do I assert that nothing is reported?

Leave the want comment off the line. analysistest checks for diagnostics that were expected and for any that were not.

Safeguard's scanning covers dependencies and SBOMs, and its reachability analysis helps show which findings your code can actually call. You can run a free scan on a repository.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.