github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/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 "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 }