mirror of
https://github.com/golang/go
synced 2024-09-04 23:44:16 +00:00
cmd/compile/internal/types2: avoid "declared but not used" errors for invalid code
Agressively mark all LHS variables in assignments as used if there is any error in the (entire) assignment. This reduces the number of spurious "declared but not used" errors in programs that are invalid in the first place. This behavior is closer to the behavior of the compiler's original type checker (types1) and lets us remove lines of the form "_ = variable" just to satisfy test cases. It also makes more important errors visible by not crowding them out. Remove the Checker.useLHS function and use Checker.use instead: useLHS didn't evaluate top-level variables, but we actually want them to be evaluated in an error scenario so that they are getting used (and thus we don't get the "declared but not used" error). Fixes #42937. Change-Id: Idda460f6b81c66735bf9fd597c54188949bf12b8 Reviewed-on: https://go-review.googlesource.com/c/go/+/351730 Trust: Robert Griesemer <gri@golang.org> Run-TryBot: Robert Griesemer <gri@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
c0766d2cd0
commit
ddb5a42b25
|
@ -156,6 +156,7 @@ func (check *Checker) initVar(lhs *Var, x *operand, context string) Type {
|
|||
|
||||
check.assignment(x, lhs.typ, context)
|
||||
if x.mode == invalid {
|
||||
lhs.used = true // avoid follow-on "declared but not used" errors
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -164,7 +165,7 @@ func (check *Checker) initVar(lhs *Var, x *operand, context string) Type {
|
|||
|
||||
func (check *Checker) assignVar(lhs syntax.Expr, x *operand) Type {
|
||||
if x.mode == invalid || x.typ == Typ[Invalid] {
|
||||
check.useLHS(lhs)
|
||||
check.use(lhs)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -306,8 +307,18 @@ func (check *Checker) initVars(lhs []*Var, orig_rhs []syntax.Expr, returnPos syn
|
|||
return
|
||||
}
|
||||
|
||||
ok := true
|
||||
for i, lhs := range lhs {
|
||||
check.initVar(lhs, rhs[i], context)
|
||||
if check.initVar(lhs, rhs[i], context) == nil {
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
|
||||
// avoid follow-on "declared but not used" errors if any initialization failed
|
||||
if !ok {
|
||||
for _, lhs := range lhs {
|
||||
lhs.used = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -315,7 +326,7 @@ func (check *Checker) assignVars(lhs, orig_rhs []syntax.Expr) {
|
|||
rhs, commaOk := check.exprList(orig_rhs, len(lhs) == 2)
|
||||
|
||||
if len(lhs) != len(rhs) {
|
||||
check.useLHS(lhs...)
|
||||
check.use(lhs...)
|
||||
// don't report an error if we already reported one
|
||||
for _, x := range rhs {
|
||||
if x.mode == invalid {
|
||||
|
@ -339,8 +350,26 @@ func (check *Checker) assignVars(lhs, orig_rhs []syntax.Expr) {
|
|||
return
|
||||
}
|
||||
|
||||
ok := true
|
||||
for i, lhs := range lhs {
|
||||
check.assignVar(lhs, rhs[i])
|
||||
if check.assignVar(lhs, rhs[i]) == nil {
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
|
||||
// avoid follow-on "declared but 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -371,7 +400,7 @@ func (check *Checker) shortVarDecl(pos syntax.Pos, lhs, rhs []syntax.Expr) {
|
|||
for i, lhs := range lhs {
|
||||
ident, _ := lhs.(*syntax.Name)
|
||||
if ident == nil {
|
||||
check.useLHS(lhs)
|
||||
check.use(lhs)
|
||||
check.errorf(lhs, "non-name %s on left side of :=", lhs)
|
||||
hasErr = true
|
||||
continue
|
||||
|
|
|
@ -627,48 +627,20 @@ Error:
|
|||
func (check *Checker) use(arg ...syntax.Expr) {
|
||||
var x operand
|
||||
for _, e := range arg {
|
||||
// Certain AST fields may legally be nil (e.g., the ast.SliceExpr.High field).
|
||||
if e == nil {
|
||||
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
|
||||
}
|
||||
if l, _ := e.(*syntax.ListExpr); l != nil {
|
||||
check.use(l.ElemList...)
|
||||
continue
|
||||
}
|
||||
check.rawExpr(&x, e, nil, 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.
|
||||
// The arguments must not be nil.
|
||||
func (check *Checker) useLHS(arg ...syntax.Expr) {
|
||||
var x operand
|
||||
for _, e := range arg {
|
||||
// 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 ident, _ := unparen(e).(*syntax.Name); ident != nil {
|
||||
// never type-check the blank name on the lhs
|
||||
if ident.Value == "_" {
|
||||
case *syntax.Name:
|
||||
// don't report an error evaluating blank
|
||||
if n.Value == "_" {
|
||||
continue
|
||||
}
|
||||
if _, obj := check.scope.LookupParent(ident.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)
|
||||
if v != nil {
|
||||
v.used = v_used // restore v.used
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -654,9 +654,6 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) {
|
|||
// declaration, but the post statement must not."
|
||||
if s, _ := s.Post.(*syntax.AssignStmt); s != nil && s.Op == syntax.Def {
|
||||
// The parser already reported an error.
|
||||
// Don't call useLHS here because we want to use the lhs in
|
||||
// this erroneous statement so that we don't get errors about
|
||||
// these lhs variables being declared but not used.
|
||||
check.use(s.Lhs) // avoid follow-up errors
|
||||
}
|
||||
check.stmt(inner, s.Body)
|
||||
|
|
|
@ -7,6 +7,5 @@
|
|||
package main
|
||||
|
||||
func main() {
|
||||
var s string = nil; // ERROR "illegal|invalid|incompatible|cannot"
|
||||
_ = s
|
||||
var s string = nil // ERROR "illegal|invalid|incompatible|cannot"
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
package main
|
||||
|
||||
func main() {
|
||||
const a uint64 = 10;
|
||||
var b int64 = a; // ERROR "convert|cannot|incompatible"
|
||||
_ = b
|
||||
const a uint64 = 10
|
||||
var b int64 = a // ERROR "convert|cannot|incompatible"
|
||||
}
|
||||
|
|
|
@ -9,6 +9,5 @@ package main
|
|||
func f() (int, bool) { return 0, true }
|
||||
|
||||
func main() {
|
||||
x, y := f(), 2; // ERROR "multi|2-valued"
|
||||
_, _ = x, y
|
||||
x, y := f(), 2 // ERROR "multi|2-valued"
|
||||
}
|
||||
|
|
|
@ -9,18 +9,14 @@
|
|||
package main
|
||||
|
||||
func f1() {
|
||||
a, b := f() // ERROR "assignment mismatch|does not match|cannot initialize"
|
||||
_ = a
|
||||
_ = b
|
||||
a, b := f() // ERROR "assignment mismatch|does not match|cannot initialize"
|
||||
}
|
||||
|
||||
func f2() {
|
||||
var a, b int
|
||||
a, b = f() // ERROR "assignment mismatch|does not match|cannot assign"
|
||||
_ = a
|
||||
_ = b
|
||||
a, b = f() // ERROR "assignment mismatch|does not match|cannot assign"
|
||||
}
|
||||
|
||||
func f() int {
|
||||
return 1;
|
||||
return 1
|
||||
}
|
||||
|
|
|
@ -13,12 +13,10 @@ const zero = 0
|
|||
|
||||
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) // 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"
|
||||
x = make(chan int) // ERROR "cannot use make\(chan int\)|incompatible"
|
||||
x = make(chan int, 0) // ERROR "cannot use make\(chan int, 0\)|incompatible"
|
||||
x = make(chan int, zero) // ERROR "cannot use make\(chan int, zero\)|incompatible"
|
||||
_ = x
|
||||
x = make(chan int) // ERROR "cannot use make\(chan int\)|incompatible"
|
||||
x = make(chan int, 0) // ERROR "cannot use make\(chan int, 0\)|incompatible"
|
||||
x = make(chan int, zero) // ERROR "cannot use make\(chan int, zero\)|incompatible"
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ type Start struct {
|
|||
|
||||
func (start *Start) Next() *Inst { return nil }
|
||||
|
||||
|
||||
func AddInst(Inst) *Inst {
|
||||
print("ok in addinst\n")
|
||||
return nil
|
||||
|
@ -33,8 +32,6 @@ func AddInst(Inst) *Inst {
|
|||
func main() {
|
||||
print("call addinst\n")
|
||||
var x Inst = AddInst(new(Start)) // ERROR "pointer to interface|incompatible type"
|
||||
_ = x
|
||||
print("return from addinst\n")
|
||||
var y *Inst = new(Start) // ERROR "pointer to interface|incompatible type"
|
||||
_ = y
|
||||
var y *Inst = new(Start) // ERROR "pointer to interface|incompatible type"
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue