Skip to content

Commit fe25404

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

File tree

3 files changed

+62
-43
lines changed

3 files changed

+62
-43
lines changed

go/analysis/passes/shadow/shadow.go

Lines changed: 36 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"golang.org/x/tools/go/analysis/passes/inspect"
1717
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
1818
"golang.org/x/tools/go/ast/inspector"
19+
"golang.org/x/tools/internal/typesinternal"
1920
)
2021

2122
// NOTE: Experimental. Not part of the vet suite.
@@ -113,7 +114,7 @@ func (s span) contains(pos token.Pos) bool {
113114

114115
// growSpan expands the span for the object to contain the source range [pos, end).
115116
func growSpan(spans map[types.Object]span, obj types.Object, pos, end token.Pos) {
116-
if strict {
117+
if strict || typesinternal.IsPackageLevel(obj) || isPkgName(obj) {
117118
return // No need
118119
}
119120
s, ok := spans[obj]
@@ -245,46 +246,43 @@ func checkShadowing(pass *analysis.Pass, spans map[types.Object]span, ident *ast
245246
if shadowed.Parent() == types.Universe {
246247
return
247248
}
248-
249-
shadowedPos := pass.Fset.Position(shadowed.Pos())
250-
identPos := pass.Fset.Position(ident.Pos())
251-
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
256-
}
257-
} 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)
249+
// Package names (imports) don't have a type and are always in scope in the file,
250+
// so they are always reported when shadowed.
251+
if !isPkgName(shadowed) {
252+
// Don't complain if the types differ: that implies the programmer really wants two different things.
253+
if !types.Identical(obj.Type(), shadowed.Type()) {
264254
return
265255
}
266-
267-
if shadowedPos.Filename == identPos.Filename && !span.contains(ident.Pos()) {
268-
return
256+
// Package-level variables are always in scope, so they're always reported when shadowed.
257+
if !strict && !typesinternal.IsPackageLevel(shadowed) {
258+
// Don't complain if the span of validity of the shadowed identifier doesn't include
259+
// the shadowing identifier.
260+
span, ok := spans[shadowed]
261+
if !ok || !span.contains(ident.Pos()) {
262+
return
263+
}
269264
}
270265
}
271-
// Don't complain if the types differ: that implies the programmer really wants two different things.
272-
if types.Identical(obj.Type(), shadowed.Type()) {
273-
// Build the message, adding filename only if in a different file
274-
message := fmt.Sprintf("declaration of %q shadows declaration at line %d", obj.Name(), shadowedPos.Line)
275-
if shadowedPos.Filename != identPos.Filename {
276-
message += fmt.Sprintf(" in %s", filepath.Base(shadowedPos.Filename))
277-
}
278-
279-
pass.Report(analysis.Diagnostic{
280-
Pos: ident.Pos(),
281-
End: ident.End(),
282-
Message: message,
283-
Related: []analysis.RelatedInformation{{
284-
Pos: shadowed.Pos(),
285-
End: shadowed.Pos() + token.Pos(len(shadowed.Name())),
286-
Message: fmt.Sprintf("shadowed symbol %q declared here", obj.Name()),
287-
}},
288-
})
266+
shadowedPos := pass.Fset.Position(shadowed.Pos())
267+
message := fmt.Sprintf("declaration of %q shadows declaration at line %d", obj.Name(), shadowedPos.Line)
268+
currentFile := pass.Fset.Position(ident.Pos()).Filename
269+
if shadowedPos.Filename != currentFile {
270+
message += fmt.Sprintf(" in %s", filepath.Base(shadowedPos.Filename))
289271
}
272+
pass.Report(analysis.Diagnostic{
273+
Pos: ident.Pos(),
274+
End: ident.End(),
275+
Message: message,
276+
Related: []analysis.RelatedInformation{{
277+
Pos: shadowed.Pos(),
278+
End: shadowed.Pos() + token.Pos(len(shadowed.Name())),
279+
Message: fmt.Sprintf("shadowed symbol %q declared here", obj.Name()),
280+
}},
281+
})
282+
}
283+
284+
// isPkgName reports whether obj is a package name (import).
285+
func isPkgName(obj types.Object) bool {
286+
_, ok := obj.(*types.PkgName)
287+
return ok
290288
}

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

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,24 @@
66

77
package crossfile
88

9+
import "fmt"
10+
911
func ShadowGlobal() {
10-
{
11-
global := 1 // want "declaration of .global. shadows declaration at line 7 in other.go"
12-
_ = global
13-
}
12+
global := 1 // want "declaration of .global. shadows declaration at line 8 in other.go"
13+
_ = global
14+
}
15+
16+
func ShadowGlobalWithDifferentType() {
17+
global := "text" // OK: different type.
1418
_ = global
1519
}
20+
21+
func ShadowPackageName() {
22+
fmt := "text" // want "declaration of .fmt. shadows declaration at line 9"
23+
_ = fmt
24+
}
25+
26+
// To import fmt package
27+
func PrintHelper() {
28+
fmt.Println()
29+
}

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

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

55
package crossfile
66

7-
var global int
7+
var (
8+
global int
9+
)
10+
11+
func ShadowUnimportedPackageName() {
12+
fmt := "text" // OK: fmt package is not imported in this file
13+
_ = fmt
14+
}

0 commit comments

Comments
 (0)