From cbcbc2a4c8a93a503adbb30e399339a5e3fa8350 Mon Sep 17 00:00:00 2001 From: Frederic BIDON Date: Mon, 23 Mar 2026 12:04:54 +0100 Subject: [PATCH 1/2] chore: replaced env var SWAGGER_GENERATE_EXTENSION by option Signed-off-by: Frederic BIDON --- application.go | 1 + parameters.go | 21 +++++++++++---------- responses.go | 17 +++++++++-------- schema.go | 50 +++++++++++++++++++++++++------------------------- schema_test.go | 20 +++++++------------- taggers.go | 8 ++++---- 6 files changed, 57 insertions(+), 60 deletions(-) diff --git a/application.go b/application.go index e055a4a..ffeb8d4 100644 --- a/application.go +++ b/application.go @@ -59,6 +59,7 @@ type Options struct { RefAliases bool // aliases result in $ref, otherwise aliases are expanded TransparentAliases bool // aliases are completely transparent, never creating definitions DescWithRef bool // allow overloaded descriptions together with $ref, otherwise jsonschema draft4 $ref predates everything + SkipExtensions bool // skip generating x-go-* vendor extensions in the spec } type scanCtx struct { diff --git a/parameters.go b/parameters.go index a38dcbb..9250ebc 100644 --- a/parameters.go +++ b/parameters.go @@ -12,7 +12,8 @@ import ( ) type paramTypable struct { - param *spec.Parameter + param *spec.Parameter + skipExt bool } func (pt paramTypable) In() string { return pt.param.In } @@ -28,7 +29,7 @@ func (pt paramTypable) SetRef(ref spec.Ref) { } func (pt paramTypable) Items() swaggerTypable { //nolint:ireturn // polymorphic by design - bdt, schema := bodyTypable(pt.param.In, pt.param.Schema) + bdt, schema := bodyTypable(pt.param.In, pt.param.Schema, pt.skipExt) if bdt != nil { pt.param.Schema = schema return bdt @@ -336,7 +337,7 @@ func (p *parameterBuilder) buildFromFieldMap(ftpe *types.Map, typable swaggerTyp ctx: p.ctx, } - if err := sb.buildFromType(ftpe.Elem(), schemaTypable{schema, typable.Level() + 1}); err != nil { + if err := sb.buildFromType(ftpe.Elem(), schemaTypable{schema, typable.Level() + 1, p.ctx.opts.SkipExtensions}); err != nil { return err } @@ -469,10 +470,10 @@ func (p *parameterBuilder) buildFieldAlias(tpe *types.Alias, typable swaggerTypa return p.buildFromField(fld, rhs, typable, seen) } -func spExtensionsSetter(ps *spec.Parameter) func(*spec.Extensions) { +func spExtensionsSetter(ps *spec.Parameter, skipExt bool) func(*spec.Extensions) { return func(exts *spec.Extensions) { for name, value := range *exts { - addExtension(&ps.VendorExtensible, name, value) + addExtension(&ps.VendorExtensible, name, value, skipExt) } } } @@ -559,9 +560,9 @@ func (p *parameterBuilder) processParamField(fld *types.Var, decl *entityDecl, s ps := seen[name] ps.In = in - var pty swaggerTypable = paramTypable{&ps} + var pty swaggerTypable = paramTypable{&ps, p.ctx.opts.SkipExtensions} if in == bodyTag { - pty = schemaTypable{pty.Schema(), 0} + pty = schemaTypable{pty.Schema(), 0, p.ctx.opts.SkipExtensions} } if in == "formData" && afld.Doc != nil && fileParam(afld.Doc) { @@ -586,9 +587,9 @@ func (p *parameterBuilder) processParamField(fld *types.Var, decl *entityDecl, s } if ps.Ref.String() != "" { - setupRefParamTaggers(sp, &ps) + setupRefParamTaggers(sp, &ps, p.ctx.opts.SkipExtensions) } else { - if err := setupInlineParamTaggers(sp, &ps, name, afld); err != nil { + if err := setupInlineParamTaggers(sp, &ps, name, afld, p.ctx.opts.SkipExtensions); err != nil { return "", err } } @@ -605,7 +606,7 @@ func (p *parameterBuilder) processParamField(fld *types.Var, decl *entityDecl, s } if name != fld.Name() { - addExtension(&ps.VendorExtensible, "x-go-name", fld.Name()) + addExtension(&ps.VendorExtensible, "x-go-name", fld.Name(), p.ctx.opts.SkipExtensions) } seen[name] = ps diff --git a/responses.go b/responses.go index 82b49fe..505acf4 100644 --- a/responses.go +++ b/responses.go @@ -15,6 +15,7 @@ type responseTypable struct { in string header *spec.Header response *spec.Response + skipExt bool } func (ht responseTypable) In() string { return ht.in } @@ -25,7 +26,7 @@ func (ht responseTypable) Typed(tpe, format string) { ht.header.Typed(tpe, format) } -func bodyTypable(in string, schema *spec.Schema) (swaggerTypable, *spec.Schema) { //nolint:ireturn // polymorphic by design +func bodyTypable(in string, schema *spec.Schema, skipExt bool) (swaggerTypable, *spec.Schema) { //nolint:ireturn // polymorphic by design if in == bodyTag { // get the schema for items on the schema property if schema == nil { @@ -38,13 +39,13 @@ func bodyTypable(in string, schema *spec.Schema) (swaggerTypable, *spec.Schema) schema.Items.Schema = new(spec.Schema) } schema.Typed("array", "") - return schemaTypable{schema.Items.Schema, 1}, schema + return schemaTypable{schema.Items.Schema, 1, skipExt}, schema } return nil, nil } func (ht responseTypable) Items() swaggerTypable { //nolint:ireturn // polymorphic by design - bdt, schema := bodyTypable(ht.in, ht.response.Schema) + bdt, schema := bodyTypable(ht.in, ht.response.Schema, ht.skipExt) if bdt != nil { ht.response.Schema = schema return bdt @@ -234,7 +235,7 @@ func (r *responseBuilder) buildFromFieldMap(ftpe *types.Map, typable swaggerTypa ctx: r.ctx, } - if err := sb.buildFromType(ftpe.Elem(), schemaTypable{schema, typable.Level() + 1}); err != nil { + if err := sb.buildFromType(ftpe.Elem(), schemaTypable{schema, typable.Level() + 1, r.ctx.opts.SkipExtensions}); err != nil { return err } @@ -289,7 +290,7 @@ func (r *responseBuilder) buildNamedType(tpe *types.Named, resp *spec.Response, default: if decl, found := r.ctx.DeclForType(o.Type()); found { var schema spec.Schema - typable := schemaTypable{schema: &schema, level: 0} + typable := schemaTypable{schema: &schema, level: 0, skipExt: r.ctx.opts.SkipExtensions} d := decl.Obj() if isStdTime(d) { @@ -350,7 +351,7 @@ func (r *responseBuilder) buildAlias(tpe *types.Alias, resp *spec.Response, seen break // builtin } - typable := schemaTypable{schema: &spec.Schema{}, level: 0} + typable := schemaTypable{schema: &spec.Schema{}, level: 0, skipExt: r.ctx.opts.SkipExtensions} return r.makeRef(decl, typable) case *types.Alias: o := rtpe.Obj() @@ -358,7 +359,7 @@ func (r *responseBuilder) buildAlias(tpe *types.Alias, resp *spec.Response, seen break // builtin } - typable := schemaTypable{schema: &spec.Schema{}, level: 0} + typable := schemaTypable{schema: &spec.Schema{}, level: 0, skipExt: r.ctx.opts.SkipExtensions} return r.makeRef(decl, typable) } @@ -504,7 +505,7 @@ func (r *responseBuilder) processResponseField(fld *types.Var, decl *entityDecl, resp.Schema.Typed("file", "") } else { debugLogf("build response %v (%v) (not a file)", fld, fld.Type()) - if err := r.buildFromField(fld, fld.Type(), responseTypable{in, &ps, resp}, seen); err != nil { + if err := r.buildFromField(fld, fld.Type(), responseTypable{in, &ps, resp, r.ctx.opts.SkipExtensions}, seen); err != nil { return err } } diff --git a/schema.go b/schema.go index 911ed8e..a6b2fe1 100644 --- a/schema.go +++ b/schema.go @@ -11,7 +11,6 @@ import ( "go/token" "go/types" "log" - "os" "reflect" "strconv" "strings" @@ -22,8 +21,8 @@ import ( "github.com/go-openapi/spec" ) -func addExtension(ve *spec.VendorExtensible, key string, value any) { - if os.Getenv("SWAGGER_GENERATE_EXTENSION") == "false" { +func addExtension(ve *spec.VendorExtensible, key string, value any, skip bool) { + if skip { return } @@ -31,8 +30,9 @@ func addExtension(ve *spec.VendorExtensible, key string, value any) { } type schemaTypable struct { - schema *spec.Schema - level int + schema *spec.Schema + level int + skipExt bool } func (st schemaTypable) In() string { return "body" } @@ -58,7 +58,7 @@ func (st schemaTypable) Items() swaggerTypable { //nolint:ireturn // polymorphic } st.schema.Typed("array", "") - return schemaTypable{st.schema.Items.Schema, st.level + 1} + return schemaTypable{st.schema.Items.Schema, st.level + 1, st.skipExt} } func (st schemaTypable) AdditionalProperties() swaggerTypable { //nolint:ireturn // polymorphic by design @@ -70,13 +70,13 @@ func (st schemaTypable) AdditionalProperties() swaggerTypable { //nolint:ireturn } st.schema.Typed("object", "") - return schemaTypable{st.schema.AdditionalProperties.Schema, st.level + 1} + return schemaTypable{st.schema.AdditionalProperties.Schema, st.level + 1, st.skipExt} } func (st schemaTypable) Level() int { return st.level } func (st schemaTypable) AddExtension(key string, value any) { - addExtension(&st.schema.VendorExtensible, key, value) + addExtension(&st.schema.VendorExtensible, key, value, st.skipExt) } func (st schemaTypable) WithEnum(values ...any) { @@ -199,9 +199,9 @@ func (s *schemaBuilder) buildFromDecl(_ *entityDecl, schema *spec.Schema) error if schema.Ref.String() == "" { // unless this is a $ref, we add traceability of the origin of this schema in source if s.Name != s.GoName { - addExtension(&schema.VendorExtensible, "x-go-name", s.GoName) + addExtension(&schema.VendorExtensible, "x-go-name", s.GoName, s.ctx.opts.SkipExtensions) } - addExtension(&schema.VendorExtensible, "x-go-package", s.decl.Obj().Pkg().Path()) + addExtension(&schema.VendorExtensible, "x-go-package", s.decl.Obj().Pkg().Path(), s.ctx.opts.SkipExtensions) } }() @@ -228,7 +228,7 @@ func (s *schemaBuilder) buildFromDecl(_ *entityDecl, schema *spec.Schema) error return s.buildDeclNamed(tpe, schema) case *types.Alias: debugLogf("alias: %v -> %v", tpe, tpe.Rhs()) - tgt := schemaTypable{schema, 0} + tgt := schemaTypable{schema, 0, s.ctx.opts.SkipExtensions} return s.buildDeclAlias(tpe, tgt) case *types.TypeParam: @@ -262,7 +262,7 @@ func (s *schemaBuilder) buildDeclNamed(tpe *types.Named, schema *spec.Schema) er return nil } - ps := schemaTypable{schema, 0} + ps := schemaTypable{schema, 0, s.ctx.opts.SkipExtensions} ti := s.decl.Pkg.TypesInfo.Types[s.decl.Spec.Type] if !ti.IsType() { return fmt.Errorf("declaration is not a type: %v: %w", o, ErrCodeScan) @@ -733,7 +733,7 @@ func (s *schemaBuilder) processAnonInterfaceMethod(fld *types.Func, it *types.In schema.Properties = make(map[string]spec.Schema) } ps := schema.Properties[name] - if err := s.buildFromType(sig.Results().At(0).Type(), schemaTypable{&ps, 0}); err != nil { + if err := s.buildFromType(sig.Results().At(0).Type(), schemaTypable{&ps, 0, s.ctx.opts.SkipExtensions}); err != nil { return err } if sfName, isStrfmt := strfmtName(afld.Doc); isStrfmt { @@ -802,7 +802,7 @@ func (s *schemaBuilder) buildFromMap(titpe *types.Map, tgt swaggerTypable) error return fmt.Errorf("items doesn't support maps: %w", ErrCodeScan) } - eleProp := schemaTypable{sch, tgt.Level()} + eleProp := schemaTypable{sch, tgt.Level(), s.ctx.opts.SkipExtensions} key := titpe.Key() if key.Underlying().String() == "string" || isTextMarshaler(key) { return s.buildFromType(titpe.Elem(), eleProp.AdditionalProperties()) @@ -891,7 +891,7 @@ func (s *schemaBuilder) processEmbeddedType(fld types.Type, flist []*ast.Field, case *types.Interface: debugLogf("embedded anonymous interface type (buildInterface): %v", ftpe) var aliasedSchema spec.Schema - ps := schemaTypable{schema: &aliasedSchema} + ps := schemaTypable{schema: &aliasedSchema, skipExt: s.ctx.opts.SkipExtensions} if err = s.buildAnonymousInterface(ftpe, ps, decl); err != nil { return false, err } @@ -902,7 +902,7 @@ func (s *schemaBuilder) processEmbeddedType(fld types.Type, flist []*ast.Field, case *types.Alias: debugLogf("embedded alias (buildInterface): %v -> %v", ftpe, ftpe.Rhs()) var aliasedSchema spec.Schema - ps := schemaTypable{schema: &aliasedSchema} + ps := schemaTypable{schema: &aliasedSchema, skipExt: s.ctx.opts.SkipExtensions} if err = s.buildAlias(ftpe, ps); err != nil { return false, err } @@ -982,7 +982,7 @@ func (s *schemaBuilder) processInterfaceMethod(fld *types.Func, it *types.Interf name := nameOverride(fld.Name(), afld.Doc) ps := tgt.Properties[name] - if err := s.buildFromType(sig.Results().At(0).Type(), schemaTypable{&ps, 0}); err != nil { + if err := s.buildFromType(sig.Results().At(0).Type(), schemaTypable{&ps, 0, s.ctx.opts.SkipExtensions}); err != nil { return err } if sfName, isStrfmt := strfmtName(afld.Doc); isStrfmt { @@ -1108,7 +1108,7 @@ func (s *schemaBuilder) buildFromStruct(decl *entityDecl, st *types.Struct, sche } name, ok := typeName(cmt) if ok { - _ = swaggerSchemaForType(name, schemaTypable{schema: schema}) + _ = swaggerSchemaForType(name, schemaTypable{schema: schema, skipExt: s.ctx.opts.SkipExtensions}) return nil } // First pass: scan anonymous/embedded fields for allOf composition. @@ -1259,7 +1259,7 @@ func (s *schemaBuilder) processStructField(fld *types.Var, decl *entityDecl, tgt } ps := tgt.Properties[name] - if err = s.buildFromType(fld.Type(), schemaTypable{&ps, 0}); err != nil { + if err = s.buildFromType(fld.Type(), schemaTypable{&ps, 0, s.ctx.opts.SkipExtensions}); err != nil { return err } if isString { @@ -1279,7 +1279,7 @@ func (s *schemaBuilder) processStructField(fld *types.Var, decl *entityDecl, tgt } if ps.Ref.String() == "" && name != fld.Name() { - addExtension(&ps.VendorExtensible, "x-go-name", fld.Name()) + addExtension(&ps.VendorExtensible, "x-go-name", fld.Name(), s.ctx.opts.SkipExtensions) } if s.ctx.app.setXNullableForPointers { @@ -1308,7 +1308,7 @@ func (s *schemaBuilder) buildAllOf(tpe types.Type, schema *spec.Schema) error { return s.buildNamedAllOf(ftpe, schema) case *types.Alias: debugLogf("allOf member is alias %v => %v", ftpe, ftpe.Rhs()) - tgt := schemaTypable{schema: schema} + tgt := schemaTypable{schema: schema, skipExt: s.ctx.opts.SkipExtensions} return s.buildAlias(ftpe, tgt) case *types.TypeParam: log.Printf("WARNING: generic type parameters are not supported yet %[1]v (%[1]T). Skipped", ftpe) @@ -1344,7 +1344,7 @@ func (s *schemaBuilder) buildNamedAllOf(ftpe *types.Named, schema *spec.Schema) } if decl.HasModelAnnotation() { - return s.makeRef(decl, schemaTypable{schema, 0}) + return s.makeRef(decl, schemaTypable{schema, 0, s.ctx.opts.SkipExtensions}) } return s.buildFromStruct(decl, utpe, schema, make(map[string]string)) @@ -1360,7 +1360,7 @@ func (s *schemaBuilder) buildNamedAllOf(ftpe *types.Named, schema *spec.Schema) } if decl.HasModelAnnotation() { - return s.makeRef(decl, schemaTypable{schema, 0}) + return s.makeRef(decl, schemaTypable{schema, 0, s.ctx.opts.SkipExtensions}) } return s.buildFromInterface(decl, utpe, schema, make(map[string]string)) @@ -1394,7 +1394,7 @@ func (s *schemaBuilder) buildEmbedded(tpe types.Type, schema *spec.Schema, seen return s.buildNamedEmbedded(ftpe, schema, seen) case *types.Alias: debugLogf("embedded alias %v => %v", ftpe, ftpe.Rhs()) - tgt := schemaTypable{schema, 0} + tgt := schemaTypable{schema, 0, s.ctx.opts.SkipExtensions} return s.buildAlias(ftpe, tgt) case *types.Union: // e.g. type X interface{ ~uint16 | ~float32 } log.Printf("WARNING: union type constraints are not supported yet %[1]v (%[1]T). Skipped", ftpe) @@ -1439,7 +1439,7 @@ func (s *schemaBuilder) buildNamedEmbedded(ftpe *types.Named, schema *spec.Schem return nil } if isStdError(o) { - tgt := schemaTypable{schema: schema} + tgt := schemaTypable{schema: schema, skipExt: s.ctx.opts.SkipExtensions} tgt.AddExtension("x-go-type", o.Name()) return swaggerSchemaForType(o.Name(), tgt) } diff --git a/schema_test.go b/schema_test.go index 26f19ca..68de053 100644 --- a/schema_test.go +++ b/schema_test.go @@ -971,8 +971,6 @@ func TestAliasedTopLevelModels(t *testing.T) { } func TestAliasedSchemas(t *testing.T) { - t.Setenv("SWAGGER_GENERATE_EXTENSION", "true") - fixturesPath := filepath.Join("fixtures", "goparsing", "go123", "aliased", "schema") var sp *spec.Swagger t.Run("end-to-end source scan should succeed", func(t *testing.T) { @@ -1505,8 +1503,6 @@ func testAliasedEmbeddedTypes(t *testing.T, sp *spec.Swagger) { } func TestSpecialSchemas(t *testing.T) { - t.Setenv("SWAGGER_GENERATE_EXTENSION", "true") - fixturesPath := filepath.Join("fixtures", "goparsing", "go123", "special") var sp *spec.Swagger @@ -2221,23 +2217,21 @@ func TestAddExtension(t *testing.T) { key := "x-go-name" value := "Name" - addExtension(ve, key, value) + addExtension(ve, key, value, false) veStr, ok := ve.Extensions[key].(string) require.TrueT(t, ok) assert.EqualT(t, value, veStr) key2 := "x-go-package" value2 := "schema" - t.Setenv("SWAGGER_GENERATE_EXTENSION", "true") - addExtension(ve, key2, value2) + addExtension(ve, key2, value2, false) veStr2, ok := ve.Extensions[key2].(string) require.TrueT(t, ok) assert.EqualT(t, value2, veStr2) key3 := "x-go-class" value3 := "Spec" - t.Setenv("SWAGGER_GENERATE_EXTENSION", "false") - addExtension(ve, key3, value3) + addExtension(ve, key3, value3, true) assert.Nil(t, ve.Extensions[key3]) } @@ -2523,13 +2517,13 @@ func TestIssue2540(t *testing.T) { func testIssue2540(descWithRef bool, expectedJSON string) func(*testing.T) { return func(t *testing.T) { - t.Setenv("SWAGGER_GENERATE_EXTENSION", "false") packagePattern := "./bugs/2540/foo" packagePath := fixturesModule + "/bugs/2540/foo" sctx, err := newScanCtx(&Options{ - Packages: []string{packagePattern}, - WorkDir: "fixtures", - DescWithRef: descWithRef, + Packages: []string{packagePattern}, + WorkDir: "fixtures", + DescWithRef: descWithRef, + SkipExtensions: true, }) require.NoError(t, err) diff --git a/taggers.go b/taggers.go index fbcd2dc..169b925 100644 --- a/taggers.go +++ b/taggers.go @@ -62,16 +62,16 @@ func parseArrayTypes(sp *sectionedParser, name string, expr ast.Expr, items *spe } // setupRefParamTaggers configures taggers for a parameter that is a $ref. -func setupRefParamTaggers(sp *sectionedParser, ps *spec.Parameter) { +func setupRefParamTaggers(sp *sectionedParser, ps *spec.Parameter, skipExt bool) { sp.taggers = []tagParser{ newSingleLineTagParser("in", &matchOnlyParam{ps, rxIn}), newSingleLineTagParser("required", &matchOnlyParam{ps, rxRequired}), - newMultiLineTagParser("Extensions", newSetExtensions(spExtensionsSetter(ps)), true), + newMultiLineTagParser("Extensions", newSetExtensions(spExtensionsSetter(ps, skipExt)), true), } } // setupInlineParamTaggers configures taggers for a fully-defined inline parameter. -func setupInlineParamTaggers(sp *sectionedParser, ps *spec.Parameter, name string, afld *ast.Field) error { +func setupInlineParamTaggers(sp *sectionedParser, ps *spec.Parameter, name string, afld *ast.Field, skipExt bool) error { sp.taggers = []tagParser{ newSingleLineTagParser("in", &matchOnlyParam{ps, rxIn}), newSingleLineTagParser("maximum", &setMaximum{paramValidations{ps}, rxf(rxMaximumFmt, "")}), @@ -88,7 +88,7 @@ func setupInlineParamTaggers(sp *sectionedParser, ps *spec.Parameter, name strin newSingleLineTagParser("default", &setDefault{&ps.SimpleSchema, paramValidations{ps}, rxf(rxDefaultFmt, "")}), newSingleLineTagParser("example", &setExample{&ps.SimpleSchema, paramValidations{ps}, rxf(rxExampleFmt, "")}), newSingleLineTagParser("required", &setRequiredParam{ps}), - newMultiLineTagParser("Extensions", newSetExtensions(spExtensionsSetter(ps)), true), + newMultiLineTagParser("Extensions", newSetExtensions(spExtensionsSetter(ps, skipExt)), true), } // check if this is a primitive, if so parse the validations from the From eefedbbc942284ae5e23e626c9aefaa8fbafde29 Mon Sep 17 00:00:00 2001 From: Frederic BIDON Date: Mon, 23 Mar 2026 12:37:40 +0100 Subject: [PATCH 2/2] chore: replace env var DEBUG by option Signed-off-by: Frederic BIDON --- application.go | 56 +++++++++++++++---------------- application_test.go | 3 +- go.mod | 2 +- parameters.go | 18 +++++----- parser.go | 14 ++++---- responses.go | 18 +++++----- routes.go | 2 +- schema.go | 82 ++++++++++++++++++++++----------------------- taggers.go | 8 ++--- 9 files changed, 102 insertions(+), 101 deletions(-) diff --git a/application.go b/application.go index ffeb8d4..0f07b51 100644 --- a/application.go +++ b/application.go @@ -9,29 +9,16 @@ import ( "go/token" "go/types" "log" - "os" "regexp" "strings" "github.com/go-openapi/spec" - "github.com/go-openapi/swag/conv" "golang.org/x/tools/go/packages" ) const pkgLoadMode = packages.NeedName | packages.NeedFiles | packages.NeedImports | packages.NeedDeps | packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo -func safeConvert(str string) bool { - b, err := conv.ConvertBool(str) - if err != nil { - return false - } - return b -} - -// Debug is true when process is run with DEBUG=1 env var. -var Debug = safeConvert(os.Getenv("DEBUG")) //nolint:gochecknoglobals // package-level configuration from environment - type node uint32 const ( @@ -60,11 +47,13 @@ type Options struct { TransparentAliases bool // aliases are completely transparent, never creating definitions DescWithRef bool // allow overloaded descriptions together with $ref, otherwise jsonschema draft4 $ref predates everything SkipExtensions bool // skip generating x-go-* vendor extensions in the spec + Debug bool // enable verbose debug logging during scanning } type scanCtx struct { - pkgs []*packages.Package - app *typeIndex + pkgs []*packages.Package + app *typeIndex + debug bool opts *Options } @@ -111,15 +100,17 @@ func newScanCtx(opts *Options) (*scanCtx, error) { withXNullableForPointers(opts.SetXNullableForPointers), withRefAliases(opts.RefAliases), withTransparentAliases(opts.TransparentAliases), + withDebug(opts.Debug), ) if err != nil { return nil, err } return &scanCtx{ - pkgs: pkgs, - app: app, - opts: opts, + pkgs: pkgs, + app: app, + debug: opts.Debug, + opts: opts, }, nil } @@ -308,14 +299,14 @@ func (s *scanCtx) FindDecl(pkgPath, name string) (*entityDecl, bool) { def, ok := pkg.TypesInfo.Defs[ts.Name] if !ok { - debugLogf("couldn't find type info for %s", ts.Name) + debugLogf(s.debug, "couldn't find type info for %s", ts.Name) continue } nt, isNamed := def.Type().(*types.Named) at, isAliased := def.Type().(*types.Alias) if !isNamed && !isAliased { - debugLogf("%s is not a named or an aliased type but a %T", ts.Name, def.Type()) + debugLogf(s.debug, "%s is not a named or an aliased type but a %T", ts.Name, def.Type()) continue } @@ -557,6 +548,12 @@ func withTransparentAliases(enabled bool) typeIndexOption { } } +func withDebug(enabled bool) typeIndexOption { + return func(a *typeIndex) { + a.debug = enabled + } +} + func newTypeIndex(pkgs []*packages.Package, opts ...typeIndexOption) (*typeIndex, error) { ac := &typeIndex{ AllPackages: make(map[string]*packages.Package), @@ -590,6 +587,7 @@ type typeIndex struct { setXNullableForPointers bool refAliases bool transparentAliases bool + debug bool } func (a *typeIndex) build(pkgs []*packages.Package) error { @@ -611,7 +609,7 @@ func (a *typeIndex) build(pkgs []*packages.Package) error { func (a *typeIndex) processPackage(pkg *packages.Package) error { if !shouldAcceptPkg(pkg.PkgPath, a.includePkgs, a.excludePkgs) { - debugLogf("package %s is ignored due to rules", pkg.Name) + debugLogf(a.debug, "package %s is ignored due to rules", pkg.Name) return nil } @@ -654,7 +652,7 @@ func (a *typeIndex) collectPathAnnotations(rx *regexp.Regexp, comments []*ast.Co continue } if !shouldAcceptTag(pp.Tags, a.includeTags, a.excludeTags) { - debugLogf("operation %s %s is ignored due to tag rules", pp.Method, pp.Path) + debugLogf(a.debug, "operation %s %s is ignored due to tag rules", pp.Method, pp.Path) continue } dst = append(dst, pp) @@ -688,21 +686,21 @@ func (a *typeIndex) processDecl(pkg *packages.Package, file *ast.File, n node, g for _, sp := range gd.Specs { switch ts := sp.(type) { case *ast.ValueSpec: - debugLogf("saw value spec: %v", ts.Names) + debugLogf(a.debug, "saw value spec: %v", ts.Names) return case *ast.ImportSpec: - debugLogf("saw import spec: %v", ts.Name) + debugLogf(a.debug, "saw import spec: %v", ts.Name) return case *ast.TypeSpec: def, ok := pkg.TypesInfo.Defs[ts.Name] if !ok { - debugLogf("couldn't find type info for %s", ts.Name) + debugLogf(a.debug, "couldn't find type info for %s", ts.Name) continue } nt, isNamed := def.Type().(*types.Named) at, isAliased := def.Type().(*types.Alias) if !isNamed && !isAliased { - debugLogf("%s is not a named or aliased type but a %T", ts.Name, def.Type()) + debugLogf(a.debug, "%s is not a named or aliased type but a %T", ts.Name, def.Type()) continue } @@ -730,7 +728,7 @@ func (a *typeIndex) processDecl(pkg *packages.Package, file *ast.File, n node, g case n&responseNode != 0 && decl.HasResponseAnnotation(): a.Responses = append(a.Responses, decl) default: - debugLogf( + debugLogf(a.debug, "type %q skipped because it is not tagged as a model, a parameter or a response. %s", decl.Obj().Name(), "It may reenter the scope because it is a discovered dependency", @@ -828,8 +826,8 @@ func (a *typeIndex) detectNodes(file *ast.File) (node, error) { return n, nil } -func debugLogf(format string, args ...any) { - if Debug { +func debugLogf(debug bool, format string, args ...any) { + if debug { _ = log.Output(logCallerDepth, fmt.Sprintf(format, args...)) } } diff --git a/application_test.go b/application_test.go index 5352321..219e463 100644 --- a/application_test.go +++ b/application_test.go @@ -41,7 +41,6 @@ func TestMain(m *testing.M) { log.SetOutput(io.Discard) } else { // enable full debug when test is run with -enable-debug arg - Debug = true log.SetFlags(log.LstdFlags | log.Lshortfile) log.SetOutput(os.Stderr) } @@ -101,6 +100,7 @@ func loadPetstorePkgsCtx(t *testing.T) *scanCtx { sctx, err := newScanCtx(&Options{ Packages: []string{"./goparsing/petstore/..."}, WorkDir: "fixtures", + Debug: enableDebug, }) require.NoError(t, err) petstoreCtx = sctx @@ -122,6 +122,7 @@ func loadClassificationPkgsCtx(t *testing.T) *scanCtx { "./goparsing/classification/operations", }, WorkDir: "fixtures", + Debug: enableDebug, }) require.NoError(t, err) classificationCtx = sctx diff --git a/go.mod b/go.mod index cead3fa..91e403c 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,6 @@ toolchain go1.26.1 require ( github.com/go-openapi/loads v0.23.3 github.com/go-openapi/spec v0.22.4 - github.com/go-openapi/swag/conv v0.25.5 github.com/go-openapi/testify/v2 v2.4.1 go.yaml.in/yaml/v3 v3.0.4 golang.org/x/tools v0.43.0 @@ -17,6 +16,7 @@ require ( require ( github.com/go-openapi/jsonpointer v0.22.5 // indirect github.com/go-openapi/jsonreference v0.21.5 // indirect + github.com/go-openapi/swag/conv v0.25.5 // indirect github.com/go-openapi/swag/jsonname v0.25.5 // indirect github.com/go-openapi/swag/jsonutils v0.25.5 // indirect github.com/go-openapi/swag/loading v0.25.5 // indirect diff --git a/parameters.go b/parameters.go index 9250ebc..c9adf3a 100644 --- a/parameters.go +++ b/parameters.go @@ -185,7 +185,7 @@ func (p *parameterBuilder) Build(operations map[string]*spec.Operation) error { operations[opid] = operation operation.ID = opid } - debugLogf("building parameters for: %s", opid) + debugLogf(p.ctx.debug, "building parameters for: %s", opid) // analyze struct body for fields etc // each exported struct field: @@ -209,7 +209,7 @@ func (p *parameterBuilder) buildFromType(otpe types.Type, op *spec.Operation, se case *types.Named: return p.buildNamedType(tpe, op, seen) case *types.Alias: - debugLogf("alias(parameters.buildFromType): got alias %v to %v", tpe, tpe.Rhs()) + debugLogf(p.ctx.debug, "alias(parameters.buildFromType): got alias %v to %v", tpe, tpe.Rhs()) return p.buildAlias(tpe, op, seen) default: return fmt.Errorf("unhandled type (%T): %s: %w", otpe, tpe.String(), ErrCodeScan) @@ -225,7 +225,7 @@ func (p *parameterBuilder) buildNamedType(tpe *types.Named, op *spec.Operation, switch stpe := o.Type().Underlying().(type) { case *types.Struct: - debugLogf("build from named type %s: %T", o.Name(), tpe) + debugLogf(p.ctx.debug, "build from named type %s: %T", o.Name(), tpe) if decl, found := p.ctx.DeclForType(o.Type()); found { return p.buildFromStruct(decl, stpe, op, seen) } @@ -285,7 +285,7 @@ func (p *parameterBuilder) buildAlias(tpe *types.Alias, op *spec.Operation, seen } func (p *parameterBuilder) buildFromField(fld *types.Var, tpe types.Type, typable swaggerTypable, seen map[string]spec.Parameter) error { - debugLogf("build from field %s: %T", fld.Name(), tpe) + debugLogf(p.ctx.debug, "build from field %s: %T", fld.Name(), tpe) switch ftpe := tpe.(type) { case *types.Basic: @@ -305,7 +305,7 @@ func (p *parameterBuilder) buildFromField(fld *types.Var, tpe types.Type, typabl case *types.Named: return p.buildNamedField(ftpe, typable) case *types.Alias: - debugLogf("alias(parameters.buildFromField): got alias %v to %v", ftpe, ftpe.Rhs()) // TODO + debugLogf(p.ctx.debug, "alias(parameters.buildFromField): got alias %v to %v", ftpe, ftpe.Rhs()) // TODO return p.buildFieldAlias(ftpe, typable, fld, seen) default: return fmt.Errorf("unknown type for %s: %T: %w", fld.String(), fld.Type(), ErrCodeScan) @@ -523,13 +523,13 @@ func (p *parameterBuilder) buildFromStruct(decl *entityDecl, tpe *types.Struct, // Returns the parameter name if the field was processed, or "" if it was skipped. func (p *parameterBuilder) processParamField(fld *types.Var, decl *entityDecl, seen map[string]spec.Parameter) (string, error) { if !fld.Exported() { - debugLogf("skipping field %s because it's not exported", fld.Name()) + debugLogf(p.ctx.debug, "skipping field %s because it's not exported", fld.Name()) return "", nil } afld := findASTField(decl.File, fld.Pos()) if afld == nil { - debugLogf("can't find source associated with %s", fld.String()) + debugLogf(p.ctx.debug, "can't find source associated with %s", fld.String()) return "", nil } @@ -587,9 +587,9 @@ func (p *parameterBuilder) processParamField(fld *types.Var, decl *entityDecl, s } if ps.Ref.String() != "" { - setupRefParamTaggers(sp, &ps, p.ctx.opts.SkipExtensions) + setupRefParamTaggers(sp, &ps, p.ctx.opts.SkipExtensions, p.ctx.debug) } else { - if err := setupInlineParamTaggers(sp, &ps, name, afld, p.ctx.opts.SkipExtensions); err != nil { + if err := setupInlineParamTaggers(sp, &ps, name, afld, p.ctx.opts.SkipExtensions, p.ctx.debug); err != nil { return "", err } } diff --git a/parser.go b/parser.go index d3c1827..92b269b 100644 --- a/parser.go +++ b/parser.go @@ -1493,16 +1493,18 @@ func parseEnum(val string, s *spec.SimpleSchema) []any { // alphaChars used when parsing for Vendor Extensions. const alphaChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" -func newSetExtensions(setter func(*spec.Extensions)) *setOpExtensions { +func newSetExtensions(setter func(*spec.Extensions), debug bool) *setOpExtensions { return &setOpExtensions{ - set: setter, - rx: rxExtensions, + set: setter, + rx: rxExtensions, + debug: debug, } } type setOpExtensions struct { - set func(*spec.Extensions) - rx *regexp.Regexp + set func(*spec.Extensions) + rx *regexp.Regexp + debug bool } type extensionObject struct { @@ -1711,7 +1713,7 @@ func (ss *setOpExtensions) Parse(lines []string) error { } else if m, ok := ext.Root.(map[string]any); ok { exts.AddExtension(ext.Extension, m[ext.Extension]) } else { - debugLogf("Unknown Extension type: %s", fmt.Sprint(reflect.TypeOf(ext.Root))) + debugLogf(ss.debug, "Unknown Extension type: %s", fmt.Sprint(reflect.TypeOf(ext.Root))) } } diff --git a/responses.go b/responses.go index 505acf4..b6c0f76 100644 --- a/responses.go +++ b/responses.go @@ -158,7 +158,7 @@ func (r *responseBuilder) Build(responses map[string]spec.Response) error { name, _ := r.decl.ResponseNames() response := responses[name] - debugLogf("building response: %s", name) + debugLogf(r.ctx.debug, "building response: %s", name) // analyze doc comment for the model sp := new(sectionedParser) @@ -182,7 +182,7 @@ func (r *responseBuilder) Build(responses map[string]spec.Response) error { } func (r *responseBuilder) buildFromField(fld *types.Var, tpe types.Type, typable swaggerTypable, seen map[string]bool) error { - debugLogf("build from field %s: %T", fld.Name(), tpe) + debugLogf(r.ctx.debug, "build from field %s: %T", fld.Name(), tpe) switch ftpe := tpe.(type) { case *types.Basic: @@ -202,7 +202,7 @@ func (r *responseBuilder) buildFromField(fld *types.Var, tpe types.Type, typable case *types.Named: return r.buildNamedField(ftpe, typable) case *types.Alias: - debugLogf("alias(responses.buildFromField): got alias %v to %v", ftpe, ftpe.Rhs()) + debugLogf(r.ctx.debug, "alias(responses.buildFromField): got alias %v to %v", ftpe, ftpe.Rhs()) return r.buildFieldAlias(ftpe, typable, fld, seen) default: return fmt.Errorf("unknown type for %s: %T: %w", fld.String(), fld.Type(), ErrCodeScan) @@ -264,7 +264,7 @@ func (r *responseBuilder) buildFromType(otpe types.Type, resp *spec.Response, se case *types.Named: return r.buildNamedType(tpe, resp, seen) case *types.Alias: - debugLogf("alias(responses.buildFromType): got alias %v to %v", tpe, tpe.Rhs()) + debugLogf(r.ctx.debug, "alias(responses.buildFromType): got alias %v to %v", tpe, tpe.Rhs()) return r.buildAlias(tpe, resp, seen) default: return fmt.Errorf("anonymous types are currently not supported for responses: %w", ErrCodeScan) @@ -281,7 +281,7 @@ func (r *responseBuilder) buildNamedType(tpe *types.Named, resp *spec.Response, switch stpe := o.Type().Underlying().(type) { // TODO(fred): this is wrong without checking for aliases? case *types.Struct: - debugLogf("build from type %s: %T", o.Name(), tpe) + debugLogf(r.ctx.debug, "build from type %s: %T", o.Name(), tpe) if decl, found := r.ctx.DeclForType(o.Type()); found { return r.buildFromStruct(decl, stpe, resp, seen) } @@ -441,7 +441,7 @@ func (r *responseBuilder) buildFromStruct(decl *entityDecl, tpe *types.Struct, r continue } if fld.Anonymous() { - debugLogf("skipping anonymous field") + debugLogf(r.ctx.debug, "skipping anonymous field") continue } @@ -465,12 +465,12 @@ func (r *responseBuilder) processResponseField(fld *types.Var, decl *entityDecl, afld := findASTField(decl.File, fld.Pos()) if afld == nil { - debugLogf("can't find source associated with %s", fld.String()) + debugLogf(r.ctx.debug, "can't find source associated with %s", fld.String()) return nil } if ignored(afld.Doc) { - debugLogf("field %v is deliberately ignored", fld) + debugLogf(r.ctx.debug, "field %v is deliberately ignored", fld) return nil } @@ -504,7 +504,7 @@ func (r *responseBuilder) processResponseField(fld *types.Var, decl *entityDecl, resp.Schema = &spec.Schema{} resp.Schema.Typed("file", "") } else { - debugLogf("build response %v (%v) (not a file)", fld, fld.Type()) + debugLogf(r.ctx.debug, "build response %v (%v) (not a file)", fld, fld.Type()) if err := r.buildFromField(fld, fld.Type(), responseTypable{in, &ps, resp, r.ctx.opts.SkipExtensions}, seen); err != nil { return err } diff --git a/routes.go b/routes.go index 132b6d6..0bbe7ff 100644 --- a/routes.go +++ b/routes.go @@ -81,7 +81,7 @@ func (r *routesBuilder) Build(tgt *spec.Paths) error { newMultiLineTagParser("Parameters", spa, false), newMultiLineTagParser("Responses", sr, false), newSingleLineTagParser("Deprecated", &setDeprecatedOp{op}), - newMultiLineTagParser("Extensions", newSetExtensions(opExtensionsSetter(op)), true), + newMultiLineTagParser("Extensions", newSetExtensions(opExtensionsSetter(op), r.ctx.debug), true), } if err := sp.Parse(r.route.Remaining); err != nil { return fmt.Errorf("operation (%s): %w", op.ID, err) diff --git a/schema.go b/schema.go index a6b2fe1..83bbfb9 100644 --- a/schema.go +++ b/schema.go @@ -208,26 +208,26 @@ func (s *schemaBuilder) buildFromDecl(_ *entityDecl, schema *spec.Schema) error switch tpe := s.decl.ObjType().(type) { // TODO(fredbi): we may safely remove all the cases here that are not Named or Alias case *types.Basic: - debugLogf("basic: %v", tpe.Name()) + debugLogf(s.ctx.debug, "basic: %v", tpe.Name()) return nil case *types.Struct: return s.buildFromStruct(s.decl, tpe, schema, make(map[string]string)) case *types.Interface: return s.buildFromInterface(s.decl, tpe, schema, make(map[string]string)) case *types.Array: - debugLogf("array: %v -> %v", s.decl.Ident.Name, tpe.Elem().String()) + debugLogf(s.ctx.debug, "array: %v -> %v", s.decl.Ident.Name, tpe.Elem().String()) return nil case *types.Slice: - debugLogf("slice: %v -> %v", s.decl.Ident.Name, tpe.Elem().String()) + debugLogf(s.ctx.debug, "slice: %v -> %v", s.decl.Ident.Name, tpe.Elem().String()) return nil case *types.Map: - debugLogf("map: %v -> [%v]%v", s.decl.Ident.Name, tpe.Key().String(), tpe.Elem().String()) + debugLogf(s.ctx.debug, "map: %v -> [%v]%v", s.decl.Ident.Name, tpe.Key().String(), tpe.Elem().String()) return nil case *types.Named: - debugLogf("named: %v", tpe) + debugLogf(s.ctx.debug, "named: %v", tpe) return s.buildDeclNamed(tpe, schema) case *types.Alias: - debugLogf("alias: %v -> %v", tpe, tpe.Rhs()) + debugLogf(s.ctx.debug, "alias: %v -> %v", tpe, tpe.Rhs()) tgt := schemaTypable{schema, 0, s.ctx.opts.SkipExtensions} return s.buildDeclAlias(tpe, tgt) @@ -256,7 +256,7 @@ func (s *schemaBuilder) buildDeclNamed(tpe *types.Named, schema *spec.Schema) er mustNotBeABuiltinType(o) - debugLogf("got the named type object: %s.%s | isAlias: %t | exported: %t", o.Pkg().Path(), o.Name(), o.IsAlias(), o.Exported()) + debugLogf(s.ctx.debug, "got the named type object: %s.%s | isAlias: %t | exported: %t", o.Pkg().Path(), o.Name(), o.IsAlias(), o.Exported()) if isStdTime(o) { schema.Typed("string", "date-time") return nil @@ -289,7 +289,7 @@ func (s *schemaBuilder) buildFromTextMarshal(tpe types.Type, tgt swaggerTypable) return swaggerSchemaForType(tio.Name(), tgt) } - debugLogf("named refined type %s.%s", tio.Pkg().Path(), tio.Name()) + debugLogf(s.ctx.debug, "named refined type %s.%s", tio.Pkg().Path(), tio.Name()) pkg, found := s.ctx.PkgForType(tpe) if strings.ToLower(tio.Name()) == "uuid" { @@ -299,7 +299,7 @@ func (s *schemaBuilder) buildFromTextMarshal(tpe types.Type, tgt swaggerTypable) if !found { // this must be a builtin - debugLogf("skipping because package is nil: %v", tpe) + debugLogf(s.ctx.debug, "skipping because package is nil: %v", tpe) return nil } @@ -332,7 +332,7 @@ func (s *schemaBuilder) buildFromTextMarshal(tpe types.Type, tgt swaggerTypable) func (s *schemaBuilder) buildFromType(tpe types.Type, tgt swaggerTypable) error { // check if the type implements encoding.TextMarshaler interface // if so, the type is rendered as a string. - debugLogf("schema buildFromType %v (%T)", tpe, tpe) + debugLogf(s.ctx.debug, "schema buildFromType %v (%T)", tpe, tpe) if isTextMarshaler(tpe) { return s.buildFromTextMarshal(tpe, tgt) @@ -364,7 +364,7 @@ func (s *schemaBuilder) buildFromType(tpe types.Type, tgt swaggerTypable) error return s.buildNamedType(titpe, tgt) case *types.Alias: // a named alias, e.g. type X = {RHS type}. - debugLogf("alias(schema.buildFromType): got alias %v to %v", titpe, titpe.Rhs()) + debugLogf(s.ctx.debug, "alias(schema.buildFromType): got alias %v to %v", titpe, titpe.Rhs()) return s.buildAlias(titpe, tgt) case *types.TypeParam: log.Printf("WARNING: generic type parameters are not supported yet %[1]v (%[1]T). Skipped", titpe) @@ -413,13 +413,13 @@ func (s *schemaBuilder) buildNamedType(titpe *types.Named, tgt swaggerTypable) e } pkg, found := s.ctx.PkgForType(titpe) - debugLogf("named refined type %s.%s", pkg, tio.Name()) + debugLogf(s.ctx.debug, "named refined type %s.%s", pkg, tio.Name()) if !found { // this must be a builtin // // This could happen for example when using unsupported types such as complex64, complex128, uintptr, // or type constraints such as comparable. - debugLogf("skipping because package is nil (builtin type): %v", tio) + debugLogf(s.ctx.debug, "skipping because package is nil (builtin type): %v", tio) return nil } @@ -436,7 +436,7 @@ func (s *schemaBuilder) buildNamedType(titpe *types.Named, tgt swaggerTypable) e } if s.decl.Spec.Assign.IsValid() { - debugLogf("found assignment: %s.%s", tio.Pkg().Path(), tio.Name()) + debugLogf(s.ctx.debug, "found assignment: %s.%s", tio.Pkg().Path(), tio.Name()) return s.buildFromType(titpe.Underlying(), tgt) } @@ -449,7 +449,7 @@ func (s *schemaBuilder) buildNamedType(titpe *types.Named, tgt swaggerTypable) e case *types.Struct: return s.buildNamedStruct(tio, cmt, tgt) case *types.Interface: - debugLogf("found interface: %s.%s", tio.Pkg().Path(), tio.Name()) + debugLogf(s.ctx.debug, "found interface: %s.%s", tio.Pkg().Path(), tio.Name()) decl, found := s.ctx.FindModel(tio.Pkg().Path(), tio.Name()) if !found { @@ -464,7 +464,7 @@ func (s *schemaBuilder) buildNamedType(titpe *types.Named, tgt swaggerTypable) e case *types.Slice: return s.buildNamedSlice(tio, cmt, utitpe.Elem(), tgt) case *types.Map: - debugLogf("found map type: %s.%s", tio.Pkg().Path(), tio.Name()) + debugLogf(s.ctx.debug, "found map type: %s.%s", tio.Pkg().Path(), tio.Name()) if decl, ok := s.ctx.FindModel(tio.Pkg().Path(), tio.Name()); ok { return s.makeRef(decl, tgt) @@ -495,7 +495,7 @@ func (s *schemaBuilder) buildNamedBasic(tio *types.TypeName, pkg *packages.Packa return nil } - debugLogf("found primitive type: %s.%s", tio.Pkg().Path(), tio.Name()) + debugLogf(s.ctx.debug, "found primitive type: %s.%s", tio.Pkg().Path(), tio.Name()) if sfnm, isf := strfmtName(cmt); isf { tgt.Typed("string", sfnm) @@ -516,7 +516,7 @@ func (s *schemaBuilder) buildNamedBasic(tio *types.TypeName, pkg *packages.Packa } if defaultName, ok := defaultName(cmt); ok { - debugLogf("default name: %s", defaultName) + debugLogf(s.ctx.debug, "default name: %s", defaultName) return nil } @@ -540,11 +540,11 @@ func (s *schemaBuilder) buildNamedBasic(tio *types.TypeName, pkg *packages.Packa } func (s *schemaBuilder) buildNamedStruct(tio *types.TypeName, cmt *ast.CommentGroup, tgt swaggerTypable) error { - debugLogf("found struct: %s.%s", tio.Pkg().Path(), tio.Name()) + debugLogf(s.ctx.debug, "found struct: %s.%s", tio.Pkg().Path(), tio.Name()) decl, ok := s.ctx.FindModel(tio.Pkg().Path(), tio.Name()) if !ok { - debugLogf("could not find model in index: %s.%s", tio.Pkg().Path(), tio.Name()) + debugLogf(s.ctx.debug, "could not find model in index: %s.%s", tio.Pkg().Path(), tio.Name()) return nil } @@ -568,7 +568,7 @@ func (s *schemaBuilder) buildNamedStruct(tio *types.TypeName, cmt *ast.CommentGr } func (s *schemaBuilder) buildNamedArray(tio *types.TypeName, cmt *ast.CommentGroup, elem types.Type, tgt swaggerTypable) error { - debugLogf("found array type: %s.%s", tio.Pkg().Path(), tio.Name()) + debugLogf(s.ctx.debug, "found array type: %s.%s", tio.Pkg().Path(), tio.Name()) if sfnm, isf := strfmtName(cmt); isf { if sfnm == goTypeByte { @@ -590,7 +590,7 @@ func (s *schemaBuilder) buildNamedArray(tio *types.TypeName, cmt *ast.CommentGro } func (s *schemaBuilder) buildNamedSlice(tio *types.TypeName, cmt *ast.CommentGroup, elem types.Type, tgt swaggerTypable) error { - debugLogf("found slice type: %s.%s", tio.Pkg().Path(), tio.Name()) + debugLogf(s.ctx.debug, "found slice type: %s.%s", tio.Pkg().Path(), tio.Name()) if sfnm, isf := strfmtName(cmt); isf { if sfnm == goTypeByte { @@ -719,7 +719,7 @@ func (s *schemaBuilder) processAnonInterfaceMethod(fld *types.Func, it *types.In afld := findASTField(decl.File, fld.Pos()) if afld == nil { - debugLogf("can't find source associated with %s for %s", fld.String(), it.String()) + debugLogf(s.ctx.debug, "can't find source associated with %s for %s", fld.String(), it.String()) return nil } @@ -878,18 +878,18 @@ func (s *schemaBuilder) buildFromInterface(decl *entityDecl, it *types.Interface } func (s *schemaBuilder) processEmbeddedType(fld types.Type, flist []*ast.Field, decl *entityDecl, schema *spec.Schema, seen map[string]string) (fieldHasAllOf bool, err error) { - debugLogf("inspecting embedded type in interface: %v", fld) + debugLogf(s.ctx.debug, "inspecting embedded type in interface: %v", fld) switch ftpe := fld.(type) { case *types.Named: - debugLogf("embedded named type (buildInterface): %v", ftpe) + debugLogf(s.ctx.debug, "embedded named type (buildInterface): %v", ftpe) o := ftpe.Obj() if isAny(o) || isStdError(o) { return false, nil } return s.buildNamedInterface(ftpe, flist, decl, schema, seen) case *types.Interface: - debugLogf("embedded anonymous interface type (buildInterface): %v", ftpe) + debugLogf(s.ctx.debug, "embedded anonymous interface type (buildInterface): %v", ftpe) var aliasedSchema spec.Schema ps := schemaTypable{schema: &aliasedSchema, skipExt: s.ctx.opts.SkipExtensions} if err = s.buildAnonymousInterface(ftpe, ps, decl); err != nil { @@ -900,7 +900,7 @@ func (s *schemaBuilder) processEmbeddedType(fld types.Type, flist []*ast.Field, schema.AddToAllOf(aliasedSchema) } case *types.Alias: - debugLogf("embedded alias (buildInterface): %v -> %v", ftpe, ftpe.Rhs()) + debugLogf(s.ctx.debug, "embedded alias (buildInterface): %v -> %v", ftpe, ftpe.Rhs()) var aliasedSchema spec.Schema ps := schemaTypable{schema: &aliasedSchema, skipExt: s.ctx.opts.SkipExtensions} if err = s.buildAlias(ftpe, ps); err != nil { @@ -925,7 +925,7 @@ func (s *schemaBuilder) processEmbeddedType(fld types.Type, flist []*ast.Field, ) } - debugLogf("got embedded interface: %v {%T}, fieldHasAllOf: %t", fld, fld, fieldHasAllOf) + debugLogf(s.ctx.debug, "got embedded interface: %v {%T}, fieldHasAllOf: %t", fld, fld, fieldHasAllOf) return fieldHasAllOf, nil } @@ -971,7 +971,7 @@ func (s *schemaBuilder) processInterfaceMethod(fld *types.Func, it *types.Interf afld := findASTField(decl.File, fld.Pos()) if afld == nil { - debugLogf("can't find source associated with %s for %s", fld.String(), it.String()) + debugLogf(s.ctx.debug, "can't find source associated with %s for %s", fld.String(), it.String()) return nil } @@ -1028,13 +1028,13 @@ func (s *schemaBuilder) buildNamedInterface(ftpe *types.Named, flist []*ast.Fiel } // decl. - debugLogf("maybe interface field %s: %s(%T)", o.Name(), o.Type().String(), o.Type()) + debugLogf(s.ctx.debug, "maybe interface field %s: %s(%T)", o.Name(), o.Type().String(), o.Type()) afld = an break } if afld == nil { - debugLogf("can't find source associated with %s", ftpe.String()) + debugLogf(s.ctx.debug, "can't find source associated with %s", ftpe.String()) return hasAllOf, nil } @@ -1158,18 +1158,18 @@ func (s *schemaBuilder) scanEmbeddedFields(decl *entityDecl, st *types.Struct, s for i := range st.NumFields() { fld := st.Field(i) if !fld.Anonymous() { - debugLogf("skipping field %q for allOf scan because not anonymous", fld.Name()) + debugLogf(s.ctx.debug, "skipping field %q for allOf scan because not anonymous", fld.Name()) continue } tg := st.Tag(i) - debugLogf( + debugLogf(s.ctx.debug, "maybe allof field(%t) %s: %s (%T) [%q](anon: %t, embedded: %t)", fld.IsField(), fld.Name(), fld.Type().String(), fld.Type(), tg, fld.Anonymous(), fld.Embedded(), ) afld := findASTField(decl.File, fld.Pos()) if afld == nil { - debugLogf("can't find source associated with %s for %s", fld.String(), st.String()) + debugLogf(s.ctx.debug, "can't find source associated with %s for %s", fld.String(), st.String()) continue } @@ -1199,7 +1199,7 @@ func (s *schemaBuilder) scanEmbeddedFields(decl *entityDecl, st *types.Struct, s } if isAliased { - debugLogf("alias member in struct: %v", fld) + debugLogf(s.ctx.debug, "alias member in struct: %v", fld) } // allOf member: fields go into a separate schema, embedded struct becomes an allOf entry @@ -1235,7 +1235,7 @@ func (s *schemaBuilder) processStructField(fld *types.Var, decl *entityDecl, tgt afld := findASTField(decl.File, fld.Pos()) if afld == nil { - debugLogf("can't find source associated with %s", fld.String()) + debugLogf(s.ctx.debug, "can't find source associated with %s", fld.String()) return nil } @@ -1299,7 +1299,7 @@ func (s *schemaBuilder) processStructField(fld *types.Var, decl *entityDecl, tgt } func (s *schemaBuilder) buildAllOf(tpe types.Type, schema *spec.Schema) error { - debugLogf("allOf %s", tpe.Underlying()) + debugLogf(s.ctx.debug, "allOf %s", tpe.Underlying()) switch ftpe := tpe.(type) { case *types.Pointer: @@ -1307,7 +1307,7 @@ func (s *schemaBuilder) buildAllOf(tpe types.Type, schema *spec.Schema) error { case *types.Named: return s.buildNamedAllOf(ftpe, schema) case *types.Alias: - debugLogf("allOf member is alias %v => %v", ftpe, ftpe.Rhs()) + debugLogf(s.ctx.debug, "allOf member is alias %v => %v", ftpe, ftpe.Rhs()) tgt := schemaTypable{schema: schema, skipExt: s.ctx.opts.SkipExtensions} return s.buildAlias(ftpe, tgt) case *types.TypeParam: @@ -1385,7 +1385,7 @@ func (s *schemaBuilder) buildNamedAllOf(ftpe *types.Named, schema *spec.Schema) } func (s *schemaBuilder) buildEmbedded(tpe types.Type, schema *spec.Schema, seen map[string]string) error { - debugLogf("embedded %v", tpe.Underlying()) + debugLogf(s.ctx.debug, "embedded %v", tpe.Underlying()) switch ftpe := tpe.(type) { case *types.Pointer: @@ -1393,7 +1393,7 @@ func (s *schemaBuilder) buildEmbedded(tpe types.Type, schema *spec.Schema, seen case *types.Named: return s.buildNamedEmbedded(ftpe, schema, seen) case *types.Alias: - debugLogf("embedded alias %v => %v", ftpe, ftpe.Rhs()) + debugLogf(s.ctx.debug, "embedded alias %v => %v", ftpe, ftpe.Rhs()) tgt := schemaTypable{schema, 0, s.ctx.opts.SkipExtensions} return s.buildAlias(ftpe, tgt) case *types.Union: // e.g. type X interface{ ~uint16 | ~float32 } @@ -1415,7 +1415,7 @@ func (s *schemaBuilder) buildEmbedded(tpe types.Type, schema *spec.Schema, seen } func (s *schemaBuilder) buildNamedEmbedded(ftpe *types.Named, schema *spec.Schema, seen map[string]string) error { - debugLogf("embedded named type: %T", ftpe.Underlying()) + debugLogf(s.ctx.debug, "embedded named type: %T", ftpe.Underlying()) if unsupportedBuiltin(ftpe) { log.Printf("WARNING: skipped unsupported builtin type: %v", ftpe) diff --git a/taggers.go b/taggers.go index 169b925..1070cc8 100644 --- a/taggers.go +++ b/taggers.go @@ -62,16 +62,16 @@ func parseArrayTypes(sp *sectionedParser, name string, expr ast.Expr, items *spe } // setupRefParamTaggers configures taggers for a parameter that is a $ref. -func setupRefParamTaggers(sp *sectionedParser, ps *spec.Parameter, skipExt bool) { +func setupRefParamTaggers(sp *sectionedParser, ps *spec.Parameter, skipExt, debug bool) { sp.taggers = []tagParser{ newSingleLineTagParser("in", &matchOnlyParam{ps, rxIn}), newSingleLineTagParser("required", &matchOnlyParam{ps, rxRequired}), - newMultiLineTagParser("Extensions", newSetExtensions(spExtensionsSetter(ps, skipExt)), true), + newMultiLineTagParser("Extensions", newSetExtensions(spExtensionsSetter(ps, skipExt), debug), true), } } // setupInlineParamTaggers configures taggers for a fully-defined inline parameter. -func setupInlineParamTaggers(sp *sectionedParser, ps *spec.Parameter, name string, afld *ast.Field, skipExt bool) error { +func setupInlineParamTaggers(sp *sectionedParser, ps *spec.Parameter, name string, afld *ast.Field, skipExt, debug bool) error { sp.taggers = []tagParser{ newSingleLineTagParser("in", &matchOnlyParam{ps, rxIn}), newSingleLineTagParser("maximum", &setMaximum{paramValidations{ps}, rxf(rxMaximumFmt, "")}), @@ -88,7 +88,7 @@ func setupInlineParamTaggers(sp *sectionedParser, ps *spec.Parameter, name strin newSingleLineTagParser("default", &setDefault{&ps.SimpleSchema, paramValidations{ps}, rxf(rxDefaultFmt, "")}), newSingleLineTagParser("example", &setExample{&ps.SimpleSchema, paramValidations{ps}, rxf(rxExampleFmt, "")}), newSingleLineTagParser("required", &setRequiredParam{ps}), - newMultiLineTagParser("Extensions", newSetExtensions(spExtensionsSetter(ps, skipExt)), true), + newMultiLineTagParser("Extensions", newSetExtensions(spExtensionsSetter(ps, skipExt), debug), true), } // check if this is a primitive, if so parse the validations from the