github.com/alash3al/go@v0.0.0-20150827002835-d497eeb00540/src/cmd/vet/shadow.go (about) 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 /* 6 This file contains the code to check for shadowed variables. 7 A shadowed variable is a variable declared in an inner scope 8 with the same name and type as a variable in an outer scope, 9 and where the outer variable is mentioned after the inner one 10 is declared. 11 12 (This definition can be refined; the module generates too many 13 false positives and is not yet enabled by default.) 14 15 For example: 16 17 func BadRead(f *os.File, buf []byte) error { 18 var err error 19 for { 20 n, err := f.Read(buf) // shadows the function variable 'err' 21 if err != nil { 22 break // causes return of wrong value 23 } 24 foo(buf) 25 } 26 return err 27 } 28 29 */ 30 31 package main 32 33 import ( 34 "flag" 35 "go/ast" 36 "go/token" 37 "go/types" 38 ) 39 40 var strictShadowing = flag.Bool("shadowstrict", false, "whether to be strict about shadowing; can be noisy") 41 42 func init() { 43 register("shadow", 44 "check for shadowed variables (experimental; must be set explicitly)", 45 checkShadow, 46 assignStmt, genDecl) 47 experimental["shadow"] = true 48 } 49 50 func checkShadow(f *File, node ast.Node) { 51 switch n := node.(type) { 52 case *ast.AssignStmt: 53 checkShadowAssignment(f, n) 54 case *ast.GenDecl: 55 checkShadowDecl(f, n) 56 } 57 } 58 59 // Span stores the minimum range of byte positions in the file in which a 60 // given variable (types.Object) is mentioned. It is lexically defined: it spans 61 // from the beginning of its first mention to the end of its last mention. 62 // A variable is considered shadowed (if *strictShadowing is off) only if the 63 // shadowing variable is declared within the span of the shadowed variable. 64 // In other words, if a variable is shadowed but not used after the shadowed 65 // variable is declared, it is inconsequential and not worth complaining about. 66 // This simple check dramatically reduces the nuisance rate for the shadowing 67 // check, at least until something cleverer comes along. 68 // 69 // One wrinkle: A "naked return" is a silent use of a variable that the Span 70 // will not capture, but the compilers catch naked returns of shadowed 71 // variables so we don't need to. 72 // 73 // Cases this gets wrong (TODO): 74 // - If a for loop's continuation statement mentions a variable redeclared in 75 // the block, we should complain about it but don't. 76 // - A variable declared inside a function literal can falsely be identified 77 // as shadowing a variable in the outer function. 78 // 79 type Span struct { 80 min token.Pos 81 max token.Pos 82 } 83 84 // contains reports whether the position is inside the span. 85 func (s Span) contains(pos token.Pos) bool { 86 return s.min <= pos && pos < s.max 87 } 88 89 // growSpan expands the span for the object to contain the instance represented 90 // by the identifier. 91 func (pkg *Package) growSpan(ident *ast.Ident, obj types.Object) { 92 if *strictShadowing { 93 return // No need 94 } 95 pos := ident.Pos() 96 end := ident.End() 97 span, ok := pkg.spans[obj] 98 if ok { 99 if span.min > pos { 100 span.min = pos 101 } 102 if span.max < end { 103 span.max = end 104 } 105 } else { 106 span = Span{pos, end} 107 } 108 pkg.spans[obj] = span 109 } 110 111 // checkShadowAssignment checks for shadowing in a short variable declaration. 112 func checkShadowAssignment(f *File, a *ast.AssignStmt) { 113 if a.Tok != token.DEFINE { 114 return 115 } 116 if f.idiomaticShortRedecl(a) { 117 return 118 } 119 for _, expr := range a.Lhs { 120 ident, ok := expr.(*ast.Ident) 121 if !ok { 122 f.Badf(expr.Pos(), "invalid AST: short variable declaration of non-identifier") 123 return 124 } 125 checkShadowing(f, ident) 126 } 127 } 128 129 // idiomaticShortRedecl reports whether this short declaration can be ignored for 130 // the purposes of shadowing, that is, that any redeclarations it contains are deliberate. 131 func (f *File) idiomaticShortRedecl(a *ast.AssignStmt) bool { 132 // Don't complain about deliberate redeclarations of the form 133 // i := i 134 // Such constructs are idiomatic in range loops to create a new variable 135 // for each iteration. Another example is 136 // switch n := n.(type) 137 if len(a.Rhs) != len(a.Lhs) { 138 return false 139 } 140 // We know it's an assignment, so the LHS must be all identifiers. (We check anyway.) 141 for i, expr := range a.Lhs { 142 lhs, ok := expr.(*ast.Ident) 143 if !ok { 144 f.Badf(expr.Pos(), "invalid AST: short variable declaration of non-identifier") 145 return true // Don't do any more processing. 146 } 147 switch rhs := a.Rhs[i].(type) { 148 case *ast.Ident: 149 if lhs.Name != rhs.Name { 150 return false 151 } 152 case *ast.TypeAssertExpr: 153 if id, ok := rhs.X.(*ast.Ident); ok { 154 if lhs.Name != id.Name { 155 return false 156 } 157 } 158 } 159 } 160 return true 161 } 162 163 // idiomaticRedecl reports whether this declaration spec can be ignored for 164 // the purposes of shadowing, that is, that any redeclarations it contains are deliberate. 165 func (f *File) idiomaticRedecl(d *ast.ValueSpec) bool { 166 // Don't complain about deliberate redeclarations of the form 167 // var i, j = i, j 168 if len(d.Names) != len(d.Values) { 169 return false 170 } 171 for i, lhs := range d.Names { 172 if rhs, ok := d.Values[i].(*ast.Ident); ok { 173 if lhs.Name != rhs.Name { 174 return false 175 } 176 } 177 } 178 return true 179 } 180 181 // checkShadowDecl checks for shadowing in a general variable declaration. 182 func checkShadowDecl(f *File, d *ast.GenDecl) { 183 if d.Tok != token.VAR { 184 return 185 } 186 for _, spec := range d.Specs { 187 valueSpec, ok := spec.(*ast.ValueSpec) 188 if !ok { 189 f.Badf(spec.Pos(), "invalid AST: var GenDecl not ValueSpec") 190 return 191 } 192 // Don't complain about deliberate redeclarations of the form 193 // var i = i 194 if f.idiomaticRedecl(valueSpec) { 195 return 196 } 197 for _, ident := range valueSpec.Names { 198 checkShadowing(f, ident) 199 } 200 } 201 } 202 203 // checkShadowing checks whether the identifier shadows an identifier in an outer scope. 204 func checkShadowing(f *File, ident *ast.Ident) { 205 if ident.Name == "_" { 206 // Can't shadow the blank identifier. 207 return 208 } 209 obj := f.pkg.defs[ident] 210 if obj == nil { 211 return 212 } 213 // obj.Parent.Parent is the surrounding scope. If we can find another declaration 214 // starting from there, we have a shadowed identifier. 215 _, shadowed := obj.Parent().Parent().LookupParent(obj.Name(), obj.Pos()) 216 if shadowed == nil { 217 return 218 } 219 // Don't complain if it's shadowing a universe-declared identifier; that's fine. 220 if shadowed.Parent() == types.Universe { 221 return 222 } 223 if *strictShadowing { 224 // The shadowed identifier must appear before this one to be an instance of shadowing. 225 if shadowed.Pos() > ident.Pos() { 226 return 227 } 228 } else { 229 // Don't complain if the span of validity of the shadowed identifier doesn't include 230 // the shadowing identifier. 231 span, ok := f.pkg.spans[shadowed] 232 if !ok { 233 f.Badf(ident.Pos(), "internal error: no range for %s", ident.Name) 234 return 235 } 236 if !span.contains(ident.Pos()) { 237 return 238 } 239 } 240 // Don't complain if the types differ: that implies the programmer really wants two different things. 241 if types.Identical(obj.Type(), shadowed.Type()) { 242 f.Badf(ident.Pos(), "declaration of %s shadows declaration at %s", obj.Name(), f.loc(shadowed.Pos())) 243 } 244 }