go.tools/go/types: simplify variable usage error detection
Now that inner functions are processed "in line", usage errors can be detected immediately after each function is processed. R=adonovan CC=golang-codereviews https://golang.org/cl/49900044
This commit is contained in:
parent
4e620158a2
commit
df3357d07f
|
@ -36,7 +36,6 @@ type checker struct {
|
||||||
methods map[string][]*Func // maps type names to associated methods
|
methods map[string][]*Func // maps type names to associated methods
|
||||||
conversions map[*ast.CallExpr]bool // set of type-checked conversions (to distinguish from calls)
|
conversions map[*ast.CallExpr]bool // set of type-checked conversions (to distinguish from calls)
|
||||||
untyped map[ast.Expr]exprInfo // map of expressions without final type
|
untyped map[ast.Expr]exprInfo // map of expressions without final type
|
||||||
lhsVarsList [][]*Var // type switch lhs variable sets, for 'declared but not used' errors
|
|
||||||
delayed []func() // delayed checks that require fully setup types
|
delayed []func() // delayed checks that require fully setup types
|
||||||
|
|
||||||
firstErr error // first error encountered
|
firstErr error // first error encountered
|
||||||
|
|
|
@ -465,32 +465,6 @@ func (check *checker) resolveFiles(files []*ast.File) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Each set of implicitly declared lhs variables in a type switch acts collectively
|
|
||||||
// as a single lhs variable. If any one was 'used', all of them are 'used'. Handle
|
|
||||||
// them before the general analysis.
|
|
||||||
// Note: This check could be done on a per-function basis.
|
|
||||||
for _, vars := range check.lhsVarsList {
|
|
||||||
// len(vars) > 0
|
|
||||||
var used bool
|
|
||||||
for _, v := range vars {
|
|
||||||
if v.used {
|
|
||||||
used = true
|
|
||||||
}
|
|
||||||
v.used = true // avoid later error
|
|
||||||
}
|
|
||||||
if !used {
|
|
||||||
v := vars[0]
|
|
||||||
check.errorf(v.pos, "%s declared but not used", v.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// spec: "Implementation restriction: A compiler may make it illegal to
|
|
||||||
// declare a variable inside a function body if the variable is never used."
|
|
||||||
// Note: Inner functions are handled via the child scopes of the enclosing function.
|
|
||||||
for _, f := range check.funcList {
|
|
||||||
check.usage(f.sig.scope)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// dependencies recursively traverses the initialization dependency graph in a depth-first
|
// dependencies recursively traverses the initialization dependency graph in a depth-first
|
||||||
|
@ -571,17 +545,6 @@ func (check *checker) dependencies(obj Object, init *declInfo, path []Object) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (check *checker) usage(scope *Scope) {
|
|
||||||
for _, obj := range scope.elems {
|
|
||||||
if v, _ := obj.(*Var); v != nil && !v.used {
|
|
||||||
check.errorf(v.pos, "%s declared but not used", v.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, scope := range scope.children {
|
|
||||||
check.usage(scope)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// objDecl type-checks the declaration of obj in its respective file scope.
|
// objDecl type-checks the declaration of obj in its respective file scope.
|
||||||
// See typeDecl for the details on def and cycleOk.
|
// See typeDecl for the details on def and cycleOk.
|
||||||
func (check *checker) objDecl(obj Object, def *Named, cycleOk bool) {
|
func (check *checker) objDecl(obj Object, def *Named, cycleOk bool) {
|
||||||
|
|
|
@ -44,6 +44,23 @@ func (check *checker) funcBody(name string, sig *Signature, body *ast.BlockStmt)
|
||||||
if sig.results.Len() > 0 && !check.isTerminating(body, "") {
|
if sig.results.Len() > 0 && !check.isTerminating(body, "") {
|
||||||
check.errorf(body.Rbrace, "missing return")
|
check.errorf(body.Rbrace, "missing return")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// spec: "Implementation restriction: A compiler may make it illegal to
|
||||||
|
// declare a variable inside a function body if the variable is never used."
|
||||||
|
// (One could check each scope after use, but that distributes this check
|
||||||
|
// over several places because CloseScope is not always called explicitly.)
|
||||||
|
check.usage(sig.scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *checker) usage(scope *Scope) {
|
||||||
|
for _, obj := range scope.elems {
|
||||||
|
if v, _ := obj.(*Var); v != nil && !v.used {
|
||||||
|
check.errorf(v.pos, "%s declared but not used", v.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, scope := range scope.children {
|
||||||
|
check.usage(scope)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// stmtContext is a bitset describing the environment
|
// stmtContext is a bitset describing the environment
|
||||||
|
@ -460,7 +477,7 @@ func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
||||||
|
|
||||||
check.multipleDefaults(s.Body.List)
|
check.multipleDefaults(s.Body.List)
|
||||||
|
|
||||||
var lhsVars []*Var // set of implicitly declared lhs variables
|
var lhsVars []*Var // list of implicitly declared lhs variables
|
||||||
seen := make(map[Type]token.Pos) // map of seen types to positions
|
seen := make(map[Type]token.Pos) // map of seen types to positions
|
||||||
for _, s := range s.Body.List {
|
for _, s := range s.Body.List {
|
||||||
clause, _ := s.(*ast.CaseClause)
|
clause, _ := s.(*ast.CaseClause)
|
||||||
|
@ -471,7 +488,7 @@ func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
||||||
// Check each type in this type switch case.
|
// Check each type in this type switch case.
|
||||||
T := check.caseTypes(&x, xtyp, clause.List, seen)
|
T := check.caseTypes(&x, xtyp, clause.List, seen)
|
||||||
check.openScope(clause)
|
check.openScope(clause)
|
||||||
// If lhs exists, declare a corresponding variable in the case-local scope if necessary.
|
// If lhs exists, declare a corresponding variable in the case-local scope.
|
||||||
if lhs != nil {
|
if lhs != nil {
|
||||||
// spec: "The TypeSwitchGuard may include a short variable declaration.
|
// spec: "The TypeSwitchGuard may include a short variable declaration.
|
||||||
// When that form is used, the variable is declared at the beginning of
|
// When that form is used, the variable is declared at the beginning of
|
||||||
|
@ -482,24 +499,29 @@ func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
||||||
T = x.typ
|
T = x.typ
|
||||||
}
|
}
|
||||||
obj := NewVar(lhs.Pos(), check.pkg, lhs.Name, T)
|
obj := NewVar(lhs.Pos(), check.pkg, lhs.Name, T)
|
||||||
|
check.declare(check.topScope, nil, obj)
|
||||||
|
check.recordImplicit(clause, obj)
|
||||||
// For the "declared but not used" error, all lhs variables act as
|
// For the "declared but not used" error, all lhs variables act as
|
||||||
// one; i.e., if any one of them is 'used', all of them are 'used'.
|
// one; i.e., if any one of them is 'used', all of them are 'used'.
|
||||||
// Collect them for later analysis.
|
// Collect them for later analysis.
|
||||||
lhsVars = append(lhsVars, obj)
|
lhsVars = append(lhsVars, obj)
|
||||||
check.declare(check.topScope, nil, obj)
|
|
||||||
check.recordImplicit(clause, obj)
|
|
||||||
}
|
}
|
||||||
check.stmtList(inner, clause.Body)
|
check.stmtList(inner, clause.Body)
|
||||||
check.closeScope()
|
check.closeScope()
|
||||||
}
|
}
|
||||||
// If a lhs variable was declared but there were no case clauses, make sure
|
|
||||||
// we have at least one (dummy) 'unused' variable to force an error message.
|
// If lhs exists, we must have at least one lhs variable that was used.
|
||||||
if len(lhsVars) == 0 && lhs != nil {
|
if lhs != nil {
|
||||||
lhsVars = []*Var{NewVar(lhs.Pos(), check.pkg, lhs.Name, x.typ)}
|
var used bool
|
||||||
}
|
for _, v := range lhsVars {
|
||||||
// Record lhs variables for this type switch, if any.
|
if v.used {
|
||||||
if len(lhsVars) > 0 {
|
used = true
|
||||||
check.lhsVarsList = append(check.lhsVarsList, lhsVars)
|
}
|
||||||
|
v.used = true // avoid usage error when checking entire function
|
||||||
|
}
|
||||||
|
if !used {
|
||||||
|
check.errorf(lhs.Pos(), "%s declared but not used", lhs.Name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case *ast.SelectStmt:
|
case *ast.SelectStmt:
|
||||||
|
|
Loading…
Reference in New Issue