fix: check the number of argument passed and returned to functions
This commit is contained in:
12
_test/fun21.go
Normal file
12
_test/fun21.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
func Bar() string {
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
println(Bar())
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 4:2: not enough arguments to return
|
||||
10
_test/fun22.go
Normal file
10
_test/fun22.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
import "time"
|
||||
|
||||
func main() {
|
||||
time.Date()
|
||||
}
|
||||
|
||||
// Error:
|
||||
// 6:2: not enough arguments in call to time.Date
|
||||
@@ -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: "&&",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user