aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/types2/assignments.go
diff options
context:
space:
mode:
authorRobert Griesemer <gri@golang.org>2021-04-20 16:18:59 -0700
committerRobert Griesemer <gri@golang.org>2021-04-22 04:04:47 +0000
commitece59353645d250162e47c792da77cbb44a84f01 (patch)
tree2badb6b2e69a4e8e1a277780a298c354115db295 /src/cmd/compile/internal/types2/assignments.go
parent48b368b01fa1f4f9a4380722f03b35d449a09871 (diff)
downloadgo-ece59353645d250162e47c792da77cbb44a84f01.tar.gz
go-ece59353645d250162e47c792da77cbb44a84f01.zip
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>
Diffstat (limited to 'src/cmd/compile/internal/types2/assignments.go')
-rw-r--r--src/cmd/compile/internal/types2/assignments.go98
1 files changed, 59 insertions, 39 deletions
diff --git a/src/cmd/compile/internal/types2/assignments.go b/src/cmd/compile/internal/types2/assignments.go
index ddcb5e00b3..ec9fdbba62 100644
--- a/src/cmd/compile/internal/types2/assignments.go
+++ b/src/cmd/compile/internal/types2/assignments.go
@@ -330,40 +330,59 @@ func (check *Checker) shortVarDecl(pos syntax.Pos, lhs, rhs []syntax.Expr) {
scope := check.scope
// collect lhs variables
- var newVars []*Var
- var lhsVars = make([]*Var, len(lhs))
+ seen := make(map[string]bool, len(lhs))
+ lhsVars := make([]*Var, len(lhs))
+ newVars := make([]*Var, 0, len(lhs))
+ hasErr := false
for i, lhs := range lhs {
- var obj *Var
- if ident, _ := lhs.(*syntax.Name); ident != nil {
- // Use the correct obj if the ident is redeclared. The
- // variable's scope starts after the declaration; so we
- // must use Scope.Lookup here and call Scope.Insert
- // (via check.declare) later.
- name := ident.Value
- if alt := scope.Lookup(name); alt != nil {
- // redeclared object must be a variable
- if alt, _ := alt.(*Var); alt != nil {
- obj = alt
- } else {
- check.errorf(lhs, "cannot assign to %s", lhs)
- }
- check.recordUse(ident, alt)
+ ident, _ := lhs.(*syntax.Name)
+ 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
+ // variable's scope starts after the declaration; so we
+ // must use Scope.Lookup here and call Scope.Insert
+ // (via check.declare) later.
+ if alt := scope.Lookup(name); alt != nil {
+ check.recordUse(ident, alt)
+ // redeclared object must be a variable
+ if obj, _ := alt.(*Var); obj != nil {
+ lhsVars[i] = obj
} else {
- // declare new variable, possibly a blank (_) variable
- obj = NewVar(ident.Pos(), check.pkg, name, nil)
- if name != "_" {
- newVars = append(newVars, obj)
- }
- check.recordDef(ident, obj)
+ check.errorf(lhs, "cannot assign to %s", lhs)
+ hasErr = true
}
- } else {
- check.useLHS(lhs)
- check.errorf(lhs, "cannot declare %s", lhs)
+ continue
}
+
+ // declare new variable
+ obj := NewVar(ident.Pos(), check.pkg, name, nil)
+ lhsVars[i] = obj
+ newVars = append(newVars, obj)
+ check.recordDef(ident, obj)
+ }
+
+ // create dummy variables where the lhs is invalid
+ for i, obj := range lhsVars {
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)
@@ -371,17 +390,18 @@ func (check *Checker) shortVarDecl(pos syntax.Pos, lhs, rhs []syntax.Expr) {
// process function literals in rhs expressions before scope changes
check.processDelayed(top)
- // declare new variables
- if len(newVars) > 0 {
- // spec: "The scope of a constant or variable identifier declared inside
- // a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl
- // for short variable declarations) and ends at the end of the innermost
- // containing block."
- scopePos := syntax.EndPos(rhs[len(rhs)-1])
- for _, obj := range newVars {
- check.declare(scope, nil, obj, scopePos) // recordObject already called
- }
- } else {
+ if len(newVars) == 0 && !hasErr {
check.softErrorf(pos, "no new variables on left side of :=")
+ return
+ }
+
+ // declare new variables
+ // spec: "The scope of a constant or variable identifier declared inside
+ // a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl
+ // for short variable declarations) and ends at the end of the innermost
+ // containing block."
+ scopePos := syntax.EndPos(rhs[len(rhs)-1])
+ for _, obj := range newVars {
+ check.declare(scope, nil, obj, scopePos) // id = nil: recordDef already called
}
}