go-yze Documentation

go-yze (package goyze) is the Go analysis runtime behind gomatic’s yze analyzer suite. It registers go/analysis analyzers, runs them over package patterns through a pluggable driver, and normalizes every finding into a lean, stickler-compatible JSON Report — with optional mechanical fixing and post-fix verification.

Install

go get github.com/gomatic/go-yze

Concepts

The library is the seam between analyzers (producers of findings) and the stickler runner (consumer of findings). Its pipeline is: register → drive → collect → (fix → verify).

Usage

Run analyzers and emit a report

Run validates registrations, drives them, and returns a normalized Report. Consumers supply their own analyzers (the yze suite wires in errconst, ptrparam, and the rest):

package main

import (
	"fmt"
	"os"

	goyze "github.com/gomatic/go-yze"
	"golang.org/x/tools/go/analysis"
)

func main() {
	// myAnalyzer is any *analysis.Analyzer — from the yze suite or your own.
	regs := []goyze.Registration{
		{
			Analyzer:   myAnalyzer,
			Name:       "errconst",
			URL:        "https://gomatic.github.io/docs.yze/errconst/",
			Categories: []goyze.Category{"errors"},
		},
	}

	report, err := goyze.Run(goyze.CheckerDriver, regs, []goyze.Pattern{"./..."})
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	out, err := goyze.MarshalReport(report)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	fmt.Println(string(out))
}

var myAnalyzer *analysis.Analyzer // supplied by the caller

Run fails fast: an invalid Registration returns ErrMissingName/ErrMissingAnalyzer before the driver runs, a driver failure is wrapped as ErrDriver, a load failure as ErrLoadPackages, and an analyzer whose Run errored as ErrAnalyzer — each matchable with errors.Is.

Configure analyzer settings

ApplyConfig sets per-analyzer flags before a run. It is keyed by analyzer name, then by flag name; unknown analyzer names are ignored (a config may target a larger suite than is present), an unknown flag is ErrUnknownSetting, and a value a known flag rejects is ErrInvalidSettingValue:

settings := goyze.Settings{
	"errconst": {"exempt": "go-error"},
}
if err := goyze.ApplyConfig(regs, settings); err != nil {
	// errors.Is(err, goyze.ErrUnknownSetting) — flag not defined
	// errors.Is(err, goyze.ErrInvalidSettingValue) — bad value
}

Apply fixes and verify

When a Diagnostic carries Fixes, ApplyFixes merges every fix’s TextEdits per file, rewrites the bytes, reformats, and writes back — deduplicating identical edits and rejecting overlapping ones (ErrOverlappingEdits, ErrEditOutOfBounds). Reader, writer, and formatter are injected; GoFormat is the gofmt default:

var fixes []goyze.Fix
for _, d := range report.Diagnostics {
	fixes = append(fixes, d.Fixes...)
}

result, err := goyze.ApplyFixes(os.ReadFile, writeFile, goyze.GoFormat, fixes)
if err != nil {
	// errors.Is(err, goyze.ErrReadFile | ErrFormat | ErrWriteFile)
}
fmt.Printf("changed %d files, applied %d edits\n", result.FilesChanged, result.EditsApplied)

// Confirm the fixed tree still compiles (test files included).
v, err := goyze.CheckerVerifier([]goyze.Pattern{"./..."})
if err == nil && !v.Clean() {
	for _, issue := range v.Issues {
		fmt.Fprintln(os.Stderr, issue) // "file:line:col: message"
	}
}

func writeFile(path string, data []byte) error { return os.WriteFile(path, data, 0o644) }

ApplyEdits is the pure, in-memory core underneath ApplyFixes: given content and a set of byte-range TextEdits (in any order), it returns the rewritten bytes without touching disk, so edit application is testable in isolation.

Design