if lhs.typ == nil {
lhs.typ = Typ[Invalid]
}
- // Note: This was reverted in go/types (https://golang.org/cl/292751).
- // TODO(gri): decide what to do (also affects test/run.go exclusion list)
- lhs.used = true // avoid follow-on "declared and not used" errors
return nil
}
check.assignment(x, lhs.typ, context)
if x.mode == invalid {
- lhs.used = true // avoid follow-on "declared and not used" errors
return nil
}
// 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)
+ check.useLHS(lhs)
return nil
}
rhs, commaOk := check.exprList(orig_rhs, len(lhs) == 2)
if len(lhs) != len(rhs) {
- check.use(lhs...)
+ check.useLHS(lhs...)
// don't report an error if we already reported one
for _, x := range rhs {
if x.mode == invalid {
return
}
- ok := true
for i, lhs := range lhs {
- if check.assignVar(lhs, rhs[i]) == nil {
- ok = false
- }
- }
-
- // avoid follow-on "declared and not used" errors if any assignment failed
- if !ok {
- // don't call check.use to avoid re-evaluation of the lhs expressions
- for _, lhs := range lhs {
- if name, _ := unparen(lhs).(*syntax.Name); name != nil {
- if obj := check.lookup(name.Value); obj != nil {
- // see comment in assignVar
- if v, _ := obj.(*Var); v != nil && v.pkg == check.pkg {
- v.used = true
- }
- }
- }
- }
+ check.assignVar(lhs, rhs[i])
}
}
for i, lhs := range lhs {
ident, _ := lhs.(*syntax.Name)
if ident == nil {
- check.use(lhs)
+ check.useLHS(lhs)
check.errorf(lhs, BadDecl, "non-name %s on left side of :=", lhs)
hasErr = true
continue
// use type-checks each argument.
// Useful to make sure expressions are evaluated
-// (and variables are "used") in the presence of other errors.
-// The arguments may be nil.
-// TODO(gri) make this accept a []syntax.Expr and use an unpack function when we have a ListExpr?
-func (check *Checker) use(arg ...syntax.Expr) {
+// (and variables are "used") in the presence of
+// other errors. Arguments may be nil.
+func (check *Checker) use(args ...syntax.Expr) {
+ for _, e := range args {
+ check.use1(e, false)
+ }
+}
+
+// useLHS is like use, but doesn't "use" top-level identifiers.
+// It should be called instead of use if the arguments are
+// expressions on the lhs of an assignment.
+func (check *Checker) useLHS(args ...syntax.Expr) {
+ for _, e := range args {
+ check.use1(e, true)
+ }
+}
+
+func (check *Checker) use1(e syntax.Expr, lhs bool) {
var x operand
- for _, e := range arg {
- switch n := e.(type) {
- case nil:
- // some AST fields may be nil (e.g., elements of syntax.SliceExpr.Index)
- // TODO(gri) can those fields really make it here?
- continue
- case *syntax.Name:
- // don't report an error evaluating blank
- if n.Value == "_" {
- continue
+ switch n := unparen(e).(type) {
+ case nil:
+ // nothing to do
+ case *syntax.Name:
+ // don't report an error evaluating blank
+ if n.Value == "_" {
+ break
+ }
+ // If the lhs is an identifier denoting a variable v, this assignment
+ // is not a 'use' of v. Remember current value of v.used and restore
+ // after evaluating the lhs via check.rawExpr.
+ var v *Var
+ var v_used bool
+ if lhs {
+ if _, obj := check.scope.LookupParent(n.Value, nopos); obj != nil {
+ // It's ok to mark non-local variables, but ignore variables
+ // from other packages to avoid potential race conditions with
+ // dot-imported variables.
+ if w, _ := obj.(*Var); w != nil && w.pkg == check.pkg {
+ v = w
+ v_used = v.used
+ }
}
- case *syntax.ListExpr:
- check.use(n.ElemList...)
- continue
}
- check.rawExpr(&x, e, nil, false)
+ check.rawExpr(&x, n, nil, true)
+ if v != nil {
+ v.used = v_used // restore v.used
+ }
+ case *syntax.ListExpr:
+ for _, e := range n.ElemList {
+ check.use1(e, lhs)
+ }
+ default:
+ check.rawExpr(&x, e, nil, true)
}
}
// 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.use(lhs)
+ check.useLHS(lhs)
return nil
}
rhs, commaOk := check.exprList(origRHS, len(lhs) == 2)
if len(lhs) != len(rhs) {
- check.use(lhs...)
+ check.useLHS(lhs...)
// don't report an error if we already reported one
for _, x := range rhs {
if x.mode == invalid {
for i, lhs := range lhs {
ident, _ := lhs.(*ast.Ident)
if ident == nil {
- check.use(lhs)
+ check.useLHS(lhs)
// TODO(rFindley) this is redundant with a parser error. Consider omitting?
check.errorf(lhs, BadDecl, "non-name %s on left side of :=", lhs)
hasErr = true
// use type-checks each argument.
// Useful to make sure expressions are evaluated
-// (and variables are "used") in the presence of other errors.
-// The arguments may be nil.
-func (check *Checker) use(arg ...ast.Expr) {
+// (and variables are "used") in the presence of
+// other errors. Arguments may be nil.
+func (check *Checker) use(args ...ast.Expr) {
+ for _, e := range args {
+ check.use1(e, false)
+ }
+}
+
+// useLHS is like use, but doesn't "use" top-level identifiers.
+// It should be called instead of use if the arguments are
+// expressions on the lhs of an assignment.
+func (check *Checker) useLHS(args ...ast.Expr) {
+ for _, e := range args {
+ check.use1(e, true)
+ }
+}
+
+func (check *Checker) use1(e ast.Expr, lhs bool) {
var x operand
- for _, e := range arg {
- switch n := e.(type) {
- case nil:
- // some AST fields may be nil (e.g., the ast.SliceExpr.High field)
- // TODO(gri) can those fields really make it here?
- continue
- case *ast.Ident:
- // don't report an error evaluating blank
- if n.Name == "_" {
- continue
+ switch n := unparen(e).(type) {
+ case nil:
+ // nothing to do
+ case *ast.Ident:
+ // don't report an error evaluating blank
+ if n.Name == "_" {
+ break
+ }
+ // If the lhs is an identifier denoting a variable v, this assignment
+ // is not a 'use' of v. Remember current value of v.used and restore
+ // after evaluating the lhs via check.rawExpr.
+ var v *Var
+ var v_used bool
+ if lhs {
+ if _, obj := check.scope.LookupParent(n.Name, nopos); obj != nil {
+ // It's ok to mark non-local variables, but ignore variables
+ // from other packages to avoid potential race conditions with
+ // dot-imported variables.
+ if w, _ := obj.(*Var); w != nil && w.pkg == check.pkg {
+ v = w
+ v_used = v.used
+ }
}
}
- check.rawExpr(&x, e, nil, false)
+ check.rawExpr(&x, n, nil, true)
+ if v != nil {
+ v.used = v_used // restore v.used
+ }
+ default:
+ check.rawExpr(&x, e, nil, true)
}
}
package main
func main() {
- var s string = nil // ERROR "illegal|invalid|incompatible|cannot"
+ var _ string = nil // ERROR "illegal|invalid|incompatible|cannot"
}
func main() {
const a uint64 = 10
- var b int64 = a // ERROR "convert|cannot|incompatible"
+ var _ int64 = a // ERROR "convert|cannot|incompatible"
}
func f1() {
a, b := f() // ERROR "assignment mismatch|does not match|cannot initialize"
+ _, _ = a, b
}
func f2() {
var a, b int
a, b = f() // ERROR "assignment mismatch|does not match|cannot assign"
+ _, _ = a, b
}
func f() int {
var t *T4
t = i // ERROR "cannot use i \(variable of type I\) as \*T4 value in assignment: need type assertion"
- _ = i
+ _ = t
}
func main() {
var x int
+ _ = x
x = make(map[int]int) // ERROR "cannot use make\(map\[int\]int\)|incompatible"
x = make(map[int]int, 0) // ERROR "cannot use make\(map\[int\]int, 0\)|incompatible"
x = make(map[int]int, zero) // ERROR "cannot use make\(map\[int\]int, zero\)|incompatible"
func main() {
print("call addinst\n")
- var x Inst = AddInst(new(Start)) // ERROR "pointer to interface|incompatible type"
+ var _ Inst = AddInst(new(Start)) // ERROR "pointer to interface|incompatible type"
print("return from addinst\n")
- var y *Inst = new(Start) // ERROR "pointer to interface|incompatible type"
+ var _ *Inst = new(Start) // ERROR "pointer to interface|incompatible type"
}