| 1 | // Copyright 2013 The Go Authors. All rights reserved. |
|---|---|
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
| 5 | // Package shadow defines an Analyzer that checks for shadowed variables. |
| 6 | package shadow |
| 7 | |
| 8 | import ( |
| 9 | "go/ast" |
| 10 | "go/token" |
| 11 | "go/types" |
| 12 | |
| 13 | "golang.org/x/tools/go/analysis" |
| 14 | "golang.org/x/tools/go/analysis/passes/inspect" |
| 15 | "golang.org/x/tools/go/ast/inspector" |
| 16 | ) |
| 17 | |
| 18 | // NOTE: Experimental. Not part of the vet suite. |
| 19 | |
| 20 | const Doc = `check for possible unintended shadowing of variables |
| 21 | |
| 22 | This analyzer check for shadowed variables. |
| 23 | A shadowed variable is a variable declared in an inner scope |
| 24 | with the same name and type as a variable in an outer scope, |
| 25 | and where the outer variable is mentioned after the inner one |
| 26 | is declared. |
| 27 | |
| 28 | (This definition can be refined; the module generates too many |
| 29 | false positives and is not yet enabled by default.) |
| 30 | |
| 31 | For example: |
| 32 | |
| 33 | func BadRead(f *os.File, buf []byte) error { |
| 34 | var err error |
| 35 | for { |
| 36 | n, err := f.Read(buf) // shadows the function variable 'err' |
| 37 | if err != nil { |
| 38 | break // causes return of wrong value |
| 39 | } |
| 40 | foo(buf) |
| 41 | } |
| 42 | return err |
| 43 | } |
| 44 | ` |
| 45 | |
| 46 | var Analyzer = &analysis.Analyzer{ |
| 47 | Name: "shadow", |
| 48 | Doc: Doc, |
| 49 | Requires: []*analysis.Analyzer{inspect.Analyzer}, |
| 50 | Run: run, |
| 51 | } |
| 52 | |
| 53 | // flags |
| 54 | var strict = false |
| 55 | |
| 56 | func init() { |
| 57 | Analyzer.Flags.BoolVar(&strict, "strict", strict, "whether to be strict about shadowing; can be noisy") |
| 58 | } |
| 59 | |
| 60 | func run(pass *analysis.Pass) (interface{}, error) { |
| 61 | inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) |
| 62 | |
| 63 | spans := make(map[types.Object]span) |
| 64 | for id, obj := range pass.TypesInfo.Defs { |
| 65 | // Ignore identifiers that don't denote objects |
| 66 | // (package names, symbolic variables such as t |
| 67 | // in t := x.(type) of type switch headers). |
| 68 | if obj != nil { |
| 69 | growSpan(spans, obj, id.Pos(), id.End()) |
| 70 | } |
| 71 | } |
| 72 | for id, obj := range pass.TypesInfo.Uses { |
| 73 | growSpan(spans, obj, id.Pos(), id.End()) |
| 74 | } |
| 75 | for node, obj := range pass.TypesInfo.Implicits { |
| 76 | // A type switch with a short variable declaration |
| 77 | // such as t := x.(type) doesn't declare the symbolic |
| 78 | // variable (t in the example) at the switch header; |
| 79 | // instead a new variable t (with specific type) is |
| 80 | // declared implicitly for each case. Such variables |
| 81 | // are found in the types.Info.Implicits (not Defs) |
| 82 | // map. Add them here, assuming they are declared at |
| 83 | // the type cases' colon ":". |
| 84 | if cc, ok := node.(*ast.CaseClause); ok { |
| 85 | growSpan(spans, obj, cc.Colon, cc.Colon) |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | nodeFilter := []ast.Node{ |
| 90 | (*ast.AssignStmt)(nil), |
| 91 | (*ast.GenDecl)(nil), |
| 92 | } |
| 93 | inspect.Preorder(nodeFilter, func(n ast.Node) { |
| 94 | switch n := n.(type) { |
| 95 | case *ast.AssignStmt: |
| 96 | checkShadowAssignment(pass, spans, n) |
| 97 | case *ast.GenDecl: |
| 98 | checkShadowDecl(pass, spans, n) |
| 99 | } |
| 100 | }) |
| 101 | return nil, nil |
| 102 | } |
| 103 | |
| 104 | // A span stores the minimum range of byte positions in the file in which a |
| 105 | // given variable (types.Object) is mentioned. It is lexically defined: it spans |
| 106 | // from the beginning of its first mention to the end of its last mention. |
| 107 | // A variable is considered shadowed (if strict is off) only if the |
| 108 | // shadowing variable is declared within the span of the shadowed variable. |
| 109 | // In other words, if a variable is shadowed but not used after the shadowed |
| 110 | // variable is declared, it is inconsequential and not worth complaining about. |
| 111 | // This simple check dramatically reduces the nuisance rate for the shadowing |
| 112 | // check, at least until something cleverer comes along. |
| 113 | // |
| 114 | // One wrinkle: A "naked return" is a silent use of a variable that the Span |
| 115 | // will not capture, but the compilers catch naked returns of shadowed |
| 116 | // variables so we don't need to. |
| 117 | // |
| 118 | // Cases this gets wrong (TODO): |
| 119 | // - If a for loop's continuation statement mentions a variable redeclared in |
| 120 | // the block, we should complain about it but don't. |
| 121 | // - A variable declared inside a function literal can falsely be identified |
| 122 | // as shadowing a variable in the outer function. |
| 123 | type span struct { |
| 124 | min token.Pos |
| 125 | max token.Pos |
| 126 | } |
| 127 | |
| 128 | // contains reports whether the position is inside the span. |
| 129 | func (s span) contains(pos token.Pos) bool { |
| 130 | return s.min <= pos && pos < s.max |
| 131 | } |
| 132 | |
| 133 | // growSpan expands the span for the object to contain the source range [pos, end). |
| 134 | func growSpan(spans map[types.Object]span, obj types.Object, pos, end token.Pos) { |
| 135 | if strict { |
| 136 | return // No need |
| 137 | } |
| 138 | s, ok := spans[obj] |
| 139 | if ok { |
| 140 | if s.min > pos { |
| 141 | s.min = pos |
| 142 | } |
| 143 | if s.max < end { |
| 144 | s.max = end |
| 145 | } |
| 146 | } else { |
| 147 | s = span{pos, end} |
| 148 | } |
| 149 | spans[obj] = s |
| 150 | } |
| 151 | |
| 152 | // checkShadowAssignment checks for shadowing in a short variable declaration. |
| 153 | func checkShadowAssignment(pass *analysis.Pass, spans map[types.Object]span, a *ast.AssignStmt) { |
| 154 | if a.Tok != token.DEFINE { |
| 155 | return |
| 156 | } |
| 157 | if idiomaticShortRedecl(pass, a) { |
| 158 | return |
| 159 | } |
| 160 | for _, expr := range a.Lhs { |
| 161 | ident, ok := expr.(*ast.Ident) |
| 162 | if !ok { |
| 163 | pass.ReportRangef(expr, "invalid AST: short variable declaration of non-identifier") |
| 164 | return |
| 165 | } |
| 166 | checkShadowing(pass, spans, ident) |
| 167 | } |
| 168 | } |
| 169 | |
| 170 | // idiomaticShortRedecl reports whether this short declaration can be ignored for |
| 171 | // the purposes of shadowing, that is, that any redeclarations it contains are deliberate. |
| 172 | func idiomaticShortRedecl(pass *analysis.Pass, a *ast.AssignStmt) bool { |
| 173 | // Don't complain about deliberate redeclarations of the form |
| 174 | // i := i |
| 175 | // Such constructs are idiomatic in range loops to create a new variable |
| 176 | // for each iteration. Another example is |
| 177 | // switch n := n.(type) |
| 178 | if len(a.Rhs) != len(a.Lhs) { |
| 179 | return false |
| 180 | } |
| 181 | // We know it's an assignment, so the LHS must be all identifiers. (We check anyway.) |
| 182 | for i, expr := range a.Lhs { |
| 183 | lhs, ok := expr.(*ast.Ident) |
| 184 | if !ok { |
| 185 | pass.ReportRangef(expr, "invalid AST: short variable declaration of non-identifier") |
| 186 | return true // Don't do any more processing. |
| 187 | } |
| 188 | switch rhs := a.Rhs[i].(type) { |
| 189 | case *ast.Ident: |
| 190 | if lhs.Name != rhs.Name { |
| 191 | return false |
| 192 | } |
| 193 | case *ast.TypeAssertExpr: |
| 194 | if id, ok := rhs.X.(*ast.Ident); ok { |
| 195 | if lhs.Name != id.Name { |
| 196 | return false |
| 197 | } |
| 198 | } |
| 199 | default: |
| 200 | return false |
| 201 | } |
| 202 | } |
| 203 | return true |
| 204 | } |
| 205 | |
| 206 | // idiomaticRedecl reports whether this declaration spec can be ignored for |
| 207 | // the purposes of shadowing, that is, that any redeclarations it contains are deliberate. |
| 208 | func idiomaticRedecl(d *ast.ValueSpec) bool { |
| 209 | // Don't complain about deliberate redeclarations of the form |
| 210 | // var i, j = i, j |
| 211 | // Don't ignore redeclarations of the form |
| 212 | // var i = 3 |
| 213 | if len(d.Names) != len(d.Values) { |
| 214 | return false |
| 215 | } |
| 216 | for i, lhs := range d.Names { |
| 217 | rhs, ok := d.Values[i].(*ast.Ident) |
| 218 | if !ok || lhs.Name != rhs.Name { |
| 219 | return false |
| 220 | } |
| 221 | } |
| 222 | return true |
| 223 | } |
| 224 | |
| 225 | // checkShadowDecl checks for shadowing in a general variable declaration. |
| 226 | func checkShadowDecl(pass *analysis.Pass, spans map[types.Object]span, d *ast.GenDecl) { |
| 227 | if d.Tok != token.VAR { |
| 228 | return |
| 229 | } |
| 230 | for _, spec := range d.Specs { |
| 231 | valueSpec, ok := spec.(*ast.ValueSpec) |
| 232 | if !ok { |
| 233 | pass.ReportRangef(spec, "invalid AST: var GenDecl not ValueSpec") |
| 234 | return |
| 235 | } |
| 236 | // Don't complain about deliberate redeclarations of the form |
| 237 | // var i = i |
| 238 | if idiomaticRedecl(valueSpec) { |
| 239 | return |
| 240 | } |
| 241 | for _, ident := range valueSpec.Names { |
| 242 | checkShadowing(pass, spans, ident) |
| 243 | } |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | // checkShadowing checks whether the identifier shadows an identifier in an outer scope. |
| 248 | func checkShadowing(pass *analysis.Pass, spans map[types.Object]span, ident *ast.Ident) { |
| 249 | if ident.Name == "_" { |
| 250 | // Can't shadow the blank identifier. |
| 251 | return |
| 252 | } |
| 253 | obj := pass.TypesInfo.Defs[ident] |
| 254 | if obj == nil { |
| 255 | return |
| 256 | } |
| 257 | // obj.Parent.Parent is the surrounding scope. If we can find another declaration |
| 258 | // starting from there, we have a shadowed identifier. |
| 259 | _, shadowed := obj.Parent().Parent().LookupParent(obj.Name(), obj.Pos()) |
| 260 | if shadowed == nil { |
| 261 | return |
| 262 | } |
| 263 | // Don't complain if it's shadowing a universe-declared identifier; that's fine. |
| 264 | if shadowed.Parent() == types.Universe { |
| 265 | return |
| 266 | } |
| 267 | if strict { |
| 268 | // The shadowed identifier must appear before this one to be an instance of shadowing. |
| 269 | if shadowed.Pos() > ident.Pos() { |
| 270 | return |
| 271 | } |
| 272 | } else { |
| 273 | // Don't complain if the span of validity of the shadowed identifier doesn't include |
| 274 | // the shadowing identifier. |
| 275 | span, ok := spans[shadowed] |
| 276 | if !ok { |
| 277 | pass.ReportRangef(ident, "internal error: no range for %q", ident.Name) |
| 278 | return |
| 279 | } |
| 280 | if !span.contains(ident.Pos()) { |
| 281 | return |
| 282 | } |
| 283 | } |
| 284 | // Don't complain if the types differ: that implies the programmer really wants two different things. |
| 285 | if types.Identical(obj.Type(), shadowed.Type()) { |
| 286 | line := pass.Fset.Position(shadowed.Pos()).Line |
| 287 | pass.ReportRangef(ident, "declaration of %q shadows declaration at line %d", obj.Name(), line) |
| 288 | } |
| 289 | } |
| 290 |
Members