📖 12 min read (~ 2500 words).

Options reference

codescan.Options is the single configuration struct passed to codescan.Run.

The zero value is a valid configuration — every flag defaults to false, every slice/map to nil, every numeric tunable to its built-in default. You set only what you need.

This page is the field-by-field catalogue, and serves the commands as much as the library: Flag gives the text to type, and Section gives the .codescan.yaml section addressing it — the key inside that section is the flag without its dash. See Setting an option for the rules those two columns follow, and for the handful of options that are nobody’s flag.

The godoc is the normative source; each field here links to the how-to guide that shows it on real input where one exists.

Note

Default is the library’s. The commands agree with it everywhere but one: -scan-models defaults to true, because a command asked for a specification and handed a package of annotated models should produce their definitions.

Note

Config Section names the .codescan.yaml section addressing the option. A dash means it has none: either the option is not a value a file can carry (a callback, a positional argument), or it names a path, which is settable on the command line only. See what a file may not set.

Note

codescan never writes to stdout or stderr. Every scan-time observation — a dropped construct, a rename, a prune — flows through the OnDiagnostic callback. See Diagnostics & observability below.

Inputs & scope

What gets loaded and which packages and types are in play. See Scope & discovery.

OptionTypeDefaultFlagConfig SectionEffect
Packages[]stringnil(positional)Package patterns to scan (e.g. ./...), resolved relative to WorkDir.
WorkDirstring"" (cwd)-workdirWorking directory the package patterns and module resolution are rooted at. Command line only: see what a file may not set.
BuildTagsstring""-build-tagsscanGo build tags to activate while loading, so tag-guarded source is scanned. See Build tags.
Include[]stringnil-includescanAllow-list of package path patterns; when non-empty only matching packages are scanned. See Scoping the scan.
Exclude[]stringnil-excludescanDeny-list of package path patterns, applied after Include. See Scoping the scan.
IncludeTags[]stringnil-include-tagsscanAllow-list filtering routes/operations by their swagger tags.
ExcludeTags[]stringnil-exclude-tagsscanDeny-list filtering routes/operations by their swagger tags.
ExcludeDepsboolfalse-exclude-depsscanSkip types reached through module dependencies, keeping the scan to first-party packages.
ScanModelsboolfalse-scan-modelsemitAlso emit a definition for every swagger:model type, not just route-reachable ones. See When the scanner emits a type.
PruneUnusedModelsboolfalse-prune-unused-modelsemitWith ScanModels, drop discovered definitions not transitively reachable from a path, shared response/parameter, or InputSpec root. Runs before name reduction; InputSpec definitions are pinned. No-op without ScanModels. See Pruning unused models.
InputSpec*spec.Swaggernil-input (genspec)documentBase document to overlay scanned discoveries onto; its definitions are pinned and seed pruning roots. See Overlaying a spec.

Loading & the go environment

Where the package graph comes from, and which platform it is built for. These change the emitted spec the way BuildTags does — by deciding which files each package is made of — or change what the scan needs in order to run at all.

They are options rather than inherited process state so that a scan is reproducible: a value picked up from whatever shell started it is easy to apply on one code path and forget on another.

Which loader, and why

Three ways to get a package graph. The table below catalogues the fields; this is how to choose between them.

Standard loader — the default. Loads your code with the Go toolchain, through golang.org/x/tools/go/packages. Maintained by the Go team, and the reference for how patterns and imports resolve: where either of the others disagrees with it, the other one is wrong. Requires Go installed, and uses the build cache.

Pure-Go loader (ToolchainFreeLoader) — loads your code with codescan’s own reimplementation. Cuts memory by roughly 45%, and needs no go command and no subprocess. It still reads GOROOT/src for the standard library, so it wants a Go installation — just not a runnable toolchain. Modules only. It uses no build cache, so cold costs what warm costs: about level with the standard loader on a warm cache, roughly 30% faster on a cold one, and the only choice whose cost does not depend on cache state. Usually the right pick for CI.

Compiled dependencies (CompiledDependencies) — the standard loader taking dependency types from the compiler’s export data instead of reading their source. It produces the same document either way — whatever the spec needs out of a dependency is read at the moment it is needed — and on a warm build cache it is the fastest by a wide margin, and several times smaller.

It must compile the dependency closure rather than type-check it, so on a cold cache it is an order of magnitude slower and writes a large build cache. Reach for it where the cache is warm by construction — your own machine, a watch loop, a pipeline that restores its cache — and leave it off where a clean checkout is the norm, as a CI runner usually is. Code that does not compile is not a reason to avoid it: such a load is retried from source automatically.

Two further options drop the GOROOT requirement altogether, for environments with no Go installation at all — a WASI guest, a browser. StubStdlib synthesizes the standard library, and pays for the reach in fidelity: a fabricated type has the right name and no structure. ExportData serves dependencies from a blob you prepare in advance, and pays in preparation instead — the types are the compiler’s own, but the blob is only valid for the toolchain that produced it, and a package it does not cover falls back to source and then to synthesis.

