Help us shape the future of Go! Take the 2026 Go Developer Survey

sourcegate

package
v3.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 30, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package sourcegate is the scaffolding the gates that judge this repository's own source share above the load: the command line they read, the exit code they end on, the declaration table they are held to, and the name a finding gives the function it sits in.

Five gates are built on it, each asking its own question of the program cmd/internal/goprogram loads. cmd/audit_test_closed_servers holds every httptest server to being closed only once its address is no longer used, cmd/audit_sdk_context holds every call into client-go to the caller's context, cmd/audit_dead_consts holds every unexported constant to something that reads it, cmd/audit_tenancy holds the tenant policy register to the code that enforces it, and cmd/audit_i18n holds every text shown to a person directly to the message catalog. What each keeps is what makes it that gate: its load options, its scanner, its findings and their order, the words of its report and its summary, its declarations and their categories, and its binary.

What they shared they used to share as copies, written once per gate in the same words: the flag set and its two exit codes, the end of a run, the split of a run's findings into the excused and the reported, the rules that report a stale declaration and an undefined category, and the spelling of a method by its receiver. A change to any of them was a change to every gate, with one copy easy to forget, which is how two copies of a rule come to answer the same tree differently; the receiver's spelling had already begun to, one of its four copies guarding a receiver list with nothing in it and the other three indexing past its end.

The command line and the exit code

Main is a gate's main function: it hands the gate's command line the process's arguments and standard streams and ends the process through Exit, the one seam a gate's test replaces to read the code instead of ending the test binary. Run parses a command line with the three flags every gate reads, -dir, -check and -v, named after the gate and described in its own words (Usage), and starts the gate on what they say; a request for help ends the run with 0 and arguments it cannot parse with 2. cmd/audit_tenancy, which adds -compare-binaries and refuses patterns, builds the same command line with NewCommand and defines its flag on Command.Flags before parsing. cmd/audit_dead_consts still reads the process's own flag set, so its help names the binary rather than the gate, and moving it here would change what its -h prints.

Finish is the end of every run: a run that could not be made says so on standard error and exits 1, and a run that was made writes its Report and exits 1 only under -check when the report is not clean.

The declaration table

A gate that lets a function do on purpose what it otherwise reports keeps a Table of those functions, each with a Declaration giving a category and a reason. Judge holds a run's findings against it and returns the Verdict a report embeds: what the table excused, what it did not, and the two ways the table itself fails, an entry that excused nothing in a package the run scanned and an entry whose category nobody defined. cmd/audit_test_closed_servers and cmd/audit_sdk_context are held to one this way, and so is cmd/audit_i18n, which hands it only the findings a declaration may answer and reports the rest as they stand. cmd/audit_dead_consts keys its table by constant rather than by function and passes over a key that names no package, and cmd/audit_tenancy holds its exemptions to rules of its own, so both keep theirs.

The name of the place a finding sits

FuncDeclName spells a declaration the way its file does, `Type.Method` with the receiver's pointer and type parameters taken off, which is the function half of a declaration key and of what a finding prints. All five gates name functions by it: cmd/audit_dead_consts adds the constant's name after it, and cmd/audit_tenancy keys by it the functions its register names as sites. IdentsAsExprs is the one-line widening two of their walks need to read a declaration and an assignment alike.

Index

Constants

This section is empty.

Variables

View Source
var Exit = os.Exit

Exit ends a gate's process with the code its run decided. It is os.Exit behind a seam, so a gate's test can run the gate's main and read that code as a value rather than as the end of the test binary; a test that replaces it puts it back in its cleanup.

Functions

func Finish

func Finish(name string, report Report, err error, check, verbose bool, stdout, stderr io.Writer) int

Finish ends a gate's run with what its audit returned, and returns the process exit code.

Exit 1 covers two different things on purpose, told apart by which stream spoke: a run that could not be made at all says so on standard error, under the gate's name, and writes no report, and a run that found something says it on standard output and fails only under -check. A report is worth having without a gate, and a gate is worth having without ceremony.

func FuncDeclName

func FuncDeclName(fn *ast.FuncDecl) string

FuncDeclName spells a function the way its file does: the bare name, or `Type.Method` with the receiver's type stripped of its pointer and of its type parameters. It is the function half of a declaration key, so a finding and the entry excusing it name the place the same way.

The receiver is read through its parentheses, as the type checker reads it: `(w (walker))` and `(w *(walker))` declare methods of walker as much as `(w walker)` does, and a key that dropped the receiver there would excuse nothing a reader wrote for it. A receiver that is not a type name once those are gone, or a receiver list with nothing in it, does not type-check, and is spelled by the method's name alone rather than guessed at.

func IdentsAsExprs

func IdentsAsExprs(names []*ast.Ident) []ast.Expr

IdentsAsExprs widens a name list so a declaration and an assignment are read by one function: the names a `var` or `const` spec declares become the left-hand side an assignment would have.

func Main

func Main(runMain func(args []string, stdout, stderr io.Writer) int)

