Skip to content

Commit aebd3be

Browse files
committed
gopls/internal/test/integration: simplify GoToDefinition
- Definition no longer calls OpenFile on the first location; it merely returns the list. Also, rename to Definitions. - GoToDefinition is renamed FirstDefinition; it asserts that the list is non-empty. - Tests that assumed OpenFile now call it explicitly. These changes should make it easier to write both positive and negative marker tests of the Definitions result set. Change-Id: I1e57bba8c78983f7b266ad876b1862b8af86a797 Reviewed-on: https://go-review.googlesource.com/c/tools/+/675016 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Madeline Kalil <[email protected]>
1 parent 423c5af commit aebd3be

19 files changed

+127
-124
lines changed

gopls/internal/test/integration/bench/definition_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,15 @@ func BenchmarkDefinition(b *testing.B) {
3131

3232
loc := env.RegexpSearch(test.file, test.regexp)
3333
env.Await(env.DoneWithOpen())
34-
env.GoToDefinition(loc) // pre-warm the query, and open the target file
34+
env.FirstDefinition(loc) // pre-warm the query, and open the target file
3535
b.ResetTimer()
3636

3737
if stopAndRecord := startProfileIfSupported(b, env, qualifiedName(test.repo, "definition")); stopAndRecord != nil {
3838
defer stopAndRecord()
3939
}
4040

4141
for b.Loop() {
42-
env.GoToDefinition(loc) // pre-warm the query
42+
env.FirstDefinition(loc) // pre-warm the query
4343
}
4444
})
4545
}

gopls/internal/test/integration/diagnostics/builtin_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const (
2626
`
2727
Run(t, src, func(t *testing.T, env *Env) {
2828
env.OpenFile("a.go")
29-
loc := env.GoToDefinition(env.RegexpSearch("a.go", "iota"))
29+
loc := env.FirstDefinition(env.RegexpSearch("a.go", "iota"))
3030
if !strings.HasSuffix(string(loc.URI), "builtin.go") {
3131
t.Fatalf("jumped to %q, want builtin.go", loc.URI)
3232
}

gopls/internal/test/integration/diagnostics/diagnostics_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1790,7 +1790,7 @@ func helloHelper() {}
17901790
env.AfterChange(
17911791
NoDiagnostics(ForFile("nested/hello/hello.go")),
17921792
)
1793-
loc := env.GoToDefinition(env.RegexpSearch("nested/hello/hello.go", "helloHelper"))
1793+
loc := env.FirstDefinition(env.RegexpSearch("nested/hello/hello.go", "helloHelper"))
17941794
want := "nested/hello/hello_helper.go"
17951795
if got := env.Sandbox.Workdir.URIToPath(loc.URI); got != want {
17961796
t.Errorf("Definition() returned %q, want %q", got, want)

gopls/internal/test/integration/fake/editor.go

Lines changed: 10 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -932,54 +932,30 @@ func (e *Editor) setBufferContentLocked(ctx context.Context, path string, dirty
932932
return nil
933933
}
934934

935-
// GoToDefinition jumps to the definition of the symbol at the given position
936-
// in an open buffer. It returns the location of the resulting jump.
937-
func (e *Editor) Definition(ctx context.Context, loc protocol.Location) (protocol.Location, error) {
935+
// Definitions returns the definitions of the symbol at the given
936+
// location in an open buffer.
937+
func (e *Editor) Definitions(ctx context.Context, loc protocol.Location) ([]protocol.Location, error) {
938938
if err := e.checkBufferLocation(loc); err != nil {
939-
return protocol.Location{}, err
939+
return nil, err
940940
}
941941
params := &protocol.DefinitionParams{}
942942
params.TextDocument.URI = loc.URI
943943
params.Position = loc.Range.Start
944944

945-
resp, err := e.Server.Definition(ctx, params)
946-
if err != nil {
947-
return protocol.Location{}, fmt.Errorf("definition: %w", err)
948-
}
949-
return e.extractFirstLocation(ctx, resp)
945+
return e.Server.Definition(ctx, params)
950946
}
951947

952-
// TypeDefinition jumps to the type definition of the symbol at the given
953-
// location in an open buffer.
954-
func (e *Editor) TypeDefinition(ctx context.Context, loc protocol.Location) (protocol.Location, error) {
948+
// TypeDefinitions returns the type definitions of the symbol at the
949+
// given location in an open buffer.
950+
func (e *Editor) TypeDefinitions(ctx context.Context, loc protocol.Location) ([]protocol.Location, error) {
955951
if err := e.checkBufferLocation(loc); err != nil {
956-
return protocol.Location{}, err
952+
return nil, err
957953
}
958954
params := &protocol.TypeDefinitionParams{}
959955
params.TextDocument.URI = loc.URI
960956
params.Position = loc.Range.Start
961957

962-
resp, err := e.Server.TypeDefinition(ctx, params)
963-
if err != nil {
964-
return protocol.Location{}, fmt.Errorf("type definition: %w", err)
965-
}
966-
return e.extractFirstLocation(ctx, resp)
967-
}
968-
969-
// extractFirstLocation returns the first location.
970-
// It opens the file if needed.
971-
func (e *Editor) extractFirstLocation(ctx context.Context, locs []protocol.Location) (protocol.Location, error) {
972-
if len(locs) == 0 {
973-
return protocol.Location{}, nil
974-
}
975-
976-
newPath := e.sandbox.Workdir.URIToPath(locs[0].URI)
977-
if !e.HasBuffer(newPath) {
978-
if err := e.OpenFile(ctx, newPath); err != nil {
979-
return protocol.Location{}, fmt.Errorf("OpenFile: %w", err)
980-
}
981-
}
982-
return locs[0], nil
958+
return e.Server.TypeDefinition(ctx, params)
983959
}
984960

985961
// Symbol performs a workspace symbol search using query

gopls/internal/test/integration/misc/definition_test.go

Lines changed: 54 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,13 @@ const message = "Hello World."
4141
func TestGoToInternalDefinition(t *testing.T) {
4242
Run(t, internalDefinition, func(t *testing.T, env *Env) {
4343
env.OpenFile("main.go")
44-
loc := env.GoToDefinition(env.RegexpSearch("main.go", "message"))
44+
loc := env.FirstDefinition(env.RegexpSearch("main.go", "message"))
4545
name := env.Sandbox.Workdir.URIToPath(loc.URI)
4646
if want := "const.go"; name != want {
47-
t.Errorf("GoToDefinition: got file %q, want %q", name, want)
47+
t.Errorf("Definition: got file %q, want %q", name, want)
4848
}
4949
if want := env.RegexpSearch("const.go", "message"); loc != want {
50-
t.Errorf("GoToDefinition: got location %v, want %v", loc, want)
50+
t.Errorf("Definition: got location %v, want %v", loc, want)
5151
}
5252
})
5353
}
@@ -90,13 +90,13 @@ func TestGoToLinknameDefinition(t *testing.T) {
9090

9191
// Jump from directives 2nd arg.
9292
start := env.RegexpSearch("upper/upper.go", `lower.bar`)
93-
loc := env.GoToDefinition(start)
93+
loc := env.FirstDefinition(start)
9494
name := env.Sandbox.Workdir.URIToPath(loc.URI)
9595
if want := "lower/lower.go"; name != want {
96-
t.Errorf("GoToDefinition: got file %q, want %q", name, want)
96+
t.Errorf("Definition: got file %q, want %q", name, want)
9797
}
9898
if want := env.RegexpSearch("lower/lower.go", `bar`); loc != want {
99-
t.Errorf("GoToDefinition: got position %v, want %v", loc, want)
99+
t.Errorf("Definition: got position %v, want %v", loc, want)
100100
}
101101
})
102102
}
@@ -139,13 +139,13 @@ func TestGoToLinknameDefinitionInReverseDep(t *testing.T) {
139139

140140
// Jump from directives 2nd arg.
141141
start := env.RegexpSearch("lower/lower.go", `upper.foo`)
142-
loc := env.GoToDefinition(start)
142+
loc := env.FirstDefinition(start)
143143
name := env.Sandbox.Workdir.URIToPath(loc.URI)
144144
if want := "upper/upper.go"; name != want {
145-
t.Errorf("GoToDefinition: got file %q, want %q", name, want)
145+
t.Errorf("Definition: got file %q, want %q", name, want)
146146
}
147147
if want := env.RegexpSearch("upper/upper.go", `foo`); loc != want {
148-
t.Errorf("GoToDefinition: got position %v, want %v", loc, want)
148+
t.Errorf("Definition: got position %v, want %v", loc, want)
149149
}
150150
})
151151
}
@@ -178,13 +178,13 @@ func TestGoToLinknameDefinitionDisconnected(t *testing.T) {
178178

179179
// Jump from directives 2nd arg.
180180
start := env.RegexpSearch("a/a.go", `b.bar`)
181-
loc := env.GoToDefinition(start)
181+
loc := env.FirstDefinition(start)
182182
name := env.Sandbox.Workdir.URIToPath(loc.URI)
183183
if want := "b/b.go"; name != want {
184-
t.Errorf("GoToDefinition: got file %q, want %q", name, want)
184+
t.Errorf("Definition: got file %q, want %q", name, want)
185185
}
186186
if want := env.RegexpSearch("b/b.go", `bar`); loc != want {
187-
t.Errorf("GoToDefinition: got position %v, want %v", loc, want)
187+
t.Errorf("Definition: got position %v, want %v", loc, want)
188188
}
189189
})
190190
}
@@ -206,30 +206,32 @@ func main() {
206206
func TestGoToStdlibDefinition_Issue37045(t *testing.T) {
207207
Run(t, stdlibDefinition, func(t *testing.T, env *Env) {
208208
env.OpenFile("main.go")
209-
loc := env.GoToDefinition(env.RegexpSearch("main.go", `fmt.(Printf)`))
209+
loc := env.FirstDefinition(env.RegexpSearch("main.go", `fmt.(Printf)`))
210210
name := env.Sandbox.Workdir.URIToPath(loc.URI)
211211
if got, want := path.Base(name), "print.go"; got != want {
212-
t.Errorf("GoToDefinition: got file %q, want %q", name, want)
212+
t.Errorf("Definition: got file %q, want %q", name, want)
213213
}
214+
env.OpenFile(name)
214215

215216
// Test that we can jump to definition from outside our workspace.
216217
// See golang.org/issues/37045.
217-
newLoc := env.GoToDefinition(loc)
218+
newLoc := env.FirstDefinition(loc)
218219
newName := env.Sandbox.Workdir.URIToPath(newLoc.URI)
219220
if newName != name {
220-
t.Errorf("GoToDefinition is not idempotent: got %q, want %q", newName, name)
221+
t.Errorf("Definition is not idempotent: got %q, want %q", newName, name)
221222
}
222223
if newLoc != loc {
223-
t.Errorf("GoToDefinition is not idempotent: got %v, want %v", newLoc, loc)
224+
t.Errorf("Definition is not idempotent: got %v, want %v", newLoc, loc)
224225
}
225226
})
226227
}
227228

228229
func TestUnexportedStdlib_Issue40809(t *testing.T) {
229230
Run(t, stdlibDefinition, func(t *testing.T, env *Env) {
230231
env.OpenFile("main.go")
231-
loc := env.GoToDefinition(env.RegexpSearch("main.go", `fmt.(Printf)`))
232+
loc := env.FirstDefinition(env.RegexpSearch("main.go", `fmt.(Printf)`))
232233
name := env.Sandbox.Workdir.URIToPath(loc.URI)
234+
env.OpenFile(name)
233235

234236
loc = env.RegexpSearch(name, `:=\s*(newPrinter)\(\)`)
235237

@@ -239,7 +241,7 @@ func TestUnexportedStdlib_Issue40809(t *testing.T) {
239241
t.Errorf("expected 5+ references to newPrinter, found: %#v", refs)
240242
}
241243

242-
loc = env.GoToDefinition(loc)
244+
loc = env.FirstDefinition(loc)
243245
content, _ := env.Hover(loc)
244246
if !strings.Contains(content.Value, "newPrinter") {
245247
t.Fatal("definition of newPrinter went to the incorrect place")
@@ -306,7 +308,7 @@ func main() {}
306308
Settings{"importShortcut": tt.importShortcut},
307309
).Run(t, mod, func(t *testing.T, env *Env) {
308310
env.OpenFile("main.go")
309-
loc := env.GoToDefinition(env.RegexpSearch("main.go", `"fmt"`))
311+
loc := env.FirstDefinition(env.RegexpSearch("main.go", `"fmt"`))
310312
if loc == (protocol.Location{}) {
311313
t.Fatalf("expected definition, got none")
312314
}
@@ -354,7 +356,7 @@ func main() {}
354356
Run(t, mod, func(t *testing.T, env *Env) {
355357
env.OpenFile("main.go")
356358

357-
loc, err := env.Editor.TypeDefinition(env.Ctx, env.RegexpSearch("main.go", tt.re))
359+
locs, err := env.Editor.TypeDefinitions(env.Ctx, env.RegexpSearch("main.go", tt.re))
358360
if tt.wantError {
359361
if err == nil {
360362
t.Fatal("expected error, got nil")
@@ -364,10 +366,13 @@ func main() {}
364366
if err != nil {
365367
t.Fatalf("expected nil error, got %s", err)
366368
}
369+
if len(locs) == 0 {
370+
t.Fatalf("TypeDefinitions: empty result")
371+
}
367372

368373
typeLoc := env.RegexpSearch("main.go", tt.wantTypeRe)
369-
if loc != typeLoc {
370-
t.Errorf("invalid pos: want %+v, got %+v", typeLoc, loc)
374+
if locs[0] != typeLoc {
375+
t.Errorf("invalid pos: want %+v, got %+v", typeLoc, locs[0])
371376
}
372377
})
373378
})
@@ -389,7 +394,16 @@ func F[T comparable]() {}
389394
Run(t, mod, func(t *testing.T, env *Env) {
390395
env.OpenFile("main.go")
391396

392-
_ = env.TypeDefinition(env.RegexpSearch("main.go", "comparable")) // must not panic
397+
// TypeDefinition of comparable should
398+
// returns an empty result, not panic.
399+
loc := env.RegexpSearch("main.go", "comparable")
400+
locs, err := env.Editor.TypeDefinitions(env.Ctx, loc)
401+
if err != nil {
402+
t.Fatal(err)
403+
}
404+
if len(locs) > 0 {
405+
t.Fatalf("unexpected result: %v", locs)
406+
}
393407
})
394408
}
395409

@@ -429,7 +443,7 @@ package client
429443
`
430444
Run(t, mod, func(t *testing.T, env *Env) {
431445
env.OpenFile("client/client_role_test.go")
432-
env.GoToDefinition(env.RegexpSearch("client/client_role_test.go", "RoleSetup"))
446+
env.FirstDefinition(env.RegexpSearch("client/client_role_test.go", "RoleSetup"))
433447
})
434448
}
435449

@@ -487,11 +501,11 @@ const _ = b.K
487501
refLoc := env.RegexpSearch("a.go", "K") // find "b.K" reference
488502

489503
// Initially, b.K is defined in the module cache.
490-
gotLoc := env.GoToDefinition(refLoc)
504+
gotLoc := env.FirstDefinition(refLoc)
491505
gotFile := env.Sandbox.Workdir.URIToPath(gotLoc.URI)
492506
wantCache := filepath.ToSlash(env.Sandbox.GOPATH()) + "/pkg/mod/other.com/[email protected]/b.go"
493507
if gotFile != wantCache {
494-
t.Errorf("GoToDefinition, before: got file %q, want %q", gotFile, wantCache)
508+
t.Errorf("Definition, before: got file %q, want %q", gotFile, wantCache)
495509
}
496510

497511
// Run 'go mod vendor' outside the editor.
@@ -501,10 +515,10 @@ const _ = b.K
501515
env.Await(env.DoneWithChangeWatchedFiles())
502516

503517
// Now, b.K is defined in the vendor tree.
504-
gotLoc = env.GoToDefinition(refLoc)
518+
gotLoc = env.FirstDefinition(refLoc)
505519
wantVendor := "vendor/other.com/b/b.go"
506520
if gotFile != wantVendor {
507-
t.Errorf("GoToDefinition, after go mod vendor: got file %q, want %q", gotFile, wantVendor)
521+
t.Errorf("Definition, after go mod vendor: got file %q, want %q", gotFile, wantVendor)
508522
}
509523

