forked from TrueCloudLab/neoneo-go
compiler: disallow generic method receiver
Either non-pointer or pointer, both cases are disallowed to be generic. Need to be reverted and properly handled within the scope of #2376. Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
This commit is contained in:
parent
415d44792a
commit
1b1b454ce4
2 changed files with 73 additions and 0 deletions
|
@ -19,6 +19,8 @@ var (
|
|||
ErrMissingExportedParamName = errors.New("exported method is not allowed to have unnamed parameter")
|
||||
// ErrInvalidExportedRetCount is returned when exported contract method has invalid return values count.
|
||||
ErrInvalidExportedRetCount = errors.New("exported method is not allowed to have more than one return value")
|
||||
// ErrGenericsUnsuppored is returned when generics-related tokens are encountered.
|
||||
ErrGenericsUnsuppored = errors.New("generics are currently unsupported, please, see the https://github.com/nspcc-dev/neo-go/issues/2376")
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -361,6 +363,13 @@ func (c *codegen) analyzeFuncAndGlobalVarUsage() funcUsage {
|
|||
case *ast.FuncDecl:
|
||||
name := c.getFuncNameFromDecl(pkgPath, n)
|
||||
|
||||
// filter out generic functions
|
||||
err := c.checkGenericsFuncDecl(n, name)
|
||||
if err != nil {
|
||||
c.prog.Err = err
|
||||
return false // Program is invalid.
|
||||
}
|
||||
|
||||
// exported functions and methods are always assumed to be used
|
||||
if isMain && n.Name.IsExported() || isInitFunc(n) || isDeployFunc(n) {
|
||||
diff[name] = true
|
||||
|
@ -535,6 +544,32 @@ func (c *codegen) analyzeFuncAndGlobalVarUsage() funcUsage {
|
|||
return usage
|
||||
}
|
||||
|
||||
// checkGenericFuncDecl checks whether provided ast.FuncDecl has generic code.
|
||||
func (c *codegen) checkGenericsFuncDecl(n *ast.FuncDecl, funcName string) error {
|
||||
var errGenerics error
|
||||
|
||||
// Generic function receiver.
|
||||
if n.Recv != nil {
|
||||
switch t := n.Recv.List[0].Type.(type) {
|
||||
case *ast.StarExpr:
|
||||
switch t.X.(type) {
|
||||
case *ast.IndexExpr:
|
||||
// func (x *Pointer[T]) Load() *T
|
||||
errGenerics = errors.New("generic pointer function receiver")
|
||||
}
|
||||
case *ast.IndexExpr:
|
||||
// func (x Structure[T]) Load() *T
|
||||
errGenerics = errors.New("generic function receiver")
|
||||
}
|
||||
}
|
||||
|
||||
if errGenerics != nil {
|
||||
return fmt.Errorf("%w: %s has %s", ErrGenericsUnsuppored, funcName, errGenerics.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// nodeContext contains ast node with the corresponding import map, type info and package information
|
||||
// required to retrieve fully qualified node name (if so).
|
||||
type nodeContext struct {
|
||||
|
|
38
pkg/compiler/generics_test.go
Normal file
38
pkg/compiler/generics_test.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
package compiler_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGenericMethodReceiver(t *testing.T) {
|
||||
t.Run("star expression", func(t *testing.T) {
|
||||
src := `
|
||||
package receiver
|
||||
type Pointer[T any] struct {
|
||||
value T
|
||||
}
|
||||
func (x *Pointer[T]) Load() *T {
|
||||
return &x.value
|
||||
}
|
||||
`
|
||||
_, _, err := compiler.CompileWithOptions("foo.go", strings.NewReader(src), nil)
|
||||
require.ErrorIs(t, err, compiler.ErrGenericsUnsuppored)
|
||||
})
|
||||
t.Run("ident expression", func(t *testing.T) {
|
||||
src := `
|
||||
package receiver
|
||||
type Pointer[T any] struct {
|
||||
value T
|
||||
}
|
||||
func (x Pointer[T]) Load() *T {
|
||||
return &x.value
|
||||
}
|
||||
`
|
||||
_, _, err := compiler.CompileWithOptions("foo.go", strings.NewReader(src), nil)
|
||||
require.ErrorIs(t, err, compiler.ErrGenericsUnsuppored)
|
||||
})
|
||||
}
|
Loading…
Reference in a new issue