mirror of
https://github.com/golang/go
synced 2024-11-17 03:04:44 -07:00
go.types, types2: factor out checking of LHS in variable assignment
Step towards disentangling assignment checking functionality. In preparation for reverse inference of function type arguments, but independently helpful in better separating concerns in the code. Change-Id: I9bac9d8005090c00d9ae6c5cfa13765aacce6b12 Reviewed-on: https://go-review.googlesource.com/c/go/+/477855 Auto-Submit: Robert Griesemer <gri@google.com> Reviewed-by: Robert Findley <rfindley@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Robert Griesemer <gri@google.com> Reviewed-by: Robert Griesemer <gri@google.com>
This commit is contained in:
parent
c013ed5b52
commit
70d836debb
@ -164,26 +164,21 @@ func (check *Checker) initVar(lhs *Var, x *operand, context string) Type {
|
|||||||
return x.typ
|
return x.typ
|
||||||
}
|
}
|
||||||
|
|
||||||
func (check *Checker) assignVar(lhs syntax.Expr, x *operand) Type {
|
// lhsVar checks a lhs variable in an assignment and returns its type.
|
||||||
if x.mode == invalid || x.typ == Typ[Invalid] {
|
// lhsVar takes care of not counting a lhs identifier as a "use" of
|
||||||
check.use(lhs)
|
// that identifier. The result is nil if it is the blank identifier,
|
||||||
return nil
|
// and Typ[Invalid] if it is an invalid lhs expression.
|
||||||
}
|
func (check *Checker) lhsVar(lhs syntax.Expr) Type {
|
||||||
|
|
||||||
// Determine if the lhs is a (possibly parenthesized) identifier.
|
// Determine if the lhs is a (possibly parenthesized) identifier.
|
||||||
ident, _ := unparen(lhs).(*syntax.Name)
|
ident, _ := unparen(lhs).(*syntax.Name)
|
||||||
|
|
||||||
// Don't evaluate lhs if it is the blank identifier.
|
// Don't evaluate lhs if it is the blank identifier.
|
||||||
if ident != nil && ident.Value == "_" {
|
if ident != nil && ident.Value == "_" {
|
||||||
check.recordDef(ident, nil)
|
check.recordDef(ident, nil)
|
||||||
check.assignment(x, nil, "assignment to _ identifier")
|
|
||||||
if x.mode == invalid {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return x.typ
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the lhs is an identifier denoting a variable v, this assignment
|
// If the lhs is an identifier denoting a variable v, this reference
|
||||||
// is not a 'use' of v. Remember current value of v.used and restore
|
// is not a 'use' of v. Remember current value of v.used and restore
|
||||||
// after evaluating the lhs via check.expr.
|
// after evaluating the lhs via check.expr.
|
||||||
var v *Var
|
var v *Var
|
||||||
@ -200,37 +195,58 @@ func (check *Checker) assignVar(lhs syntax.Expr, x *operand) Type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var z operand
|
var x operand
|
||||||
check.expr(&z, lhs)
|
check.expr(&x, lhs)
|
||||||
|
|
||||||
if v != nil {
|
if v != nil {
|
||||||
v.used = v_used // restore v.used
|
v.used = v_used // restore v.used
|
||||||
}
|
}
|
||||||
|
|
||||||
if z.mode == invalid || z.typ == Typ[Invalid] {
|
if x.mode == invalid || x.typ == Typ[Invalid] {
|
||||||
return nil
|
return Typ[Invalid]
|
||||||
}
|
}
|
||||||
|
|
||||||
// spec: "Each left-hand side operand must be addressable, a map index
|
// spec: "Each left-hand side operand must be addressable, a map index
|
||||||
// expression, or the blank identifier. Operands may be parenthesized."
|
// expression, or the blank identifier. Operands may be parenthesized."
|
||||||
switch z.mode {
|
switch x.mode {
|
||||||
case invalid:
|
case invalid:
|
||||||
return nil
|
return Typ[Invalid]
|
||||||
case variable, mapindex:
|
case variable, mapindex:
|
||||||
// ok
|
// ok
|
||||||
default:
|
default:
|
||||||
if sel, ok := z.expr.(*syntax.SelectorExpr); ok {
|
if sel, ok := x.expr.(*syntax.SelectorExpr); ok {
|
||||||
var op operand
|
var op operand
|
||||||
check.expr(&op, sel.X)
|
check.expr(&op, sel.X)
|
||||||
if op.mode == mapindex {
|
if op.mode == mapindex {
|
||||||
check.errorf(&z, UnaddressableFieldAssign, "cannot assign to struct field %s in map", syntax.String(z.expr))
|
check.errorf(&x, UnaddressableFieldAssign, "cannot assign to struct field %s in map", syntax.String(x.expr))
|
||||||
return nil
|
return Typ[Invalid]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
check.errorf(&z, UnassignableOperand, "cannot assign to %s", &z)
|
check.errorf(&x, UnassignableOperand, "cannot assign to %s", &x)
|
||||||
|
return Typ[Invalid]
|
||||||
|
}
|
||||||
|
|
||||||
|
return x.typ
|
||||||
|
}
|
||||||
|
|
||||||
|
// assignVar checks the assignment lhs = x and returns the type of x.
|
||||||
|
// If the assignment is invalid, the result is nil.
|
||||||
|
func (check *Checker) assignVar(lhs syntax.Expr, x *operand) Type {
|
||||||
|
if x.mode == invalid || x.typ == Typ[Invalid] {
|
||||||
|
check.use(lhs)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
check.assignment(x, z.typ, "assignment")
|
T := check.lhsVar(lhs) // nil if lhs is _
|
||||||
|
if T == Typ[Invalid] {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
context := "assignment"
|
||||||
|
if T == nil {
|
||||||
|
context = "assignment to _ identifier"
|
||||||
|
}
|
||||||
|
check.assignment(x, T, context)
|
||||||
if x.mode == invalid {
|
if x.mode == invalid {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -158,26 +158,21 @@ func (check *Checker) initVar(lhs *Var, x *operand, context string) Type {
|
|||||||
return x.typ
|
return x.typ
|
||||||
}
|
}
|
||||||
|
|
||||||
func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type {
|
// lhsVar checks a lhs variable in an assignment and returns its type.
|
||||||
if x.mode == invalid || x.typ == Typ[Invalid] {
|
// lhsVar takes care of not counting a lhs identifier as a "use" of
|
||||||
check.useLHS(lhs)
|
// that identifier. The result is nil if it is the blank identifier,
|
||||||
return nil
|
// and Typ[Invalid] if it is an invalid lhs expression.
|
||||||
}
|
func (check *Checker) lhsVar(lhs ast.Expr) Type {
|
||||||
|
|
||||||
// Determine if the lhs is a (possibly parenthesized) identifier.
|
// Determine if the lhs is a (possibly parenthesized) identifier.
|
||||||
ident, _ := unparen(lhs).(*ast.Ident)
|
ident, _ := unparen(lhs).(*ast.Ident)
|
||||||
|
|
||||||
// Don't evaluate lhs if it is the blank identifier.
|
// Don't evaluate lhs if it is the blank identifier.
|
||||||
if ident != nil && ident.Name == "_" {
|
if ident != nil && ident.Name == "_" {
|
||||||
check.recordDef(ident, nil)
|
check.recordDef(ident, nil)
|
||||||
check.assignment(x, nil, "assignment to _ identifier")
|
|
||||||
if x.mode == invalid {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return x.typ
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the lhs is an identifier denoting a variable v, this assignment
|
// If the lhs is an identifier denoting a variable v, this reference
|
||||||
// is not a 'use' of v. Remember current value of v.used and restore
|
// is not a 'use' of v. Remember current value of v.used and restore
|
||||||
// after evaluating the lhs via check.expr.
|
// after evaluating the lhs via check.expr.
|
||||||
var v *Var
|
var v *Var
|
||||||
@ -194,37 +189,58 @@ func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var z operand
|
var x operand
|
||||||
check.expr(&z, lhs)
|
check.expr(&x, lhs)
|
||||||
|
|
||||||
if v != nil {
|
if v != nil {
|
||||||
v.used = v_used // restore v.used
|
v.used = v_used // restore v.used
|
||||||
}
|
}
|
||||||
|
|
||||||
if z.mode == invalid || z.typ == Typ[Invalid] {
|
if x.mode == invalid || x.typ == Typ[Invalid] {
|
||||||
return nil
|
return Typ[Invalid]
|
||||||
}
|
}
|
||||||
|
|
||||||
// spec: "Each left-hand side operand must be addressable, a map index
|
// spec: "Each left-hand side operand must be addressable, a map index
|
||||||
// expression, or the blank identifier. Operands may be parenthesized."
|
// expression, or the blank identifier. Operands may be parenthesized."
|
||||||
switch z.mode {
|
switch x.mode {
|
||||||
case invalid:
|
case invalid:
|
||||||
return nil
|
return Typ[Invalid]
|
||||||
case variable, mapindex:
|
case variable, mapindex:
|
||||||
// ok
|
// ok
|
||||||
default:
|
default:
|
||||||
if sel, ok := z.expr.(*ast.SelectorExpr); ok {
|
if sel, ok := x.expr.(*ast.SelectorExpr); ok {
|
||||||
var op operand
|
var op operand
|
||||||
check.expr(&op, sel.X)
|
check.expr(&op, sel.X)
|
||||||
if op.mode == mapindex {
|
if op.mode == mapindex {
|
||||||
check.errorf(&z, UnaddressableFieldAssign, "cannot assign to struct field %s in map", ExprString(z.expr))
|
check.errorf(&x, UnaddressableFieldAssign, "cannot assign to struct field %s in map", ExprString(x.expr))
|
||||||
return nil
|
return Typ[Invalid]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
check.errorf(&z, UnassignableOperand, "cannot assign to %s", &z)
|
check.errorf(&x, UnassignableOperand, "cannot assign to %s", &x)
|
||||||
|
return Typ[Invalid]
|
||||||
|
}
|
||||||
|
|
||||||
|
return x.typ
|
||||||
|
}
|
||||||
|
|
||||||
|
// assignVar checks the assignment lhs = x and returns the type of x.
|
||||||
|
// If the assignment is invalid, the result is nil.
|
||||||
|
func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type {
|
||||||
|
if x.mode == invalid || x.typ == Typ[Invalid] {
|
||||||
|
check.useLHS(lhs)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
check.assignment(x, z.typ, "assignment")
|
T := check.lhsVar(lhs) // nil if lhs is _
|
||||||
|
if T == Typ[Invalid] {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
context := "assignment"
|
||||||
|
if T == nil {
|
||||||
|
context = "assignment to _ identifier"
|
||||||
|
}
|
||||||
|
check.assignment(x, T, context)
|
||||||
if x.mode == invalid {
|
if x.mode == invalid {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user