From 6def489dbc844eb35c40ec82eb33720933d7ddfc Mon Sep 17 00:00:00 2001 From: sdghchj Date: Tue, 29 Nov 2022 00:24:40 +0800 Subject: [PATCH 1/5] enhancement for enums: 1. explicit enum conversion; 2. implicit integer conversion; 3. keep the type of the right operand of a shift operation; 3. parse escape characters. --- const.go | 820 +++++++++++++++++++++++++++++++++ enums_test.go | 9 + package.go | 115 +++-- testdata/enums/consts/const.go | 9 + testdata/enums/main.go | 1 - testdata/enums/types/model.go | 21 +- 6 files changed, 922 insertions(+), 53 deletions(-) diff --git a/const.go b/const.go index d9f970bb1..27f069430 100644 --- a/const.go +++ b/const.go @@ -1,7 +1,13 @@ package swag import ( + "fmt" "go/ast" + "go/token" + "reflect" + "strconv" + "strings" + "unicode/utf8" ) // ConstVariable a model to record a const variable @@ -13,3 +19,817 @@ type ConstVariable struct { File *ast.File Pkg *PackageDefinitions } + +var escapedChars = map[uint8]uint8{ + 'n': '\n', + 'r': '\r', + 't': '\t', + 'v': '\v', + '\\': '\\', + '"': '"', +} + +func EvaluateEscapedChar(text string) rune { + if len(text) == 1 { + return rune(text[0]) + } + + if len(text) == 2 && text[0] == '\\' { + return rune(escapedChars[text[1]]) + } + + if len(text) == 6 && text[0:2] == "\\u" { + n, err := strconv.ParseInt(text[2:], 16, 32) + if err == nil { + return rune(n) + } + } + + return 0 +} + +func EvaluateEscapedString(text string) string { + if !strings.ContainsRune(text, '\\') { + return text + } + result := make([]byte, 0, len(text)) + for i := 0; i < len(text); i++ { + if text[i] == '\\' { + i++ + if text[i] == 'u' { + i++ + n, err := strconv.ParseInt(text[i:i+4], 16, 32) + if err == nil { + result = utf8.AppendRune(result, rune(n)) + } + i += 3 + } else if c, ok := escapedChars[text[i]]; ok { + result = append(result, c) + } + } else { + result = append(result, text[i]) + } + } + return string(result) +} + +func EvaluateDataConversion(x interface{}, typeName string) interface{} { + switch value := x.(type) { + case int: + switch typeName { + case "int": + return int(value) + case "byte": + return byte(value) + case "int8": + return int8(value) + case "int16": + return int16(value) + case "int32": + return int32(value) + case "int64": + return int64(value) + case "uint": + return uint(value) + case "uint8": + return uint8(value) + case "uint16": + return uint16(value) + case "uint32": + return uint32(value) + case "uint64": + return uint64(value) + case "rune": + return rune(value) + } + case uint: + switch typeName { + case "int": + return int(value) + case "byte": + return byte(value) + case "int8": + return int8(value) + case "int16": + return int16(value) + case "int32": + return int32(value) + case "int64": + return int64(value) + case "uint": + return uint(value) + case "uint8": + return uint8(value) + case "uint16": + return uint16(value) + case "uint32": + return uint32(value) + case "uint64": + return uint64(value) + case "rune": + return rune(value) + } + case int8: + switch typeName { + case "int": + return int(value) + case "byte": + return byte(value) + case "int8": + return int8(value) + case "int16": + return int16(value) + case "int32": + return int32(value) + case "int64": + return int64(value) + case "uint": + return uint(value) + case "uint8": + return uint8(value) + case "uint16": + return uint16(value) + case "uint32": + return uint32(value) + case "uint64": + return uint64(value) + case "rune": + return rune(value) + } + case uint8: + switch typeName { + case "int": + return int(value) + case "byte": + return byte(value) + case "int8": + return int8(value) + case "int16": + return int16(value) + case "int32": + return int32(value) + case "int64": + return int64(value) + case "uint": + return uint(value) + case "uint8": + return uint8(value) + case "uint16": + return uint16(value) + case "uint32": + return uint32(value) + case "uint64": + return uint64(value) + case "rune": + return rune(value) + } + case int16: + switch typeName { + case "int": + return int(value) + case "byte": + return byte(value) + case "int8": + return int8(value) + case "int16": + return int16(value) + case "int32": + return int32(value) + case "int64": + return int64(value) + case "uint": + return uint(value) + case "uint8": + return uint8(value) + case "uint16": + return uint16(value) + case "uint32": + return uint32(value) + case "uint64": + return uint64(value) + case "rune": + return rune(value) + } + case uint16: + switch typeName { + case "int": + return int(value) + case "byte": + return byte(value) + case "int8": + return int8(value) + case "int16": + return int16(value) + case "int32": + return int32(value) + case "int64": + return int64(value) + case "uint": + return uint(value) + case "uint8": + return uint8(value) + case "uint16": + return uint16(value) + case "uint32": + return uint32(value) + case "uint64": + return uint64(value) + case "rune": + return rune(value) + } + case int32: + switch typeName { + case "int": + return int(value) + case "byte": + return byte(value) + case "int8": + return int8(value) + case "int16": + return int16(value) + case "int32": + return int32(value) + case "int64": + return int64(value) + case "uint": + return uint(value) + case "uint8": + return uint8(value) + case "uint16": + return uint16(value) + case "uint32": + return uint32(value) + case "uint64": + return uint64(value) + case "rune": + return rune(value) + case "string": + return string(value) + } + case uint32: + switch typeName { + case "int": + return int(value) + case "byte": + return byte(value) + case "int8": + return int8(value) + case "int16": + return int16(value) + case "int32": + return int32(value) + case "int64": + return int64(value) + case "uint": + return uint(value) + case "uint8": + return uint8(value) + case "uint16": + return uint16(value) + case "uint32": + return uint32(value) + case "uint64": + return uint64(value) + case "rune": + return rune(value) + } + case int64: + switch typeName { + case "int": + return int(value) + case "byte": + return byte(value) + case "int8": + return int8(value) + case "int16": + return int16(value) + case "int32": + return int32(value) + case "int64": + return int64(value) + case "uint": + return uint(value) + case "uint8": + return uint8(value) + case "uint16": + return uint16(value) + case "uint32": + return uint32(value) + case "uint64": + return uint64(value) + case "rune": + return rune(value) + } + case uint64: + switch typeName { + case "int": + return int(value) + case "byte": + return byte(value) + case "int8": + return int8(value) + case "int16": + return int16(value) + case "int32": + return int32(value) + case "int64": + return int64(value) + case "uint": + return uint(value) + case "uint8": + return uint8(value) + case "uint16": + return uint16(value) + case "uint32": + return uint32(value) + case "uint64": + return uint64(value) + case "rune": + return rune(value) + } + case string: + switch typeName { + case "string": + return value + } + } + return nil +} + +func EvaluateUnary(x interface{}, operator token.Token, xtype ast.Expr) (interface{}, ast.Expr) { + switch operator { + case token.SUB: + switch value := x.(type) { + case int: + return -value, xtype + case int8: + return -value, xtype + case int16: + return -value, xtype + case int32: + return -value, xtype + case int64: + return -value, xtype + } + case token.XOR: + switch value := x.(type) { + case int: + return ^value, xtype + case int8: + return ^value, xtype + case int16: + return ^value, xtype + case int32: + return ^value, xtype + case int64: + return ^value, xtype + case uint: + return ^value, xtype + case uint8: + return ^value, xtype + case uint16: + return ^value, xtype + case uint32: + return ^value, xtype + case uint64: + return ^value, xtype + } + } + return nil, nil +} + +func EvaluateBinary(x, y interface{}, operator token.Token, xtype, ytype ast.Expr) (interface{}, ast.Expr) { + if operator == token.SHR || operator == token.SHL { + var rightOperand uint64 + yValue := reflect.ValueOf(y) + if yValue.CanUint() { + rightOperand = yValue.Uint() + } else if yValue.CanInt() { + rightOperand = uint64(yValue.Int()) + } + + switch operator { + case token.SHL: + switch xValue := x.(type) { + case int: + return xValue << rightOperand, xtype + case int8: + return xValue << rightOperand, xtype + case int16: + return xValue << rightOperand, xtype + case int32: + return xValue << rightOperand, xtype + case int64: + return xValue << rightOperand, xtype + case uint: + return xValue << rightOperand, xtype + case uint8: + return xValue << rightOperand, xtype + case uint16: + return xValue << rightOperand, xtype + case uint32: + return xValue << rightOperand, xtype + case uint64: + return xValue << rightOperand, xtype + } + case token.SHR: + switch xValue := x.(type) { + case int: + return xValue >> rightOperand, xtype + case int8: + return xValue >> rightOperand, xtype + case int16: + return xValue >> rightOperand, xtype + case int32: + return xValue >> rightOperand, xtype + case int64: + return xValue >> rightOperand, xtype + case uint: + return xValue >> rightOperand, xtype + case uint8: + return xValue >> rightOperand, xtype + case uint16: + return xValue >> rightOperand, xtype + case uint32: + return xValue >> rightOperand, xtype + case uint64: + return xValue >> rightOperand, xtype + } + } + return nil, nil + } + + evalType := xtype + if evalType == nil { + evalType = ytype + } + + xValue := reflect.ValueOf(x) + yValue := reflect.ValueOf(y) + if xValue.Kind() == reflect.String && yValue.Kind() == reflect.String { + return xValue.String() + yValue.String(), evalType + } + + var targetValue reflect.Value + if xValue.Kind() != reflect.Int { + targetValue = reflect.New(xValue.Type()).Elem() + } else { + targetValue = reflect.New(yValue.Type()).Elem() + } + + switch operator { + case token.ADD: + if xValue.CanInt() && yValue.CanInt() { + targetValue.SetInt(xValue.Int() + yValue.Int()) + } else if xValue.CanUint() && yValue.CanUint() { + targetValue.SetUint(xValue.Uint() + yValue.Uint()) + } else if xValue.CanInt() && yValue.CanUint() { + targetValue.SetUint(uint64(xValue.Int()) + yValue.Uint()) + } else if xValue.CanUint() && yValue.CanInt() { + targetValue.SetUint(xValue.Uint() + uint64(yValue.Int())) + } + case token.SUB: + if xValue.CanInt() && yValue.CanInt() { + targetValue.SetInt(xValue.Int() - yValue.Int()) + } else if xValue.CanUint() && yValue.CanUint() { + targetValue.SetUint(xValue.Uint() - yValue.Uint()) + } else if xValue.CanInt() && yValue.CanUint() { + targetValue.SetUint(uint64(xValue.Int()) - yValue.Uint()) + } else if xValue.CanUint() && yValue.CanInt() { + targetValue.SetUint(xValue.Uint() - uint64(yValue.Int())) + } + case token.MUL: + if xValue.CanInt() && yValue.CanInt() { + targetValue.SetInt(xValue.Int() * yValue.Int()) + } else if xValue.CanUint() && yValue.CanUint() { + targetValue.SetUint(xValue.Uint() * yValue.Uint()) + } else if xValue.CanInt() && yValue.CanUint() { + targetValue.SetUint(uint64(xValue.Int()) * yValue.Uint()) + } else if xValue.CanUint() && yValue.CanInt() { + targetValue.SetUint(xValue.Uint() * uint64(yValue.Int())) + } + case token.QUO: + if xValue.CanInt() && yValue.CanInt() { + targetValue.SetInt(xValue.Int() / yValue.Int()) + } else if xValue.CanUint() && yValue.CanUint() { + targetValue.SetUint(xValue.Uint() / yValue.Uint()) + } else if xValue.CanInt() && yValue.CanUint() { + targetValue.SetUint(uint64(xValue.Int()) / yValue.Uint()) + } else if xValue.CanUint() && yValue.CanInt() { + targetValue.SetUint(xValue.Uint() / uint64(yValue.Int())) + } + case token.REM: + if xValue.CanInt() && yValue.CanInt() { + targetValue.SetInt(xValue.Int() % yValue.Int()) + } else if xValue.CanUint() && yValue.CanUint() { + targetValue.SetUint(xValue.Uint() % yValue.Uint()) + } else if xValue.CanInt() && yValue.CanUint() { + targetValue.SetUint(uint64(xValue.Int()) % yValue.Uint()) + } else if xValue.CanUint() && yValue.CanInt() { + targetValue.SetUint(xValue.Uint() % uint64(yValue.Int())) + } + case token.AND: + if xValue.CanInt() && yValue.CanInt() { + targetValue.SetInt(xValue.Int() & yValue.Int()) + } else if xValue.CanUint() && yValue.CanUint() { + targetValue.SetUint(xValue.Uint() & yValue.Uint()) + } else if xValue.CanInt() && yValue.CanUint() { + targetValue.SetUint(uint64(xValue.Int()) & yValue.Uint()) + } else if xValue.CanUint() && yValue.CanInt() { + targetValue.SetUint(xValue.Uint() & uint64(yValue.Int())) + } + case token.OR: + if xValue.CanInt() && yValue.CanInt() { + targetValue.SetInt(xValue.Int() | yValue.Int()) + } else if xValue.CanUint() && yValue.CanUint() { + targetValue.SetUint(xValue.Uint() | yValue.Uint()) + } else if xValue.CanInt() && yValue.CanUint() { + targetValue.SetUint(uint64(xValue.Int()) | yValue.Uint()) + } else if xValue.CanUint() && yValue.CanInt() { + targetValue.SetUint(xValue.Uint() | uint64(yValue.Int())) + } + case token.XOR: + if xValue.CanInt() && yValue.CanInt() { + targetValue.SetInt(xValue.Int() ^ yValue.Int()) + } else if xValue.CanUint() && yValue.CanUint() { + targetValue.SetUint(xValue.Uint() ^ yValue.Uint()) + } else if xValue.CanInt() && yValue.CanUint() { + targetValue.SetUint(uint64(xValue.Int()) ^ yValue.Uint()) + } else if xValue.CanUint() && yValue.CanInt() { + targetValue.SetUint(xValue.Uint() ^ uint64(yValue.Int())) + } + } + return targetValue.Interface(), evalType + + switch operator { + case token.ADD: + switch xValue := x.(type) { + case int: + switch yValue := y.(type) { + case int: + return xValue + yValue, evalType + case int8: + return int8(xValue) + yValue, evalType + case int16: + return int16(xValue) + yValue, evalType + case int32: + return int32(xValue) + yValue, evalType + case int64: + return int64(xValue) + yValue, evalType + case uint: + return uint(xValue) + yValue, evalType + case uint8: + return uint8(xValue) + yValue, evalType + case uint16: + return uint16(xValue) + yValue, evalType + case uint32: + return uint32(xValue) + yValue, evalType + case uint64: + return uint64(xValue) + yValue, evalType + } + return xValue + y.(int), evalType + case int8: + switch yValue := y.(type) { + case int: + return xValue + int8(yValue), evalType + case int8: + return xValue + yValue, evalType + } + return xValue + y.(int8), evalType + case int16: + return xValue + y.(int16), evalType + case int32: + return xValue + y.(int32), evalType + case int64: + return xValue + y.(int64), evalType + case uint: + return xValue + y.(uint), evalType + case uint8: + return xValue + y.(uint8), evalType + case uint16: + return xValue + y.(uint16), evalType + case uint32: + return xValue + y.(uint32), evalType + case uint64: + return xValue + y.(uint64), evalType + case string: + return xValue + y.(string), evalType + } + case token.SUB: + switch xValue := x.(type) { + case int: + return xValue - y.(int), evalType + case int8: + return xValue - y.(int8), evalType + case int16: + return xValue - y.(int16), evalType + case int32: + return xValue - y.(int32), evalType + case int64: + return xValue - y.(int64), evalType + case uint: + return xValue - y.(uint), evalType + case uint8: + return xValue - y.(uint8), evalType + case uint16: + return xValue - y.(uint16), evalType + case uint32: + return xValue - y.(uint32), evalType + case uint64: + return xValue - y.(uint64), evalType + } + case token.MUL: + switch xValue := x.(type) { + case int: + return xValue * y.(int), evalType + case int8: + return xValue * y.(int8), evalType + case int16: + return xValue * y.(int16), evalType + case int32: + return xValue * y.(int32), evalType + case int64: + return xValue * y.(int64), evalType + case uint: + return xValue * y.(uint), evalType + case uint8: + return xValue * y.(uint8), evalType + case uint16: + return xValue * y.(uint16), evalType + case uint32: + return xValue * y.(uint32), evalType + case uint64: + return xValue * y.(uint64), evalType + } + case token.QUO: + switch xValue := x.(type) { + case int: + return xValue / y.(int), evalType + case int8: + return xValue / y.(int8), evalType + case int16: + return xValue / y.(int16), evalType + case int32: + return xValue / y.(int32), evalType + case int64: + return xValue / y.(int64), evalType + case uint: + return xValue / y.(uint), evalType + case uint8: + return xValue / y.(uint8), evalType + case uint16: + return xValue / y.(uint16), evalType + case uint32: + return xValue / y.(uint32), evalType + case uint64: + return xValue / y.(uint64), evalType + } + case token.REM: + switch xValue := x.(type) { + case int: + return xValue % y.(int), evalType + case int8: + return xValue % y.(int8), evalType + case int16: + return xValue % y.(int16), evalType + case int32: + return xValue % y.(int32), evalType + case int64: + return xValue % y.(int64), evalType + case uint: + return xValue % y.(uint), evalType + case uint8: + return xValue % y.(uint8), evalType + case uint16: + return xValue % y.(uint16), evalType + case uint32: + return xValue % y.(uint32), evalType + case uint64: + return xValue % y.(uint64), evalType + } + case token.AND: + switch xValue := x.(type) { + case int: + return xValue & y.(int), evalType + case int8: + return xValue & y.(int8), evalType + case int16: + return xValue & y.(int16), evalType + case int32: + return xValue & y.(int32), evalType + case int64: + return xValue & y.(int64), evalType + case uint: + return xValue & y.(uint), evalType + case uint8: + return xValue & y.(uint8), evalType + case uint16: + return xValue & y.(uint16), evalType + case uint32: + return xValue & y.(uint32), evalType + case uint64: + return xValue & y.(uint64), evalType + } + case token.OR: + switch xValue := x.(type) { + case int: + return xValue | y.(int), evalType + case int8: + return xValue | y.(int8), evalType + case int16: + return xValue | y.(int16), evalType + case int32: + return xValue | y.(int32), evalType + case int64: + return xValue | y.(int64), evalType + case uint: + return xValue | y.(uint), evalType + case uint8: + return xValue | y.(uint8), evalType + case uint16: + return xValue | y.(uint16), evalType + case uint32: + return xValue | y.(uint32), evalType + case uint64: + return xValue | y.(uint64), evalType + } + case token.XOR: + switch xValue := x.(type) { + case int: + return xValue ^ y.(int), evalType + case int8: + return xValue ^ y.(int8), evalType + case int16: + return xValue ^ y.(int16), evalType + case int32: + return xValue ^ y.(int32), evalType + case int64: + return xValue ^ y.(int64), evalType + case uint: + return xValue ^ y.(uint), evalType + case uint8: + return xValue ^ y.(uint8), evalType + case uint16: + return xValue ^ y.(uint16), evalType + case uint32: + return xValue ^ y.(uint32), evalType + case uint64: + return xValue ^ y.(uint64), evalType + } + case token.SHL: + rightOperand, err := strconv.ParseUint(fmt.Sprintf("%v", y), 10, 64) + if err != nil { + panic(err) + } + switch xValue := x.(type) { + case int: + return xValue << rightOperand, xtype + case int8: + return xValue << rightOperand, xtype + case int16: + return xValue << rightOperand, xtype + case int32: + return xValue << rightOperand, xtype + case int64: + return xValue << rightOperand, xtype + case uint: + return xValue << rightOperand, xtype + case uint8: + return xValue << rightOperand, xtype + case uint16: + return xValue << rightOperand, xtype + case uint32: + return xValue << rightOperand, xtype + case uint64: + return xValue << rightOperand, xtype + } + case token.SHR: + rightOperand, err := strconv.ParseUint(fmt.Sprintf("%v", y), 10, 64) + if err != nil { + panic(err) + } + switch xValue := x.(type) { + case int: + return xValue >> rightOperand, xtype + case int8: + return xValue >> rightOperand, xtype + case int16: + return xValue >> rightOperand, xtype + case int32: + return xValue >> rightOperand, xtype + case int64: + return xValue >> rightOperand, xtype + case uint: + return xValue >> rightOperand, xtype + case uint8: + return xValue >> rightOperand, xtype + case uint16: + return xValue >> rightOperand, xtype + case uint32: + return xValue >> rightOperand, xtype + case uint64: + return xValue >> rightOperand, xtype + } + } + return nil, nil +} diff --git a/enums_test.go b/enums_test.go index b77f1d227..dbae10d83 100644 --- a/enums_test.go +++ b/enums_test.go @@ -20,4 +20,13 @@ func TestParseGlobalEnums(t *testing.T) { b, err := json.MarshalIndent(p.swagger, "", " ") assert.NoError(t, err) assert.Equal(t, string(expected), string(b)) + constsPath := "github.com/swaggo/swag/testdata/enums/consts" + assert.Equal(t, 64, p.packages.packages[constsPath].ConstTable["uintSize"].Value) + assert.Equal(t, int32(62), p.packages.packages[constsPath].ConstTable["maxBase"].Value) + assert.Equal(t, 8, p.packages.packages[constsPath].ConstTable["shlByLen"].Value) + assert.Equal(t, 255, p.packages.packages[constsPath].ConstTable["hexnum"].Value) + assert.Equal(t, 15, p.packages.packages[constsPath].ConstTable["octnum"].Value) + assert.Equal(t, `aa\nbb\u8888cc`, p.packages.packages[constsPath].ConstTable["nonescapestr"].Value) + assert.Equal(t, "aa\nbb\u8888cc", p.packages.packages[constsPath].ConstTable["escapestr"].Value) + assert.Equal(t, '\u8888', p.packages.packages[constsPath].ConstTable["escapechar"].Value) } diff --git a/package.go b/package.go index bcce3f65c..08f8b92b3 100644 --- a/package.go +++ b/package.go @@ -3,6 +3,7 @@ package swag import ( "go/ast" "go/token" + "reflect" "strconv" ) @@ -31,6 +32,7 @@ type PackageDefinitions struct { type ConstVariableGlobalEvaluator interface { EvaluateConstValue(pkg *PackageDefinitions, cv *ConstVariable, recursiveStack map[string]struct{}) (interface{}, ast.Expr) EvaluateConstValueByName(file *ast.File, pkgPath, constVariableName string, recursiveStack map[string]struct{}) (interface{}, ast.Expr) + FindTypeSpec(typeName string, file *ast.File) *TypeSpecDef } // NewPackageDefinitions new a PackageDefinitions object @@ -92,68 +94,89 @@ func (pkg *PackageDefinitions) evaluateConstValue(file *ast.File, iota int, expr case *ast.BasicLit: switch valueExpr.Kind { case token.INT: - x, err := strconv.ParseInt(valueExpr.Value, 10, 64) - if err != nil { - return nil, nil + // hexadecimal + if len(valueExpr.Value) > 2 && valueExpr.Value[0] == '0' && valueExpr.Value[1] == 'x' { + if x, err := strconv.ParseInt(valueExpr.Value[2:], 16, 64); err == nil { + return int(x), nil + } else if x, err := strconv.ParseUint(valueExpr.Value[2:], 16, 64); err == nil { + return x, nil + } else { + panic(err) + } + } + + //octet + if len(valueExpr.Value) > 1 && valueExpr.Value[0] == '0' { + if x, err := strconv.ParseInt(valueExpr.Value[1:], 8, 64); err == nil { + return int(x), nil + } else if x, err := strconv.ParseUint(valueExpr.Value[1:], 8, 64); err == nil { + return x, nil + } else { + panic(err) + } + } + + //a basic literal integer is int type in default, or must have an explicit converting type in front + if x, err := strconv.ParseInt(valueExpr.Value, 10, 64); err == nil { + return int(x), nil + } else if x, err := strconv.ParseUint(valueExpr.Value, 10, 64); err == nil { + return x, nil + } else { + panic(err) } - return int(x), nil - case token.STRING, token.CHAR: - return valueExpr.Value[1 : len(valueExpr.Value)-1], nil + case token.STRING: + if valueExpr.Value[0] == '`' { + return valueExpr.Value[1 : len(valueExpr.Value)-1], nil + } + return EvaluateEscapedString(valueExpr.Value[1 : len(valueExpr.Value)-1]), nil + case token.CHAR: + return EvaluateEscapedChar(valueExpr.Value[1 : len(valueExpr.Value)-1]), nil } case *ast.UnaryExpr: x, evalType := pkg.evaluateConstValue(file, iota, valueExpr.X, globalEvaluator, recursiveStack) if x == nil { - return nil, nil - } - switch valueExpr.Op { - case token.SUB: - return -x.(int), evalType - case token.XOR: - return ^(x.(int)), evalType + return x, evalType } + return EvaluateUnary(x, valueExpr.Op, evalType) case *ast.BinaryExpr: x, evalTypex := pkg.evaluateConstValue(file, iota, valueExpr.X, globalEvaluator, recursiveStack) y, evalTypey := pkg.evaluateConstValue(file, iota, valueExpr.Y, globalEvaluator, recursiveStack) if x == nil || y == nil { return nil, nil } - 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 - } + return EvaluateBinary(x, y, valueExpr.Op, evalTypex, evalTypey) case *ast.ParenExpr: return pkg.evaluateConstValue(file, iota, valueExpr.X, globalEvaluator, recursiveStack) case *ast.CallExpr: //data conversion - if ident, ok := valueExpr.Fun.(*ast.Ident); ok && len(valueExpr.Args) == 1 && IsGolangPrimitiveType(ident.Name) { - arg, _ := pkg.evaluateConstValue(file, iota, valueExpr.Args[0], globalEvaluator, recursiveStack) - return arg, nil + if len(valueExpr.Args) != 1 { + return nil, nil + } + arg := valueExpr.Args[0] + if ident, ok := valueExpr.Fun.(*ast.Ident); ok { + name := ident.Name + if name == "uintptr" { + name = "uint" + } + if IsGolangPrimitiveType(name) { + value, _ := pkg.evaluateConstValue(file, iota, arg, globalEvaluator, recursiveStack) + value = EvaluateDataConversion(value, name) + return value, nil + } else if name == "len" { + value, _ := pkg.evaluateConstValue(file, iota, arg, globalEvaluator, recursiveStack) + return reflect.ValueOf(value).Len(), nil + } + typeDef := globalEvaluator.FindTypeSpec(name, file) + if typeDef == nil { + return nil, nil + } + return arg, valueExpr.Fun + } else if selector, ok := valueExpr.Fun.(*ast.SelectorExpr); ok { + typeDef := globalEvaluator.FindTypeSpec(fullTypeName(selector.X.(*ast.Ident).Name, selector.Sel.Name), file) + if typeDef == nil { + return nil, nil + } + return arg, typeDef.TypeSpec.Type } } return nil, nil diff --git a/testdata/enums/consts/const.go b/testdata/enums/consts/const.go index db3ff311e..83dc97fa7 100644 --- a/testdata/enums/consts/const.go +++ b/testdata/enums/consts/const.go @@ -1,3 +1,12 @@ package consts const Base = 1 + +const uintSize = 32 << (^uint(uintptr(0)) >> 63) +const maxBase = 10 + ('z' - 'a' + 1) + ('Z' - 'A' + 1) +const shlByLen = 1 << len("aaa") +const hexnum = 0xFF +const octnum = 017 +const nonescapestr = `aa\nbb\u8888cc` +const escapestr = "aa\nbb\u8888cc" +const escapechar = '\u8888' diff --git a/testdata/enums/main.go b/testdata/enums/main.go index 8238df86d..b3c29b55d 100644 --- a/testdata/enums/main.go +++ b/testdata/enums/main.go @@ -14,5 +14,4 @@ package main // @BasePath /v2 func main() { - } diff --git a/testdata/enums/types/model.go b/testdata/enums/types/model.go index 79a4e83f3..8438a8017 100644 --- a/testdata/enums/types/model.go +++ b/testdata/enums/types/model.go @@ -11,8 +11,8 @@ const ( A Class = consts.Base + (iota+1-1)*2/2%100 - (1&1 | 1) + (2 ^ 2) // AAA B /* BBB */ C - D - F = D + 1 + D = C + 1 + F = Class(5) //G is not enum G = H + 10 //H is not enum @@ -21,13 +21,15 @@ const ( I = int(F + 2) ) +const J = 1 << uint16(I) + type Mask int const ( - Mask1 Mask = 2 << iota >> 1 // Mask1 - Mask2 /* Mask2 */ - Mask3 // Mask3 - Mask4 // Mask4 + Mask1 Mask = 0x02 << iota >> 1 // Mask1 + Mask2 /* Mask2 */ + Mask3 // Mask3 + Mask4 // Mask4 ) type Type string @@ -40,6 +42,13 @@ const ( OtherUnknown = string(Other + Unknown) ) +type Sex rune + +const ( + Male Sex = 'M' + Female = 'F' +) + type Person struct { Name string Class Class From c0edc4c0a7e8c45a15e0574e9db8395d553ac81c Mon Sep 17 00:00:00 2001 From: sdghchj Date: Tue, 29 Nov 2022 01:25:41 +0800 Subject: [PATCH 2/5] fix tests --- const.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/const.go b/const.go index 27f069430..4a9aecb9c 100644 --- a/const.go +++ b/const.go @@ -29,6 +29,7 @@ var escapedChars = map[uint8]uint8{ '"': '"', } +// EvaluateEscapedChar parse escaped character func EvaluateEscapedChar(text string) rune { if len(text) == 1 { return rune(text[0]) @@ -48,6 +49,7 @@ func EvaluateEscapedChar(text string) rune { return 0 } +// EvaluateEscapedString parse escaped characters in string func EvaluateEscapedString(text string) string { if !strings.ContainsRune(text, '\\') { return text @@ -73,6 +75,7 @@ func EvaluateEscapedString(text string) string { return string(result) } +// EvaluateDataConversion evaluate the type a explicit type conversion func EvaluateDataConversion(x interface{}, typeName string) interface{} { switch value := x.(type) { case int: @@ -356,6 +359,7 @@ func EvaluateDataConversion(x interface{}, typeName string) interface{} { return nil } +// EvaluateUnary evaluate the type and value of a unary expression func EvaluateUnary(x interface{}, operator token.Token, xtype ast.Expr) (interface{}, ast.Expr) { switch operator { case token.SUB: @@ -398,6 +402,7 @@ func EvaluateUnary(x interface{}, operator token.Token, xtype ast.Expr) (interfa return nil, nil } +// EvaluateBinary evaluate the type and value of a binary expression func EvaluateBinary(x, y interface{}, operator token.Token, xtype, ytype ast.Expr) (interface{}, ast.Expr) { if operator == token.SHR || operator == token.SHL { var rightOperand uint64 From 63ff96aebdf273cc796ddb97ccba469f5efcc56e Mon Sep 17 00:00:00 2001 From: sdghchj Date: Tue, 29 Nov 2022 12:18:03 +0800 Subject: [PATCH 3/5] fix tests for go version difference --- const.go | 284 +------------------------------------------------ utils_go18.go | 30 ++++++ utils_other.go | 41 +++++++ 3 files changed, 76 insertions(+), 279 deletions(-) create mode 100644 utils_go18.go create mode 100644 utils_other.go diff --git a/const.go b/const.go index 4a9aecb9c..1353b6d1d 100644 --- a/const.go +++ b/const.go @@ -1,13 +1,11 @@ package swag import ( - "fmt" "go/ast" "go/token" "reflect" "strconv" "strings" - "unicode/utf8" ) // ConstVariable a model to record a const variable @@ -60,9 +58,9 @@ func EvaluateEscapedString(text string) string { i++ if text[i] == 'u' { i++ - n, err := strconv.ParseInt(text[i:i+4], 16, 32) + char, err := strconv.ParseInt(text[i:i+4], 16, 32) if err == nil { - result = utf8.AppendRune(result, rune(n)) + result = AppendUtf8Rune(result, rune(char)) } i += 3 } else if c, ok := escapedChars[text[i]]; ok { @@ -406,7 +404,7 @@ func EvaluateUnary(x interface{}, operator token.Token, xtype ast.Expr) (interfa func EvaluateBinary(x, y interface{}, operator token.Token, xtype, ytype ast.Expr) (interface{}, ast.Expr) { if operator == token.SHR || operator == token.SHL { var rightOperand uint64 - yValue := reflect.ValueOf(y) + yValue := CanIntegerValue{reflect.ValueOf(y)} if yValue.CanUint() { rightOperand = yValue.Uint() } else if yValue.CanInt() { @@ -469,8 +467,8 @@ func EvaluateBinary(x, y interface{}, operator token.Token, xtype, ytype ast.Exp evalType = ytype } - xValue := reflect.ValueOf(x) - yValue := reflect.ValueOf(y) + xValue := CanIntegerValue{reflect.ValueOf(x)} + yValue := CanIntegerValue{reflect.ValueOf(y)} if xValue.Kind() == reflect.String && yValue.Kind() == reflect.String { return xValue.String() + yValue.String(), evalType } @@ -565,276 +563,4 @@ func EvaluateBinary(x, y interface{}, operator token.Token, xtype, ytype ast.Exp } } return targetValue.Interface(), evalType - - switch operator { - case token.ADD: - switch xValue := x.(type) { - case int: - switch yValue := y.(type) { - case int: - return xValue + yValue, evalType - case int8: - return int8(xValue) + yValue, evalType - case int16: - return int16(xValue) + yValue, evalType - case int32: - return int32(xValue) + yValue, evalType - case int64: - return int64(xValue) + yValue, evalType - case uint: - return uint(xValue) + yValue, evalType - case uint8: - return uint8(xValue) + yValue, evalType - case uint16: - return uint16(xValue) + yValue, evalType - case uint32: - return uint32(xValue) + yValue, evalType - case uint64: - return uint64(xValue) + yValue, evalType - } - return xValue + y.(int), evalType - case int8: - switch yValue := y.(type) { - case int: - return xValue + int8(yValue), evalType - case int8: - return xValue + yValue, evalType - } - return xValue + y.(int8), evalType - case int16: - return xValue + y.(int16), evalType - case int32: - return xValue + y.(int32), evalType - case int64: - return xValue + y.(int64), evalType - case uint: - return xValue + y.(uint), evalType - case uint8: - return xValue + y.(uint8), evalType - case uint16: - return xValue + y.(uint16), evalType - case uint32: - return xValue + y.(uint32), evalType - case uint64: - return xValue + y.(uint64), evalType - case string: - return xValue + y.(string), evalType - } - case token.SUB: - switch xValue := x.(type) { - case int: - return xValue - y.(int), evalType - case int8: - return xValue - y.(int8), evalType - case int16: - return xValue - y.(int16), evalType - case int32: - return xValue - y.(int32), evalType - case int64: - return xValue - y.(int64), evalType - case uint: - return xValue - y.(uint), evalType - case uint8: - return xValue - y.(uint8), evalType - case uint16: - return xValue - y.(uint16), evalType - case uint32: - return xValue - y.(uint32), evalType - case uint64: - return xValue - y.(uint64), evalType - } - case token.MUL: - switch xValue := x.(type) { - case int: - return xValue * y.(int), evalType - case int8: - return xValue * y.(int8), evalType - case int16: - return xValue * y.(int16), evalType - case int32: - return xValue * y.(int32), evalType - case int64: - return xValue * y.(int64), evalType - case uint: - return xValue * y.(uint), evalType - case uint8: - return xValue * y.(uint8), evalType - case uint16: - return xValue * y.(uint16), evalType - case uint32: - return xValue * y.(uint32), evalType - case uint64: - return xValue * y.(uint64), evalType - } - case token.QUO: - switch xValue := x.(type) { - case int: - return xValue / y.(int), evalType - case int8: - return xValue / y.(int8), evalType - case int16: - return xValue / y.(int16), evalType - case int32: - return xValue / y.(int32), evalType - case int64: - return xValue / y.(int64), evalType - case uint: - return xValue / y.(uint), evalType - case uint8: - return xValue / y.(uint8), evalType - case uint16: - return xValue / y.(uint16), evalType - case uint32: - return xValue / y.(uint32), evalType - case uint64: - return xValue / y.(uint64), evalType - } - case token.REM: - switch xValue := x.(type) { - case int: - return xValue % y.(int), evalType - case int8: - return xValue % y.(int8), evalType - case int16: - return xValue % y.(int16), evalType - case int32: - return xValue % y.(int32), evalType - case int64: - return xValue % y.(int64), evalType - case uint: - return xValue % y.(uint), evalType - case uint8: - return xValue % y.(uint8), evalType - case uint16: - return xValue % y.(uint16), evalType - case uint32: - return xValue % y.(uint32), evalType - case uint64: - return xValue % y.(uint64), evalType - } - case token.AND: - switch xValue := x.(type) { - case int: - return xValue & y.(int), evalType - case int8: - return xValue & y.(int8), evalType - case int16: - return xValue & y.(int16), evalType - case int32: - return xValue & y.(int32), evalType - case int64: - return xValue & y.(int64), evalType - case uint: - return xValue & y.(uint), evalType - case uint8: - return xValue & y.(uint8), evalType - case uint16: - return xValue & y.(uint16), evalType - case uint32: - return xValue & y.(uint32), evalType - case uint64: - return xValue & y.(uint64), evalType - } - case token.OR: - switch xValue := x.(type) { - case int: - return xValue | y.(int), evalType - case int8: - return xValue | y.(int8), evalType - case int16: - return xValue | y.(int16), evalType - case int32: - return xValue | y.(int32), evalType - case int64: - return xValue | y.(int64), evalType - case uint: - return xValue | y.(uint), evalType - case uint8: - return xValue | y.(uint8), evalType - case uint16: - return xValue | y.(uint16), evalType - case uint32: - return xValue | y.(uint32), evalType - case uint64: - return xValue | y.(uint64), evalType - } - case token.XOR: - switch xValue := x.(type) { - case int: - return xValue ^ y.(int), evalType - case int8: - return xValue ^ y.(int8), evalType - case int16: - return xValue ^ y.(int16), evalType - case int32: - return xValue ^ y.(int32), evalType - case int64: - return xValue ^ y.(int64), evalType - case uint: - return xValue ^ y.(uint), evalType - case uint8: - return xValue ^ y.(uint8), evalType - case uint16: - return xValue ^ y.(uint16), evalType - case uint32: - return xValue ^ y.(uint32), evalType - case uint64: - return xValue ^ y.(uint64), evalType - } - case token.SHL: - rightOperand, err := strconv.ParseUint(fmt.Sprintf("%v", y), 10, 64) - if err != nil { - panic(err) - } - switch xValue := x.(type) { - case int: - return xValue << rightOperand, xtype - case int8: - return xValue << rightOperand, xtype - case int16: - return xValue << rightOperand, xtype - case int32: - return xValue << rightOperand, xtype - case int64: - return xValue << rightOperand, xtype - case uint: - return xValue << rightOperand, xtype - case uint8: - return xValue << rightOperand, xtype - case uint16: - return xValue << rightOperand, xtype - case uint32: - return xValue << rightOperand, xtype - case uint64: - return xValue << rightOperand, xtype - } - case token.SHR: - rightOperand, err := strconv.ParseUint(fmt.Sprintf("%v", y), 10, 64) - if err != nil { - panic(err) - } - switch xValue := x.(type) { - case int: - return xValue >> rightOperand, xtype - case int8: - return xValue >> rightOperand, xtype - case int16: - return xValue >> rightOperand, xtype - case int32: - return xValue >> rightOperand, xtype - case int64: - return xValue >> rightOperand, xtype - case uint: - return xValue >> rightOperand, xtype - case uint8: - return xValue >> rightOperand, xtype - case uint16: - return xValue >> rightOperand, xtype - case uint32: - return xValue >> rightOperand, xtype - case uint64: - return xValue >> rightOperand, xtype - } - } - return nil, nil } diff --git a/utils_go18.go b/utils_go18.go new file mode 100644 index 000000000..450f6baa4 --- /dev/null +++ b/utils_go18.go @@ -0,0 +1,30 @@ +//go:build go1.18 +// +build go1.18 + +package swag + +import ( + "reflect" + "unicode/utf8" +) + +// AppendUtf8Rune appends the UTF-8 encoding of r to the end of p and +// returns the extended buffer. If the rune is out of range, +// it appends the encoding of RuneError. +func AppendUtf8Rune(p []byte, r rune) []byte { + return utf8.AppendRune(p, r) +} + +type CanIntegerValue struct { + reflect.Value +} + +// CanInt reports whether Uint can be used without panicking. +func (v CanIntegerValue) CanInt() bool { + return v.Value.CanInt() +} + +// CanUint reports whether Uint can be used without panicking. +func (v CanIntegerValue) CanUint() bool { + return v.Value.CanUint() +} diff --git a/utils_other.go b/utils_other.go new file mode 100644 index 000000000..6f823cfc9 --- /dev/null +++ b/utils_other.go @@ -0,0 +1,41 @@ +//go:build !go1.18 +// +build !go1.18 + +package swag + +// AppendUtf8Rune appends the UTF-8 encoding of r to the end of p and +// returns the extended buffer. If the rune is out of range, +// it appends the encoding of RuneError. +func AppendUtf8Rune(p []byte, r rune) []byte { + length := utf8.RuneLen(rune(r)) + if length > 0 { + utf8Slice := make([]byte, length) + utf8.EncodeRune(utf8Slice, rune(r)) + p = append(p, utf8Slice...) + } + return p +} + +type CanIntegerValue struct { + reflect.Value +} + +// CanInt reports whether Uint can be used without panicking. +func (v CanIntegerValue) CanInt() bool { + switch v.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return true + default: + return false + } +} + +// CanUint reports whether Uint can be used without panicking. +func (v CanIntegerValue) CanUint() bool { + switch v.Kind() { + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return true + default: + return false + } +} From e38165f6131efa027e3214bbc9c500615139feaa Mon Sep 17 00:00:00 2001 From: sdghchj Date: Tue, 29 Nov 2022 12:52:59 +0800 Subject: [PATCH 4/5] fix tests for go version difference --- utils_go18.go | 1 + utils_other.go | 1 + 2 files changed, 2 insertions(+) diff --git a/utils_go18.go b/utils_go18.go index 450f6baa4..814f93433 100644 --- a/utils_go18.go +++ b/utils_go18.go @@ -15,6 +15,7 @@ func AppendUtf8Rune(p []byte, r rune) []byte { return utf8.AppendRune(p, r) } +// CanIntegerValue a wrapper of reflect.Value type CanIntegerValue struct { reflect.Value } diff --git a/utils_other.go b/utils_other.go index 6f823cfc9..1db12d3ba 100644 --- a/utils_other.go +++ b/utils_other.go @@ -16,6 +16,7 @@ func AppendUtf8Rune(p []byte, r rune) []byte { return p } +// CanIntegerValue a wrapper of reflect.Value type CanIntegerValue struct { reflect.Value } From 939c8d6d309a194baa5910c4812b64937e9d9c24 Mon Sep 17 00:00:00 2001 From: sdghchj Date: Tue, 29 Nov 2022 13:02:46 +0800 Subject: [PATCH 5/5] fix tests for go version difference --- utils_other.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/utils_other.go b/utils_other.go index 1db12d3ba..531c0df12 100644 --- a/utils_other.go +++ b/utils_other.go @@ -3,6 +3,11 @@ package swag +import ( + "reflect" + "unicode/utf8" +) + // AppendUtf8Rune appends the UTF-8 encoding of r to the end of p and // returns the extended buffer. If the rune is out of range, // it appends the encoding of RuneError.