Main is a gate's main function: it runs the gate's command line on the process's arguments, standard output and standard error, in that order, and ends the process through Exit with the code the command line decided.

func Run

func Run(name string, usage Usage, args []string, stderr io.Writer, start func(cmd *Command) int) int

Run parses a gate's command line with the three flags every gate reads and hands what it read to start, whose answer is the exit code. When parsing ends the run, a request for help or arguments it cannot parse, start is not called and the code is the one Command.Parse decided.

Types

type Command

type Command struct {
	Flags   *flag.FlagSet
	Dir     string
	Check   bool
	Verbose bool
}

Command is one gate's command line: a flag set named after the gate that writes its complaints and its help to the gate's standard error, with -dir, -check and -v defined on it.

The fields hold what Command.Parse read. A gate with a flag of its own defines it on Flags before parsing, and reads the arguments left after the flags from Flags as well.

func NewCommand

func NewCommand(name string, usage Usage, stderr io.Writer) *Command

NewCommand returns the command line of the gate called name.

The set is made with flag.ContinueOnError rather than the process's flag.CommandLine, so a test can run a gate's whole command line any number of times and read the exit code it decided, rather than the end of the test binary.

func (*Command) Parse

func (c *Command) Parse(args []string) (code int, goOn bool)

Parse parses a command line and reports whether the run goes on. When it does not, the code is what the process exits with: 0 for a request for help, which the flag package has already answered on standard error, and 2 for arguments it cannot parse, which it has already complained about there.

func (*Command) Patterns

func (c *Command) Patterns(defaults []string) []string

Patterns are the arguments left after the flags, or the gate's own defaults when there are none, so a run with no arguments audits what the gate is for.

type Counts

type Counts struct {
	Findings int `json:"findings"`
	Excused  int `json:"excused"`
	Stale    int `json:"stale_declarations"`
	Unknown  int `json:"unknown_categories"`
	Unjudged int `json:"unjudged_files"`
}

Counts are a verdict's figures, which a gate's summary embeds so a clean report says what it was clean over and a failing one how much it found.

type Declaration

type Declaration struct {
	Category string
	Reason   string
}

Declaration is one entry of a gate's declaration table: why the function it names may do what the gate otherwise reports, in a category and in words.

type Report

type Report interface {
	// Write renders the report to out, with what the declarations excused
	// when verbose.
	Write(out io.Writer, verbose bool)
	// OK reports whether the run found nothing to answer for.
	OK() bool
}

Report is what a gate's audit hands back: a report that writes itself, with what its declarations excused when the run is verbose, and says whether the run found nothing to answer for.

type Table

type Table struct {
	Declared   map[string]Declaration
	Categories map[string]string
}

Table is a gate's declaration table with the categories an entry may give, each with what it means.

An entry is keyed by the package the repository names, a colon, and the function as its file spells it (FuncDeclName), which is also how a finding names where it sits, so an entry survives an edit above the line it excuses.

func (Table) UnknownCategories

func (t Table) UnknownCategories() []string

UnknownCategories names the declarations whose category is not one of the table's, in a stable order. A category nobody defined is an excuse nobody reviewed, so such an entry is reported rather than trusted, whatever it excuses.

type Usage

type Usage struct {
	Dir     string
	Check   string
	Verbose string
}

Usage is the help text of the three flags every gate reads, in the gate's own words, since what -check refuses and what -v adds are the gate's question rather than the scaffolding's.

type Verdict

type Verdict[F any] struct {
	// Findings are the findings no declaration excuses, which fail the gate.
	Findings []F `json:"findings"`
	// Excused are the findings a declaration answers, kept so a verbose run
	// can show what the table is standing in front of.
	Excused []F `json:"excused,omitempty"`
	// Stale are the declarations that excused nothing this run.
	Stale []string `json:"stale_declarations,omitempty"`
	// Unknown are the declarations naming a category nobody defined.
	Unknown []string `json:"unknown_categories,omitempty"`
	// Unjudged are the files the load left out that could hold what the gate
	// judges, whose contents this run could not see.
	Unjudged []string `json:"unjudged_files,omitempty"`
}

Verdict is the part of a gate's report its declaration table and its load decide: the findings the table does not excuse and the ones it does, the declarations that excused nothing or give a category nobody defined, and the files the load left out that could hold what the gate looks for.

A gate's report embeds it beside a summary of its own, so the fields read as the report's, and Verdict.OK is the report's verdict.

func Judge

func Judge[F any](findings []F, key func(F) string, table Table, scanned map[string]struct{}, unjudged []string) Verdict[F]

Judge holds a run's findings against a declaration table: each one whose key the table declares is excused, every other one is reported, both in the order given, and the table is then held to what this run scanned. The unjudged files are reported sorted.

func (Verdict[F]) Counts

func (v Verdict[F]) Counts() Counts

Counts returns the verdict's figures.

func (Verdict[F]) OK

func (v Verdict[F]) OK() bool

OK reports whether the run found nothing to answer for: no finding the table does not excuse, no declaration that excused nothing or gives an undefined category, and no file the load hid.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL