Skip to content

Commit

Permalink
enhancement for PR #1387: evaluate const across packages (#1388)
Browse files Browse the repository at this point in the history
* enhancement: evaluate const across packages
  • Loading branch information
sdghchj committed Nov 22, 2022
1 parent 8117f4c commit e5d507d
Show file tree
Hide file tree
Showing 14 changed files with 229 additions and 181 deletions.
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

0 comments on commit e5d507d

Please sign in to comment.