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 ¶
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 ¶
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 ¶
ExtractGoBlocks scans a single Markdown file and returns every ```go fenced code block it contains, in document order.
func ExtractGoBlocksFromFiles ¶
ExtractGoBlocksFromFiles extracts blocks from multiple files, in the order the files are given.
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.