mirror of
https://github.com/golang/go
synced 2024-11-18 17:14:45 -07:00
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
|
||||
arg, nargs, _ = unpack(func(x *operand, i int) { check.expr(x, call.Args[i]) }, nargs, false)
|
||||
if arg == nil {
|
||||
x.mode = invalid
|
||||
return
|
||||
}
|
||||
// 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.
|
||||
// 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) {
|
||||
passSlice := false
|
||||
if call.Ellipsis.IsValid() {
|
||||
// last argument is of the form x...
|
||||
if sig.variadic {
|
||||
passSlice = true
|
||||
} else {
|
||||
if len(call.Args) == 1 && n > 1 {
|
||||
// f()... is not permitted if f() is multi-valued
|
||||
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)
|
||||
// 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++ {
|
||||
arg(x, i)
|
||||
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.
|
||||
// If passSlice is set, the argument is followed by ... in the call.
|
||||
func (check *Checker) argument(sig *Signature, i int, x *operand, passSlice bool) {
|
||||
// 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, ellipsis token.Pos) {
|
||||
n := sig.params.Len()
|
||||
|
||||
// determine parameter type
|
||||
@ -232,13 +240,19 @@ func (check *Checker) argument(sig *Signature, i int, x *operand, passSlice bool
|
||||
return
|
||||
}
|
||||
|
||||
if passSlice {
|
||||
if ellipsis.IsValid() {
|
||||
// argument is of the form x...
|
||||
if i != n-1 {
|
||||
check.errorf(x.pos(), "can only use ... with matching parameter")
|
||||
check.errorf(ellipsis, "can only use ... with matching parameter")
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ func (check *Checker) reportCycle(cycle []*objNode, i int) {
|
||||
obj := cycle[i].obj
|
||||
check.errorf(obj.Pos(), "initialization cycle for %s", obj.Name())
|
||||
// print cycle
|
||||
for _ = range cycle {
|
||||
for range cycle {
|
||||
check.errorf(obj.Pos(), "\t%s refers to", obj.Name()) // secondary error, \t indented
|
||||
i++
|
||||
if i >= len(cycle) {
|
||||
|
6
go/types/testdata/builtins.src
vendored
6
go/types/testdata/builtins.src
vendored
@ -24,11 +24,11 @@ func append1() {
|
||||
_ = append(s, b)
|
||||
_ = append(s, x /* ERROR cannot pass argument x */ )
|
||||
_ = append(s, s /* ERROR cannot pass argument s */ )
|
||||
_ = append(s /* ERROR can only use ... with matching parameter */ ...)
|
||||
_ = append(s, b, 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, 1, 2, 3)
|
||||
_ = 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)
|
||||
|
||||
type S []byte
|
||||
|
8
go/types/testdata/expr3.src
vendored
8
go/types/testdata/expr3.src
vendored
@ -439,7 +439,7 @@ func _calls() {
|
||||
fv(s /* ERROR "cannot pass" */ )
|
||||
fv(s...)
|
||||
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" */ ()...)
|
||||
|
||||
@ -448,7 +448,7 @@ func _calls() {
|
||||
t.fm(1, 2.0, x)
|
||||
t.fm(s /* ERROR "cannot pass" */ )
|
||||
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" */ ()...)
|
||||
|
||||
@ -456,7 +456,7 @@ func _calls() {
|
||||
T.fm(t, 1, 2.0, x)
|
||||
T.fm(t, s /* ERROR "cannot pass" */ )
|
||||
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" */ ()...)
|
||||
|
||||
@ -465,7 +465,7 @@ func _calls() {
|
||||
i.fm(1, 2.0, x)
|
||||
i.fm(s /* ERROR "cannot pass" */ )
|
||||
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" */ ()...)
|
||||
|
||||
|
30
go/types/testdata/issues.src
vendored
30
go/types/testdata/issues.src
vendored
@ -41,3 +41,33 @@ func issue9182() {
|
||||
// no error for composite literal based on unknown type
|
||||
_ = 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
Block a user