github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/go/analysis/passes/shadow/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 // 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 "github.com/powerman/golang-tools/go/analysis" 14 "github.com/powerman/golang-tools/go/analysis/passes/inspect" 15 "github.com/powerman/golang-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 // 124 type span struct { 125 min token.Pos 126 max token.Pos 127 } 128 129 // contains reports whether the position is inside the span. 130 func (s span) contains(pos token.Pos) bool { 131 return s.min <= pos && pos < s.max 132 } 133 134 // growSpan expands the span for the object to contain the source range [pos, end). 135 func growSpan(spans map[types.Object]span, obj types.Object, pos, end token.Pos) { 136 if strict { 137 return // No need 138 } 139 s, ok := spans[obj] 140 if ok { 141 if s.min > pos { 142 s.min = pos 143 } 144 if s.max < end { 145 s.max = end 146 } 147 } else { 148 s = span{pos, end} 149 } 150 spans[obj] = s 151 } 152 153 // checkShadowAssignment checks for shadowing in a short variable declaration. 154 func checkShadowAssignment(pass *analysis.Pass, spans map[types.Object]span, a *ast.AssignStmt) { 155 if a.Tok != token.DEFINE { 156 return 157 } 158 if idiomaticShortRedecl(pass, a) { 159 return 160 } 161 for _, expr := range a.Lhs { 162 ident, ok := expr.(*ast.Ident) 163 if !ok { 164 pass.ReportRangef(expr, "invalid AST: short variable declaration of non-identifier") 165 return 166 } 167 checkShadowing(pass, spans, ident) 168 } 169 } 170 171 // idiomaticShortRedecl reports whether this short declaration can be ignored for 172 // the purposes of shadowing, that is, that any redeclarations it contains are deliberate. 173 func idiomaticShortRedecl(pass *analysis.Pass, a *ast.AssignStmt) bool { 174 // Don't complain about deliberate redeclarations of the form 175 // i := i 176 // Such constructs are idiomatic in range loops to create a new variable 177 // for each iteration. Another example is 178 // switch n := n.(type) 179 if len(a.Rhs) != len(a.Lhs) { 180 return false 181 } 182 // We know it's an assignment, so the LHS must be all identifiers. (We check anyway.) 183 for i, expr := range a.Lhs { 184 lhs, ok := expr.(*ast.Ident) 185 if !ok { 186 pass.ReportRangef(expr, "invalid AST: short variable declaration of non-identifier") 187 return true // Don't do any more processing. 188 } 189 switch rhs := a.Rhs[i].(type) { 190 case *ast.Ident: 191 if lhs.Name != rhs.Name { 192 return false 193 } 194 case *ast.TypeAssertExpr: 195 if id, ok := rhs.X.(*ast.Ident); ok { 196 if lhs.Name != id.Name { 197 return false 198 } 199 } 200 default: 201 return false 202 } 203 } 204 return true 205 } 206 207 // idiomaticRedecl reports whether this declaration spec can be ignored for 208 // the purposes of shadowing, that is, that any redeclarations it contains are deliberate. 209 func idiomaticRedecl(d *ast.ValueSpec) bool { 210 // Don't complain about deliberate redeclarations of the form 211 // var i, j = i, j 212 // Don't ignore redeclarations of the form 213 // var i = 3 214 if len(d.Names) != len(d.Values) { 215 return false 216 } 217 for i, lhs := range d.Names { 218 rhs, ok := d.Values[i].(*ast.Ident) 219 if !ok || lhs.Name != rhs.Name { 220 return false 221 } 222 } 223 return true 224 } 225 226 // checkShadowDecl checks for shadowing in a general variable declaration. 227 func checkShadowDecl(pass *analysis.Pass, spans map[types.Object]span, d *ast.GenDecl) { 228 if d.Tok != token.VAR { 229 return 230 } 231 for _, spec := range d.Specs { 232 valueSpec, ok := spec.(*ast.ValueSpec) 233 if !ok { 234 pass.ReportRangef(spec, "invalid AST: var GenDecl not ValueSpec") 235 return 236 } 237 // Don't complain about deliberate redeclarations of the form 238 // var i = i 239 if idiomaticRedecl(valueSpec) { 240 return 241 } 242 for _, ident := range valueSpec.Names { 243 checkShadowing(pass, spans, ident) 244 } 245 } 246 } 247 248 // checkShadowing checks whether the identifier shadows an identifier in an outer scope. 249 func checkShadowing(pass *analysis.Pass, spans map[types.Object]span, ident *ast.Ident) { 250 if ident.Name == "_" { 251 // Can't shadow the blank identifier. 252 return 253 } 254 obj := pass.TypesInfo.Defs[ident] 255 if obj == nil { 256 return 257 } 258 // obj.Parent.Parent is the surrounding scope. If we can find another declaration 259 // starting from there, we have a shadowed identifier. 260 _, shadowed := obj.Parent().Parent().LookupParent(obj.Name(), obj.Pos()) 261 if shadowed == nil { 262 return 263 } 264 // Don't complain if it's shadowing a universe-declared identifier; that's fine. 265 if shadowed.Parent() == types.Universe { 266 return 267 } 268 if strict { 269 // The shadowed identifier must appear before this one to be an instance of shadowing. 270 if shadowed.Pos() > ident.Pos() { 271 return 272 } 273 } else { 274 // Don't complain if the span of validity of the shadowed identifier doesn't include 275 // the shadowing identifier. 276 span, ok := spans[shadowed] 277 if !ok { 278 pass.ReportRangef(ident, "internal error: no range for %q", ident.Name) 279 return 280 } 281 if !span.contains(ident.Pos()) { 282 return 283 } 284 } 285 // Don't complain if the types differ: that implies the programmer really wants two different things. 286 if types.Identical(obj.Type(), shadowed.Type()) { 287 line := pass.Fset.Position(shadowed.Pos()).Line 288 pass.ReportRangef(ident, "declaration of %q shadows declaration at line %d", obj.Name(), line) 289 } 290 }