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