510524
// Delete the vendor tree.
@@ -520,10 +534,10 @@ const _ = b.K
520534
env.Await(env.DoneWithChangeWatchedFiles())
521535

522536
// b.K is once again defined in the module cache.
523-
gotLoc = env.GoToDefinition(gotLoc)
537+
gotLoc = env.FirstDefinition(gotLoc)
524538
gotFile = env.Sandbox.Workdir.URIToPath(gotLoc.URI)
525539
if gotFile != wantCache {
526-
t.Errorf("GoToDefinition, after rm -rf vendor: got file %q, want %q", gotFile, wantCache)
540+
t.Errorf("Definition, after rm -rf vendor: got file %q, want %q", gotFile, wantCache)
527541
}
528542
})
529543
}
@@ -554,16 +568,16 @@ FOO
554568
SKIP
555569
`
556570

557-
func TestGoToEmbedDefinition(t *testing.T) {
571+
func TestEmbedDefinition(t *testing.T) {
558572
Run(t, embedDefinition, func(t *testing.T, env *Env) {
559573
env.OpenFile("main.go")
560574

561575
start := env.RegexpSearch("main.go", `\*.txt`)
562-
loc := env.GoToDefinition(start)
576+
loc := env.FirstDefinition(start)
563577

564578
name := env.Sandbox.Workdir.URIToPath(loc.URI)
565579
if want := "foo.txt"; name != want {
566-
t.Errorf("GoToDefinition: got file %q, want %q", name, want)
580+
t.Errorf("Definition: got file %q, want %q", name, want)
567581
}
568582
})
569583
}
@@ -588,10 +602,10 @@ func _(err error) {
588602
env.OpenFile("a.go")
589603

590604
start := env.RegexpSearch("a.go", `Error`)
591-
loc := env.GoToDefinition(start)
605+
loc := env.FirstDefinition(start)
592606

593607
if !strings.HasSuffix(string(loc.URI), "builtin.go") {
594-
t.Errorf("GoToDefinition(err.Error) = %#v, want builtin.go", loc)
608+
t.Errorf("Definition(err.Error) = %#v, want builtin.go", loc)
595609
}
596610
})
597611
}
@@ -628,13 +642,13 @@ var _ = foo(123) // call
628642

629643
// Definition at the call"foo(123)" takes us to the Go declaration.
630644
callLoc := env.RegexpSearch("a.go", regexp.QuoteMeta("foo(123)"))
631-
declLoc := env.GoToDefinition(callLoc)
645+
declLoc := env.FirstDefinition(callLoc)
632646
if got, want := locString(declLoc), "a.go:5:5-5:8"; got != want {
633647
t.Errorf("Definition(call): got %s, want %s", got, want)
634648
}
635649

636650
// Definition a second time takes us to the assembly implementation.
637-
implLoc := env.GoToDefinition(declLoc)
651+
implLoc := env.FirstDefinition(declLoc)
638652
if got, want := locString(implLoc), "foo_darwin_arm64.s:2:6-2:9"; got != want {
639653
t.Errorf("Definition(go decl): got %s, want %s", got, want)
640654
}
@@ -670,7 +684,7 @@ func Foo() {
670684
env.OpenFile("a.go")
671685

672686
fooLoc := env.RegexpSearch("a.go", "()Foo")
673-
loc0 := env.GoToDefinition(fooLoc)
687+
loc0 := env.FirstDefinition(fooLoc)
674688

675689
// Insert a space that will be removed by formatting.
676690
env.EditBuffer("a.go", protocol.TextEdit{
@@ -679,7 +693,7 @@ func Foo() {
679693
})
680694
env.SaveBuffer("a.go") // reformats the file before save
681695
env.AfterChange()
682-
loc1 := env.GoToDefinition(env.RegexpSearch("a.go", "Foo"))
696+
loc1 := env.FirstDefinition(env.RegexpSearch("a.go", "Foo"))
683697
if diff := cmp.Diff(loc0, loc1); diff != "" {
684698
t.Errorf("mismatching locations (-want +got):\n%s", diff)
685699
}

0 commit comments

Comments
 (0)