Skip to content

Commit 5f02a3e

Browse files
adonovangopherbot
authored andcommitted
gopls/internal/analysis/modernize: don't import slices within slices
This CL strengthens the check that the various modernizer passes use to skip packages in which the fixes cannot be applied. Before, we would not add an import of "slices" from withing the "slices" package itself, but we cannot add this import from any package that "slices" itself transitively depends upon, as this would create an import cycle. So, we consult the std dependency graph baked into the executable. This feature was tested interactively by running modernize on std. Updates golang/go#71847 Change-Id: Iaec6ef07b58ca07df498db63369dae8087331ab9 Reviewed-on: https://go-review.googlesource.com/c/tools/+/653595 Reviewed-by: Robert Findley <[email protected]> Auto-Submit: Alan Donovan <[email protected]> Reviewed-by: Jonathan Amsterdam <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent b2aa62b commit 5f02a3e

File tree

7 files changed

+50
-6
lines changed

7 files changed

+50
-6
lines changed

gopls/internal/analysis/modernize/maps.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ import (
4141
// m = make(M)
4242
// m = M{}
4343
func mapsloop(pass *analysis.Pass) {
44-
if pass.Pkg.Path() == "maps " {
44+
// Skip the analyzer in packages where its
45+
// fixes would create an import cycle.
46+
if within(pass, "maps", "bytes", "runtime") {
4547
return
4648
}
4749

gopls/internal/analysis/modernize/modernize.go

+22
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ import (
1818
"golang.org/x/tools/go/analysis/passes/inspect"
1919
"golang.org/x/tools/go/ast/inspector"
2020
"golang.org/x/tools/gopls/internal/util/astutil"
21+
"golang.org/x/tools/gopls/internal/util/moreiters"
2122
"golang.org/x/tools/internal/analysisinternal"
2223
"golang.org/x/tools/internal/astutil/cursor"
24+
"golang.org/x/tools/internal/stdlib"
2325
"golang.org/x/tools/internal/versions"
2426
)
2527

@@ -125,6 +127,26 @@ func filesUsing(inspect *inspector.Inspector, info *types.Info, version string)
125127
}
126128
}
127129

130+
// within reports whether the current pass is analyzing one of the
131+
// specified standard packages or their dependencies.
132+
func within(pass *analysis.Pass, pkgs ...string) bool {
133+
path := pass.Pkg.Path()
134+
return standard(path) &&
135+
moreiters.Contains(stdlib.Dependencies(pkgs...), path)
136+
}
137+
138+
// standard reports whether the specified package path belongs to a
139+
// package in the standard library (including internal dependencies).
140+
func standard(path string) bool {
141+
// A standard package has no dot in its first segment.
142+
// (It may yet have a dot, e.g. "vendor/golang.org/x/foo".)
143+
slash := strings.IndexByte(path, '/')
144+
if slash < 0 {
145+
slash = len(path)
146+
}
147+
return !strings.Contains(path[:slash], ".") && path != "testdata"
148+
}
149+
128150
var (
129151
builtinAny = types.Universe.Lookup("any")
130152
builtinAppend = types.Universe.Lookup("append")

gopls/internal/analysis/modernize/slices.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ import (
4646
// The fix does not always preserve nilness the of base slice when the
4747
// addends (a, b, c) are all empty.
4848
func appendclipped(pass *analysis.Pass) {
49-
switch pass.Pkg.Path() {
50-
case "slices", "bytes":
49+
// Skip the analyzer in packages where its
50+
// fixes would create an import cycle.
51+
if within(pass, "slices", "bytes", "runtime") {
5152
return
5253
}
5354

gopls/internal/analysis/modernize/slicescontains.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,9 @@ import (
4747
// (Mostly this appears to be a desirable optimization, avoiding
4848
// redundantly repeated evaluation.)
4949
func slicescontains(pass *analysis.Pass) {
50-
// Don't modify the slices package itself.
51-
if pass.Pkg.Path() == "slices" {
50+
// Skip the analyzer in packages where its
51+
// fixes would create an import cycle.
52+
if within(pass, "slices", "runtime") {
5253
return
5354
}
5455

gopls/internal/analysis/modernize/slicesdelete.go

+6
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ import (
2121
// Other variations that will also have suggested replacements include:
2222
// append(s[:i-1], s[i:]...) and append(s[:i+k1], s[i+k2:]) where k2 > k1.
2323
func slicesdelete(pass *analysis.Pass) {
24+
// Skip the analyzer in packages where its
25+
// fixes would create an import cycle.
26+
if within(pass, "slices", "runtime") {
27+
return
28+
}
29+
2430
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
2531
info := pass.TypesInfo
2632
report := func(file *ast.File, call *ast.CallExpr, slice1, slice2 *ast.SliceExpr) {

gopls/internal/analysis/modernize/sortslice.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ import (
3636
// - sort.Sort(x) where x has a named slice type whose Less method is the natural order.
3737
// -> sort.Slice(x)
3838
func sortslice(pass *analysis.Pass) {
39-
if !analysisinternal.Imports(pass.Pkg, "sort") {
39+
// Skip the analyzer in packages where its
40+
// fixes would create an import cycle.
41+
if within(pass, "slices", "sort", "runtime") {
4042
return
4143
}
4244

gopls/internal/util/moreiters/iters.go

+10
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,13 @@ func First[T any](seq iter.Seq[T]) (z T, ok bool) {
1414
}
1515
return z, false
1616
}
17+
18+
// Contains reports whether x is an element of the sequence seq.
19+
func Contains[T comparable](seq iter.Seq[T], x T) bool {
20+
for cand := range seq {
21+
if cand == x {
22+
return true
23+
}
24+
}
25+
return false
26+
}

0 commit comments

Comments
 (0)