compiler: do not enforce variadic event args check on ellipsis usage

In case of ellipsis usage compiler defines argument type as ArrayT
(which is correct, because it's a natural representation of the last
argument, it represents the array of interface{}).
Here goes the problem:
```
=== RUN   TestEventWarnings/variadic_event_args_via_ellipsis
    compiler_test.go:251:
        	Error Trace:	compiler_test.go:251
        	Error:      	Received unexpected error:
        	            	event 'Event' should have 'Integer' as type of 1 parameter, got: Array
        	Test:       	TestEventWarnings/variadic_event_args_via_ellipsis
```

Parsing the last argument in this case is a separate complicated problem
due to the fact that we need to grab types of elements of []interface{} inside the
fully qualified ast node which may looks like:
```
runtime.Notify("Event", (append([]interface{}{1, 2}, (([]interface{}{someVar, 4}))...))...)
```

Temporary solution is to exclude such notifications from analysis until we're
able to properly resolve element types of []interface{}.
This commit is contained in:
Anna Shaleva 2022-09-29 15:41:53 +03:00
parent 08427f23b6
commit 80f71a4e6e
2 changed files with 27 additions and 7 deletions

View file

@ -230,6 +230,25 @@ func TestEventWarnings(t *testing.T) {
require.NoError(t, err)
})
})
t.Run("variadic event args via ellipsis", func(t *testing.T) {
src := `package payable
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
func Main() {
runtime.Notify("Event", []interface{}{1}...)
}`
_, di, err := compiler.CompileWithOptions("eventTest.go", strings.NewReader(src), nil)
require.NoError(t, err)
_, err = compiler.CreateManifest(di, &compiler.Options{
Name: "eventTest",
ContractEvents: []manifest.Event{{
Name: "Event",
Parameters: []manifest.Parameter{manifest.NewParameter("number", smartcontract.IntegerType)},
}},
})
require.NoError(t, err)
})
}
func TestNotifyInVerify(t *testing.T) {

View file

@ -36,7 +36,8 @@ func (c *codegen) inlineCall(f *funcScope, n *ast.CallExpr) {
pkg := c.packageCache[f.pkg.Path()]
sig := c.typeOf(n.Fun).(*types.Signature)
c.processStdlibCall(f, n.Args)
hasVarArgs := !n.Ellipsis.IsValid()
c.processStdlibCall(f, n.Args, !hasVarArgs)
// When inlined call is used during global initialization
// there is no func scope, thus this if.
@ -68,7 +69,6 @@ func (c *codegen) inlineCall(f *funcScope, n *ast.CallExpr) {
scope: oldScope,
})
}
hasVarArgs := !n.Ellipsis.IsValid()
needPack := sig.Variadic() && hasVarArgs
for i := range n.Args {
c.scope.vars.locals = oldScope
@ -129,13 +129,13 @@ func (c *codegen) inlineCall(f *funcScope, n *ast.CallExpr) {
c.pkgInfoInline = c.pkgInfoInline[:len(c.pkgInfoInline)-1]
}
func (c *codegen) processStdlibCall(f *funcScope, args []ast.Expr) {
func (c *codegen) processStdlibCall(f *funcScope, args []ast.Expr, hasEllipsis bool) {
if f == nil {
return
}
if f.pkg.Path() == interopPrefix+"/runtime" && (f.name == "Notify" || f.name == "Log") {
c.processNotify(f, args)
c.processNotify(f, args, hasEllipsis)
}
if f.pkg.Path() == interopPrefix+"/contract" && f.name == "Call" {
@ -143,7 +143,7 @@ func (c *codegen) processStdlibCall(f *funcScope, args []ast.Expr) {
}
}
func (c *codegen) processNotify(f *funcScope, args []ast.Expr) {
func (c *codegen) processNotify(f *funcScope, args []ast.Expr, hasEllipsis bool) {
if c.scope != nil && c.isVerifyFunc(c.scope.decl) &&
c.scope.pkg == c.mainPkg.Types && (c.buildInfo.options == nil || !c.buildInfo.options.NoEventsCheck) {
c.prog.Err = fmt.Errorf("runtime.%s is not allowed in `Verify`", f.name)
@ -154,10 +154,11 @@ func (c *codegen) processNotify(f *funcScope, args []ast.Expr) {
return
}
// Sometimes event name is stored in a var.
// Sometimes event name is stored in a var. Or sometimes event args are provided
// via ellipses (`slice...`).
// Skip in this case.
tv := c.typeAndValueOf(args[0])
if tv.Value == nil {
if tv.Value == nil || hasEllipsis {
return
}