scanner

package
v0.36.1 Latest Latest
Warning

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

Go to latest
Published: Aug 1, 2026 License: Apache-2.0 Imports: 17 Imported by: 0

README

scanner — maintainer notes

This document is the long-form companion to the scanner package code. The source files keep godoc concise; complex invariants, design trade-offs, and known quirks live here.

The scanner package owns package loading and entity discovery. It turns a set of Go package patterns into a ScanCtx that exposes the classified per-decl inventory (meta, routes, operations, models, parameters, responses) consumed by the builder layer.


Table of contents

  • §optionsOptions.DescWithRef shape and rationale
  • §descwithref — the description-only-decoration $ref shape and why it has a flag
  • §diagnosticsOnDiagnostic contract and experimental-API caveat
  • §prunePruneUnusedModels reachability and why it runs before name reduction
  • §subtypes — discriminator subtype discovery — the reverse swagger:allOf index
  • §model-lookupGetModel vs FindModel — pure read vs implicit registration
  • §classifierdetectNodes bitmask semantics and struct-annotation exclusivity
  • §after-declAfterDeclComments — reading annotations inside / below a declaration
  • §enum-values — where swagger:enum member values come from, and what the degraded reading can still see
  • §clean-godocCleanGoDoc — filtering godoc syntax out of carried-over title / description prose
  • §quirks-open — deferred follow-ups

§options — Options overview

Options is the externally-visible configuration struct. It is re-exported from the package root as codescan.Options. The default zero value is a valid configuration: every flag defaults to false and every slice/map defaults to nil.

Most fields are simple toggles (scope inclusion, debug, vendor extension suppression). Two fields carry non-trivial semantics that warrant the inline godoc and the deeper notes below:

  • DescWithRef — controls the $ref shape used when a struct field resolves to a named type and its only decoration is a description. See §descwithref.
  • OnDiagnostic — diagnostic callback hook. See §diagnostics.
  • PruneUnusedModels — drop discovered definitions unreachable from any root, on top of ScanModels. See §prune.

§descwithref — description-only-decoration $ref shape

When a struct field's Go type resolves to a named type (so the spec emits a $ref to its definition) and its only field-level decoration is a description (no validations, no user-authored vendor extensions), the spec has two possible shapes:

  1. Bare $ref{$ref: ...}. The field's description is dropped. This is the conservative default when DescWithRef is false.
  2. Single-arm allOf{description: "...", allOf: [{$ref}]}. The description is preserved by wrapping the $ref in a single-arm allOf compound. This is JSON-Schema-draft-4 correct for sibling description.

DescWithRef=true opts into the second shape. The default is false because the bare-$ref shape interoperates more broadly with Swagger 2.0 tooling that does not implement the allOf compound.

When the field also carries validation overrides (pattern, enum, example, etc.) or user-authored vendor extensions, the allOf compound is mandatory regardless of DescWithRef — the override would be lost otherwise.

§diagnostics — OnDiagnostic callback

Options.OnDiagnostic, when non-nil, is invoked for every grammar.Diagnostic the builder layer records: lexer/parser warnings, semantic-validation failures from the validations package, and any future diagnostic class wired into the builder pipeline.

Contract:

  • The callback fires once per diagnostic, in source order.
  • Diagnostics never block the build. An invalid construct is silently dropped from the output spec; the explanation flows through this channel instead.
  • The callback may be called from any per-decl builder; it is the caller's responsibility to make it goroutine-safe if the consumer ever drives codescan.Run concurrently (today it is single- goroutine, but the callback contract makes no such guarantee).

The diagnostic surface is experimental. Once the LSP integration matures the shape is expected to grow: typed severity classes, structural deduplication, per-position provenance. Callers that adopt OnDiagnostic today should treat the signature as subject to breaking change in a future minor release.

ScanCtx.OnDiagnostic returns the user-supplied callback verbatim; builders pipe diagnostics through it via common.Builder.RecordDiagnostic.

§prune — PruneUnusedModels reachability

Options.PruneUnusedModels is a modifier on ScanModels (-m). The three emission modes:

  1. no ScanModels — only models transitively reachable from routes/responses/parameters are emitted (discovery-driven).
  2. ScanModels — every swagger:model type is emitted, reachable or not.
  3. ScanModels + PruneUnusedModels — discovery runs as in (2), then unreachable definitions are pruned again. The middle ground a shared-library scan wants: keep only the $ref'd subset (go-swagger/go-swagger#2639).

Without ScanModels the flag is a no-op (the set is already reachable-only) and raises one positionless scan.pruned-unused Hint.

