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:
parent
08427f23b6
commit
80f71a4e6e
2 changed files with 27 additions and 7 deletions
|
@ -230,6 +230,25 @@ func TestEventWarnings(t *testing.T) {
|
||||||
require.NoError(t, err)
|
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) {
|
func TestNotifyInVerify(t *testing.T) {
|
||||||
|
|
|
@ -36,7 +36,8 @@ func (c *codegen) inlineCall(f *funcScope, n *ast.CallExpr) {
|
||||||
pkg := c.packageCache[f.pkg.Path()]
|
pkg := c.packageCache[f.pkg.Path()]
|
||||||
sig := c.typeOf(n.Fun).(*types.Signature)
|
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
|
// When inlined call is used during global initialization
|
||||||
// there is no func scope, thus this if.
|
// there is no func scope, thus this if.
|
||||||
|
@ -68,7 +69,6 @@ func (c *codegen) inlineCall(f *funcScope, n *ast.CallExpr) {
|
||||||
scope: oldScope,
|
scope: oldScope,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
hasVarArgs := !n.Ellipsis.IsValid()
|
|
||||||
needPack := sig.Variadic() && hasVarArgs
|
needPack := sig.Variadic() && hasVarArgs
|
||||||
for i := range n.Args {
|
for i := range n.Args {
|
||||||
c.scope.vars.locals = oldScope
|
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]
|
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 {
|
if f == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.pkg.Path() == interopPrefix+"/runtime" && (f.name == "Notify" || f.name == "Log") {
|
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" {
|
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) &&
|
if c.scope != nil && c.isVerifyFunc(c.scope.decl) &&
|
||||||
c.scope.pkg == c.mainPkg.Types && (c.buildInfo.options == nil || !c.buildInfo.options.NoEventsCheck) {
|
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)
|
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
|
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.
|
// Skip in this case.
|
||||||
tv := c.typeAndValueOf(args[0])
|
tv := c.typeAndValueOf(args[0])
|
||||||
if tv.Value == nil {
|
if tv.Value == nil || hasEllipsis {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue