@@ -42,6 +42,8 @@ func run(pass *analysis.Pass) (any, error) {
4242 inspect := pass .ResultOf [inspect .Analyzer ].(* inspector.Inspector )
4343
4444 spans := make (map [types.Object ]span )
45+ usedPkgVars := make (map [types.Object ]struct {})
46+
4547 for id , obj := range pass .TypesInfo .Defs {
4648 // Ignore identifiers that don't denote objects
4749 // (package names, symbolic variables such as t
@@ -52,6 +54,10 @@ func run(pass *analysis.Pass) (any, error) {
5254 }
5355 for id , obj := range pass .TypesInfo .Uses {
5456 growSpan (spans , obj , id .Pos (), id .End ())
57+ // Track usage for package-level variables.
58+ if obj .Parent () == obj .Pkg ().Scope () {
59+ usedPkgVars [obj ] = struct {}{}
60+ }
5561 }
5662 for node , obj := range pass .TypesInfo .Implicits {
5763 // A type switch with a short variable declaration
@@ -74,9 +80,9 @@ func run(pass *analysis.Pass) (any, error) {
7480 inspect .Preorder (nodeFilter , func (n ast.Node ) {
7581 switch n := n .(type ) {
7682 case * ast.AssignStmt :
77- checkShadowAssignment (pass , spans , n )
83+ checkShadowAssignment (pass , spans , usedPkgVars , n )
7884 case * ast.GenDecl :
79- checkShadowDecl (pass , spans , n )
85+ checkShadowDecl (pass , spans , usedPkgVars , n )
8086 }
8187 })
8288 return nil , nil
@@ -131,7 +137,7 @@ func growSpan(spans map[types.Object]span, obj types.Object, pos, end token.Pos)
131137}
132138
133139// checkShadowAssignment checks for shadowing in a short variable declaration.
134- func checkShadowAssignment (pass * analysis.Pass , spans map [types.Object ]span , a * ast.AssignStmt ) {
140+ func checkShadowAssignment (pass * analysis.Pass , spans map [types.Object ]span , usedPkgVars map [types. Object ] struct {}, a * ast.AssignStmt ) {
135141 if a .Tok != token .DEFINE {
136142 return
137143 }
@@ -144,7 +150,7 @@ func checkShadowAssignment(pass *analysis.Pass, spans map[types.Object]span, a *
144150 pass .ReportRangef (expr , "invalid AST: short variable declaration of non-identifier" )
145151 return
146152 }
147- checkShadowing (pass , spans , ident )
153+ checkShadowing (pass , spans , usedPkgVars , ident )
148154 }
149155}
150156
@@ -204,7 +210,7 @@ func idiomaticRedecl(d *ast.ValueSpec) bool {
204210}
205211
206212// checkShadowDecl checks for shadowing in a general variable declaration.
207- func checkShadowDecl (pass * analysis.Pass , spans map [types.Object ]span , d * ast.GenDecl ) {
213+ func checkShadowDecl (pass * analysis.Pass , spans map [types.Object ]span , usedPkgVars map [types. Object ] struct {}, d * ast.GenDecl ) {
208214 if d .Tok != token .VAR {
209215 return
210216 }
@@ -220,13 +226,13 @@ func checkShadowDecl(pass *analysis.Pass, spans map[types.Object]span, d *ast.Ge
220226 return
221227 }
222228 for _ , ident := range valueSpec .Names {
223- checkShadowing (pass , spans , ident )
229+ checkShadowing (pass , spans , usedPkgVars , ident )
224230 }
225231 }
226232}
227233
228234// checkShadowing checks whether the identifier shadows an identifier in an outer scope.
229- func checkShadowing (pass * analysis.Pass , spans map [types.Object ]span , ident * ast.Ident ) {
235+ func checkShadowing (pass * analysis.Pass , spans map [types.Object ]span , usedPkgVars map [types. Object ] struct {}, ident * ast.Ident ) {
230236 if ident .Name == "_" {
231237 // Can't shadow the blank identifier.
232238 return
@@ -249,23 +255,31 @@ func checkShadowing(pass *analysis.Pass, spans map[types.Object]span, ident *ast
249255 shadowedPos := pass .Fset .Position (shadowed .Pos ())
250256 identPos := pass .Fset .Position (ident .Pos ())
251257
252- if strict {
253- // The shadowed identifier must appear before this one to be an instance of shadowing.
254- if shadowed .Pos () > ident .Pos () {
255- return
258+ // Check if the shadowed variable is at package level.
259+ if shadowed .Parent () == shadowed .Pkg ().Scope () {
260+ if ! strict {
261+ // Don't complain if the package variable is unused.
262+ if _ , ok := usedPkgVars [shadowed ]; ! ok {
263+ return
264+ }
256265 }
257266 } else {
258- // Don't complain if the span of validity of the shadowed identifier doesn't include
259- // the shadowing identifier, except for cross-file shadowing where file processing
260- // order affects span checks.
261- span , ok := spans [shadowed ]
262- if ! ok {
263- pass .ReportRangef (ident , "internal error: no range for %q" , ident .Name )
264- return
265- }
266-
267- if shadowedPos .Filename == identPos .Filename && ! span .contains (ident .Pos ()) {
268- return
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+ }
269283 }
270284 }
271285 // Don't complain if the types differ: that implies the programmer really wants two different things.
0 commit comments