go.tools/go.types: more missing assigment checks implemented
Assignments to "comma, ok" expressions on the lhs of an assignment are not permitted unless we have map index "comma, ok" expression. Created new operand mode 'mapindex' to distinguish this case. Renamed mode 'valueok' to the more commonly used 'commaok' term, which also makes it easier to distinguish from simply 'value'. Added corresponding tests. Fixes a TODO. R=adonovan CC=golang-dev https://golang.org/cl/14526049
This commit is contained in:
parent
f50f6c858a
commit
7ca228a514
|
@ -26,7 +26,7 @@ func (check *checker) assignment(x *operand, T Type) bool {
|
||||||
switch x.mode {
|
switch x.mode {
|
||||||
case invalid:
|
case invalid:
|
||||||
return true // error reported before
|
return true // error reported before
|
||||||
case constant, variable, value, valueok:
|
case constant, variable, mapindex, value, commaok:
|
||||||
// ok
|
// ok
|
||||||
default:
|
default:
|
||||||
unreachable()
|
unreachable()
|
||||||
|
@ -99,12 +99,7 @@ func (check *checker) initConst(lhs *Const, x *operand) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// rhs type must be a valid constant type
|
assert(isConstType(x.typ))
|
||||||
if !isConstType(x.typ) {
|
|
||||||
check.errorf(x.pos(), "%s has invalid constant type", x)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
lhs.val = x.val
|
lhs.val = x.val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,14 +188,18 @@ func (check *checker) assignVar(lhs ast.Expr, x *operand) Type {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if z.mode == constant || z.mode == value {
|
// spec: Each left-hand side operand must be addressable, a map index
|
||||||
check.errorf(z.pos(), "cannot assign to non-variable %s", &z)
|
// expression, or the blank identifier. Operands may be parenthesized.
|
||||||
|
switch z.mode {
|
||||||
|
case invalid:
|
||||||
|
return nil
|
||||||
|
case variable, mapindex:
|
||||||
|
// ok
|
||||||
|
default:
|
||||||
|
check.errorf(z.pos(), "cannot assign to %s", &z)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(gri) z.mode can also be valueok which in some cases is ok (maps)
|
|
||||||
// but in others isn't (channels). Complete the checks here.
|
|
||||||
|
|
||||||
if !check.assignment(x, z.typ) {
|
if !check.assignment(x, z.typ) {
|
||||||
if x.mode != invalid {
|
if x.mode != invalid {
|
||||||
check.errorf(x.pos(), "cannot assign %s to %s", x, &z)
|
check.errorf(x.pos(), "cannot assign %s to %s", x, &z)
|
||||||
|
@ -258,7 +257,7 @@ func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, returnPos token.Pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !returnPos.IsValid() && x.mode == valueok && l == 2 {
|
if !returnPos.IsValid() && (x.mode == mapindex || x.mode == commaok) && l == 2 {
|
||||||
// comma-ok expression (not permitted with return statements)
|
// comma-ok expression (not permitted with return statements)
|
||||||
x.mode = value
|
x.mode = value
|
||||||
t1 := check.initVar(lhs[0], &x)
|
t1 := check.initVar(lhs[0], &x)
|
||||||
|
@ -328,7 +327,7 @@ func (check *checker) assignVars(lhs, rhs []ast.Expr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if x.mode == valueok && l == 2 {
|
if (x.mode == mapindex || x.mode == commaok) && l == 2 {
|
||||||
// comma-ok expression
|
// comma-ok expression
|
||||||
x.mode = value
|
x.mode = value
|
||||||
t1 := check.assignVar(lhs[0], &x)
|
t1 := check.assignVar(lhs[0], &x)
|
||||||
|
|
|
@ -130,7 +130,7 @@ func unpack(get getter, n int, allowCommaOk bool) (getter, int) {
|
||||||
}, t.Len()
|
}, t.Len()
|
||||||
}
|
}
|
||||||
|
|
||||||
if x0.mode == valueok {
|
if x0.mode == mapindex || x0.mode == commaok {
|
||||||
// comma-ok value
|
// comma-ok value
|
||||||
if allowCommaOk {
|
if allowCommaOk {
|
||||||
return func(x *operand, i int) {
|
return func(x *operand, i int) {
|
||||||
|
|
|
@ -102,7 +102,7 @@ func EvalNode(fset *token.FileSet, node ast.Expr, pkg *Package, scope *Scope) (t
|
||||||
case constant:
|
case constant:
|
||||||
val = x.val
|
val = x.val
|
||||||
fallthrough
|
fallthrough
|
||||||
case typexpr, variable, value, valueok:
|
case typexpr, variable, mapindex, value, commaok:
|
||||||
typ = x.typ
|
typ = x.typ
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -107,7 +107,7 @@ func (check *checker) unary(x *operand, op token.Token) {
|
||||||
x.mode = invalid
|
x.mode = invalid
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
x.mode = valueok
|
x.mode = commaok
|
||||||
x.typ = typ.elt
|
x.typ = typ.elt
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1168,7 +1168,7 @@ func (check *checker) expr0(x *operand, e ast.Expr, hint Type) exprKind {
|
||||||
}
|
}
|
||||||
goto Error
|
goto Error
|
||||||
}
|
}
|
||||||
x.mode = valueok
|
x.mode = mapindex
|
||||||
x.typ = typ.elt
|
x.typ = typ.elt
|
||||||
x.expr = e
|
x.expr = e
|
||||||
return expression
|
return expression
|
||||||
|
@ -1308,7 +1308,7 @@ func (check *checker) expr0(x *operand, e ast.Expr, hint Type) exprKind {
|
||||||
goto Error
|
goto Error
|
||||||
}
|
}
|
||||||
check.typeAssertion(x.pos(), x, xtyp, T)
|
check.typeAssertion(x.pos(), x, xtyp, T)
|
||||||
x.mode = valueok
|
x.mode = commaok
|
||||||
x.typ = T
|
x.typ = T
|
||||||
|
|
||||||
case *ast.CallExpr:
|
case *ast.CallExpr:
|
||||||
|
|
|
@ -24,8 +24,9 @@ const (
|
||||||
typexpr // operand is a type
|
typexpr // operand is a type
|
||||||
constant // operand is a constant; the operand's typ is a Basic type
|
constant // operand is a constant; the operand's typ is a Basic type
|
||||||
variable // operand is an addressable variable
|
variable // operand is an addressable variable
|
||||||
|
mapindex // operand is a map index expression (acts like a variable on lhs, commaok on rhs of an assignment)
|
||||||
value // operand is a computed value
|
value // operand is a computed value
|
||||||
valueok // like value, but operand may be used in a comma,ok expression
|
commaok // like value, but operand may be used in a comma,ok expression
|
||||||
)
|
)
|
||||||
|
|
||||||
var operandModeString = [...]string{
|
var operandModeString = [...]string{
|
||||||
|
@ -35,8 +36,9 @@ var operandModeString = [...]string{
|
||||||
typexpr: "type",
|
typexpr: "type",
|
||||||
constant: "constant",
|
constant: "constant",
|
||||||
variable: "variable",
|
variable: "variable",
|
||||||
|
mapindex: "map index expression",
|
||||||
value: "value",
|
value: "value",
|
||||||
valueok: "value, ok",
|
commaok: "comma, ok expression",
|
||||||
}
|
}
|
||||||
|
|
||||||
// An operand represents an intermediate value during type checking.
|
// An operand represents an intermediate value during type checking.
|
||||||
|
@ -83,11 +85,14 @@ func (x *operand) pos() token.Pos {
|
||||||
// variable <expr> (<untyped kind> <mode> )
|
// variable <expr> (<untyped kind> <mode> )
|
||||||
// variable <expr> ( <mode> of type <typ>)
|
// variable <expr> ( <mode> of type <typ>)
|
||||||
//
|
//
|
||||||
|
// mapindex <expr> (<untyped kind> <mode> )
|
||||||
|
// mapindex <expr> ( <mode> of type <typ>)
|
||||||
|
//
|
||||||
// value <expr> (<untyped kind> <mode> )
|
// value <expr> (<untyped kind> <mode> )
|
||||||
// value <expr> ( <mode> of type <typ>)
|
// value <expr> ( <mode> of type <typ>)
|
||||||
//
|
//
|
||||||
// valueok <expr> (<untyped kind> <mode> )
|
// commaok <expr> (<untyped kind> <mode> )
|
||||||
// valueok <expr> ( <mode> of type <typ>)
|
// commaok <expr> ( <mode> of type <typ>)
|
||||||
//
|
//
|
||||||
func (x *operand) String() string {
|
func (x *operand) String() string {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|
|
@ -34,7 +34,7 @@ func f(x int, m map[string]int) {
|
||||||
|
|
||||||
// value, ok's
|
// value, ok's
|
||||||
const s = "foo"
|
const s = "foo"
|
||||||
m /* ERROR "m\[s\] \(value, ok of type int\) is not used" */ [s]
|
m /* ERROR "m\[s\] \(map index expression of type int\) is not used" */ [s]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valid ERROR comments can have a variety of forms.
|
// Valid ERROR comments can have a variety of forms.
|
||||||
|
|
|
@ -92,6 +92,34 @@ func assignments1() {
|
||||||
(_) = 0
|
(_) = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func assignments2() {
|
||||||
|
var m map[string][]bool
|
||||||
|
var s []bool
|
||||||
|
var b bool
|
||||||
|
_ = s
|
||||||
|
_ = b
|
||||||
|
|
||||||
|
// assignments to map index expressions are ok
|
||||||
|
s, b = m["foo"]
|
||||||
|
m["foo"] = nil
|
||||||
|
m["foo"] = nil /* ERROR assignment count mismatch */ , false
|
||||||
|
_ = append(m["foo"])
|
||||||
|
_ = append(m["foo"], true)
|
||||||
|
|
||||||
|
var c chan int
|
||||||
|
_, b = <-c
|
||||||
|
<- /* ERROR cannot assign */ c = 0
|
||||||
|
<-c = 0 /* ERROR assignment count mismatch */ , false
|
||||||
|
|
||||||
|
var x interface{}
|
||||||
|
_, b = x.(int)
|
||||||
|
x /* ERROR cannot assign */ .(int) = 0
|
||||||
|
x.(int) = 0 /* ERROR assignment count mismatch */ , false
|
||||||
|
|
||||||
|
assignments2 /* ERROR used as value */ () = nil
|
||||||
|
int /* ERROR not an expression */ = 0
|
||||||
|
}
|
||||||
|
|
||||||
func issue6487() {
|
func issue6487() {
|
||||||
type S struct{x int}
|
type S struct{x int}
|
||||||
_ = &S /* ERROR "cannot take address" */ {}.x
|
_ = &S /* ERROR "cannot take address" */ {}.x
|
||||||
|
|
Loading…
Reference in New Issue