github.com/go-openapi/codescan is a Go source code scanner that produces
Swagger 2.0 (OpenAPI 2.0) specifications.
It reads specially formatted comments (annotations) in Go source files and
extracts API metadata β routes, parameters, responses, schemas and more β to
build a complete spec.Swagger document. It supports Go modules (since
go1.11).
The scanner works entirely at the AST / go/types level: it never compiles
or executes the code it scans. It only reads the source and its annotation
comments.
Learn codescan by spec concept β model definitions, routes and operations,
validations, examples, and document metadata β each shown as annotated Go
next to the Swagger it produces.
How-to guides for the knobs that change how the same Go source renders into
the spec β $ref vs inline, alias handling, descriptions beside a $ref,
nullable pointers, vendor extensions, and spec overlays.
The complete, normative reference for the codescan annotation language β
every annotation, every keyword, the embedded sub-languages, and the formal
grammar the parser implements.
Subsections of go-openapi codescan
Project
This section holds material specific to this repository:
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
About
codescan is a code-first OpenAPI engine: it reads specially formatted
comments (annotations) in your Go source and produces a
Swagger 2.0 specification. It works entirely at the AST /
go/types level β it never compiles or runs the code it scans.
Two ways to build an API
APIs and their documentation tend to evolve along one of two paths. The
go-openapi / go-swagger toolkit supports both.
Design-first (contract-first) β you write the OpenAPI document first and
treat it as the contract, then generate servers and clients from it. If this
is your workflow, reach for go-swagger (swagger generate server / swagger generate client).
Code-first β you write annotated Go and scan it to produce the spec. This
keeps the document in sync with the code as it changes, and lets you produce a
valid specification for a service that already exists.
codescan is the engine for the code-first path.
Relationship to go-swagger
codescan began life as a single package inside go-swagger and was
spun out into its own go-openapi repository. It is the scanner
behind the go-swagger command:
swagger generate spec ./...
go-swagger remains the main command-line consumer of this library. This site
documents the scanner library itself β the layer beneath swagger generate spec β so it sits upstream of go-swagger’s “generate spec” documentation. If
you arrived here from go-swagger: the annotations are exactly the same, and you
can either keep using the swagger CLI or call codescan.Run directly from
your own program (see Getting started).
Why scan from source
One source of truth. The spec is derived from the code, so it cannot
silently drift from what the service actually exposes.
Fast iteration. Add a field, add its annotation, regenerate β no separate
document to keep in step by hand.
Document what exists. Produce a standards-compliant spec for an API server
that is already deployed, so it becomes interoperable with new clients and
tooling.
When document-level metadata (info, security, servers) is more naturally
hand-authored, you do not have to push it into the code: scan the code for the
operations and models, and overlay the result onto a hand-written base
document (see Shaping the output β Overlaying a spec).
A community toolkit
go-openapi and go-swagger are community-driven, open-source building blocks
meant to be assembled and customized β there are too many ways to approach APIs
to cover them all. Fork, reuse, and adapt what you find useful. See the
go-swagger project’s “About” page for the wider toolkit
story.
Import codescan, annotate a package, and produce a Swagger 2.0 specification
from your Go program.
Today, codescan is used as a Go library (below). Additional usage modes will
appear here as siblings as the toolkit grows.
Subsections of Getting started
Usage as a library
The most direct way to use codescan is to import it and call Run from your
own Go program β a generator, a go:generate step, or a test that keeps your
spec in sync with the source.
Annotate your source
Annotations are special comments following the go-swagger
convention (swagger:meta, swagger:route, swagger:model,
swagger:parameters, swagger:response, β¦).
A package-level swagger:meta block carries the top-level metadata of the
spec:
// Package petstore Petstore API//
// A tiny pet store, used to demonstrate codescan annotations: the package// comment is a `swagger:meta` block carrying the top-level metadata of the// generated specification (title, description, version, base path, β¦).//
// Schemes: https// Version: 1.0.0// BasePath: /v1//
// Consumes:// - application/json//
// Produces:// - application/json//
// swagger:meta
A swagger:model annotation turns a Go struct into a definition; field-level
comments become validations and descriptions:
// Pet is a single pet in the store.//
// swagger:model PettypePetstruct {
// The id of the pet.//
// required: true// minimum: 1IDint64`json:"id"`// The name of the pet.//
// required: true// min length: 1Namestring`json:"name"`// The tags associated with this pet.Tags []string`json:"tags,omitempty"`}
The returned *spec.Swagger is the standard
github.com/go-openapi/spec
document β marshal it to JSON or YAML, feed it to a validator, or merge it onto
an existing spec via Options.InputSpec.
Options worth knowing
Field
Effect
Packages
Relative go list patterns to scan (e.g. ./...).
WorkDir
Directory the patterns resolve against.
ScanModels
Also emit definitions for swagger:model types.
InputSpec
Overlay: merge discoveries on top of an existing spec.
Tutorials β the worked, by-concept version of
the above, each with the spec it produces.
Annotation index β every annotation at a
glance, linked to its example and its full reference.
Maintainers reference β the complete
annotation vocabulary, keywords, and grammar.
Tutorials
These tutorials teach codescan by spec concept, not annotation by
annotation. Each page takes one thing you want in your OpenAPI document β a
model definition, a route, a validated field β and shows the Go annotation that
produces it next to the resulting JSON, side by side.
Every Go snippet on these pages comes from the test-covered
docs/examples
module, and every JSON pane is a golden file a test regenerates β so the
examples cannot drift from what the scanner actually emits.
Reading the panes
The example panes put the annotation in on the left and the spec concept
out on the right:
Annotated Go
// Pet is a single pet in the store.//
// swagger:model PettypePetstruct {
// The id of the pet.//
// required: true// minimum: 1IDint64`json:"id"`// The name of the pet.//
// required: true// min length: 1Namestring`json:"name"`// The tags associated with this pet.Tags []string`json:"tags,omitempty"`}
Drive JSON-Schema validations from field doc comments β numeric ranges,
length and array bounds, patterns, formats, and enums β and understand the
reduced surface on parameters and headers.
The smallest end-to-end use of codescan: annotate a package, scan it, and
get back a Swagger 2.0 document.
When you want the exhaustive rule rather than an example, every page links into
the Maintainers reference; the
Annotation index maps every annotation to
both.
Subsections of Tutorials
Model definitions
A definitions entry is the most common thing you ask codescan to produce. This
page walks the annotations that create and shape one, from the plain
swagger:model struct to the per-type overrides. Each pane pairs the annotated
Go (left) with the exact fragment the scanner emits (right) β both come from the
test-covered docs/examples/concepts/models
package.
For the exhaustive rule on any annotation below, follow its link to the
Maintainers reference.
swagger:model
swagger:model publishes a Go struct as a definition. Field doc comments become
property descriptions; json tags drive the property names; the Go type drives
the JSON-Schema type / format.
Annotated Go
// Pet is a single pet in the store.//
// swagger:modeltypePetstruct {
// ID is the unique identifier.IDint64`json:"id"`// Name is the pet's display name.Namestring`json:"name"`// Tags categorise the pet.Tags []string`json:"tags,omitempty"`}
swagger:strfmt <name> marks a named string type as a custom format. The type
does not become its own definition β instead, every field typed by it renders as
{type: string, format: <name>}.
Annotated Go
// MAC is a hardware address rendered as a colon-separated hex string.//
// swagger:strfmt mactypeMACstringfunc (mMAC) MarshalText() ([]byte, error) { return []byte(m), nil }
func (m*MAC) UnmarshalText(b []byte) error { *m = MAC(b); returnnil }
// Device exposes a strfmt-typed field: wherever MAC appears it renders inline// as {type: string, format: mac}.//
// swagger:modeltypeDevicestruct {
// Addr is the hardware address.AddrMAC`json:"addr"`}
swagger:enum <name> collects the type’s const values. When a model field
references the type, the property carries the enum array and an
x-go-enum-desc extension built from the per-value doc comments. (The enum type
is reachable, and so emitted, only because a model field points at it.)
Annotated Go
// Priority is the urgency level on a task.//
// swagger:enum PrioritytypePrioritystringconst (
// PriorityLow is for tasks that can wait.PriorityLowPriority = "low"// PriorityMedium is the default.PriorityMediumPriority = "medium"// PriorityHigh is for tasks that must run soon.PriorityHighPriority = "high")
// Task is a unit of work carrying an enum-typed field. Referencing Priority// from a model is what makes the enum reachable, and so emitted.//
// swagger:modeltypeTaskstruct {
// Priority is the task's urgency.PriorityPriority`json:"priority"`}
{
"description": "Task is a unit of work carrying an enum-typed field. Referencing Priority\nfrom a model is what makes the enum reachable, and so emitted.",
"type": "object",
"properties": {
"priority": {
"description": "Priority is the task's urgency.\nlow PriorityLow is for tasks that can wait.\nmedium PriorityMedium is the default.\nhigh PriorityHigh is for tasks that must run soon.",
"type": "string",
"enum": [
"low",
"medium",
"high" ],
"x-go-enum-desc": "low PriorityLow is for tasks that can wait.\nmedium PriorityMedium is the default.\nhigh PriorityHigh is for tasks that must run soon.",
"x-go-name": "Priority" }
},
"x-go-package": "github.com/go-openapi/codescan/docs/examples/concepts/models"}
Embedding base types under swagger:allOf composes a schema. Each embedded base
becomes a $ref arm of the allOf; the struct’s own (non-embedded) fields form
a final inline arm. That last arm is inline rather than a $ref because those
fields are new to this type β they belong to no existing definition to point at.
Here Dog embeds two bases (Animal, Tagged) and adds breed, producing
three arms: two $refs and one inline object.
Annotated Go
// Animal is one abstract base.//
// swagger:modeltypeAnimalstruct {
// Kind discriminates the animal.Kindstring`json:"kind"`}
// Tagged is a second reusable base.//
// swagger:modeltypeTaggedstruct {
// Tags label the resource.Tags []string`json:"tags"`}
// Dog composes two base models plus its own fields: each embedded base becomes// a $ref arm of the allOf, and the struct's own (non-embedded) fields β which// are new and cannot be a $ref β form the final inline arm.//
// swagger:modeltypeDogstruct {
// swagger:allOfAnimal// swagger:allOfTagged// Breed is the dog's breed.Breedstring`json:"breed"`}
{
"description": "Dog composes two base models plus its own fields: each embedded base becomes\na $ref arm of the allOf, and the struct's own (non-embedded) fields β which\nare new and cannot be a $ref β form the final inline arm.",
"allOf": [
{
"$ref": "#/definitions/Animal" },
{
"$ref": "#/definitions/Tagged" },
{
"type": "object",
"properties": {
"breed": {
"description": "Breed is the dog's breed.",
"type": "string",
"x-go-name": "Breed" }
}
}
],
"x-go-package": "github.com/go-openapi/codescan/docs/examples/concepts/models"}
swagger:type <type> overrides the type codescan would infer. Here a [16]byte
field is published as a string.
Annotated Go
// ULID is a 128-bit identifier stored as bytes but rendered as a string.//
// swagger:type stringtypeULID [16]byte// Token carries a field whose inferred type is overridden, inline.//
// swagger:modeltypeTokenstruct {
// ID renders as a string despite its [16]byte Go type.IDULID`json:"id"`}
{
"type": "object",
"title": "Token carries a field whose inferred type is overridden, inline.",
"properties": {
"id": {
"description": "ID renders as a string despite its [16]byte Go type.",
"type": "string",
"x-go-name": "ID" }
},
"x-go-package": "github.com/go-openapi/codescan/docs/examples/concepts/models"}
The accepted values are the scalar Swagger types β string, integer,
number, boolean, object (plus the Go builtin names codescan resolves).
array and file are not accepted here; an unrecognized value leaves the field
on its underlying Go type.
swagger:name
A model defined as an interface publishes one property per nullary method.
By default the property name is the camelCased method name β so Maker()
already becomes maker with no annotation. swagger:name <name> is the
override for when that default is not what you want (interface methods
cannot carry a json tag). Here StructType() would default to structType;
the annotation publishes it as jsonClass instead.
Annotated Go
// Car is exposed as a schema via its method set. Interface methods cannot carry// a json tag, so by default each property takes the camelCased method name;// swagger:name overrides that where the default is not what you want.//
// swagger:modeltypeCarinterface {
// Maker is the manufacturer. With no override the property is the// camelCased method name, "maker".Maker() string// StructType is the polymorphic class. Without the override the property// would be "structType"; swagger:name publishes it as "jsonClass".//
// swagger:name jsonClassStructType() string}
{
"description": "Car is exposed as a schema via its method set. Interface methods cannot carry\na json tag, so by default each property takes the camelCased method name;",
"type": "object",
"properties": {
"jsonClass": {
"description": "StructType is the polymorphic class. Without the override the property\nwould be \"structType\"; swagger:name publishes it as \"jsonClass\".",
"type": "string",
"x-go-name": "StructType" },
"maker": {
"description": "Maker is the manufacturer. With no override the property is the\ncamelCased method name, \"maker\".",
"type": "string",
"x-go-name": "Maker" }
},
"x-go-package": "github.com/go-openapi/codescan/docs/examples/concepts/models"}
swagger:ignore drops a declaration from the output. The scanner sees Secret,
classifies it, then excludes it β so it never reaches the definitions (a fact
the example’s TestIgnoreOmitsType asserts).
// Secret never reaches the spec.//
// swagger:ignoretypeSecretstruct {
// Token is internal.Tokenstring`json:"token"`}
Routes & operations β wire
these models into paths, parameters and responses.
Validations β constrain field values
with keyword annotations.
Shaping the output β alias handling,
$ref vs inline, nullable pointers and more.
Routes & operations
Routes and operations turn an annotation into an entry in the spec’s paths
map, wired to the parameters it accepts and the responses it returns. This page
covers the two operation annotations and the companion structs they reference.
Each pane pairs the annotated Go (left) with the exact fragment the scanner
emits (right), from the test-covered
docs/examples/concepts/routes
package.
For the exhaustive rule on any annotation below, follow its link to the
Maintainers reference; the
Parameters: / Responses: body grammars are covered in
Sub-languages.
swagger:route
swagger:route <METHOD> <path> [tags] <operationID> declares a path and its
operation in one annotation. The body’s responses: block ties status codes to
named responses ($ref into the spec’s responses). It lives in a plain
comment block β no Go declaration required.
Annotated Go
// swagger:route GET /pets pets listPets//
// Lists pets in the store, optionally filtered by tag.//
// responses://
// 200: petsResponse// default: errorResponse
swagger:operation carries the same header but spells the operation out as a
YAML document after a --- fence β useful when you want to author the operation
object directly (here a path parameter and an inline $ref response schema).
Annotated Go
// swagger:operation GET /pets/{id} pets getPet//
// ---// summary: Get a pet by ID.// parameters:// - name: id// in: path// required: true// type: integer// format: int64// responses:// '200':// description: the requested pet// schema:// $ref: '#/definitions/Pet'// default:// $ref: '#/responses/errorResponse'
swagger:parameters <operationID>β¦ declares a struct whose fields become the
parameters of the named operation(s). Field doc comments carry in:, the
validations, and the description; the parameters attach to every operation ID
listed.
Annotated Go
// ListPetsParams is the parameter set for the listPets operation. Each field// becomes one parameter; the operation IDs after swagger:parameters name the// operations the set applies to.//
// swagger:parameters listPetstypeListPetsParamsstruct {
// Tag filters pets by tag.//
// in: queryTagstring`json:"tag"`// Limit caps the number of results.//
// in: query// minimum: 1// maximum: 100Limitint32`json:"limit"`}
swagger:response <name> declares a struct as a named entry in the spec’s
top-level responses. A Body field (or in: body) becomes the response
schema; routes reference it by name. Here the body is a []Pet, so the schema
is an array of $refs.
Annotated Go
// PetsResponse is the list returned by listPets.//
// swagger:response petsResponsetypePetsResponsestruct {
// in: bodyBody []Pet}
// ErrorResponse is the default error payload.//
// swagger:response errorResponsetypeErrorResponsestruct {
// in: bodyBodystruct {
// Message is a human-readable error message.Messagestring`json:"message"` }
}
swagger:file on a parameter field marks it as a binary upload β the parameter
emits as {type: file}. It belongs on a formData field of a
swagger:parameters struct.
Annotated Go
// swagger:route POST /pets/{id}/photo pets uploadPetPhoto//
// responses://
// 200: petsResponse// UploadParams is the multipart upload for the uploadPetPhoto operation.//
// swagger:parameters uploadPetPhototypeUploadParamsstruct {
// Photo is the image to upload.//
// in: formData// swagger:filePhotoio.ReadCloser`json:"photo"`}
Shaping the output β $ref vs inline,
aliases, and the other rendering knobs.
Validations
Validations are keyword-driven: you write keyword: value lines in a field’s
doc comment and they become minimum, maxLength, pattern, enum, and the
rest of the validation surface on that property. Each pane below pairs the
annotated Go (left) with the fragment the scanner emits (right), from the
test-covered docs/examples/concepts/validations
package.
For the per-keyword reference card β value shapes, aliases, and legal contexts β
see Keywords.
On a model field β the full surface
A swagger:model field accepts the full JSON-schema validation vocabulary:
Numeric β minimum, maximum, multipleOf (on Price).
Length β min length / max length (on Name).
Arrays β min items / max items / unique (on Tags).
Pattern β a regular expression (on SKU).
Enum β a fixed value set (on Grade).
Required β required: true lifts the property into the schema’s
object-level required array (sku).
Annotated Go
// Product is a model whose fields carry the full JSON-schema validation surface.//
// swagger:modeltypeProductstruct {
// SKU is the stock code.//
// required: true// pattern: ^[A-Z]{3}-[0-9]{4}$SKUstring`json:"sku"`// Price is the price in cents.//
// minimum: 1// maximum: 1000000// multipleOf: 1Priceint64`json:"price"`// Name is the display name.//
// min length: 1// max length: 120Namestring`json:"name"`// Grade is a quality band.//
// enum: A,B,CGradestring`json:"grade"`// Tags label the product.//
// min items: 1// max items: 10// unique: trueTags []string`json:"tags"`}
Simple schemas have a reduced surface. Parameters other than in: body, and
response headers, are simple schemas in OpenAPI 2.0 β not full JSON schemas.
They accept the validation subset (maximum/minimum/multipleOf,
maxLength/minLength/pattern, maxItems/minItems/uniqueItems, enum,
plus the simple-schema-only collectionFormat) but not schema-only
constructs. A schema-only keyword such as readOnly placed on a query parameter
is simply not emitted β spec.Parameter has nowhere to carry it.
The same numeric and length keywords work on a query parameter; arrays add
collectionFormat:
Annotated Go
// SearchParams is the simple-schema parameter set for searchProducts. Query// parameters accept the reduced OAS 2.0 validation surface.//
// swagger:parameters searchProductstypeSearchParamsstruct {
// Q is the search text.//
// in: query// min length: 3// max length: 50Qstring`json:"q"`// Limit caps the number of results.//
// in: query// minimum: 1// maximum: 100Limitint32`json:"limit"`// Sort lists the sort fields.//
// in: query// collection format: csv// unique: trueSort []string`json:"sort"`}
A response header is also a simple schema, so it takes the same reduced
validation set (here minimum on an integer header). Note headers carry no
required flag.
Annotated Go
// RateLimited is a response carrying a validated header (a simple schema).//
// swagger:response rateLimitedtypeRateLimitedstruct {
// XRateRemaining is the remaining request budget.//
// minimum: 0XRateRemainingint32`json:"X-Rate-Remaining"`}
Keyword reference β every keyword,
its value shape, and where it is legal.
Examples & defaults
Example values and defaults are documentation that travels with the schema: an
example: shows a caller what a value looks like, a default: declares what the
field is when the caller omits it. Both are typed to the field β a numeric
default on an integer field is a JSON number, not a string. The panes below pair
the annotated Go with the fragment the scanner emits, from the test-covered
docs/examples/concepts/examples
package.
For the exact value shapes these keywords accept, see
Keywords.
example
example: <value> attaches an example to the property, coerced to the field’s
type β Hello, world! stays a string, 3 becomes a number.
Annotated Go
// Greeting carries an example value for documentation.//
// swagger:modeltypeGreetingstruct {
// Message is the greeting text.//
// example: Hello, world!Messagestring`json:"message"`// Count is how many times to repeat it.//
// example: 3Countint32`json:"count"`}
{
"type": "object",
"title": "Greeting carries an example value for documentation.",
"properties": {
"count": {
"description": "Count is how many times to repeat it.",
"type": "integer",
"format": "int32",
"x-go-name": "Count",
"example": 3 },
"message": {
"description": "Message is the greeting text.",
"type": "string",
"x-go-name": "Message",
"example": "Hello, world!" }
},
"x-go-package": "github.com/go-openapi/codescan/docs/examples/concepts/examples"}
default: <value> sets the property’s default, again typed to the field β 8080
is a number, false a boolean, auto a string.
Annotated Go
// Settings carries default values applied when a field is omitted.//
// swagger:modeltypeSettingsstruct {
// Port is the listen port.//
// default: 8080Portint32`json:"port"`// Mode is the run mode.//
// default: autoModestring`json:"mode"`// Verbose toggles verbose logging.//
// default: falseVerbosebool`json:"verbose"`}
swagger:default is a narrow, value-only classifier hint placed on a var or
const. It does not publish a spec entity of its own β it has no standalone
output β so most spec defaults are carried by the default: keyword above
rather than this annotation.
// DefaultPort is the fallback port used wherever Port is not supplied. The// swagger:default annotation is a narrow value-only discovery hint.//
// swagger:defaultvarDefaultPort = 8080
Validations β constrain the values
these examples illustrate.
Other type decorators
Beyond validations, a couple of keyword decorators annotate a property’s or
operation’s role. The panes below pair the annotated Go with the fragment the
scanner emits, from the test-covered
docs/examples/concepts/decorators
package.
For the value shapes and legal contexts of each, see the
Keyword reference.
readOnly
read only: true on a model field marks the property readOnly β the server
sets it, clients must not.
Annotated Go
// Token is issued by the server.//
// swagger:modeltypeTokenstruct {
// ID is assigned by the server and cannot be set by clients.//
// read only: trueIDstring`json:"id"`// Value is the token value.Valuestring`json:"value"`}
{
"type": "object",
"title": "Token is issued by the server.",
"properties": {
"id": {
"description": "ID is assigned by the server and cannot be set by clients.",
"type": "string",
"x-go-name": "ID",
"readOnly": true },
"value": {
"description": "Value is the token value.",
"type": "string",
"x-go-name": "Value" }
},
"x-go-package": "github.com/go-openapi/codescan/docs/examples/concepts/decorators"}
deprecated is an operation-level flag in OpenAPI 2.0. The Schema object has
no native deprecated, so deprecated: on a model field is not emitted on the
property (and produces no x-deprecated).
A single swagger:meta block on a package doc comment carries the document’s
top-level metadata: its info (title, description, version, license, contact),
the host and basePath, the default schemes, and consumes/produces. The
pane pairs the annotated package with the document it produces, from the
test-covered docs/examples/concepts/meta
package.
swagger:meta
The block lives in the package doc comment. The title comes from the first
line with the Package <name> prefix stripped; the following paragraph becomes
the description. The indented Key: value lines and list blocks populate the
rest β License: and Contact: parse into structured objects.
Package doc comment
// Package meta Pet Store.//
// A small API that demonstrates the document-level swagger:meta block: the// package doc comment carries the spec's top-level metadata.//
// Schemes: https// Host: api.example.com// BasePath: /v1// Version: 1.2.0// License: Apache 2.0 https://www.apache.org/licenses/LICENSE-2.0.html// Contact: API Team <api@example.com> https://example.com/support//
// Consumes:// - application/json//
// Produces:// - application/json//
// swagger:metapackagemeta
For the full meta keyword surface (security definitions, external docs,
extensions, terms of service), see the
swagger:meta reference
and the meta keywords.
This capstone scans a tiny annotated “petstore” package and produces a Swagger
2.0 spec β the concepts from the pages above, assembled into one runnable
example. It is the worked version of
usage as a library.
The annotated API
A package-level swagger:meta block sets the top-level metadata:
// Package petstore Petstore API//
// A tiny pet store, used to demonstrate codescan annotations: the package// comment is a `swagger:meta` block carrying the top-level metadata of the// generated specification (title, description, version, base path, β¦).//
// Schemes: https// Version: 1.0.0// BasePath: /v1//
// Consumes:// - application/json//
// Produces:// - application/json//
// swagger:meta
A swagger:model struct becomes a definition, with field comments driving
validations:
// Pet is a single pet in the store.//
// swagger:model PettypePetstruct {
// The id of the pet.//
// required: true// minimum: 1IDint64`json:"id"`// The name of the pet.//
// required: true// min length: 1Namestring`json:"name"`// The tags associated with this pet.Tags []string`json:"tags,omitempty"`}
Marshalling the returned *spec.Swagger to JSON yields the document below β
the meta block became the top-level info / basePath, the swagger:route
became the /pets path, and the swagger:model became the Pet definition:
{
"consumes": [
"application/json" ],
"produces": [
"application/json" ],
"schemes": [
"https" ],
"swagger": "2.0",
"info": {
"description": "A tiny pet store, used to demonstrate codescan annotations: the package\ncomment is a `swagger:meta` block carrying the top-level metadata of the\ngenerated specification (title, description, version, base path, β¦).",
"title": "Petstore API",
"version": "1.0.0" },
"basePath": "/v1",
"paths": {
"/pets": {
"get": {
"tags": [
"pets" ],
"summary": "Lists all the pets in the store.",
"operationId": "listPets",
"responses": {
"200": {
"$ref": "#/responses/petsResponse" }
}
}
}
},
"definitions": {
"Pet": {
"type": "object",
"title": "Pet is a single pet in the store.",
"required": [
"id",
"name" ],
"properties": {
"id": {
"description": "The id of the pet.",
"type": "integer",
"format": "int64",
"minimum": 1,
"x-go-name": "ID" },
"name": {
"description": "The name of the pet.",
"type": "string",
"minLength": 1,
"x-go-name": "Name" },
"tags": {
"description": "The tags associated with this pet.",
"type": "array",
"items": {
"type": "string" },
"x-go-name": "Tags" }
},
"x-go-package": "github.com/go-openapi/codescan/docs/examples/petstore" }
},
"responses": {
"petsResponse": {
"description": "petsResponse is the list of pets returned by listPets.",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/Pet" }
}
}
}
}
This JSON is not hand-written: it is a golden file the example’s test
regenerates and compares on every run (UPDATE_GOLDEN=1 go test ./...). Because
the example is ordinary, test-covered Go, go test ./docs/examples/... keeps
the page honest β if the scanner’s output changes, CI fails before the
documentation can go stale.
Shaping the output
The same annotated Go can render into the spec in more than one shape. A handful
of codescan.Options
let you choose: should an alias become a $ref or expand inline? Should a
field’s description survive next to a $ref? Should pointer fields be marked
nullable?
Each guide here is task-oriented β “I want the output to look like this” β and
shows the same input rendered both ways, as before/after golden output the
example tests verify.
codescan never invents definitions β a type appears only when it is reachable
or registered. Understand reachability and swagger:model so nothing goes
missing or appears unexpectedly.
Choose how Go type aliases render β dissolved to their target, or exposed as a
first-class $ref via swagger:model, with RefAliases / TransparentAliases.
Scan source guarded by Go build constraints by passing build tags to the
scanner.
For the field-by-field meaning of each option, see the
Options godoc.
Subsections of Shaping the output
Scoping the scan
Several options narrow what codescan looks at, independent of how individual
types render. They decide which packages are loaded and which discovered
operations survive into the spec.
Package patterns and WorkDir
Options.Packages takes relative go list-style patterns β ./petstore,
./... for a whole tree β resolved against Options.WorkDir (the module root).
This is the worked form in the Getting started
guide:
Options.Include and Options.Exclude are lists of regular expressions matched
against package import paths. Include acts as an allow-list (when non-empty,
only matching packages are scanned); Exclude removes matches. Use them to keep
internal or generated packages out of the spec:
Options.IncludeTags / Options.ExcludeTags filter operations by their
Swagger tags after discovery β handy for publishing a public subset of an API
while keeping the admin routes in the source:
By default codescan may follow types into dependency packages to resolve
referenced models. Options.ExcludeDeps keeps the scan within your own module,
leaving out types pulled in from dependencies.
Build constraints get their own guide β see
Build tags.
When the scanner emits a type
codescan does not emit a definition for every type it can see. A named type
reaches the spec when either of these holds:
it is reachable β referenced (directly or transitively) from an operation,
parameter, response, or another emitted model; or
it is registered β annotated swagger:model, which (with
Options.ScanModels) publishes it even when nothing references it.
A type that is neither reachable nor registered is simply absent β the scanner
never invents it. The package below has one of each case:
// Order is reached only through Cart below. A referenced named type is emitted// as a $ref target even without swagger:model.typeOrderstruct {
// ID is the order identifier.IDstring`json:"id"`}
// Cart references Order, so Order gets a definition and the field a $ref.//
// swagger:modeltypeCartstruct {
// Order is the referenced (and therefore emitted) nested model.OrderOrder`json:"order"`}
// Standalone is never referenced, but swagger:model together with ScanModels// publishes it anyway.//
// swagger:modeltypeStandalonestruct {
// Label is a free-text label.Labelstring`json:"label"`}
// Orphan is never referenced and carries no swagger:model β the scanner does// not invent it, so it never reaches the spec.typeOrphanstruct {
// Secret is internal.Secretstring`json:"secret"`}
Scanned with ScanModels: true, the definitions are:
{
"Cart": {
"type": "object",
"title": "Cart references Order, so Order gets a definition and the field a $ref.",
"properties": {
"order": {
"$ref": "#/definitions/Order" }
},
"x-go-package": "github.com/go-openapi/codescan/docs/examples/shaping/discovery" },
"Order": {
"description": "Order is reached only through Cart below. A referenced named type is emitted\nas a $ref target even without swagger:model.",
"type": "object",
"properties": {
"id": {
"description": "ID is the order identifier.",
"type": "string",
"x-go-name": "ID" }
},
"x-go-package": "github.com/go-openapi/codescan/docs/examples/shaping/discovery" },
"Standalone": {
"description": "Standalone is never referenced, but swagger:model together with ScanModels\npublishes it anyway.",
"type": "object",
"properties": {
"label": {
"description": "Label is a free-text label.",
"type": "string",
"x-go-name": "Label" }
},
"x-go-package": "github.com/go-openapi/codescan/docs/examples/shaping/discovery" }
}
Order β has noswagger:model, yet it is emitted (as a $ref
target) because Cart references it. You do not need to annotate every nested
type.
Standalone β a swagger:model that nothing references; ScanModels
publishes it anyway.
Orphan β neither referenced nor annotated, so it never appears.
Info
If a model is missing from your spec, it is almost always unreachable: no
operation/parameter/response/model leads to it. Either reference it, or annotate
it swagger:model and scan with ScanModels.
Nullable pointers
Swagger 2.0 has no native nullable flag; the go-openapi toolchain uses the
x-nullable vendor extension. Options.SetXNullableForPointers decides whether
pointer-typed struct fields acquire it automatically. The model below has two
pointer fields:
// Profile has required and optional (pointer) fields.//
// swagger:modeltypeProfilestruct {
// Name is always present.Namestring`json:"name"`// Nickname is optional.Nickname*string`json:"nickname"`// Age is optional.Age*int32`json:"age"`}
omitempty changes the meaning. A pointer field tagged
json:"β¦,omitempty" is treated as optional (may be absent) rather than
nullable (may be null), so it does not receive x-nullable even with the
option on. Drop omitempty when you mean the value can be present-but-null.
Vendor extensions
By default codescan records where each spec object came from in Go via
x-go-name (and x-go-package on definitions) β useful for round-tripping and
code generation. Options.SkipExtensions removes them for a leaner spec.
// Widget is a small model.//
// codescan records each field's Go origin as vendor extensions unless// SkipExtensions is set.//
// swagger:modeltypeWidgetstruct {
// Label is the display label.Labelstring`json:"label"`// Size is the widget size in pixels.Sizeint32`json:"size"`}
{
"description": "codescan records each field's Go origin as vendor extensions unless\nSkipExtensions is set.",
"type": "object",
"title": "Widget is a small model.",
"properties": {
"label": {
"description": "Label is the display label.",
"type": "string",
"x-go-name": "Label" },
"size": {
"description": "Size is the widget size in pixels.",
"type": "integer",
"format": "int32",
"x-go-name": "Size" }
},
"x-go-package": "github.com/go-openapi/codescan/docs/examples/shaping/extensions"}
{
"description": "codescan records each field's Go origin as vendor extensions unless\nSkipExtensions is set.",
"type": "object",
"title": "Widget is a small model.",
"properties": {
"label": {
"description": "Label is the display label.",
"type": "string" },
"size": {
"description": "Size is the widget size in pixels.",
"type": "integer",
"format": "int32" }
}
}
SkipExtensions removes the scanner-derived x-go-* extensions. Extensions you
author yourself (via the Extensions: keyword) are not affected.
Descriptions beside a $ref
When a struct field’s only decoration is a description and its Go type resolves
to a named model (a $ref), JSON Schema draft 4 cannot carry a sibling
description next to a $ref. Options.DescWithRef decides what happens to
that description.
// Address is a referenced model.//
// swagger:modeltypeAddressstruct {
// Street is the street line.Streetstring`json:"street"`}
// Person references Address through a field whose only decoration is a// description.//
// swagger:modeltypePersonstruct {
// Home is where the person lives.HomeAddress`json:"home"`}
By default the description is dropped (a bare $ref); with DescWithRef it is
preserved by wrapping the $ref in a single-arm allOf:
Default β description dropped
{
"description": "Person references Address through a field whose only decoration is a\ndescription.",
"type": "object",
"properties": {
"home": {
"$ref": "#/definitions/Address" }
},
"x-go-package": "github.com/go-openapi/codescan/docs/examples/shaping/descref"}
{
"description": "Person references Address through a field whose only decoration is a\ndescription.",
"type": "object",
"properties": {
"home": {
"description": "Home is where the person lives.",
"allOf": [
{
"$ref": "#/definitions/Address" }
],
"x-go-name": "Home" }
},
"x-go-package": "github.com/go-openapi/codescan/docs/examples/shaping/descref"}
When the field carries more than a description β a validation override or a
user-authored extension β the allOf wrapper is emitted regardless of this
flag, because the override would otherwise be lost. DescWithRef only governs
the description-only case.
Alias rendering
A Go type alias (type Price = Money) is, to the Go type system, literally the
same type as its target. codescan’s default is to treat it that way: at a use
site the alias dissolves to its target, producing no definition of its own.
Annotated Go
// Money is the underlying model.//
// swagger:modeltypeMoneystruct {
// Cents is the amount in cents.Centsint64`json:"cents"`// Currency is the ISO currency code.Currencystring`json:"currency"`}
// Price is a Go alias of Money. By default an alias is a Go implementation// detail: at use sites it dissolves to its target, producing no definition of// its own.typePrice = Money// Invoice references Price; the field resolves to Money.//
// swagger:modeltypeInvoicestruct {
// Total is the invoice total.TotalPrice`json:"total"`}
Invoice.total is typed Price, but the field resolves straight to
#/definitions/Money β Price itself never appears.
Exposing an alias as a first-class entity
This is an advanced, rarely-needed case. To keep the alias name in the spec
(its own definition that other schemas $ref), annotate the alias with
swagger:model. Two top-level options then govern how that first-class alias
definition is shaped:
default (expand) β the alias definition is a structural copy of the
target.
RefAliases: true β the alias definition is a $ref chain to the target
({"$ref": "#/definitions/Money"}), preserving the alias name at use sites.
TransparentAliases: true β aliases dissolve to their target everywhere,
overriding the per-declaration annotation (use sites become $ref to the
target, as in the default-dissolve example above).
For the precise per-mode contract and the canonical witnesses, see the
swagger:alias reference
and the fixtures/enhancements/alias-calibration-embed golden trio.
Note
Most APIs never need first-class aliases β prefer naming a real swagger:model
type over aliasing one. Reach for RefAliases / TransparentAliases only when
you specifically need to control whether an alias name survives in the output.
Overlaying a spec
Options.InputSpec seeds the scan with an existing *spec.Swagger: codescan
merges what it discovers on top of it rather than starting from a blank
document. Use it to keep hand-authored top-level metadata or a hand-written
definition, or to compose a spec across several scans.
The scanned package contributes one model:
// Widget is discovered by the scan and merged onto the input spec.//
// swagger:modeltypeWidgetstruct {
// ID identifies the widget.IDstring`json:"id"`}
The document’s info, host, basePath and the hand-authored Health
definition survive untouched; only the discovered definitions are added.
Build tags
Go files can be guarded by //go:build constraints. By default codescan loads a
package under the default build configuration, so tag-gated files are skipped.
Options.BuildTags passes the tags through to the package loader, so the
annotations in those files are scanned too.
The package has an always-present model plus this one, in a file that opens with
the constraint //go:build experimental:
// Experimental is only scanned when the "experimental" build tag is set.//
// swagger:modeltypeExperimentalstruct {
// Beta flags a beta-only feature.Betabool`json:"beta"`}
BuildTags accepts the same comma-separated form as go build -tags.
Annotation index
The complete swagger:* vocabulary, one row each. By example jumps to the
tutorial that shows the annotation as runnable Go next to the spec it produces;
Reference jumps to the exhaustive rule in the Maintainers compendium.
Validations, examples and defaults inside a block are driven by keywords
(minimum:, pattern:, enum:, example:, default:, β¦), not annotations.
See the Validations and
Examples & defaults
tutorials, and the Keyword reference.
Maintainers
This section is the reference compendium: the precise, exhaustive
description of the language codescan parses. It is written for people who want
the full contract β annotation authors looking up an exact rule, and
contributors porting, extending, or debugging the parser.
If you are learning codescan by example, start with the
Tutorials instead β they show the same concepts
as runnable Go with the spec they produce, side by side. The
Annotation index cross-references every
annotation to both its tutorial and its entry here.
The formal ISO-14977 EBNF the parser implements, from comment preprocessing through the typed walker.
Annotations β the swagger:* vocabulary:
what each annotation does, where it attaches, its argument shape, and the
keywords it admits. The author-facing normative reference.
Keywords β the per-keyword reference card:
every keyword: value form, its value shape, and the contexts where it is
legal.
Sub-languages β the smaller languages
embedded inside annotation bodies (Parameters: / Responses: grammars,
YAML surfaces, prose classification).
Grammar β the formal ISO-14977 EBNF the
parser implements, from comment preprocessing through the typed walker.
Subsections of Maintainers
Annotations
Annotations are the swagger:<name> markers the scanner recognises in
Go doc comments. Each annotation classifies the surrounding
declaration β telling the scanner “this is a model definition”, “this
is a route handler”, “this is meta-information about the API” β and
opens the door for keywords inside the same comment
block.
There are twelve annotations. They divide cleanly by what they
attach to:
Spec-level: swagger:meta.
Model declarations: swagger:model, swagger:strfmt,
swagger:enum, swagger:allOf, swagger:alias.
Local hints: swagger:ignore, swagger:name, swagger:type,
swagger:file, swagger:default.
This file is the author-first reference. Each entry covers:
What the annotation does and what it produces in the spec.
Where in the Go source it goes (package doc, type doc, field doc).
The shape of any argument the annotation accepts.
A short Go sample.
A pointer to the keywords that are legal inside the block.
A pointer to a real fixture in this repo for the full executable
example.
For the per-keyword reference, see keywords.md.
For the embedded sub-languages (Parameters: and Responses: body
grammars, YAML extensions, etc.), see
sub-languages.md. For the formal grammar,
see grammar.md.
An annotation is recognised when it appears at the start of a comment
line in a doc comment. Leading whitespace, the // marker, and any
/* */ block-comment continuation noise are stripped β the lexer
applies the same content-prefix-trim that every other godoc-aware
tool does.
Annotations attach to whichever Go declaration owns the comment
group:
Package doc (// Package foo β¦ followed by package foo) β
carries swagger:meta.
Type declaration (type T struct { β¦ }, type T int,
type T = Other) β carries swagger:model, swagger:strfmt,
swagger:enum, swagger:allOf, swagger:alias, swagger:ignore,
swagger:type.
Function or variable declaration (func ServeAPI() { β¦ },
var DoIt = func() { β¦ }) β carries swagger:route,
swagger:operation.
Struct field doc β carries swagger:name, swagger:type,
swagger:ignore, plus any of the keyword reference
entries legal in schema / param / header context.
One comment group may carry MORE than one annotation when the
combinations are semantically compatible β e.g. swagger:model +
swagger:type together overrides the auto-detected Go type while
still publishing the model. The grammar parses both and the builder
honours both.
The first annotation in source order wins as the “primary”
classifier β for example, a comment carrying swagger:model followed
by swagger:ignore produces a model (the ignore is silently
overridden because only the source-order-first annotation drives the
short-circuit). Subsequent annotations are still parsed and visible
via Block.AnnotationKind()-iteration, but the primary classifier
determines which builder owns the decl.
Annotation argument shapes
After the swagger:<name> head, an annotation may carry positional
arguments. The shapes:
No args: swagger:meta, swagger:ignore, swagger:enum,
swagger:allOf, swagger:file, swagger:default β bare
annotation, the surrounding decl supplies the entity name.
One IDENT arg: swagger:model Pet, swagger:response errorResponse, swagger:strfmt uuid, swagger:name fullName,
swagger:type integer, swagger:alias TimestampAlias β the
argument overrides or names the entity.
One IDENT arg, optional: swagger:model (bare β derives the
name from the Go decl) vs swagger:model Pet (overrides).
List of IDENT args: swagger:parameters listItems createItem
β declares the parameters group as legal for multiple operations.
Header line: swagger:route GET /pets pets users listPets and
swagger:operation GET /pets users listPets β a structured header
carrying method, path, tags, and operation ID. See the
per-annotation entries for the exact rules.
swagger:meta
What it does. Declares the package as the OpenAPI spec
container. The scanner reads the package doc comment for top-level
spec fields: title (via stripPackagePrefix of
the doc’s first line), description, license, contact, host,
basePath, version, schemes, consumes, produces, securityDefinitions,
extensions, and the rest of the meta keyword surface.
Where it goes. On the package doc comment.
Argument shape. No args. Bare annotation.
Sample.
// Package petstore Petstore API.//
// The purpose of this application is to provide an application// that is using plain Go code to define an API.//
// Schemes: http, https// Host: petstore.swagger.io// BasePath: /v2// Version: 1.0.0//
// Consumes:// - application/json//
// Produces:// - application/json//
// swagger:metapackagepetstore
Legal keywords. All meta single-line keywords
(schemes, version, host, basePath, license, contact) plus
the meta-scope body keywords
(consumes, produces, security, securityDefinitions,
extensions, infoExtensions, tos, externalDocs).
Full example.fixtures/goparsing/spec/api.go.
swagger:model
What it does. Declares a Go type as a published model. The
scanner walks the type, emits a schema into the spec’s definitions
map, and resolves cross-references between models.
Where it goes. On a type declaration (type T struct { β¦ },
type T int, type T = Other, β¦).
Argument shape. Optional IDENT β the name the model takes in
definitions. Default: the Go type’s name.
Sample.
// Pet is the petstore's primary entity.//
// swagger:modeltypePetstruct {
// ID is the unique identifier.IDint64`json:"id"`// Name is the pet's display name.Namestring`json:"name"`// Tags categorise the pet.Tags []string`json:"tags,omitempty"`}
Full example.fixtures/enhancements/named-struct-tags-ref/types.go.
swagger:strfmt
What it does. Marks a named type as a custom string format.
Wherever the type appears as a field, the emitted schema is
{type: string, format: <name>}. Useful for UUID, Email,
URL-style types that have a Go type but should serialise as a
JSON string with a known format.
Where it goes. On a type declaration whose underlying form is a
string-marshalable type (typically implementing encoding.TextMarshaler
or encoding.TextUnmarshaler).
Argument shape. Required IDENT β the format name (uuid, email,
mac, etc.).
Sample.
// MAC is a hardware address rendered as a colon-separated hex string.//
// swagger:strfmt mactypeMACstringfunc (mMAC) MarshalText() ([]byte, error) { return []byte(m), nil }
func (m*MAC) UnmarshalText(b []byte) error { *m = MAC(b); returnnil }
A field typed MAC emits as {type: string, format: mac}. The
underlying MAC type does NOT appear as a top-level model definition
(strfmt-tagged structs are replaced by their format at every
reference).
Legal keywords. None at the type level beyond swagger:strfmt
itself; the format name is the entire surface.
Full example.fixtures/enhancements/text-marshal/types.go.
swagger:enum
What it does. Marks a string-typed (or integer-typed) named type
as an enum and collects the type’s const declarations. The values
are applied inline on each model field that references the type:
the property gets an enum array plus an x-go-enum-desc extension
carrying the per-value godoc descriptions in <value> <doc-text>
shape. The enum type itself is not emitted as a standalone
definition β the values travel with each referencing property.
(Edge case: if swagger:enum names a type for which no matching
const values are found, the enum semantics are dropped and the type
falls through to ordinary type resolution β typically a plain
definition referenced by $ref, with no enum array.)
Where it goes. On a named type declaration. The type’s const
values are discovered via Go’s type-system traversal; they do not
need to live in the same file. The values surface only when a model
reaches the enum type through a field.
Argument shape. IDENT naming the type whose const values to
collect (its own name).
Sample.
// Priority is the urgency level on a task.//
// swagger:enum PrioritytypePrioritystringconst (
// PriorityLow is for tasks that can wait.PriorityLowPriority = "low"// PriorityMedium is the default.PriorityMediumPriority = "medium"// PriorityHigh is for tasks that must run soon.PriorityHighPriority = "high")
// Task references Priority, which is what makes the enum reachable.//
// swagger:modeltypeTaskstruct {
PriorityPriority`json:"priority"`}
Produces (extract) β the values land on Task’s priority property,
not on a Priority definition:
{
"Task": {
"type": "object",
"properties": {
"priority": {
"type": "string",
"enum": ["low", "medium", "high"],
"x-go-enum-desc": "low PriorityLow is for tasks that can wait.\nmedium PriorityMedium is the default.\nhigh PriorityHigh is for tasks that must run soon." }
}
}
}
Legal keywords. Schema-context keywords. The enum: keyword can
ALSO be used inline on the type doc to force a value set; when present,
it overrides the const-derived values and the x-go-enum-desc is
recomputed (or dropped) accordingly.
Full example.fixtures/enhancements/enum-overrides/types.go.
swagger:allOf
What it does. Marks a struct as participating in an allOf
composition. The struct’s fields plus any embedded
swagger:model-tagged base produce an allOf: [$ref base, {inline fields}] schema. The companion convention is to embed the base
type as an anonymous field with this annotation on the embedding’s
doc comment (or on the embedded type itself).
Where it goes. On a struct field that embeds another type, or on
a struct type that has at least one embedded base.
Argument shape. No args.
Sample.
// Animal is the abstract base.//
// swagger:modeltypeAnimalstruct {
Kindstring`json:"kind"`}
// Dog is an Animal with a breed.//
// swagger:modeltypeDogstruct {
// swagger:allOfAnimalBreedstring`json:"breed"`}
Legal keywords. Schema-context keywords on the inline-object
member (the second allOf element).
Full example.fixtures/enhancements/allof-edges/types.go.
swagger:alias
What it does. Marks a Go alias declaration (type T = Other) as
a model that should publish as a $ref to Other’s definition
rather than as a duplicate of Other’s schema.
The scanner also honours RefAliases and TransparentAliases
top-level options, which can globally enable alias-as-ref behaviour
without per-decl annotation. swagger:alias is the per-decl override
for cases where the global mode isn’t appropriate.
Where it goes. On a type alias declaration.
Argument shape. Optional IDENT β the published name. Default:
the alias’s Go name.
Sample.
// Timestamp aliases time.Time. The published model carries// format: date-time via the time.Time β strfmt resolution.//
// swagger:aliastypeTimestamp = time.Time
Without the annotation (and without global RefAliases), the alias
either expands the target’s full schema or is silently ignored
depending on context.
Legal keywords. Schema-context keywords.
Full example.fixtures/enhancements/ref-alias-chain/types.go.
swagger:route
What it does. Declares an HTTP route + operation in one
annotation. The header line carries the method, path, optional tags,
and the operation ID; the comment body carries the operation’s
metadata (consumes / produces / schemes / security / parameters /
responses / extensions).
This is the terser of the two operation-declaration annotations.
Most go-swagger projects use swagger:route for hand-written
operations.
Where it goes. On a function or variable declaration whose doc
comment carries the annotation. The Go entity itself doesn’t have to
be a handler β the annotation publishes a path/operation independent
of the carrier.
<METHOD> β GET, POST, PUT, DELETE, PATCH, HEAD,
OPTIONS. Case insensitive.
<path> β starts with /. Supports path-parameter braces:
/items/{id}.
[tag1 tag2 β¦] β optional whitespace-separated list of tags. At
least two characters each.
<operationID> β the unique operation identifier.
A godoc-style identifier may precede the annotation on the same
comment line:
// ListPets swagger:route GET /pets pets users listPets
That leading identifier is recognised as a godoc convention and is
not part of the annotation surface.
Sample.
// ListPets swagger:route GET /pets pets users listPets//
// List pets filtered by some parameters.//
// Consumes:// - application/json//
// Produces:// - application/json//
// Schemes: http, https//
// Security:// api_key:// oauth: read, write//
// Parameters:// + name: limit// in: query// type: integer// minimum: 1// maximum: 100//
// Responses:// 200: body:[]Pet the pet list// default: response:genericErrorfuncListPets() {}
Legal keywords. All
body keywords legal in route context
(consumes, produces, schemes, security, parameters,
responses, extensions) plus inline deprecated:.
Full example.fixtures/enhancements/routes-full-petstore-shape/handlers.go.
swagger:operation
What it does. Same payload as swagger:route but with a
different body shape: instead of the structured Parameters: /
Responses: keyword surface, swagger:operation’s body is a
single YAML document spelling out the OpenAPI operation object
directly.
Use swagger:operation when you want to author the operation in
YAML (closer to the OpenAPI spec text) or when the operation has
shapes the keyword surface doesn’t cover.
Where it goes. Same as swagger:route β function or variable
doc comment.
Argument shape. Same header shape as swagger:route:
// swagger:operation GET /items/{id} items getItem//
// ---// summary: Get item by ID// parameters:// - name: id// in: path// required: true// type: integer// responses:// '200':// description: the requested item// schema:// $ref: '#/definitions/Item'// default:// $ref: '#/responses/genericError'funcGetItem() {}
The --- delimits the YAML body; everything between the fences is
parsed as an OpenAPI 2.0 operation object.
Legal keywords. None inside the YAML body (it’s structurally
YAML, not the keyword grammar). The header line is the entire
annotation surface.
Full example.fixtures/enhancements/parameters-map-postdecl/api.go.
swagger:parameters
What it does. Declares a Go struct as the parameters set for one
or more operations. Each field of the struct becomes one parameter
on the named operation(s). The field’s doc comment carries the
parameter’s in:, required:, validation, and description.
Where it goes. On a struct declaration.
Argument shape. Required IDENTs β the operation IDs this
parameters set applies to. At least one. The same operation ID may
appear in multiple swagger:parameters annotations to compose a
parameter set from several structs.
Sample.
// ListItemsParams declares pagination + filter parameters for the// listItems operation.//
// swagger:parameters listItemstypeListItemsParamsstruct {
// Offset is the page offset.//
// in: query// minimum: 0// default: 0Offsetint`json:"offset"`// Limit is the page size.//
// in: query// minimum: 1// maximum: 100// default: 20Limitint`json:"limit"`// Tag is the filter tag.//
// in: query// required: falseTagstring`json:"tag,omitempty"`}
Legal keywords on fields.param-context keywords
(in, required, the numeric / length / format validations,
default, example, enum, allowEmptyValue, collectionFormat).
Full example.fixtures/enhancements/simple-schema-violation/api.go.
swagger:response
What it does. Declares a Go struct as a named response object,
emitted into the spec’s top-level responses map. Routes / operations
reference it by name via the response sub-language (Responses:
body in swagger:route, or the YAML $ref form in
swagger:operation).
The struct’s fields contribute the response shape:
A field named Body (or carrying in: body) becomes the response
body schema.
Other fields carrying in: header become response headers.
Where it goes. On a struct declaration.
Argument shape. Optional IDENT β the published response name.
Default: the Go type’s name.
Sample.
// GenericError is the catch-all error response.//
// swagger:response genericErrortypeGenericErrorstruct {
// in: bodyBodystruct {
// Message is the human-readable error message.Messagestring`json:"message"`// Code is the machine-readable error category.Codestring`json:"code,omitempty"` }
// X-Request-ID echoes the request correlation header.//
// in: headerXRequestIDstring`json:"X-Request-ID"`}
Routes can then reference it via response:genericError in their
Responses: body.
Legal keywords on body field. Schema-context keywords.
Legal keywords on header field. Header-context keywords β
numeric / length / format validations, pattern, enum, default,
example, collectionFormat. required: is silently dropped on
headers (the OAS v2 Header object does not carry a required field).
Full example.fixtures/enhancements/routes-full-petstore-shape/handlers.go.
swagger:ignore
What it does. Excludes the surrounding declaration from the
generated spec. The scanner sees the decl and the doc, classifies
it, then drops it.
Where it goes. On a type declaration to exclude the whole type,
or on a struct field doc to exclude that one field.
Argument shape. No args.
Sample (type):
// Internal is not exposed.//
// swagger:ignoretypeInternalstruct {
SecretFieldstring}
Sample (field):
typeUserstruct {
Namestring`json:"name"`// PasswordHash is internal.//
// swagger:ignorePasswordHashstring`json:"-"`}
Interaction: when swagger:ignore appears AFTER another
classifier on the same comment block (e.g., swagger:model first,
then swagger:ignore), the first annotation wins and the ignore is
silently overridden. Place swagger:ignore first if you genuinely
want the decl excluded.
Full example.fixtures/enhancements/top-level-kinds/types.go.
swagger:name
What it does. Overrides the JSON property name that a struct
field or interface method renders as. By default the scanner derives
names from json:"β¦" struct tags (or the Go identifier for fields /
methods with no tag); swagger:name is the per-field override when
the tag-based shape isn’t appropriate β typically on interface
methods, which cannot carry struct tags.
Where it goes. On a struct field doc OR an interface method doc.
Argument shape. Required IDENT β the JSON property name to use.
Sample (interface method):
// UserProfile is the user's profile interface.//
// swagger:modeltypeUserProfileinterface {
// ID is the user identifier.ID() string// FullName is the user's display name.//
// swagger:name fullNameFullName() string}
Without swagger:name, the method FullName() would publish as
property FullName (PascalCase). The annotation renames it to
fullName.
Legal keywords. None β the override name is the entire surface.
Full example.fixtures/enhancements/interface-methods/types.go.
swagger:type
What it does. Overrides the inferred Swagger type for a named
type or struct field. The Go type’s natural inference (struct β
object, named string β string, time.Time β date-time, β¦) is
replaced with the annotation’s argument.
Where it goes. On a type declaration OR a struct field doc.
Argument shape. Required IDENT β the Swagger type name (string,
integer, number, boolean, array, object).
Sample (type-level override):
// ULID is a Crockford-base32 unique identifier rendered as a string.//
// swagger:type stringtypeULID [16]byte
Fields typed ULID emit as {type: string} regardless of the
underlying [16]byte shape.
Sample (field-level override):
typeDocumentstruct {
// Body is an opaque payload published as a string blob.//
// swagger:type stringBodyjson.RawMessage`json:"body"`}
Interaction: when combined with swagger:strfmt on the same
type, both apply β the strfmt format goes onto the published
{type: string, format: β¦}.
Full example.fixtures/enhancements/named-struct-tags-ref/types.go.
swagger:file
What it does. Marks a parameter or response body as a binary file
({type: file}). The scanner emits the file-type marker without
further introspection of the Go type.
Where it goes. On a struct field doc inside a
swagger:parameters (multipart file upload) or swagger:response
(file download) struct.
Argument shape. No args.
Sample.
// UploadParams declares a multipart file upload.//
// swagger:parameters uploadFiletypeUploadParamsstruct {
// File is the uploaded asset.//
// in: formData// swagger:fileFileio.ReadCloser`json:"file"`}
Legal keywords. Standard parameter / response keywords; the file
marker stacks with in: and other parameter shape keywords.
swagger:default
What it does. Marks the surrounding declaration as the spec’s
default value for the corresponding shape. Used in narrow contexts
where the scanner expects an explicit anchor for a default.
This annotation is value-only β there’s no exported entity it
publishes; it’s a classifier hint the scanner consumes during
discovery.
Where it goes. On a value declaration (var, const) or a
struct field.
Argument shape. No args.
Sample.
// DefaultLimit is the default page size used wherever Limit is not// supplied by the caller.//
// swagger:defaultvarDefaultLimit = 20
This annotation has a narrow surface and is not commonly authored
directly. Most spec defaults are carried by the default: keyword on
the relevant field.
Annotation Γ keyword compatibility matrix
A quick orientation for which annotations can carry which keyword
families. See keywords.md for the per-keyword
contracts.
Annotation
Numeric/length validations
Schema decorators
in:
Meta keywords
Parameters: body
Responses: body
YAML body
swagger:meta
β
β
β
β
β
β
β (security defs, extensions)
swagger:model
β (on fields)
β
β
β
β
β
β
swagger:strfmt
β
β
β
β
β
β
β
swagger:enum
β
(enum keyword via const)
β
β
β
β
β
swagger:allOf
β (on member fields)
β
β
β
β
β
β
swagger:alias
β
β
β
β
β
β
β
swagger:route
β
(deprecated only)
β
(schemes/consumes/produces/security)
β
β
(extensions)
swagger:operation
β
β
β
β
β
β
β (full op as YAML)
swagger:parameters
β (on fields)
β (on fields)
β
β
β
β
β
swagger:response
β (on header fields)
β (on body field)
β (body/header)
β
β
β
β
swagger:ignore
β
β
β
β
β
β
β
swagger:name
β
β
β
β
β
β
β
swagger:type
β
β
β
β
β
β
β
swagger:file
β
β
β
β
β
β
β
swagger:default
β
β
β
β
β
β
β
A blank cell means the keyword family is not legal in that context;
attempting to use it emits CodeContextInvalid and the keyword is
dropped.
Keyword reference
This document catalogs the keyword: value forms recognised inside
annotation blocks. The keywords come in two flavours:
Inline keywords β one line, keyword: value shape, with the
value classified by a value shape (number, integer,
boolean, string, β¦).
Body keywords β a header line followed by indented continuation
lines. The body’s interpretation depends on the keyword (a flat
token list for Consumes:, a YAML map for SecurityDefinitions:,
a per-line sub-language for Parameters: / Responses: on
swagger:route).
The reader-friendly orientation is in annotations.md
(which annotation accepts which keywords, with examples); this file
is the per-keyword reference card. Implementers wanting the
formal productions should read grammar.md.
Name β canonical spelling. This is what Property.Keyword.Name
compares equal to. Comparisons are case-insensitive on the
canonical spelling and on every alias.
Aliases β alternate spellings the lexer accepts. They map to
the canonical name at lex time; consumers never see alias values.
Value shape β the lexical category of the value. See
value shapes for what each one means and how it
surfaces to consumers.
Contexts β the family-level scopes where the keyword is legal.
Using a keyword outside its legal contexts emits a
CodeContextInvalid diagnostic and the keyword is dropped from
the affected block.
Value shapes
The grammar’s lexer classifies every value into one of these
shapes. The shape determines which Walker callback fires for the
property and which field of Property.Typed carries the parsed
value.
Shape
Typed payload
Example value forms
number
float64 (with optional </<=/>/>=/= prefix)
5, 1.5, <10, >=0, =42
integer
int64
5, 100
boolean
bool
true, false, 1, 0
string
raw string
^[a-z]+$, date-time, multipart/form-data
comma-list
raw string; split on , by Property.AsList()
http, https, a,b,c
enum-option
typed string (closed-vocab match)
csv, pipes for collectionFormat:
raw-block
accumulated body lines on Property.Body
multi-line YAML, indented token lists
raw-value
the verbatim post-colon text on Property.Value
42, "orange", [1, 2, 3]
When typing fails (e.g. maximum: notanumber) the lexer emits a
CodeInvalidNumber / CodeInvalidInteger / CodeInvalidBoolean
diagnostic and the property reaches the Walker with a zero-value
payload. Consumers gate on Property.IsTyped() to skip
malformed-typed values; the corresponding builder field stays
unwritten.
Annotation contexts
The closed set of contexts a keyword can legally appear in. A keyword
table entry’s Contexts field combines these:
Context
Meaning
param
Parameter doc on a swagger:parameters struct field, or a + name: chunk inside swagger:route Parameters:
header
Header field on a swagger:response struct
schema
Top-level model or struct field on a swagger:model
items
Items-level (array element) validation on either parameter or schema
route
Route-level metadata under swagger:route
operation
Inline operation metadata under swagger:operation
meta
Package-level metadata under swagger:meta
response
Response-level decorations
Summary table
The full keyword surface, in the order the keyword table declares
them. Detailed entries follow this table.
Keyword
Aliases
Shape
Contexts
maximum
max
number
param, header, schema, items
minimum
min
number
param, header, schema, items
multipleOf
multiple of, multiple-of
number
param, header, schema, items
maxLength
max length, max-length, maxLen, max len, max-len, maximum length, maximum-length, maximumLength, maximum len, maximum-len
integer
param, header, schema, items
minLength
min length, min-length, minLen, min len, min-len, minimum length, minimum-length, minimumLength, minimum len, minimum-len
integer
param, header, schema, items
pattern
β
string
param, header, schema, items
maxItems
max items, max-items, max.items, maximum items, maximum-items, maximumItems
integer
param, header, schema, items
minItems
min items, min-items, min.items, minimum items, minimum-items, minimumItems
integer
param, header, schema, items
unique
β
boolean
param, header, schema, items
collectionFormat
collection format, collection-format
enum-option (csv, ssv, tsv, pipes, multi)
param, header, items
default
β
raw-value
param, header, schema, items
example
β
raw-value
param, header, schema, items
enum
β
raw-value
param, header, schema, items
required
β
boolean
param, schema
readOnly
read only, read-only
boolean
schema
discriminator
β
boolean
schema
deprecated
β
boolean
operation, route, schema
in
β
enum-option (query, path, header, body, formData)
param
schemes
β
raw-block (token list)
meta, route, operation
version
β
string
meta
host
β
string
meta
basePath
base path, base-path
string
meta
license
β
string
meta
contact
contact info, contact-info
string
meta
consumes
β
raw-block (token list)
meta, route, operation
produces
β
raw-block (token list)
meta, route, operation
security
β
raw-block (security requirements)
meta, route, operation
securityDefinitions
security definitions, security-definitions
raw-block (YAML map)
meta
responses
β
raw-block (response sub-language)
route, operation
parameters
β
raw-block (parameter chunk sub-language)
route, operation
extensions
β
raw-block (YAML map of x-* entries)
meta, route, operation, schema, param, header
infoExtensions
info extensions, info-extensions
raw-block (YAML map of x-* entries)
meta
tos
terms of service, terms-of-service, termsOfService
raw-block (prose paragraph)
meta
externalDocs
external docs, external-docs
raw-block (YAML map)
meta, route, operation, schema
Numeric validations
Apply to numeric schema types (integer, number). On a typed
schema with a non-numeric type, these keywords emit
CodeShapeMismatch and drop. On a typeless schema (no type:
declared upstream), they apply best-effort.
maximum
Upper bound on a numeric value. Alias: max.
The value may carry a leading comparison operator that becomes the
exclusive/inclusive bound:
maximum: 10 β inclusive (β€ 10).
maximum: <10 β exclusive (< 10).
maximum: <=10 β inclusive (same as no prefix).
maximum: =10 β inclusive.
Maps to schema.maximum and schema.exclusiveMaximum.
// Limit is the cap on items per page.//
// maximum: 100// minimum: 1typeLimitint
β from fixtures/enhancements/... (any numeric-validation fixture).
minimum
Lower bound on a numeric value. Alias: min. Same operator-prefix
shape as maximum. Maps to schema.minimum and
schema.exclusiveMinimum.
multipleOf
Divisibility constraint. The value must be a positive number.
Aliases: multiple of, multiple-of. Maps to schema.multipleOf.
// AllowedStep enforces increments of 5.//
// multipleOf: 5typeAllowedStepint
Length / array validations
maxLength / minLength apply only to string-typed schemas;
maxItems / minItems apply only to array-typed schemas. Using
the wrong pairing emits CodeShapeMismatch and drops the keyword.
maxLength
Maximum string length. Many aliases for ergonomic spelling:
max length, max-length, maxLen, max len, max-len,
maximum length, maximum-length, maximumLength, maximum len,
maximum-len. Maps to schema.maxLength.
minLength
Minimum string length. Same alias set as maxLength with min in
place of max. Maps to schema.minLength.
maxItems
Maximum array length. Aliases: max items, max-items,
max.items, maximum items, maximum-items, maximumItems. Maps
to schema.maxItems.
minItems
Minimum array length. Same alias shape as maxItems with min in
place of max. Maps to schema.minItems.
// Tags is a non-empty, bounded list.//
// minItems: 1// maxItems: 20// unique: truetypeTags []string
Format validations
pattern
A regex constraint on a string value. The pattern is preserved
verbatim on schema.pattern. The grammar runs a best-effort RE2
compile (Go’s regex engine) on the value; if it fails, a
CodeInvalidAnnotation diagnostic surfaces with the compile error.
The value still lands on the schema β downstream tools may use
JSON Schema’s wider regex dialect.
// Slug is a URL-friendly identifier.//
// pattern: ^[a-z0-9-]+$typeSlugstring
unique
Marks an array-typed schema as set-valued (no duplicates). Boolean.
Maps to schema.uniqueItems.
collectionFormat
How an array value is serialised on the wire. Closed-vocab:
csv β comma-separated (default).
ssv β space-separated.
tsv β tab-separated.
pipes β pipe-separated.
multi β repeated ?key=val&key=val2 (query params only).
Aliases: collection format, collection-format. Maps to
parameter.collectionFormat / items.collectionFormat. Schema-level
contexts ignore this keyword (it’s a SimpleSchema concept; schemas
serialise via application/json).
When the source value doesn’t match the closed vocab, the raw value
is preserved verbatim on the parameter (matches the original
behaviour where pipe as a typo for pipes round-trips).
// Tags is the form-data array of label tokens.//
// in: query// type: array// collectionFormat: csv// items.type: stringtypeTagsParam []string
Schema decorators
default
Default value for a schema or simple-schema field. Raw-value shape β
the post-colon text is captured verbatim and coerced against the
resolved schema type at write time (ParseDefault /
CoerceValue).
Multi-line bodies are accepted for complex literals:
// Limits is the throughput envelope.//
// default:// {// "rps": 100,// "burst": 200// }typeLimitsstruct { ... }
Single-line form for primitives:
// Page is the page number.//
// in: query// type: integer// default: 1typePageParamint
example
An example value for the schema, surfaced in tooling. Same raw-value
shape as default. Maps to schema.example (or parameter.example
for SimpleSchema parameters).
enum
A closed set of allowed values. Three accepted surface forms:
Comma list: enum: red, green, blue β split on , and
trimmed.
JSON array: enum: ["red", "green", "blue"] β parsed via
YAML/JSON.
Multi-line list with - markers:
enum:
- red
- green
- blue
Each element is coerced against the resolved schema type. Maps to
schema.enum.
For string-typed enums driven by Go const declarations the
swagger:enum annotation is the more idiomatic surface β it picks
up the constant names AND their godoc descriptions and produces an
x-go-enum-desc extension alongside the enum values. The
enum: keyword is the manual override.
required
Marks a field as required. Boolean.
On a swagger:model struct field: adds the field’s name to the
enclosing schema’s required array.
On a swagger:parameters struct field: sets parameter.required.
On a swagger:response header: not applicable; the keyword is
silently dropped (response headers don’t carry required).
readOnly
Marks a schema property as read-only. Aliases: read only,
read-only. Maps to schema.readOnly.
Schema-only β emitting readOnly: inside a SimpleSchema context
(non-body parameter, response header) emits
CodeUnsupportedInSimpleSchema and drops the keyword.
discriminator
Marks the property as the discriminator for an allOf polymorphic
schema. Boolean. Writes the property’s name onto the enclosing
schema’s discriminator field. Schema-only.
deprecated
Marks the carrying entity as deprecated. Boolean. Legal on operations
(operation.deprecated), routes (operation.deprecated on the
synthesised op), and schemas (some downstream tools render this).
Parameter location
in
Where the parameter value comes from. Closed-vocab:
query β query string parameter.
path β path-parameter substitution.
header β request header.
body β request body (JSON, etc.).
formData β form-data body field (note: form accepted as an
alias inside swagger:route Parameters: chunks; the lexer
normalises to formData at the canonical surface).
A non-matching value emits a context-invalid diagnostic; the
parameter loses its in and may end up incorrectly classified
downstream.
β¦where the trailing token starting with a URL scheme becomes
license.url and the prefix becomes license.name. A bare name
with no URL is accepted too.
contact
Contact declaration. Author writes a Name <email> URL triple, in
any order. The grammar recognises:
Name <email@example.com> β Go’s net/mail.ParseAddress form.
Name <email@example.com> http://example.com β same + trailing
URL.
Just a URL, no name.
Aliases: contact info, contact-info. Maps to info.contact.
Body keywords
Body keywords have a header line ending in : and indented
continuation lines. The body’s structure depends on the keyword.
See sub-languages.md for the full
sub-language specifications; this section covers the keyword
shape.
consumes / produces
Media-type lists. Same flex-list rule as schemes: β comma
inline, multi-line bare, YAML - markers, or any combination.
Maps to consumes / produces on the surrounding scope (spec,
operation).
Security-requirements list. Each line is one requirement of shape
schemeName: scope1, scope2. An empty scope list (schemeName:)
means “no scopes required, but the scheme must be active.”
Security:
api_key:
oauth2: read, write
Maps to security (array of single-key maps).
securityDefinitions
YAML map declaring security schemes. The body is parsed as YAML
directly into the spec.securityDefinitions shape β see
OAS v2 Β§5.2.16.
Per-route / per-operation response declarations. Each line is one
response in the form <code>: <tokens>. See
sub-languages.md Β§responses for the
full per-line grammar.
Responses:
200: body:User the requested user
404: description: not found
default: response:genericError
parameters
Per-route / per-operation parameter declarations. Body is a sequence
of + name: chunks (the + is the chunk-start sigil; - is
accepted as an alias). See
sub-languages.md Β§parameters for
the full per-chunk grammar.
Aliases: info extensions, info-extensions (for infoExtensions).
tos
Terms-of-service prose paragraph. Multi-line body is joined with
\n after dropping whitespace-only lines. Aliases:
terms of service, terms-of-service, termsOfService. Maps to
info.termsOfService. Meta-only.
externalDocs
External documentation pointer as a YAML map with description and
url keys. Aliases: external docs, external-docs. Multi-context
(meta, route, operation, schema).
A handful of keyword interactions are worth flagging:
default + example + enum on the same field: all three
may co-occur. The values are coerced against the resolved schema
type independently. If enum is declared and default is not a
member of it, no diagnostic fires today β downstream JSON Schema
validation catches it.
type + numeric validations + format on a body parameter:
the schema dispatcher’s checkShape gates numeric / length
validations against the resolved type. format is type-blind
(any format string lands).
required on a $ref'd field: writes to the enclosing
schema’s required array (the standard JSON-Schema-draft-4
shape). If the field has sibling overrides, the $ref rewrites
into an allOf compound β see
grammar.md Β§refoverride.
Sub-languages
The annotation body grammar is not a single language β it’s a
top-level keyword grammar that embeds several smaller languages
inside specific body keywords. Each embedded language has its own
shape rules.
This document catalogs the embedded languages and how they fit
together. For the per-keyword surface, see
keywords.md; for the formal grammar that hosts
them, see grammar.md.
Comment lines that don’t match any keyword head OR YAML fence OR
annotation marker are classified as prose β free-form text. The
lexer splits prose into two token kinds:
TITLE β the first paragraph of prose, expected to fit on a
short summary line.
DESC β every prose paragraph after the title (or following a
blank line within the first paragraph).
Three heuristics decide the title-vs-desc boundary, evaluated in
order. The first to fire wins:
Blank-line split. Any blank line inside the prose run ends
the title paragraph and starts the description.
Closing punctuation. If the first prose line ends with
Unicode punctuation (., ?, !, β¦, :, β¦), the title is
just that one line; everything after becomes description.
Markdown ATX heading. If the first prose line matches
markdown’s # Heading shape, the # markers are stripped and
the remaining text becomes the title.
When no heuristic fires, the entire prose run is title (the schema
builder later collapses to a description-only schema when
appropriate).
Package <name> prefix strip
The swagger:meta annotation’s title comes from the package doc
comment, which by Go convention starts with Package <name>. The
spec builder strips that prefix before publishing:
// Package petstore Petstore API.//
// Description of the petstore service.//
// swagger:metapackagepetstore
Produces info.title = "Petstore API." (the Package petstore
prefix stripped) and info.description = "Description of the petstore service."
Only the capital-P Package form is recognised β author prose like
“package this carefully” is not chopped.
Body keywords that publish a flat list of tokens (schemes:,
consumes:, produces:) accept multiple surface forms uniformly.
The unified reader is Property.AsList().
Accepted forms
# Inline, comma-separated
Schemes: http, https
# Multi-line, indented bare lines
Schemes:
http
https
# Multi-line, YAML-style dash markers
Schemes:
- http
- https
# Inline value plus indented continuation
Schemes: http
- https
# All combinations of the above
Consumes: application/json, application/xml
- application/protobuf
All five forms produce the same ["http", "https"] (or
["application/json", "application/xml", "application/protobuf"])
output.
Algorithm
For each input line β Property.Value first (if non-empty), then
each line of Property.Body:
Trim surrounding whitespace.
Drop a leading - YAML marker if present.
Re-trim whitespace.
Comma-split.
Trim each token; drop empties.
Aggregate into a single slice in source order.
What flex-list does NOT touch
Enum values (enum: ...) β their elements may themselves be
complex (JSON arrays, quoted strings with commas). enum: keeps
its raw-value path; the value coercion layer handles array /
comma-list / multi-line shapes per the schema type.
Parameters chunks β the + name: chunk grammar is not a
simple token list; see Β§parameters.
YAML structural bodies β securityDefinitions:,
extensions:, infoExtensions: parse the body as YAML directly;
their structure isn’t a flat list. See
Β§yaml-extensions.
Parameters
The Parameters: body in swagger:route and swagger:operation
carries a sequence of parameter declarations separated by + name:
chunks (the + is the chunk-start sigil; - is accepted as an
alias for forward compatibility with proper YAML).
Chunk shape
Parameters:
+ name: id
in: path
type: integer
description: the item identifier
required: true
+ name: limit
in: query
type: integer
minimum: 1
maximum: 100
default: 20
+ name: body
in: body
type: User
required: true
Per-chunk fields
The fields are classified into head fields (consumed by the
orchestrator to populate the *spec.Parameter shell) and
validation fields (lowered to grammar properties and dispatched
through the standard validation pipeline).
Head fields:
Field
Lands on
Notes
name:
parameter.name
Required. Identifies the parameter.
in:
parameter.in
One of path / query / header / body / formData. form accepted as an alias for formData.
type:
parameter.type (for SimpleSchema) or determines the body $ref
For non-body: one of string / integer / number / boolean / array. For body: a Go ident referring to a swagger:model-declared type, optionally with [] array prefixes ([][]Pet). bool accepted as an alias for boolean.
format:
parameter.format or parameter.schema.format
Free-form string. Applied after validation dispatch so it doesn’t interfere with default/example coercion.
description:
parameter.description
Free-form prose.
required:
parameter.required
Boolean.
allowempty: / allowemptyvalue:
parameter.allowEmptyValue
Boolean.
Validation fields: any other recognised
keyword β min, max, minLength, maxLength,
minItems, maxItems, pattern, unique, collectionFormat,
default, example, enum. These are looked up via
grammar.Lookup (which accepts canonical names + aliases) and
dispatched through the standard handlers seam.
Empty chunks and unknown keys
A bare + (or -) sigil with no follow-up content emits a
CodeInvalidAnnotation diagnostic and is dropped. The legacy
parser silently emitted an empty Parameter{} object β current
behaviour rejects it.
Unknown keys (typos like defualt:) emit
CodeInvalidAnnotation and drop. The legacy parser silently
discarded them.
Body parameters
When in: body, the orchestrator looks up type: as either:
A primitive (string, integer, number, boolean, array,
object) β emits a typed schema with the primitive on
parameter.schema.type.
A Go ident β emits a $ref to #/definitions/<Ident>. With []
prefixes, wraps the ref in nested array schemas.
Validation properties on a body chunk apply to the schema, gated by
the schema’s resolved type via checkShape. A min: 0 on a body
chunk with type: Pet (object) emits CodeShapeMismatch and drops;
a min: 0 with type: integer lands on the schema’s minimum.
Validation on SimpleSchema (non-body) parameters
For in: other than body, validation properties apply directly to
the parameter (not to a sub-schema). Type-gating still applies:
minLength on type: integer emits a diagnostic and drops.
Responses
The Responses: body in swagger:route carries one response
declaration per line. Each line has the shape:
<code>: <token>*
where <code> is default (case-insensitive) or a decimal HTTP
status code, and <token> is either a tag:value form or an
untagged token.
Recognised tags
Tag
Value shape
Lands on
body:
Go ident with optional [] prefixes (body:[]Pet)
A $ref to #/definitions/<name>, array-wrapped per [] count
response:
Go ident referring to a swagger:response-declared type
A $ref to #/responses/<name>
description:
Free-form prose (rest of line)
response.description
Untagged token rules
The first untagged token defaults to a response ref. The
orchestrator resolves it against the operation’s responses map
first, then falls back to definitions β if found in definitions
(not responses), it’s silently promoted to a body ref.
Subsequent untagged tokens accumulate into the description.
Examples
Responses:
200: User the user as returned # untagged β response="User", desc="the user as returned"
200: body:User the user # body ref + description
200: response:userResponse the user # named response ref
201: body:Pet the created pet
404: description: not found
default: response:genericError
default: body:[]ErrorList the error list # array-wrapped body ref
Diagnostics
Unknown tag (200: weird:value) β emits
CodeInvalidAnnotation and drops the line.
Duplicate body/response tags on one line
(200: body:Pet response:errors) β emits
CodeInvalidAnnotation; the line drops.
Space-separated body Foo (instead of body:Foo) β detected
as a likely typo and dropped with diagnostic. The legacy parser
silently treated it as response="body" (a dangling ref to a
non-existent response).
Unresolvable response ref β when a response name appears in
neither responses nor definitions, the line drops with
diagnostic. The legacy parser emitted a dangling $ref.
Empty value lines
A line like 204: with nothing after the colon produces a Response
with the code and an empty description. This is intentional β some
authors want a 204 No Content with no body and no description.
YAML extensions
Several body keywords parse their body as YAML directly:
extensions: and infoExtensions: β a YAML map of x-* entries.
securityDefinitions: β a YAML map matching OAS v2’s
securityDefinitions shape.
externalDocs: β a YAML map with description and url keys.
Extension typing
Extension values are NOT coerced to strings β they preserve their
YAML-typed form: bool, float64, string, []any, or
map[string]any for nested structures.
Keys that don’t start with x- or X- emit a
CodeInvalidAnnotation diagnostic and drop. The build still
succeeds. Authors who relied on the legacy “hard error on non-x-*”
behaviour see a diagnostic + a clean spec missing the typo’d key.
The YAML extension bodies use indentation to delimit. A line
that returns to the indentation level of the keyword head β or
introduces a sibling keyword β terminates the body. The grammar
also recognises --- fence pairs around the body (matching the
swagger:operation YAML shape) and absorbs them silently.
Security requirements
The security: body (in swagger:meta, swagger:route, and
swagger:operation) carries OAuth-style security requirements
where each line is one requirement.
Shape
Each line: schemeName: scope1, scope2, β¦
schemeName matches a scheme declared in
securityDefinitions.
Scope list is comma-separated; trimmed; empties dropped.
An empty scope list (schemeName:) means “this scheme is
required, no scopes.” Common for apiKey and basic.
Each requirement is a single-key map; the array is an OR
relationship (the request satisfies security if it matches ANY
entry).
Contact / License
Inline single-line meta keywords with structured value
parsing.
Contact
The contact: value carries up to three components: name, email,
URL. Recognised forms:
Contact: Name <email@example.com> https://example.com
Contact: Name <email@example.com>
Contact: https://example.com
Contact: <email@example.com>
The grammar splits the value on the first URL prefix it finds
(https://, http://, ftps://, ftp://, wss://, ws://),
then parses the prefix portion as Name <email> via Go’s
net/mail.ParseAddress.
A malformed Name <email> head (e.g., unbalanced angle
brackets) surfaces as an error from Block.Contact(); the
meta builder propagates it as a build failure.
An empty contact line produces an empty Contact value (no
error, no diagnostic β equivalent to omitting the keyword).
Aliases: contact info, contact-info.
License
The license: value is split similarly:
License: Apache 2.0 https://www.apache.org/licenses/LICENSE-2.0
License: MIT
License: https://opensource.org/licenses/Custom
Same URL-prefix detection. Everything before the URL is the
license name; the URL (when present) is the license URL.
Either part may be empty.
License does NOT use mail.ParseAddress β the name is taken as
raw text up to the URL boundary.
Sub-language interactions
Two interaction points worth flagging:
Block-comment continuation lines and the
parameters/responses sub-languages. A
/* swagger:route β¦ */ block with Parameters: inside requires
the chunk-start sigils (+ / - ) to be at the start of the
trimmed line. Block-comment continuation noise (\t, *) is
stripped first; if your editor inserts a * continuation marker,
the lexer handles it transparently.
Flex-list and description: on a parameter chunk.
description: is a head field, not a list β it does NOT comma-split.
Authors who write description: foo, bar get a single description
"foo, bar", not two descriptions. (This was a real ambiguity
in older versions of go-swagger; the current grammar resolves
it cleanly.)
Grammar
The formal grammar of the codescan annotation surface. This document
specifies the language a Go comment must conform to so that the
scanner classifies it, dispatches it to the right builder, and
populates the OpenAPI spec deterministically.
Audience. Implementers β anyone porting, extending, or debugging
the parser. Annotation authors typically need
annotations.md and keywords.md
instead.
The grammar is layered:
Preprocess β comment-marker stripping (see
Β§preprocess).
Lex β terminal token emission, including multi-line body
accumulation (see Β§lexer).
Parse β block construction, family dispatch, keyword
classification (see Β§parser).
Walk β typed dispatch through grammar.Walker callbacks to
the builders (see Β§walker).
The productions below operate on the lexer’s terminal alphabet,
not raw text. Per-terminal lexical detail (how the lexer recognises
a number, a string, an annotation, β¦) is described in Β§lexer; the
EBNF that follows consumes pre-classified terminals.
The grammar is rigorous ISO-14977 EBNF. Required vs. optional
arguments, value typing, and family membership are
grammar-visible β every legality constraint expressible by token
sequencing is expressed that way.
Input is a *ast.CommentGroup from go/parser. Each *ast.Comment
in the group is one source-level comment node (either // β¦ or
/* β¦ */). The preprocessor produces a flat sequence of Line
structs, each one source line with:
Line.Text β content after comment-marker stripping and
leading content-prefix trim.
Line.Raw β content after comment-marker stripping only
(preserves leading whitespace).
Line.Pos β token.Position of the first content byte.
Stripping rules:
For // comments: drop the // marker. Line.Text runs
trimContentPrefix (strips leading \t*/|); Line.Raw keeps
the post-marker spacing.
For /* */ block comments: split body on newlines.
First line: drop the /* marker.
Continuation lines: run stripBlockContinuation (strips
leading whitespace + optional * continuation marker + one
following space), then trimContentPrefix.
Last line: drop the trailing */.
trimContentPrefix strips \t*/ and a single trailing | from
the line head. It does NOT strip - (so YAML list markers and
markdown dash items survive intact).
For synthetic per-line comments produced by upstream tooling
(notably parsers.ParseRoutePathAnnotation), a // prefix is
prepended before stripping so the // branch fires and the leading
whitespace gets shed correctly.
Lexer
The lexer turns a []Line into a []Token ending in TokenEOF.
Pipeline:
Line classifier β emit one preliminary token per line
(annotation / keyword / fence / blank / text).
Body accumulator β fold multi-line bodies (OPAQUE_YAML,
RAW_BLOCK_, RAW_VALUE_) into single body tokens.
Prose classifier β re-type surviving text tokens as
TokenTitle / TokenDesc.
Terminal vocabulary
Annotation name terminals (TokenAnnotation)
Each recognises an annotation name only β positional arguments
are emitted as separate terminals.
Terminal
Annotation
ANN_MODEL
swagger:model
ANN_RESPONSE
swagger:response
ANN_PARAMETERS
swagger:parameters
ANN_ROUTE
swagger:route
ANN_OPERATION
swagger:operation
ANN_META
swagger:meta
ANN_STRFMT
swagger:strfmt
ANN_ALIAS
swagger:alias
ANN_NAME
swagger:name
ANN_ALLOF
swagger:allOf
ANN_ENUM
swagger:enum
ANN_IGNORE
swagger:ignore
ANN_DEFAULT
swagger:default
ANN_TYPE
swagger:type
ANN_FILE
swagger:file
Argument terminals
Terminal
Recognises
IDENT_NAME
Identifier-shaped token. Used for every named arg and reference.
GET / POST / PUT / PATCH / HEAD / DELETE / OPTIONS / TRACE (case-insensitive).
URL_PATH
RFC-3986 URL path token (used as the second positional arg of OperationArgs).
Keyword head terminals (TokenKeyword)
Each recognises the keyword name only. See
keywords.md for the complete keyword surface.
Inline value terminals
The lexer types values per their lexical shape; semantic coercion
against the Go target happens in the analyzer.
Terminal
Recognises
NUMBER_VALUE
Signed decimal literal (integer or fractional).
INT_VALUE
Unsigned decimal integer.
BOOL_VALUE
true / false (case-insensitive).
STRING_VALUE
Verbatim non-LF text.
COMMA_LIST_VALUE
Comma-separated list of strings, trim-stripped.
ENUM_OPTION_VALUE
One of a closed token set declared per keyword (query/path/β¦ for in:, csv/ssv/β¦ for collectionFormat).
When the lexer fails to type a value against its keyword’s expected
shape, the property reaches the analyzer with Property.Typed.Type == ShapeNone and a CodeInvalidNumber / CodeInvalidInteger /
CodeInvalidBoolean diagnostic is emitted.
Multi-line body terminals
Single tokens spanning multiple source lines. The lexer absorbs the
head and the body lines.
A raw-block / raw-value keyword opens a body. The body terminates at
the next sibling structural token in the same family β either
another TokenAnnotation, another body-keyword head whose context
makes it a sibling, or TokenEOF.
Blank lines do NOT terminate the body. They are absorbed as visual
separators inside list-shaped bodies.
For raw-block heads, the inline post-colon value (when non-empty)
is prepended to the body as its first line. This means
Consumes: application/json (inline single value) and
Consumes:\n - application/json (multi-line body) both yield the
same body content; consumers don’t need to special-case the inline
form.
YAML fence handling
A line whose trimmed content is exactly --- opens (or closes) a
YAML fence. While the cursor sits between matching fences:
Annotation and keyword recognition is suspended; every line emits
as tokenRawLine carrying the verbatim source text.
The body accumulator captures the fenced region as a single
OPAQUE_YAML token attached to the surrounding annotation
(typically swagger:operation or a fenced extensions body).
A missing closing fence emits a CodeUnterminatedFence
diagnostic; the OPAQUE_YAML token is marked truncated and the
builder degrades gracefully.
Prose classification
Surviving tokenText tokens (not consumed by a body, not an
annotation or keyword head) re-type as either TokenTitle or
TokenDesc per three heuristics evaluated in order:
Blank-line split β a blank line inside the prose run ends
the title and starts the description.
Closing punctuation β if the first prose line ends with
Unicode punctuation, the title is just that one line.
Markdown ATX heading β if the first prose line matches
markdown’s # Heading shape, the # markers are stripped and
the line becomes the title.
When no heuristic fires, the entire prose run is title.
The parser consumes the lexer’s terminal stream and produces typed
Block values, one per *ast.CommentGroup. A single comment group
may produce MORE than one Block when multiple annotations appear
(each annotation closes the preceding Block and opens a fresh one).
The dispatcher reads the first ANN_* terminal; its identity
selects the family. If no annotation appears, the input is an
UnboundBlock β typically a Go struct field with description-only
documentation.
Block.AnnotationKind() returns the family discriminator.
Block.AnnotationArg() returns the leading IDENT argument (if any)
without requiring the caller to type-assert on the typed Block
kind.
Schema family
Bodies of swagger:model, swagger:parameters, swagger:response,
swagger:name.
swagger:route and swagger:operation are distinct block productions
because their bodies differ structurally β swagger:route accepts
the structured keyword surface; swagger:operation accepts an
OPAQUE_YAML body.
OperationFamilyBlock =RouteBlock | InlineOperationBlock ;
RouteBlock =ANN_ROUTE , OperationArgs , [ Title ]
, [ Description ]
, RouteBody ;
InlineOperationBlock =ANN_OPERATION , OperationArgs , [ Title ]
, [ Description ]
, InlineOperationBody ;
OperationArgs =HTTP_METHOD , URL_PATH , { IDENT_NAME } , IDENT_NAME ;
(* Trailing IDENT_NAME is the OperationID;
the run between URL_PATH and the OpID is
the tag list. *)RouteBody = { CommonOperationBodyItem | BLANK } ;
InlineOperationBody = { CommonOperationBodyItem | OPAQUE_YAML | BLANK } ;
CommonOperationBodyItem =OperationKeyword | OperationDecorator | OperationRawBlock | ExtensionsBlock | ExternalDocsBlock ;
OperationKeyword =KW_SCHEMES , COMMA_LIST_VALUE ;
OperationDecorator =DeprecatedLine ;
OperationRawBlock =RAW_BLOCK_CONSUMES | RAW_BLOCK_PRODUCES | RAW_BLOCK_SECURITY | RAW_BLOCK_RESPONSES | RAW_BLOCK_PARAMETERS ;
The <GoIdent> swagger:route ... godoc-prefix exception (which
allows a leading Go identifier on the route annotation line) is
absorbed by the lexer; the EBNF sees a plain ANN_ROUTE.
Classifiers are stateless markers β they carry no validation body
of their own. The surrounding declaration’s other annotations (or
the absence thereof) determine where the classification lands.
Cross-cutting productions
These appear in multiple families and share a single production.
Vendor extensions (ExtensionsBlock, InfoExtensionsBlock) accept
YAML map bodies; non-x-* keys emit CodeInvalidAnnotation and
drop. The lexer additionally surfaces them via
Block.Extensions() with an Extension.Source discriminator
(KwExtensions vs KwInfoExtensions) so consumers can route to
the correct spec field (spec.extensions vs info.extensions).
Walker
Block.Walk(grammar.Walker{...}) dispatches Properties through
typed callbacks. The Walker maps a Property to a callback by
Keyword.Shape:
Shape
Callback
Payload
ShapeNumber
Number
(p, float64, exclusive bool)
ShapeInt
Integer
(p, int64)
ShapeBool
Bool
(p, bool)
ShapeString
String
(p, string) β value on p.Value
ShapeEnumOption
String
(p, string) β closed-vocab token on p.Typed.String
ShapeRawBlock
Raw
(p) β caller reads p.Body / p.Raw
ShapeRawValue
Raw
(p)
ShapeCommaList
Raw
(p) β caller splits via Property.AsList
ShapeNone (failed typing)
Raw
(p) β diagnostic fired separately
Additional callbacks fire outside the per-Property dispatch:
Title(s string) β once, before any property, if non-empty.
Description(s string) β once, before any property, if non-empty.
Extension(ext grammar.Extension) β once per typed extension.
Diagnostic(d grammar.Diagnostic) β block-level diagnostics fire
before Title; per-property diagnostics fire immediately before
the property’s main callback.
Walker.FilterDepth gates property callbacks by Property.ItemsDepth.
Pass 0 for level-0 properties (default); pass N for items-level
N; pass AllDepths (-1) for every depth.
Keyword applied to a schema type that doesn’t accept it (e.g. minLength on a number)
CodeContextInvalid
Warning
Keyword used outside its legal annotation context
CodeUnsupportedInSimpleSchema
Warning
Full-schema-only keyword used in SimpleSchema (non-body param, header)
CodeInvalidYAMLExtensions
Warning
YAML parse failed inside an extensions body
CodeUnterminatedFence
Warning
YAML fence opened but not closed before EOF
All diagnostics drop the offending property / annotation /
extension and continue the build. The accumulator on
common.Builder collects them in source order; the consumer’s
OnDiagnostic callback (if wired) fires inline.
What this grammar does not describe
The grammar’s job ends at producing typed Property and Block
values. The analyzer (builders / spec orchestrator) owns:
Type coercion β default: 1.5 against an integer schema is
a lexical success and an analyzer rejection. validations.CoerceValue
and validations.ParseDefault apply the schema-type-aware
coercion at write time.
Cross-reference resolution β $ref targets, alias-chain
resolution, post-decl discovery. The grammar emits the names; the
analyzer resolves them.
Schema-shape gating β validations.IsLegalForType decides
whether minLength applies to the resolved schema type. The
grammar always emits the property; the handler dispatch decides
whether to write it.
Ordering & merging across multiple comment groups β when
several swagger:parameters Foo Bar Baz declarations contribute
to the same operation, the spec builder merges them.
The grammar is also deliberately single-pass β it never
revisits a *ast.CommentGroup after Parse(cg) returns. The
common.Builder blockCache memoises results across the analyzer’s
recursive type descent (see
common README Β§blockcache).