| // Copyright 2013 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 shadow defines an Analyzer that checks for shadowed variables. | |
| package shadow | |
| import ( | |
| "go/ast" | |
| "go/token" | |
| "go/types" | |
| "golang.org/x/tools/go/analysis" | |
| "golang.org/x/tools/go/analysis/passes/inspect" | |
| "golang.org/x/tools/go/ast/inspector" | |
| ) | |
| // NOTE: Experimental. Not part of the vet suite. | |
| const Doc = `check for possible unintended shadowing of variables | |
| This analyzer check for shadowed variables. | |
| A shadowed variable is a variable declared in an inner scope | |
| with the same name and type as a variable in an outer scope, | |
| and where the outer variable is mentioned after the inner one | |
| is declared. | |
| (This definition can be refined; the module generates too many | |
| false positives and is not yet enabled by default.) | |
| For example: | |
| func BadRead(f *os.File, buf []byte) error { | |
| var err error | |
| for { | |
| n, err := f.Read(buf) // shadows the function variable 'err' | |
| if err != nil { | |
| break // causes return of wrong value | |
| } | |
| foo(buf) | |
| } | |
| return err | |
| } | |
| ` | |
| var Analyzer = &analysis.Analyzer{ | |
| Name: "shadow", | |
| Doc: Doc, | |
| Requires: []*analysis.Analyzer{inspect.Analyzer}, | |
| Run: run, | |
| } | |
| // flags | |
| var strict = false | |
| func init() { | |
| Analyzer.Flags.BoolVar(&strict, "strict", strict, "whether to be strict about shadowing; can be noisy") | |
| } | |
| func run(pass *analysis.Pass) (interface{}, error) { | |
| inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) | |
| spans := make(map[types.Object]span) | |
| for id, obj := range pass.TypesInfo.Defs { | |
| // Ignore identifiers that don't denote objects | |
| // (package names, symbolic variables such as t | |
| // in t := x.(type) of type switch headers). | |
| if obj != nil { | |
| growSpan(spans, obj, id.Pos(), id.End()) | |
| } | |
| } | |
| for id, obj := range pass.TypesInfo.Uses { | |
| growSpan(spans, obj, id.Pos(), id.End()) | |
| } | |
| for node, obj := range pass.TypesInfo.Implicits { | |
| // A type switch with a short variable declaration | |
| // such as t := x.(type) doesn't declare the symbolic | |
| // variable (t in the example) at the switch header; | |
| // instead a new variable t (with specific type) is | |
| // declared implicitly for each case. Such variables | |
| // are found in the types.Info.Implicits (not Defs) | |
| // map. Add them here, assuming they are declared at | |
| // the type cases' colon ":". | |
| if cc, ok := node.(*ast.CaseClause); ok { | |
| growSpan(spans, obj, cc.Colon, cc.Colon) | |
| } | |
| } | |
| nodeFilter := []ast.Node{ | |
| (*ast.AssignStmt)(nil), | |
| (*ast.GenDecl)(nil), | |
| } | |
| inspect.Preorder(nodeFilter, func(n ast.Node) { | |
| switch n := n.(type) { | |
| case *ast.AssignStmt: | |
| checkShadowAssignment(pass, spans, n) | |
| case *ast.GenDecl: | |
| checkShadowDecl(pass, spans, n) | |
| } | |
| }) | |
| return nil, nil | |
| } | |
| // A span stores the minimum range of byte positions in the file in which a | |
| // given variable (types.Object) is mentioned. It is lexically defined: it spans | |
| // from the beginning of its first mention to the end of its last mention. | |
| // A variable is considered shadowed (if strict is off) only if the | |
| // shadowing variable is declared within the span of the shadowed variable. | |
| // In other words, if a variable is shadowed but not used after the shadowed | |
| // variable is declared, it is inconsequential and not worth complaining about. | |
| // This simple check dramatically reduces the nuisance rate for the shadowing | |
| // check, at least until something cleverer comes along. | |
| // | |
| // One wrinkle: A "naked return" is a silent use of a variable that the Span | |
| // will not capture, but the compilers catch naked returns of shadowed | |
| // variables so we don't need to. | |
| // | |
| // Cases this gets wrong (TODO): | |
| // - If a for loop's continuation statement mentions a variable redeclared in | |
| // the block, we should complain about it but don't. | |
| // - A variable declared inside a function literal can falsely be identified | |
| // as shadowing a variable in the outer function. | |
| type span struct { | |
| min token.Pos | |
| max token.Pos | |
| } | |
| // contains reports whether the position is inside the span. | |
| func (s span) contains(pos token.Pos) bool { | |
| return s.min <= pos && pos < s.max | |
| } | |
| // growSpan expands the span for the object to contain the source range [pos, end). | |
| func growSpan(spans map[types.Object]span, obj types.Object, pos, end token.Pos) { | |
| if strict { | |
| return // No need | |
| } | |
| s, ok := spans[obj] | |
| if ok { | |
| if s.min > pos { | |
| s.min = pos | |
| } | |
| if s.max < end { | |
| s.max = end | |
| } | |
| } else { | |
| s = span{pos, end} | |
| } | |
| spans[obj] = s | |
| } | |
| // checkShadowAssignment checks for shadowing in a short variable declaration. | |
| func checkShadowAssignment(pass *analysis.Pass, spans map[types.Object]span, a *ast.AssignStmt) { | |
| if a.Tok != token.DEFINE { | |
| return | |
| } | |
| if idiomaticShortRedecl(pass, a) { | |
| return | |
| } | |
| for _, expr := range a.Lhs { | |
| ident, ok := expr.(*ast.Ident) | |
| if !ok { | |
| pass.ReportRangef(expr, "invalid AST: short variable declaration of non-identifier") | |
| return | |
| } | |
| checkShadowing(pass, spans, ident) | |
| } | |
| } | |
| // idiomaticShortRedecl reports whether this short declaration can be ignored for | |
| // the purposes of shadowing, that is, that any redeclarations it contains are deliberate. | |
| func idiomaticShortRedecl(pass *analysis.Pass, a *ast.AssignStmt) bool { | |
| // Don't complain about deliberate redeclarations of the form | |
| // i := i | |
| // Such constructs are idiomatic in range loops to create a new variable | |
| // for each iteration. Another example is | |
| // switch n := n.(type) | |
| if len(a.Rhs) != len(a.Lhs) { | |
| return false | |
| } | |
| // We know it's an assignment, so the LHS must be all identifiers. (We check anyway.) | |
| for i, expr := range a.Lhs { | |
| lhs, ok := expr.(*ast.Ident) | |
| if !ok { | |
| pass.ReportRangef(expr, "invalid AST: short variable declaration of non-identifier") | |
| return true // Don't do any more processing. | |
| } | |
| switch rhs := a.Rhs[i].(type) { | |
| case *ast.Ident: | |
| if lhs.Name != rhs.Name { | |
| return false | |
| } | |
| case *ast.TypeAssertExpr: | |
| if id, ok := rhs.X.(*ast.Ident); ok { | |
| if lhs.Name != id.Name { | |
| return false | |
| } | |
| } | |
| default: | |
| return false | |
| } | |
| } | |
| return true | |
| } | |
| // idiomaticRedecl reports whether this declaration spec can be ignored for | |
| // the purposes of shadowing, that is, that any redeclarations it contains are deliberate. | |
| func idiomaticRedecl(d *ast.ValueSpec) bool { | |
| // Don't complain about deliberate redeclarations of the form | |
| // var i, j = i, j | |
| // Don't ignore redeclarations of the form | |
| // var i = 3 | |
| if len(d.Names) != len(d.Values) { | |
| return false | |
| } | |
| for i, lhs := range d.Names { | |
| rhs, ok := d.Values[i].(*ast.Ident) | |
| if !ok || lhs.Name != rhs.Name { | |
| return false | |
| } | |
| } | |
| return true | |
| } | |
| // checkShadowDecl checks for shadowing in a general variable declaration. | |
| func checkShadowDecl(pass *analysis.Pass, spans map[types.Object]span, d *ast.GenDecl) { | |
| if d.Tok != token.VAR { | |
| return | |
| } | |
| for _, spec := range d.Specs { | |
| valueSpec, ok := spec.(*ast.ValueSpec) | |
| if !ok { | |
| pass.ReportRangef(spec, "invalid AST: var GenDecl not ValueSpec") | |
| return | |
| } | |
| // Don't complain about deliberate redeclarations of the form | |
| // var i = i | |
| if idiomaticRedecl(valueSpec) { | |
| return | |
| } | |
| for _, ident := range valueSpec.Names { | |
| checkShadowing(pass, spans, ident) | |
| } | |
| } | |
| } | |
| // checkShadowing checks whether the identifier shadows an identifier in an outer scope. | |
| func checkShadowing(pass *analysis.Pass, spans map[types.Object]span, ident *ast.Ident) { | |
| if ident.Name == "_" { | |
| // Can't shadow the blank identifier. | |
| return | |
| } | |
| obj := pass.TypesInfo.Defs[ident] | |
| if obj == nil { | |
| return | |
| } | |
| // obj.Parent.Parent is the surrounding scope. If we can find another declaration | |
| // starting from there, we have a shadowed identifier. | |
| _, shadowed := obj.Parent().Parent().LookupParent(obj.Name(), obj.Pos()) | |
| if shadowed == nil { | |
| return | |
| } | |
| // Don't complain if it's shadowing a universe-declared identifier; that's fine. | |
| if shadowed.Parent() == types.Universe { | |
| return | |
| } | |
| if strict { | |
| // The shadowed identifier must appear before this one to be an instance of shadowing. | |
| if shadowed.Pos() > ident.Pos() { | |
| return | |
| } | |
| } else { | |
| // Don't complain if the span of validity of the shadowed identifier doesn't include | |
| // the shadowing identifier. | |
| span, ok := spans[shadowed] | |
| if !ok { | |
| pass.ReportRangef(ident, "internal error: no range for %q", ident.Name) | |
| return | |
| } | |
| if !span.contains(ident.Pos()) { | |
| return | |
| } | |
| } | |
| // Don't complain if the types differ: that implies the programmer really wants two different things. | |
| if types.Identical(obj.Type(), shadowed.Type()) { | |
| line := pass.Fset.Position(shadowed.Pos()).Line | |
| pass.ReportRangef(ident, "declaration of %q shadows declaration at line %d", obj.Name(), line) | |
| } | |
| } |