doccheck

package
v0.0.26 Latest Latest
Warning

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

Go to latest
Published: Sep 24, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package doccheck extracts and validates the ```go fenced code blocks that appear in the project's Markdown documentation (docs/*.md and README.md).

Every block is classified as either:

  • a full program: the block itself starts with "package main" (or "package <name>") and is a complete, compilable Go file. These are compiled for real, as an isolated module that requires this repo via a replace directive, using `go build`.
  • a fragment: an illustrative snippet (statements, a lone declaration, a bare function signature, ...) that is not meant to compile standalone. Fragments are checked in two independent ways: 1. Syntax validity: the fragment is wrapped in a synthetic file and parsed with go/parser. This catches malformed Go (typos, unbalanced braces, bad syntax) without tripping over placeholder identifiers such as `listUsers` or `myStruct` that are illustrative, not real. 2. Symbol existence: the raw fragment text is scanned for `<pkg>.<Identifier>` references where <pkg> resolves to a known package (the root `fh` package or one of the `mw/*`/`pkg/*` packages in this module). Each such reference is checked against that package's real top-level exported identifiers (obtained via `go doc`). This catches renamed/removed exported functions, types, vars and consts. It deliberately does NOT check methods on values (e.g. `c.JSON(...)`) or locally-scoped placeholder identifiers, since that would require full type-checking and would produce spurious failures on intentionally illustrative code.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CheckFragmentSymbols

func CheckFragmentSymbols(b Block, reg PackageRegistry, st *SymbolTable) []string

CheckFragmentSymbols scans the raw fragment text for <pkg>.<Identifier> references where <pkg> is a known package short name (per reg), and verifies each Identifier exists as a top-level exported declaration in that package's real source (per st). If the same short name resolves to more than one real package (e.g. mw/httpsignature and pkg/httpsignature both declare `package httpsignature`), the reference is accepted if the identifier exists in ANY candidate - disambiguating which one a given fragment means would need full type-checking, which is out of scope (see package doc comment), so this deliberately trades a little precision for not flagging genuinely correct references as broken.

A short name that the fragment itself declares as a local variable or parameter (e.g. `admin := app.Group(...)`, later used as `admin.Get(...)`) is skipped entirely: that is a method call on a local value, not a package-qualified reference, even though it lexically collides with a real package name (e.g. mw/admin).

It returns one problem string per unresolved reference; a nil/empty slice means every checkable reference resolved (or there were none to check).

func CheckFragmentSyntax

func CheckFragmentSyntax(b Block) error

CheckFragmentSyntax verifies that a fragment is syntactically valid Go. It wraps the fragment in a synthetic file: named top-level func/method declarations are kept at package scope (Go disallows nesting them); everything else (statements, local type/const/var decls, expressions) is gathered into one synthetic function body. A segment that is a bare function *type* signature with no body (e.g. `func(c fh.Ctx) error`, used to document a handler signature) is special-cased into a type alias. A segment that is pure method-signature notation (e.g. `file.Save(dst string) error`, describing a method rather than showing runnable code) is recognized and skipped rather than forced through the parser - see isSignatureNotationSegment.

func CompileFullProgram

func CompileFullProgram(b Block, repoRoot, dir string) error

CompileFullProgram writes b.Source out as its own throwaway module (with a replace directive pointing at repoRoot) under dir and runs `go build ./...` in it. dir must already exist (e.g. a t.TempDir()).

Types

type Block

type Block struct {
	File      string // path to the markdown file, relative to repo root
	StartLine int    // 1-based line number of the “`go fence itself
	Source    string // raw code inside the fence, exactly as written
}

Block is one ```go fenced code block extracted from a Markdown file.

func ExtractGoBlocks

func ExtractGoBlocks(path string) ([]Block, error)

ExtractGoBlocks scans a single Markdown file and returns every ```go fenced code block it contains, in document order.

func ExtractGoBlocksFromFiles

func ExtractGoBlocksFromFiles(paths []string) ([]Block, error)

ExtractGoBlocksFromFiles extracts blocks from multiple files, in the order the files are given.

func (Block) Pos

func (b Block) Pos() string

Pos returns a "file:line" string for error messages.

type Kind

type Kind int

Kind classifies a Block.

const (
	// KindFullProgram: the block itself is a complete "package main" (or
	// other package) source file.
	KindFullProgram Kind = iota
	// KindFragment: an illustrative snippet, not standalone.
	KindFragment
)

func Classify

func Classify(b Block) Kind

Classify determines whether a block is a full standalone program or an illustrative fragment. A block is a full program only if its very first non-blank line is a bare `package <name>` clause - i.e. it is written as a complete Go source file.

type PackageInfo

type PackageInfo struct {
	ImportPath string
	Dir        string // absolute directory containing the package's source
}

PackageInfo records where a package short name (as used in qualified references like `fh.New` or `compress.New`) actually lives.

type PackageRegistry

type PackageRegistry map[string][]PackageInfo

PackageRegistry maps a Go package identifier (as it would be written in code, e.g. "compress") to every real package in this module that declares that identifier as its package name. It is a slice, not a single value, because short names can collide across independent packages - e.g. this module has both mw/httpsignature and pkg/httpsignature, both declaring `package httpsignature`. A qualified reference `httpsignature.Foo` is accepted if Foo exists in ANY candidate; see CheckFragmentSymbols.

func DiscoverPackages

func DiscoverPackages(repoRoot, modulePath string) (PackageRegistry, error)

DiscoverPackages walks the module rooted at repoRoot and returns every package that documentation might plausibly reference by short name: the root module package (aliased "fh") plus every package under mw/ and pkg/.

type SymbolTable

type SymbolTable struct {
	// contains filtered or unexported fields
}

SymbolTable caches the exported top-level identifiers of packages, keyed by directory.

func NewSymbolTable

func NewSymbolTable() *SymbolTable

func (*SymbolTable) Load

func (st *SymbolTable) Load(dir string) (map[string]bool, error)

Load parses every non-test .go file directly in dir and returns the set of exported top-level identifiers (funcs, types, vars, consts - including consts/vars declared inside a type's own doc grouping) using go/doc, which - unlike the `go doc` CLI's human-oriented summary - never truncates grouped declarations, so it reliably reflects every real exported symbol.

Jump to

Keyboard shortcuts

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