1
0
mirror of https://github.com/golang/go synced 2024-11-16 22:24:47 -07:00

cmd/compile/internal/types2: better errors for invalid short var decls

- rewrite Checker.shortVarDecl core loop for clarity
- match compiler error messages (#43087)
- don't allow multiple identical redeclarations (#45652)

For #43087.
For #45652.

Change-Id: I8c3329a553aa104d7853fbaea8b88049bc9b3b88
Reviewed-on: https://go-review.googlesource.com/c/go/+/312170
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Robert Griesemer 2021-04-20 16:18:59 -07:00
parent 48b368b01f
commit ece5935364
3 changed files with 106 additions and 43 deletions

View File

@ -330,40 +330,59 @@ func (check *Checker) shortVarDecl(pos syntax.Pos, lhs, rhs []syntax.Expr) {
scope := check.scope scope := check.scope
// collect lhs variables // collect lhs variables
var newVars []*Var seen := make(map[string]bool, len(lhs))
var lhsVars = make([]*Var, len(lhs)) lhsVars := make([]*Var, len(lhs))
newVars := make([]*Var, 0, len(lhs))
hasErr := false
for i, lhs := range lhs { for i, lhs := range lhs {
var obj *Var ident, _ := lhs.(*syntax.Name)
if ident, _ := lhs.(*syntax.Name); ident != nil { if ident == nil {
check.useLHS(lhs)
check.errorf(lhs, "non-name %s on left side of :=", lhs)
hasErr = true
continue
}
name := ident.Value
if name == "_" {
continue
}
if seen[name] {
check.errorf(lhs, "%s repeated on left side of :=", lhs)
hasErr = true
continue
}
seen[name] = true
// Use the correct obj if the ident is redeclared. The // Use the correct obj if the ident is redeclared. The
// variable's scope starts after the declaration; so we // variable's scope starts after the declaration; so we
// must use Scope.Lookup here and call Scope.Insert // must use Scope.Lookup here and call Scope.Insert
// (via check.declare) later. // (via check.declare) later.
name := ident.Value
if alt := scope.Lookup(name); alt != nil { if alt := scope.Lookup(name); alt != nil {
check.recordUse(ident, alt)
// redeclared object must be a variable // redeclared object must be a variable
if alt, _ := alt.(*Var); alt != nil { if obj, _ := alt.(*Var); obj != nil {
obj = alt lhsVars[i] = obj
} else { } else {
check.errorf(lhs, "cannot assign to %s", lhs) check.errorf(lhs, "cannot assign to %s", lhs)
hasErr = true
} }
check.recordUse(ident, alt) continue
} else { }
// declare new variable, possibly a blank (_) variable
obj = NewVar(ident.Pos(), check.pkg, name, nil) // declare new variable
if name != "_" { obj := NewVar(ident.Pos(), check.pkg, name, nil)
lhsVars[i] = obj
newVars = append(newVars, obj) newVars = append(newVars, obj)
}
check.recordDef(ident, obj) check.recordDef(ident, obj)
} }
} else {
check.useLHS(lhs) // create dummy variables where the lhs is invalid
check.errorf(lhs, "cannot declare %s", lhs) for i, obj := range lhsVars {
}
if obj == nil { if obj == nil {
obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable lhsVars[i] = NewVar(lhs[i].Pos(), check.pkg, "_", nil)
} }
lhsVars[i] = obj
} }
check.initVars(lhsVars, rhs, nopos) check.initVars(lhsVars, rhs, nopos)
@ -371,17 +390,18 @@ func (check *Checker) shortVarDecl(pos syntax.Pos, lhs, rhs []syntax.Expr) {
// process function literals in rhs expressions before scope changes // process function literals in rhs expressions before scope changes
check.processDelayed(top) check.processDelayed(top)
if len(newVars) == 0 && !hasErr {
check.softErrorf(pos, "no new variables on left side of :=")
return
}
// declare new variables // declare new variables
if len(newVars) > 0 {
// spec: "The scope of a constant or variable identifier declared inside // spec: "The scope of a constant or variable identifier declared inside
// a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl // a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl
// for short variable declarations) and ends at the end of the innermost // for short variable declarations) and ends at the end of the innermost
// containing block." // containing block."
scopePos := syntax.EndPos(rhs[len(rhs)-1]) scopePos := syntax.EndPos(rhs[len(rhs)-1])
for _, obj := range newVars { for _, obj := range newVars {
check.declare(scope, nil, obj, scopePos) // recordObject already called check.declare(scope, nil, obj, scopePos) // id = nil: recordDef already called
}
} else {
check.softErrorf(pos, "no new variables on left side of :=")
} }
} }

View File

@ -0,0 +1,43 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package p
func _() {
a, b, b /* ERROR b repeated on left side of := */ := 1, 2, 3
_ = a
_ = b
}
func _() {
a, _, _ := 1, 2, 3 // multiple _'s ok
_ = a
}
func _() {
var b int
a, b, b /* ERROR b repeated on left side of := */ := 1, 2, 3
_ = a
_ = b
}
func _() {
var a []int
a /* ERROR non-name .* on left side of := */ [0], b := 1, 2
_ = a
_ = b
}
func _() {
var a int
a, a /* ERROR a repeated on left side of := */ := 1, 2
_ = a
}
func _() {
var a, b int
a, b := /* ERROR no new variables on left side of := */ 1, 2
_ = a
_ = b
}

View File

@ -143,11 +143,11 @@ func issue6487() {
} }
func issue6766a() { func issue6766a() {
a, a /* ERROR redeclared */ := 1, 2 a, a /* ERROR a repeated on left side of := */ := 1, 2
_ = a _ = a
a, b, b /* ERROR redeclared */ := 1, 2, 3 a, b, b /* ERROR b repeated on left side of := */ := 1, 2, 3
_ = b _ = b
c, c /* ERROR redeclared */, b := 1, 2, 3 c, c /* ERROR c repeated on left side of := */, b := 1, 2, 3
_ = c _ = c
a, b := /* ERROR no new variables */ 1, 2 a, b := /* ERROR no new variables */ 1, 2
} }