Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 28 additions & 29 deletions application.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -59,11 +46,14 @@ 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
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
}
Expand Down Expand Up @@ -110,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
}

Expand Down Expand Up @@ -307,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
}

Expand Down Expand Up @@ -556,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),
Expand Down Expand Up @@ -589,6 +587,7 @@ type typeIndex struct {
setXNullableForPointers bool
refAliases bool
transparentAliases bool
debug bool
}

func (a *typeIndex) build(pkgs []*packages.Package) error {
Expand All @@ -610,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
}

Expand Down Expand Up @@ -653,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)
Expand Down Expand Up @@ -687,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
}
Expand Down Expand Up @@ -729,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",
Expand Down Expand Up @@ -827,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...))
}
}
3 changes: 2 additions & 1 deletion application_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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
Expand All @@ -122,6 +122,7 @@ func loadClassificationPkgsCtx(t *testing.T) *scanCtx {
"./goparsing/classification/operations",
},
WorkDir: "fixtures",
Debug: enableDebug,
})
require.NoError(t, err)
classificationCtx = sctx
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
35 changes: 18 additions & 17 deletions parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
)

type paramTypable struct {
param *spec.Parameter
param *spec.Parameter
skipExt bool
}

func (pt paramTypable) In() string { return pt.param.In }
Expand All @@ -28,7 +29,7 @@
}

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
Expand Down Expand Up @@ -184,7 +185,7 @@
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:
Expand All @@ -208,7 +209,7 @@
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)
Expand All @@ -224,7 +225,7 @@

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)
}
Expand Down Expand Up @@ -284,7 +285,7 @@
}

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:
Expand All @@ -304,7 +305,7 @@
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

Check notice on line 308 in parameters.go

View check run for this annotation

codefactor.io / CodeFactor

parameters.go#L308

Warning comment detected, consider resolving the issue. (warning-comment)
return p.buildFieldAlias(ftpe, typable, fld, seen)
default:
return fmt.Errorf("unknown type for %s: %T: %w", fld.String(), fld.Type(), ErrCodeScan)
Expand Down Expand Up @@ -336,7 +337,7 @@
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
}

Expand Down Expand Up @@ -469,10 +470,10 @@
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)
}
}
}
Expand Down Expand Up @@ -522,13 +523,13 @@
// 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
}

Expand Down Expand Up @@ -559,9 +560,9 @@

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) {
Expand All @@ -586,9 +587,9 @@
}

if ps.Ref.String() != "" {
setupRefParamTaggers(sp, &ps)
setupRefParamTaggers(sp, &ps, p.ctx.opts.SkipExtensions, p.ctx.debug)
} else {
if err := setupInlineParamTaggers(sp, &ps, name, afld); err != nil {
if err := setupInlineParamTaggers(sp, &ps, name, afld, p.ctx.opts.SkipExtensions, p.ctx.debug); err != nil {
return "", err
}
}
Expand All @@ -605,7 +606,7 @@
}

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
Expand Down
14 changes: 8 additions & 6 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)))
}
}

Expand Down
Loading
Loading