This commit is contained in:
2026-04-01 10:53:51 +03:00
parent 281af49d27
commit c1890216c5
16 changed files with 722 additions and 62 deletions
+67 -7
View File
@@ -55,6 +55,8 @@ func parseASTFile(fset *token.FileSet, f *ast.File) (Schema, error) {
knownStructs := map[string]bool{}
namedPrimitives := map[string]PrimitiveKind{}
unsupportedNamedPrimitives := map[string]string{}
unresolvedNamedPrimitives := map[string]string{}
var enumOrder []string
for _, decl := range f.Decls {
@@ -73,8 +75,14 @@ func parseASTFile(fset *token.FileSet, f *ast.File) (Schema, error) {
case *ast.StructType:
knownStructs[typeSpec.Name.Name] = true
case *ast.Ident:
switch t.Name {
case "int", "uint", "uintptr":
unsupportedNamedPrimitives[typeSpec.Name.Name] = t.Name
continue
}
primKind, isPrimitive := goPrimitiveKind(t.Name)
if !isPrimitive {
unresolvedNamedPrimitives[typeSpec.Name.Name] = t.Name
continue
}
namedPrimitives[typeSpec.Name.Name] = primKind
@@ -85,6 +93,19 @@ func parseASTFile(fset *token.FileSet, f *ast.File) (Schema, error) {
}
}
for changed := true; changed; {
changed = false
for name, target := range unresolvedNamedPrimitives {
if _, ok := unsupportedNamedPrimitives[name]; ok {
continue
}
if baseType, ok := unsupportedNamedPrimitives[target]; ok {
unsupportedNamedPrimitives[name] = baseType
changed = true
}
}
}
info, err := typeCheckFile(fset, f)
if err != nil {
return Schema{}, err
@@ -119,7 +140,14 @@ func parseASTFile(fset *token.FileSet, f *ast.File) (Schema, error) {
continue
}
msg, err := parseStruct(pkgName, typeSpec.Name.Name, structType, knownStructs, namedPrimitives)
msg, err := parseStruct(
pkgName,
typeSpec.Name.Name,
structType,
knownStructs,
namedPrimitives,
unsupportedNamedPrimitives,
)
if err != nil {
return Schema{}, fmt.Errorf("struct %s: %w", typeSpec.Name.Name, err)
}
@@ -187,6 +215,7 @@ func parseStruct(
st *ast.StructType,
knownStructs map[string]bool,
namedPrimitives map[string]PrimitiveKind,
unsupportedNamedPrimitives map[string]string,
) (Message, error) {
msg := Message{PackageName: pkg, Name: name}
@@ -202,7 +231,14 @@ func parseStruct(
}
for _, fieldName := range astField.Names {
field, err := parseFieldType(fieldName.Name, astField.Type, rawTag, knownStructs, namedPrimitives)
field, err := parseFieldType(
fieldName.Name,
astField.Type,
rawTag,
knownStructs,
namedPrimitives,
unsupportedNamedPrimitives,
)
if err != nil {
return Message{}, fmt.Errorf("field %s: %w", fieldName.Name, err)
}
@@ -219,14 +255,22 @@ func parseFieldType(
rawTag string,
knownStructs map[string]bool,
namedPrimitives map[string]PrimitiveKind,
unsupportedNamedPrimitives map[string]string,
) (Field, error) {
switch t := expr.(type) {
case *ast.Ident:
return parsePrimitiveOrNested(name, t.Name, rawTag, knownStructs, namedPrimitives)
return parsePrimitiveOrNested(
name,
t.Name,
rawTag,
knownStructs,
namedPrimitives,
unsupportedNamedPrimitives,
)
case *ast.ArrayType:
if t.Len == nil {
elem, err := parseFieldType("", t.Elt, rawTag, knownStructs, namedPrimitives)
elem, err := parseFieldType("", t.Elt, rawTag, knownStructs, namedPrimitives, unsupportedNamedPrimitives)
if err != nil {
return Field{}, fmt.Errorf("slice element: %w", err)
}
@@ -243,7 +287,7 @@ func parseFieldType(
return Field{}, fmt.Errorf("array length: %w", err)
}
elem, err := parseFieldType("", t.Elt, rawTag, knownStructs, namedPrimitives)
elem, err := parseFieldType("", t.Elt, rawTag, knownStructs, namedPrimitives, unsupportedNamedPrimitives)
if err != nil {
return Field{}, fmt.Errorf("array element: %w", err)
}
@@ -271,9 +315,25 @@ func parsePrimitiveOrNested(
rawTag string,
knownStructs map[string]bool,
namedPrimitives map[string]PrimitiveKind,
unsupportedNamedPrimitives map[string]string,
) (Field, error) {
switch typeName {
case "int", "uint", "uintptr":
return Field{}, fmt.Errorf(
"platform-dependent type %q is not supported; use int32/int64, uint32/uint64, or fixed-size integer IDs instead",
typeName,
)
}
primKind, isPrimitive := goPrimitiveKind(typeName)
if !isPrimitive {
if baseType, ok := unsupportedNamedPrimitives[typeName]; ok {
return Field{}, fmt.Errorf(
"type %q aliases unsupported platform-dependent %q; use int32/int64, uint32/uint64, or fixed-size integer IDs instead",
typeName,
baseType,
)
}
if namedPrimitive, ok := namedPrimitives[typeName]; ok {
return buildPrimitiveField(name, typeName, namedPrimitive, rawTag)
}
@@ -385,7 +445,7 @@ func goPrimitiveKind(name string) (PrimitiveKind, bool) {
return KindInt8, true
case "int16":
return KindInt16, true
case "int32", "int":
case "int32":
return KindInt32, true
case "int64":
return KindInt64, true
@@ -393,7 +453,7 @@ func goPrimitiveKind(name string) (PrimitiveKind, bool) {
return KindUint8, true
case "uint16":
return KindUint16, true
case "uint32", "uint":
case "uint32":
return KindUint32, true
case "uint64":
return KindUint64, true
+90
View File
@@ -1,6 +1,7 @@
package parser
import (
"strings"
"testing"
)
@@ -266,3 +267,92 @@ type Msg struct {
t.Fatal("expected error for unknown nested type, got nil")
}
}
func TestUnsupportedPlatformDependentIntTypes(t *testing.T) {
cases := []struct {
name string
src string
wantErr string
}{
{
name: "direct int field",
src: `package p
type Msg struct {
X int
}
`,
wantErr: `platform-dependent type "int" is not supported`,
},
{
name: "direct uint field",
src: `package p
type Msg struct {
X uint
}
`,
wantErr: `platform-dependent type "uint" is not supported`,
},
{
name: "direct uintptr field",
src: `package p
type Msg struct {
X uintptr
}
`,
wantErr: `platform-dependent type "uintptr" is not supported`,
},
{
name: "alias of int",
src: `package p
type Counter int
type Msg struct {
X Counter
}
`,
wantErr: `type "Counter" aliases unsupported platform-dependent "int"`,
},
{
name: "alias of uint",
src: `package p
type Counter uint
type Msg struct {
X Counter
}
`,
wantErr: `type "Counter" aliases unsupported platform-dependent "uint"`,
},
{
name: "alias of uintptr",
src: `package p
type Handle uintptr
type Msg struct {
X Handle
}
`,
wantErr: `type "Handle" aliases unsupported platform-dependent "uintptr"`,
},
{
name: "transitive alias of int",
src: `package p
type Base int
type Counter Base
type Msg struct {
X Counter
}
`,
wantErr: `type "Counter" aliases unsupported platform-dependent "int"`,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
_, err := ParseSource(tc.src)
if err == nil {
t.Fatal("expected error, got nil")
}
if !strings.Contains(err.Error(), tc.wantErr) {
t.Fatalf("expected error containing %q, got %v", tc.wantErr, err)
}
})
}
}