diff --git a/docs/doc-site/tutorials/model-definitions.md b/docs/doc-site/tutorials/model-definitions.md
index 90fbd9a6..97c63484 100644
--- a/docs/doc-site/tutorials/model-definitions.md
+++ b/docs/doc-site/tutorials/model-definitions.md
@@ -24,6 +24,19 @@ the JSON-Schema `type` / `format`. Well-known standard-library types resolve
automatically — a `time.Time` field, for instance, is published as
`{type: string, format: date-time}`.
+UUIDs are recognised two different ways, and both publish
+`{type: string, format: uuid}`:
+
+- **by type identity** — the standard-library `uuid.UUID` introduced in Go 1.27,
+ matched on its import path and name, so nothing else can be mistaken for it;
+- **by type name** — any *other* type named `UUID` (case-insensitively) that
+ marshals as text, which covers `github.com/google/uuid`, `gofrs/uuid`,
+ `strfmt.UUID` and the like.
+
+The name-based rule is a heuristic and stays available whichever Go version you
+build with. Either way an explicit [`swagger:strfmt`](#swaggerstrfmt) on the type
+wins, so you can always overrule the recognition.
+
{{< example go="concepts/models/models.go" goregion="model"
json="concepts/models/testdata/model.json" jsonlabel="#/definitions/Pet" >}}
diff --git a/fixtures/goparsing/go127/uuid/doc.go b/fixtures/goparsing/go127/uuid/doc.go
new file mode 100644
index 00000000..13430ccc
--- /dev/null
+++ b/fixtures/goparsing/go127/uuid/doc.go
@@ -0,0 +1,10 @@
+// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
+// SPDX-License-Identifier: Apache-2.0
+
+// Package uuidmodels witnesses the identity-based recognizer for the go1.27 stdlib uuid package.
+//
+// This file is deliberately UNTAGGED while model.go carries `//go:build go1.27`: under an older
+// toolchain `import "uuid"` does not compile, and a package left with no buildable files at all
+// fails `go build ./...` with "build constraints exclude all Go files". This doc file keeps the
+// package loadable while contributing nothing to the emitted spec.
+package uuidmodels
diff --git a/fixtures/goparsing/go127/uuid/model.go b/fixtures/goparsing/go127/uuid/model.go
new file mode 100644
index 00000000..063b55f9
--- /dev/null
+++ b/fixtures/goparsing/go127/uuid/model.go
@@ -0,0 +1,67 @@
+// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
+// SPDX-License-Identifier: Apache-2.0
+
+//go:build go1.27
+
+package uuidmodels
+
+import "uuid"
+
+// Order carries the stdlib uuid.UUID in every position the schema builder can reach it from.
+//
+// swagger:model Order
+type Order struct {
+ // ID is a plain field.
+ ID uuid.UUID `json:"id"`
+
+ // PtrID is a pointer, peeled before recognition.
+ PtrID *uuid.UUID `json:"ptrId"`
+
+ // IDs exercises the items chain.
+ IDs []uuid.UUID `json:"ids"`
+
+ // ByName exercises a map value; the key is a plain string.
+ ByName map[string]uuid.UUID `json:"byName"`
+
+ // Keyed exercises uuid.UUID as a map KEY: it marshals to a JSON object because the key is a
+ // TextMarshaler, and the key type itself is not rendered in Swagger 2.0.
+ Keyed map[uuid.UUID]string `json:"keyed"`
+
+ // Named refs the named type declared below.
+ Named OrderID `json:"named"`
+
+ // Overridden proves the explicit classifier still beats the recognizer.
+ //
+ // swagger:strfmt date
+ Overridden uuid.UUID `json:"overridden"`
+}
+
+// OrderID is a named type whose underlying type is the stdlib uuid.UUID.
+//
+// swagger:model OrderID
+type OrderID uuid.UUID
+
+// AliasID is an ALIAS of the stdlib uuid.UUID: it dissolves at use sites.
+type AliasID = uuid.UUID
+
+// Tagged uses the alias.
+//
+// swagger:model Tagged
+type Tagged struct {
+ // Alias is declared through the alias above.
+ Alias AliasID `json:"alias"`
+}
+
+// Embedder embeds the stdlib uuid.UUID.
+//
+// Witness for the embedded arm, which the fuzzy name heuristic never reached (it is caller-gated to
+// buildFromTextMarshal). Note the promoted MarshalText makes encoding/json render the WHOLE struct
+// as a bare string, dropping Name from the wire — see quirks-open.md Q33.
+//
+// swagger:model Embedder
+type Embedder struct {
+ uuid.UUID
+
+ // Name is NOT on the wire, whatever the emitted schema says.
+ Name string `json:"name"`
+}
diff --git a/fixtures/integration/golden/go127_uuid_spec.json b/fixtures/integration/golden/go127_uuid_spec.json
new file mode 100644
index 00000000..77e4d222
--- /dev/null
+++ b/fixtures/integration/golden/go127_uuid_spec.json
@@ -0,0 +1,92 @@
+{
+ "swagger": "2.0",
+ "paths": {},
+ "definitions": {
+ "Embedder": {
+ "description": "Witness for the embedded arm, which the fuzzy name heuristic never reached (it is caller-gated to\nbuildFromTextMarshal). Note the promoted MarshalText makes encoding/json render the WHOLE struct\nas a bare string, dropping Name from the wire — see quirks-open.md Q33.",
+ "type": "object",
+ "title": "Embedder embeds the stdlib uuid.UUID.",
+ "properties": {
+ "name": {
+ "description": "Name is NOT on the wire, whatever the emitted schema says.",
+ "type": "string",
+ "x-go-name": "Name"
+ }
+ },
+ "x-go-package": "github.com/go-openapi/codescan/fixtures/goparsing/go127/uuid"
+ },
+ "Order": {
+ "type": "object",
+ "title": "Order carries the stdlib uuid.UUID in every position the schema builder can reach it from.",
+ "properties": {
+ "byName": {
+ "description": "ByName exercises a map value; the key is a plain string.",
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "format": "uuid"
+ },
+ "x-go-name": "ByName"
+ },
+ "id": {
+ "description": "ID is a plain field.",
+ "type": "string",
+ "format": "uuid",
+ "x-go-name": "ID"
+ },
+ "ids": {
+ "description": "IDs exercises the items chain.",
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "uuid"
+ },
+ "x-go-name": "IDs"
+ },
+ "keyed": {
+ "description": "Keyed exercises uuid.UUID as a map KEY: it marshals to a JSON object because the key is a\nTextMarshaler, and the key type itself is not rendered in Swagger 2.0.",
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ },
+ "x-go-name": "Keyed"
+ },
+ "named": {
+ "$ref": "#/definitions/OrderID"
+ },
+ "overridden": {
+ "description": "Overridden proves the explicit classifier still beats the recognizer.",
+ "type": "string",
+ "format": "date",
+ "x-go-name": "Overridden"
+ },
+ "ptrId": {
+ "description": "PtrID is a pointer, peeled before recognition.",
+ "type": "string",
+ "format": "uuid",
+ "x-go-name": "PtrID"
+ }
+ },
+ "x-go-package": "github.com/go-openapi/codescan/fixtures/goparsing/go127/uuid"
+ },
+ "OrderID": {
+ "type": "string",
+ "format": "uuid",
+ "title": "OrderID is a named type whose underlying type is the stdlib uuid.UUID.",
+ "x-go-package": "github.com/go-openapi/codescan/fixtures/goparsing/go127/uuid"
+ },
+ "Tagged": {
+ "type": "object",
+ "title": "Tagged uses the alias.",
+ "properties": {
+ "alias": {
+ "description": "Alias is declared through the alias above.",
+ "type": "string",
+ "format": "uuid",
+ "x-go-name": "Alias"
+ }
+ },
+ "x-go-package": "github.com/go-openapi/codescan/fixtures/goparsing/go127/uuid"
+ }
+ }
+}
\ No newline at end of file
diff --git a/internal/builders/resolvers/assertions.go b/internal/builders/resolvers/assertions.go
index 0df6e57b..4ba5de01 100644
--- a/internal/builders/resolvers/assertions.go
+++ b/internal/builders/resolvers/assertions.go
@@ -151,6 +151,19 @@ func IsStdTime(o *types.TypeName) bool {
return o.Pkg() != nil && o.Pkg().Name() == "time" && o.Name() == "Time"
}
+// IsStdUUID reports whether o is the go1.27 stdlib [uuid.UUID].
+//
+// Identity-based, so it never misfires on the many third-party types also named UUID
+// (github.com/google/uuid, gofrs, strfmt, …) — those keep going through the fuzzy
+// name heuristic in the schema builder.
+//
+// Deliberately NOT behind a go1.27 build tag: codescan compares types harvested from
+// *scanned* code, never imports uuid itself, and ships as a binary that may be built by
+// an older toolchain than the module it scans.
+func IsStdUUID(o *types.TypeName) bool {
+ return o.Pkg() != nil && o.Pkg().Path() == "uuid" && o.Name() == "UUID"
+}
+
func IsStdError(o *types.TypeName) bool {
return o.Pkg() == nil && o.Name() == "error"
}
diff --git a/internal/builders/resolvers/assertions_test.go b/internal/builders/resolvers/assertions_test.go
new file mode 100644
index 00000000..bb1ef435
--- /dev/null
+++ b/internal/builders/resolvers/assertions_test.go
@@ -0,0 +1,60 @@
+// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
+// SPDX-License-Identifier: Apache-2.0
+
+package resolvers
+
+import (
+ "go/token"
+ "go/types"
+ "testing"
+
+ "github.com/go-openapi/testify/v2/assert"
+)
+
+// TestIsStdUUID covers the go1.27 stdlib uuid.UUID predicate.
+//
+// Deliberately synthesizes the *types.TypeName instead of scanning a fixture, so the predicate stays
+// covered on every supported toolchain — including the ones where `import "uuid"` does not compile.
+// The end-to-end wiring is witnessed by the go1.27-tagged integration test instead.
+func TestIsStdUUID(t *testing.T) {
+ // stdlib packages have a bare import path; uuid.UUID is [16]byte.
+ stdUUID := func() *types.TypeName {
+ pkg := types.NewPackage("uuid", "uuid")
+ return types.NewTypeName(token.NoPos, pkg, "UUID", types.NewArray(types.Typ[types.Byte], 16))
+ }
+
+ named := func(path, name, typeName string) *types.TypeName {
+ pkg := types.NewPackage(path, name)
+ return types.NewTypeName(token.NoPos, pkg, typeName, types.NewArray(types.Typ[types.Byte], 16))
+ }
+
+ t.Run("the go1.27 stdlib uuid.UUID is recognized", func(t *testing.T) {
+ assert.TrueT(t, IsStdUUID(stdUUID()))
+ })
+
+ t.Run("third-party UUID types are NOT recognized by identity", func(t *testing.T) {
+ // These stay on the fuzzy name heuristic in the schema builder; identity must not claim them.
+ assert.FalseT(t, IsStdUUID(named("github.com/google/uuid", "uuid", "UUID")))
+ assert.FalseT(t, IsStdUUID(named("github.com/gofrs/uuid/v5", "uuid", "UUID")))
+ assert.FalseT(t, IsStdUUID(named("github.com/go-openapi/strfmt", "strfmt", "UUID")))
+ })
+
+ t.Run("a user package merely named uuid is NOT recognized", func(t *testing.T) {
+ // Package *name* is uuid, but the import path carries the module prefix — only the stdlib
+ // gets a bare path. This is why the predicate tests Path(), not Name().
+ assert.FalseT(t, IsStdUUID(named("example.com/mymod/internal/uuid", "uuid", "UUID")))
+ })
+
+ t.Run("another type in the stdlib uuid package is NOT recognized", func(t *testing.T) {
+ assert.FalseT(t, IsStdUUID(named("uuid", "uuid", "Version")))
+ })
+
+ t.Run("the match is case-sensitive", func(t *testing.T) {
+ assert.FalseT(t, IsStdUUID(named("uuid", "uuid", "Uuid")))
+ })
+
+ t.Run("a builtin (no package) does not panic", func(t *testing.T) {
+ builtin := types.NewTypeName(token.NoPos, nil, "error", nil)
+ assert.FalseT(t, IsStdUUID(builtin))
+ })
+}
diff --git a/internal/builders/schema/README.md b/internal/builders/schema/README.md
index 8e75bc33..6917317c 100644
--- a/internal/builders/schema/README.md
+++ b/internal/builders/schema/README.md
@@ -12,7 +12,7 @@ trade-offs, and known quirks live here.
- [§build-entry](#build-entry) — `Build` modes and dispatch entry points
- [§dispatch-table](#dispatch-table) — `buildNamedType`'s underlying-shape table
- [§dissolve-named](#dissolve-named) — when a named type is unwrapped instead of `$ref`'d
-- [§special-types](#special-types) — `applyStdlibSpecials`, `applySpecialType`, UUID heuristic
+- [§special-types](#special-types) — `applyStdlibSpecials`, `applySpecialType`, the two UUID recognizers
- [§textmarshal-order](#textmarshal-order) — `buildFromTextMarshal` precedence
- [§aliases](#aliases) — `TransparentAliases` vs `RefAliases` vs default-expand
- [§discovery](#discovery) — `Models` / `ExtraModels` / `discovered`, dedup layers
@@ -137,7 +137,7 @@ Fixture: `fixtures/enhancements/generic-instantiation/`.
---
-## §special-types — `applyStdlibSpecials`, `applySpecialType`, UUID heuristic
+## §special-types — `applyStdlibSpecials`, `applySpecialType`, the two UUID recognizers
Three layers, all in `special_types.go`:
@@ -147,15 +147,46 @@ Three layers, all in `special_types.go`:
`recognizeUUID` which is **fuzzy** (case-insensitive name match).
- **`applyStdlibSpecials(obj, target)`** — the canonical safe set
- `{recognizeAny, recognizeTime, recognizeError, recognizeRawMessage}`.
- All four are identity-based and cannot misfire on user types,
- so this helper is **called uniformly at every site** that handles
- a `*types.TypeName`.
-
-- **`recognizeUUID`** — opt-in via `applySpecialType`'s variadic.
- Currently used **only by `buildFromTextMarshal`** because the
- upstream `IsTextMarshaler` gate guarantees the type renders as
- text, making the fuzzy name match safe.
+ `{recognizeAny, recognizeTime, recognizeError, recognizeRawMessage,
+ recognizeStdUUID}`. All five are identity-based and cannot misfire
+ on user types, so this helper is **called uniformly at every site**
+ that handles a `*types.TypeName`.
+
+The two UUID recognizers are a **certain/guessed pair**, and the
+distinction is the whole reason both exist:
+
+- **`recognizeStdUUID`** — identity match on the go1.27 stdlib
+ `uuid.UUID` (`resolvers.IsStdUUID`: import path `uuid`, name `UUID`).
+ Certain, therefore in the safe set. It is **not** behind a
+ `//go:build go1.27` tag, on purpose: codescan compares types
+ harvested from *scanned* code and never imports `uuid` itself, so
+ tagging it would mean a codescan built by an older toolchain fails
+ to recognize a go1.27 user type — the inverted matrix. Build tags
+ live on the fixture and the integration test, which genuinely cannot
+ compile before go1.27; the predicate keeps toolchain-independent
+ coverage via `TestIsStdUUID`.
+
+- **`recognizeUUID`** — fuzzy, case-insensitive name match, the
+ fallback for `github.com/google/uuid`, `gofrs/uuid`, `strfmt.UUID`
+ and every other third-party type named UUID. Opt-in via
+ `applySpecialType`'s variadic and used **only by
+ `buildFromTextMarshal`**, because the upstream `IsTextMarshaler`
+ gate guarantees the type renders as text, making the name match
+ safe. `buildFromTextMarshal` orders identity **before** fuzzy so
+ the certain match wins.
+
+Neither beats an explicit `swagger:strfmt` / `swagger:type` — the
+classifier runs first, see [§textmarshal-order](#textmarshal-order).
+
+**Not reached by either: a struct embed.** `uuid.UUID` has an array
+underlying type, so `buildNamedEmbedded` falls to its `default` arm
+(only the *interface* arm consults `applyStdlibSpecials`) and skips
+the embed with a `CodeUnsupportedGoType` warning. That asymmetry is
+long-standing and uuid-agnostic — any `strfmt.UUID` / `time.Time`
+embed behaves the same, and Go itself renders such a struct as a bare
+string via the promoted `MarshalText`. Tracked as Q33; the identity
+recognizer deliberately does not change it. Witnessed by
+`TestStdlibUUID`'s embed sub-test.
The seven call sites of `applyStdlibSpecials` (`buildFromDecl`,
`buildDeclAlias` RHS, `buildAlias`, `buildNamedType`,
@@ -177,7 +208,7 @@ The function is entered from `buildFromType`'s shortcut
2. route aliases through `buildAlias` (honour `TransparentAliases` / `RefAliases`)
3. type-assert to `*types.Named` (fallback: `{string, ""}`)
4. **classifier (`swagger:strfmt`) — explicit user intent wins**
-5. **stdlib trio via `applySpecialType(recognizeError, recognizeTime, recognizeRawMessage, recognizeUUID)`**
+5. **stdlib recognizers via `applySpecialType(recognizeError, recognizeTime, recognizeRawMessage, recognizeStdUUID, recognizeUUID)`** — identity before fuzzy
6. `PkgForType`-miss bail (gates only the generic fallback below)
7. **generic fallback** — `{string, ""}` + `x-go-type: pkg.Name`
diff --git a/internal/builders/schema/special_types.go b/internal/builders/schema/special_types.go
index 11d5b0aa..719acd61 100644
--- a/internal/builders/schema/special_types.go
+++ b/internal/builders/schema/special_types.go
@@ -39,8 +39,9 @@ func (s *Builder) buildFromTextMarshal(tpe types.Type, target ifaces.SwaggerTypa
if s.classifierTextMarshal(typeNamed, target) {
return nil
}
- // Implicit recognizers in priority order.
- if applySpecialType(tio, target, s.skipExtensions, recognizeError, recognizeTime, recognizeRawMessage, recognizeUUID) {
+ // Implicit recognizers in priority order: certain (identity) before guessed (fuzzy name).
+ if applySpecialType(tio, target, s.skipExtensions,
+ recognizeError, recognizeTime, recognizeRawMessage, recognizeStdUUID, recognizeUUID) {
return nil
}
// Generic fallback: x-go-type carries pkg.Name, so PkgForType-miss must bail (can't produce the
@@ -61,6 +62,11 @@ const (
recognizeAny
recognizeError
recognizeRawMessage
+ // recognizeStdUUID is an identity match on the go1.27 stdlib uuid.UUID.
+ //
+ // Safe everywhere, so it lives in the canonical set applied by [applyStdlibSpecials].
+ // See [§special-types](./README.md#special-types).
+ recognizeStdUUID
// recognizeUUID is a fuzzy name-only match (case-insensitive "uuid").
//
// Caller-gated — opt in only where the type is guaranteed to render as text.
@@ -69,7 +75,7 @@ const (
)
// applyStdlibSpecials runs the canonical safe set of identity-based recognizers (any / time.Time /
-// error / json.RawMessage).
+// error / json.RawMessage / go1.27 uuid.UUID).
//
// Safe at every call site that handles a *types.TypeName.
//
@@ -78,7 +84,7 @@ const (
// See [§special-types](./README.md#special-types).
func applyStdlibSpecials(obj *types.TypeName, target ifaces.SwaggerTypable, skipExt bool) bool {
return applySpecialType(obj, target, skipExt,
- recognizeAny, recognizeTime, recognizeError, recognizeRawMessage)
+ recognizeAny, recognizeTime, recognizeError, recognizeRawMessage, recognizeStdUUID)
}
// applySpecialType iterates wanted recognizers in order and applies the first match to target,
@@ -123,6 +129,12 @@ func applySpecialType(obj *types.TypeName, target ifaces.SwaggerTypable, skipExt
return true
}
+ case recognizeStdUUID: // identity — go1.27 stdlib uuid.UUID.
+ if resolvers.IsStdUUID(obj) {
+ target.Typed("string", "uuid")
+ return true
+ }
+
case recognizeUUID: // fuzzy — see [§special-types](./README.md#special-types).
if obj != nil && strings.ToLower(obj.Name()) == "uuid" {
target.Typed("string", "uuid")
diff --git a/internal/integration/schema_uuid_go127_test.go b/internal/integration/schema_uuid_go127_test.go
new file mode 100644
index 00000000..6173a51d
--- /dev/null
+++ b/internal/integration/schema_uuid_go127_test.go
@@ -0,0 +1,134 @@
+// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
+// SPDX-License-Identifier: Apache-2.0
+
+//go:build go1.27
+
+package integration_test
+
+import (
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "github.com/go-openapi/codescan"
+ "github.com/go-openapi/codescan/internal/scantest"
+ "github.com/go-openapi/testify/v2/assert"
+ "github.com/go-openapi/testify/v2/require"
+
+ oaispec "github.com/go-openapi/spec"
+)
+
+// TestStdlibUUID witnesses end-to-end recognition of the go1.27 stdlib uuid.UUID.
+//
+// Tagged go1.27 because the fixture cannot compile on an older toolchain ("package uuid is not in
+// std"). The recognizer itself is untagged production code — codescan compares types harvested from
+// scanned sources and never imports uuid — so the predicate is covered on every supported toolchain
+// by TestIsStdUUID in internal/builders/resolvers.
+func TestStdlibUUID(t *testing.T) {
+ fixturesPath := filepath.Join(scantest.FixturesDir(), "goparsing", "go127", "uuid")
+ var (
+ sp *oaispec.Swagger
+ diagnostics []codescan.Diagnostic
+ )
+
+ t.Run("end-to-end source scan should succeed", func(t *testing.T) {
+ var err error
+ sp, err = codescan.Run(&codescan.Options{
+ WorkDir: fixturesPath,
+ ScanModels: true,
+ OnDiagnostic: func(d codescan.Diagnostic) {
+ diagnostics = append(diagnostics, d)
+ },
+ })
+ require.NoError(t, err)
+ })
+
+ require.NotNil(t, sp)
+
+ order, ok := sp.Definitions["Order"]
+ require.TrueT(t, ok)
+ props := order.Properties
+ require.NotEmpty(t, props)
+
+ assertUUID := func(t *testing.T, s oaispec.Schema) {
+ t.Helper()
+ assert.TrueT(t, s.Type.Contains("string"))
+ assert.EqualT(t, "uuid", s.Format)
+ }
+
+ t.Run("a plain uuid.UUID field renders as a formatted string", func(t *testing.T) {
+ assertUUID(t, props["id"])
+ })
+
+ t.Run("a pointer to uuid.UUID is peeled before recognition", func(t *testing.T) {
+ assertUUID(t, props["ptrId"])
+ })
+
+ t.Run("a slice of uuid.UUID carries the format on its items", func(t *testing.T) {
+ ids := props["ids"]
+ require.TrueT(t, ids.Type.Contains("array"))
+ require.NotNil(t, ids.Items)
+ require.NotNil(t, ids.Items.Schema)
+ assertUUID(t, *ids.Items.Schema)
+ })
+
+ t.Run("a map value of uuid.UUID carries the format on additionalProperties", func(t *testing.T) {
+ byName := props["byName"]
+ require.TrueT(t, byName.Type.Contains("object"))
+ require.NotNil(t, byName.AdditionalProperties)
+ require.NotNil(t, byName.AdditionalProperties.Schema)
+ assertUUID(t, *byName.AdditionalProperties.Schema)
+ })
+
+ t.Run("uuid.UUID as a map KEY still yields an object", func(t *testing.T) {
+ // The key type is not rendered in Swagger 2.0; what matters is that the map is recognized as
+ // an object at all, which requires the key to marshal as a JSON string.
+ keyed := props["keyed"]
+ assert.TrueT(t, keyed.Type.Contains("object"))
+ })
+
+ t.Run("an explicit swagger:strfmt still beats the recognizer", func(t *testing.T) {
+ overridden := props["overridden"]
+ assert.TrueT(t, overridden.Type.Contains("string"))
+ assert.EqualT(t, "date", overridden.Format)
+ })
+
+ t.Run("a named type over uuid.UUID renders as a formatted string", func(t *testing.T) {
+ orderID, ok := sp.Definitions["OrderID"]
+ require.TrueT(t, ok)
+ assertUUID(t, orderID)
+ })
+
+ t.Run("an alias of uuid.UUID dissolves to a formatted string at the use site", func(t *testing.T) {
+ tagged, ok := sp.Definitions["Tagged"]
+ require.TrueT(t, ok)
+ assertUUID(t, tagged.Properties["alias"])
+ })
+
+ t.Run("an EMBEDDED uuid.UUID is dropped, with a warning — quirks-open.md Q33", func(t *testing.T) {
+ // Current behaviour, not desired behaviour. uuid.UUID has an array underlying type, so
+ // buildNamedEmbedded's switch falls to its default arm (only the *interface* arm consults
+ // applyStdlibSpecials) and skips the embed. Go itself renders the whole struct as a bare
+ // string via the promoted MarshalText, so "name" is not on the wire either.
+ //
+ // This is a pre-existing, uuid-agnostic quirk shared with every strfmt.UUID / time.Time
+ // embed; the identity recognizer deliberately does not change it. When Q33 is decided, this
+ // sub-test and the golden move together.
+ embedder, ok := sp.Definitions["Embedder"]
+ require.TrueT(t, ok)
+ assert.TrueT(t, embedder.Type.Contains("object"))
+ assert.Len(t, embedder.Properties, 1)
+ assert.Contains(t, embedder.Properties, "name")
+
+ var warned bool
+ for _, d := range diagnostics {
+ if d.Severity == codescan.SeverityWarning && strings.Contains(d.Message, "buildNamedEmbedded") {
+ warned = true
+ break
+ }
+ }
+ assert.TrueT(t, warned, "the dropped embed must not be silent")
+ })
+
+ scantest.CompareOrDumpJSON(t, sp, "go127_uuid_spec.json")
+}