Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions parse/getast.go
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,15 @@ 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 {
Expand Down
59 changes: 59 additions & 0 deletions parse/getast_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package parse

import (
"go/ast"
"go/parser"
"go/token"
"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) {
src := `package p

type HandleAny any

type HandleAnyAlias = any

type HandleIface interface{}

type Plain struct {
A int
}
`
f, err := parser.ParseFile(token.NewFileSet(), "p.go", src, parser.ParseComments)
if err != nil {
t.Fatal(err)
}

fs := &FileSet{
Specs: make(map[string]ast.Expr),
Aliases: make(map[string]ast.Expr),
Interfaces: make(map[string]ast.Expr),
Consts: make(map[string]ast.Expr),
Identities: make(map[string]gen.Elem),
}
fs.getTypeSpecs(f)

for _, name := range []string{"HandleAny", "HandleAnyAlias", "HandleIface"} {
if _, ok := fs.Interfaces[name]; !ok {
t.Errorf("%s: expected in Interfaces", name)
}
if _, ok := fs.Specs[name]; ok {
t.Errorf("%s: must not be in Specs (would generate methods on an interface)", name)
}
if _, ok := fs.Aliases[name]; ok {
t.Errorf("%s: must not be in Aliases", name)
}
}

if _, ok := fs.Specs["Plain"]; !ok {
t.Errorf("Plain: expected in Specs")
}
}
27 changes: 27 additions & 0 deletions tests/anydecl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// This file exercises top-level empty-interface type declarations: the
// generator must skip `type X any` exactly like `type X interface{}` (methods
// cannot be generated for an interface receiver, and the IDENT/Intf fallbacks
// emit code that does not compile), while ordinary types declared alongside
// them still generate.
package tests

//go:generate msgp

// HandleAny is an opaque reference in the style of go-algorand's
// agreement.MessageHandle and network.Peer; no methods may be generated.
type HandleAny any

// HandleAnyAlias is the alias form of the same declaration.
type HandleAnyAlias = any

// HandleIface is the pre-Go-1.18 spelling of HandleAny.
type HandleIface interface{}

// AnyDeclNeighbor must still get generated code despite the interface
// declarations surrounding it.
type AnyDeclNeighbor struct {
_struct struct{} `codec:",omitempty"`

Name string `codec:"n"`
Count uint64 `codec:"c"`
}
162 changes: 162 additions & 0 deletions tests/anydecl_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

74 changes: 74 additions & 0 deletions tests/anydecl_gen_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading