A phase-aware driver for Go static analysis (golang.org/x/tools/go/analysis).
Unlike multichecker, which runs all analyzers per-package with no phase ordering, phasedchecker executes analyzers in sequential phases — each phase completes for all packages before the next phase starts. This enables inter-phase communication where later phases can consume results produced by earlier ones.
- Go 1.25+
go get github.com/miyamo2/phasedcheckerpackage main
import (
"fmt"
"os"
"golang.org/x/tools/go/analysis"
"github.com/miyamo2/phasedchecker"
)
var myAnalyzer = &analysis.Analyzer{
Name: "example",
Doc: "reports something",
Run: func(pass *analysis.Pass) (any, error) {
// your analysis logic here
return nil, nil
},
}
func main() {
cfg := phasedchecker.Config{
Pipeline: phasedchecker.Pipeline{
Phases: []phasedchecker.Phase{
{
Name: "lint",
Analyzers: []*analysis.Analyzer{myAnalyzer},
},
},
},
DiagnosticPolicy: phasedchecker.DiagnosticPolicy{
DefaultSeverity: phasedchecker.SeverityWarn,
},
}
phasedchecker.Main(cfg)
}go run . ./...Phase— A named group of analyzers that run together. All analyzers in a phase complete on all packages before the next phase starts.Pipeline— An ordered sequence ofPhases executed sequentially.Config— Combines aPipelinewith aDiagnosticPolicyto control the checker's behavior.
DiagnosticPolicy maps diagnostic categories to severity levels using an ordered list of CategoryRules. The first matching rule wins. If no rule matches, DefaultSeverity is applied.
phasedchecker.DiagnosticPolicy{
Rules: []phasedchecker.CategoryRule{
{Category: "todo", Severity: phasedchecker.SeverityInfo},
{Category: "panic", Severity: phasedchecker.SeverityError},
},
DefaultSeverity: phasedchecker.SeverityWarn,
}Each Phase can have an optional AfterPhase callback that receives the *checker.Graph after the phase completes. This enables inter-phase communication — for example, Phase 1 can collect data via Action.Result values, and Phase 2 can use that data.
phasedchecker.Phase{
Name: "scan",
Analyzers: []*analysis.Analyzer{scanAnalyzer},
AfterPhase: func(graph *checker.Graph) error {
for act := range graph.All() {
if act.Analyzer == scanAnalyzer && act.Err == nil {
// extract and aggregate act.Result
}
}
return nil
},
}See the multiphase example for a complete demonstration.
| Severity | Reported | Exit Code | Pipeline |
|---|---|---|---|
SeverityInfo |
No | No effect | Continues |
SeverityWarn |
Yes (stderr) | 3 (if no errors and fix mode disabled) | Continues |
SeverityError |
Yes (stderr) | 1 | Continues |
SeverityCritical |
Yes (stderr) | 1 | Aborts at the current phase |
The binary built with phasedchecker.Main accepts the following flags:
| Flag | Default | Description |
|---|---|---|
-fix |
false |
Apply suggested fixes |
-diff |
false |
With -fix, print unified diffs instead of updating files |
-json |
false |
Emit JSON diagnostics to stdout |
-test |
true |
Include test files in analysis |
-debug |
"" |
Debug flags, any subset of "fpstv" |
-V |
— | Print version info and exit (go vet protocol) |
| Flag | Description |
|---|---|
f |
Log analysis facts to stderr |
p |
Sequential execution (disable parallelism) |
s |
Enable sanity checks |
t |
Print timing information |
v |
Verbose logging |
| Code | Meaning |
|---|---|
| 0 | Clean (no diagnostics) or JSON mode |
| 1 | Error/Critical diagnostics or internal error |
| 3 | Warnings only (no fix mode) |
The checkertest package provides testing utilities analogous to analysistest, designed for phase-based pipelines.
Run executes the pipeline against test packages and verifies that diagnostics match // want directives in source files.
func TestMyPipeline(t *testing.T) {
cfg := phasedchecker.Config{
Pipeline: phasedchecker.Pipeline{
Phases: []phasedchecker.Phase{
{Name: "lint", Analyzers: []*analysis.Analyzer{myAnalyzer}},
},
},
DiagnosticPolicy: phasedchecker.DiagnosticPolicy{
DefaultSeverity: phasedchecker.SeverityWarn,
},
}
checkertest.Run(t, "testdata", cfg, "./...")
}Like Run, but additionally applies SuggestedFixes and compares the results against .golden files.
checkertest.RunWithSuggestedFixes(t, "testdata", cfg, "./...")Place // want comments in test source files to declare expected diagnostics:
var x = 1 // want "diagnostic message regex"Multiple expectations on the same line:
var x = 1 // want "first pattern" "second pattern"Expect a diagnostic on a line offset from the comment:
// want +2 "pattern on line two below"
var x = 1For RunWithSuggestedFixes, place .golden files alongside test source files. The file should contain the expected source after fixes are applied. Both plain files and txtar archive format are supported.
The .examples/ directory contains runnable examples:
| Example | Description |
|---|---|
basic |
Single-phase naming convention checker |
multiphase |
Inter-phase communication via AfterPhase callbacks |
severity |
Per-category severity control with DiagnosticPolicy |