@@ -38,6 +38,9 @@ import (
3838const (
3939 // defPrefix is the prefix used to link to definitions in the OpenAPI schema.
4040 defPrefix = "#/definitions/"
41+
42+ // arrayType is the JSON schema type for arrays.
43+ arrayType = "array"
4144)
4245
4346// byteType is the types.Type for byte (see the types documention
@@ -203,7 +206,7 @@ func applyMarkers(ctx *schemaContext, markerSet markers.MarkerValues, props *api
203206 }
204207
205208 for _ , schemaMarker := range itemsMarkers {
206- if props .Type != "array" || props .Items == nil || props .Items .Schema == nil {
209+ if props .Type != arrayType || props .Items == nil || props .Items .Schema == nil {
207210 err := fmt .Errorf ("must apply %s to an array value, found %s" , schemaMarker .Name , props .Type )
208211 ctx .pkg .AddError (loader .ErrFromNode (err , node ))
209212 } else {
@@ -314,26 +317,30 @@ func localNamedToSchema(ctx *schemaContext, ident *ast.Ident) *apiextensionsv1.J
314317 ctx .requestSchema (pkgPath , typeNameInfo .Name ())
315318 link := TypeRefLink (pkgPath , typeNameInfo .Name ())
316319
317- // In cases where we have a named type, apply the type and format from the named schema
318- // to allow markers that need this information to apply correctly.
319- var typ , fmt string
320- if namedInfo , isNamed := typeInfo .(* types.Named ); isNamed {
320+ props := & apiextensionsv1.JSONSchemaProps {
321+ Ref : & link ,
322+ }
323+
324+ // Set the type for slice and map aliases so field-level markers like +listType
325+ // and +mapType can work. Structs are skipped — they use a $ref only.
326+ // Go 1.23+ (gotypesalias=1) represents `type Foo = []T` as *types.Alias, so
327+ // unalias first to reach the real underlying type.
328+ switch types .Unalias (typeInfo .(types.Type )).Underlying ().(type ) {
329+ case * types.Slice :
330+ props .Type = arrayType
331+ case * types.Map :
332+ //nolint:goconst
333+ props .Type = "object"
334+ case * types.Basic :
321335 // We don't want/need to do this for structs, maps, or arrays.
322336 // These are already handled in infoToSchema if they have custom marshalling.
323- if _ , isBasic := namedInfo .Underlying ().(* types.Basic ); isBasic {
324- namedTypeInfo := ctx .schemaRequester .LookupType (ctx .pkg , namedInfo .Obj ().Name ())
325-
326- namedSchema := infoToSchema (ctx .ForInfo (namedTypeInfo ))
327- typ = namedSchema .Type
328- fmt = namedSchema .Format
329- }
337+ namedTypeInfo := ctx .schemaRequester .LookupType (ctx .pkg , typeNameInfo .Name ())
338+ namedSchema := infoToSchema (ctx .ForInfo (namedTypeInfo ))
339+ props .Type = namedSchema .Type
340+ props .Format = namedSchema .Format
330341 }
331342
332- return & apiextensionsv1.JSONSchemaProps {
333- Type : typ ,
334- Format : fmt ,
335- Ref : & link ,
336- }
343+ return props
337344 default :
338345 ctx .pkg .AddError (loader .ErrFromNode (fmt .Errorf ("unsupported type %T for identifier %s" , typeInfo , ident .Name ), ident ))
339346 return & apiextensionsv1.JSONSchemaProps {}
@@ -356,10 +363,19 @@ func namedToSchema(ctx *schemaContext, named *ast.SelectorExpr) *apiextensionsv1
356363 nonVendorPath := loader .NonVendorPath (typeNameInfo .Pkg ().Path ())
357364 ctx .requestSchema (nonVendorPath , typeNameInfo .Name ())
358365 link := TypeRefLink (nonVendorPath , typeNameInfo .Name ())
359- return & apiextensionsv1.JSONSchemaProps {
366+ // Set the type for slice and map aliases so field-level markers like +listType
367+ // and +mapType can work. Structs are skipped — they use a $ref only.
368+ props := & apiextensionsv1.JSONSchemaProps {
360369 Ref : & link ,
361370 }
371+ switch typeInfoRaw .Underlying ().(type ) {
372+ case * types.Slice :
373+ props .Type = arrayType
374+ case * types.Map :
375+ props .Type = "object"
376+ }
362377 // NB(directxman12): we special-case things like resource.Quantity during the "collapse" phase.
378+ return props
363379}
364380
365381// arrayToSchema creates a schema for the items of the given array, dealing appropriately
@@ -378,7 +394,7 @@ func arrayToSchema(ctx *schemaContext, array *ast.ArrayType) *apiextensionsv1.JS
378394 items := typeToSchema (ctx .ForInfo (& markers.TypeInfo {}), array .Elt )
379395
380396 return & apiextensionsv1.JSONSchemaProps {
381- Type : "array" ,
397+ Type : arrayType ,
382398 Items : & apiextensionsv1.JSONSchemaPropsOrArray {Schema : items },
383399 }
384400}
@@ -436,7 +452,6 @@ func mapToSchema(ctx *schemaContext, mapType *ast.MapType) *apiextensionsv1.JSON
436452 return & apiextensionsv1.JSONSchemaProps {}
437453 }
438454
439- //nolint:goconst
440455 return & apiextensionsv1.JSONSchemaProps {
441456 Type : "object" ,
442457 AdditionalProperties : & apiextensionsv1.JSONSchemaPropsOrBool {
0 commit comments