compiler: disallow unnamed parameters for exported methods

This commit is contained in:
Anna Shaleva 2022-07-14 15:34:57 +03:00
parent d84f8940f1
commit 94f6a9ee61
3 changed files with 96 additions and 0 deletions

View file

@ -2,6 +2,7 @@ package compiler
import ( import (
"errors" "errors"
"fmt"
"go/ast" "go/ast"
"go/token" "go/token"
"go/types" "go/types"
@ -12,6 +13,9 @@ import (
"golang.org/x/tools/go/packages" "golang.org/x/tools/go/packages"
) )
// ErrMissingExportedParamName is returned when exported contract method has unnamed parameter.
var ErrMissingExportedParamName = errors.New("exported method is not allowed to have unnamed parameter")
var ( var (
// Go language builtin functions. // Go language builtin functions.
goBuiltins = []string{"len", "append", "panic", "make", "copy", "recover", "delete"} goBuiltins = []string{"len", "append", "panic", "make", "copy", "recover", "delete"}
@ -284,12 +288,31 @@ func (c *codegen) analyzeFuncUsage() funcUsage {
if isMain && n.Name.IsExported() || isInitFunc(n) || isDeployFunc(n) { if isMain && n.Name.IsExported() || isInitFunc(n) || isDeployFunc(n) {
diff[name] = true diff[name] = true
} }
if isMain && n.Name.IsExported() {
if n.Type.Params.List != nil {
for i, param := range n.Type.Params.List {
if param.Names == nil {
c.prog.Err = fmt.Errorf("%w: %s", ErrMissingExportedParamName, n.Name)
return false // Program is invalid.
}
for _, name := range param.Names {
if name == nil || name.Name == "_" {
c.prog.Err = fmt.Errorf("%w: %s/%d", ErrMissingExportedParamName, n.Name, i)
return false // Program is invalid.
}
}
}
}
}
nodeCache[name] = declPair{n, c.importMap, pkgPath} nodeCache[name] = declPair{n, c.importMap, pkgPath}
return false // will be processed in the next stage return false // will be processed in the next stage
} }
return true return true
}) })
}) })
if c.prog.Err != nil {
return nil
}
usage := funcUsage{} usage := funcUsage{}
for len(diff) != 0 { for len(diff) != 0 {

View file

@ -2106,6 +2106,9 @@ func (c *codegen) compile(info *buildInfo, pkg *packages.Package) error {
c.analyzePkgOrder() c.analyzePkgOrder()
c.fillDocumentInfo() c.fillDocumentInfo()
funUsage := c.analyzeFuncUsage() funUsage := c.analyzeFuncUsage()
if c.prog.Err != nil {
return c.prog.Err
}
// Bring all imported functions into scope. // Bring all imported functions into scope.
c.ForEachFile(c.resolveFuncDecls) c.ForEachFile(c.resolveFuncDecls)

View file

@ -343,3 +343,73 @@ func TestInvokedContractsPermissons(t *testing.T) {
}) })
}) })
} }
func TestUnnamedParameterCheck(t *testing.T) {
t.Run("single argument", func(t *testing.T) {
src := `
package testcase
func Main(_ int) int {
x := 10
return x
}
`
_, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
require.Error(t, err)
require.ErrorIs(t, err, compiler.ErrMissingExportedParamName)
})
t.Run("several arguments", func(t *testing.T) {
src := `
package testcase
func Main(a int, b string, _ int) int {
x := 10
return x
}
`
_, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
require.Error(t, err)
require.ErrorIs(t, err, compiler.ErrMissingExportedParamName)
})
t.Run("interface", func(t *testing.T) {
src := `
package testcase
func OnNEP17Payment(h string, i int, _ interface{}){}
`
_, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
require.Error(t, err)
require.ErrorIs(t, err, compiler.ErrMissingExportedParamName)
})
t.Run("a set of unnamed params", func(t *testing.T) {
src := `
package testcase
func OnNEP17Payment(_ string, _ int, _ interface{}){}
`
_, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
require.Error(t, err)
require.ErrorIs(t, err, compiler.ErrMissingExportedParamName)
})
t.Run("mixed named and unnamed params", func(t *testing.T) {
src := `
package testcase
func OnNEP17Payment(s0, _, s2 string){}
`
_, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
require.Error(t, err)
require.ErrorIs(t, err, compiler.ErrMissingExportedParamName)
})
t.Run("empty args", func(t *testing.T) {
src := `
package testcase
func OnNEP17Payment(){}
`
_, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
require.NoError(t, err)
})
t.Run("good", func(t *testing.T) {
src := `
package testcase
func OnNEP17Payment(s string, i int, iface interface{}){}
`
_, _, err := compiler.CompileWithOptions("test.go", strings.NewReader(src), nil)
require.NoError(t, err)
})
}