Cleaning godoc doc-links
A Go doc comment can use godoc’s doc-link
syntax — [Gadget], [Order.CustName], reference-style [text]: url lines.
Those render as live links in pkg.go.dev, but carried verbatim into a spec
title / description they read as bracket noise, and the bracketed Go
identifier is rarely the name the schema is actually exposed under.
CleanGoDoc tidies that up. With the option on, godoc doc-link brackets are
removed and — when a link resolves to a scanned schema — the span is recomposed
to the name that schema is exposed under, so the prose stays true to the
generated definitions. It applies only to godoc-derived prose; an
author-written
swagger:title / swagger:description
override is deliberate text and is never touched.
Each pane below pairs the annotated Go (left) with the exact fragment the scanner
emits (right), from the test-covered
docs/examples/shaping/godoclinks
package.
What gets cleaned
This model — its doc comment and its fields — is dense with doc-link syntax: a self-reference, links to other models, a pointer, a cross-package link, an unknown identifier, ordinary brackets, and a reference-definition line:
// Widget is the primary [Gadget] holder and references [Order.CustName].
//
// More detail mentions a [*Gadget] pointer, a [inventory.Ledger], and an
// unknown [Sprocket].
//
// swagger:model gizmo
type Widget struct {
// Holder points at the [Gadget] that owns this widget.
Holder string `json:"holder"`
// Ledger is the cross-package [inventory.Ledger] reference.
Ledger *inventory.Ledger `json:"ledger"`
// Index is element [0] in the [see notes] list; the [id] stays bare.
Index int `json:"index"`
// Spec points at [Gadget]; the reference-definition line below is godoc
// link plumbing that carries no prose.
//
// [the spec]: https://example.com/spec
Spec string `json:"spec"`
}Full source: docs/examples/shaping/godoclinks/godoclinks.go
Scanned with CleanGoDoc off the godoc is emitted verbatim; on, every doc-link
is resolved or humanized and the reference-definition line is dropped:
{
"description": "More detail mentions a [*Gadget] pointer, a [inventory.Ledger], and an\nunknown [Sprocket].",
"type": "object",
"title": "Widget is the primary [Gadget] holder and references [Order.CustName].",
"properties": {
"holder": {
"description": "Holder points at the [Gadget] that owns this widget.",
"type": "string",
"x-go-name": "Holder"
},
"index": {
"description": "Index is element [0] in the [see notes] list; the [id] stays bare.",
"type": "integer",
"format": "int64",
"x-go-name": "Index"
},
"ledger": {
"$ref": "#/definitions/Ledger"
},
"spec": {
"description": "Spec points at [Gadget]; the reference-definition line below is godoc\nlink plumbing that carries no prose.\n\n[the spec]: https://example.com/spec",
"type": "string",
"x-go-name": "Spec"
}
},
"x-go-name": "Widget",
"x-go-package": "github.com/go-openapi/codescan/docs/examples/shaping/godoclinks"
}
Full source: docs/examples/shaping/godoclinks/testdata/gizmo_off.json
{
"description": "More detail mentions a Gadget pointer, a Ledger, and an\nunknown sprocket.",
"type": "object",
"title": "Gizmo is the primary Gadget holder and references Order.customer_name.",
"properties": {
"holder": {
"description": "Holder points at the Gadget that owns this widget.",
"type": "string",
"x-go-name": "Holder"
},
"index": {
"description": "Index is element [0] in the [see notes] list; the [id] stays bare.",
"type": "integer",
"format": "int64",
"x-go-name": "Index"
},
"ledger": {
"$ref": "#/definitions/Ledger"
},
"spec": {
"description": "Spec points at Gadget; the reference-definition line below is godoc\nlink plumbing that carries no prose.",
"type": "string",
"x-go-name": "Spec"
}
},
"x-go-name": "Widget",
"x-go-package": "github.com/go-openapi/codescan/docs/examples/shaping/godoclinks"
}
Full source: docs/examples/shaping/godoclinks/testdata/gizmo_on.json
Reading the cleaned pane:
- Links recompose to the exposed name.
[Gadget]→Gadget(no override, so its Go name); the[Order.CustName]member link →Order.customer_name(the model name plus the field’sjsonname); and the leading self-nameWidget→Gizmo, because the model is published asswagger:model gizmo(restored to sentence case). A cross-package[inventory.Ledger]resolves through the file’s imports toLedger. - Unresolved links are humanized.
[Sprocket]names no scanned model, so it becomes the plain wordsprocketrather than a dangling bracket. - Reference-definition lines are dropped. The
[the spec]: https://…line on thespecfield is link plumbing carrying no prose, so the whole line is removed.
Info
It recomposes to the final exposed name. The substitution runs after codescan resolves definition names, so a link to a model that gets renamed to deconflict a collision points at the renamed definition, not the original Go identifier.
Conservative by design
Only a genuine doc-link is rewritten — a dotted chain ([pkg.Type]) or an
uppercase-led identifier ([Widget]). Ordinary prose brackets are left exactly
as written, as the index field above shows: [0], [see notes] and the
bare-lowercase [id] all survive untouched.
CleanGoDoc is opt-in and defaults to off — with it off, output is
byte-identical to before, so existing specs never shift under you.
What’s next
- Overriding titles & descriptions — replace the godoc text outright (overrides are never cleaned).
- Resolving
$refname conflicts — the exposed-name resolution that doc-links recompose to.