Shared objects pruned first (C4). Before the definition walk, the shared parameters (#/parameters/*) and responses (#/responses/*) that no operation and no path-item references are themselves pruned (spec/prune.go, pruneUnusedSharedObjects; the read-only "is referenced" mirror is collectSharedRefs). InputSpec-supplied shared objects are pinned (never pruned), mirroring the definitions rule. Each drop raises a located scan.pruned-unused Hint. Because this precedes the definition walk, a definition kept alive only by a now-pruned shared object becomes prunable in turn. A pruned shared response's buffered provenance anchors are dropped (DropDeferredOrigins) so none dangle — shared-response anchors are buffered (BeginDeferredOrigins) and flushed verbatim after the prune only when PruneUnusedModels is set, so the non-prune anchor stream is unchanged.

Reachability. Roots are the paths (operation body parameters + response schemas), the surviving shared responses and parameters, and every definition supplied via InputSpec. Overlay definitions are pinned: never pruned and seeded as roots so their $ref targets survive. The walk (spec/prune.go, collectDefRefs) is the read-only mirror of the ref-rewriter (reduce.go, rewriteSchemaRefs) and must cover the same container set; a visited set handles recursive / cyclic models. A model referenced only by another unreferenced model is itself pruned.

Ordering — before name reduction. The prune runs before reduceDefinitionNames, in the fully-qualified #/definitions/<pkgpath>/ <name> key namespace. This is the point of the feature, not an implementation detail: name reduction deconflicts cross-package leaf collisions (a.Thing / b.ThingAThing / BThing). Pruning an unused twin first means the collision never materialises, so the surviving model keeps its bare leaf name — no spurious concat churn. Each prune raises a located scan.pruned-unused Hint; the buffered provenance for a pruned node is dropped so no anchor dangles. The collision renames the reduce stage does perform are surfaced as scan.renamed-definition Hints (located at the Go type).

Discriminated families are kept whole. A subtype $refs its base, never the reverse, so the walk above cannot see the subtypes of a discriminated base and would prune a polymorphic family down to its base alone. A reachable definition carrying discriminator therefore also marks its subtypes reachable, via the reverse swagger:allOf index (spec/subtypes.go, subtypeKeysOf). Note the rule keeps a reached base's family — it does not make bases roots: a discriminated base nothing references is still pruned, together with its subtypes. See §subtypes.

§subtypes — discriminator subtype discovery

Interface-based polymorphism emits a base definition carrying discriminator plus one allOf: [{$ref base}, {own props}] definition per subtype — a struct embedding the base under swagger:allOf.

The reference direction is the problem: a subtype $refs its base; nothing $refs a subtype. So a route that references only the base reaches the base and stops, and the emitted family is the base alone — useless to a consumer that has to unmarshal the polymorphic payload (go-swagger/go-swagger#1913). Before v0.37 the only way to get the subtypes was ScanModels, which emits every annotated model whether it belongs to the family or not.

Reverse index. spec/subtypes.go builds, once per scan, a base Go type identity → subtype declarations map from the model index (TypeIndex.Models, which classification populates whether or not ScanModels is set — that independence is what makes the pull possible). A subtype relation is an embedded member carrying swagger:allOf — a struct's anonymous field or an interface's anonymous interface, which is how a mid-level type in a multi-level hierarchy is written:

  • the pointer is unwrapped (*Base composes like Base), and an alias embed is indexed under both its own and the aliased type's identity, so the relation is found whichever definition the allOf member ends up $refing under RefAliases / TransparentAliases;
  • swagger:ignore on the embed drops it, exactly as in the schema builder — the index never claims a relation the document lacks;
  • a plain (unannotated) embed is not a subtype: it inlines the base's properties, no allOf member. DefaultAllOfForEmbeds is deliberately not honoured here — it is a rendering knob, and letting it decide which definitions exist would be a surprising coupling.

The index is keyed by Go type identity (<pkgpath>.<TypeName>), not by swagger name: it is the one fact both ends can compute without knowing the other's swagger:model override. Entries are ordered by definition key so the pull order — and hence the Hint order — does not follow map iteration.

Two hooks, one per hole.

  1. Discovery (spec.go, buildDiscoveredSchemadiscriminatedSubtypesOf): when a definition has just been built and carries a discriminator, its subtypes are appended to s.discovered, so the existing fixpoint loop builds them, discovers their dependencies, and cascades if a subtype is itself a discriminated base. Each genuinely new pull raises a located scan.discovered-subtype Hint. A no-op under ScanModels, where every model is built up front anyway.
  2. Prune reachability (prune.go): see §prune — required because under ScanModels the family is built and then lost, not never-built.

The gate is the built definition's own discriminator (isDiscriminated), not a source-level re-derivation. It reads identically for an interface base with a discriminator: true member and a struct base with a discriminator: true property, and it is the same fact the emitted document exposes. A base with no discriminator pulls nothing: its allOf users are ordinary compositions, not a polymorphic family.

Multi-level hierarchies. A mid-level type — a subtype that is itself a base — renders as allOf: [{$ref parent}, {own props, discriminator}]: its properties, and therefore its discriminator, sit in its own compound member, not at the top level. So the gate looks for an inline discriminator anywhere in the definition's own schema. The $ref member is deliberately not followed: a leaf must not inherit its base's discriminator, or every subtype would pull in its own siblings. Because hook A feeds the discovery fixpoint, the levels cascade — the root pulls the mid-level, and the mid-level (only just pulled in itself) pulls the leaves on the next round.

Fixtures: fixtures/enhancements/discriminated-subtypes (edges/ holds the embed-shape corner cases, in a family no route references — which also locks the other half of the gate: an unreached base pulls nothing) and fixtures/enhancements/discriminated-subtypes-nested (two-level hierarchy: ShapePolygonSquare/Triangle).

§model-lookup — GetModel vs FindModel

ScanCtx exposes two lookup helpers with similar signatures but different side-effect contracts. The choice between them is load-bearing for the shape of the emitted spec.

GetModel(pkgPath, name) — pure read

Looks up a model decl across three sources, in order:

  1. Models — decls annotated with swagger:model. Always emitted as top-level definitions regardless of lookup.
  2. ExtraModels — decls discovered as dependencies of other emitted shapes. Already enqueued for top-level emission.
  3. FindDecl — fall through to a syntactic search over the loaded packages.

No side effect. A FindDecl hit through GetModel does not register the decl in ExtraModels. Callers that want the lookup to also surface the decl as a top-level definition must follow up with AddDiscoveredModel explicitly.

FindModel(pkgPath, name) — implicit registration

The older sibling of GetModel. It does the same three-source lookup, but a FindDecl hit also writes the decl into ExtraModels as a side effect.

FindModel is deprecated. The implicit registration surprises readers and pulls stdlib types (notably time.Time, json.RawMessage) into the spec's top-level definitions when they should be inlined where referenced. Builders that need the registration should use the explicit GetModel + AddDiscoveredModel pair.

AddDiscoveredModel — explicit registration

Registers a decl in ExtraModels. No-op for decls already in Models (annotated decls are emitted unconditionally — registering them as discovered would create a Models↔ExtraModels bouncing loop in the spec orchestrator's joinExtraModels pass). Nil and Ident-less decls are silently ignored, which is defensive against the scanner emitting partial decls during error recovery.

§classifier — detectNodes bitmask

TypeIndex.detectNodes scans every comment group in a file and returns a bitmask of detected annotation kinds. Each kind drives downstream processing:

Bit Annotation Downstream
metaNode swagger:meta file-level meta block
routeNode swagger:route path-level route annotations
operationNode swagger:operation path-level operation annotations
modelNode swagger:model per-decl model registration
parametersNode swagger:parameters per-decl parameter registration
responseNode swagger:response per-decl response registration

route, operation, and meta accumulate freely across comment groups in a file. The three struct-level annotations (model, parameters, response) are mutually exclusive within a single comment group — a struct cannot simultaneously be a model and a parameters bag, for instance. checkStructConflict enforces the rule per comment group and returns an error if the constraint is violated.

The annotation vocabulary recognised by the classifier is a closed set. Unknown annotations beginning with swagger: raise a classifier error. A handful of annotation tokens (strfmt, name, enum, default, alias, type, title, description, …) are recognised but produce no bit — they are field/decl-level decorations that downstream builders parse out of the comment block directly. (title / description are the godoc title/description overrides; see the schema builder's §user-overrides.)

§after-decl — AfterDeclComments

Options.AfterDeclComments (opt-in, default false) lets swagger annotations live inside a declaration or inlined as a trailing comment, so the godoc above the declaration stays clean and human-facing. It is solely a scanner concern — the located comments are folded into the comment source the builders already consume (EntityDecl.Comments and ast.Field.Doc), so the grammar and builders are untouched. Same annotation grammar, no new syntax.

What the scanner folds, by shape (index.go):

Shape Folded comment Into
struct type leading body comment groups (after {, before the first field, excluding any field .Doc) — leadingBodyComments a fresh merged EntityDecl.Comments (ts.Doc untouched)
alias / non-struct type trailing TypeSpec.Comment (type X = Y // swagger:model …) same
struct field trailing Field.Comment (B string // swagger:strfmt date) — enrichStructFields the shared Field.Doc (the one mutation, see below)

The clean godoc above still provides the title/description: the merged group is docAbove ++ located, and because positions stay ascending (doc above < the inside/trailing comment below), the grammar reconstructs a blank-line gap and parses it without change. Discovery works because detectNodes already scans every file.Comments group (the file bitmask flips), and the merged EntityDecl.Comments makes the per-decl HasModelAnnotation gate pass.

Idempotency. Decl-level folding is pure construction — ts.Doc is never mutated, so re-processing is safe with no guard. Field-level folding is the only place the shared AST is mutated (Field.Doc is repointed to the merged group), guarded by TypeIndex.enrichedFields so a field is rewritten at most once.

Routes / operations are already position-agnostic (collectRoute/OperationPathAnnotations scan all file.Comments), so a swagger:route inside a func body is discovered with or without this option.

Out of scope. A standalone const X = … // swagger:enum: swagger:enum is type-based (it resolves a type and collects that type's consts via FindEnumValues), so a lone const is not an enum carrier and has no builder semantics today. Supporting it would mean new builder behaviour, which this scanner-only feature deliberately avoids. Nested/anonymous inline structs are likewise not enriched (only named struct type decls are walked).

§enum-values — reading swagger:enum members

FindEnumValues walks the const declarations of a package and emits one row per constant whose type is the annotated enum type. Two decisions shape it.

Membership is decided per name, from the type-checker

The spec's syntactic type (vs.Type) is not usable as the membership test, because inside an iota block only the first spec carries a type at all:

const (
    Sunday Weekday = iota   // Type=Weekday  Values=[iota]
    Monday                  // Type=<nil>    Values=<nil>
    Tuesday                 // Type=<nil>    Values=<nil>
)

Monday and Tuesday inherit both implicitly, so a syntactic reader sees two specs that declare nothing. Membership therefore comes from TypesInfo.Defs[name].(*types.Const).Type() — the type the checker assigned — which also covers a constant declared without a written type (const Extra = StatusOn).

The test is type identity, not name: the named type must also come from the package being walked. A constant declared in the annotated package can perfectly well have an imported type (const ForeignDay foreign.Weekday = 13 next to a local Weekday enum), and it is a member of neither. The syntactic reading ruled that out structurally — a qualified type is a selector expression, not the bare ident it required — so the package check is what keeps the type-checked reading from being wider than the one it replaced.

An enum cannot be hosted on an alias to a basic type (type Unsigned = uint64): the checker erases the alias, so const Zero Unsigned = 0 is indistinguishable from any other uint64 constant and there is nothing left to match on. The annotation is a no-op there — as it was before this change, since the classifier never reaches an alias decl either. An alias to a named enum type (type Weekday2 = Weekday) is fine: the underlying named type survives.

Values come from the type-checker, not from the literal

A const's right-hand side is only incidentally a literal. It can be iota, an expression (1 << 3), a reference to an earlier member (Prev * 2), a rune literal ('a', whose constant is the integer 97), or true / false — which are predeclared identifiers, since Go has no boolean literal token. Reading the value out of the syntax means reimplementing Go's constant evaluator: iota counting, implicit repetition, and constant folding.

go/types has already done that, exactly and with arbitrary precision, so enumConstantValue converts the resulting constant.Value by kind (Intint64, or uint64 past MaxInt64; Floatfloat64; String; Bool). A constant with no JSON representation (complex) or one the checker could not evaluate is dropped rather than emitted as a null member.

The degraded reading. When the package only partially type-checked (see ErrDegradedLoad), a constant may have no value in Defs. Rather than let an annotated enum vanish, enumValue falls back to the literal syntax — a lone literal, optionally signed, with rune literals and raw/escaped strings handled. It is a strict subset: iota, expressions and references are invisible to it by construction, and its values keep the kind their literal implies rather than the kind of their declared type. The builder's validations.CoerceConstant closes that last gap — see §enum-const-values.

§clean-godoc — CleanGoDoc

Options.CleanGoDoc (opt-in, default false) rewrites godoc-specific syntax that reads as bracket noise when a title / description is carried from godoc into the spec, and recomposes resolvable doc-links to the name the referenced schema is exposed under. Off ⇒ output is byte-identical.

The scanner side is thin: it holds the flag (CleanGoDoc()) and a shared mangling.NameMangler (Mangler(), used for humanization). The transform, the consumption-seam wiring, the go/types resolver, and the post-reduce marker substitution all live in the builders — see internal/builders/godoclink/README.md for the two-phase marker contract and the full mechanics.

Like swagger:title / swagger:description (overrides) and AfterDeclComments, this is part of the clean-godoc cluster: keep the Go-facing doc clean while the API spec carries curated text. Crucially it touches only godoc-derived prose — author-written overrides (harvested separately) are never filtered.

§quirks-open — deferred follow-ups

Where open quirks live. This section documents caveats of this package. The project-wide register of what is actually open — verified, with the stale historical registers called out — is .claude/plans/quirks-open.md.

  • FindModel deprecation. The deprecated alias is still on the ScanCtx surface for in-tree callers. Once every builder has been audited and migrated to the GetModel + AddDiscoveredModel pair, the deprecated method can be removed in a future major release.
  • Recognised-but-unused annotation tokens. detectNodes recognises a list of field-level tokens (strfmt, name, discriminated, file, enum, default, alias, type, allOf, ignore, title, description) only to avoid raising the "unknown annotation" error. Promoting them to per-file bits would let downstream builders skip whole files that carry no decorations — an optimisation, not a correctness change.
  • shouldAcceptTag precedence. When both includeTags and excludeTags are populated, includeTags wins (a tag in includeTags admits the operation even if it also appears in excludeTags). This is deliberate but easy to mis-read; an explicit "the include list takes precedence" doc on Options would help callers, but the field-level prose is already dense.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrDegradedLoad = errors.New("degraded package load")

ErrDegradedLoad is the base error for a degraded package load detected by detectDegradedLoad (no packages matched, or a scanned package failed to load / type-check).

It is wrapped with the per-package detail and, at the public API boundary, with ErrCodeScan.

View Source
var ErrScanner = errors.New("codescan:scanner")

ErrScanner is the sentinel error for all errors originating from the scanner package.

Functions

func JSONPointer added in v0.35.0

func JSONPointer(segments ...string) string

JSONPointer builds an RFC 6901 pointer from raw (unescaped) segments, escaping each per the spec (~ → ~0, / → ~1).

The output matches what the spec-side index derives via jsontext, so source- and spec-side pointers for the same node are byte-identical and join cleanly.

Types

type EntityDecl

type EntityDecl struct {
	Comments *ast.CommentGroup
	Type     *types.Named
	Alias    *types.Alias // added to supplement Named, after go1.22
	Ident    *ast.Ident
	Spec     *ast.TypeSpec
	File     *ast.File
	Pkg      *packages.Package
	// contains filtered or unexported fields
}

func (*EntityDecl) DefKey added in v0.35.0

func (d *EntityDecl) DefKey() string

DefKey returns the fully-qualified, compiler-unique definition key for this declaration: "<pkgpath>/<name>", where <name> is the swagger:model override when present, else the Go type name (the first return of Names).

This is the build-time key for the definitions map and for every "#/definitions/" $ref target, so two distinct Go types that share a short name can never collide before the spec.Builder's reduce stage shortens names back.

See the name-identity / cyclic-$ref design (.claude/plans/name-identity-cyclic-ref.md §9.1, §12.1).

Universe / package-less types (no enclosing package) fall back to the bare name; in practice those are intercepted as stdlib specials before they ever reach a definition key.

func (*EntityDecl) HasModelAnnotation

func (d *EntityDecl) HasModelAnnotation() bool

func (*EntityDecl) HasParameterAnnotation

func (d *EntityDecl) HasParameterAnnotation() bool

func (*EntityDecl) HasResponseAnnotation

func (d *EntityDecl) HasResponseAnnotation() bool

func (*EntityDecl) ModelOverrideSuppressed added in v0.35.0

func (d *EntityDecl) ModelOverrideSuppressed() bool

ModelOverrideSuppressed reports whether SuppressModelOverride was set.

func (*EntityDecl) Names

func (d *EntityDecl) Names() (name, goName string)

func (*EntityDecl) Obj

func (d *EntityDecl) Obj() *types.TypeName

Obj returns the type name for the declaration defining the named type or alias t.

func (*EntityDecl) ObjType

func (d *EntityDecl) ObjType() types.Type

func (*EntityDecl) SuppressModelOverride added in v0.35.0

func (d *EntityDecl) SuppressModelOverride()

SuppressModelOverride drops this declaration's `swagger:model <name>` override so that Names / DefKey fall back to the Go type name.

Used to resolve a same-package duplicate, where two distinct types in one package claim the same override name (a user error): the first keeps the name, later ones revert to their Go name. See name-identity design D-4 (.claude/plans/name-identity-cyclic-ref.md §9.1).

type Options

type Options struct {
	Packages                []string
	InputSpec               *spec.Swagger
	ScanModels              bool
	WorkDir                 string
	BuildTags               string
	ExcludeDeps             bool
	Include                 []string
	Exclude                 []string
	IncludeTags             []string
	ExcludeTags             []string
	SetXNullableForPointers bool
	RefAliases              bool // aliases result in $ref, otherwise aliases are expanded
	TransparentAliases      bool // aliases are completely transparent, never creating definitions
	// DescWithRef controls description preservation on $ref'd fields in the
	// description-only-decoration case: when a struct field's Go type resolves to a named type ($ref)
	// and its only field-level decoration is a description (no validations, no user-authored
	// extensions).
	//
	//   - false (default): the description is dropped and the field
	//     emits as a bare `{$ref: ...}`.
	//   - true: the description is preserved by wrapping the $ref in
	//     a single-arm `allOf` compound — `{description: "...",
	//     allOf: [{$ref}]}` — the JSON-Schema-draft-4 correct shape
	//     for sibling description.
	//
	// When the field also carries validation overrides (pattern, enum, example, etc.) or user-authored
	// vendor extensions, the allOf compound is mandatory regardless of this flag — the override
	// would be lost otherwise.
	//
	// Deprecated: prefer EmitRefSiblings, which preserves description AND extensions as direct $ref
	// siblings (the modern, lenient shape).
	// DescWithRef is retained with its original semantics (the strict draft-4 single-arm allOf wrap
	// for the description-only case) and remains a no-op when EmitRefSiblings is set.
	//
	// See [§ref-override](../builders/schema/README.md#ref-override).
	DescWithRef bool

	// EmitRefSiblings emits a $ref'd field's description and vendor extensions as DIRECT siblings of
	// the `$ref` (`{$ref, description, x-*}`) instead of wrapping them in an allOf compound.
	//
	// Strict JSON-Schema-draft-4 ignores siblings of `$ref` (hence the default allOf wrap), but
	// OpenAPI 3.1 / JSON Schema 2020-12 and most modern Swagger-UI renderers honour them.
	//
	//   - false (default): description / extensions follow the legacy
	//     wrap behaviour (extensions lift onto a single-arm allOf;
	//     description-only is governed by DescWithRef).
	//   - true: description and extensions ride directly alongside the
	//     `$ref`, no allOf.
	//
	// Validations and externalDocs are NOT siblings-eligible: when present they still force an allOf
	// compound (validations on the override arm), and description / extensions then ride the outer
	// compound.
	// This flag changes only the no-forced-compound cases.
	//
	// See [§ref-override](../builders/schema/README.md#ref-override).
	EmitRefSiblings bool

	// SkipAllOfCompounding disables the allOf-compound rewrite for $ref'd struct fields entirely: no
	// allOf compound is ever emitted.
	//
	//   - false (default): siblings are preserved via the allOf compound
	//     (or, under EmitRefSiblings, as direct $ref siblings).
	//   - true: no compound is produced. Validations and externalDocs —
	//     which can only ride a compound — are DROPPED. Description and
	//     extensions are likewise dropped UNLESS EmitRefSiblings is also
	//     set, in which case they survive as direct `$ref` siblings.
	//     Every drop raises one diagnostic through OnDiagnostic — the
	//     loss is never silent.
	//
	// `required:` is a parent-side concern (it lands on the enclosing object's `required` list, not as
	// a $ref sibling) and is preserved regardless of this flag.
	//
	// Intended for downstream consumers (e.g. go-swagger codegen) that expect a bare `$ref` for a
	// field pointing at a model and do not handle the allOf-compounded shape.
	// See [§ref-override].
	SkipAllOfCompounding bool

	// DefaultAllOfForEmbeds changes how a plain (non-`swagger:allOf`-tagged) struct embed renders:
	// into allOf composition instead of inlined properties.
	//
	// By default codescan inlines an embedded struct's properties into the embedding schema (mirroring
	// Go field promotion), so the "this composes Y" relationship is lost — every embedding struct
	// emits a flat copy of the embedded fields.
	//
	// Downstream client generators that want a reusable base type per embed prefer the composition
	// shape instead.
	//
	//   - false (default): plain embeds inline their properties (existing
	//     behaviour).
	//   - true: a plain embed is treated as if it carried `swagger:allOf` —
	//     it becomes an allOf member ($ref to the embedded type's definition
	//     when that type is a model, otherwise an inline member), and the
	//     embedding struct's own fields move into a sibling allOf member.
	//
	// Scope and precedence:
	//   - Only STRUCT embeds are affected. Interface embeds already compose via
	//     allOf and are unchanged.
	//   - An explicit `swagger:allOf` annotation already produces this shape;
	//     the flag only makes it the default for untagged embeds.
	//   - An embed carrying an explicit json tag name (or `swagger:name`) is a
	//     single named property, not a promotion, so it is left as a nested
	//     property regardless of this flag (go-swagger#2038).
	//   - Pointer embeds are peeled; aliased embeds resolve to their unaliased
	//     type; stdlib specials (`error`, `time.Time`) keep their canonical
	//     recognizer shape — all via the existing allOf path.
	//
	// See [§allof](../builders/schema/README.md#allof).
	DefaultAllOfForEmbeds bool

	SkipExtensions bool // skip generating x-go-* vendor extensions in the spec

	// NameFromTags is the ordered list of struct-tag types consulted to derive the emitted name of a
	// schema property, parameter, or response header from a Go struct field.
	//
	// The first listed tag type that supplies a usable name wins; a tag type that is absent or carries
	// only options (e.g. `,omitempty`) is skipped and the next is tried.
	// When no listed tag names the field, the Go field name is used.
	//
	//   - nil / unset (default): ["json"] — the historic behaviour.
	//   - explicit empty slice: no struct tag is consulted; the name derives
	//     from the Go field name.
	//   - e.g. ["form","json"]: prefer the `form:` name (used by gin), falling
	//     back to `json:` (go-swagger#2912, go-swagger#1391).
	//
	// Only the NAME is sourced this way.
	// The encoding/json directives `-` (exclude), `,omitempty` (→ not required) and `,string` are
	// always read from the `json` tag regardless of this setting.
	//
	// Targeted renames — the `name:` keyword (parameters / response headers) and `swagger:name` /
	// `swagger:model {name}` (schema) — still take precedence over any tag-derived name.
	NameFromTags []string

	// SkipJSONifyInterfaceMethods opts out of the auto-jsonify mangler applied to interface-method
	// property names.
	//
	// An interface method has no "natural" JSON serialization (Go's encoding/json cannot marshal
	// embedded interface methods without a custom marshaler), so codescan invents a default property
	// name by running the swag/mangling ToJSONName transform on the Go method name (`CreatedAt` →
	// `createdAt`, `ID` → `id`).
	//
	// This convention will not always match the author's intent — e.g. an interface already named
	// for its JSON shape, or a codebase with its own canonical-name discipline.
	//
	//   - false (default): interface-method names auto-jsonify (existing
	//     behaviour).
	//   - true: the Go method name is emitted verbatim; the mangler is skipped.
	//
	// A `swagger:name X` override is taken verbatim regardless of this flag — it already bypasses
	// the mangler.
	// This flag only changes the fallback used when no override is present.
	// It does not affect struct-field naming, which mirrors what encoding/json actually produces.
	//
	// See [§interface-naming](../builders/schema/README.md#interface-naming).
	SkipJSONifyInterfaceMethods bool

	// SkipEnumDescriptions controls whether the per-enum-value const-name mapping built from
	// `swagger:enum` (e.g. "FIRST TestEnumFirst") is folded into the property / parameter / header
	// `description`.
	//
	//   - false (default): the mapping is appended to the authored
	//     description AND exposed via the `x-go-enum-desc` vendor extension
	//     (backward-compatible behaviour).
	//   - true: the description is left as the authored prose; the mapping
	//     rides `x-go-enum-desc` only.
	//
	// Independent of SkipExtensions: with SkipExtensions also set, the mapping is suppressed
	// everywhere.
	// See go-swagger/go-swagger#2922.
	SkipEnumDescriptions bool

	// NameConcatBudget tunes the readability cutoff used when the name-identity reduce stage
	// deconflicts colliding definition names by concatenating package segments (b.Test / c.Test ->
	// BTest / CTest).
	//
	// Each candidate concat is scored in [0,1] — lower is more readable (shorter overall, fewer
	// parts, no over-long segment).
	// A collision group whose best concat scores ABOVE the budget is a candidate for the hierarchical
	// fallback (name-identity Stage 3 / K3).
	//
	// The zero value selects the built-in default (0.65).
	// Raise it toward 1.0 to accept longer concats; lower it to fall back sooner.
	NameConcatBudget float64

	// EmitHierarchicalNames enables the hierarchical fail-safe for the rare collision groups whose
	// best flat concat exceeds NameConcatBudget.
	//
	// When set, such a group is emitted as nested container definitions (`#/definitions/<pkg>/<Name>`,
	// with `additionalProperties:true` + `x-go-package` on each container) instead of a long flat
	// concat, and an explanatory diagnostic is raised.
	//
	// Default false — and deliberately so: a nested definition is a deep JSON pointer that only
	// `ExpandSpec` resolves, and a definitions- enumerating consumer (e.g. go-swagger codegen, one
	// model per entry) sees the container nodes rather than the models.
	//
	// The always-correct flat concat stays the default; enable this only when you prefer the nested
	// shape for the over-budget tail.
	EmitHierarchicalNames bool

	// EmitXGoType stamps an `x-go-type` vendor extension on every emitted definition, recording the
	// fully-qualified originating Go type (`<package path>.<type name>`) alongside the existing
	// `x-go-name` / `x-go-package` traceability extensions.
	//
	//   - false (default): no `x-go-type` is emitted for ordinary types
	//     (the extension still appears on the narrow special-type cases
	//     that have always carried it — `error`, the unmodellable
	//     generic-type fallback).
	//   - true: each definition carries `x-go-type`, useful for
	//     round-tripping a generated spec back to its source Go types.
	//
	// Under the SkipExtensions umbrella: with SkipExtensions also set, no vendor extension is emitted
	// regardless.
	// See go-swagger/go-swagger#2924.
	EmitXGoType bool

	// SingleLineCommentAsDescription routes a single-line doc comment to the object's `description`
	// regardless of trailing punctuation, never to `title` / `summary`.
	//
	//   - false (default): the first-sentence convention applies — a
	//     single-line comment ending in punctuation (`.`, `!`, `?`)
	//     becomes the `title` (model / info) or `summary` (operation);
	//     without trailing punctuation it is a `description`.
	//   - true: a single-line comment is always a `description`. Multi-
	//     line comments keep the existing title/description split (the
	//     first line, or the paragraph before the first blank line, is
	//     still the title).
	//
	// See go-swagger/go-swagger#2626.
	SingleLineCommentAsDescription bool

	// AfterDeclComments, when set, lets swagger annotations live INSIDE a declaration (the leading
	// comment of a struct body) or INLINED as a trailing comment, in addition to the doc comment above
	// the declaration.
	//
	// The godoc above the declaration then stays clean and human-facing while the swagger machinery
	// lives out of the published documentation.
	// The scanner folds the located comments into the comment source the builders already consume —
	// same annotation grammar, no new syntax.
	//
	// Default false.
	//
	// v0.36 scope: type declarations (swagger:model / swagger:parameters / swagger:response) —
	// struct inside-body leading comments and the trailing comment of an alias / non-struct type.
	// Routes / operations are already position-agnostic.
	// Struct fields and const enums are follow-ups.
	AfterDeclComments bool

	// CleanGoDoc rewrites godoc-specific syntax that reads as noise when a title / description is
	// carried from a Go doc comment into the spec.
	//
	// It applies ONLY to godoc-derived prose — author-written swagger:title / swagger:description
	// overrides are never touched.
	//
	//   - false (default): godoc prose is emitted verbatim (existing
	//     behaviour; output is byte-identical).
	//   - true: godoc doc-link brackets are removed and the identifier is
	//     humanized (`[CustName]` → "cust name"); reference-style link
	//     definition lines (`[text]: url`) are dropped; and when a doc-link
	//     resolves to an emitted schema, it is recomposed to the name that
	//     schema is actually exposed under (so the prose stays true to the
	//     generated definitions). The first identifier of a title /
	//     description is restored to sentence case.
	//
	CleanGoDoc bool

	// PruneUnusedModels, when set together with ScanModels, drops every discovered definition that is
	// not transitively referenced from a path, a shared response, a shared parameter, or a definition
	// supplied via InputSpec.
	//
	// It is the middle ground between the two default modes:
	//
	//   - without ScanModels: only route-reachable models are emitted;
	//   - with ScanModels (`-m`): every swagger:model type is emitted, reachable
	//     or not;
	//   - with ScanModels + PruneUnusedModels: swagger:model discovery runs, then
	//     the unreachable definitions are pruned again — useful when scanning a
	//     large shared library where only the $ref'd subset is wanted.
	//
	// Pruning runs BEFORE definition-name reduction, so an unused model can no longer force a spurious
	// cross-package name collision on a model that IS used (the survivor keeps its clean short name).
	// Definitions supplied via InputSpec are pinned: they are never pruned and seed the reachability
	// roots.
	//
	// Each pruned definition raises a scan.pruned-unused Hint through OnDiagnostic — the loss is
	// never silent.
	//
	// Without ScanModels this flag is a no-op (the emitted set is already reachable-only); setting it
	// alone raises one Hint.
	// Default false.
	//
	// Note: a discriminator base references its subtypes by mapping string, not by $ref, so a subtype
	// reachable only through a discriminator could be pruned. codescan does not auto-wire
	// discriminator subtypes today; revisit if it ever does.
	// See go-swagger/go-swagger#2639.
	PruneUnusedModels bool

	// Debug is deprecated and has no effect.
	//
	// It formerly enabled verbose debug logging to stderr during scanning.
	// That logger was retired: scan-time observations now flow exclusively through OnDiagnostic (which
	// the caller routes to a logger of their choice), and codescan no longer writes to stdout/stderr
	// — keeping it usable from a TUI or a WASI/WASM host.
	//
	// Deprecated: wire OnDiagnostic instead.
	// This field is retained for API compatibility and is ignored.
	Debug bool

	// OnDiagnostic, when non-nil, is invoked for every diagnostic the builder layer records
	// (lexer/parser warnings, semantic-validation failures from the validations package, etc.).
	//
	// The callback fires once per diagnostic in source order; diagnostics never block the build —
	// invalid constructs are silently dropped from the output spec while their explanation flows
	// through this channel.
	//
	// Experimental: the public API surface for diagnostics is subject to change while LSP integration
	// matures.
	// See [§diagnostics](./README.md#diagnostics).
	OnDiagnostic func(grammar.Diagnostic)

	// OnProvenance, when non-nil, is invoked once per anchor node in the produced spec, carrying its
	// JSON pointer and the source position of the Go construct that produced it (see [Provenance]).
	//
	// Anchors are code-detail nodes (type decls, fields, values, route/meta blocks); finer nodes
	// resolve to their nearest anchored ancestor at the consumer.
	// The callback never blocks the build.
	//
	// Experimental: the cross-ref surface may change while LSP / TUI integration matures.
	OnProvenance func(Provenance)
}

Options configures a scan.

The zero value is a valid configuration: every flag defaults to false and every slice/map defaults to nil.

Details

See [§options](./README.md#options) for the field overview, and [§descwithref](./README.md#descwithref) and [§diagnostics](./README.md#diagnostics) for the two fields with non-trivial semantics (DescWithRef and OnDiagnostic).

type ParameterRef added in v0.35.1

type ParameterRef struct {
	Comments *ast.CommentGroup
	File     *ast.File
	Pkg      *packages.Package
}

ParameterRef is a standalone `swagger:parameters` marker hosted by a func (or other non-struct declaration) rather than a struct definition.

Per the disambiguation rule, such a marker is a *reference*: it wires existing shared parameters into an operation or path-item as `$ref`s — its first argument token is the target (an operation id or a `/path`) and the remaining tokens are shared-parameter names.

The scanner only discovers and locates the marker; its target and names are parsed from Comments by the grammar (grammar.ParametersBlock) when the shared-parameters builder consumes it. §1b.

type Provenance added in v0.35.0

type Provenance struct {
	// Pointer is the RFC 6901 JSON pointer of the anchored spec node, e.g. "/definitions/User" or
	// "/paths/~1pets/get".
	Pointer string
	// Pos is the source location (file:line:col) of the producing construct.
	Pos token.Position
}

Provenance ties a node in the produced Swagger spec (by RFC 6901 JSON pointer) to the source position of the Go construct that produced it.

It is the source-side half of the cross-ref linker (see the genspec-tui linkage design). Provenance is emitted via Options.OnProvenance only at "anchor" nodes — those born from a code detail (a type declaration, a struct field, a const/var value, a route/meta annotation block).

Finer nodes carry no Provenance of their own; a consumer resolves them to their nearest anchored ancestor.

Experimental: this surface may change while LSP / TUI integration matures.

type ScanCtx

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

func NewScanCtx

func NewScanCtx(opts *Options) (*ScanCtx, error)

func (*ScanCtx) AddDiscoveredModel added in v0.34.1

func (s *ScanCtx) AddDiscoveredModel(decl *EntityDecl)

AddDiscoveredModel registers decl in the ExtraModels index so the spec orchestrator emits a top-level definition for it.

No-op when decl is already an annotated swagger:model (in Models); annotated decls are emitted unconditionally and re-registering them as "discovered" would create a Models↔ExtraModels bouncing loop in joinExtraModels. Nil and Ident-less decls are silently ignored.

Use only at sites that explicitly intend the registration — pure-read lookups should use GetModel. See [§model-lookup](./README.md#model-lookup).

func (*ScanCtx) BeginDefOrigins added in v0.35.1

func (s *ScanCtx) BeginDefOrigins(defKey string)

BeginDefOrigins opens a buffering window for the definition keyed by defKey (its fully-qualified EntityDecl.DefKey).

Until [EndDefOrigins], every [RecordOrigin] call is buffered under defKey instead of fired. No-op when no provenance sink is wired. Non-reentrant: each definition is built in its own pass, so windows never nest.

func (*ScanCtx) BeginDeferredOrigins added in v0.35.1

func (s *ScanCtx) BeginDeferredOrigins(key string)

BeginDeferredOrigins opens a buffering window keyed by key for a top-level spec node that may be pruned after the build (a shared response).

Until [EndDeferredOrigins], every [RecordOrigin] call is buffered under key instead of fired, so it can be dropped wholesale ([DropDeferredOrigins]) if the node is pruned, or flushed verbatim ([FlushDeferredOrigins]) if it survives. No-op when no provenance sink is wired.

Non-reentrant.

func (*ScanCtx) CleanGoDoc added in v0.35.1

func (s *ScanCtx) CleanGoDoc() bool

CleanGoDoc reports whether godoc-syntax filtering is enabled (Options.CleanGoDoc).

func (*ScanCtx) DeclForType

func (s *ScanCtx) DeclForType(t types.Type) (*EntityDecl, bool)

func (*ScanCtx) DefaultAllOfForEmbeds added in v0.35.1

func (s *ScanCtx) DefaultAllOfForEmbeds() bool

DefaultAllOfForEmbeds reports whether plain struct embeds should render as allOf composition instead of inlined properties (Options.DefaultAllOfForEmbeds).

func (*ScanCtx) DescWithRef

func (s *ScanCtx) DescWithRef() bool

func (*ScanCtx) DropDefOrigins added in v0.35.1

func (s *ScanCtx) DropDefOrigins(defKey string)

DropDefOrigins discards the buffered anchors for a definition that has been pruned, so its provenance is never emitted (no orphan pointer into a definition absent from the final document).

func (*ScanCtx) DropDeferredOrigins added in v0.35.1

func (s *ScanCtx) DropDeferredOrigins(key string)

DropDeferredOrigins discards the buffered anchors for a deferred node that has been pruned, so its provenance is never emitted (no orphan pointer into a node absent from the final document).

func (*ScanCtx) EmitDiagnostic added in v0.35.0

func (s *ScanCtx) EmitDiagnostic(d grammar.Diagnostic)

EmitDiagnostic delivers d to the consumer's Options.OnDiagnostic sink, suppressing exact duplicates — same position, code and message — for the lifetime of the scan.

The build re-processes the same field/annotation in several passes (most visibly a swagger:parameters struct applied to multiple operation ids, which rebuilds every field once per id), so the identical diagnostic would otherwise surface once per visit.

The accumulator returned by common.Builder.Diagnostics() is unaffected — only the callback stream dedups.

func (*ScanCtx) EmitHierarchicalNames added in v0.35.0

func (s *ScanCtx) EmitHierarchicalNames() bool

EmitHierarchicalNames reports whether the caller opted into the hierarchical fail-safe for over-budget collision groups.

func (*ScanCtx) EmitRefSiblings added in v0.35.0

func (s *ScanCtx) EmitRefSiblings() bool

func (*ScanCtx) EmitXGoType added in v0.35.0

func (s *ScanCtx) EmitXGoType() bool

func (*ScanCtx) EndDefOrigins added in v0.35.1

func (s *ScanCtx) EndDefOrigins()

EndDefOrigins closes the current definition buffering window.

func (*ScanCtx) EndDeferredOrigins added in v0.35.1

func (s *ScanCtx) EndDeferredOrigins()

EndDeferredOrigins closes the current deferred buffering window.

func (*ScanCtx) ExtraModels

func (s *ScanCtx) ExtraModels() iter.Seq2[*ast.Ident, *EntityDecl]

func (*ScanCtx) FileForPos added in v0.35.0

func (s *ScanCtx) FileForPos(pkgPath string, pos token.Pos) (*ast.File, bool)

FileForPos returns the *ast.File in package pkgPath whose source interval contains pos.

Used when a struct's fields are defined in a different file than the decl that carries them — e.g. embedding a cross-package defined type (`type AnotherPackageAlias color.Color`), where the promoted fields live in the underlying type's source file, not in the embedding type's file. See go-swagger#2417.

Matching is done via the shared FileSet: positions and ast.File starts resolve through the same *token.File, so the comparison is independent of go/ast's File range accessors.

func (*ScanCtx) FileSet added in v0.34.1

func (s *ScanCtx) FileSet() *token.FileSet

FileSet returns the shared *token.FileSet used by the scan's loaded packages.

Callers that construct a grammar.Parser for comment groups not owned by a single EntityDecl's *packages.Package (notably operation and route path-level annotations aggregated across packages) read the FileSet from here so the produced positions resolve against the same file table the rest of the scan uses.

func (*ScanCtx) FindComments

func (s *ScanCtx) FindComments(pkg *packages.Package, name string) (*ast.CommentGroup, bool)

func (*ScanCtx) FindDecl

func (s *ScanCtx) FindDecl(pkgPath, name string) (*EntityDecl, bool)

func (*ScanCtx) FindEnumValues

func (s *ScanCtx) FindEnumValues(pkg *packages.Package, enumName string) (list []any, descList []string, posList []token.Pos, _ bool)

FindEnumValues returns the enum values, per-value descriptions and per-value source positions for the constants typed enumName, plus ok.

The positions are parallel to the values (one token.Pos per value, the const identifier) and feed the cross-ref /…/enum/{i} anchors; callers that don't need them ignore the third result.

func (*ScanCtx) FindModel deprecated

func (s *ScanCtx) FindModel(pkgPath, name string) (*EntityDecl, bool)

FindModel returns the model decl for (pkgPath, name) and, when the hit comes from FindDecl fallback, registers it in ExtraModels as a side effect.

Deprecated: prefer the explicit pair GetModel (pure read) and AddDiscoveredModel (explicit registration).

The implicit registration side effect surprises readers and pulls stdlib types (notably time.Time, json.RawMessage) into the spec's top-level definitions when they should be inlined where referenced. See [§model-lookup](./README.md#model-lookup).

func (*ScanCtx) FindModelsByLeaf added in v0.35.0

func (s *ScanCtx) FindModelsByLeaf(name string) []*EntityDecl

FindModelsByLeaf returns every annotated swagger:model whose Go type name equals name, across all scanned packages, sorted by package path for determinism.

It is the build-time analogue of the reduce stage's resolveDefinitionByLeaf: the type-name keyword sites use it to resolve a bare leaf to a model declared in another package (unique -> promote; several -> ambiguous).

Only the annotated model set (fixed before building) is searched — not the discovery-grown ExtraModels — so the result is a pure function of the source, independent of build order (W6).

func (*ScanCtx) FlushDefOrigins added in v0.35.1

func (s *ScanCtx) FlushDefOrigins(finalName func(defKey string) string)

FlushDefOrigins fires every buffered definition anchor, re-pointing each from its build-time fully-qualified base (#/definitions/<defKey>) to the definition's final name. finalName maps a definition key to the name the spec emits for it (identity when unchanged).

Pointers are emitted in a deterministic (sorted) order. After the flush the buffer is cleared.

func (*ScanCtx) FlushDeferredOrigins added in v0.35.1

func (s *ScanCtx) FlushDeferredOrigins()

FlushDeferredOrigins fires every still-buffered deferred anchor verbatim (the nodes are never renamed) in a deterministic order, then clears the buffer.

func (*ScanCtx) GetModel added in v0.34.1

func (s *ScanCtx) GetModel(pkgPath, name string) (*EntityDecl, bool)

GetModel is a pure read: it returns the model decl for (pkgPath, name) without any side effect.

Details

See [§model-lookup](./README.md#model-lookup) — the three-source lookup order (Models, ExtraModels, FindDecl), and how this differs from FindModel.

Returns (nil, false) when no matching decl exists in any of the three sources. Callers that want the lookup hit registered as a discovered model must follow up with AddDiscoveredModel explicitly.

func (*ScanCtx) Mangler added in v0.35.1

func (s *ScanCtx) Mangler() *mangling.NameMangler

Mangler returns the scan's shared name mangler (swag-style name transforms).

func (*ScanCtx) Meta

func (s *ScanCtx) Meta() iter.Seq[*ast.CommentGroup]

func (*ScanCtx) Models

func (s *ScanCtx) Models() iter.Seq2[*ast.Ident, *EntityDecl]

func (*ScanCtx) MoveExtraToModel

func (s *ScanCtx) MoveExtraToModel(k *ast.Ident)

func (*ScanCtx) NameConcatBudget added in v0.35.0

func (s *ScanCtx) NameConcatBudget() float64

NameConcatBudget returns the caller-supplied readability budget for collision-deconflicted definition names, or 0 when unset — the spec builder substitutes its built-in default in that case.

func (*ScanCtx) NameFromTags added in v0.35.1

func (s *ScanCtx) NameFromTags() []string

NameFromTags returns the ordered list of struct-tag types consulted to derive a field's emitted name.

A nil/unset option defaults to ["json"] (the historic behaviour); an explicit empty slice means no tag is consulted and names fall back to the Go field name.

func (*ScanCtx) NumExtraModels

func (s *ScanCtx) NumExtraModels() int

func (*ScanCtx) OnDiagnostic added in v0.34.1

func (s *ScanCtx) OnDiagnostic() func(grammar.Diagnostic)

OnDiagnostic returns the user-supplied diagnostic sink, or nil when the consumer has not opted into diagnostic delivery.

Details

See [§diagnostics](./README.md#diagnostics) — callback contract, ordering guarantee, experimental-API caveat.

func (*ScanCtx) Operations

func (s *ScanCtx) Operations() iter.Seq[parsers.ParsedPathContent]

func (*ScanCtx) OriginEnabled added in v0.35.0

func (s *ScanCtx) OriginEnabled() bool

OriginEnabled reports whether a provenance sink is wired, so callers can skip JSON-pointer construction entirely when no consumer is listening.

func (*ScanCtx) ParamOrigin added in v0.35.0

func (s *ScanCtx) ParamOrigin(opID, name string) (token.Position, bool)

ParamOrigin returns the captured source position for parameter name on operation opID, recorded earlier via [RecordParamOrigin].

The spec builder's deferred pass uses it to emit /paths/{path}/{method}/parameters/{i} anchors once the final path binding and array index are known.

func (*ScanCtx) ParameterRefs added in v0.35.1

func (s *ScanCtx) ParameterRefs() iter.Seq[*ParameterRef]

ParameterRefs iterates the standalone `swagger:parameters` reference markers discovered on func declarations (the references that wire shared parameters into operations / path-items as $refs).

See ParameterRef.

func (*ScanCtx) Parameters

func (s *ScanCtx) Parameters() iter.Seq[*EntityDecl]

func (*ScanCtx) PkgForType

func (s *ScanCtx) PkgForType(t types.Type) (*packages.Package, bool)

func (*ScanCtx) PosOf added in v0.34.1

func (s *ScanCtx) PosOf(p token.Pos) token.Position

PosOf resolves p to a token.Position via the active FileSet.

Returns the zero token.Position when p is invalid or no FileSet is available. Useful for attaching a source location to a Diagnostic without each caller re-deriving the FileSet.

func (*ScanCtx) PruneUnusedModels added in v0.35.1

func (s *ScanCtx) PruneUnusedModels() bool

PruneUnusedModels reports whether the caller opted into pruning discovered definitions that are not transitively referenced from a root (paths, shared responses/parameters, overlay definitions).

See Options.PruneUnusedModels.

func (*ScanCtx) RecordOrigin added in v0.35.0

func (s *ScanCtx) RecordOrigin(pointer string, pos token.Position)

RecordOrigin fires the consumer's Options.OnProvenance callback for one anchor node, when wired.

Unlike diagnostics it accumulates nothing — the cross-ref index is owned by the consumer (see the genspec-tui linkage design).

Exception: while a definition build is in progress (between [BeginDefOrigins] and [EndDefOrigins]) the anchor is buffered instead of fired, so it can be re-pointed to the definition's final name — or dropped if the definition is pruned — by [FlushDefOrigins] at the end of the build.

Anchors outside a definition build (paths, responses, info, parameters) fire inline as before; name reduction never renames those.

func (*ScanCtx) RecordParamOrigin added in v0.35.0

func (s *ScanCtx) RecordParamOrigin(opID, name string, pos token.Position)

RecordParamOrigin stashes the source position of one parameter field, keyed by the operation id it applies to and the parameter name, for deferred anchor emission.

No-op when no provenance sink is wired. See [ParamOrigin].

func (*ScanCtx) RefAliases

func (s *ScanCtx) RefAliases() bool

func (*ScanCtx) Responses

func (s *ScanCtx) Responses() iter.Seq[*EntityDecl]

func (*ScanCtx) Routes

func (s *ScanCtx) Routes() iter.Seq[parsers.ParsedPathContent]

func (*ScanCtx) SetXNullableForPointers

func (s *ScanCtx) SetXNullableForPointers() bool

func (*ScanCtx) SingleLineCommentAsDescription added in v0.35.0

func (s *ScanCtx) SingleLineCommentAsDescription() bool

func (*ScanCtx) SkipAllOfCompounding added in v0.35.0

func (s *ScanCtx) SkipAllOfCompounding() bool

func (*ScanCtx) SkipEnumDescriptions added in v0.35.0

func (s *ScanCtx) SkipEnumDescriptions() bool

func (*ScanCtx) SkipExtensions

func (s *ScanCtx) SkipExtensions() bool

func (*ScanCtx) SkipJSONifyInterfaceMethods added in v0.35.1

func (s *ScanCtx) SkipJSONifyInterfaceMethods() bool

SkipJSONifyInterfaceMethods reports whether the interface-method auto-jsonify mangler is disabled (Options.SkipJSONifyInterfaceMethods).

A `swagger:name` override is honored verbatim regardless.

func (*ScanCtx) TransparentAliases

func (s *ScanCtx) TransparentAliases() bool

type TypeIndex

type TypeIndex struct {
	AllPackages   map[string]*packages.Package
	Models        map[*ast.Ident]*EntityDecl
	ExtraModels   map[*ast.Ident]*EntityDecl
	Meta          []*ast.CommentGroup
	Routes        []parsers.ParsedPathContent
	Operations    []parsers.ParsedPathContent
	Parameters    []*EntityDecl
	ParameterRefs []*ParameterRef
	Responses     []*EntityDecl
	// contains filtered or unexported fields
}

func NewTypeIndex

func NewTypeIndex(pkgs []*packages.Package, opts ...TypeIndexOption) (*TypeIndex, error)

type TypeIndexOption

type TypeIndexOption func(*TypeIndex)

func WithAfterDeclComments added in v0.35.1

func WithAfterDeclComments(enabled bool) TypeIndexOption

WithAfterDeclComments enables folding a declaration's inside-body leading comment (struct) or trailing comment (alias / non-struct type) into the decl's annotation source.

See Options.AfterDeclComments.

func WithExcludeDeps

func WithExcludeDeps(excluded bool) TypeIndexOption

func WithExcludePkgs

func WithExcludePkgs(excluded []string) TypeIndexOption

func WithExcludeTags

func WithExcludeTags(excluded map[string]bool) TypeIndexOption

func WithIncludePkgs

func WithIncludePkgs(included []string) TypeIndexOption

func WithIncludeTags

func WithIncludeTags(included map[string]bool) TypeIndexOption

func WithOnDiagnostic added in v0.35.0

func WithOnDiagnostic(cb func(grammar.Diagnostic)) TypeIndexOption

WithOnDiagnostic wires the consumer's diagnostic sink so the index can surface scan-environment observations (e.g. a package or route omitted by the caller's own include/exclude rules) as informational Hints.

The index is built before the ScanCtx exists, so it reports through the raw callback directly, exactly as detectDegradedLoad does.

func WithRefAliases

func WithRefAliases(enabled bool) TypeIndexOption

func WithTransparentAliases

func WithTransparentAliases(enabled bool) TypeIndexOption

func WithXNullableForPointers

func WithXNullableForPointers(enabled bool) TypeIndexOption

Directories

Path Synopsis
Package classify provides small classification predicates used by the scanner and by builders to decide whether a given name or comment line belongs to a particular Swagger-annotation family.
Package classify provides small classification predicates used by the scanner and by builders to decide whether a given name or comment line belongs to a particular Swagger-annotation family.

Jump to

Keyboard shortcuts

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