go/types: don't allow f()... if f() is multi-valued
Also: Better position for error messages related to wrong use of ... . Fixes #9473. Change-Id: I90565f51a42897b7292f651a84a23611a5d8f359 Reviewed-on: https://go-review.googlesource.com/2390 Reviewed-by: Alan Donovan <adonovan@google.com>
This commit is contained in:
parent
4ad370efaa
commit
44ee65f545
|
@ -48,7 +48,6 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||||
// make argument getter
|
// make argument getter
|
||||||
arg, nargs, _ = unpack(func(x *operand, i int) { check.expr(x, call.Args[i]) }, nargs, false)
|
arg, nargs, _ = unpack(func(x *operand, i int) { check.expr(x, call.Args[i]) }, nargs, false)
|
||||||
if arg == nil {
|
if arg == nil {
|
||||||
x.mode = invalid
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// evaluate first argument, if present
|
// evaluate first argument, if present
|
||||||
|
|
|
@ -179,14 +179,18 @@ func unpack(get getter, n int, allowCommaOk bool) (getter, int, bool) {
|
||||||
// arguments checks argument passing for the call with the given signature.
|
// arguments checks argument passing for the call with the given signature.
|
||||||
// The arg function provides the operand for the i'th argument.
|
// The arg function provides the operand for the i'th argument.
|
||||||
func (check *Checker) arguments(x *operand, call *ast.CallExpr, sig *Signature, arg getter, n int) {
|
func (check *Checker) arguments(x *operand, call *ast.CallExpr, sig *Signature, arg getter, n int) {
|
||||||
passSlice := false
|
|
||||||
if call.Ellipsis.IsValid() {
|
if call.Ellipsis.IsValid() {
|
||||||
// last argument is of the form x...
|
// last argument is of the form x...
|
||||||
if sig.variadic {
|
if len(call.Args) == 1 && n > 1 {
|
||||||
passSlice = true
|
// f()... is not permitted if f() is multi-valued
|
||||||
} else {
|
check.errorf(call.Ellipsis, "cannot use ... with %d-valued expression %s", n, call.Args[0])
|
||||||
|
check.useGetter(arg, n)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !sig.variadic {
|
||||||
check.errorf(call.Ellipsis, "cannot use ... in call to non-variadic %s", call.Fun)
|
check.errorf(call.Ellipsis, "cannot use ... in call to non-variadic %s", call.Fun)
|
||||||
// ok to continue
|
check.useGetter(arg, n)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,7 +198,11 @@ func (check *Checker) arguments(x *operand, call *ast.CallExpr, sig *Signature,
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
arg(x, i)
|
arg(x, i)
|
||||||
if x.mode != invalid {
|
if x.mode != invalid {
|
||||||
check.argument(sig, i, x, passSlice && i == n-1)
|
var ellipsis token.Pos
|
||||||
|
if i == n-1 && call.Ellipsis.IsValid() {
|
||||||
|
ellipsis = call.Ellipsis
|
||||||
|
}
|
||||||
|
check.argument(sig, i, x, ellipsis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,8 +219,8 @@ func (check *Checker) arguments(x *operand, call *ast.CallExpr, sig *Signature,
|
||||||
}
|
}
|
||||||
|
|
||||||
// argument checks passing of argument x to the i'th parameter of the given signature.
|
// argument checks passing of argument x to the i'th parameter of the given signature.
|
||||||
// If passSlice is set, the argument is followed by ... in the call.
|
// If ellipsis is valid, the argument is followed by ... at that position in the call.
|
||||||
func (check *Checker) argument(sig *Signature, i int, x *operand, passSlice bool) {
|
func (check *Checker) argument(sig *Signature, i int, x *operand, ellipsis token.Pos) {
|
||||||
n := sig.params.Len()
|
n := sig.params.Len()
|
||||||
|
|
||||||
// determine parameter type
|
// determine parameter type
|
||||||
|
@ -232,13 +240,19 @@ func (check *Checker) argument(sig *Signature, i int, x *operand, passSlice bool
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if passSlice {
|
if ellipsis.IsValid() {
|
||||||
// argument is of the form x...
|
// argument is of the form x...
|
||||||
if i != n-1 {
|
if i != n-1 {
|
||||||
check.errorf(x.pos(), "can only use ... with matching parameter")
|
check.errorf(ellipsis, "can only use ... with matching parameter")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if _, ok := x.typ.Underlying().(*Slice); !ok {
|
switch t := x.typ.Underlying().(type) {
|
||||||
|
case *Slice:
|
||||||
|
// ok
|
||||||
|
case *Tuple:
|
||||||
|
check.errorf(ellipsis, "cannot use ... with %d-valued expression %s", t.Len(), x)
|
||||||
|
return
|
||||||
|
default:
|
||||||
check.errorf(x.pos(), "cannot use %s as parameter of type %s", x, typ)
|
check.errorf(x.pos(), "cannot use %s as parameter of type %s", x, typ)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,7 +137,7 @@ func (check *Checker) reportCycle(cycle []*objNode, i int) {
|
||||||
obj := cycle[i].obj
|
obj := cycle[i].obj
|
||||||
check.errorf(obj.Pos(), "initialization cycle for %s", obj.Name())
|
check.errorf(obj.Pos(), "initialization cycle for %s", obj.Name())
|
||||||
// print cycle
|
// print cycle
|
||||||
for _ = range cycle {
|
for range cycle {
|
||||||
check.errorf(obj.Pos(), "\t%s refers to", obj.Name()) // secondary error, \t indented
|
check.errorf(obj.Pos(), "\t%s refers to", obj.Name()) // secondary error, \t indented
|
||||||
i++
|
i++
|
||||||
if i >= len(cycle) {
|
if i >= len(cycle) {
|
||||||
|
|
|
@ -24,11 +24,11 @@ func append1() {
|
||||||
_ = append(s, b)
|
_ = append(s, b)
|
||||||
_ = append(s, x /* ERROR cannot pass argument x */ )
|
_ = append(s, x /* ERROR cannot pass argument x */ )
|
||||||
_ = append(s, s /* ERROR cannot pass argument s */ )
|
_ = append(s, s /* ERROR cannot pass argument s */ )
|
||||||
_ = append(s /* ERROR can only use ... with matching parameter */ ...)
|
_ = append(s... /* ERROR can only use ... with matching parameter */ )
|
||||||
_ = append(s, b, s /* ERROR can only use ... with matching parameter */ ...)
|
_ = append(s, b, s... /* ERROR can only use ... with matching parameter */ )
|
||||||
_ = append(s, 1, 2, 3)
|
_ = append(s, 1, 2, 3)
|
||||||
_ = append(s, 1, 2, 3, x /* ERROR cannot pass argument x */ , 5, 6, 6)
|
_ = append(s, 1, 2, 3, x /* ERROR cannot pass argument x */ , 5, 6, 6)
|
||||||
_ = append(s, 1, 2, s /* ERROR can only use ... with matching parameter */ ...)
|
_ = append(s, 1, 2, s... /* ERROR can only use ... with matching parameter */ )
|
||||||
_ = append([]interface{}(nil), 1, 2, "foo", x, 3.1425, false)
|
_ = append([]interface{}(nil), 1, 2, "foo", x, 3.1425, false)
|
||||||
|
|
||||||
type S []byte
|
type S []byte
|
||||||
|
|
|
@ -439,7 +439,7 @@ func _calls() {
|
||||||
fv(s /* ERROR "cannot pass" */ )
|
fv(s /* ERROR "cannot pass" */ )
|
||||||
fv(s...)
|
fv(s...)
|
||||||
fv(x /* ERROR "cannot use" */ ...)
|
fv(x /* ERROR "cannot use" */ ...)
|
||||||
fv(1, s /* ERROR "can only use ... with matching parameter" */ ...)
|
fv(1, s... /* ERROR "can only use ... with matching parameter" */ )
|
||||||
fv(gs /* ERROR "cannot pass" */ ())
|
fv(gs /* ERROR "cannot pass" */ ())
|
||||||
fv(gs /* ERROR "cannot pass" */ ()...)
|
fv(gs /* ERROR "cannot pass" */ ()...)
|
||||||
|
|
||||||
|
@ -448,7 +448,7 @@ func _calls() {
|
||||||
t.fm(1, 2.0, x)
|
t.fm(1, 2.0, x)
|
||||||
t.fm(s /* ERROR "cannot pass" */ )
|
t.fm(s /* ERROR "cannot pass" */ )
|
||||||
t.fm(g1())
|
t.fm(g1())
|
||||||
t.fm(1, s /* ERROR "can only use ... with matching parameter" */ ...)
|
t.fm(1, s... /* ERROR "can only use ... with matching parameter" */ )
|
||||||
t.fm(gs /* ERROR "cannot pass" */ ())
|
t.fm(gs /* ERROR "cannot pass" */ ())
|
||||||
t.fm(gs /* ERROR "cannot pass" */ ()...)
|
t.fm(gs /* ERROR "cannot pass" */ ()...)
|
||||||
|
|
||||||
|
@ -456,7 +456,7 @@ func _calls() {
|
||||||
T.fm(t, 1, 2.0, x)
|
T.fm(t, 1, 2.0, x)
|
||||||
T.fm(t, s /* ERROR "cannot pass" */ )
|
T.fm(t, s /* ERROR "cannot pass" */ )
|
||||||
T.fm(t, g1())
|
T.fm(t, g1())
|
||||||
T.fm(t, 1, s /* ERROR "can only use ... with matching parameter" */ ...)
|
T.fm(t, 1, s... /* ERROR "can only use ... with matching parameter" */ )
|
||||||
T.fm(t, gs /* ERROR "cannot pass" */ ())
|
T.fm(t, gs /* ERROR "cannot pass" */ ())
|
||||||
T.fm(t, gs /* ERROR "cannot pass" */ ()...)
|
T.fm(t, gs /* ERROR "cannot pass" */ ()...)
|
||||||
|
|
||||||
|
@ -465,7 +465,7 @@ func _calls() {
|
||||||
i.fm(1, 2.0, x)
|
i.fm(1, 2.0, x)
|
||||||
i.fm(s /* ERROR "cannot pass" */ )
|
i.fm(s /* ERROR "cannot pass" */ )
|
||||||
i.fm(g1())
|
i.fm(g1())
|
||||||
i.fm(1, s /* ERROR "can only use ... with matching parameter" */ ...)
|
i.fm(1, s... /* ERROR "can only use ... with matching parameter" */ )
|
||||||
i.fm(gs /* ERROR "cannot pass" */ ())
|
i.fm(gs /* ERROR "cannot pass" */ ())
|
||||||
i.fm(gs /* ERROR "cannot pass" */ ()...)
|
i.fm(gs /* ERROR "cannot pass" */ ()...)
|
||||||
|
|
||||||
|
|
|
@ -41,3 +41,33 @@ func issue9182() {
|
||||||
// no error for composite literal based on unknown type
|
// no error for composite literal based on unknown type
|
||||||
_ = Point{x: 1, y: 2}
|
_ = Point{x: 1, y: 2}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func f0() (a []int) { return }
|
||||||
|
func f1() (a []int, b int) { return }
|
||||||
|
func f2() (a, b []int) { return }
|
||||||
|
|
||||||
|
func append_([]int, ...int) {}
|
||||||
|
|
||||||
|
func issue9473(a []int, b ...int) {
|
||||||
|
// variadic builtin function
|
||||||
|
_ = append(f0())
|
||||||
|
_ = append(f0(), f0()...)
|
||||||
|
_ = append(f1())
|
||||||
|
_ = append(f2 /* ERROR cannot pass argument */ ())
|
||||||
|
_ = append(f2()... /* ERROR cannot use ... */ )
|
||||||
|
_ = append(f0(), f1 /* ERROR 2-valued expression */ ())
|
||||||
|
_ = append(f0(), f2 /* ERROR 2-valued expression */ ())
|
||||||
|
_ = append(f0(), f1()... /* ERROR cannot use ... */ )
|
||||||
|
_ = append(f0(), f2()... /* ERROR cannot use ... */ )
|
||||||
|
|
||||||
|
// variadic user-defined function
|
||||||
|
append_(f0())
|
||||||
|
append_(f0(), f0()...)
|
||||||
|
append_(f1())
|
||||||
|
append_(f2 /* ERROR cannot pass argument */ ())
|
||||||
|
append_(f2()... /* ERROR cannot use ... */ )
|
||||||
|
append_(f0(), f1 /* ERROR 2-valued expression */ ())
|
||||||
|
append_(f0(), f2 /* ERROR 2-valued expression */ ())
|
||||||
|
append_(f0(), f1()... /* ERROR cannot use */ )
|
||||||
|
append_(f0(), f2()... /* ERROR cannot use */ )
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue