Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enhancement for PR #1387: evaluate const across packages #1388

Merged
merged 7 commits into from Nov 22, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
113 changes: 3 additions & 110 deletions const.go
Expand Up @@ -2,121 +2,14 @@ package swag

import (
"go/ast"
"go/token"
"strconv"
)

// ConstVariable a model to record an const variable
// ConstVariable a model to record a const variable
type ConstVariable struct {
Name *ast.Ident
Type ast.Expr
Value interface{}
Comment *ast.CommentGroup
}

// EvaluateValue evaluate the value
func (cv *ConstVariable) EvaluateValue(constTable map[string]*ConstVariable) interface{} {
if expr, ok := cv.Value.(ast.Expr); ok {
value, evalType := evaluateConstValue(cv.Name.Name, cv.Name.Obj.Data.(int), expr, constTable, make(map[string]struct{}))
if cv.Type == nil && evalType != nil {
cv.Type = evalType
}
if value != nil {
cv.Value = value
}
return value
}
return cv.Value
}

func evaluateConstValue(name string, iota int, expr ast.Expr, constTable map[string]*ConstVariable, recursiveStack map[string]struct{}) (interface{}, ast.Expr) {
if len(name) > 0 {
if _, ok := recursiveStack[name]; ok {
return nil, nil
}
recursiveStack[name] = struct{}{}
}

switch valueExpr := expr.(type) {
case *ast.Ident:
if valueExpr.Name == "iota" {
return iota, nil
}
if constTable != nil {
if cv, ok := constTable[valueExpr.Name]; ok {
if expr, ok = cv.Value.(ast.Expr); ok {
value, evalType := evaluateConstValue(valueExpr.Name, cv.Name.Obj.Data.(int), expr, constTable, recursiveStack)
if cv.Type == nil {
cv.Type = evalType
}
if value != nil {
cv.Value = value
}
return value, evalType
}
return cv.Value, cv.Type
}
}
case *ast.BasicLit:
switch valueExpr.Kind {
case token.INT:
x, err := strconv.ParseInt(valueExpr.Value, 10, 64)
if err != nil {
return nil, nil
}
return int(x), nil
case token.STRING, token.CHAR:
return valueExpr.Value[1 : len(valueExpr.Value)-1], nil
}
case *ast.UnaryExpr:
x, evalType := evaluateConstValue("", iota, valueExpr.X, constTable, recursiveStack)
switch valueExpr.Op {
case token.SUB:
return -x.(int), evalType
case token.XOR:
return ^(x.(int)), evalType
}
case *ast.BinaryExpr:
x, evalTypex := evaluateConstValue("", iota, valueExpr.X, constTable, recursiveStack)
y, evalTypey := evaluateConstValue("", iota, valueExpr.Y, constTable, recursiveStack)
evalType := evalTypex
if evalType == nil {
evalType = evalTypey
}
switch valueExpr.Op {
case token.ADD:
if ix, ok := x.(int); ok {
return ix + y.(int), evalType
} else if sx, ok := x.(string); ok {
return sx + y.(string), evalType
}
case token.SUB:
return x.(int) - y.(int), evalType
case token.MUL:
return x.(int) * y.(int), evalType
case token.QUO:
return x.(int) / y.(int), evalType
case token.REM:
return x.(int) % y.(int), evalType
case token.AND:
return x.(int) & y.(int), evalType
case token.OR:
return x.(int) | y.(int), evalType
case token.XOR:
return x.(int) ^ y.(int), evalType
case token.SHL:
return x.(int) << y.(int), evalType
case token.SHR:
return x.(int) >> y.(int), evalType
}
case *ast.ParenExpr:
return evaluateConstValue("", iota, valueExpr.X, constTable, recursiveStack)
case *ast.CallExpr:
//data conversion
if ident, ok := valueExpr.Fun.(*ast.Ident); ok && len(valueExpr.Args) == 1 && IsGolangPrimitiveType(ident.Name) {
arg, _ := evaluateConstValue("", iota, valueExpr.Args[0], constTable, recursiveStack)
return arg, nil
}
}
return nil, nil
File *ast.File
Pkg *PackageDefinitions
}
2 changes: 1 addition & 1 deletion enums.go
Expand Up @@ -5,7 +5,7 @@ const (
enumCommentsExtension = "x-enum-comments"
)

// EnumValue a model to record an enum const variable
// EnumValue a model to record an enum consts variable
type EnumValue struct {
key string
Value interface{}
Expand Down
5 changes: 3 additions & 2 deletions gen/gen.go
Expand Up @@ -162,7 +162,9 @@ func (g *Gen) Build(config *Config) error {

g.debug.Printf("Generate swagger docs....")

p := swag.New(swag.SetMarkdownFileDirectory(config.MarkdownFilesDir),
p := swag.New(
swag.SetParseDependency(config.ParseDependency),
swag.SetMarkdownFileDirectory(config.MarkdownFilesDir),
swag.SetDebugger(config.Debugger),
swag.SetExcludedDirsAndFiles(config.Excludes),
swag.SetCodeExamplesDirectory(config.CodeExampleFilesDir),
Expand All @@ -174,7 +176,6 @@ func (g *Gen) Build(config *Config) error {

p.PropNamingStrategy = config.PropNamingStrategy
p.ParseVendor = config.ParseVendor
p.ParseDependency = config.ParseDependency
p.ParseInternal = config.ParseInternal
p.RequiredByDefault = config.RequiredByDefault

Expand Down
16 changes: 8 additions & 8 deletions generics.go
Expand Up @@ -26,7 +26,7 @@ func (t *genericTypeSpec) TypeName() string {
return t.Name
}

func (pkgDefs *PackagesDefinitions) parametrizeGenericType(file *ast.File, original *TypeSpecDef, fullGenericForm string, parseDependency bool) *TypeSpecDef {
func (pkgDefs *PackagesDefinitions) parametrizeGenericType(file *ast.File, original *TypeSpecDef, fullGenericForm string) *TypeSpecDef {
if original == nil || original.TypeSpec.TypeParams == nil || len(original.TypeSpec.TypeParams.List) == 0 {
return original
}
Expand All @@ -51,7 +51,7 @@ func (pkgDefs *PackagesDefinitions) parametrizeGenericType(file *ast.File, origi
arrayDepth++
}

typeDef := pkgDefs.FindTypeSpec(genericParam, file, parseDependency)
typeDef := pkgDefs.FindTypeSpec(genericParam, file)
if typeDef != nil {
genericParam = typeDef.TypeName()
if _, ok := pkgDefs.uniqueDefinitions[genericParam]; !ok {
Expand Down Expand Up @@ -95,7 +95,7 @@ func (pkgDefs *PackagesDefinitions) parametrizeGenericType(file *ast.File, origi
NamePos: original.TypeSpec.Name.NamePos,
Obj: original.TypeSpec.Name.Obj,
},
Type: pkgDefs.resolveGenericType(original.File, original.TypeSpec.Type, genericParamTypeDefs, parseDependency),
Type: pkgDefs.resolveGenericType(original.File, original.TypeSpec.Type, genericParamTypeDefs),
Doc: original.TypeSpec.Doc,
Assign: original.TypeSpec.Assign,
},
Expand Down Expand Up @@ -159,7 +159,7 @@ func (pkgDefs *PackagesDefinitions) getParametrizedType(genTypeSpec *genericType
return &ast.Ident{Name: genTypeSpec.Name}
}

func (pkgDefs *PackagesDefinitions) resolveGenericType(file *ast.File, expr ast.Expr, genericParamTypeDefs map[string]*genericTypeSpec, parseDependency bool) ast.Expr {
func (pkgDefs *PackagesDefinitions) resolveGenericType(file *ast.File, expr ast.Expr, genericParamTypeDefs map[string]*genericTypeSpec) ast.Expr {
switch astExpr := expr.(type) {
case *ast.Ident:
if genTypeSpec, ok := genericParamTypeDefs[astExpr.Name]; ok {
Expand All @@ -171,18 +171,18 @@ func (pkgDefs *PackagesDefinitions) resolveGenericType(file *ast.File, expr ast.
}
case *ast.ArrayType:
return &ast.ArrayType{
Elt: pkgDefs.resolveGenericType(file, astExpr.Elt, genericParamTypeDefs, parseDependency),
Elt: pkgDefs.resolveGenericType(file, astExpr.Elt, genericParamTypeDefs),
Len: astExpr.Len,
Lbrack: astExpr.Lbrack,
}
case *ast.StarExpr:
return &ast.StarExpr{
Star: astExpr.Star,
X: pkgDefs.resolveGenericType(file, astExpr.X, genericParamTypeDefs, parseDependency),
X: pkgDefs.resolveGenericType(file, astExpr.X, genericParamTypeDefs),
}
case *ast.IndexExpr, *ast.IndexListExpr:
fullGenericName, _ := getGenericFieldType(file, expr, genericParamTypeDefs)
typeDef := pkgDefs.FindTypeSpec(fullGenericName, file, parseDependency)
typeDef := pkgDefs.FindTypeSpec(fullGenericName, file)
if typeDef != nil {
return typeDef.TypeSpec.Type
}
Expand All @@ -205,7 +205,7 @@ func (pkgDefs *PackagesDefinitions) resolveGenericType(file *ast.File, expr ast.
Comment: field.Comment,
}

newField.Type = pkgDefs.resolveGenericType(file, field.Type, genericParamTypeDefs, parseDependency)
newField.Type = pkgDefs.resolveGenericType(file, field.Type, genericParamTypeDefs)

newStructTypeDef.Fields.List = append(newStructTypeDef.Fields.List, newField)
}
Expand Down
2 changes: 1 addition & 1 deletion generics_other.go
Expand Up @@ -15,7 +15,7 @@ type genericTypeSpec struct {
Name string
}

func (pkgDefs *PackagesDefinitions) parametrizeGenericType(file *ast.File, original *TypeSpecDef, fullGenericForm string, parseDependency bool) *TypeSpecDef {
func (pkgDefs *PackagesDefinitions) parametrizeGenericType(file *ast.File, original *TypeSpecDef, fullGenericForm string) *TypeSpecDef {
return original
}

Expand Down
4 changes: 2 additions & 2 deletions generics_other_test.go
Expand Up @@ -32,10 +32,10 @@ func TestParametrizeStruct(t *testing.T) {
},
}

tr := pd.parametrizeGenericType(&ast.File{}, tSpec, "", false)
tr := pd.parametrizeGenericType(&ast.File{}, tSpec, "")
assert.Equal(t, tr, tSpec)

tr = pd.parametrizeGenericType(&ast.File{}, tSpec, "", true)
tr = pd.parametrizeGenericType(&ast.File{}, tSpec, "")
assert.Equal(t, tr, tSpec)
}

Expand Down
15 changes: 7 additions & 8 deletions generics_test.go
Expand Up @@ -110,8 +110,7 @@ func TestParseGenericsPackageAlias(t *testing.T) {
expected, err := os.ReadFile(filepath.Join(searchDir, "expected.json"))
assert.NoError(t, err)

p := New()
p.ParseDependency = true
p := New(SetParseDependency(true))
err = p.ParseAPI(searchDir, mainAPIFile, defaultParseDepth)
assert.NoError(t, err)
b, err := json.MarshalIndent(p.swagger, "", " ")
Expand All @@ -133,7 +132,7 @@ func TestParametrizeStruct(t *testing.T) {
Name: &ast.Ident{Name: "Field"},
TypeParams: &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{{Name: "T"}}}, {Names: []*ast.Ident{{Name: "T2"}}}}},
Type: &ast.StructType{Struct: 100, Fields: &ast.FieldList{Opening: 101, Closing: 102}},
}}, "test.Field[string, []string]", false)
}}, "test.Field[string, []string]")
assert.NotNil(t, typeSpec)
assert.Equal(t, "$test.Field-string-array_string", typeSpec.Name())
assert.Equal(t, "test.Field-string-array_string", typeSpec.TypeName())
Expand All @@ -146,7 +145,7 @@ func TestParametrizeStruct(t *testing.T) {
Name: &ast.Ident{Name: "Field"},
TypeParams: &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{{Name: "T"}}}}},
Type: &ast.StructType{Struct: 100, Fields: &ast.FieldList{Opening: 101, Closing: 102}},
}}, "test.Field[string, string]", false)
}}, "test.Field[string, string]")
assert.Nil(t, typeSpec)

// definition contains two type params, but only one is used
Expand All @@ -157,7 +156,7 @@ func TestParametrizeStruct(t *testing.T) {
Name: &ast.Ident{Name: "Field"},
TypeParams: &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{{Name: "T"}}}, {Names: []*ast.Ident{{Name: "T2"}}}}},
Type: &ast.StructType{Struct: 100, Fields: &ast.FieldList{Opening: 101, Closing: 102}},
}}, "test.Field[string]", false)
}}, "test.Field[string]")
assert.Nil(t, typeSpec)

// name is not a valid type name
Expand All @@ -168,7 +167,7 @@ func TestParametrizeStruct(t *testing.T) {
Name: &ast.Ident{Name: "Field"},
TypeParams: &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{{Name: "T"}}}, {Names: []*ast.Ident{{Name: "T2"}}}}},
Type: &ast.StructType{Struct: 100, Fields: &ast.FieldList{Opening: 101, Closing: 102}},
}}, "test.Field[string", false)
}}, "test.Field[string")
assert.Nil(t, typeSpec)

typeSpec = pd.parametrizeGenericType(
Expand All @@ -178,7 +177,7 @@ func TestParametrizeStruct(t *testing.T) {
Name: &ast.Ident{Name: "Field"},
TypeParams: &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{{Name: "T"}}}, {Names: []*ast.Ident{{Name: "T2"}}}}},
Type: &ast.StructType{Struct: 100, Fields: &ast.FieldList{Opening: 101, Closing: 102}},
}}, "test.Field[string, [string]", false)
}}, "test.Field[string, [string]")
assert.Nil(t, typeSpec)

typeSpec = pd.parametrizeGenericType(
Expand All @@ -188,7 +187,7 @@ func TestParametrizeStruct(t *testing.T) {
Name: &ast.Ident{Name: "Field"},
TypeParams: &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{{Name: "T"}}}, {Names: []*ast.Ident{{Name: "T2"}}}}},
Type: &ast.StructType{Struct: 100, Fields: &ast.FieldList{Opening: 101, Closing: 102}},
}}, "test.Field[string, ]string]", false)
}}, "test.Field[string, ]string]")
assert.Nil(t, typeSpec)
}

Expand Down