github.com/likebike/go--@v0.0.0-20190911215757-0bd925d16e96/go/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 default: 159 return false 160 } 161 } 162 return true 163 } 164 165 // idiomaticRedecl reports whether this declaration spec can be ignored for 166 // the purposes of shadowing, that is, that any redeclarations it contains are deliberate. 167 func (f *File) idiomaticRedecl(d *ast.ValueSpec) bool { 168 // Don't complain about deliberate redeclarations of the form 169 // var i, j = i, j 170 if len(d.Names) != len(d.Values) { 171 return false 172 } 173 for i, lhs := range d.Names { 174 if rhs, ok := d.Values[i].(*ast.Ident); ok { 175 if lhs.Name != rhs.Name { 176 return false 177 } 178 } 179 } 180 return true 181 } 182 183 // checkShadowDecl checks for shadowing in a general variable declaration. 184 func checkShadowDecl(f *File, d *ast.GenDecl) { 185 if d.Tok != token.VAR { 186 return 187 } 188 for _, spec := range d.Specs { 189 valueSpec, ok := spec.(*ast.ValueSpec) 190 if !ok { 191 f.Badf(spec.Pos(), "invalid AST: var GenDecl not ValueSpec") 192 return 193 } 194 // Don't complain about deliberate redeclarations of the form 195 // var i = i 196 if f.idiomaticRedecl(valueSpec) { 197 return 198 } 199 for _, ident := range valueSpec.Names { 200 checkShadowing(f, ident) 201 } 202 } 203 } 204 205 // checkShadowing checks whether the identifier shadows an identifier in an outer scope. 206 func checkShadowing(f *File, ident *ast.Ident) { 207 if ident.Name == "_" { 208 // Can't shadow the blank identifier. 209 return 210 } 211 obj := f.pkg.defs[ident] 212 if obj == nil { 213 return 214 } 215 // obj.Parent.Parent is the surrounding scope. If we can find another declaration 216 // starting from there, we have a shadowed identifier. 217 _, shadowed := obj.Parent().Parent().LookupParent(obj.Name(), obj.Pos()) 218 if shadowed == nil { 219 return 220 } 221 // Don't complain if it's shadowing a universe-declared identifier; that's fine. 222 if shadowed.Parent() == types.Universe { 223 return 224 } 225 if *strictShadowing { 226 // The shadowed identifier must appear before this one to be an instance of shadowing. 227 if shadowed.Pos() > ident.Pos() { 228 return 229 } 230 } else { 231 // Don't complain if the span of validity of the shadowed identifier doesn't include 232 // the shadowing identifier. 233 span, ok := f.pkg.spans[shadowed] 234 if !ok { 235 f.Badf(ident.Pos(), "internal error: no range for %q", ident.Name) 236 return 237 } 238 if !span.contains(ident.Pos()) { 239 return 240 } 241 } 242 // Don't complain if the types differ: that implies the programmer really wants two different things. 243 if types.Identical(obj.Type(), shadowed.Type()) { 244 f.Badf(ident.Pos(), "declaration of %q shadows declaration at %s", obj.Name(), f.loc(shadowed.Pos())) 245 } 246 }