From ea1679488ff70f989521972b7f550babc35f6107 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 1 Aug 2026 08:54:15 +0000 Subject: [PATCH] Update module github.com/go-swagger/go-swagger to v0.36.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- test/tools/go.mod | 36 +- test/tools/go.sum | 78 +-- .../go-openapi/codescan/.codecov.yml | 7 +- .../go-openapi/codescan/.gitattributes | 1 + .../github.com/go-openapi/codescan/.gitignore | 3 + .../go-openapi/codescan/.golangci.yml | 20 + .../github.com/go-openapi/codescan/README.md | 22 +- .../github.com/go-openapi/codescan/go.work | 20 + .../internal/builders/schema/README.md | 336 ++++------- .../internal/builders/schema/allof.go | 20 +- .../internal/builders/schema/fields.go | 5 + .../codescan/internal/builders/schema/omit.go | 307 ++++++++++ .../internal/builders/schema/schema.go | 7 + .../internal/builders/schema/struct.go | 6 + .../internal/builders/schema/type_override.go | 2 +- .../builders/schema/walker_classifiers.go | 13 +- .../codescan/internal/builders/spec/prune.go | 10 + .../codescan/internal/builders/spec/spec.go | 21 + .../internal/builders/spec/subtypes.go | 293 +++++++++ .../internal/builders/validations/README.md | 2 +- .../internal/parsers/grammar/annotations.go | 40 +- .../internal/parsers/grammar/diagnostic.go | 37 +- .../internal/parsers/grammar/disambiguate.go | 2 +- .../internal/parsers/grammar/lexer.go | 4 + .../internal/parsers/grammar/parser.go | 2 +- .../internal/parsers/routebody/README.md | 9 +- .../codescan/internal/scanner/README.md | 97 ++- .../codescan/internal/scanner/index.go | 2 +- .../go-openapi/inflect/CONTRIBUTORS.md | 4 +- .../github.com/go-openapi/inflect/README.md | 18 +- .../github.com/go-openapi/runtime/form.go | 13 +- .../go-openapi/runtime/multipart_stream.go | 556 ++++++++++++++++++ .../github.com/go-openapi/swag/conv/format.go | 26 +- .../go-swagger/generator/operation.go | 74 +++ .../go-swagger/generator/structs.go | 3 + .../templates/server/configureapi.gotmpl | 5 +- .../templates/server/parameter.gotmpl | 40 +- .../go-swagger/go-swagger/generator/types.go | 23 +- ...{isatty_tcgets.go => isatty_tiocgwinsz.go} | 6 +- .../vendor/github.com/oklog/ulid/v2/ulid.go | 11 +- .../vendor/go.yaml.in/yaml/v3/parserc.go | 178 +++--- test/tools/vendor/go.yaml.in/yaml/v3/yamlh.go | 44 +- test/tools/vendor/modules.txt | 36 +- 43 files changed, 1958 insertions(+), 481 deletions(-) create mode 100644 test/tools/vendor/github.com/go-openapi/codescan/go.work create mode 100644 test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/omit.go create mode 100644 test/tools/vendor/github.com/go-openapi/codescan/internal/builders/spec/subtypes.go create mode 100644 test/tools/vendor/github.com/go-openapi/runtime/multipart_stream.go rename test/tools/vendor/github.com/mattn/go-isatty/{isatty_tcgets.go => isatty_tiocgwinsz.go} (62%) diff --git a/test/tools/go.mod b/test/tools/go.mod index 59ea137cd8..7d9b4ad43a 100644 --- a/test/tools/go.mod +++ b/test/tools/go.mod @@ -8,7 +8,7 @@ go 1.25.0 require ( github.com/cpuguy83/go-md2man/v2 v2.0.7 - github.com/go-swagger/go-swagger v0.35.3 + github.com/go-swagger/go-swagger v0.36.0 ) require ( @@ -21,26 +21,26 @@ require ( github.com/felixge/httpsnoop v1.1.0 // indirect github.com/fsnotify/fsnotify v1.10.1 // indirect github.com/go-openapi/analysis v0.25.5 // indirect - github.com/go-openapi/codescan v0.35.1 // indirect + github.com/go-openapi/codescan v0.36.0 // indirect github.com/go-openapi/errors v0.22.8 // indirect - github.com/go-openapi/inflect v0.21.6 // indirect + github.com/go-openapi/inflect v1.0.0 // indirect github.com/go-openapi/jsonpointer v1.0.0 // indirect github.com/go-openapi/jsonreference v1.0.0 // indirect github.com/go-openapi/loads v0.25.0 // indirect - github.com/go-openapi/runtime v0.32.6 // indirect - github.com/go-openapi/runtime/server-middleware v0.32.6 // indirect + github.com/go-openapi/runtime v0.33.0 // indirect + github.com/go-openapi/runtime/server-middleware v0.33.0 // indirect github.com/go-openapi/spec v0.22.9 // indirect github.com/go-openapi/strfmt v0.27.0 // indirect - github.com/go-openapi/swag/conv v0.27.3 // indirect - github.com/go-openapi/swag/fileutils v0.27.3 // indirect - github.com/go-openapi/swag/jsonutils v0.27.3 // indirect - github.com/go-openapi/swag/loading v0.27.3 // indirect - github.com/go-openapi/swag/mangling v0.27.3 // indirect - github.com/go-openapi/swag/netutils v0.27.3 // indirect - github.com/go-openapi/swag/pools v0.27.3 // indirect - github.com/go-openapi/swag/stringutils v0.27.3 // indirect - github.com/go-openapi/swag/typeutils v0.27.3 // indirect - github.com/go-openapi/swag/yamlutils v0.27.3 // indirect + github.com/go-openapi/swag/conv v0.28.0 // indirect + github.com/go-openapi/swag/fileutils v0.28.0 // indirect + github.com/go-openapi/swag/jsonutils v0.28.0 // indirect + github.com/go-openapi/swag/loading v0.28.0 // indirect + github.com/go-openapi/swag/mangling v0.28.0 // indirect + github.com/go-openapi/swag/netutils v0.28.0 // indirect + github.com/go-openapi/swag/pools v0.28.0 // indirect + github.com/go-openapi/swag/stringutils v0.28.0 // indirect + github.com/go-openapi/swag/typeutils v0.28.0 // indirect + github.com/go-openapi/swag/yamlutils v0.28.0 // indirect github.com/go-openapi/validate v0.26.1 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/google/uuid v1.6.0 // indirect @@ -50,10 +50,10 @@ require ( github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/mattn/go-colorable v0.1.15 // indirect - github.com/mattn/go-isatty v0.0.23 // indirect + github.com/mattn/go-isatty v0.0.24 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/oklog/ulid/v2 v2.1.1 // indirect + github.com/oklog/ulid/v2 v2.1.2 // indirect github.com/pelletier/go-toml/v2 v2.4.3 // indirect github.com/rogpeppe/go-internal v1.15.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect @@ -65,7 +65,7 @@ require ( github.com/spf13/viper v1.21.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/toqueteos/webbrowser v1.2.1 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/crypto v0.54.0 // indirect golang.org/x/mod v0.38.0 // indirect golang.org/x/net v0.57.0 // indirect diff --git a/test/tools/go.sum b/test/tools/go.sum index 5b37250bc6..75e52fc7d9 100644 --- a/test/tools/go.sum +++ b/test/tools/go.sum @@ -23,57 +23,57 @@ github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx5 github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= github.com/go-openapi/analysis v0.25.5 h1:xPYEvTb90o1y0epuiOPAoG4QqahjP3cdp5xNlHeKJRI= github.com/go-openapi/analysis v0.25.5/go.mod h1:d3UGtQC5uq5Kqqqis2VH09Km/v3vwsWrYkbp4gdm+Rc= -github.com/go-openapi/codescan v0.35.1 h1:ajmiS6ha1cA6S/V4uSF1KLEEi5/DqhM7Bef1polLl14= -github.com/go-openapi/codescan v0.35.1/go.mod h1:2mzXphT7B2mM6IsMcP8pi01t2apd3LlIOuemfz4KLqI= +github.com/go-openapi/codescan v0.36.0 h1:sp2RVgGp5IUlYRU3kwlQK6cUiQ9a1GNbgKbaAjdeSoI= +github.com/go-openapi/codescan v0.36.0/go.mod h1:n0z3IJDm3ysnon+bLfBbHoy0WrRzlBE0LfeuAxGEnuM= github.com/go-openapi/errors v0.22.8 h1:oP7sW7TWc3wFFjrzzj0nI83H2qMBkNjNfSd+XRejk/I= github.com/go-openapi/errors v0.22.8/go.mod h1:BuUoHcYrU6E7V9gfj1I5wLQqgtIHnup/alXZ8KdgQ0w= -github.com/go-openapi/inflect v0.21.6 h1:0Se5BlyDT4hnV9JQQKA63W9TIUU6SC4hPJiY3qJOEzQ= -github.com/go-openapi/inflect v0.21.6/go.mod h1:ksYcnLD7j24H79hdqOMmWaLXjFXd0LTkRoBA0UazLW8= +github.com/go-openapi/inflect v1.0.0 h1:IzG7K5YBu97odaCXhjODGGt25WaNtEYLJc9NfUcW4AI= +github.com/go-openapi/inflect v1.0.0/go.mod h1:ksYcnLD7j24H79hdqOMmWaLXjFXd0LTkRoBA0UazLW8= github.com/go-openapi/jsonpointer v1.0.0 h1:kR9tHqY0CtZaOPVFm622dPVNhrvYpwr4uCxgL3h1H8s= github.com/go-openapi/jsonpointer v1.0.0/go.mod h1:Z3rw7dWu1p9IgitXCFamSlA5lmDiklEB6vkaxcNZW5Y= github.com/go-openapi/jsonreference v1.0.0 h1:jlmTr6torcd1YgDQvSfNmRtKzYDO4FGBkrAdlAVWnpY= github.com/go-openapi/jsonreference v1.0.0/go.mod h1:jtwdyGbJk0Xhe5Y+rwtglQP6Sb1WZST4rT32LWB+sv0= github.com/go-openapi/loads v0.25.0 h1:74Bc2snfaVlsHzwdQj/3gsA9XJz3daXTJVs+4ZaK7jI= github.com/go-openapi/loads v0.25.0/go.mod h1:JFBw4SIB9+PTIFHDfcXuSSy5h6aWzjtUCrPYyx3qWU8= -github.com/go-openapi/runtime v0.32.6 h1:hrcTTF8P7ZZr2Majzq11I65QtL/s85o7Q+zJf+AvFN4= -github.com/go-openapi/runtime v0.32.6/go.mod h1:+rsupH3+TFKqmFysqkmgBOTxpVJV8eV+j9myvvea2Xw= -github.com/go-openapi/runtime/server-middleware v0.32.6 h1:IGTYzybyFrUeSqQEwwO1y/9KnOk4QsabFNtAQtIHxDE= -github.com/go-openapi/runtime/server-middleware v0.32.6/go.mod h1:OQHTBqMGquJShXhPYQ62yAqDMtC1rYpsEwldNWjYKhA= +github.com/go-openapi/runtime v0.33.0 h1:Dd3Oj2ig+WH8ckK95l0Wn2V8a4bH/UqWPRZVT0vc8yU= +github.com/go-openapi/runtime v0.33.0/go.mod h1:+rsupH3+TFKqmFysqkmgBOTxpVJV8eV+j9myvvea2Xw= +github.com/go-openapi/runtime/server-middleware v0.33.0 h1:ZFUNyaa2eUs9DhLd6MTe/QRsuxeYn4Lq0C8iAoY13XE= +github.com/go-openapi/runtime/server-middleware v0.33.0/go.mod h1:OQHTBqMGquJShXhPYQ62yAqDMtC1rYpsEwldNWjYKhA= github.com/go-openapi/spec v0.22.9 h1:/vKIFDcGKp0ktZWGbym/tJEWbk6/XOEmAVU0kqKMH+w= github.com/go-openapi/spec v0.22.9/go.mod h1:b/mNUYIOQOyIiUzUzXEE8xzyZqf93KvM9hQGP91yfl0= github.com/go-openapi/strfmt v0.27.0 h1:kbcTeaD9TXuXD0hhMXzuYa1sdTo6+dWGvwjW93E80IM= github.com/go-openapi/strfmt v0.27.0/go.mod h1:s/qhDqfY72irigXUGJmtgid2Rm+3tnz3k8hZaRmvWYc= -github.com/go-openapi/swag v0.27.3 h1:i6oVKkGZeFgETHMiBHGtj9gIQ1aLtWDdJnT/SRZeets= -github.com/go-openapi/swag/conv v0.27.3 h1:iqJFmGEjmX3AY0lSszABFqRVqOSt99XS0LzNIMJYuhU= -github.com/go-openapi/swag/conv v0.27.3/go.mod h1:nPRmN6jgNme99hpf+nM0auDZGALWIqlwhisKPK/bQhQ= -github.com/go-openapi/swag/fileutils v0.27.3 h1:3UVoZ2RLaIs1lt+2jcKzL8RM3Yk0rmsDE9FLA/HGxFE= -github.com/go-openapi/swag/fileutils v0.27.3/go.mod h1:VvJFZLTZS0AI854gEQz5tk7dBESdLjiNUMSZ/th2ry8= -github.com/go-openapi/swag/jsonutils v0.27.3 h1:1DEz+O82frtSMBcos/7XIn1GnpNTbsD4Bru4Dc/uhRc= -github.com/go-openapi/swag/jsonutils v0.27.3/go.mod h1:qiDCoQvzkMxrV3G8FLEdIU5L+EFYc0zcDOHWT3Yofvo= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.27.3 h1:h/eT9kmGCDdFLJF29lOhzLtF0FmP1AX2MhLJWVebsb8= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.27.3/go.mod h1:mofwUWx70wvskwESqRJ//k/9kURmCgyJl5m5Ppoh5kY= -github.com/go-openapi/swag/loading v0.27.3 h1:L9nQkEgzU7QgFQL+pLEMfGUKxeM4pWwGwbET9Z3weW0= -github.com/go-openapi/swag/loading v0.27.3/go.mod h1:rJ0NeaKsF4CVPnMGjPQl7JlSHzvD0bc2DKXLss1hiuE= -github.com/go-openapi/swag/mangling v0.27.3 h1:gRzzD1PAUoLTtGMgI3KpBmCSOlTuLTFWnviLxLcTnyg= -github.com/go-openapi/swag/mangling v0.27.3/go.mod h1:jtBE2+V+3pILxOR7Vgce+Cwp6A2PgZbvVqfNntbVs0w= -github.com/go-openapi/swag/netutils v0.27.3 h1:IoBvfCoprsE6E87kAIm9basnISqDDqB79mJ8MN+f5PU= -github.com/go-openapi/swag/netutils v0.27.3/go.mod h1:J+WYyFMLtvtCGqa6jLv+YNUmIKI3ZRQRrvfNDMoQoEQ= -github.com/go-openapi/swag/pools v0.27.3 h1:gXjImP3F6/56wRRcFgEPld084Y6u2gs21ikPBt8NKBk= -github.com/go-openapi/swag/pools v0.27.3/go.mod h1:kVQefhSK5RWuRe7BXsL8htgBPAMpN7HDGpGEknqugeE= -github.com/go-openapi/swag/stringutils v0.27.3 h1:Ru28hnbAvN5wycALQYy8IobHvASq+FUFMlp1QzLM0JI= -github.com/go-openapi/swag/stringutils v0.27.3/go.mod h1:lzRN95CxXmA03XcDWHLOb6nOMcxCqR5rGY0lOgsfRoM= -github.com/go-openapi/swag/typeutils v0.27.3 h1:l6SSrx5eR5/WVwrGNzN6bQ9WqL04mrxNBl9YgQ3rcJ4= -github.com/go-openapi/swag/typeutils v0.27.3/go.mod h1:Srm0xFNRZ1Y+vCxJclo5qzx8aj+1pAKda/YfFPrG0dQ= -github.com/go-openapi/swag/yamlutils v0.27.3 h1:cRFCAoYtslYn9L9T0xWryHy1t7c1MACC+DMj3CLvwvs= -github.com/go-openapi/swag/yamlutils v0.27.3/go.mod h1:6JYBGj8sw/NawMllyZY+cTA8Mzk2etS3ZBASdcyPsiU= +github.com/go-openapi/swag v0.28.0 h1:xkgbOSKj6DZziNpyqRRAOt3GJGtgjgsd2RoyT30VWuw= +github.com/go-openapi/swag/conv v0.28.0 h1:GtqqbyFe7vR5Y7ehxG9W6/OvrSFdf1OLeTGp40TqxH8= +github.com/go-openapi/swag/conv v0.28.0/go.mod h1:mbUE+mzctnhxi864m0Q07SpN8OowD9JhxmxuYvZZD/k= +github.com/go-openapi/swag/fileutils v0.28.0 h1:Z04XWQD7R8Eq+7GnOrjovBxPPmZzsS4gt2H2GPGIViU= +github.com/go-openapi/swag/fileutils v0.28.0/go.mod h1:VvJFZLTZS0AI854gEQz5tk7dBESdLjiNUMSZ/th2ry8= +github.com/go-openapi/swag/jsonutils v0.28.0 h1:YIch6FwO7RXzeAnbO8Tu7dWBZeUEH+4nA0HXltVTnv4= +github.com/go-openapi/swag/jsonutils v0.28.0/go.mod h1:CYM3WlTUcagR2ZoHdz54di/cbBqt82tuxuXgAjxw+mg= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.28.0 h1:qV+VVUAx5Oro8WjVWpZeql7YReTKhT4smR4zhcOQZr0= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.28.0/go.mod h1:mofwUWx70wvskwESqRJ//k/9kURmCgyJl5m5Ppoh5kY= +github.com/go-openapi/swag/loading v0.28.0 h1:td8QZdZC9MIYGGSnSPKShKiK22I2tU5UQvuUhIBPRLU= +github.com/go-openapi/swag/loading v0.28.0/go.mod h1:rXB0QiQX5mMveXEA7ouM4KiiM9jVJe4K6BVbwhD1M4k= +github.com/go-openapi/swag/mangling v0.28.0 h1:pH8eyeNO9SLYsTMWJrurnNfKmDa28XrlA+HePVD53VM= +github.com/go-openapi/swag/mangling v0.28.0/go.mod h1:jtBE2+V+3pILxOR7Vgce+Cwp6A2PgZbvVqfNntbVs0w= +github.com/go-openapi/swag/netutils v0.28.0 h1:YXN6TALEi2pzts8/8GNm6T61HTAZsieukGZidap989k= +github.com/go-openapi/swag/netutils v0.28.0/go.mod h1:J+WYyFMLtvtCGqa6jLv+YNUmIKI3ZRQRrvfNDMoQoEQ= +github.com/go-openapi/swag/pools v0.28.0 h1:HPMZWSAfce3rdVTFcjFiCIBtDg9h4x2QlRrHipwhxeU= +github.com/go-openapi/swag/pools v0.28.0/go.mod h1:kVQefhSK5RWuRe7BXsL8htgBPAMpN7HDGpGEknqugeE= +github.com/go-openapi/swag/stringutils v0.28.0 h1:ixsc9iYgDPubHL/8nSkbnryEHpD2VRlBMLKpQyPXcDU= +github.com/go-openapi/swag/stringutils v0.28.0/go.mod h1:lzRN95CxXmA03XcDWHLOb6nOMcxCqR5rGY0lOgsfRoM= +github.com/go-openapi/swag/typeutils v0.28.0 h1:nRBKSBXjDgf01VDPB3fWeD9nQuhCOVeIYAkUx2tbkyY= +github.com/go-openapi/swag/typeutils v0.28.0/go.mod h1:Srm0xFNRZ1Y+vCxJclo5qzx8aj+1pAKda/YfFPrG0dQ= +github.com/go-openapi/swag/yamlutils v0.28.0 h1:TV3JXH6DS46KUroDtMLAYHGkdWf5VDq3wVWFirmzROY= +github.com/go-openapi/swag/yamlutils v0.28.0/go.mod h1:x0q/yndZHEgk9Rx3DyDqzFUmHy55KTvIZldvF2dTJXs= github.com/go-openapi/testify/enable/yaml/v2 v2.6.0 h1:gGHwAJ0R/5jU8BEGDbfRNR3hL68dAVi84WuOApp29B0= github.com/go-openapi/testify/enable/yaml/v2 v2.6.0/go.mod h1:tY+St1SGq4NFl0QIqdTY4aEdbChAHxhyB77XQi9iJCo= github.com/go-openapi/testify/v2 v2.6.0 h1:5PKH2HE7YJ/LuRPQGvSxBRlFXNQhSetBLlGAgUEu3ug= github.com/go-openapi/testify/v2 v2.6.0/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= github.com/go-openapi/validate v0.26.1 h1:pZSbvtRO8G2R2FpWTYRn3w8LrsNwbtaVhP2dWiBa0Us= github.com/go-openapi/validate v0.26.1/go.mod h1:B8UMgXiQiwwQWIbmuROlwJZDPGlikPuh7iHV1vPX9Oo= -github.com/go-swagger/go-swagger v0.35.3 h1:TjesBnknEKhd5y1KOngCAYuySA+SN42UmSy4kYtTLNU= -github.com/go-swagger/go-swagger v0.35.3/go.mod h1:qvb30RtfTWVMAQKI0f4dNyHsulfVapjDfobkGP6ZbWc= +github.com/go-swagger/go-swagger v0.36.0 h1:YDdoOZ6QNzRWZwoRNLT9hOKN8xdvlSzjXM9JMRE9Gvc= +github.com/go-swagger/go-swagger v0.36.0/go.mod h1:zGi9Fhr7htWfJwNtjhEE93A1ITPDoeCulv4ASB46Gg8= github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= @@ -92,14 +92,14 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY= github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.23 h1:cYwCQTQf3HB6xUC+BtyCLZNr7IzbOmoZbmssVNzSyiQ= -github.com/mattn/go-isatty v0.0.23/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A= +github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI= +github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s= -github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= +github.com/oklog/ulid/v2 v2.1.2 h1:IEclFb9JNvzYA6MW2SCxbLzcHTVsfqm3PrqGQJH5zec= +github.com/oklog/ulid/v2 v2.1.2/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY= github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= @@ -129,8 +129,8 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/toqueteos/webbrowser v1.2.1 h1:O7IsnnU7XQyJ1nHMRfAktUUJOAZD3aQyUVnxzhWphCg= github.com/toqueteos/webbrowser v1.2.1/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk= diff --git a/test/tools/vendor/github.com/go-openapi/codescan/.codecov.yml b/test/tools/vendor/github.com/go-openapi/codescan/.codecov.yml index c8edfe2a07..a5ba8e96d8 100644 --- a/test/tools/vendor/github.com/go-openapi/codescan/.codecov.yml +++ b/test/tools/vendor/github.com/go-openapi/codescan/.codecov.yml @@ -1,8 +1,9 @@ +codecov: + notify: + after_n_builds: 2 + coverage: status: patch: default: target: 80% -ignore: - - internal/scantest - - fixtures diff --git a/test/tools/vendor/github.com/go-openapi/codescan/.gitattributes b/test/tools/vendor/github.com/go-openapi/codescan/.gitattributes index edb53f7e7e..a68cd0d017 100644 --- a/test/tools/vendor/github.com/go-openapi/codescan/.gitattributes +++ b/test/tools/vendor/github.com/go-openapi/codescan/.gitattributes @@ -2,5 +2,6 @@ # so byte-level comparison doesn't trip on Windows checkouts that # default to core.autocrlf=true. *.json text eol=lf +*.txt text eol=lf internal/parsers/grammar/grammar_test/testdata/golden/* text eol=lf fixtures/integration/golden/* text eol=lf diff --git a/test/tools/vendor/github.com/go-openapi/codescan/.gitignore b/test/tools/vendor/github.com/go-openapi/codescan/.gitignore index 24693b020b..4528f28701 100644 --- a/test/tools/vendor/github.com/go-openapi/codescan/.gitignore +++ b/test/tools/vendor/github.com/go-openapi/codescan/.gitignore @@ -20,6 +20,9 @@ profile.cov # Dependency directories (remove the comment below to include it) # vendor/ +# Go workspace: commit go.work, ignore the generated checksum file +go.work.sum + # env file .env diff --git a/test/tools/vendor/github.com/go-openapi/codescan/.golangci.yml b/test/tools/vendor/github.com/go-openapi/codescan/.golangci.yml index 1515b84ff7..de017b93ef 100644 --- a/test/tools/vendor/github.com/go-openapi/codescan/.golangci.yml +++ b/test/tools/vendor/github.com/go-openapi/codescan/.golangci.yml @@ -7,6 +7,7 @@ linters: - gomoddirectives # mono-repo, multi-modules (docs/examples): local replace directives are needed for proper releasing - goconst # disabled, perhaps temporarily as this linter has become way too pick and noisy - godox + - gomoddirectives - gomodguard - gomodguard_v2 - exhaustruct @@ -54,6 +55,25 @@ linters: - third_party$ - builtin$ - examples$ + rules: + # cmd/genspec-tui is a bubbletea front-end. These four disagree with the + # shape of TUI code rather than with the code itself, and each fires often + # enough (102 between them) that //nolint would be the scattering this + # repo's linting rule tells us to avoid. Scoped by path so the library + # keeps all four. + - path: cmd/genspec-tui/ + linters: + # Layout arithmetic, where the number IS the explanation: w-2 is the + # two border columns, h-3 is border plus title row. + - mnd + # Key, mouse and message switches read open-ended INPUT, not a closed + # domain. Falling through to the code after the switch is the + # behaviour, so a required `default:` would be an empty statement + # asserting nothing. The scanner's go/types switches still get this. + - exhaustive + # clamp/clampInt keep a general (v, lo, hi) signature though every + # call site happens to pass lo=0. + - unparam formatters: enable: - gofmt diff --git a/test/tools/vendor/github.com/go-openapi/codescan/README.md b/test/tools/vendor/github.com/go-openapi/codescan/README.md index 4ff7b76620..6485bf54ef 100644 --- a/test/tools/vendor/github.com/go-openapi/codescan/README.md +++ b/test/tools/vendor/github.com/go-openapi/codescan/README.md @@ -18,7 +18,9 @@ Supports Go modules (since go1.11). ## Announcements -* **2025-04-19** : large package layout reshuffle +* **2026-07-31** : landed a new spec generation TUI tool + +* **2026-04-19** : large package layout reshuffle * the entire project is being refactored to restore a reasonable level of maintenability * the only exposed API is Run() and Options. @@ -32,7 +34,7 @@ API is stable. go get github.com/go-openapi/codescan ``` -## Basic usage +## Basic usage as a library ```go import ( @@ -44,6 +46,22 @@ swaggerSpec, err := codescan.Run(&codescan.Options{ }) ``` +## Work with the TUI + +This project comes with a terminal UI to quickly render a Swagger spec from source +and navigate your code annotations. It shows diagnostics and you may test the impact +of the various available options. + +```cmd +go install github.com/go-openapi/codescan/cmd/genspec-tui@latest +``` + +```cmd +genspec-tui -workdir [my source location] +``` + +![tui_screenshot](docs/genspec-tui.png) + ## Change log See diff --git a/test/tools/vendor/github.com/go-openapi/codescan/go.work b/test/tools/vendor/github.com/go-openapi/codescan/go.work new file mode 100644 index 0000000000..976a6a2d7e --- /dev/null +++ b/test/tools/vendor/github.com/go-openapi/codescan/go.work @@ -0,0 +1,20 @@ +go 1.25.0 + +// Workspace for the codescan monorepo: the main library module (.) and the +// genspec-tui front-end module (./cmd/genspec-tui), kept in separate go.mod +// files so the TUI's bubbletea dependency tree never pollutes the lean library. +// +// The workspace is what lets CI test every module in one pass (`go test work +// ./...`, via the shared go-test-monorepo workflow) — the TUI has no CI of its +// own. `go install .../cmd/genspec-tui@latest` ignores this file, so the TUI +// module's own go.mod carries the real `require` on the library. +// +// Keep the `go` directive above in step with the modules (all 1.25.0): raising +// it past them imposes a toolchain floor the code does not need, and would take +// the oldstable CI job out. +use ( + . + ./cmd/genspec-tui + ./docs/examples + ./fixtures +) diff --git a/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/README.md b/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/README.md index b3d0f8888b..63b1fe02fc 100644 --- a/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/README.md +++ b/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/README.md @@ -20,6 +20,7 @@ trade-offs, and known quirks live here. - [§allof](#allof) — `buildAllOf`, `buildNamedAllOf`, `scanEmbeddedFields` - [§embedded](#embedded) — embed routing, struct/interface specials asymmetry - [§embed-depth](#embed-depth) — ambiguous-embed diagnostic mechanism +- [§omit](#omit) — `swagger:omit` — the author's pre-filter on promoted fields - [§method-mangler](#method-mangler) — interface-method JSON-name derivation - [§user-overrides](#user-overrides) — explicit user-driven type/format overrides at decl-site and field-site - [§traceability](#traceability) — `x-go-name` / `x-go-package` / `x-go-type` origin extensions and `EmitXGoType` @@ -587,6 +588,90 @@ contribute an `{}` entry to the outer schema. --- +## §omit — `swagger:omit`, the author's pre-filter on promoted fields + +Promoting an embed can produce a schema the author never meant, and codescan must not guess which +one they meant. The two cases that motivated the annotation (go-swagger#1992 plus the override +defects found while auditing `DefaultAllOfForEmbeds`): + +| shape | Go marshals | inlined | composed (`allOf`) | +|---|---|---|---| +| outer `ID` re-declared to add `readOnly` | one `ID` | one, decorated | `ID` in BOTH members | +| outer `ID string` over `ID int64` | one, string | string | integer AND string — unsatisfiable | +| a shared type embedded in a request body | every field | every field | every field | + +Inlining **resolves** an override (it applies Go's depth rule and emits the winner); `allOf` +**accumulates** — members conjoin, and conjunction can only narrow, never replace. So an override +that replaces is not expressible as composition, and no amount of tidying the members fixes it. +`swagger:omit` is the escape hatch: the author states which promoted fields do not belong, and the +scanner keeps mirroring the code otherwise. + +### It is a pre-filter, not a post-hoc delete + +`swagger:omit` means *"do not promote this field when walking the embed"*. One rule then covers both +renderings — the field is simply never written: + +- inlined: an outer re-declaration wins as it already did (so omitting + its promoted twin is correctly a no-op there); +- composed: the base member is built without the field, which removes + the duplicate and the unsatisfiable pair above. + +No mode-awareness, and no Go-name↔JSON-name reconciliation, because filtering happens *before* names +are computed — which is why targets are Go field names and why the annotation is indifferent to json +tags and to `NameFromTags`. + +The filter itself is one condition in `processStructField` (`struct.go`); everything else lives in +`omit.go`. + +### Resolution runs against the type, not the walk + +Each target is resolved once, at the annotation site, with `types.LookupFieldOrMethod` — Go's own +promoted-field lookup, so depth and ambiguity rules come for free. The scope then carries the +resulting `*types.Var` objects and the filter is pointer identity. + +Consequences worth keeping: there is no "what was actually omitted" bookkeeping (a target either +names a field of the type or it does not, known before the walk starts); a target already excluded +for another reason resolves fine and is silently redundant; and two same-named fields at different +depths can never be confused, because distinct fields are distinct objects. + +### Placement + +- **on the embed** (ergonomic): targets are plain field names of that + embedded type; +- **on the declaration** (power form): a dotted path names the embed + chain (`Base.ID`, head consumed per level), a bare name is offered to + each embed and resolved against its promoted set. + +Every path segment but the last must name an **embedded** field: `omit` removes promoted content, +the only thing the enclosing schema owns. A segment naming a regular field stops the walk and is +reported as unresolved — reaching through one would edit either another type's inlined copy or a +`$ref`'d definition. + +### Diagnostics (all Hints) + +| code | fires when | +|---|---| +| `scan.omit-unresolved` | the target names no field of the embedded type — a typo, or a rename upstream | +| `scan.omit-behind-ref` | the embed is composed as a `$ref` (an annotated model), where OAS2 cannot subtract a property; the omission is dropped rather than silently forking the definition | +| `scan.shadowed-embed-field` | a field re-declared with `json:"-"` carries the Go name of a promoted field (see below) | + +`swagger:omit` is the only construct whose output depends on a hand-written name the compiler never +checks — everything else is derived from types. `scan.omit-unresolved` is what stops it rotting +silently when a field is renamed upstream. + +### The `json:"-"` shadow is not what authors think + +`fields.go` deletes a promoted property when an outer field re-declares it with `json:"-"`. That is +**unfaithful to `encoding/json`**, which ignores a `-` field entirely: it never enters the name set, +so it cannot shadow the promoted one and Go keeps marshalling the embedded field. The behaviour is +locked by `TestOverridingOneIgnore` and left in place for now; the Hint points at `swagger:omit`, +which removes the field for real. Removing the eviction is a separate decision — see +`.claude/plans/swagger-omit.md` §7. + +Fixtures: `fixtures/enhancements/swagger-omit` (the annotation, both renderings, all three Hints, +plus the go-swagger#1992 shape verbatim) and `fixtures/enhancements/default-allof-embeds-override` +(the same overrides *without* the annotation — the documented limit). + ## §embed-depth — ambiguous-embed diagnostic mechanism `Builder.embedDepth` (incremented around `buildNamedEmbedded`'s @@ -1456,70 +1541,36 @@ edges fixture covers a different (strfmt-tagged) shape already. --- +### ✅ Named-strfmt + `swagger:model` combo (was 🟡 deferred) + +A type carrying both `swagger:strfmt phone` and `swagger:model` used to emit an +inconsistent pair: `{type: string, format: phone}` at the field site, but a +struct walk (`{type: object, properties: …}`) for the top-level definition. The +decl-level strfmt now wins and the field `$ref`s it — verified in +`enhancements_named_struct_tags-ref.json`: `PhoneNumber` is +`{type: string, format: phone}` and `Contact.phone` is a `$ref` to it. + +Fixed by the F-series pass (`8e20d2f`, quirk F1), not by the attempt described +in the original entry — which was reverted. History: +`.claude/plans/archive/deferred-quirks.md` D3. + +### ✅ Cross-package definition-name collisions (was 🟡 silently overwrite) + +Two packages declaring the same identifier (`pkg/a.User`, `pkg/b.User`) both +mapped to `definitions["User"]`, and the second build silently overwrote the +first — one `User` in the output, no record of the collision. + +Fixed by the name-identity / cyclic-`$ref` work: every definition is keyed by a +compiler-unique `DefKey` (`/`) while building, and a final reduce +stage projects each back to the shortest unique name, deconflicting collisions +(`AWidget` / `BWidget`) and raising a `scan.renamed-definition` Hint. See +[§discovery](#discovery) and `.claude/plans/name-identity-cyclic-ref.md`. + ## §quirks-open — still open -### 🟡 Named-strfmt + `swagger:model` combo (deferred) - -When the author combines `swagger:strfmt` with `swagger:model` -on the same type, the FIELD reference inlines as `{string, format}` -(via the strfmt classifier) but the TOP-LEVEL definition body is -still emitted from walking the underlying struct. - -**Reproduction.** Fixture `fixtures/enhancements/named-struct-tags-ref/types.go` -declares `PhoneNumber` with both `swagger:strfmt phone` and -`swagger:model`, used by `Contact.Phone`. The golden -`enhancements_named_struct_tags-ref.json` captures the observable -inconsistency: - -- Field site: `{type: "string", format: "phone"}` — strfmt wins. -- Top-level definition: `{type: "object", properties: {CountryCode, Number}}` — - the struct walk wins; the strfmt annotation is ignored at decl time. - -The author asked for "named strfmt" (a reusable `PhoneNumber` -definition rendered as a formatted string) but gets an inconsistent -pair: the field says string, the definition says object. - -**Attempted fix and reasons it reverted.** The first attempt -(referred to as "Option 1") would have: - -1. Detected `swagger:strfmt` on the decl in `buildDeclNamed` and - emitted `{string, fmt}` instead of walking the struct body. -2. In `buildNamedStruct`, when the target also has `swagger:model`, - emitted `$ref` instead of inlining the strfmt. - -This was reverted before merge because: - -- Pre-existing fixtures in - `fixtures/goparsing/classification/transitive/mods/aliases.go` use - the same `swagger:strfmt + swagger:model` combination on - defined-from-`time.Time` types (e.g. `SomeTimeType time.Time`). - The existing tests (`TestAliasedTypes`, `TestAliasedModels`) - assert the *inline* baseline (`scantest.AssertProperty(..., "string", ...)`) - rather than a `$ref`. Option 1 flips these to `$ref`, requiring - coordinated test updates. -- The decl-level `StrfmtName` check also over-fires on slice / array / - map underlyings: `type SomeTimesType []time.Time` with - `swagger:strfmt date-time` should emit - `{array, items: {string, date-time}}`, not flatten to `{string}`. - A correct fix would gate the check on struct-underlying first, - then symmetrically consider whether `buildNamedSlice` / - `buildNamedArray` / `buildNamedMap` should also route through - `$ref` under the `swagger:model` combination. - -The surface area is wider than the Option 1 code change suggested, -and the existing test coverage of the combination is entangled with -the inconsistency itself. - -**Why deferred.** The combination is niche, the footgun is narrow -(you get what you asked for on one side of the indirection, not -both), and v2's annotation redesign can reshape the contract without -carrying this legacy. A focused decision on "named strfmt" semantics -belongs in the v2 design, not a bug-fix pass. - -The `named-struct-tags-ref` fixture and its golden are checked in as -a deliberate marker — the golden captures the observable -inconsistency (inline field + struct-body definition) so future work -on this decision has a failing test to anchor against. +> **Where open quirks live.** This section documents caveats *of this package*. +> The project-wide register of what is actually open — verified, with the stale +> historical registers called out — is `.claude/plans/quirks-open.md`. ### 🟦 `interface{}` literals (documented behaviour) @@ -1542,155 +1593,18 @@ the substituted underlying via the `TypeArgs` short-circuit without a concrete instantiation simply have no representable schema. -### 🟡 Cross-package definition-name collisions silently overwrite +### 🟡 A field-level enum override discards the type's per-value docs, silently -`buildFromDecl` writes the top-level schema as -`s.definitions[s.Name] = schema`, keyed only by the Go identifier -(`decl.Names()[0]`). When two packages in a single scan declare a type -with the same identifier — `pkg/a.User` and `pkg/b.User` — both map -to `definitions["User"]` and the second build silently overwrites the -first. The output spec carries only one `User`, with no record of the -collision and no signal of which package won. +When a field whose type is marked `swagger:enum TypeName` carries its own +`enum:` override, the inherited `x-go-enum-desc` is stripped along with the +replaced values — and **no diagnostic is raised**, so the per-value docs +`TypeName` contributed vanish without a trace. Reproduced by +`fixtures/enhancements/enum-overrides` case E (`NotificationE`). -The existing `nameByJSON` (`propOwner`) map in field emission is **not** -a defense against this case: it tracks JSON property names within a -single struct's field set plus its embeds (for the ambiguous-embed -diagnostic), not type-level identifier conflicts across packages. +Whether the docs should be filtered through (when the override *subsets* the +type's values) or dropped with a Hint (when it narrows to *different* ones) is a +design call that belongs with the enum feature, not with this package: see +`.claude/plans/features/enum-richer-values.md` §1.2b. -#### Target shape - -A proper fix needs three pieces: - -1. **Detection** — at write time, recognise the case "definition key - already exists with non-empty schema and originates from a different - package" (use `x-go-package`, or stash origin in the `Builder`). -2. **Diagnostic** — emit `CodeNameConflict` (severity - `SeverityWarning` minimum, possibly `SeverityError` under strict - mode) carrying both `(pkg, name)` pairs. -3. **Policy** — open design call: - - **a. Rename** — prefix loser(s) with a stable short-package - (e.g. `a_User`, `b_User`). Stable but ugly; needs all `$ref`s - to follow the rename — cross-cutting. - - **b. Skip + warn** — keep the first writer, drop subsequent - ones, emit a warning. Predictable but lossy. - - **c. Fail the build** — under strict mode, treat as an error. - Forces the author to rename in source. Cleanest semantics, - most disruptive. - -#### Why deferred - -Each policy choice changes the contract for downstream code generators -(go-swagger, oapi-codegen, …) — they have assumptions about -`definitions` keys matching exported Go names. The "rename" path -additionally requires every `$ref` writer in the builders to consult a -rename map; the surface is wide. - -For multi-package scans where the author controls both packages, the -workaround today is to scope scans to one package per spec, or to -rename one of the colliding types at the source. A future strict-mode -flag (e.g. `Options.StrictNameConflicts`) could enable option (c) -without breaking existing scans. - -### 🟡 Stale `x-go-enum-desc` after a field-level enum override - -When a field uses a type marked `swagger:enum TypeName` **and** carries -its own `enum: …` override, v1 mutates the schema in place: it replaces -`Enum`, strips the inherited `x-go-enum-desc`, and trims the matching -description suffix. This is **lossy** — the per-value docs contributed -by `TypeName` are silently discarded. - -Concretely, given (fixture `fixtures/enhancements/enum-overrides/`, -case E): - -```go -// swagger:enum PriorityE -type PriorityE string - -const ( - PriorityELow PriorityE = "low" // low-priority requests - PriorityEMed PriorityE = "medium" // medium-priority requests - PriorityEHigh PriorityE = "high" // high-priority requests -) - -type NotificationE struct { - // Inline enum provides a narrower set than the const block. - // - // enum: urgent, normal - Priority PriorityE `json:"priority"` -} -``` - -v1 emits: - -```yaml -priority: - type: string - enum: [urgent, normal] # the override wins - description: "Inline enum provides a narrower set than the const block." - # x-go-enum-desc removed by clearStaleEnumDesc - # PriorityE's per-value doc lines silently dropped from description -``` - -The cleanup runs reactively from `schemaValidations.SetEnum` -([typable.go](typable.go#L128)) via `clearStaleEnumDesc` -([extensions.go](extensions.go#L42)). It treats any -`x-go-enum-desc` present at `SetEnum` time as inherited (and therefore -stale once `Enum` is replaced), deletes it, and trims the matching -suffix off `Description`. The `TrimSuffix` dance is fragile — it -relies on the enum-desc pipeline having appended the doc lines as a -literal suffix — but it works under v1's emission discipline. - -#### Target shape (allOf composition) - -OpenAPI 2.0 supports `allOf` for schema composition, so the cleaner -model does not have to wait for OAS 3. The replacement shape is: - -```yaml -# PriorityE promoted to a top-level definition: -definitions: - PriorityE: - type: string - enum: [low, medium, high] - description: | - low: low-priority requests - medium: medium-priority requests - high: high-priority requests - x-go-enum-desc: | - low: low-priority requests - medium: medium-priority requests - high: high-priority requests - - NotificationE: - type: object - properties: - priority: - description: "Inline enum provides a narrower set than the const block." - allOf: - - $ref: '#/definitions/PriorityE' # inherited enum + per-value docs - - enum: [urgent, normal] # the override -``` - -Each branch keeps its own concern: - -- the `$ref` branch carries `PriorityE`'s full schema (values + docs + - `x-go-enum-desc`), untouched and reusable by every field that - references `PriorityE`; -- the inline branch carries the narrowing override only. - -No mutation of the inherited schema, no `TrimSuffix` dance. Validator -semantics for enum-narrowing `allOf` aren't perfectly uniform across -tools, but for the documentation / code-gen use cases codescan feeds -(go-swagger, oapi-codegen, redoc, …) this composition preserves both -layers cleanly. - -#### Prerequisites for the migration (both currently missing) - -1. **Promote unannotated `swagger:enum` types to top-level definitions** - so the `$ref` branch has a target. Today they exist only as inlined - fragments on each referring field. -2. **Move override detection from `SetEnum` (validation hook) to the - field-emission path**, so the override is composed alongside the - inherited schema instead of mutating it after the fact. - -Until both land, `clearStaleEnumDesc` stays in place. The TODO in -`extensions.go` flags it as the replacement target. +The stripping itself lives in `handlers/dispatch_schema.go:clearStaleEnumDesc`, +which is where either resolution would land. diff --git a/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/allof.go b/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/allof.go index a85ceea86d..8f2680f9d7 100644 --- a/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/allof.go +++ b/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/allof.go @@ -28,6 +28,10 @@ import ( func (s *Builder) scanEmbeddedFields( decl *scanner.EntityDecl, st *types.Struct, schema *oaispec.Schema, nameByJSON map[string]propOwner, ) (target *oaispec.Schema, hasAllOf bool, err error) { + // `swagger:omit` written on the DECLARATION (the power form) applies to the embeds walked below; + // the ergonomic form lives on each embed and is read from its fieldDoc. + declOmits := s.declOmitTargets(decl.Comments) + for fld := range st.Fields() { if !fld.Anonymous() { continue @@ -60,8 +64,18 @@ func (s *Builder) scanEmbeddedFields( isAllOf = true } + // `swagger:omit` is a PRE-filter: the listed fields are dropped before the embed is walked, so + // nothing is ever written for them. That makes the annotation mean the same thing whether the + // embed is inlined or composed into an allOf member — see omit.go. + // An embed emitted as a $ref cannot have a property subtracted; embedOmitTargets reports that + // and filters nothing. + restoreOmits := s.pushOmitted( + s.embedOmitTargets(fld, afld, fd, declOmits, isAllOf && s.isModelEmbed(fld.Type())), + ) + if !isAllOf { target, err = s.buildPlainEmbed(fld, afld, fd, isString, omitEmpty, schema, target, nameByJSON) + restoreOmits() if err != nil { return nil, false, err } @@ -73,8 +87,10 @@ func (s *Builder) scanEmbeddedFields( target = &oaispec.Schema{} } var newSch oaispec.Schema - if err := s.buildAllOf(fld.Type(), &newSch); err != nil { - return nil, false, err + buildErr := s.buildAllOf(fld.Type(), &newSch) + restoreOmits() + if buildErr != nil { + return nil, false, buildErr } if fd.AllOfClass != "" { diff --git a/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/fields.go b/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/fields.go index 687404cf4c..f840207ac0 100644 --- a/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/fields.go +++ b/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/fields.go @@ -270,6 +270,11 @@ func (s *Builder) structFieldCarrier(fld *types.Var, decl *scanner.EntityDecl, t return fieldCarrier{}, false, err } if ignore { + // A `json:"-"` re-declaration does NOT shadow a promoted field in Go — encoding/json ignores the + // field entirely, so the embedded one keeps marshalling. Report it and point at swagger:omit, + // which drops it for real. + s.warnShadowedByJSONDash(fld, afld, target, nameByJSON) + for jsonName, prior := range nameByJSON { if prior.goName == fld.Name() { delete(target.Properties, jsonName) diff --git a/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/omit.go b/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/omit.go new file mode 100644 index 0000000000..d888a1e383 --- /dev/null +++ b/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/omit.go @@ -0,0 +1,307 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package schema + +import ( + "go/ast" + "go/token" + "go/types" + "strings" + + "github.com/go-openapi/codescan/internal/parsers/grammar" + oaispec "github.com/go-openapi/spec" +) + +// omitPathSep separates the segments of a qualified `swagger:omit` target (`Base.ID`). +const omitPathSep = "." + +// parseOmitTargets splits a `swagger:omit` argument into its targets. +// +// The lexer captures the whole remainder verbatim (the list may carry spaces after commas), so the +// split happens here: comma-separated, each target trimmed, empties dropped. +func parseOmitTargets(arg string) []string { + var out []string + for raw := range strings.SplitSeq(arg, ",") { + if t := strings.TrimSpace(raw); t != "" { + out = append(out, t) + } + } + + return out +} + +// resolveOmitTargets resolves each target against the embedded type, returning the field objects to +// filter out of the promotion walk. +// +// Resolution runs against the TYPE, not the walk: a target either names a (promoted) field of the +// embedded type or it does not, and that is known before a single property is written. The scope +// then carries field OBJECTS, so the pre-filter is pointer identity — no name matching, no +// bookkeeping of what was "actually omitted", and no collision between same-named fields at +// different depths. +// +// [types.LookupFieldOrMethod] is Go's own promoted-field lookup, so depth and ambiguity rules come +// for free: a target that Go itself could not resolve unambiguously resolves to nothing here and is +// reported. +// +// Unresolved targets raise a scan.omit-unresolved Hint (see [grammar.CodeOmitUnresolved]) — this is +// what stops the annotation rotting silently when a field is renamed upstream. +func (s *Builder) resolveOmitTargets(embedded types.Type, targets []string, pos token.Position) []*types.Var { + if len(targets) == 0 { + return nil + } + + out := make([]*types.Var, 0, len(targets)) + for _, target := range targets { + fld, ok := lookupOmitPath(embedded, strings.Split(target, omitPathSep)) + if !ok { + s.hintf(pos, grammar.CodeOmitUnresolved, + "swagger:omit: %s has no field %q; the target is ignored", typeLabel(embedded), target) + + continue + } + out = append(out, fld) + } + + return out +} + +// lookupOmitPath walks a dotted target (`Inner.Deep`) through the embed chain of tpe and returns the +// field the last segment names. +// +// Every segment but the last must name an EMBEDDED field: `swagger:omit` removes promoted content, +// which is the only thing the enclosing schema owns. A segment naming a regular field stops the walk +// (reported as unresolved by the caller) — reaching through a regular field would either edit +// another type's inlined copy or, worse, a `$ref`'d definition. +func lookupOmitPath(tpe types.Type, path []string) (*types.Var, bool) { + cur := tpe + for i, seg := range path { + obj, _, _ := types.LookupFieldOrMethod(cur, true, pkgOf(cur), seg) + fld, isField := obj.(*types.Var) + if !isField { + return nil, false + } + if i == len(path)-1 { + return fld, true + } + if !fld.Embedded() { + return nil, false + } + cur = fld.Type() + } + + return nil, false +} + +// pkgOf returns the package a named type belongs to, or nil for an unnamed one. +// +// [types.LookupFieldOrMethod] needs it only to decide whether unexported names are visible; a nil +// package restricts the lookup to exported fields, which is the right default for an unnamed type. +func pkgOf(tpe types.Type) *types.Package { + switch t := types.Unalias(tpe).(type) { + case *types.Pointer: + return pkgOf(t.Elem()) + case *types.Named: + if obj := t.Obj(); obj != nil { + return obj.Pkg() + } + } + + return nil +} + +// typeLabel names a type for a diagnostic — the declared name when there is one, else the type +// string. +func typeLabel(tpe types.Type) string { + if named, ok := types.Unalias(tpe).(*types.Named); ok { + return named.Obj().Name() + } + + return tpe.String() +} + +// pushOmitted adds fields to the active omit scope and returns the function restoring it. +// +// The scope is a set of field objects in force for the subtree currently being walked; nesting +// accumulates, and each level removes only what it added, so sibling embeds never leak into one +// another. +func (s *Builder) pushOmitted(fields []*types.Var) func() { + if len(fields) == 0 { + return func() {} + } + if s.omitted == nil { + s.omitted = make(map[*types.Var]struct{}, len(fields)) + } + + added := make([]*types.Var, 0, len(fields)) + for _, fld := range fields { + if _, dup := s.omitted[fld]; dup { + continue + } + s.omitted[fld] = struct{}{} + added = append(added, fld) + } + + return func() { + for _, fld := range added { + delete(s.omitted, fld) + } + } +} + +// isOmitted reports whether fld is filtered out by the active `swagger:omit` scope. +// +// This is the whole enforcement: a pre-filter on the promotion walk, so the property is never +// written. Because it runs before any name is computed, it is indifferent to json tags, to +// `NameFromTags`, and to whether the enclosing schema is inlined or composed with allOf. +func (s *Builder) isOmitted(fld *types.Var) bool { + if len(s.omitted) == 0 { + return false + } + _, omitted := s.omitted[fld] + + return omitted +} + +// embedOmitTargets collects the `swagger:omit` targets that apply to one embed and resolves them +// against the embedded type. +// +// Sources: the embed's own annotation (the ergonomic form — targets are plain field names of that +// type) and the enclosing declaration's annotation (the power form — a bare name resolved against +// the promoted set, or a dotted path whose head names this embed). +// +// isAllOfRef marks an embed that will be emitted as a `$ref` rather than inlined; the omission +// cannot be expressed there, so it is dropped with a scan.omit-behind-ref Hint instead of silently +// forking the referenced definition. +func (s *Builder) embedOmitTargets( + fld *types.Var, afld *ast.Field, fd fieldDoc, declOmits []string, isAllOfRef bool, +) []*types.Var { + targets := append([]string(nil), fd.OmitTargets...) + targets = append(targets, declOmitsFor(fld, declOmits)...) + if len(targets) == 0 { + return nil + } + + pos := s.Ctx.PosOf(afld.Pos()) + if isAllOfRef { + s.hintf(pos, grammar.CodeOmitBehindRef, + "swagger:omit: %s is composed as a $ref; a referenced definition cannot have a property "+ + "subtracted, so the omission is dropped", typeLabel(fld.Type())) + + return nil + } + + return s.resolveOmitTargets(fld.Type(), targets, pos) +} + +// declOmitsFor selects the declaration-level targets that apply to one embed: a dotted path whose +// head names this embed (with the head consumed), and every bare name — a bare name is resolved +// against the promoted set of each embed in turn, exactly as Go resolves a promoted field. +// +// A bare name that matches no embed is reported once per embed it was tried against; that is the +// intended behaviour for a typo and is silent for the (redundant, harmless) case where the field was +// already excluded for another reason. +func declOmitsFor(fld *types.Var, declOmits []string) []string { + if len(declOmits) == 0 { + return nil + } + + var out []string + for _, target := range declOmits { + head, tail, qualified := strings.Cut(target, omitPathSep) + if !qualified { + out = append(out, target) + + continue + } + if head == fld.Name() { + out = append(out, tail) + } + } + + return out +} + +// hintf emits an informational diagnostic through the builder's sink. +func (s *Builder) hintf(pos token.Position, code grammar.Code, format string, args ...any) { + if onDiag := s.Ctx.OnDiagnostic(); onDiag != nil { + onDiag(grammar.Hintf(pos, code, format, args...)) + } +} + +// declOmitTargets reads the declaration-level `swagger:omit` targets off a type's doc comment. +// +// These are the power form, for embeds the author cannot annotate directly (a type they do not own, +// or one nested deeper than their own source). +func (s *Builder) declOmitTargets(cg *ast.CommentGroup) []string { + if cg == nil { + return nil + } + + var out []string + for _, b := range s.ParseBlocks(cg) { + if b.AnnotationKind() != grammar.AnnOmit { + continue + } + if arg, ok := b.AnnotationArg(); ok { + out = append(out, parseOmitTargets(arg)...) + } + } + + return out +} + +// isModelEmbed reports whether an embedded type is a `swagger:model`, i.e. whether composing it into +// an allOf member emits a `$ref` rather than an inline copy (see buildNamedAllOf). +// +// A `$ref` member is where `swagger:omit` cannot be expressed at all. +func (s *Builder) isModelEmbed(tpe types.Type) bool { + named, ok := types.Unalias(derefType(tpe)).(*types.Named) + if !ok { + return false + } + obj := named.Obj() + if obj == nil || obj.Pkg() == nil { + return false + } + decl, found := s.Ctx.GetModel(obj.Pkg().Path(), obj.Name()) + + return found && decl.HasModelAnnotation() +} + +// derefType peels a pointer, so `*Base` classifies exactly like `Base`. +func derefType(tpe types.Type) types.Type { + if ptr, ok := types.Unalias(tpe).(*types.Pointer); ok { + return ptr.Elem() + } + + return tpe +} + +// warnShadowedByJSONDash reports a field re-declared with `json:"-"` that carries the same Go name as +// a field promoted from an embed. +// +// encoding/json ignores a `-` field ENTIRELY — it never enters the name set, so it cannot shadow the +// promoted field and Go keeps marshalling the embedded one. The author almost certainly meant +// `swagger:omit`, which removes it from the schema for real. +// +// See [grammar.CodeShadowedEmbedField]. +func (s *Builder) warnShadowedByJSONDash(fld *types.Var, afld *ast.Field, target *oaispec.Schema, nameByJSON map[string]propOwner) { + if afld == nil || len(nameByJSON) == 0 { + return + } + for jsonName, prior := range nameByJSON { + if prior.goName != fld.Name() || prior.depth == 0 { + continue + } + if _, written := target.Properties[jsonName]; !written { + continue + } + s.hintf(s.Ctx.PosOf(afld.Pos()), grammar.CodeShadowedEmbedField, + "field %s is re-declared with `json:\"-\"`, but encoding/json ignores such a field entirely: "+ + "it does not shadow the %q promoted from an embed, which Go still marshals. "+ + "Use swagger:omit on the embed to drop it from the schema", fld.Name(), jsonName) + + return + } +} diff --git a/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/schema.go b/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/schema.go index 89cf3022e6..2b260a135b 100644 --- a/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/schema.go +++ b/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/schema.go @@ -48,6 +48,13 @@ type Builder struct { // The mechanism is shared with the parameters and responses builders via common.EmbedInheritance; // the schema builder consumes only Required (it has no `in:` location concept). embedInherited common.EmbedInheritance + + // omitted is the set of struct fields the active `swagger:omit` scope filters out of the + // promotion walk, keyed by field object (see omit.go). + // + // Pushed around each embed walk and popped after it, so sibling embeds never leak into one + // another. + omitted map[*types.Var]struct{} } // NewBuilder constructs an initialized [Builder]. diff --git a/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/struct.go b/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/struct.go index aac5257a03..97cd39faba 100644 --- a/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/struct.go +++ b/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/struct.go @@ -74,6 +74,12 @@ func (s *Builder) buildStructFields(decl *scanner.EntityDecl, st *types.Struct, } func (s *Builder) processStructField(fld *types.Var, decl *scanner.EntityDecl, target *oaispec.Schema, nameByJSON map[string]propOwner) error { + // `swagger:omit` pre-filter: the field is not promoted, so no property is written for it and no + // name is ever computed. See omit.go. + if s.isOmitted(fld) { + return nil + } + c, ok, err := s.structFieldCarrier(fld, decl, target, nameByJSON) if err != nil || !ok { return err diff --git a/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/type_override.go b/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/type_override.go index 140be036f1..78e5a9607e 100644 --- a/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/type_override.go +++ b/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/type_override.go @@ -20,7 +20,7 @@ import ( // (never a $ref). // // It is the single resolution point for the keyword consumed at every swagger:type site (the F3 -// reconciliation — see .claude/plans/quirks-F-series-fix.md). +// reconciliation — see .claude/plans/archive/quirks-F-series-fix.md). // // - ownType is the annotated field/decl's Go type, consumed by the // `inline` / `array` keywords (which expand that type in place). May be diff --git a/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/walker_classifiers.go b/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/walker_classifiers.go index 40bd547a73..fd55c30c74 100644 --- a/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/walker_classifiers.go +++ b/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/schema/walker_classifiers.go @@ -81,7 +81,7 @@ func (s *Builder) classifierTextMarshal(tpe types.Type, tgt ifaces.SwaggerTypabl // It is the single resolution point for `swagger:type` on a named type: it routes the argument // through resolveTypeOverride (always inlining — keyword scalars / Go builtins / `[]T` / `inline` // / `array` / type-name refs), and applies a co-present `swagger:strfmt` as a supplementary format -// only when compatible with the resolved type (F3 — see .claude/plans/quirks-F-series-fix.md). +// only when compatible with the resolved type (F3 — see .claude/plans/archive/quirks-F-series-fix.md). // ownType is the named Go type (consumed by the `inline`/`array` keywords); pos drives diagnostics. // // Reports back via the (handled, fallthrough) tuple: @@ -291,6 +291,11 @@ type fieldDoc struct { // // Empty for bare `swagger:allOf`. AllOfClass string + // OmitTargets — arguments of `swagger:omit [,…]` written ON AN EMBED: the Go field + // names not to promote out of the embedded type. + // + // Nil when the annotation is absent. See omit.go. + OmitTargets []string } // scanFieldDoc inspects afld's docstring through the ParseBlocks cache and returns every @@ -306,7 +311,7 @@ func (s *Builder) scanFieldDoc(afld *ast.Field) fieldDoc { } var nameKeyword string for _, b := range s.ParseBlocks(afld.Doc) { - switch b.AnnotationKind() { //nolint:exhaustive // field-level walker only consumes these five kinds + switch b.AnnotationKind() { //nolint:exhaustive // field-level walker only consumes these six kinds case grammar.AnnIgnore: fd.Ignored = true case grammar.AnnName: @@ -326,6 +331,10 @@ func (s *Builder) scanFieldDoc(afld *ast.Field) fieldDoc { if name, ok := b.AnnotationArg(); ok { fd.AllOfClass = name } + case grammar.AnnOmit: + if arg, ok := b.AnnotationArg(); ok { + fd.OmitTargets = append(fd.OmitTargets, parseOmitTargets(arg)...) + } } // The `name:` keyword is the canonical field-naming keyword, honoured uniformly across schema / // param / header (doc-quirk G2). diff --git a/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/spec/prune.go b/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/spec/prune.go index cce057c8e1..60f3cc4c01 100644 --- a/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/spec/prune.go +++ b/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/spec/prune.go @@ -96,6 +96,16 @@ func (s *Builder) pruneUnusedModels() { queue = queue[1:] sch := s.input.Definitions[cur] collectDefRefs(&sch, mark) + + // A reachable DISCRIMINATED base keeps its whole polymorphic family: the subtypes reference the + // base, never the reverse, so the $ref walk above cannot see them and would prune the family down + // to its base alone. + // This is the discriminator reachability rule deferred here by §12 (go-swagger#1913 / §15). + if isDiscriminated(&sch) { + for _, sub := range s.subtypeKeysOf(cur) { + mark(sub) + } + } } // Prune the unreachable, deterministically. diff --git a/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/spec/spec.go b/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/spec/spec.go index 6e5f546a2a..a10f3dcf17 100644 --- a/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/spec/spec.go +++ b/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/spec/spec.go @@ -55,6 +55,15 @@ type Builder struct { // DefKey, so the prune (scan.pruned-unused) and rename (scan.renamed-definition) Hints can be // located at the originating Go type even though the spec node may by then be gone or renamed. declPos map[string]token.Position + // declIdentity records the Go type identity (typeIdentity) behind each discovered definition, + // keyed by DefKey — the bridge from definition-key space back to the reverse `swagger:allOf` index + // used by the discriminator-subtype pull (see subtypes.go). + declIdentity map[string]string + // subtypeIdx is the lazily-built reverse `swagger:allOf` index (base type identity → subtype + // declarations). + // + // Built once per Build, from the model index, which is independent of ScanModels. + subtypeIdx subtypeIndex // sharedParamPos / sharedRespPos record the source position of each scanned shared parameter / // response, keyed by its registered name, so the shared prune (scan.pruned-unused, C4) can locate // its Hint at the originating Go declaration. @@ -110,6 +119,7 @@ func NewBuilder(input *oaispec.Swagger, sc *scanner.ScanCtx, scanModels bool) *B responses: input.Responses, parameters: input.Parameters, declPos: make(map[string]token.Position), + declIdentity: make(map[string]string), sharedParamPos: make(map[string]token.Position), sharedRespPos: make(map[string]token.Position), pinnedParams: pinnedParams, @@ -362,6 +372,10 @@ func (s *Builder) buildDiscoveredSchema(decl *scanner.EntityDecl) error { // node may have been dropped or renamed. s.declPos[decl.DefKey()] = s.ctx.PosOf(decl.Ident.Pos()) + // Stash the Go type behind this definition so the prune reachability walk can ask the reverse + // `swagger:allOf` index for the subtypes of a discriminated base (see subtypeKeysOf). + s.declIdentity[decl.DefKey()] = typeIdentity(decl.Obj()) + // Cross-ref linkage: initiate the base pointer for this definition so the schema builder // path-joins its members (properties, …) under it, and anchor the definition node itself to its // type declaration. @@ -392,6 +406,13 @@ func (s *Builder) buildDiscoveredSchema(decl *scanner.EntityDecl) error { s.discovered = append(s.discovered, sb.PostDeclarations()...) + // Reverse discovery: if this definition turned out to be a discriminated base, its subtypes belong + // to the emitted document too — nothing $refs them, so top-down discovery can never reach them + // (go-swagger#1913). + // Queued like any other discovery, so the loop above keeps pulling their own dependencies, and a + // subtype that is itself a discriminated base cascades. + s.discovered = append(s.discovered, s.discriminatedSubtypesOf(decl)...) + return nil } diff --git a/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/spec/subtypes.go b/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/spec/subtypes.go new file mode 100644 index 0000000000..279de2ebc6 --- /dev/null +++ b/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/spec/subtypes.go @@ -0,0 +1,293 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package spec + +import ( + "go/ast" + "go/types" + "sort" + + "github.com/go-openapi/codescan/internal/parsers/grammar" + "github.com/go-openapi/codescan/internal/scanner" + oaispec "github.com/go-openapi/spec" +) + +// subtypeIndex is the reverse `swagger:allOf` index: Go type identity of a base (see typeIdentity) +// mapped to every `swagger:model` declaration that declares that base as an `allOf` member. +// +// It is the inverse of the reference direction the spec carries: a subtype `$ref`s its base, never +// the other way round, so a discriminated base entering the reachable closure can only pull its +// family in by looking the relation up backwards. +// §15 / go-swagger#1913. +type subtypeIndex map[string][]*scanner.EntityDecl + +// subtypes returns the reverse `swagger:allOf` index, building it on first use. +// +// The source is ScanCtx.Models() — populated by classification whether or not ScanModels is set, so +// the index sees every annotated subtype even when none of them would be built (the no-`-m` case +// this feature exists for). +// +// Entries are ordered by definition key so the pull order — and hence the order of the emitted +// scan.discovered-subtype Hints — does not depend on map iteration. +func (s *Builder) subtypes() subtypeIndex { + if s.subtypeIdx != nil { + return s.subtypeIdx + } + + idx := make(subtypeIndex) + parser := grammar.NewParser(s.ctx.FileSet()) + for _, decl := range s.ctx.Models() { + for _, base := range allOfBases(decl, parser) { + idx[base] = append(idx[base], decl) + } + } + for base := range idx { + decls := idx[base] + sort.Slice(decls, func(i, j int) bool { return decls[i].DefKey() < decls[j].DefKey() }) + } + + s.subtypeIdx = idx + + return idx +} + +// discriminatedSubtypesOf returns the declarations to pull into discovery because decl — whose +// definition has just been built — is a discriminated base. +// +// The gate is the built definition's own `discriminator`, not a source-level re-derivation: it holds +// uniformly for an interface base with a `discriminator: true` member and for a struct base with a +// `discriminator: true` field, and it is the same fact the emitted document exposes. A base with no +// discriminator pulls nothing: its `allOf` users are ordinary compositions, not a polymorphic +// family, and inventing definitions for them would over-generate. +// +// Under ScanModels the pass is a no-op: buildModels already builds every annotated model, so there +// is nothing to pull — and pulling anyway would emit Hints whose order follows the model index's map +// iteration. The `-m` case is served by the prune reachability rule instead (subtypeKeysOf), which is +// where a discriminated family can actually be lost. +// +// Subtypes already emitted are skipped; the discovery loop dedups queued ones, and only genuinely new +// pulls are reported as Hints. +func (s *Builder) discriminatedSubtypesOf(decl *scanner.EntityDecl) []*scanner.EntityDecl { + if s.scanModels { + return nil + } + + sch, built := s.definitions[decl.DefKey()] + if !built || !isDiscriminated(&sch) { + return nil + } + + candidates := s.subtypes()[typeIdentity(decl.Obj())] + if len(candidates) == 0 { + return nil + } + + onDiag := s.ctx.OnDiagnostic() + out := make([]*scanner.EntityDecl, 0, len(candidates)) + for _, sub := range candidates { + if _, already := s.definitions[sub.DefKey()]; already { + continue + } + out = append(out, sub) + if onDiag != nil { + onDiag(grammar.Hintf(s.ctx.PosOf(sub.Ident.Pos()), grammar.CodeDiscoveredSubtype, + "definition %q discovered as a subtype of discriminated base %q", + leafName(sub.DefKey()), leafName(decl.DefKey()))) + } + } + + return out +} + +// isDiscriminated reports whether a definition DECLARES a discriminator of its own — the gate both +// hooks share. +// +// Two shapes carry one, depending on how the type is written: +// +// - a plain base puts it at the top level ({type: object, properties, +// discriminator}); +// - a MID-LEVEL base — a subtype that is itself a base — renders as +// `allOf: [{$ref parent}, {own props, discriminator}]`, so its +// discriminator sits in its own compound member, not at the top. +// Multi-level hierarchies would otherwise stop at the first level. +// +// Only an inline discriminator counts. A leaf subtype's `$ref` member is not followed, so it does not +// inherit its base's discriminator and does not masquerade as a base itself. +func isDiscriminated(sch *oaispec.Schema) bool { + if sch == nil { + return false + } + if sch.Discriminator != "" { + return true + } + for i := range sch.AllOf { + if isDiscriminated(&sch.AllOf[i]) { + return true + } + } + + return false +} + +// subtypeKeysOf returns the definition keys of the subtypes of the base emitted under defKey. +// +// Used by the prune reachability walk, which works in definition-key space; the bridge back to Go +// type identity is the declIdentity side table, recorded for every definition as it is built. +// Keys are returned whether or not they are (still) present in the definitions map — marking is +// membership-checked by the caller. +func (s *Builder) subtypeKeysOf(defKey string) []string { + identity, known := s.declIdentity[defKey] + if !known { + return nil + } + + subs := s.subtypes()[identity] + if len(subs) == 0 { + return nil + } + + out := make([]string, 0, len(subs)) + for _, sub := range subs { + out = append(out, sub.DefKey()) + } + + return out +} + +// allOfBases returns the Go type identity of every base that decl declares as an `allOf` member, +// i.e. each embedded field carrying `swagger:allOf`. +// +// Only explicit `swagger:allOf` counts. DefaultAllOfForEmbeds — which renders a plain embed AS allOf +// composition — is deliberately not honoured: it is a rendering knob, and letting it decide which +// definitions exist would make the emitted set depend on an unrelated option. The polymorphic idiom +// is the explicit annotation. +// +// `swagger:ignore` on the embed drops it here exactly as it does in the schema builder, so the index +// never claims a relation the document does not carry. +// Declarations with no embeddable members (an alias, a named basic type, …) yield nothing. +func allOfBases(decl *scanner.EntityDecl, parser grammar.Parser) []string { + if decl.Spec == nil || decl.Pkg == nil || decl.Pkg.TypesInfo == nil { + return nil + } + + var out []string + seen := make(map[string]struct{}) + for _, afld := range embeddableMembers(decl.Spec) { + if len(afld.Names) != 0 { // named field: never an embed + continue + } + if !isAllOfEmbed(afld, parser) { + continue + } + tv, known := decl.Pkg.TypesInfo.Types[afld.Type] + if !known { + continue + } + // Deduped: a struct can reach one base through two embeds (the type and an alias of it), and the + // index must not list the same subtype twice under one base. + for _, id := range baseIdentities(tv.Type) { + if _, dup := seen[id]; dup { + continue + } + seen[id] = struct{}{} + out = append(out, id) + } + } + + return out +} + +// embeddableMembers returns the member list of a declaration that can hold an embed: a struct's +// fields or an interface's method list. +// +// Both matter: a struct embeds its base as an anonymous field, and an INTERFACE embeds its base as an +// anonymous interface — which is how a mid-level type in a multi-level hierarchy is written (a +// subtype of the root that is itself a discriminated base). In both AST shapes an embed is the entry +// with no Names. +// Returns nothing for any other type shape. +func embeddableMembers(spec *ast.TypeSpec) []*ast.Field { + switch tpe := spec.Type.(type) { + case *ast.StructType: + if tpe.Fields == nil { + return nil + } + return tpe.Fields.List + case *ast.InterfaceType: + if tpe.Methods == nil { + return nil + } + return tpe.Methods.List + default: + return nil + } +} + +// isAllOfEmbed reports whether an embedded field's doc marks it as an `allOf` member. +// +// This mirrors the `IsAllOfMember` / `Ignored` half of the schema builder's field-doc classifier +// (schema.scanFieldDoc) — the index needs those two signals only, and the spec builder owns no +// field-level walker to borrow them from. +func isAllOfEmbed(afld *ast.Field, parser grammar.Parser) bool { + if afld.Doc == nil { + return false + } + + var isAllOf bool + for _, b := range parser.ParseAll(afld.Doc) { + switch b.AnnotationKind() { //nolint:exhaustive // only these two signals matter to the index + case grammar.AnnIgnore: + return false + case grammar.AnnAllOf: + isAllOf = true + } + } + + return isAllOf +} + +// baseIdentities returns every type identity an embedded base may be indexed under: the pointer is +// unwrapped (`*Base` composes exactly like `Base`), and an alias contributes BOTH its own identity +// and that of the type it names. +// +// Both alias identities are kept because which one the emitted `allOf` member `$ref`s depends on +// RefAliases / TransparentAliases: indexing both means the relation is found either way, and since +// identities are unique per declaration there is no risk of matching an unrelated base. +// +// Returns nothing for an unnamed type — an anonymous struct / interface embed has no declaration to +// index against. +func baseIdentities(t types.Type) []string { + if ptr, isPtr := t.(*types.Pointer); isPtr { + t = ptr.Elem() + } + + var out []string + if alias, isAlias := t.(*types.Alias); isAlias { + out = append(out, typeIdentity(alias.Obj())) + } + if named, isNamed := types.Unalias(t).(*types.Named); isNamed { + if id := typeIdentity(named.Obj()); len(out) == 0 || out[0] != id { + out = append(out, id) + } + } + + return out +} + +// typeIdentity is the index key for a base: ".", the compiler's identity for the +// declared type. +// +// The Go type — not the swagger definition name — is what both ends of the relation can compute +// without knowing the other's annotations: the embed site resolves a type, the base declaration owns +// one, while either may carry a `swagger:model ` override. +// A package-less (universe) type falls back to its bare name. +func typeIdentity(obj *types.TypeName) string { + if obj == nil { + return "" + } + if pkg := obj.Pkg(); pkg != nil { + return pkg.Path() + "." + obj.Name() + } + + return obj.Name() +} diff --git a/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/validations/README.md b/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/validations/README.md index 1cf13c05a5..6cfac9c42d 100644 --- a/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/validations/README.md +++ b/test/tools/vendor/github.com/go-openapi/codescan/internal/builders/validations/README.md @@ -161,7 +161,7 @@ once a concrete consumer asks for them. the `swagger:type` + `swagger:strfmt` combination, where `swagger:type` wins on the type axis and the strfmt format is applied as a **supplementary hint only when it is consistent with that type** (the F3 reconciliation — see -`.claude/plans/quirks-F-series-fix.md`). It is **not** used for the +`.claude/plans/archive/quirks-F-series-fix.md`). It is **not** used for the strfmt-alone path, where strfmt still forces `{type: string, format: X}` (go-swagger#1512). diff --git a/test/tools/vendor/github.com/go-openapi/codescan/internal/parsers/grammar/annotations.go b/test/tools/vendor/github.com/go-openapi/codescan/internal/parsers/grammar/annotations.go index 89b637e5a7..756287a8e6 100644 --- a/test/tools/vendor/github.com/go-openapi/codescan/internal/parsers/grammar/annotations.go +++ b/test/tools/vendor/github.com/go-openapi/codescan/internal/parsers/grammar/annotations.go @@ -15,18 +15,27 @@ type AnnotationKind int const ( AnnUnknown AnnotationKind = iota - AnnModel // swagger:model - AnnResponse // swagger:response - AnnParameters // swagger:parameters - AnnRoute // swagger:route - AnnOperation // swagger:operation - AnnMeta // swagger:meta - AnnStrfmt // swagger:strfmt - AnnAlias // swagger:alias - AnnName // swagger:name - AnnAllOf // swagger:allOf - AnnEnum // swagger:enum - AnnIgnore // swagger:ignore + AnnModel // swagger:model + AnnResponse // swagger:response + AnnParameters // swagger:parameters + AnnRoute // swagger:route + AnnOperation // swagger:operation + AnnMeta // swagger:meta + AnnStrfmt // swagger:strfmt + AnnAlias // swagger:alias + AnnName // swagger:name + AnnAllOf // swagger:allOf + AnnEnum // swagger:enum + AnnIgnore // swagger:ignore + // AnnOmit — swagger:omit [,…]. + // + // An embed-level / type-level escape hatch: the listed Go field names are not promoted out of the + // embedded type, so the author resolves an embed conflict the scanner must not decide for them + // (go-swagger#1992). + // + // The whole remainder is captured as one raw arg token; the schema builder splits the list and + // resolves each name against the embedded type. + AnnOmit AnnDefaultName // swagger:default — value-only classifier annotation AnnType // swagger:type AnnFile // swagger:file @@ -68,6 +77,7 @@ const ( labelAllOf = "allOf" labelEnum = "enum" labelIgnore = "ignore" + labelOmit = "omit" labelDefault = "default" labelType = "type" labelFile = "file" @@ -105,6 +115,8 @@ func (a AnnotationKind) String() string { return labelEnum case AnnIgnore: return labelIgnore + case AnnOmit: + return labelOmit case AnnDefaultName: return labelDefault case AnnType: @@ -155,6 +167,8 @@ func AnnotationKindFromName(name string) AnnotationKind { return AnnEnum case labelIgnore: return AnnIgnore + case labelOmit: + return AnnOmit case labelDefault: return AnnDefaultName case labelType: @@ -208,7 +222,7 @@ func (a AnnotationKind) family() annotationFamily { case AnnMeta: return familyMeta case AnnStrfmt, AnnAlias, AnnAllOf, AnnEnum, - AnnIgnore, AnnDefaultName, AnnType, AnnFile, + AnnIgnore, AnnOmit, AnnDefaultName, AnnType, AnnFile, AnnAdditionalProperties, AnnPatternProperties: return familyClassifier case AnnUnknown: diff --git a/test/tools/vendor/github.com/go-openapi/codescan/internal/parsers/grammar/diagnostic.go b/test/tools/vendor/github.com/go-openapi/codescan/internal/parsers/grammar/diagnostic.go index 55448fdede..586956ffa1 100644 --- a/test/tools/vendor/github.com/go-openapi/codescan/internal/parsers/grammar/diagnostic.go +++ b/test/tools/vendor/github.com/go-openapi/codescan/internal/parsers/grammar/diagnostic.go @@ -89,7 +89,7 @@ const ( // is not valid (e.g. `inline`/`array` as an array element). // // The override is dropped and the subject falls through to its Go type. - // See the F3 reconciliation in .claude/plans/quirks-F-series-fix.md. + // See the F3 reconciliation in .claude/plans/archive/quirks-F-series-fix.md. CodeUnsupportedType Code = "validate.unsupported-type" // CodeDeprecated fires when an accepted-but-deprecated annotation or keyword value is used (the @@ -191,6 +191,41 @@ const ( // See scanner.Options PruneUnusedModels. CodePrunedUnused Code = "scan.pruned-unused" + // CodeDiscoveredSubtype fires when a definition is emitted because it is a subtype of a + // discriminated base that entered the reachable set — a `swagger:model` declaring that base as an + // `allOf` member (go-swagger#1913). + // + // Such a subtype is unreachable top-down (it references the base, nothing references it), so it is + // pulled in by the reverse `swagger:allOf` index rather than by any $ref in the document. + // Informational (Hint); carries the subtype's own source position, so a definition that appears + // without ScanModels can be traced to the family that pulled it in. + CodeDiscoveredSubtype Code = "scan.discovered-subtype" + + // CodeOmitUnresolved fires when a `swagger:omit` target names no field of the embedded type it is + // applied to — a typo, or a field renamed upstream. + // + // `swagger:omit` is the only construct whose output depends on a hand-written name the compiler + // never checks (everything else is derived from types), so an unresolved target is reported rather + // than silently ignored: otherwise a rename upstream would make the omitted field quietly reappear. + // Informational (Hint); located at the annotation. + CodeOmitUnresolved Code = "scan.omit-unresolved" + + // CodeOmitBehindRef fires when a `swagger:omit` target resolves, but the embed carrying it is + // emitted as a `$ref` (the embedded type is a `swagger:model` composed with allOf). + // + // Swagger 2.0 cannot subtract a property from a `$ref`, so the omission is dropped rather than + // silently forking the referenced definition. + // Informational (Hint); located at the annotation. + CodeOmitBehindRef Code = "scan.omit-behind-ref" + + // CodeShadowedEmbedField fires when a struct field re-declared with `json:"-"` carries the same Go + // name as a field promoted from an embed. + // + // encoding/json ignores a `-` field entirely, so it never shadows the promoted one: Go keeps + // marshalling the embedded field. The author most likely meant `swagger:omit`. + // Informational (Hint); located at the re-declared field. + CodeShadowedEmbedField Code = "scan.shadowed-embed-field" + // CodeRenamedDefinition fires when the reduce stage renames a definition to deconflict a // cross-package name collision (e.g. b.Test / c.Test -> BTest / CTest), so a consumer that tracks // source <-> spec links (the genspec TUI) learns the final name a Go type landed under. diff --git a/test/tools/vendor/github.com/go-openapi/codescan/internal/parsers/grammar/disambiguate.go b/test/tools/vendor/github.com/go-openapi/codescan/internal/parsers/grammar/disambiguate.go index 26b7b1ed9a..30b552bbb8 100644 --- a/test/tools/vendor/github.com/go-openapi/codescan/internal/parsers/grammar/disambiguate.go +++ b/test/tools/vendor/github.com/go-openapi/codescan/internal/parsers/grammar/disambiguate.go @@ -184,7 +184,7 @@ func classifyHTTPMethod(s string) (string, bool) { // It is a LEXICAL check only: the grammar no longer owns the closed type vocabulary. // Semantic validity (is it a known keyword / scanned type? is the format compatible?) is resolved // by the builder, which alone knows the scanned definitions and the annotated Go type (the F3 -// reconciliation — see .claude/plans/quirks-F-series-fix.md). +// reconciliation — see .claude/plans/archive/quirks-F-series-fix.md). // // The lexer only rejects structurally malformed tokens (empty, embedded spaces, a bare `[]`, a // leading digit, illegal characters), which the parser flags. diff --git a/test/tools/vendor/github.com/go-openapi/codescan/internal/parsers/grammar/lexer.go b/test/tools/vendor/github.com/go-openapi/codescan/internal/parsers/grammar/lexer.go index 7d2a605d72..b415551344 100644 --- a/test/tools/vendor/github.com/go-openapi/codescan/internal/parsers/grammar/lexer.go +++ b/test/tools/vendor/github.com/go-openapi/codescan/internal/parsers/grammar/lexer.go @@ -348,6 +348,10 @@ func classifyAnnotationArgs(kind AnnotationKind, rest string, linePos token.Posi // type-name). // The builder does the semantic resolution. return []Token{argTypeRef(rest, pos)} + case AnnOmit: + // The arg is a comma-separated list of Go field names / dotted embed paths, which may carry + // spaces after the commas — capture the whole remainder verbatim; the builder splits it. + return []Token{{Kind: TokenRawValue, Pos: pos, Text: strings.TrimSpace(rest)}} case AnnPatternProperties: // The arg is a `"": , …` pair list that may contain spaces/colons/commas inside // quoted regexes — capture the whole remainder verbatim; the builder parses the pairs. diff --git a/test/tools/vendor/github.com/go-openapi/codescan/internal/parsers/grammar/parser.go b/test/tools/vendor/github.com/go-openapi/codescan/internal/parsers/grammar/parser.go index 29f171fb37..1e1449e69a 100644 --- a/test/tools/vendor/github.com/go-openapi/codescan/internal/parsers/grammar/parser.go +++ b/test/tools/vendor/github.com/go-openapi/codescan/internal/parsers/grammar/parser.go @@ -615,7 +615,7 @@ func (s *parseState) parseClassifierBlock(annIdx int, annTok Token, kind Annotat s.emit(Errorf(annTok.Args[0].Pos, CodeInvalidTypeRef, "swagger:type: %q is not a well-formed type reference", annTok.Args[0].Text)) } - case AnnAllOf, AnnIgnore, AnnAlias, AnnFile: + case AnnAllOf, AnnIgnore, AnnAlias, AnnFile, AnnOmit: // Optional / no args. default: // ignored annotation diff --git a/test/tools/vendor/github.com/go-openapi/codescan/internal/parsers/routebody/README.md b/test/tools/vendor/github.com/go-openapi/codescan/internal/parsers/routebody/README.md index 364dd97e3f..251bac10c9 100644 --- a/test/tools/vendor/github.com/go-openapi/codescan/internal/parsers/routebody/README.md +++ b/test/tools/vendor/github.com/go-openapi/codescan/internal/parsers/routebody/README.md @@ -151,7 +151,14 @@ orchestrator level. ## §quirks-open — deferred follow-ups -- **Column tracking.** routebody does not track per-line column +> **Where open quirks live.** This section documents caveats *of this package*. +> The project-wide register of what is actually open — verified, with the stale +> historical registers called out — is `.claude/plans/quirks-open.md`. + +- **Column tracking** (owned by + `.claude/plans/features/column-precision-unicode.md` — an LSP prerequisite, + paired there with the multi-byte column question since both touch the same + `Line.Pos` contract). routebody does not track per-line column information; diagnostics inherit `basePos.Column`. If the LSP integration needs per-token positions on body sub-language diagnostics, the body parser will need to track lex state more diff --git a/test/tools/vendor/github.com/go-openapi/codescan/internal/scanner/README.md b/test/tools/vendor/github.com/go-openapi/codescan/internal/scanner/README.md index 8820a816c7..be5d34fbf1 100644 --- a/test/tools/vendor/github.com/go-openapi/codescan/internal/scanner/README.md +++ b/test/tools/vendor/github.com/go-openapi/codescan/internal/scanner/README.md @@ -20,6 +20,8 @@ parameters, responses) consumed by the builder layer. experimental-API caveat - [§prune](#prune) — `PruneUnusedModels` reachability and why it runs before name reduction +- [§subtypes](#subtypes) — discriminator subtype discovery — the + reverse `swagger:allOf` index - [§model-lookup](#model-lookup) — `GetModel` vs `FindModel` — pure read vs implicit registration - [§classifier](#classifier) — `detectNodes` bitmask semantics and @@ -155,10 +157,93 @@ provenance for a pruned node is dropped so no anchor dangles. The collision renames the reduce stage *does* perform are surfaced as `scan.renamed-definition` Hints (located at the Go type). -**Known limitation.** A discriminator base references its subtypes by -mapping string, not by `$ref`, so a subtype reachable only through a -discriminator could be pruned. codescan does not auto-wire discriminator -subtypes today; revisit if it ever does (forthcoming-features §15). +**Discriminated families are kept whole.** A subtype `$ref`s its base, +never the reverse, so the walk above cannot see the subtypes of a +discriminated base and would prune a polymorphic family down to its base +alone. A reachable definition carrying `discriminator` therefore also +marks its subtypes reachable, via the reverse `swagger:allOf` index +(`spec/subtypes.go`, `subtypeKeysOf`). Note the rule keeps a *reached* +base's family — it does not make bases roots: a discriminated base +nothing references is still pruned, together with its subtypes. See +[§subtypes](#subtypes). + +## §subtypes — discriminator subtype discovery + +Interface-based polymorphism emits a base definition carrying +`discriminator` plus one `allOf: [{$ref base}, {own props}]` definition +per subtype — a struct embedding the base under `swagger:allOf`. + +The reference direction is the problem: **a subtype `$ref`s its base; +nothing `$ref`s a subtype.** So a route that references only the base +reaches the base and stops, and the emitted family is the base alone — +useless to a consumer that has to unmarshal the polymorphic payload +(go-swagger/go-swagger#1913). Before v0.37 the only way to get the +subtypes was `ScanModels`, which emits every annotated model whether it +belongs to the family or not. + +**Reverse index.** `spec/subtypes.go` builds, once per scan, a +`base Go type identity → subtype declarations` map from the model index +(`TypeIndex.Models`, which classification populates whether or not +`ScanModels` is set — that independence is what makes the pull possible). +A subtype relation is an *embedded member carrying `swagger:allOf`* — a +struct's anonymous field **or an interface's anonymous interface**, which +is how a mid-level type in a multi-level hierarchy is written: + +- the pointer is unwrapped (`*Base` composes like `Base`), and an alias + embed is indexed under both its own and the aliased type's identity, so + the relation is found whichever definition the `allOf` member ends up + `$ref`ing under `RefAliases` / `TransparentAliases`; +- `swagger:ignore` on the embed drops it, exactly as in the schema + builder — the index never claims a relation the document lacks; +- a *plain* (unannotated) embed is not a subtype: it inlines the base's + properties, no `allOf` member. `DefaultAllOfForEmbeds` is deliberately + **not** honoured here — it is a rendering knob, and letting it decide + which definitions exist would be a surprising coupling. + +The index is keyed by **Go type identity** (`.`), not +by swagger name: it is the one fact both ends can compute without knowing +the other's `swagger:model` override. Entries are ordered by definition +key so the pull order — and hence the Hint order — does not follow map +iteration. + +**Two hooks, one per hole.** + +1. *Discovery* (`spec.go`, `buildDiscoveredSchema` → + `discriminatedSubtypesOf`): when a definition has just been built and + carries a `discriminator`, its subtypes are appended to `s.discovered`, + so the existing fixpoint loop builds them, discovers *their* + dependencies, and cascades if a subtype is itself a discriminated + base. Each genuinely new pull raises a located + `scan.discovered-subtype` Hint. A no-op under `ScanModels`, where every + model is built up front anyway. +2. *Prune reachability* (`prune.go`): see + [§prune](#prune) — required because under `ScanModels` the family is + built and then lost, not never-built. + +**The gate is the built definition's own `discriminator`** +(`isDiscriminated`), not a source-level re-derivation. It reads +identically for an interface base with a `discriminator: true` member and +a struct base with a `discriminator: true` property, and it is the same +fact the emitted document exposes. A base with no discriminator pulls +nothing: its `allOf` users are ordinary compositions, not a polymorphic +family. + +**Multi-level hierarchies.** A mid-level type — a subtype that is itself a +base — renders as `allOf: [{$ref parent}, {own props, discriminator}]`: +its properties, and therefore its discriminator, sit in its own compound +member, *not* at the top level. So the gate looks for an inline +discriminator anywhere in the definition's own schema. The `$ref` member +is deliberately **not** followed: a leaf must not inherit its base's +discriminator, or every subtype would pull in its own siblings. Because +hook A feeds the discovery fixpoint, the levels cascade — the root pulls +the mid-level, and the mid-level (only just pulled in itself) pulls the +leaves on the next round. + +Fixtures: `fixtures/enhancements/discriminated-subtypes` (`edges/` holds +the embed-shape corner cases, in a family no route references — which +also locks the other half of the gate: an unreached base pulls nothing) +and `fixtures/enhancements/discriminated-subtypes-nested` (two-level +hierarchy: `Shape` → `Polygon` → `Square`/`Triangle`). ## §model-lookup — `GetModel` vs `FindModel` @@ -297,6 +382,10 @@ prose** — author-written overrides (harvested separately) are never filtered. ## §quirks-open — deferred follow-ups +> **Where open quirks live.** This section documents caveats *of this package*. +> The project-wide register of what is actually open — verified, with the stale +> historical registers called out — is `.claude/plans/quirks-open.md`. + - **`FindModel` deprecation.** The deprecated alias is still on the `ScanCtx` surface for in-tree callers. Once every builder has been audited and migrated to the `GetModel` + `AddDiscoveredModel` pair, diff --git a/test/tools/vendor/github.com/go-openapi/codescan/internal/scanner/index.go b/test/tools/vendor/github.com/go-openapi/codescan/internal/scanner/index.go index 8f089dbe33..de4d7d3be8 100644 --- a/test/tools/vendor/github.com/go-openapi/codescan/internal/scanner/index.go +++ b/test/tools/vendor/github.com/go-openapi/codescan/internal/scanner/index.go @@ -521,7 +521,7 @@ func (a *TypeIndex) detectNodes(file *ast.File) (node, error) { } case "strfmt", "name", "discriminated", "file", "enum", "default", "alias", "type", "additionalProperties", "patternProperties", "title", "description": // Proposal for enhancement: perhaps collect these and pass along to avoid lookups later on - case "allOf": + case "allOf", "omit": case "ignore": default: return 0, fmt.Errorf("classifier: unknown swagger annotation %q: %w", annotation, ErrScanner) diff --git a/test/tools/vendor/github.com/go-openapi/inflect/CONTRIBUTORS.md b/test/tools/vendor/github.com/go-openapi/inflect/CONTRIBUTORS.md index 033a1d94c9..e4ce6b2a56 100644 --- a/test/tools/vendor/github.com/go-openapi/inflect/CONTRIBUTORS.md +++ b/test/tools/vendor/github.com/go-openapi/inflect/CONTRIBUTORS.md @@ -4,11 +4,11 @@ | Total Contributors | Total Contributions | | --- | --- | -| 5 | 47 | +| 5 | 52 | | Username | All Time Contribution Count | All Commits | | --- | --- | --- | -| @fredbi | 26 | | +| @fredbi | 31 | | | @chrisfarms | 15 | | | @hirochachacha | 4 | | | @casualjim | 1 | | diff --git a/test/tools/vendor/github.com/go-openapi/inflect/README.md b/test/tools/vendor/github.com/go-openapi/inflect/README.md index 32da4aa176..b94fb725a9 100644 --- a/test/tools/vendor/github.com/go-openapi/inflect/README.md +++ b/test/tools/vendor/github.com/go-openapi/inflect/README.md @@ -20,12 +20,9 @@ Originally forked from https://bitbucket.org/pkg/inflect under a MIT License. * **2025-12-19** : new community chat on discord * a new discord community channel is available to be notified of changes and support users - * our venerable Slack channel remains open, and will be eventually discontinued on **2026-03-31** You may join the discord community by clicking the invite link on the discord badge (also above). [![Discord Channel][discord-badge]][discord-url] -Or join our Slack channel: [![Slack Channel][slack-logo]![slack-badge]][slack-url] - ## Status API is stable. @@ -73,11 +70,9 @@ on top of which it has been built. ## Other documentation * [All-time contributors](./CONTRIBUTORS.md) -* [Contributing guidelines](.github/CONTRIBUTING.md) - +* [Contributing guidelines][contributing-doc-site] +* [Maintainers documentation][maintainers-doc-site] +* [Code style][style-doc-site] ## Cutting a new release @@ -112,9 +107,6 @@ Maintainers can cut a new release by either: [doc-url]: https://goswagger.io/go-openapi [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/inflect [godoc-url]: http://pkg.go.dev/github.com/go-openapi/inflect -[slack-logo]: https://a.slack-edge.com/e6a93c1/img/icons/favicon-32.png -[slack-badge]: https://img.shields.io/badge/slack-blue?link=https%3A%2F%2Fgoswagger.slack.com%2Farchives%2FC04R30YM -[slack-url]: https://goswagger.slack.com/archives/C04R30YMU [discord-badge]: https://img.shields.io/discord/1446918742398341256?logo=discord&label=discord&color=blue [discord-url]: https://discord.gg/FfnFYaC3k5 @@ -126,3 +118,7 @@ Maintainers can cut a new release by either: [goversion-url]: https://github.com/go-openapi/inflect/blob/master/go.mod [top-badge]: https://img.shields.io/github/languages/top/go-openapi/inflect [commits-badge]: https://img.shields.io/github/commits-since/go-openapi/inflect/latest + +[contributing-doc-site]: https://go-openapi.github.io/doc-site/contributing/contributing/index.html +[maintainers-doc-site]: https://go-openapi.github.io/doc-site/maintainers/index.html +[style-doc-site]: https://go-openapi.github.io/doc-site/contributing/style/index.html diff --git a/test/tools/vendor/github.com/go-openapi/runtime/form.go b/test/tools/vendor/github.com/go-openapi/runtime/form.go index b4b36f1470..213757f1f4 100644 --- a/test/tools/vendor/github.com/go-openapi/runtime/form.go +++ b/test/tools/vendor/github.com/go-openapi/runtime/form.go @@ -71,11 +71,16 @@ type FileBinder func(file multipart.File, header *multipart.FileHeader) error // behaviour) be added without breaking the signature. type BindOption func(*bindConfig) -type bindConfig struct { - maxParseMemory int64 +type multipartFormLimits struct { maxBody int64 maxFiles int maxFilenameLen int +} + +type bindConfig struct { + multipartFormLimits + + maxParseMemory int64 files []formFileSpec } @@ -198,7 +203,9 @@ func BindFormFile(name string, required bool, bind FileBinder) BindOption { // it directly as a filesystem path. func BindForm(r *http.Request, opts ...BindOption) (fatal bool, err error) { cfg := bindConfig{ - maxFilenameLen: DefaultMaxUploadFilenameLength, + multipartFormLimits: multipartFormLimits{ + maxFilenameLen: DefaultMaxUploadFilenameLength, + }, } for _, opt := range opts { opt(&cfg) diff --git a/test/tools/vendor/github.com/go-openapi/runtime/multipart_stream.go b/test/tools/vendor/github.com/go-openapi/runtime/multipart_stream.go new file mode 100644 index 0000000000..76e1bf4607 --- /dev/null +++ b/test/tools/vendor/github.com/go-openapi/runtime/multipart_stream.go @@ -0,0 +1,556 @@ +// SPDX-FileCopyrightText: Copyright 2015-2026 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package runtime + +import ( + "context" + stderrors "errors" + "fmt" + "io" + "mime" + "mime/multipart" + "net/http" + "net/textproto" + "net/url" + + "github.com/go-openapi/errors" +) + +// MultipartFormStreamOption configures [NewMultipartFormStream]. +type MultipartFormStreamOption func(*multipartFormStreamConfig) + +const defaultMultipartFormStreamMaxParts = 1000 + +type multipartFormStreamConfig struct { + multipartFormLimits + + maxParts int +} + +// MultipartFormStreamMaxBody caps the total number of request-body bytes read +// by a [MultipartFormStream]. +// +// A value of 0 applies [DefaultMaxUploadBodySize]. A negative value disables +// the cap when the caller has already limited the request body upstream. +func MultipartFormStreamMaxBody(n int64) MultipartFormStreamOption { + return func(c *multipartFormStreamConfig) { c.maxBody = n } +} + +// MultipartFormStreamMaxFiles rejects a multipart stream after more than n +// file parts have been encountered. A value of 0 means no file-count cap. +func MultipartFormStreamMaxFiles(n int) MultipartFormStreamOption { + return func(c *multipartFormStreamConfig) { c.maxFiles = n } +} + +// MultipartFormStreamMaxParts rejects a multipart stream after more than n +// total parts have been encountered. The default is 1000, matching +// [multipart.Reader.ReadForm]. A value of 0 disables the limit. +func MultipartFormStreamMaxParts(n int) MultipartFormStreamOption { + return func(c *multipartFormStreamConfig) { c.maxParts = n } +} + +// MultipartFormStreamMaxFilenameLen rejects file parts whose filename exceeds +// n bytes. A value of 0 disables the limit. When this option is not supplied, +// [DefaultMaxUploadFilenameLength] is used. +func MultipartFormStreamMaxFilenameLen(n int) MultipartFormStreamOption { + return func(c *multipartFormStreamConfig) { c.maxFilenameLen = n } +} + +// StreamedFile exposes a file part directly from the multipart request body. +// +// Reads block until bytes arrive from the client. StreamedFile is not seekable +// and is not safe for concurrent use. Its form name, filename and MIME headers +// are available before the payload is consumed. The underlying [multipart.Part] +// remains private so callers cannot bypass Close and its error-preserving drain +// semantics; Header exposes the part metadata without exposing that lifecycle. +// +// Closing a StreamedFile drains only the unread remainder of that file part. +// Close may therefore block while the client is still uploading the current +// part. The owning [MultipartFormStream] may then advance to the next part. +type StreamedFile struct { + FieldName string + Filename string + Header textproto.MIMEHeader + part *multipart.Part + closeErr error +} + +// MultipartFileInfo describes a file part discovered by [MultipartFormStream]. +// +// The payload reader is intentionally omitted. File parts remain sequential and +// are consumed through [MultipartFormStream.NextFile]. Header is a snapshot of +// the client-supplied MIME headers and must be treated as untrusted input. +type MultipartFileInfo struct { + FieldName string + Filename string + Header textproto.MIMEHeader +} + +// Read reads file payload bytes directly from the request body. +func (f *StreamedFile) Read(p []byte) (int, error) { + if f == nil || f.part == nil { + return 0, io.ErrClosedPipe + } + + return f.part.Read(p) +} + +// Close discards the unread remainder of this file part. +// +// Close does not close the underlying HTTP request body and does not consume +// subsequent multipart parts. After Close returns successfully, the parent +// MultipartFormStream may advance to the next part. +// +// Any error encountered while discarding the unread payload is returned. +func (f *StreamedFile) Close() error { + if f == nil { + return nil + } + if f.part == nil { + return f.closeErr + } + + part := f.part + f.part = nil + // multipart.Part.Close drains with io.Copy but intentionally discards the + // resulting error, so drain explicitly to preserve error propagation. + _, f.closeErr = io.Copy(io.Discard, part) + + return f.closeErr +} + +// MultipartFormStream reads multipart/form-data sequentially without parsing +// the complete request body before exposing file payloads. +// +// [MultipartFormStream.NextFile] consumes ordinary form fields until it reaches +// the next file part. Fields are appended to request.PostForm and request.Form +// as they are encountered. Consequently, fields after a file become visible +// only after the caller consumes or closes that file and advances the stream. +// +// A stream and its returned files are not safe for concurrent use. There is at +// most one active file part. Calling [MultipartFormStream.NextFile] closes and +// drains an unread active file before advancing, and may therefore block until +// the current part finishes arriving. No background goroutines are started. +// +// The caller owns the stream. Call [MultipartFormStream.Drain] to consume the +// remaining body, collect trailing fields and allow HTTP connection reuse when +// possible. Call [MultipartFormStream.Close] to stop multipart processing +// without explicitly draining the remaining parts. +// +// [MultipartFormStream.Fields] and [MultipartFormStream.Files] expose snapshots +// of the multipart fields and file metadata discovered so far. They never read +// ahead: fields or files after the active file become visible only after the +// stream advances. +type MultipartFormStream struct { + multipartFormStreamConfig + + request *http.Request + reader *multipart.Reader + current *StreamedFile + + fields url.Values + fileInfos []MultipartFileInfo + parts int + closed bool + done bool +} + +// NewMultipartFormStream creates a sequential multipart/form-data stream over +// r.Body. +// +// For POST, PUT and PATCH requests, the constructor accepts only +// multipart/form-data, validates that a non-empty boundary is present, but does +// not consume multipart parts. For other methods, it returns an empty stream +// whose NextFile method reports io.EOF without reading the request body. +// +// The constructor initializes request.Form and request.PostForm in the same way +// as [http.Request.ParseForm], then populates multipart values incrementally as +// [MultipartFormStream.NextFile] advances. +// +// For POST, PUT and PATCH requests, NewMultipartFormStream marks the request +// as handled by MultipartReader. Callers must not subsequently call +// [http.Request.ParseMultipartForm] or [BindForm] for the same request. +// +// File payloads are exposed directly from the request body and are not buffered +// in memory or temporary files. Ordinary form values are read into memory as +// they are encountered. Parts are processed in wire order. +// +// At most one StreamedFile may be active at a time. Calling +// [MultipartFormStream.NextFile] automatically closes and drains an unread +// current file before advancing. No background goroutines are started. +// +// MultipartFormStream is not safe for concurrent use. +// +// File names and MIME headers are supplied by the client and remain untrusted. +func NewMultipartFormStream(r *http.Request, opts ...MultipartFormStreamOption) (*MultipartFormStream, error) { + cfg := multipartFormStreamConfig{ + multipartFormLimits: multipartFormLimits{ + maxFilenameLen: DefaultMaxUploadFilenameLength, + }, + maxParts: defaultMultipartFormStreamMaxParts, + } + for _, opt := range opts { + opt(&cfg) + } + + if r == nil { + return nil, errors.NewParseError("body", "formData", "", stderrors.New("nil request")) + } + + if !supportsMultipartFormStream(r.Method) { + if err := r.ParseForm(); err != nil { + return nil, errors.NewParseError("body", "formData", "", err) + } + + return &MultipartFormStream{ + request: r, + multipartFormStreamConfig: cfg, + fields: make(url.Values), + done: true, + }, nil + } + + if r.Body == nil { + return nil, errors.NewParseError("body", "formData", "", stderrors.New("nil request body")) + } + + contentType := r.Header.Get(HeaderContentType) + mediaType, params, err := mime.ParseMediaType(contentType) + if err != nil { + return nil, errors.NewParseError(HeaderContentType, "header", contentType, err) + } + if mediaType != MultipartFormMime { + return nil, errors.NewParseError(HeaderContentType, "header", mediaType, http.ErrNotMultipart) + } + if params["boundary"] == "" { + return nil, errors.NewParseError(HeaderContentType, "header", contentType, http.ErrMissingBoundary) + } + if err = r.ParseForm(); err != nil { + return nil, errors.NewParseError("body", "formData", "", err) + } + + body := r.Body + if cfg.maxBody >= 0 { + maxBody := cfg.maxBody + if maxBody == 0 { + maxBody = DefaultMaxUploadBodySize + } + body = http.MaxBytesReader(nil, body, maxBody) + } + body = &contextReadCloser{ctx: r.Context(), ReadCloser: body} + r.Body = body + + reader, err := r.MultipartReader() + if err != nil { + return nil, errors.NewParseError("body", "formData", "", err) + } + + return &MultipartFormStream{ + request: r, + reader: reader, + multipartFormStreamConfig: cfg, + fields: make(url.Values), + }, nil +} + +// Fields returns a snapshot of ordinary multipart form fields discovered so far. +// +// URL query values are not included. Repeated multipart fields preserve their +// encounter order. Fields after the active file are not visible until that file +// is consumed or closed and the stream advances. Mutating the returned values +// does not affect the stream or the request. +func (s *MultipartFormStream) Fields() url.Values { + if s == nil { + return nil + } + + return cloneMultipartValues(s.fields) +} + +// Files returns snapshots of file metadata discovered so far in wire order. +// +// The currently active file is included as soon as NextFile returns it. Payload +// readers are not retained in the index. Mutating the returned slice or MIME +// headers does not affect the stream. +func (s *MultipartFormStream) Files() []MultipartFileInfo { + if s == nil { + return nil + } + + files := make([]MultipartFileInfo, len(s.fileInfos)) + for i, file := range s.fileInfos { + files[i] = MultipartFileInfo{ + FieldName: file.FieldName, + Filename: file.Filename, + Header: cloneMultipartMIMEHeader(file.Header), + } + } + + return files +} + +// NextFile advances through the multipart body and returns the next file part. +// Ordinary form fields encountered before that file are added to request.Form +// and request.PostForm. +// +// If the previously returned file is still open, NextFile closes and drains it +// before advancing. This may block while the client is still uploading that +// part. Any drain error is returned and the stream is aborted. +// +// NextFile returns io.EOF when no file parts remain. At that point all trailing +// ordinary fields have been collected. +func (s *MultipartFormStream) NextFile() (*StreamedFile, error) { + if err := s.prepareNextFile(); err != nil { + return nil, err + } + + return s.readNextFile() +} + +// Drain consumes the rest of the multipart body. +// +// Unread file payloads are discarded. Non-file form fields encountered while +// draining are collected in the request form values. +// +// Drain closes the underlying request body after reaching EOF. Subsequent calls +// to NextFile return io.EOF. Drain returns any multipart parsing, payload drain +// or request-body close error. +func (s *MultipartFormStream) Drain() error { + if s == nil || s.closed { + return nil + } + + for { + file, err := s.NextFile() + if stderrors.Is(err, io.EOF) { + return s.Close() + } + if err != nil { + return stderrors.Join(err, s.Close()) + } + if err = file.Close(); err != nil { + return stderrors.Join(err, s.Close()) + } + } +} + +// Close stops multipart processing and closes the underlying HTTP request body +// without explicitly draining the remaining multipart parts. +// +// The concrete request body may perform its own work during Close. In +// particular, a net/http server request body may discard a limited amount of +// unread data to allow connection reuse, so Close is not guaranteed to return +// immediately. Call Drain when trailing form fields must be collected. +func (s *MultipartFormStream) Close() error { + if s == nil || s.closed { + return nil + } + + s.closed = true + if s.current != nil { + s.current.part = nil + s.current = nil + } + + if s.request == nil || s.request.Body == nil { + return nil + } + + return s.request.Body.Close() +} + +func supportsMultipartFormStream(method string) bool { + switch method { + case http.MethodPost, http.MethodPut, http.MethodPatch: + return true + default: + return false + } +} + +func (s *MultipartFormStream) abort(err error) error { + return stderrors.Join(err, s.Close()) +} + +func (s *MultipartFormStream) closeCurrent() error { + if s.current == nil { + return nil + } + + err := s.current.Close() + s.current = nil + + return err +} + +func (s *MultipartFormStream) bindValue(part *multipart.Part, name string) error { + value, err := io.ReadAll(part) + if err != nil { + return err + } + + s.fields.Add(name, string(value)) + s.request.PostForm.Add(name, string(value)) + // Match net/http.ParseMultipartForm: query values already present in Form + // keep precedence over multipart body values, which are appended. + s.request.Form.Add(name, string(value)) + + return nil +} + +func discardPart(part *multipart.Part) error { + _, err := io.Copy(io.Discard, part) + + return err +} + +func cloneMultipartValues(values url.Values) url.Values { + cloned := make(url.Values, len(values)) + for name, entries := range values { + cloned[name] = append([]string(nil), entries...) + } + + return cloned +} + +func cloneMultipartMIMEHeader(header textproto.MIMEHeader) textproto.MIMEHeader { + cloned := make(textproto.MIMEHeader, len(header)) + for name, entries := range header { + cloned[name] = append([]string(nil), entries...) + } + + return cloned +} + +type contextReadCloser struct { + io.ReadCloser + + ctx context.Context //nolint:containedctx // Read has no context parameter, so the wrapper must retain it +} + +func (r *contextReadCloser) Read(p []byte) (int, error) { + if err := r.ctx.Err(); err != nil { + return 0, err + } + + return r.ReadCloser.Read(p) +} + +func (s *MultipartFormStream) prepareNextFile() error { + if s == nil { + return io.ErrClosedPipe + } + if s.done { + return io.EOF + } + if s.closed { + return io.ErrClosedPipe + } + if err := s.closeCurrent(); err != nil { + return s.abort(err) + } + + return nil +} + +func (s *MultipartFormStream) readNextFile() (*StreamedFile, error) { + for { + part, err := s.reader.NextPart() + if err != nil { + return nil, s.handleNextPartError(err) + } + + s.parts++ + if s.maxParts > 0 && s.parts > s.maxParts { + err := errors.NewParseError( + "body", + "formData", + "", + fmt.Errorf( + "multipart form contains %d parts, exceeds limit %d", + s.parts, + s.maxParts, + ), + ) + + return nil, s.abort(err) + } + + fieldName := part.FormName() + if fieldName == "" { + if err := discardPart(part); err != nil { + return nil, s.abort(err) + } + + continue + } + + filename := part.FileName() + if filename == "" { + if err := s.bindValue(part, fieldName); err != nil { + return nil, s.abort(err) + } + + continue + } + + return s.openFile(part, fieldName, filename) + } +} + +func (s *MultipartFormStream) handleNextPartError(err error) error { + if stderrors.Is(err, io.EOF) { + s.done = true + + return io.EOF + } + + return s.abort(err) +} + +func (s *MultipartFormStream) openFile( + part *multipart.Part, + fieldName string, + filename string, +) (*StreamedFile, error) { + fileCount := len(s.fileInfos) + 1 + if s.maxFiles > 0 && fileCount > s.maxFiles { + err := errors.NewParseError( + "body", + "formData", + "", + fmt.Errorf( + "multipart form contains %d file parts, exceeds limit %d", + fileCount, + s.maxFiles, + ), + ) + + return nil, s.abort(err) + } + + if err := ValidateFilenameLength( + fieldName, + "formData", + filename, + s.maxFilenameLen, + ); err != nil { + return nil, s.abort(err) + } + + file := &StreamedFile{ + FieldName: fieldName, + Filename: filename, + Header: part.Header, + part: part, + } + s.fileInfos = append(s.fileInfos, MultipartFileInfo{ + FieldName: fieldName, + Filename: filename, + Header: cloneMultipartMIMEHeader(part.Header), + }) + s.current = file + + return file, nil +} diff --git a/test/tools/vendor/github.com/go-openapi/swag/conv/format.go b/test/tools/vendor/github.com/go-openapi/swag/conv/format.go index 5b87b8e146..e14e5bfcfb 100644 --- a/test/tools/vendor/github.com/go-openapi/swag/conv/format.go +++ b/test/tools/vendor/github.com/go-openapi/swag/conv/format.go @@ -7,14 +7,16 @@ import ( "strconv" ) +const baseDecimal = 10 + // FormatInteger turns an integer type into a string. func FormatInteger[T Signed](value T) string { - return strconv.FormatInt(int64(value), 10) + return strconv.FormatInt(int64(value), baseDecimal) } // FormatUinteger turns an unsigned integer type into a string. func FormatUinteger[T Unsigned](value T) string { - return strconv.FormatUint(uint64(value), 10) + return strconv.FormatUint(uint64(value), baseDecimal) } // FormatFloat turns a floating point numerical value into a string. @@ -26,3 +28,23 @@ func FormatFloat[T Float](value T) string { func FormatBool(value bool) string { return strconv.FormatBool(value) } + +// AppendInteger appends the decimal representation of an integer to a slice of bytes. +func AppendInteger[T Signed](dst []byte, value T) []byte { + return strconv.AppendInt(dst, int64(value), baseDecimal) +} + +// AppendUinteger appends the decimal representation of an unsigned integer to a slice of bytes. +func AppendUinteger[T Unsigned](dst []byte, value T) []byte { + return strconv.AppendUint(dst, uint64(value), baseDecimal) +} + +// AppendFloat appends the decimal representation of a floating point number to a slice of bytes. +func AppendFloat[T Float](dst []byte, value T) []byte { + return strconv.AppendFloat(dst, float64(value), 'g', -1, bitsize(value)) +} + +// AppendBool appends the text representation of a boolean to a slice of bytes. +func AppendBool(dst []byte, value bool) []byte { + return strconv.AppendBool(dst, value) +} diff --git a/test/tools/vendor/github.com/go-swagger/go-swagger/generator/operation.go b/test/tools/vendor/github.com/go-swagger/go-swagger/generator/operation.go index fdb7d64af4..67f5b7a5c5 100644 --- a/test/tools/vendor/github.com/go-swagger/go-swagger/generator/operation.go +++ b/test/tools/vendor/github.com/go-swagger/go-swagger/generator/operation.go @@ -260,6 +260,11 @@ func (b *codeGenOpBuilder) MakeOperation() (GenOperation, error) { var hasQueryParams, hasPathParams, hasHeaderParams, hasFormParams, hasFileParams, hasFormValueParams, hasBodyParams bool paramsForOperation := b.Analyzed.ParamsFor(b.Method, b.Path) + hasStreamingForm, err := streamingFormEnabled(paramsForOperation, b.Method, b.Path) + if err != nil { + return GenOperation{}, err + } + idMapping, timeoutName, ctxName, err := b.paramMappings(paramsForOperation) if err != nil { return GenOperation{}, err @@ -303,6 +308,12 @@ func (b *codeGenOpBuilder) MakeOperation() (GenOperation, error) { sort.Sort(hp) sort.Sort(fp) + serverParams := serverParameters(params, hasStreamingForm) + multipartFormName := "" + if hasStreamingForm { + multipartFormName = deconflictMultipartFormName(serverParams) + } + var srs responses if operation.Responses != nil { srs = sortedResponses(operation.Responses.StatusCodeResponses) @@ -415,6 +426,7 @@ func (b *codeGenOpBuilder) MakeOperation() (GenOperation, error) { DefaultImports: b.DefaultImports, Imports: b.Imports, Params: params, + ServerParams: serverParams, Summary: trimBOM(operation.Summary), QueryParams: qp, PathParams: pp, @@ -427,7 +439,9 @@ func (b *codeGenOpBuilder) MakeOperation() (GenOperation, error) { HasFormValueParams: hasFormValueParams, HasFileParams: hasFileParams, HasBodyParams: hasBodyParams, + HasStreamingForm: hasStreamingForm, HasStreamingResponse: hasStreamingResponse, + MultipartFormName: multipartFormName, Authorized: b.Authed, Security: b.makeSecurityRequirements(receiver), // resolved security requirements, for codegen SecurityDefinitions: b.makeSecuritySchemes(receiver), @@ -874,6 +888,57 @@ func (b *codeGenOpBuilder) MakeBodyParameterItemsAndMaps(res *GenParameter, it * return items } +func streamingFormEnabled(params map[string]spec.Parameter, method, path string) (bool, error) { + enabled := false + for _, param := range params { + raw, found := param.Extensions[xGoServerStreaming] + if !found { + continue + } + + streaming, ok := raw.(bool) + if !ok { + return false, fmt.Errorf(`%s %s, parameter %q: %q must be a boolean, not a %T`, + method, path, param.Name, xGoServerStreaming, raw) + } + if !streaming { + continue + } + if param.In != formData || param.Type != file { + return false, fmt.Errorf(`%s %s, parameter %q: %q may only be enabled on a formData file parameter`, + method, path, param.Name, xGoServerStreaming) + } + enabled = true + } + + return enabled, nil +} + +func serverParameters(params GenParameters, streamingForm bool) GenParameters { + if !streamingForm { + return params + } + + serverParams := make(GenParameters, 0, len(params)) + for _, param := range params { + if param.IsFormParam() { + continue + } + serverParams = append(serverParams, param) + } + + return serverParams +} + +func deconflictMultipartFormName(params GenParameters) string { + seenIDs := make(map[string]any, len(params)) + for _, param := range params { + seenIDs[strings.ToLower(param.ID)] = struct{}{} + } + + return rename(multipartFormNamePreferences)(seenIDs, multipartFormNamePreferences[0], 0) +} + // paramMappings yields a map of safe parameter names for an operation. func (b *codeGenOpBuilder) paramMappings(params map[string]spec.Parameter) (map[string]map[string]string, string, string, error) { idMapping := map[string]map[string]string{ @@ -964,6 +1029,15 @@ var ( "opContext", "operContext", } + + multipartFormNamePreferences = []string{ + "MultipartForm", + "RequestMultipartForm", + "HTTPMultipartForm", + "SwaggerMultipartForm", + "OperationMultipartForm", + "OpMultipartForm", + } ) // rename the variable in use by client template to avoid conflicting diff --git a/test/tools/vendor/github.com/go-swagger/go-swagger/generator/structs.go b/test/tools/vendor/github.com/go-swagger/go-swagger/generator/structs.go index b2ed62ad29..72b45df069 100644 --- a/test/tools/vendor/github.com/go-swagger/go-swagger/generator/structs.go +++ b/test/tools/vendor/github.com/go-swagger/go-swagger/generator/structs.go @@ -639,6 +639,7 @@ type GenOperation struct { DefaultResponse *GenResponse Params GenParameters + ServerParams GenParameters QueryParams GenParameters PathParams GenParameters HeaderParams GenParameters @@ -650,7 +651,9 @@ type GenOperation struct { HasFormValueParams bool HasFileParams bool HasBodyParams bool + HasStreamingForm bool HasStreamingResponse bool + MultipartFormName string Schemes []string ExtraSchemes []string diff --git a/test/tools/vendor/github.com/go-swagger/go-swagger/generator/templates/server/configureapi.gotmpl b/test/tools/vendor/github.com/go-swagger/go-swagger/generator/templates/server/configureapi.gotmpl index 17a7ad76ff..078b11b723 100644 --- a/test/tools/vendor/github.com/go-swagger/go-swagger/generator/templates/server/configureapi.gotmpl +++ b/test/tools/vendor/github.com/go-swagger/go-swagger/generator/templates/server/configureapi.gotmpl @@ -128,7 +128,10 @@ func configureAPI(api *{{.APIPackageAlias}}.{{ pascalize .Name }}API) http.Handl {{- $package := .Package }} {{- $apipackagealias := .APIPackageAlias }} {{- range .Operations }} - {{- if .HasFormParams }} + {{- if .HasStreamingForm }} + // You may change here the maximum body size for this streaming multipart form. Below is the default (32 MB). + // {{ if ne .Package $package }}{{ .PackageAlias }}{{ else }}{{ $apipackagealias }}{{ end }}.{{ pascalize .Name }}MaxBodySize = 32 << 20 + {{- else if .HasFormParams }} // You may change here the memory limit for this multipart form parser. Below is the default (32 MB). // {{ if ne .Package $package }}{{ .PackageAlias }}{{ else }}{{ $apipackagealias }}{{ end }}.{{ pascalize .Name }}MaxParseMemory = 32 << 20 {{- end }} diff --git a/test/tools/vendor/github.com/go-swagger/go-swagger/generator/templates/server/parameter.gotmpl b/test/tools/vendor/github.com/go-swagger/go-swagger/generator/templates/server/parameter.gotmpl index 8dbb28dc86..34e269b8f4 100644 --- a/test/tools/vendor/github.com/go-swagger/go-swagger/generator/templates/server/parameter.gotmpl +++ b/test/tools/vendor/github.com/go-swagger/go-swagger/generator/templates/server/parameter.gotmpl @@ -319,6 +319,7 @@ import ( ) {{- if .HasFormParams }} +{{- if not .HasStreamingForm }} // {{ pascalize .Name }}MaxParseMemory sets the maximum size in bytes for // the multipart form parser for this operation. @@ -326,6 +327,7 @@ import ( // The default value is 32 MB. // The multipart parser stores up to this + 10MB. var {{ pascalize .Name }}MaxParseMemory int64 = 32 << 20 +{{- end }} // {{ pascalize .Name }}MaxBodySize caps the size of the form body. // @@ -334,17 +336,17 @@ var {{ pascalize .Name }}MaxBodySize int64 = 32 << 20 {{- end }} // New{{ pascalize .Name }}Params creates a new {{ pascalize .Name }}Params object -{{- if .Params.HasSomeDefaults }} +{{- if .ServerParams.HasSomeDefaults }} // with the default values initialized. {{- else }} // // There are no default values defined in the spec. {{- end }} func New{{ pascalize .Name }}Params() {{ pascalize .Name }}Params { -{{ if .Params.HasSomeDefaults }} +{{ if .ServerParams.HasSomeDefaults }} var ( // initialize parameters with default values - {{ range .Params }} + {{ range .ServerParams }} {{ if .HasDefault -}} {{ if not .IsFileParam }}{{ varname .ID}}Default = {{- if and .IsPrimitive .IsCustomFormatter (not (stringContains .Zero "(\"" )) }}{{ .Zero }}{{/* strfmt type initializer requires UnmarshalText(), e.g. Date, Datetime, Duration */}} @@ -363,7 +365,7 @@ func New{{ pascalize .Name }}Params() {{ pascalize .Name }}Params { {{- end }} ) -{{ range .Params }}{{ if .HasDefault -}}{{- /* carry out UnmarshalText initialization strategy */ -}} +{{ range .ServerParams }}{{ if .HasDefault -}}{{- /* carry out UnmarshalText initialization strategy */ -}} {{ if and .IsPrimitive .IsCustomFormatter (not (stringContains .Zero "(\"")) }}{{ varname .ID}}Default.UnmarshalText([]byte({{ printf "%q" .Default }})) {{ else if .IsArray -}} {{ if or ( and .Child.IsPrimitive .Child.IsCustomFormatter ) .Child.IsArray -}} @@ -377,7 +379,7 @@ func New{{ pascalize .Name }}Params() {{ pascalize .Name }}Params { {{ end -}} {{- end }} {{ end }} - return {{ pascalize .Name }}Params{ {{ range .Params }}{{ if .HasDefault }} + return {{ pascalize .Name }}Params{ {{ range .ServerParams }}{{ if .HasDefault }} {{ .ID}}: {{ if and (not .IsArray) (not .HasDiscriminator) (not .IsInterface) (not .IsStream) .IsNullable }}&{{ end }}{{ varname .ID }}Default, {{ end }}{{ end }} } } @@ -389,7 +391,14 @@ func New{{ pascalize .Name }}Params() {{ pascalize .Name }}Params { type {{ pascalize .Name }}Params struct { // HTTP Request Object HTTPRequest *http.Request `json:"-"` - {{- range .Params }} + {{- if .HasStreamingForm }} + + // {{ .MultipartFormName }} is the lazily consumed multipart form stream. + // + // The handler owns traversal, validation and closing or draining this stream. + {{ .MultipartFormName }} *runtime.MultipartFormStream `json:"-"` + {{- end }} + {{- range .ServerParams }} {{- if .Description }} {{ lineComment .Description }} @@ -422,11 +431,11 @@ func ({{ .ReceiverName }} *{{ pascalize .Name }}Params) BindRequest(r *http.Requ {{- if .HasQueryParams }} qs := runtime.Values(r.URL.Query()) {{- end }} -{{- if .HasFormParams }} +{{- if and .HasFormParams (not .HasStreamingForm) }} isBlocking, err := runtime.BindForm(r, runtime.BindFormMaxParseMemory({{ pascalize .Name }}MaxParseMemory), runtime.BindFormMaxBody({{ pascalize .Name }}MaxBodySize), - {{- range .Params }} + {{- range .ServerParams }} {{- if .IsFileParam }} runtime.BindFormFile({{ .Path }}, {{ or .Required (not .IsNullable) }}, {{ .ReceiverName }}.bind{{ .ID }}), {{- end }} @@ -445,7 +454,7 @@ func ({{ .ReceiverName }} *{{ pascalize .Name }}Params) BindRequest(r *http.Requ {{- end }} {{- end }} -{{ range .Params }} +{{ range .ServerParams }} {{- if not .IsArray }} {{- if .IsQueryParam }} @@ -542,11 +551,22 @@ func ({{ .ReceiverName }} *{{ pascalize .Name }}Params) BindRequest(r *http.Requ if len(res) > 0 { return errors.CompositeValidationError(res...) } +{{- if .HasStreamingForm }} + + multipartForm, err := runtime.NewMultipartFormStream( + r, + runtime.MultipartFormStreamMaxBody({{ pascalize .Name }}MaxBodySize), + ) + if err != nil { + return err + } + {{ .ReceiverName }}.{{ .MultipartFormName }} = multipartForm +{{- end }} return nil } {{- $className := (pascalize .Name) }} -{{ range .Params }} +{{ range .ServerParams }} {{- if .IsFileParam }} // bind{{ .ID }} validates file parameter File1 and assigns it as a *runtime.File on success. // diff --git a/test/tools/vendor/github.com/go-swagger/go-swagger/generator/types.go b/test/tools/vendor/github.com/go-swagger/go-swagger/generator/types.go index 492b72bef6..ab22e01ecd 100644 --- a/test/tools/vendor/github.com/go-swagger/go-swagger/generator/types.go +++ b/test/tools/vendor/github.com/go-swagger/go-swagger/generator/types.go @@ -36,17 +36,18 @@ const ( // Extensions supported by go-swagger. const ( - xClass = "x-class" // class name used by discriminator - xGoCustomTag = "x-go-custom-tag" // additional tag for serializers on struct fields - xGoName = "x-go-name" // name of the generated go variable - xGoType = "x-go-type" // reuse existing type (do not generate) - xIsNullable = "x-isnullable" - xNullable = "x-nullable" // turns the schema into a pointer - xOmitEmpty = "x-omitempty" - xSchemes = "x-schemes" // additional schemes supported for operations (server generation) - xOrder = "x-order" // sort order for properties (or any schema) - xGoJSONString = "x-go-json-string" - xGoEnumCI = "x-go-enum-ci" // make string enumeration case-insensitive + xClass = "x-class" // class name used by discriminator + xGoCustomTag = "x-go-custom-tag" // additional tag for serializers on struct fields + xGoName = "x-go-name" // name of the generated go variable + xGoType = "x-go-type" // reuse existing type (do not generate) + xIsNullable = "x-isnullable" + xNullable = "x-nullable" // turns the schema into a pointer + xOmitEmpty = "x-omitempty" + xSchemes = "x-schemes" // additional schemes supported for operations (server generation) + xOrder = "x-order" // sort order for properties (or any schema) + xGoJSONString = "x-go-json-string" + xGoEnumCI = "x-go-enum-ci" // make string enumeration case-insensitive + xGoServerStreaming = "x-go-server-streaming" // stream multipart form payloads directly to server handlers xGoOperationTag = "x-go-operation-tag" // additional tag to override generation in operation groups ) diff --git a/test/tools/vendor/github.com/mattn/go-isatty/isatty_tcgets.go b/test/tools/vendor/github.com/mattn/go-isatty/isatty_tiocgwinsz.go similarity index 62% rename from test/tools/vendor/github.com/mattn/go-isatty/isatty_tcgets.go rename to test/tools/vendor/github.com/mattn/go-isatty/isatty_tiocgwinsz.go index 0337d8cf6d..6aecf8704c 100644 --- a/test/tools/vendor/github.com/mattn/go-isatty/isatty_tcgets.go +++ b/test/tools/vendor/github.com/mattn/go-isatty/isatty_tiocgwinsz.go @@ -8,8 +8,12 @@ package isatty import "golang.org/x/sys/unix" // IsTerminal return true if the file descriptor is terminal. +// TIOCGWINSZ is used instead of TCGETS because TCGETS shares its ioctl +// number with SNDCTL_TMR_TIMEBASE of the OSS sound API, so it may succeed +// (and even change the device mode) on non-tty devices. musl's isatty does +// the same. func IsTerminal(fd uintptr) bool { - _, err := unix.IoctlGetTermios(int(fd), unix.TCGETS) + _, err := unix.IoctlGetWinsize(int(fd), unix.TIOCGWINSZ) return err == nil } diff --git a/test/tools/vendor/github.com/oklog/ulid/v2/ulid.go b/test/tools/vendor/github.com/oklog/ulid/v2/ulid.go index 77e9ddd634..e07e1a6818 100644 --- a/test/tools/vendor/github.com/oklog/ulid/v2/ulid.go +++ b/test/tools/vendor/github.com/oklog/ulid/v2/ulid.go @@ -505,7 +505,16 @@ func (id *ULID) Scan(src interface{}) error { case string: return id.UnmarshalText([]byte(x)) case []byte: - return id.UnmarshalBinary(x) + // Drivers often return text/varchar columns as []byte. Accept both + // the 16-byte binary form and the 26-character text encoding. + switch len(x) { + case len(*id): + return id.UnmarshalBinary(x) + case EncodedSize: + return id.UnmarshalText(x) + default: + return ErrDataSize + } } return ErrScanValue diff --git a/test/tools/vendor/go.yaml.in/yaml/v3/parserc.go b/test/tools/vendor/go.yaml.in/yaml/v3/parserc.go index 25fe823637..f35829db4d 100644 --- a/test/tools/vendor/go.yaml.in/yaml/v3/parserc.go +++ b/test/tools/vendor/go.yaml.in/yaml/v3/parserc.go @@ -226,9 +226,9 @@ func yaml_parser_state_machine(parser *yaml_parser_t, event *yaml_event_t) bool } // Parse the production: -// stream ::= STREAM-START implicit_document? explicit_document* STREAM-END // -// ************ +// stream ::= STREAM-START implicit_document? explicit_document* STREAM-END +// ************ func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -249,13 +249,11 @@ func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t) } // Parse the productions: -// implicit_document ::= block_node DOCUMENT-END* // -// * -// -// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* -// -// ************************* +// implicit_document ::= block_node DOCUMENT-END* +// * +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// ************************* func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t, implicit bool) bool { token := peek_token(parser) @@ -359,9 +357,9 @@ func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t } // Parse the productions: -// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* // -// *********** +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// *********** func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -382,11 +380,10 @@ func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event } // Parse the productions: -// implicit_document ::= block_node DOCUMENT-END* // -// ************* -// -// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* +// implicit_document ::= block_node DOCUMENT-END* +// ************* +// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* func yaml_parser_parse_document_end(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -432,42 +429,32 @@ func yaml_parser_set_event_comments(parser *yaml_parser_t, event *yaml_event_t) } // Parse the productions: -// block_node_or_indentless_sequence ::= // -// ALIAS -// ***** -// | properties (block_content | indentless_block_sequence)? -// ********** * -// | block_content | indentless_block_sequence -// * -// -// block_node ::= ALIAS -// -// ***** -// | properties block_content? -// ********** * -// | block_content -// * -// -// flow_node ::= ALIAS -// -// ***** -// | properties flow_content? -// ********** * -// | flow_content -// * -// -// properties ::= TAG ANCHOR? | ANCHOR TAG? -// -// ************************* -// -// block_content ::= block_collection | flow_collection | SCALAR -// -// ****** -// -// flow_content ::= flow_collection | SCALAR -// -// ****** +// block_node_or_indentless_sequence ::= +// ALIAS +// ***** +// | properties (block_content | indentless_block_sequence)? +// ********** * +// | block_content | indentless_block_sequence +// * +// block_node ::= ALIAS +// ***** +// | properties block_content? +// ********** * +// | block_content +// * +// flow_node ::= ALIAS +// ***** +// | properties flow_content? +// ********** * +// | flow_content +// * +// properties ::= TAG ANCHOR? | ANCHOR TAG? +// ************************* +// block_content ::= block_collection | flow_collection | SCALAR +// ****** +// flow_content ::= flow_collection | SCALAR +// ****** func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, indentless_sequence bool) bool { //defer trace("yaml_parser_parse_node", "block:", block, "indentless_sequence:", indentless_sequence)() @@ -697,9 +684,9 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i } // Parse the productions: -// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END // -// ******************** *********** * ********* +// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END +// ******************** *********** * ********* func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) @@ -755,9 +742,9 @@ func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_e } // Parse the productions: -// indentless_sequence ::= (BLOCK-ENTRY block_node?)+ // -// *********** * +// indentless_sequence ::= (BLOCK-ENTRY block_node?)+ +// *********** * func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -821,15 +808,15 @@ func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) { } // Parse the productions: -// block_mapping ::= BLOCK-MAPPING_START // -// ******************* -// ((KEY block_node_or_indentless_sequence?)? -// *** * -// (VALUE block_node_or_indentless_sequence?)?)* +// block_mapping ::= BLOCK-MAPPING_START +// ******************* +// ((KEY block_node_or_indentless_sequence?)? +// *** * +// (VALUE block_node_or_indentless_sequence?)?)* // -// BLOCK-END -// ********* +// BLOCK-END +// ********* func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) @@ -896,13 +883,14 @@ func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_even } // Parse the productions: -// block_mapping ::= BLOCK-MAPPING_START // -// ((KEY block_node_or_indentless_sequence?)? +// block_mapping ::= BLOCK-MAPPING_START // -// (VALUE block_node_or_indentless_sequence?)?)* -// ***** * -// BLOCK-END +// ((KEY block_node_or_indentless_sequence?)? +// +// (VALUE block_node_or_indentless_sequence?)?)* +// ***** * +// BLOCK-END func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -929,19 +917,17 @@ func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_ev } // Parse the productions: -// flow_sequence ::= FLOW-SEQUENCE-START // -// ******************* -// (flow_sequence_entry FLOW-ENTRY)* -// * ********** -// flow_sequence_entry? -// * -// FLOW-SEQUENCE-END -// ***************** -// -// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? -// -// * +// flow_sequence ::= FLOW-SEQUENCE-START +// ******************* +// (flow_sequence_entry FLOW-ENTRY)* +// * ********** +// flow_sequence_entry? +// * +// FLOW-SEQUENCE-END +// ***************** +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) @@ -1005,9 +991,9 @@ func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_ev } // Parse the productions: -// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // -// *** * +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// *** * func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -1026,9 +1012,9 @@ func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, ev } // Parse the productions: -// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // -// ***** * +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// ***** * func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -1050,9 +1036,9 @@ func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t, } // Parse the productions: -// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // -// * +// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { @@ -1068,18 +1054,17 @@ func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, ev } // Parse the productions: -// flow_mapping ::= FLOW-MAPPING-START // -// ****************** -// (flow_mapping_entry FLOW-ENTRY)* -// * ********** -// flow_mapping_entry? -// ****************** -// FLOW-MAPPING-END -// **************** -// -// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? -// - *** * +// flow_mapping ::= FLOW-MAPPING-START +// ****************** +// (flow_mapping_entry FLOW-ENTRY)* +// * ********** +// flow_mapping_entry? +// ****************** +// FLOW-MAPPING-END +// **************** +// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * *** * func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) @@ -1144,8 +1129,9 @@ func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event } // Parse the productions: -// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? -// - ***** * +// +// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? +// * ***** * func yaml_parser_parse_flow_mapping_value(parser *yaml_parser_t, event *yaml_event_t, empty bool) bool { token := peek_token(parser) if token == nil { diff --git a/test/tools/vendor/go.yaml.in/yaml/v3/yamlh.go b/test/tools/vendor/go.yaml.in/yaml/v3/yamlh.go index f59aa40f64..07c442361c 100644 --- a/test/tools/vendor/go.yaml.in/yaml/v3/yamlh.go +++ b/test/tools/vendor/go.yaml.in/yaml/v3/yamlh.go @@ -433,21 +433,19 @@ type yaml_document_t struct { // The prototype of a read handler. // -// The read handler is called when the parser needs to read more bytes from the -// source. The handler should write not more than size bytes to the buffer. -// The number of written bytes should be set to the size_read variable. +// The read handler is called when the parser needs to read more bytes from the +// source. The handler should write not more than size bytes to the buffer. +// The number of written bytes should be set to the size_read variable. // -// [in,out] data A pointer to an application data specified by +// [in,out] data A pointer to an application data specified by +// yaml_parser_set_input(). +// [out] buffer The buffer to write the data from the source. +// [in] size The size of the buffer. +// [out] size_read The actual number of bytes read from the source. // -// yaml_parser_set_input(). -// -// [out] buffer The buffer to write the data from the source. -// [in] size The size of the buffer. -// [out] size_read The actual number of bytes read from the source. -// -// On success, the handler should return 1. If the handler failed, -// the returned value should be 0. On EOF, the handler should set the -// size_read to 0 and return 1. +// On success, the handler should return 1. If the handler failed, +// the returned value should be 0. On EOF, the handler should set the +// size_read to 0 and return 1. type yaml_read_handler_t func(parser *yaml_parser_t, buffer []byte) (n int, err error) // This structure holds information about a potential simple key. @@ -655,19 +653,17 @@ type yaml_comment_t struct { // The prototype of a write handler. // -// The write handler is called when the emitter needs to flush the accumulated -// characters to the output. The handler should write @a size bytes of the -// @a buffer to the output. +// The write handler is called when the emitter needs to flush the accumulated +// characters to the output. The handler should write @a size bytes of the +// @a buffer to the output. // -// @param[in,out] data A pointer to an application data specified by +// @param[in,out] data A pointer to an application data specified by +// yaml_emitter_set_output(). +// @param[in] buffer The buffer with bytes to be written. +// @param[in] size The size of the buffer. // -// yaml_emitter_set_output(). -// -// @param[in] buffer The buffer with bytes to be written. -// @param[in] size The size of the buffer. -// -// @returns On success, the handler should return @c 1. If the handler failed, -// the returned value should be @c 0. +// @returns On success, the handler should return @c 1. If the handler failed, +// the returned value should be @c 0. type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error type yaml_emitter_state_t int diff --git a/test/tools/vendor/modules.txt b/test/tools/vendor/modules.txt index 30431df40c..b7489c2c42 100644 --- a/test/tools/vendor/modules.txt +++ b/test/tools/vendor/modules.txt @@ -37,7 +37,7 @@ github.com/go-openapi/analysis/internal/flatten/operations github.com/go-openapi/analysis/internal/flatten/replace github.com/go-openapi/analysis/internal/flatten/schutils github.com/go-openapi/analysis/internal/flatten/sortref -# github.com/go-openapi/codescan v0.35.1 +# github.com/go-openapi/codescan v0.36.0 ## explicit; go 1.25.0 github.com/go-openapi/codescan github.com/go-openapi/codescan/internal/builders/common @@ -62,7 +62,7 @@ github.com/go-openapi/codescan/internal/scanner/classify # github.com/go-openapi/errors v0.22.8 ## explicit; go 1.25.0 github.com/go-openapi/errors -# github.com/go-openapi/inflect v0.21.6 +# github.com/go-openapi/inflect v1.0.0 ## explicit; go 1.25.0 github.com/go-openapi/inflect # github.com/go-openapi/jsonpointer v1.0.0 @@ -76,10 +76,10 @@ github.com/go-openapi/jsonreference/internal # github.com/go-openapi/loads v0.25.0 ## explicit; go 1.25.0 github.com/go-openapi/loads -# github.com/go-openapi/runtime v0.32.6 +# github.com/go-openapi/runtime v0.33.0 ## explicit; go 1.25.0 github.com/go-openapi/runtime -# github.com/go-openapi/runtime/server-middleware v0.32.6 +# github.com/go-openapi/runtime/server-middleware v0.33.0 ## explicit; go 1.25.0 github.com/go-openapi/runtime/server-middleware/docui # github.com/go-openapi/spec v0.22.9 @@ -90,43 +90,43 @@ github.com/go-openapi/spec github.com/go-openapi/strfmt github.com/go-openapi/strfmt/internal/bsonlite github.com/go-openapi/strfmt/internal/countries -# github.com/go-openapi/swag/conv v0.27.3 +# github.com/go-openapi/swag/conv v0.28.0 ## explicit; go 1.25.0 github.com/go-openapi/swag/conv -# github.com/go-openapi/swag/fileutils v0.27.3 +# github.com/go-openapi/swag/fileutils v0.28.0 ## explicit; go 1.25.0 github.com/go-openapi/swag/fileutils -# github.com/go-openapi/swag/jsonutils v0.27.3 +# github.com/go-openapi/swag/jsonutils v0.28.0 ## explicit; go 1.25.0 github.com/go-openapi/swag/jsonutils github.com/go-openapi/swag/jsonutils/adapters github.com/go-openapi/swag/jsonutils/adapters/ifaces github.com/go-openapi/swag/jsonutils/adapters/stdlib/json -# github.com/go-openapi/swag/loading v0.27.3 +# github.com/go-openapi/swag/loading v0.28.0 ## explicit; go 1.25.0 github.com/go-openapi/swag/loading -# github.com/go-openapi/swag/mangling v0.27.3 +# github.com/go-openapi/swag/mangling v0.28.0 ## explicit; go 1.25.0 github.com/go-openapi/swag/mangling -# github.com/go-openapi/swag/netutils v0.27.3 +# github.com/go-openapi/swag/netutils v0.28.0 ## explicit; go 1.25.0 github.com/go-openapi/swag/netutils -# github.com/go-openapi/swag/pools v0.27.3 +# github.com/go-openapi/swag/pools v0.28.0 ## explicit; go 1.25.0 github.com/go-openapi/swag/pools -# github.com/go-openapi/swag/stringutils v0.27.3 +# github.com/go-openapi/swag/stringutils v0.28.0 ## explicit; go 1.25.0 github.com/go-openapi/swag/stringutils -# github.com/go-openapi/swag/typeutils v0.27.3 +# github.com/go-openapi/swag/typeutils v0.28.0 ## explicit; go 1.25.0 github.com/go-openapi/swag/typeutils -# github.com/go-openapi/swag/yamlutils v0.27.3 +# github.com/go-openapi/swag/yamlutils v0.28.0 ## explicit; go 1.25.0 github.com/go-openapi/swag/yamlutils # github.com/go-openapi/validate v0.26.1 ## explicit; go 1.25.0 github.com/go-openapi/validate -# github.com/go-swagger/go-swagger v0.35.3 +# github.com/go-swagger/go-swagger v0.36.0 ## explicit; go 1.25.0 github.com/go-swagger/go-swagger/cmd/swagger github.com/go-swagger/go-swagger/cmd/swagger/commands @@ -161,7 +161,7 @@ github.com/kr/text # github.com/mattn/go-colorable v0.1.15 ## explicit; go 1.18 github.com/mattn/go-colorable -# github.com/mattn/go-isatty v0.0.23 +# github.com/mattn/go-isatty v0.0.24 ## explicit; go 1.20 github.com/mattn/go-isatty # github.com/mitchellh/copystructure v1.2.0 @@ -170,7 +170,7 @@ github.com/mitchellh/copystructure # github.com/mitchellh/reflectwalk v1.0.2 ## explicit github.com/mitchellh/reflectwalk -# github.com/oklog/ulid/v2 v2.1.1 +# github.com/oklog/ulid/v2 v2.1.2 ## explicit; go 1.15 github.com/oklog/ulid/v2 # github.com/pelletier/go-toml/v2 v2.4.3 @@ -218,7 +218,7 @@ github.com/subosito/gotenv # github.com/toqueteos/webbrowser v1.2.1 ## explicit; go 1.12 github.com/toqueteos/webbrowser -# go.yaml.in/yaml/v3 v3.0.4 +# go.yaml.in/yaml/v3 v3.0.5 ## explicit; go 1.16 go.yaml.in/yaml/v3 # golang.org/x/crypto v0.54.0