From 4f8e1de2676945dea947b20638eef47d3b04b5d5 Mon Sep 17 00:00:00 2001 From: Marc Vertes Date: Tue, 19 May 2020 14:56:04 +0200 Subject: [PATCH] fix: check the number of argument passed and returned to functions --- _test/fun21.go | 12 +++++++++ _test/fun22.go | 10 +++++++ interp/ast.go | 2 ++ interp/cfg.go | 46 +++++++++++++++++++++++++++++++- interp/interp_consistent_test.go | 12 +++++++++ interp/type.go | 11 +++++--- 6 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 _test/fun21.go create mode 100644 _test/fun22.go diff --git a/_test/fun21.go b/_test/fun21.go new file mode 100644 index 00000000..e616a39b --- /dev/null +++ b/_test/fun21.go @@ -0,0 +1,12 @@ +package main + +func Bar() string { + return +} + +func main() { + println(Bar()) +} + +// Error: +// 4:2: not enough arguments to return diff --git a/_test/fun22.go b/_test/fun22.go new file mode 100644 index 00000000..ed151d11 --- /dev/null +++ b/_test/fun22.go @@ -0,0 +1,10 @@ +package main + +import "time" + +func main() { + time.Date() +} + +// Error: +// 6:2: not enough arguments in call to time.Date diff --git a/interp/ast.go b/interp/ast.go index 046b2b4d..0ad884fe 100644 --- a/interp/ast.go +++ b/interp/ast.go @@ -206,6 +206,7 @@ const ( aGreaterEqual aGetFunc aGetIndex + aGetMethod aGetSym aInc aLand @@ -265,6 +266,7 @@ var actions = [...]string{ aGreater: ">", aGetFunc: "getFunc", aGetIndex: "getIndex", + aGetMethod: "getMethod", aGetSym: ".", aInc: "++", aLand: "&&", diff --git a/interp/cfg.go b/interp/cfg.go index 3685a0a9..f712dab4 100644 --- a/interp/cfg.go +++ b/interp/cfg.go @@ -812,7 +812,23 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) { } case isBinCall(n): n.gen = callBin - if typ := n.child[0].typ.rtype; typ.NumOut() > 0 { + typ := n.child[0].typ.rtype + numIn := len(n.child) - 1 + tni := typ.NumIn() + if numIn == 1 && n.child[1].action == aCall { + numIn = n.child[1].typ.numOut() + } + if n.child[0].action == aGetMethod { + tni-- // The first argument is the method receiver. + } + if typ.IsVariadic() { + tni-- // The last argument could be empty. + } + if numIn < tni { + err = n.cfgErrorf("not enough arguments in call to %v", n.child[0].name()) + break + } + if typ.NumOut() > 0 { if funcType := n.child[0].typ.val; funcType != nil { // Use the original unwrapped function type, to allow future field and // methods resolutions, otherwise impossible on the opaque bin type. @@ -1196,6 +1212,16 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) { } case returnStmt: + if mustReturnValue(sc.def.child[2]) { + nret := len(n.child) + if nret == 1 && n.child[0].action == aCall { + nret = n.child[0].child[0].typ.numOut() + } + if nret < sc.def.typ.numOut() { + err = n.cfgErrorf("not enough arguments to return") + break + } + } wireChild(n) n.tnext = nil n.val = sc.def @@ -1232,6 +1258,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) { case ok: n.val = method.Index n.gen = getIndexBinMethod + n.action = aGetMethod n.recv = &receiver{node: n.child[0]} n.typ = &itype{cat: valueT, rtype: method.Type, isBinMethod: true} case n.typ.rtype.Kind() == reflect.Ptr: @@ -1255,6 +1282,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) { n.gen = getIndexBinPtrMethod n.typ = &itype{cat: valueT, rtype: m2.Type} n.recv = &receiver{node: n.child[0]} + n.action = aGetMethod } else { err = n.cfgErrorf("undefined field or method: %s", n.child[1].ident) } @@ -1269,11 +1297,13 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) { n.typ = &itype{cat: valueT, rtype: method.Type} n.recv = &receiver{node: n.child[0]} n.gen = getIndexBinMethod + n.action = aGetMethod } else if method, ok := reflect.PtrTo(n.typ.val.rtype).MethodByName(n.child[1].ident); ok { n.val = method.Index n.gen = getIndexBinMethod n.typ = &itype{cat: valueT, rtype: method.Type} n.recv = &receiver{node: n.child[0]} + n.action = aGetMethod } else if field, ok := n.typ.val.rtype.FieldByName(n.child[1].ident); ok { n.typ = &itype{cat: valueT, rtype: field.Type} n.val = field.Index @@ -1312,6 +1342,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) { err = n.cfgErrorf("undefined selector: %s.%s", pkg, name) } } else if m, lind := n.typ.lookupMethod(n.child[1].ident); m != nil { + n.action = aGetMethod if n.child[0].isType(sc) { // Handle method as a function with receiver in 1st argument n.val = m @@ -1328,6 +1359,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) { n.recv = &receiver{node: n.child[0], index: lind} } } else if m, lind, isPtr, ok := n.typ.lookupBinMethod(n.child[1].ident); ok { + n.action = aGetMethod if isPtr && n.typ.fieldSeq(lind).cat != ptrT { n.gen = getIndexSeqPtrMethod } else { @@ -1985,6 +2017,18 @@ func isBinCall(n *node) bool { return n.kind == callExpr && n.child[0].typ.cat == valueT && n.child[0].typ.rtype.Kind() == reflect.Func } +func mustReturnValue(n *node) bool { + if len(n.child) < 2 { + return false + } + for _, f := range n.child[1].child { + if len(f.child) > 1 { + return false + } + } + return true +} + func isRegularCall(n *node) bool { return n.kind == callExpr && n.child[0].typ.cat == funcT } diff --git a/interp/interp_consistent_test.go b/interp/interp_consistent_test.go index 80e26c1e..adda634b 100644 --- a/interp/interp_consistent_test.go +++ b/interp/interp_consistent_test.go @@ -39,6 +39,8 @@ func TestInterpConsistencyBuild(t *testing.T) { file.Name() == "export1.go" || // non-main package file.Name() == "export0.go" || // non-main package file.Name() == "for7.go" || // expect error + file.Name() == "fun21.go" || // expect error + file.Name() == "fun22.go" || // expect error file.Name() == "if2.go" || // expect error file.Name() == "import6.go" || // expect error file.Name() == "io0.go" || // use random number @@ -166,6 +168,16 @@ func TestInterpErrorConsistency(t *testing.T) { expectedInterp: "4:14: non-bool used as for condition", expectedExec: "4:2: non-bool i (type int) used as for condition", }, + { + fileName: "fun21.go", + expectedInterp: "4:2: not enough arguments to return", + expectedExec: "4:2: not enough arguments to return", + }, + { + fileName: "fun22.go", + expectedInterp: "6:2: not enough arguments in call to time.Date", + expectedExec: "6:11: not enough arguments in call to time.Date", + }, { fileName: "op1.go", expectedInterp: "5:2: illegal operand types for '+=' operator", diff --git a/interp/type.go b/interp/type.go index 9b7684a7..73c5bdbd 100644 --- a/interp/type.go +++ b/interp/type.go @@ -706,10 +706,15 @@ func (t *itype) referTo(name string, seen map[*itype]bool) bool { } func (t *itype) numOut() int { - if t.cat == valueT { - return t.rtype.NumOut() + switch t.cat { + case funcT: + return len(t.ret) + case valueT: + if t.rtype.Kind() == reflect.Func { + return t.rtype.NumOut() + } } - return len(t.ret) + return 1 } func (t *itype) concrete() *itype {