Note

The percentages are indicative, not a promise: the balance moves with the size of the tree being scanned, and on a small one the pure-Go loader is slower warm than the standard loader. The figures, the corpora they were taken on and the method are in internal/benchmarks, whose harness also takes an extra corpus of your own to measure alongside them.

OptionTypeDefaultFlagSectionEffect
GOOS / GOARCHstring"" (this machine)-goos / -goarchgoThe platform the scanned code is built for. //go:build lines and _linux.go / _amd64.go filename suffixes resolve against them, so they select which files a package is made of.
GOFLAGSstring"" (process env)-goflagsgoDefault go command flags, e.g. -tags=integration. Flags given through BuildTags win, as they do for the go command.
GOWORKstring"" (search upwards)-goworkgoWorkspace selection: off disables it, a path names a go.work. Inside a workspace a sibling module resolves to the copy being worked on rather than to the module cache — miss that and its types are read stale, or synthesized empty.
GOEXPERIMENTstring"" (process env)-goexperimentgoToolchain experiments, e.g. jsonv2; each contributes a goexperiment.<name> build tag.
ToolchainFreeLoaderboolfalse-loader=ownloadResolve the package graph with codescan’s own loader instead of golang.org/x/tools/go/packages. Same job and, across the fixture corpus, the same spec; it differs in needing no installed toolchain and no subprocess, since it never runs go list. Experimental.
FSfs.FSnilRead source through a virtual filesystem — an in-memory tree, an uploaded archive, an embed.FS — instead of the real one. Implies ToolchainFreeLoader, since go list can only read the real filesystem. FS is the whole world the scan can read: dependencies and GOROOT come through it too, absolute paths map by dropping the leading separator, and anything unreachable is synthesized — a valid but quietly thinner spec, announced by scan.synthesized-import and scan.degraded-load. Experimental.
StubStdlibboolfalse-stub-stdlibloadSynthesize the standard library from the names the code selects, rather than reading GOROOT. Toolchain-free loader only. Identity recognition still works (time.Time, json.RawMessage are matched on package and name), but a synthesized type has no fields and no method set — so json.RawMessage stops rendering as a byte array and nothing is seen to implement encoding.TextMarshaler. Trades fidelity for reach, quietly; prefer a full graph where GOROOT is available. Experimental.
ExportDatafs.FSnil-export-data (genspec-wasi)Serve dependencies from pre-computed export data (one <import path>.export file per package) under the toolchain-free loader. Unlike StubStdlib this costs no fidelity, the types being the ones the compiler computed — but it is valid only for the toolchain that produced it, and an uncovered package falls back to source, then to synthesis. The module under scan is never read this way, and neither is a dependency whose source carries annotations or one the spec later needs a declaration from. Experimental.
CompiledDependenciesboolfalse-compiled-dependenciesloadTake dependency types from the compiler’s export data instead of reading every dependency from source, under the go/packages loader. It costs no meaning: a dependency whose source carries annotations is read back after the load, and one that merely declares a type the spec carries is read at the lookup that wants it — so a swagger:strfmt written in a library still counts, and a model declared in an unannotated dependency keeps its doc comment and its fields. Set it for cost alone, and only where the build cache is warm: it is markedly faster warm and markedly slower cold, since go list -export compiles the closure before it can read it. A closure that does not compile is handled either way — the load falls back to source and raises scan.compiled-dependencies.
Note

The virtual-filesystem and export-data options exist to make a scan possible where no Go toolchain is present: they let codescan run compiled to WebAssembly. See the Playground.

Names & references

How definitions are named and how $refs render. See Names & $refs.

OptionTypeDefaultFlagSectionEffect
NameFromTags[]stringnil (⇒ ["json"])-name-from-tagsemitOrdered struct-tag types a property/parameter/header name is derived from; first that supplies a name wins. Explicit empty slice ⇒ Go field name. Only the name — json encoding directives (-, ,omitempty, ,string) always come from json. See Naming from struct tags.
SkipJSONifyInterfaceMethodsboolfalse-skip-jsonify-interface-methodsemitEmit interface-method property names verbatim (ID, CreatedAt) instead of auto-jsonifying them (id, createdAt). Only affects interface methods; struct fields and swagger:name overrides are unchanged. See Interface-method property names.
RefAliasesboolfalse-ref-aliasesemitRender Go type aliases as a first-class $ref (via swagger:model) instead of expanding them inline. See Alias rendering.
TransparentAliasesboolfalse-transparent-aliasesemitMake aliases fully transparent — never creating a definition. See Alias rendering.
DefaultAllOfForEmbedsboolfalse-default-all-of-for-embedsemitRender a plain (untagged, unnamed) struct embed as an allOf member — a $ref for a model embed, an inline member otherwise — with the embedding struct’s own fields in a sibling member, instead of inlining promoted properties. json-named embeds, swagger:allOf embeds, and interface embeds are unaffected. See Composing embeds with allOf.
NameConcatBudgetfloat640 (⇒ 0.65)-name-concat-budgetemitReadability cutoff [0,1] for the package-segment concatenation that deconflicts colliding definition names; lower scores are more readable. A group whose best concat scores above the budget is a candidate for the hierarchical fallback. See Resolving $ref name conflicts.
EmitHierarchicalNamesboolfalse-emit-hierarchical-namesemitFor the rare collision group whose best flat concat exceeds NameConcatBudget, emit nested container definitions (#/definitions/<pkg>/<Name>) instead of a long flat concat, with an explanatory diagnostic. The always-correct flat concat is the default. See Resolving $ref name conflicts.
EmitRefSiblingsboolfalse-emit-ref-siblingsemitEmit a $ref’d field’s description and vendor extensions as direct $ref siblings ({$ref, description, x-*}) instead of an allOf wrap. Validations/externalDocs still force a compound. See Descriptions beside a $ref.
SkipAllOfCompoundingboolfalse-skip-all-of-compoundingemitNever emit an allOf compound for a $ref’d field. Validations/externalDocs are dropped (description/extensions too, unless EmitRefSiblings keeps them as siblings); each drop raises a diagnostic. required is unaffected. See Descriptions beside a $ref.
DescWithRefboolfalseDeprecated — prefer EmitRefSiblings. In the description-only case, wrap the $ref in a single-arm allOf to preserve the description (strict draft-4 shape). No-op when EmitRefSiblings is set. See Descriptions beside a $ref.

Titles & descriptions

The human-readable text the spec carries. See Titles & descriptions.

OptionTypeDefaultFlagSectionEffect
SingleLineCommentAsDescriptionboolfalse-single-line-comment-as-descriptionemitRoute every single-line doc comment to description, never to title/summary (the first-sentence convention otherwise applies). Multi-line comments keep the title/description split. See Single-line comments as descriptions.
AfterDeclCommentsboolfalse-after-decl-commentsemitLet swagger annotations live inside a struct body or as a trailing comment, in addition to the doc comment above the declaration, so the godoc stays clean. v0.36 scope: type declarations (struct inside-body + alias trailing comment). See Keeping annotations out of the godoc.
CleanGoDocboolfalse-clean-go-docemitStrip godoc doc-link brackets from generated title/description (humanizing unresolved ones, dropping reference-definition lines, recomposing resolved links to each schema’s exposed name). Applies only to godoc-derived prose; overrides are untouched. See Cleaning godoc doc-links.

Field types, formats & extensions

How an individual property renders. See Field types & formats.

OptionTypeDefaultFlagSectionEffect
SetXNullableForPointersboolfalse-set-x-nullable-for-pointersemitEmit x-nullable: true on pointer-typed fields. See Nullable pointers.
SkipExtensionsboolfalse-skip-extensionsemitSuppress all x-go-* vendor extensions in the output. See Vendor extensions.
EmitXGoTypeboolfalse-emit-x-go-typeemitStamp an x-go-type extension (fully-qualified originating Go type) on every emitted definition, for round-tripping a spec back to its Go types. Suppressed under SkipExtensions. See Vendor extensions.
SkipEnumDescriptionsboolfalse-skip-enum-descriptionsemitKeep the per-enum-value const-name mapping (from swagger:enum) out of the description, exposing it only via the x-go-enum-desc extension. Suppressed entirely under SkipExtensions.

Diagnostics & observability

Channels for what the scan observed; these do not change the output spec.

OptionTypeDefaultFlagSectionEffect
OnDiagnosticfunc(Diagnostic)nilInvoked once per diagnostic in source order (parser warnings, validation failures, prunes, renames). Diagnostics never block the build — invalid constructs are dropped from the spec while their explanation flows here. The only output channel. Experimental while LSP integration matures.
OnProvenancefunc(Provenance)nilInvoked once per anchor node in the produced spec, carrying its JSON pointer and the source position of the Go construct that produced it. Never blocks the build. Experimental while LSP/TUI integration matures.
DebugboolfalseDeprecated, ignored. The legacy stderr debug logger was retired; wire OnDiagnostic instead. Retained for API compatibility.

The two callbacks are how the commands report: genspec renders diagnostics on standard error, and genspec-wasi -format=json carries both in its envelope for a program to read.

See also

  • Setting an option — the flag and key rules the two middle columns follow, and which spelling wins.
  • Annotations — the swagger:* vocabulary the scanner reads from comments.
  • Keyword reference — the keyword: value forms inside annotation bodies.
  • Shaping the output — task-oriented how-tos that put these options to work on real input.