1
0
mirror of https://github.com/golang/go synced 2024-11-19 04:24:39 -07:00

go.tools/go/types: add missing checks for short variable declarations

Fixes golang/go#6766.

R=adonovan
CC=golang-dev
https://golang.org/cl/24330044
This commit is contained in:
Robert Griesemer 2013-11-15 14:26:29 -08:00
parent 01f122e48b
commit e785f050b6
3 changed files with 53 additions and 25 deletions

View File

@ -347,11 +347,12 @@ func (check *checker) assignVars(lhs, rhs []ast.Expr) {
check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r) check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r)
} }
func (check *checker) shortVarDecl(lhs, rhs []ast.Expr) { func (check *checker) shortVarDecl(pos token.Pos, lhs, rhs []ast.Expr) {
scope := check.topScope scope := check.topScope
// collect lhs variables // collect lhs variables
vars := make([]*Var, len(lhs)) var newVars []*Var
var lhsVars = make([]*Var, len(lhs))
for i, lhs := range lhs { for i, lhs := range lhs {
var obj *Var var obj *Var
if ident, _ := lhs.(*ast.Ident); ident != nil { if ident, _ := lhs.(*ast.Ident); ident != nil {
@ -368,6 +369,7 @@ func (check *checker) shortVarDecl(lhs, rhs []ast.Expr) {
} else { } else {
// declare new variable // declare new variable
obj = NewVar(ident.Pos(), check.pkg, ident.Name, nil) obj = NewVar(ident.Pos(), check.pkg, ident.Name, nil)
newVars = append(newVars, obj)
} }
if obj != nil { if obj != nil {
check.recordObject(ident, obj) check.recordObject(ident, obj)
@ -378,18 +380,18 @@ func (check *checker) shortVarDecl(lhs, rhs []ast.Expr) {
if obj == nil { if obj == nil {
obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable
} }
vars[i] = obj lhsVars[i] = obj
} }
check.initVars(vars, rhs, token.NoPos) check.initVars(lhsVars, rhs, token.NoPos)
// declare variables // declare new variables
n := scope.Len() if len(newVars) > 0 {
for _, obj := range vars { for _, obj := range newVars {
scope.Insert(obj) check.declare(scope, nil, obj) // recordObject already called
} }
if n == scope.Len() { } else {
check.errorf(lhs[0].Pos(), "no new variables on left side of :=") check.errorf(pos, "no new variables on left side of :=")
} }
} }

View File

@ -235,7 +235,7 @@ func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) {
return return
} }
if s.Tok == token.DEFINE { if s.Tok == token.DEFINE {
check.shortVarDecl(s.Lhs, s.Rhs) check.shortVarDecl(s.TokPos, s.Lhs, s.Rhs)
} else { } else {
// regular assignment // regular assignment
check.assignVars(s.Lhs, s.Rhs) check.assignVars(s.Lhs, s.Rhs)
@ -570,8 +570,9 @@ func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) {
rhs := [2]Type{key, val} rhs := [2]Type{key, val}
if decl { if decl {
// declaration; variable scope starts after the range clause // short variable declaration; variable scope starts after the range clause
var idents []*ast.Ident // (the for loop opens a new scope, so variables on the lhs never redeclare
// previously declared variables)
var vars []*Var var vars []*Var
for i, lhs := range lhs { for i, lhs := range lhs {
if lhs == nil { if lhs == nil {
@ -579,17 +580,20 @@ func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) {
} }
// determine lhs variable // determine lhs variable
name := "_" // dummy, in case lhs is not an identifier var obj *Var
ident, _ := lhs.(*ast.Ident) if ident, _ := lhs.(*ast.Ident); ident != nil {
if ident != nil { // declare new variable
name = ident.Name name := ident.Name
obj = NewVar(ident.Pos(), check.pkg, name, nil)
check.recordObject(ident, obj)
// _ variables don't count as new variables
if name != "_" {
vars = append(vars, obj)
}
} else { } else {
check.errorf(lhs.Pos(), "cannot declare %s", lhs) check.errorf(lhs.Pos(), "cannot declare %s", lhs)
obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable
} }
idents = append(idents, ident)
obj := NewVar(lhs.Pos(), check.pkg, name, nil)
vars = append(vars, obj)
// initialize lhs variable // initialize lhs variable
x.mode = value x.mode = value
@ -599,8 +603,12 @@ func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) {
} }
// declare variables // declare variables
for i, ident := range idents { if len(vars) > 0 {
check.declare(check.topScope, ident, vars[i]) for _, obj := range vars {
check.declare(check.topScope, nil, obj) // recordObject already called
}
} else {
check.errorf(s.TokPos, "no new variables on left side of :=")
} }
} else { } else {
// ordinary assignment // ordinary assignment

View File

@ -137,7 +137,17 @@ func issue6487() {
_ = &m /* ERROR "cannot take address" */ ["foo"].x _ = &m /* ERROR "cannot take address" */ ["foo"].x
} }
func shortVarDecls() { func issue6766a() {
a, a /* ERROR redeclared */ := 1, 2
_ = a
a, b, b /* ERROR redeclared */ := 1, 2, 3
_ = b
c, c /* ERROR redeclared */, b := 1, 2, 3
_ = c
a, b := /* ERROR no new variables */ 1, 2
}
func shortVarDecls1() {
const c = 0 const c = 0
type d int type d int
a, b, c /* ERROR "cannot assign" */ , d /* ERROR "cannot assign" */ := 1, "zwei", 3.0, 4 a, b, c /* ERROR "cannot assign" */ , d /* ERROR "cannot assign" */ := 1, "zwei", 3.0, 4
@ -684,6 +694,14 @@ func rangeloops2() {
for _, r /* ERROR cannot assign */ = range "foo" {} for _, r /* ERROR cannot assign */ = range "foo" {}
} }
func issue6766b() {
for _ := /* ERROR no new variables */ range "" {}
for a, a /* ERROR redeclared */ := range "" { _ = a }
var a int
_ = a
for a, a /* ERROR redeclared */ := range []int{1, 2, 3} { _ = a }
}
func labels0() { func labels0() {
goto L0 goto L0
goto L1 goto L1