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
- Decide the one pattern to report and write it in the Doc string.
- Create the Analyzer value and the run function.
- Use the Requires field if you rely on another analyzer's output, so work is not repeated.
- Report with a position and a message a developer can act on.
- Create a testdata directory with Go files that contain both flagged and safe code.
- Add a test that calls analysistest.Run on that directory.
- 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 content | What it proves |
|---|---|
| Flagged call with a want comment | The analyzer finds the pattern |
| Similar safe call with no comment | The analyzer does not over-report |
| Call inside a helper or closure | Nested cases are reached |
| Aliased import | Whether 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.