Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / internal / lsp / lsp_test.go
1 // Copyright 2018 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package lsp
6
7 import (
8         "context"
9         "fmt"
10         "go/token"
11         "os"
12         "os/exec"
13         "path/filepath"
14         "sort"
15         "testing"
16
17         "golang.org/x/tools/internal/lsp/cache"
18         "golang.org/x/tools/internal/lsp/diff"
19         "golang.org/x/tools/internal/lsp/diff/myers"
20         "golang.org/x/tools/internal/lsp/protocol"
21         "golang.org/x/tools/internal/lsp/source"
22         "golang.org/x/tools/internal/lsp/tests"
23         "golang.org/x/tools/internal/span"
24         "golang.org/x/tools/internal/testenv"
25 )
26
27 func TestMain(m *testing.M) {
28         testenv.ExitIfSmallMachine()
29         os.Exit(m.Run())
30 }
31
32 func TestLSP(t *testing.T) {
33         tests.RunTests(t, "testdata", true, testLSP)
34 }
35
36 type runner struct {
37         server      *Server
38         data        *tests.Data
39         diagnostics map[span.URI][]*source.Diagnostic
40         ctx         context.Context
41 }
42
43 func testLSP(t *testing.T, datum *tests.Data) {
44         ctx := tests.Context(t)
45
46         cache := cache.New(ctx, nil)
47         session := cache.NewSession(ctx)
48         options := source.DefaultOptions().Clone()
49         tests.DefaultOptions(options)
50         session.SetOptions(options)
51         options.SetEnvSlice(datum.Config.Env)
52         view, snapshot, release, err := session.NewView(ctx, datum.Config.Dir, span.URIFromPath(datum.Config.Dir), "", options)
53         if err != nil {
54                 t.Fatal(err)
55         }
56
57         defer view.Shutdown(ctx)
58
59         // Enable type error analyses for tests.
60         // TODO(golang/go#38212): Delete this once they are enabled by default.
61         tests.EnableAllAnalyzers(view, options)
62         view.SetOptions(ctx, options)
63
64         // Only run the -modfile specific tests in module mode with Go 1.14 or above.
65         datum.ModfileFlagAvailable = len(snapshot.ModFiles()) > 0 && testenv.Go1Point() >= 14
66         release()
67
68         var modifications []source.FileModification
69         for filename, content := range datum.Config.Overlay {
70                 kind := source.DetectLanguage("", filename)
71                 if kind != source.Go {
72                         continue
73                 }
74                 modifications = append(modifications, source.FileModification{
75                         URI:        span.URIFromPath(filename),
76                         Action:     source.Open,
77                         Version:    -1,
78                         Text:       content,
79                         LanguageID: "go",
80                 })
81         }
82         if err := session.ModifyFiles(ctx, modifications); err != nil {
83                 t.Fatal(err)
84         }
85         r := &runner{
86                 server: NewServer(session, testClient{}),
87                 data:   datum,
88                 ctx:    ctx,
89         }
90         tests.Run(t, r, datum)
91 }
92
93 // testClient stubs any client functions that may be called by LSP functions.
94 type testClient struct {
95         protocol.Client
96 }
97
98 // Trivially implement PublishDiagnostics so that we can call
99 // server.publishReports below to de-dup sent diagnostics.
100 func (c testClient) PublishDiagnostics(context.Context, *protocol.PublishDiagnosticsParams) error {
101         return nil
102 }
103
104 func (r *runner) CallHierarchy(t *testing.T, spn span.Span, expectedCalls *tests.CallHierarchyResult) {
105         mapper, err := r.data.Mapper(spn.URI())
106         if err != nil {
107                 t.Fatal(err)
108         }
109         loc, err := mapper.Location(spn)
110         if err != nil {
111                 t.Fatalf("failed for %v: %v", spn, err)
112         }
113
114         params := &protocol.CallHierarchyPrepareParams{
115                 TextDocumentPositionParams: protocol.TextDocumentPositionParams{
116                         TextDocument: protocol.TextDocumentIdentifier{URI: loc.URI},
117                         Position:     loc.Range.Start,
118                 },
119         }
120
121         items, err := r.server.PrepareCallHierarchy(r.ctx, params)
122         if err != nil {
123                 t.Fatal(err)
124         }
125         if len(items) == 0 {
126                 t.Fatalf("expected call hierarchy item to be returned for identifier at %v\n", loc.Range)
127         }
128
129         callLocation := protocol.Location{
130                 URI:   items[0].URI,
131                 Range: items[0].Range,
132         }
133         if callLocation != loc {
134                 t.Fatalf("expected server.PrepareCallHierarchy to return identifier at %v but got %v\n", loc, callLocation)
135         }
136
137         incomingCalls, err := r.server.IncomingCalls(r.ctx, &protocol.CallHierarchyIncomingCallsParams{Item: items[0]})
138         if err != nil {
139                 t.Error(err)
140         }
141         var incomingCallItems []protocol.CallHierarchyItem
142         for _, item := range incomingCalls {
143                 incomingCallItems = append(incomingCallItems, item.From)
144         }
145         msg := tests.DiffCallHierarchyItems(incomingCallItems, expectedCalls.IncomingCalls)
146         if msg != "" {
147                 t.Error(fmt.Sprintf("incoming calls: %s", msg))
148         }
149
150         outgoingCalls, err := r.server.OutgoingCalls(r.ctx, &protocol.CallHierarchyOutgoingCallsParams{Item: items[0]})
151         if err != nil {
152                 t.Error(err)
153         }
154         var outgoingCallItems []protocol.CallHierarchyItem
155         for _, item := range outgoingCalls {
156                 outgoingCallItems = append(outgoingCallItems, item.To)
157         }
158         msg = tests.DiffCallHierarchyItems(outgoingCallItems, expectedCalls.OutgoingCalls)
159         if msg != "" {
160                 t.Error(fmt.Sprintf("outgoing calls: %s", msg))
161         }
162 }
163
164 func (r *runner) CodeLens(t *testing.T, uri span.URI, want []protocol.CodeLens) {
165         if source.DetectLanguage("", uri.Filename()) != source.Mod {
166                 return
167         }
168         got, err := r.server.codeLens(r.ctx, &protocol.CodeLensParams{
169                 TextDocument: protocol.TextDocumentIdentifier{
170                         URI: protocol.DocumentURI(uri),
171                 },
172         })
173         if err != nil {
174                 t.Fatal(err)
175         }
176         if diff := tests.DiffCodeLens(uri, want, got); diff != "" {
177                 t.Errorf("%s: %s", uri, diff)
178         }
179 }
180
181 func (r *runner) Diagnostics(t *testing.T, uri span.URI, want []*source.Diagnostic) {
182         // Get the diagnostics for this view if we have not done it before.
183         v := r.server.session.View(r.data.Config.Dir)
184         r.collectDiagnostics(v)
185         d := r.diagnostics[uri]
186         got := make([]*source.Diagnostic, len(d))
187         copy(got, d)
188         // A special case to test that there are no diagnostics for a file.
189         if len(want) == 1 && want[0].Source == "no_diagnostics" {
190                 if len(got) != 0 {
191                         t.Errorf("expected no diagnostics for %s, got %v", uri, got)
192                 }
193                 return
194         }
195         if diff := tests.DiffDiagnostics(uri, want, got); diff != "" {
196                 t.Error(diff)
197         }
198 }
199
200 func (r *runner) FoldingRanges(t *testing.T, spn span.Span) {
201         uri := spn.URI()
202         view, err := r.server.session.ViewOf(uri)
203         if err != nil {
204                 t.Fatal(err)
205         }
206         original := view.Options()
207         modified := original
208
209         // Test all folding ranges.
210         modified.LineFoldingOnly = false
211         view, err = view.SetOptions(r.ctx, modified)
212         if err != nil {
213                 t.Error(err)
214                 return
215         }
216         ranges, err := r.server.FoldingRange(r.ctx, &protocol.FoldingRangeParams{
217                 TextDocument: protocol.TextDocumentIdentifier{
218                         URI: protocol.URIFromSpanURI(uri),
219                 },
220         })
221         if err != nil {
222                 t.Error(err)
223                 return
224         }
225         r.foldingRanges(t, "foldingRange", uri, ranges)
226
227         // Test folding ranges with lineFoldingOnly = true.
228         modified.LineFoldingOnly = true
229         view, err = view.SetOptions(r.ctx, modified)
230         if err != nil {
231                 t.Error(err)
232                 return
233         }
234         ranges, err = r.server.FoldingRange(r.ctx, &protocol.FoldingRangeParams{
235                 TextDocument: protocol.TextDocumentIdentifier{
236                         URI: protocol.URIFromSpanURI(uri),
237                 },
238         })
239         if err != nil {
240                 t.Error(err)
241                 return
242         }
243         r.foldingRanges(t, "foldingRange-lineFolding", uri, ranges)
244         view.SetOptions(r.ctx, original)
245 }
246
247 func (r *runner) foldingRanges(t *testing.T, prefix string, uri span.URI, ranges []protocol.FoldingRange) {
248         m, err := r.data.Mapper(uri)
249         if err != nil {
250                 t.Fatal(err)
251         }
252         // Fold all ranges.
253         nonOverlapping := nonOverlappingRanges(ranges)
254         for i, rngs := range nonOverlapping {
255                 got, err := foldRanges(m, string(m.Content), rngs)
256                 if err != nil {
257                         t.Error(err)
258                         continue
259                 }
260                 tag := fmt.Sprintf("%s-%d", prefix, i)
261                 want := string(r.data.Golden(tag, uri.Filename(), func() ([]byte, error) {
262                         return []byte(got), nil
263                 }))
264
265                 if want != got {
266                         t.Errorf("%s: foldingRanges failed for %s, expected:\n%v\ngot:\n%v", tag, uri.Filename(), want, got)
267                 }
268         }
269
270         // Filter by kind.
271         kinds := []protocol.FoldingRangeKind{protocol.Imports, protocol.Comment}
272         for _, kind := range kinds {
273                 var kindOnly []protocol.FoldingRange
274                 for _, fRng := range ranges {
275                         if fRng.Kind == string(kind) {
276                                 kindOnly = append(kindOnly, fRng)
277                         }
278                 }
279
280                 nonOverlapping := nonOverlappingRanges(kindOnly)
281                 for i, rngs := range nonOverlapping {
282                         got, err := foldRanges(m, string(m.Content), rngs)
283                         if err != nil {
284                                 t.Error(err)
285                                 continue
286                         }
287                         tag := fmt.Sprintf("%s-%s-%d", prefix, kind, i)
288                         want := string(r.data.Golden(tag, uri.Filename(), func() ([]byte, error) {
289                                 return []byte(got), nil
290                         }))
291
292                         if want != got {
293                                 t.Errorf("%s: foldingRanges failed for %s, expected:\n%v\ngot:\n%v", tag, uri.Filename(), want, got)
294                         }
295                 }
296
297         }
298 }
299
300 func nonOverlappingRanges(ranges []protocol.FoldingRange) (res [][]protocol.FoldingRange) {
301         for _, fRng := range ranges {
302                 setNum := len(res)
303                 for i := 0; i < len(res); i++ {
304                         canInsert := true
305                         for _, rng := range res[i] {
306                                 if conflict(rng, fRng) {
307                                         canInsert = false
308                                         break
309                                 }
310                         }
311                         if canInsert {
312                                 setNum = i
313                                 break
314                         }
315                 }
316                 if setNum == len(res) {
317                         res = append(res, []protocol.FoldingRange{})
318                 }
319                 res[setNum] = append(res[setNum], fRng)
320         }
321         return res
322 }
323
324 func conflict(a, b protocol.FoldingRange) bool {
325         // a start position is <= b start positions
326         return (a.StartLine < b.StartLine || (a.StartLine == b.StartLine && a.StartCharacter <= b.StartCharacter)) &&
327                 (a.EndLine > b.StartLine || (a.EndLine == b.StartLine && a.EndCharacter > b.StartCharacter))
328 }
329
330 func foldRanges(m *protocol.ColumnMapper, contents string, ranges []protocol.FoldingRange) (string, error) {
331         foldedText := "<>"
332         res := contents
333         // Apply the edits from the end of the file forward
334         // to preserve the offsets
335         for i := len(ranges) - 1; i >= 0; i-- {
336                 fRange := ranges[i]
337                 spn, err := m.RangeSpan(protocol.Range{
338                         Start: protocol.Position{
339                                 Line:      fRange.StartLine,
340                                 Character: fRange.StartCharacter,
341                         },
342                         End: protocol.Position{
343                                 Line:      fRange.EndLine,
344                                 Character: fRange.EndCharacter,
345                         },
346                 })
347                 if err != nil {
348                         return "", err
349                 }
350                 start := spn.Start().Offset()
351                 end := spn.End().Offset()
352
353                 tmp := res[0:start] + foldedText
354                 res = tmp + res[end:]
355         }
356         return res, nil
357 }
358
359 func (r *runner) Format(t *testing.T, spn span.Span) {
360         uri := spn.URI()
361         filename := uri.Filename()
362         gofmted := string(r.data.Golden("gofmt", filename, func() ([]byte, error) {
363                 cmd := exec.Command("gofmt", filename)
364                 out, _ := cmd.Output() // ignore error, sometimes we have intentionally ungofmt-able files
365                 return out, nil
366         }))
367
368         edits, err := r.server.Formatting(r.ctx, &protocol.DocumentFormattingParams{
369                 TextDocument: protocol.TextDocumentIdentifier{
370                         URI: protocol.URIFromSpanURI(uri),
371                 },
372         })
373         if err != nil {
374                 if gofmted != "" {
375                         t.Error(err)
376                 }
377                 return
378         }
379         m, err := r.data.Mapper(uri)
380         if err != nil {
381                 t.Fatal(err)
382         }
383         sedits, err := source.FromProtocolEdits(m, edits)
384         if err != nil {
385                 t.Error(err)
386         }
387         got := diff.ApplyEdits(string(m.Content), sedits)
388         if gofmted != got {
389                 t.Errorf("format failed for %s, expected:\n%v\ngot:\n%v", filename, gofmted, got)
390         }
391 }
392
393 func (r *runner) SemanticTokens(t *testing.T, spn span.Span) {
394         uri := spn.URI()
395         filename := uri.Filename()
396         // this is called solely for coverage in semantic.go
397         _, err := r.server.semanticTokensFull(r.ctx, &protocol.SemanticTokensParams{
398                 TextDocument: protocol.TextDocumentIdentifier{
399                         URI: protocol.URIFromSpanURI(uri),
400                 },
401         })
402         if err != nil {
403                 t.Errorf("%v for %s", err, filename)
404         }
405         _, err = r.server.semanticTokensRange(r.ctx, &protocol.SemanticTokensRangeParams{
406                 TextDocument: protocol.TextDocumentIdentifier{
407                         URI: protocol.URIFromSpanURI(uri),
408                 },
409                 // any legal range. Just to exercise the call.
410                 Range: protocol.Range{
411                         Start: protocol.Position{
412                                 Line:      0,
413                                 Character: 0,
414                         },
415                         End: protocol.Position{
416                                 Line:      2,
417                                 Character: 0,
418                         },
419                 },
420         })
421         if err != nil {
422                 t.Errorf("%v for Range %s", err, filename)
423         }
424 }
425
426 func (r *runner) Import(t *testing.T, spn span.Span) {
427         uri := spn.URI()
428         filename := uri.Filename()
429         actions, err := r.server.CodeAction(r.ctx, &protocol.CodeActionParams{
430                 TextDocument: protocol.TextDocumentIdentifier{
431                         URI: protocol.URIFromSpanURI(uri),
432                 },
433         })
434         if err != nil {
435                 t.Fatal(err)
436         }
437         m, err := r.data.Mapper(uri)
438         if err != nil {
439                 t.Fatal(err)
440         }
441         got := string(m.Content)
442         if len(actions) > 0 {
443                 res, err := applyTextDocumentEdits(r, actions[0].Edit.DocumentChanges)
444                 if err != nil {
445                         t.Fatal(err)
446                 }
447                 got = res[uri]
448         }
449         want := string(r.data.Golden("goimports", filename, func() ([]byte, error) {
450                 return []byte(got), nil
451         }))
452         if want != got {
453                 d := myers.ComputeEdits(uri, want, got)
454                 t.Errorf("import failed for %s: %s", filename, diff.ToUnified("want", "got", want, d))
455         }
456 }
457
458 func (r *runner) SuggestedFix(t *testing.T, spn span.Span, actionKinds []string) {
459         uri := spn.URI()
460         view, err := r.server.session.ViewOf(uri)
461         if err != nil {
462                 t.Fatal(err)
463         }
464
465         snapshot, release := view.Snapshot(r.ctx)
466         defer release()
467
468         fh, err := snapshot.GetVersionedFile(r.ctx, uri)
469         if err != nil {
470                 t.Fatal(err)
471         }
472         m, err := r.data.Mapper(uri)
473         if err != nil {
474                 t.Fatal(err)
475         }
476         rng, err := m.Range(spn)
477         if err != nil {
478                 t.Fatal(err)
479         }
480         // Get the diagnostics for this view if we have not done it before.
481         r.collectDiagnostics(view)
482         var diagnostics []protocol.Diagnostic
483         for _, d := range r.diagnostics[uri] {
484                 // Compare the start positions rather than the entire range because
485                 // some diagnostics have a range with the same start and end position (8:1-8:1).
486                 // The current marker functionality prevents us from having a range of 0 length.
487                 if protocol.ComparePosition(d.Range.Start, rng.Start) == 0 {
488                         diagnostics = append(diagnostics, toProtocolDiagnostics([]*source.Diagnostic{d})...)
489                         break
490                 }
491         }
492         codeActionKinds := []protocol.CodeActionKind{}
493         for _, k := range actionKinds {
494                 codeActionKinds = append(codeActionKinds, protocol.CodeActionKind(k))
495         }
496         actions, err := r.server.CodeAction(r.ctx, &protocol.CodeActionParams{
497                 TextDocument: protocol.TextDocumentIdentifier{
498                         URI: protocol.URIFromSpanURI(uri),
499                 },
500                 Range: rng,
501                 Context: protocol.CodeActionContext{
502                         Only:        codeActionKinds,
503                         Diagnostics: diagnostics,
504                 },
505         })
506         if err != nil {
507                 t.Fatalf("CodeAction %s failed: %v", spn, err)
508         }
509         if len(actions) != 1 {
510                 // Hack: We assume that we only get one code action per range.
511                 // TODO(rstambler): Support multiple code actions per test.
512                 t.Fatalf("unexpected number of code actions, want 1, got %v", len(actions))
513         }
514         action := actions[0]
515         var match bool
516         for _, k := range codeActionKinds {
517                 if action.Kind == k {
518                         match = true
519                         break
520                 }
521         }
522         if !match {
523                 t.Fatalf("unexpected kind for code action %s, expected one of %v, got %v", action.Title, codeActionKinds, action.Kind)
524         }
525         var res map[span.URI]string
526         if cmd := action.Command; cmd != nil {
527                 edits, err := commandToEdits(r.ctx, snapshot, fh, rng, action.Command.Command)
528                 if err != nil {
529                         t.Fatal(err)
530                 }
531                 res, err = applyTextDocumentEdits(r, edits)
532                 if err != nil {
533                         t.Fatal(err)
534                 }
535         } else {
536                 res, err = applyTextDocumentEdits(r, action.Edit.DocumentChanges)
537                 if err != nil {
538                         t.Fatal(err)
539                 }
540         }
541         for u, got := range res {
542                 want := string(r.data.Golden("suggestedfix_"+tests.SpanName(spn), u.Filename(), func() ([]byte, error) {
543                         return []byte(got), nil
544                 }))
545                 if want != got {
546                         t.Errorf("suggested fixes failed for %s:\n%s", u.Filename(), tests.Diff(want, got))
547                 }
548         }
549 }
550
551 func commandToEdits(ctx context.Context, snapshot source.Snapshot, fh source.VersionedFileHandle, rng protocol.Range, cmd string) ([]protocol.TextDocumentEdit, error) {
552         var command *source.Command
553         for _, c := range source.Commands {
554                 if c.ID() == cmd {
555                         command = c
556                         break
557                 }
558         }
559         if command == nil {
560                 return nil, fmt.Errorf("no known command for %s", cmd)
561         }
562         if !command.Applies(ctx, snapshot, fh, rng) {
563                 return nil, fmt.Errorf("cannot apply %v", command.ID())
564         }
565         return command.SuggestedFix(ctx, snapshot, fh, rng)
566 }
567
568 func (r *runner) FunctionExtraction(t *testing.T, start span.Span, end span.Span) {
569         uri := start.URI()
570         view, err := r.server.session.ViewOf(uri)
571         if err != nil {
572                 t.Fatal(err)
573         }
574
575         snapshot, release := view.Snapshot(r.ctx)
576         defer release()
577
578         fh, err := snapshot.GetVersionedFile(r.ctx, uri)
579         if err != nil {
580                 t.Fatal(err)
581         }
582         m, err := r.data.Mapper(uri)
583         if err != nil {
584                 t.Fatal(err)
585         }
586         spn := span.New(start.URI(), start.Start(), end.End())
587         rng, err := m.Range(spn)
588         if err != nil {
589                 t.Fatal(err)
590         }
591         actions, err := r.server.CodeAction(r.ctx, &protocol.CodeActionParams{
592                 TextDocument: protocol.TextDocumentIdentifier{
593                         URI: protocol.URIFromSpanURI(uri),
594                 },
595                 Range: rng,
596                 Context: protocol.CodeActionContext{
597                         Only: []protocol.CodeActionKind{"refactor.extract"},
598                 },
599         })
600         if err != nil {
601                 t.Fatal(err)
602         }
603         // Hack: We assume that we only get one code action per range.
604         // TODO(rstambler): Support multiple code actions per test.
605         if len(actions) == 0 || len(actions) > 1 {
606                 t.Fatalf("unexpected number of code actions, want 1, got %v", len(actions))
607         }
608         edits, err := commandToEdits(r.ctx, snapshot, fh, rng, actions[0].Command.Command)
609         if err != nil {
610                 t.Fatal(err)
611         }
612         res, err := applyTextDocumentEdits(r, edits)
613         if err != nil {
614                 t.Fatal(err)
615         }
616         for u, got := range res {
617                 want := string(r.data.Golden("functionextraction_"+tests.SpanName(spn), u.Filename(), func() ([]byte, error) {
618                         return []byte(got), nil
619                 }))
620                 if want != got {
621                         t.Errorf("function extraction failed for %s:\n%s", u.Filename(), tests.Diff(want, got))
622                 }
623         }
624 }
625
626 func (r *runner) Definition(t *testing.T, spn span.Span, d tests.Definition) {
627         sm, err := r.data.Mapper(d.Src.URI())
628         if err != nil {
629                 t.Fatal(err)
630         }
631         loc, err := sm.Location(d.Src)
632         if err != nil {
633                 t.Fatalf("failed for %v: %v", d.Src, err)
634         }
635         tdpp := protocol.TextDocumentPositionParams{
636                 TextDocument: protocol.TextDocumentIdentifier{URI: loc.URI},
637                 Position:     loc.Range.Start,
638         }
639         var locs []protocol.Location
640         var hover *protocol.Hover
641         if d.IsType {
642                 params := &protocol.TypeDefinitionParams{
643                         TextDocumentPositionParams: tdpp,
644                 }
645                 locs, err = r.server.TypeDefinition(r.ctx, params)
646         } else {
647                 params := &protocol.DefinitionParams{
648                         TextDocumentPositionParams: tdpp,
649                 }
650                 locs, err = r.server.Definition(r.ctx, params)
651                 if err != nil {
652                         t.Fatalf("failed for %v: %+v", d.Src, err)
653                 }
654                 v := &protocol.HoverParams{
655                         TextDocumentPositionParams: tdpp,
656                 }
657                 hover, err = r.server.Hover(r.ctx, v)
658         }
659         if err != nil {
660                 t.Fatalf("failed for %v: %v", d.Src, err)
661         }
662         if len(locs) != 1 {
663                 t.Errorf("got %d locations for definition, expected 1", len(locs))
664         }
665         didSomething := false
666         if hover != nil {
667                 didSomething = true
668                 tag := fmt.Sprintf("%s-hover", d.Name)
669                 expectHover := string(r.data.Golden(tag, d.Src.URI().Filename(), func() ([]byte, error) {
670                         return []byte(hover.Contents.Value), nil
671                 }))
672                 if hover.Contents.Value != expectHover {
673                         t.Errorf("%s:\n%s", d.Src, tests.Diff(expectHover, hover.Contents.Value))
674                 }
675         }
676         if !d.OnlyHover {
677                 didSomething = true
678                 locURI := locs[0].URI.SpanURI()
679                 lm, err := r.data.Mapper(locURI)
680                 if err != nil {
681                         t.Fatal(err)
682                 }
683                 if def, err := lm.Span(locs[0]); err != nil {
684                         t.Fatalf("failed for %v: %v", locs[0], err)
685                 } else if def != d.Def {
686                         t.Errorf("for %v got %v want %v", d.Src, def, d.Def)
687                 }
688         }
689         if !didSomething {
690                 t.Errorf("no tests ran for %s", d.Src.URI())
691         }
692 }
693
694 func (r *runner) Implementation(t *testing.T, spn span.Span, impls []span.Span) {
695         sm, err := r.data.Mapper(spn.URI())
696         if err != nil {
697                 t.Fatal(err)
698         }
699         loc, err := sm.Location(spn)
700         if err != nil {
701                 t.Fatalf("failed for %v: %v", spn, err)
702         }
703         tdpp := protocol.TextDocumentPositionParams{
704                 TextDocument: protocol.TextDocumentIdentifier{URI: loc.URI},
705                 Position:     loc.Range.Start,
706         }
707         var locs []protocol.Location
708         params := &protocol.ImplementationParams{
709                 TextDocumentPositionParams: tdpp,
710         }
711         locs, err = r.server.Implementation(r.ctx, params)
712         if err != nil {
713                 t.Fatalf("failed for %v: %v", spn, err)
714         }
715         if len(locs) != len(impls) {
716                 t.Fatalf("got %d locations for implementation, expected %d", len(locs), len(impls))
717         }
718
719         var results []span.Span
720         for i := range locs {
721                 locURI := locs[i].URI.SpanURI()
722                 lm, err := r.data.Mapper(locURI)
723                 if err != nil {
724                         t.Fatal(err)
725                 }
726                 imp, err := lm.Span(locs[i])
727                 if err != nil {
728                         t.Fatalf("failed for %v: %v", locs[i], err)
729                 }
730                 results = append(results, imp)
731         }
732         // Sort results and expected to make tests deterministic.
733         sort.SliceStable(results, func(i, j int) bool {
734                 return span.Compare(results[i], results[j]) == -1
735         })
736         sort.SliceStable(impls, func(i, j int) bool {
737                 return span.Compare(impls[i], impls[j]) == -1
738         })
739         for i := range results {
740                 if results[i] != impls[i] {
741                         t.Errorf("for %dth implementation of %v got %v want %v", i, spn, results[i], impls[i])
742                 }
743         }
744 }
745
746 func (r *runner) Highlight(t *testing.T, src span.Span, locations []span.Span) {
747         m, err := r.data.Mapper(src.URI())
748         if err != nil {
749                 t.Fatal(err)
750         }
751         loc, err := m.Location(src)
752         if err != nil {
753                 t.Fatalf("failed for %v: %v", locations[0], err)
754         }
755         tdpp := protocol.TextDocumentPositionParams{
756                 TextDocument: protocol.TextDocumentIdentifier{URI: loc.URI},
757                 Position:     loc.Range.Start,
758         }
759         params := &protocol.DocumentHighlightParams{
760                 TextDocumentPositionParams: tdpp,
761         }
762         highlights, err := r.server.DocumentHighlight(r.ctx, params)
763         if err != nil {
764                 t.Fatal(err)
765         }
766         if len(highlights) != len(locations) {
767                 t.Fatalf("got %d highlights for highlight at %v:%v:%v, expected %d", len(highlights), src.URI().Filename(), src.Start().Line(), src.Start().Column(), len(locations))
768         }
769         // Check to make sure highlights have a valid range.
770         var results []span.Span
771         for i := range highlights {
772                 h, err := m.RangeSpan(highlights[i].Range)
773                 if err != nil {
774                         t.Fatalf("failed for %v: %v", highlights[i], err)
775                 }
776                 results = append(results, h)
777         }
778         // Sort results to make tests deterministic since DocumentHighlight uses a map.
779         sort.SliceStable(results, func(i, j int) bool {
780                 return span.Compare(results[i], results[j]) == -1
781         })
782         // Check to make sure all the expected highlights are found.
783         for i := range results {
784                 if results[i] != locations[i] {
785                         t.Errorf("want %v, got %v\n", locations[i], results[i])
786                 }
787         }
788 }
789
790 func (r *runner) References(t *testing.T, src span.Span, itemList []span.Span) {
791         sm, err := r.data.Mapper(src.URI())
792         if err != nil {
793                 t.Fatal(err)
794         }
795         loc, err := sm.Location(src)
796         if err != nil {
797                 t.Fatalf("failed for %v: %v", src, err)
798         }
799         for _, includeDeclaration := range []bool{true, false} {
800                 t.Run(fmt.Sprintf("refs-declaration-%v", includeDeclaration), func(t *testing.T) {
801                         want := make(map[protocol.Location]bool)
802                         for i, pos := range itemList {
803                                 // We don't want the first result if we aren't including the declaration.
804                                 if i == 0 && !includeDeclaration {
805                                         continue
806                                 }
807                                 m, err := r.data.Mapper(pos.URI())
808                                 if err != nil {
809                                         t.Fatal(err)
810                                 }
811                                 loc, err := m.Location(pos)
812                                 if err != nil {
813                                         t.Fatalf("failed for %v: %v", src, err)
814                                 }
815                                 want[loc] = true
816                         }
817                         params := &protocol.ReferenceParams{
818                                 TextDocumentPositionParams: protocol.TextDocumentPositionParams{
819                                         TextDocument: protocol.TextDocumentIdentifier{URI: loc.URI},
820                                         Position:     loc.Range.Start,
821                                 },
822                                 Context: protocol.ReferenceContext{
823                                         IncludeDeclaration: includeDeclaration,
824                                 },
825                         }
826                         got, err := r.server.References(r.ctx, params)
827                         if err != nil {
828                                 t.Fatalf("failed for %v: %v", src, err)
829                         }
830                         if len(got) != len(want) {
831                                 t.Errorf("references failed: different lengths got %v want %v", len(got), len(want))
832                         }
833                         for _, loc := range got {
834                                 if !want[loc] {
835                                         t.Errorf("references failed: incorrect references got %v want %v", loc, want)
836                                 }
837                         }
838                 })
839         }
840 }
841
842 func (r *runner) Rename(t *testing.T, spn span.Span, newText string) {
843         tag := fmt.Sprintf("%s-rename", newText)
844
845         uri := spn.URI()
846         filename := uri.Filename()
847         sm, err := r.data.Mapper(uri)
848         if err != nil {
849                 t.Fatal(err)
850         }
851         loc, err := sm.Location(spn)
852         if err != nil {
853                 t.Fatalf("failed for %v: %v", spn, err)
854         }
855
856         wedit, err := r.server.Rename(r.ctx, &protocol.RenameParams{
857                 TextDocument: protocol.TextDocumentIdentifier{
858                         URI: protocol.URIFromSpanURI(uri),
859                 },
860                 Position: loc.Range.Start,
861                 NewName:  newText,
862         })
863         if err != nil {
864                 renamed := string(r.data.Golden(tag, filename, func() ([]byte, error) {
865                         return []byte(err.Error()), nil
866                 }))
867                 if err.Error() != renamed {
868                         t.Errorf("rename failed for %s, expected:\n%v\ngot:\n%v\n", newText, renamed, err)
869                 }
870                 return
871         }
872         res, err := applyTextDocumentEdits(r, wedit.DocumentChanges)
873         if err != nil {
874                 t.Fatal(err)
875         }
876         var orderedURIs []string
877         for uri := range res {
878                 orderedURIs = append(orderedURIs, string(uri))
879         }
880         sort.Strings(orderedURIs)
881
882         var got string
883         for i := 0; i < len(res); i++ {
884                 if i != 0 {
885                         got += "\n"
886                 }
887                 uri := span.URIFromURI(orderedURIs[i])
888                 if len(res) > 1 {
889                         got += filepath.Base(uri.Filename()) + ":\n"
890                 }
891                 val := res[uri]
892                 got += val
893         }
894         want := string(r.data.Golden(tag, filename, func() ([]byte, error) {
895                 return []byte(got), nil
896         }))
897         if want != got {
898                 t.Errorf("rename failed for %s:\n%s", newText, tests.Diff(want, got))
899         }
900 }
901
902 func (r *runner) PrepareRename(t *testing.T, src span.Span, want *source.PrepareItem) {
903         m, err := r.data.Mapper(src.URI())
904         if err != nil {
905                 t.Fatal(err)
906         }
907         loc, err := m.Location(src)
908         if err != nil {
909                 t.Fatalf("failed for %v: %v", src, err)
910         }
911         tdpp := protocol.TextDocumentPositionParams{
912                 TextDocument: protocol.TextDocumentIdentifier{URI: loc.URI},
913                 Position:     loc.Range.Start,
914         }
915         params := &protocol.PrepareRenameParams{
916                 TextDocumentPositionParams: tdpp,
917         }
918         got, err := r.server.PrepareRename(context.Background(), params)
919         if err != nil {
920                 t.Errorf("prepare rename failed for %v: got error: %v", src, err)
921                 return
922         }
923         // we all love typed nils
924         if got == nil {
925                 if want.Text != "" { // expected an ident.
926                         t.Errorf("prepare rename failed for %v: got nil", src)
927                 }
928                 return
929         }
930         if got.Start == got.End {
931                 // Special case for 0-length ranges. Marks can't specify a 0-length range,
932                 // so just compare the start.
933                 if got.Start != want.Range.Start {
934                         t.Errorf("prepare rename failed: incorrect point, got %v want %v", got.Start, want.Range.Start)
935                 }
936         } else {
937                 if protocol.CompareRange(*got, want.Range) != 0 {
938                         t.Errorf("prepare rename failed: incorrect range got %v want %v", *got, want.Range)
939                 }
940         }
941 }
942
943 func applyTextDocumentEdits(r *runner, edits []protocol.TextDocumentEdit) (map[span.URI]string, error) {
944         res := map[span.URI]string{}
945         for _, docEdits := range edits {
946                 uri := docEdits.TextDocument.URI.SpanURI()
947                 var m *protocol.ColumnMapper
948                 // If we have already edited this file, we use the edited version (rather than the
949                 // file in its original state) so that we preserve our initial changes.
950                 if content, ok := res[uri]; ok {
951                         m = &protocol.ColumnMapper{
952                                 URI: uri,
953                                 Converter: span.NewContentConverter(
954                                         uri.Filename(), []byte(content)),
955                                 Content: []byte(content),
956                         }
957                 } else {
958                         var err error
959                         if m, err = r.data.Mapper(uri); err != nil {
960                                 return nil, err
961                         }
962                 }
963                 res[uri] = string(m.Content)
964                 sedits, err := source.FromProtocolEdits(m, docEdits.Edits)
965                 if err != nil {
966                         return nil, err
967                 }
968                 res[uri] = applyEdits(res[uri], sedits)
969         }
970         return res, nil
971 }
972
973 func applyEdits(contents string, edits []diff.TextEdit) string {
974         res := contents
975
976         // Apply the edits from the end of the file forward
977         // to preserve the offsets
978         for i := len(edits) - 1; i >= 0; i-- {
979                 edit := edits[i]
980                 start := edit.Span.Start().Offset()
981                 end := edit.Span.End().Offset()
982                 tmp := res[0:start] + edit.NewText
983                 res = tmp + res[end:]
984         }
985         return res
986 }
987
988 func (r *runner) Symbols(t *testing.T, uri span.URI, expectedSymbols []protocol.DocumentSymbol) {
989         params := &protocol.DocumentSymbolParams{
990                 TextDocument: protocol.TextDocumentIdentifier{
991                         URI: protocol.URIFromSpanURI(uri),
992                 },
993         }
994         got, err := r.server.DocumentSymbol(r.ctx, params)
995         if err != nil {
996                 t.Fatal(err)
997         }
998         if len(got) != len(expectedSymbols) {
999                 t.Errorf("want %d top-level symbols in %v, got %d", len(expectedSymbols), uri, len(got))
1000                 return
1001         }
1002         symbols := make([]protocol.DocumentSymbol, len(got))
1003         for i, s := range got {
1004                 s, ok := s.(protocol.DocumentSymbol)
1005                 if !ok {
1006                         t.Fatalf("%v: wanted []DocumentSymbols but got %v", uri, got)
1007                 }
1008                 symbols[i] = s
1009         }
1010         if diff := tests.DiffSymbols(t, uri, expectedSymbols, symbols); diff != "" {
1011                 t.Error(diff)
1012         }
1013 }
1014
1015 func (r *runner) WorkspaceSymbols(t *testing.T, query string, expectedSymbols []protocol.SymbolInformation, dirs map[string]struct{}) {
1016         r.callWorkspaceSymbols(t, query, source.SymbolCaseInsensitive, dirs, expectedSymbols)
1017 }
1018
1019 func (r *runner) FuzzyWorkspaceSymbols(t *testing.T, query string, expectedSymbols []protocol.SymbolInformation, dirs map[string]struct{}) {
1020         r.callWorkspaceSymbols(t, query, source.SymbolFuzzy, dirs, expectedSymbols)
1021 }
1022
1023 func (r *runner) CaseSensitiveWorkspaceSymbols(t *testing.T, query string, expectedSymbols []protocol.SymbolInformation, dirs map[string]struct{}) {
1024         r.callWorkspaceSymbols(t, query, source.SymbolCaseSensitive, dirs, expectedSymbols)
1025 }
1026
1027 func (r *runner) callWorkspaceSymbols(t *testing.T, query string, matcher source.SymbolMatcher, dirs map[string]struct{}, expectedSymbols []protocol.SymbolInformation) {
1028         t.Helper()
1029
1030         original := r.server.session.Options()
1031         modified := original
1032         modified.SymbolMatcher = matcher
1033         r.server.session.SetOptions(modified)
1034         defer r.server.session.SetOptions(original)
1035
1036         params := &protocol.WorkspaceSymbolParams{
1037                 Query: query,
1038         }
1039         got, err := r.server.Symbol(r.ctx, params)
1040         if err != nil {
1041                 t.Fatal(err)
1042         }
1043         got = tests.FilterWorkspaceSymbols(got, dirs)
1044         if diff := tests.DiffWorkspaceSymbols(expectedSymbols, got); diff != "" {
1045                 t.Error(diff)
1046         }
1047 }
1048
1049 func (r *runner) SignatureHelp(t *testing.T, spn span.Span, want *protocol.SignatureHelp) {
1050         m, err := r.data.Mapper(spn.URI())
1051         if err != nil {
1052                 t.Fatal(err)
1053         }
1054         loc, err := m.Location(spn)
1055         if err != nil {
1056                 t.Fatalf("failed for %v: %v", loc, err)
1057         }
1058         tdpp := protocol.TextDocumentPositionParams{
1059                 TextDocument: protocol.TextDocumentIdentifier{
1060                         URI: protocol.URIFromSpanURI(spn.URI()),
1061                 },
1062                 Position: loc.Range.Start,
1063         }
1064         params := &protocol.SignatureHelpParams{
1065                 TextDocumentPositionParams: tdpp,
1066         }
1067         got, err := r.server.SignatureHelp(r.ctx, params)
1068         if err != nil {
1069                 // Only fail if we got an error we did not expect.
1070                 if want != nil {
1071                         t.Fatal(err)
1072                 }
1073                 return
1074         }
1075         if want == nil {
1076                 if got != nil {
1077                         t.Errorf("expected no signature, got %v", got)
1078                 }
1079                 return
1080         }
1081         if got == nil {
1082                 t.Fatalf("expected %v, got nil", want)
1083         }
1084         if diff := tests.DiffSignatures(spn, want, got); diff != "" {
1085                 t.Error(diff)
1086         }
1087 }
1088
1089 func (r *runner) Link(t *testing.T, uri span.URI, wantLinks []tests.Link) {
1090         m, err := r.data.Mapper(uri)
1091         if err != nil {
1092                 t.Fatal(err)
1093         }
1094         got, err := r.server.DocumentLink(r.ctx, &protocol.DocumentLinkParams{
1095                 TextDocument: protocol.TextDocumentIdentifier{
1096                         URI: protocol.URIFromSpanURI(uri),
1097                 },
1098         })
1099         if err != nil {
1100                 t.Fatal(err)
1101         }
1102         if diff := tests.DiffLinks(m, wantLinks, got); diff != "" {
1103                 t.Error(diff)
1104         }
1105 }
1106
1107 func TestBytesOffset(t *testing.T) {
1108         tests := []struct {
1109                 text string
1110                 pos  protocol.Position
1111                 want int
1112         }{
1113                 {text: `a𐐀b`, pos: protocol.Position{Line: 0, Character: 0}, want: 0},
1114                 {text: `a𐐀b`, pos: protocol.Position{Line: 0, Character: 1}, want: 1},
1115                 {text: `a𐐀b`, pos: protocol.Position{Line: 0, Character: 2}, want: 1},
1116                 {text: `a𐐀b`, pos: protocol.Position{Line: 0, Character: 3}, want: 5},
1117                 {text: `a𐐀b`, pos: protocol.Position{Line: 0, Character: 4}, want: 6},
1118                 {text: `a𐐀b`, pos: protocol.Position{Line: 0, Character: 5}, want: -1},
1119                 {text: "aaa\nbbb\n", pos: protocol.Position{Line: 0, Character: 3}, want: 3},
1120                 {text: "aaa\nbbb\n", pos: protocol.Position{Line: 0, Character: 4}, want: 3},
1121                 {text: "aaa\nbbb\n", pos: protocol.Position{Line: 1, Character: 0}, want: 4},
1122                 {text: "aaa\nbbb\n", pos: protocol.Position{Line: 1, Character: 3}, want: 7},
1123                 {text: "aaa\nbbb\n", pos: protocol.Position{Line: 1, Character: 4}, want: 7},
1124                 {text: "aaa\nbbb\n", pos: protocol.Position{Line: 2, Character: 0}, want: 8},
1125                 {text: "aaa\nbbb\n", pos: protocol.Position{Line: 2, Character: 1}, want: -1},
1126                 {text: "aaa\nbbb\n\n", pos: protocol.Position{Line: 2, Character: 0}, want: 8},
1127         }
1128
1129         for i, test := range tests {
1130                 fname := fmt.Sprintf("test %d", i)
1131                 fset := token.NewFileSet()
1132                 f := fset.AddFile(fname, -1, len(test.text))
1133                 f.SetLinesForContent([]byte(test.text))
1134                 uri := span.URIFromPath(fname)
1135                 converter := span.NewContentConverter(fname, []byte(test.text))
1136                 mapper := &protocol.ColumnMapper{
1137                         URI:       uri,
1138                         Converter: converter,
1139                         Content:   []byte(test.text),
1140                 }
1141                 got, err := mapper.Point(test.pos)
1142                 if err != nil && test.want != -1 {
1143                         t.Errorf("unexpected error: %v", err)
1144                 }
1145                 if err == nil && got.Offset() != test.want {
1146                         t.Errorf("want %d for %q(Line:%d,Character:%d), but got %d", test.want, test.text, int(test.pos.Line), int(test.pos.Character), got.Offset())
1147                 }
1148         }
1149 }
1150
1151 func (r *runner) collectDiagnostics(view source.View) {
1152         if r.diagnostics != nil {
1153                 return
1154         }
1155         r.diagnostics = make(map[span.URI][]*source.Diagnostic)
1156
1157         snapshot, release := view.Snapshot(r.ctx)
1158         defer release()
1159
1160         // Always run diagnostics with analysis.
1161         reports, _ := r.server.diagnose(r.ctx, snapshot, true)
1162         r.server.publishReports(r.ctx, snapshot, reports, false)
1163         for uri, sent := range r.server.delivered {
1164                 var diagnostics []*source.Diagnostic
1165                 for _, d := range sent.sorted {
1166                         diagnostics = append(diagnostics, &source.Diagnostic{
1167                                 Range:    d.Range,
1168                                 Message:  d.Message,
1169                                 Related:  d.Related,
1170                                 Severity: d.Severity,
1171                                 Source:   d.Source,
1172                                 Tags:     d.Tags,
1173                         })
1174                 }
1175                 r.diagnostics[uri] = diagnostics
1176         }
1177 }