Skip to content

Commit 498fb53

Browse files
committed
go/analysis/passes/shadow: separate package-level variables logic
1 parent 28b865a commit 498fb53

File tree

3 files changed

+43
-24
lines changed

3 files changed

+43
-24
lines changed

go/analysis/passes/shadow/shadow.go

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -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.

go/analysis/passes/shadow/testdata/src/crossfile/crossfile.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ package crossfile
88

99
func ShadowGlobal() {
1010
{
11-
global := 1 // want "declaration of .global. shadows declaration at line 7 in other.go"
11+
global := 1 // want "declaration of .global. shadows declaration at line 8 in other.go"
1212
_ = global
13+
unusedGlobal := 2 // OK - shadowed package variable is never used
14+
_ = unusedGlobal
1315
}
1416
_ = global
1517
}

go/analysis/passes/shadow/testdata/src/crossfile/other.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,7 @@
44

55
package crossfile
66

7-
var global int
7+
var (
8+
global int
9+
unusedGlobal int
10+
)

0 commit comments

Comments
 (0)