From 8305e75079694b04258b69de4cd42c9c1a0d56c2 Mon Sep 17 00:00:00 2001 From: cce <51567+cce@users.noreply.github.com> Date: Fri, 10 Jul 2026 10:39:21 -0400 Subject: [PATCH 1/2] better support any using modern 1.25 ast and golang tools packages --- go.mod | 10 ++-- go.sum | 18 ++++---- parse/getast.go | 25 +++++----- parse/getast_test.go | 76 ++++++++++++++++++++++++++----- parse/testdata/anydecl/anydecl.go | 13 ++++++ tests/go.mod | 2 +- 6 files changed, 104 insertions(+), 40 deletions(-) create mode 100644 parse/testdata/anydecl/anydecl.go diff --git a/go.mod b/go.mod index d865580..eb71b26 100644 --- a/go.mod +++ b/go.mod @@ -1,19 +1,17 @@ module github.com/algorand/msgp -go 1.23 +go 1.25.0 require ( github.com/daixiang0/gci v0.3.2 github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31 - golang.org/x/tools v0.1.5 + golang.org/x/tools v0.48.0 ) require ( github.com/hexops/gotextdiff v1.0.3 // indirect github.com/kr/text v0.2.0 // indirect - golang.org/x/mod v0.5.0 // indirect - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + golang.org/x/mod v0.38.0 // indirect + golang.org/x/sync v0.22.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) diff --git a/go.sum b/go.sum index 1da34a3..e7958df 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,8 @@ github.com/daixiang0/gci v0.3.2 h1:MDBsgEJGSJ++/N6Je/b2yK5x5WqmKYlzmILLVP41nl8= github.com/daixiang0/gci v0.3.2/go.mod h1:jaASoJmv/ykO9dAAPy31iJnreV19248qKDdVWf3QgC4= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= @@ -15,16 +17,12 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31 h1:OXcKh35JaYsGMRzpvFkLv/MEyPuL49CThT1pZ8aSml4= github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= -golang.org/x/mod v0.5.0 h1:UG21uOlmZabA4fW5i7ZX6bjw1xELEGg/ZLgZq9auk/Q= -golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk= +golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE= +golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/parse/getast.go b/parse/getast.go index 431f6e7..c9c59c0 100644 --- a/parse/getast.go +++ b/parse/getast.go @@ -3,6 +3,7 @@ package parse import ( "fmt" "go/ast" + "go/types" "reflect" "sort" "strings" @@ -42,7 +43,7 @@ func File(name string, unexported bool, warnPkgMask string) (*FileSet, error) { defer popstate() cfg := &packages.Config{ - Mode: packages.NeedName | packages.NeedImports | packages.NeedDeps | packages.NeedSyntax | packages.NeedFiles | packages.NeedExportsFile | packages.NeedTypesInfo, + Mode: packages.LoadAllSyntax, } pkgs, err := packages.Load(cfg, name) @@ -123,7 +124,7 @@ func packageToFileSet(p *packages.Package, imps map[string]*FileSet, unexported fs.ImportName[importname] = pkgpath } - fs.getTypeSpecs(fl) + fs.getTypeSpecs(fl, p.TypesInfo) popstate() } @@ -323,7 +324,7 @@ func (f *FileSet) PrintTo(p *gen.Printer) error { // getTypeSpecs extracts all of the *ast.TypeSpecs in the file // into fs.Identities, but does not set the actual element -func (fs *FileSet) getTypeSpecs(f *ast.File) { +func (fs *FileSet) getTypeSpecs(f *ast.File, typeInfo *types.Info) { // collect all imports... fs.Imports = append(fs.Imports, f.Imports...) @@ -340,6 +341,15 @@ func (fs *FileSet) getTypeSpecs(f *ast.File) { // for ast.TypeSpecs.... switch s := s.(type) { case *ast.TypeSpec: + if typeInfo != nil { + if typ := typeInfo.TypeOf(s.Type); typ != nil { + if _, ok := typ.Underlying().(*types.Interface); ok { + fs.Interfaces[s.Name.Name] = s.Type + continue + } + } + } + switch s.Type.(type) { // this is the list of parse-able @@ -355,15 +365,6 @@ func (fs *FileSet) getTypeSpecs(f *ast.File) { continue } - // `type X any` declares an empty interface just like - // `type X interface{}`; no methods can be generated - // for an interface type, so classify it with the - // interfaces rather than the generatable specs - if id, ok := s.Type.(*ast.Ident); ok && id.Name == "any" { - fs.Interfaces[s.Name.Name] = s.Type - continue - } - if s.Assign == 0 { fs.Specs[s.Name.Name] = s.Type } else { diff --git a/parse/getast_test.go b/parse/getast_test.go index 38ef11e..f23903c 100644 --- a/parse/getast_test.go +++ b/parse/getast_test.go @@ -4,17 +4,17 @@ import ( "go/ast" "go/parser" "go/token" + "go/types" "testing" "github.com/algorand/msgp/gen" ) -// TestGetTypeSpecsAnyDeclaration covers classification of empty-interface -// type declarations. `type X interface{}` is routed to fs.Interfaces so no -// methods are generated for it; `type X any` must be routed the same way, -// since methods cannot be generated for an interface receiver (and the -// fallback used to emit non-compiling code for such declarations). -func TestGetTypeSpecsAnyDeclaration(t *testing.T) { +// TestGetTypeSpecsInterfaceDeclarations covers semantic classification of +// interface type declarations. Methods cannot be generated for an interface +// receiver, including when the interface is reached through an alias or +// another named type. +func TestGetTypeSpecsInterfaceDeclarations(t *testing.T) { src := `package p type HandleAny any @@ -23,14 +23,35 @@ type HandleAnyAlias = any type HandleIface interface{} +type HandleNamed HandleIface + +type HandleNamedAlias = HandleIface + +type NonEmptyIface interface { + Method() +} + +type NonEmptyNamed NonEmptyIface + +type Scalar uint64 + +type ScalarNamed Scalar + +type ScalarAlias = Scalar + type Plain struct { A int } ` - f, err := parser.ParseFile(token.NewFileSet(), "p.go", src, parser.ParseComments) + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, "p.go", src, parser.ParseComments) if err != nil { t.Fatal(err) } + typeInfo := &types.Info{Types: make(map[ast.Expr]types.TypeAndValue)} + if _, err = (&types.Config{}).Check("p", fset, []*ast.File{f}, typeInfo); err != nil { + t.Fatal(err) + } fs := &FileSet{ Specs: make(map[string]ast.Expr), @@ -39,9 +60,18 @@ type Plain struct { Consts: make(map[string]ast.Expr), Identities: make(map[string]gen.Elem), } - fs.getTypeSpecs(f) + fs.getTypeSpecs(f, typeInfo) - for _, name := range []string{"HandleAny", "HandleAnyAlias", "HandleIface"} { + interfaces := []string{ + "HandleAny", + "HandleAnyAlias", + "HandleIface", + "HandleNamed", + "HandleNamedAlias", + "NonEmptyIface", + "NonEmptyNamed", + } + for _, name := range interfaces { if _, ok := fs.Interfaces[name]; !ok { t.Errorf("%s: expected in Interfaces", name) } @@ -53,7 +83,31 @@ type Plain struct { } } - if _, ok := fs.Specs["Plain"]; !ok { - t.Errorf("Plain: expected in Specs") + for _, name := range []string{"Scalar", "ScalarNamed", "Plain"} { + if _, ok := fs.Specs[name]; !ok { + t.Errorf("%s: expected in Specs", name) + } + } + if _, ok := fs.Aliases["ScalarAlias"]; !ok { + t.Errorf("ScalarAlias: expected in Aliases") + } +} + +func TestFileClassifiesAnyAsInterface(t *testing.T) { + fs, err := File("./testdata/anydecl", false, "") + if err != nil { + t.Fatal(err) + } + + for _, name := range []string{"HandleAny", "HandleAnyAlias", "HandleIface", "HandleNamed"} { + if _, ok := fs.Interfaces[name]; !ok { + t.Errorf("%s: expected in Interfaces", name) + } + if _, ok := fs.Identities[name]; ok { + t.Errorf("%s: must not be in Identities", name) + } + } + if _, ok := fs.Identities["Neighbor"]; !ok { + t.Error("Neighbor: expected in Identities") } } diff --git a/parse/testdata/anydecl/anydecl.go b/parse/testdata/anydecl/anydecl.go new file mode 100644 index 0000000..cd5868f --- /dev/null +++ b/parse/testdata/anydecl/anydecl.go @@ -0,0 +1,13 @@ +package anydecl + +type HandleAny any + +type HandleAnyAlias = any + +type HandleIface interface{} + +type HandleNamed HandleIface + +type Neighbor struct { + Value uint64 +} diff --git a/tests/go.mod b/tests/go.mod index 05758f1..cc24344 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -6,7 +6,7 @@ // dependency. Run these tests with: cd tests && go test ./... module github.com/algorand/msgp/tests -go 1.23 +go 1.25.0 require ( github.com/algorand/go-algorand v0.0.0-00010101000000-000000000000 From 3ea8eee866357a615491b70e82d95b2da2174da3 Mon Sep 17 00:00:00 2001 From: cce <51567+cce@users.noreply.github.com> Date: Fri, 10 Jul 2026 10:50:04 -0400 Subject: [PATCH 2/2] fix CI build.yml --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 672e40d..23489b8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,5 +10,5 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: '1.23.6' + go-version-file: go.mod - run: make test