Skip to content

Commit 365b9b0

Browse files
committed
gopls/internal/golang: fix extract on anonymous structs
Extracting a code block that contains an anonymous struct currently fails with a "parent nil" error. This CL allows extracting of anonymous structs and fields. It also fixes a bug where a locally defined type is used in but not included in the extracted block by throwing an error when the extraction is attempted. Fixes golang/go#61496 Change-Id: Iabecb2efa136a1806917deff11a4d320118579f5 Reviewed-on: https://go-review.googlesource.com/c/tools/+/689196 Reviewed-by: Alan Donovan <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 4faf573 commit 365b9b0

File tree

2 files changed

+231
-1
lines changed

2 files changed

+231
-1
lines changed

gopls/internal/golang/extract.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -772,7 +772,9 @@ func extractFunctionMethod(cpkg *cache.Package, pgf *parsego.File, start, end to
772772
// cannot be its own reassignment or redefinition (objOverriden).
773773
vscope := v.obj.Parent()
774774
if vscope == nil {
775-
return nil, nil, fmt.Errorf("parent nil")
775+
// v.obj could be a field on an anonymous struct. We'll examine the
776+
// struct in a different iteration so don't return an error here.
777+
continue
776778
}
777779
isUsed, firstUseAfter := objUsed(info, end, vscope.End(), v.obj)
778780
if v.assigned && isUsed && !varOverridden(info, firstUseAfter, v.obj, v.free, outer) {
@@ -1566,6 +1568,12 @@ func collectFreeVars(info *types.Info, file *ast.File, start, end token.Pos, nod
15661568
if !ok {
15671569
return nil, fmt.Errorf("no seen types.Object for %v", obj)
15681570
}
1571+
if named, ok := v.obj.Type().(typesinternal.NamedOrAlias); ok {
1572+
namedPos := named.Obj().Pos()
1573+
if isLocal(named.Obj()) && !(start <= namedPos && namedPos <= end) {
1574+
return nil, fmt.Errorf("Cannot extract selection: the code refers to a local type whose definition lies outside the extracted block")
1575+
}
1576+
}
15691577
variables = append(variables, v)
15701578
}
15711579
return variables, nil
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
This test checks of the behavior of extract function when the extracted block includes anonymous structs.
2+
-- go.mod --
3+
module mod.com
4+
5+
go 1.12
6+
-- a/a.go --
7+
package a
8+
9+
func _() {
10+
var x struct{ y int } //@codeaction("var", "refactor.extract.function", end=endA, result=anonA)
11+
println(x.y) //@loc(endA, ")")
12+
}
13+
14+
-- b/b.go --
15+
package b
16+
17+
func _() {
18+
type T struct {
19+
y int
20+
}
21+
var x T //@codeaction("var", "refactor.extract.function", end=endB, err="the code refers to a local type")
22+
println(x.y) //@loc(endB, ")")
23+
}
24+
25+
-- @anonA/a/a.go --
26+
package a
27+
28+
func _() {
29+
newFunction() //@loc(endA, ")")
30+
}
31+
32+
func newFunction() {
33+
var x struct{ y int } //@codeaction("var", "refactor.extract.function", end=endA, result=anonA)
34+
println(x.y)
35+
}
36+
37+
-- d/d.go --
38+
package d
39+
40+
func _() {
41+
s := []struct{ y int }{
42+
{y: 1},
43+
{y: 2},
44+
}
45+
for _, v := range s { //@codeaction("for", "refactor.extract.function", end=endD, result=anonD)
46+
println(v.y)
47+
} //@loc(endD, "}")
48+
}
49+
50+
-- @anonD/d/d.go --
51+
package d
52+
53+
func _() {
54+
s := []struct{ y int }{
55+
{y: 1},
56+
{y: 2},
57+
}
58+
newFunction(s) //@loc(endD, "}")
59+
}
60+
61+
func newFunction(s []struct{y int}) {
62+
for _, v := range s { //@codeaction("for", "refactor.extract.function", end=endD, result=anonD)
63+
println(v.y)
64+
}
65+
}
66+
67+
-- e/e.go --
68+
package e
69+
70+
func _() {
71+
var x int
72+
s := []struct { //@codeaction("s", "refactor.extract.function", end=endE, result=anonE)
73+
y int
74+
}{
75+
{y: 1},
76+
{y: 2},
77+
}
78+
x = s[0].y //@loc(endE, "x = s[0].y")
79+
println(x)
80+
}
81+
82+
-- @anonE/e/e.go --
83+
package e
84+
85+
func _() {
86+
var x int
87+
x = newFunction(x) //@loc(endE, "x = s[0].y")
88+
println(x)
89+
}
90+
91+
func newFunction(x int) int {
92+
s := []struct { //@codeaction("s", "refactor.extract.function", end=endE, result=anonE)
93+
y int
94+
}{
95+
{y: 1},
96+
{y: 2},
97+
}
98+
x = s[0].y
99+
return x
100+
}
101+
102+
-- f/f.go --
103+
package f
104+
func _() int {
105+
x := struct{ y int } { y: 1 } //@codeaction("x", "refactor.extract.function", end=endF, result=anonF)
106+
return x.y //@loc(endF, "y")
107+
}
108+
109+
-- @anonF/f/f.go --
110+
package f
111+
func _() int {
112+
return newFunction() //@loc(endF, "y")
113+
}
114+
115+
func newFunction() int {
116+
x := struct{ y int }{y: 1} //@codeaction("x", "refactor.extract.function", end=endF, result=anonF)
117+
return x.y
118+
}
119+
120+
-- g/g.go --
121+
package g
122+
123+
import "fmt"
124+
125+
func _() error {
126+
x := struct{ y error }{fmt.Errorf("test error")}
127+
return x.y //@ loc(endG, "y"), codeaction("return", "refactor.extract.function", end=endG, result=anonG)
128+
}
129+
130+
-- @anonG/g/g.go --
131+
package g
132+
133+
import "fmt"
134+
135+
func _() error {
136+
x := struct{ y error }{fmt.Errorf("test error")}
137+
return newFunction(x) //@ loc(endG, "y"), codeaction("return", "refactor.extract.function", end=endG, result=anonG)
138+
}
139+
140+
func newFunction(x struct{y error}) error {
141+
return x.y
142+
}
143+
144+
-- h/h.go --
145+
package h
146+
147+
import "fmt"
148+
149+
func _() string {
150+
type A error
151+
type B struct {
152+
A
153+
}
154+
a := B{A: fmt.Errorf("test error")} //@codeaction("a", "refactor.extract.function", end=endH, err="the code refers to a local type")
155+
return a.Error() //@loc(endH, "Error()")
156+
}
157+
158+
-- i/i.go --
159+
package i
160+
161+
import "fmt"
162+
163+
func _() string {
164+
var a struct{ e error } //@codeaction("var", "refactor.extract.function", end=endI, result=anonI)
165+
a.e = fmt.Errorf("test error")
166+
return a.e.Error() //@loc(endI, "Error()")
167+
}
168+
169+
-- @anonI/i/i.go --
170+
package i
171+
172+
import "fmt"
173+
174+
func _() string {
175+
return newFunction() //@loc(endI, "Error()")
176+
}
177+
178+
func newFunction() string {
179+
var a struct{ e error } //@codeaction("var", "refactor.extract.function", end=endI, result=anonI)
180+
a.e = fmt.Errorf("test error")
181+
return a.e.Error()
182+
}
183+
184+
-- j/j.go --
185+
package j
186+
187+
import "unsafe"
188+
189+
func _() uintptr {
190+
var x struct{ p unsafe.Pointer }
191+
y := uintptr(x.p) //@codeaction("y", "refactor.extract.function", end=endJ, result=anonJ)
192+
return y //@loc(endJ, "y")
193+
}
194+
195+
-- @anonJ/j/j.go --
196+
package j
197+
198+
import "unsafe"
199+
200+
func _() uintptr {
201+
var x struct{ p unsafe.Pointer }
202+
return newFunction(x) //@loc(endJ, "y")
203+
}
204+
205+
func newFunction(x struct{p unsafe.Pointer}) uintptr {
206+
y := uintptr(x.p) //@codeaction("y", "refactor.extract.function", end=endJ, result=anonJ)
207+
return y
208+
}
209+
210+
-- k/k.go --
211+
package k
212+
213+
import "unsafe"
214+
215+
func _(x int) unsafe.Pointer {
216+
type A struct {
217+
p unsafe.Pointer
218+
}
219+
c := A{p: unsafe.Pointer(&x)} //@codeaction("c", "refactor.extract.function", end=endK, err="the code refers to a local type")
220+
return c.p //@loc(endK, "c.p")
221+
}
222+

0 commit comments

Comments
 (0)