Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / 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         // no client, so use default
395         rememberToks(SemanticTypes(), SemanticModifiers())
396         uri := spn.URI()
397         filename := uri.Filename()
398         // this is called solely for coverage in semantic.go
399         _, err := r.server.semanticTokensFull(r.ctx, &protocol.SemanticTokensParams{
400                 TextDocument: protocol.TextDocumentIdentifier{
401                         URI: protocol.URIFromSpanURI(uri),
402                 },
403         })
404         if err != nil {
405                 t.Errorf("%v for %s", err, filename)
406         }
407         _, err = r.server.semanticTokensRange(r.ctx, &protocol.SemanticTokensRangeParams{
408                 TextDocument: protocol.TextDocumentIdentifier{
409                         URI: protocol.URIFromSpanURI(uri),
410                 },
411                 // any legal range. Just to exercise the call.
412                 Range: protocol.Range{
413                         Start: protocol.Position{
414                                 Line:      0,
415                                 Character: 0,
416                         },
417                         End: protocol.Position{
418                                 Line:      2,
419                                 Character: 0,
420                         },
421                 },
422         })
423         if err != nil {
424                 t.Errorf("%v for Range %s", err, filename)
425         }
426 }
427
428 func (r *runner) Import(t *testing.T, spn span.Span) {
429         uri := spn.URI()
430         filename := uri.Filename()
431         actions, err := r.server.CodeAction(r.ctx, &protocol.CodeActionParams{
432                 TextDocument: protocol.TextDocumentIdentifier{
433                         URI: protocol.URIFromSpanURI(uri),
434                 },
435         })
436         if err != nil {
437                 t.Fatal(err)
438         }
439         m, err := r.data.Mapper(uri)
440         if err != nil {
441                 t.Fatal(err)
442         }
443         got := string(m.Content)
444         if len(actions) > 0 {
445                 res, err := applyTextDocumentEdits(r, actions[0].Edit.DocumentChanges)
446                 if err != nil {
447                         t.Fatal(err)
448                 }
449                 got = res[uri]
450         }
451         want := string(r.data.Golden("goimports", filename, func() ([]byte, error) {
452                 return []byte(got), nil
453         }))
454         if want != got {
455                 d := myers.ComputeEdits(uri, want, got)
456                 t.Errorf("import failed for %s: %s", filename, diff.ToUnified("want", "got", want, d))
457         }
458 }
459
460 func (r *runner) SuggestedFix(t *testing.T, spn span.Span, actionKinds []string) {
461         uri := spn.URI()
462         view, err := r.server.session.ViewOf(uri)
463         if err != nil {
464                 t.Fatal(err)
465         }
466
467         snapshot, release := view.Snapshot(r.ctx)
468         defer release()
469
470         fh, err := snapshot.GetVersionedFile(r.ctx, uri)
471         if err != nil {
472                 t.Fatal(err)
473         }
474         m, err := r.data.Mapper(uri)
475         if err != nil {
476                 t.Fatal(err)
477         }
478         rng, err := m.Range(spn)
479         if err != nil {
480                 t.Fatal(err)
481         }
482         // Get the diagnostics for this view if we have not done it before.
483         r.collectDiagnostics(view)
484         var diagnostics []protocol.Diagnostic
485         for _, d := range r.diagnostics[uri] {
486                 // Compare the start positions rather than the entire range because
487                 // some diagnostics have a range with the same start and end position (8:1-8:1).
488                 // The current marker functionality prevents us from having a range of 0 length.
489                 if protocol.ComparePosition(d.Range.Start, rng.Start) == 0 {
490                         diagnostics = append(diagnostics, toProtocolDiagnostics([]*source.Diagnostic{d})...)
491                         break
492                 }
493         }
494         codeActionKinds := []protocol.CodeActionKind{}
495         for _, k := range actionKinds {
496                 codeActionKinds = append(codeActionKinds, protocol.CodeActionKind(k))
497         }
498         actions, err := r.server.CodeAction(r.ctx, &protocol.CodeActionParams{
499                 TextDocument: protocol.TextDocumentIdentifier{
500                         URI: protocol.URIFromSpanURI(uri),
501                 },
502                 Range: rng,
503                 Context: protocol.CodeActionContext{
504                         Only:        codeActionKinds,
505                         Diagnostics: diagnostics,
506                 },
507         })
508         if err != nil {
509                 t.Fatalf("CodeAction %s failed: %v", spn, err)
510         }
511         if len(actions) != 1 {
512                 // Hack: We assume that we only get one code action per range.
513                 // TODO(rstambler): Support multiple code actions per test.
514                 t.Fatalf("unexpected number of code actions, want 1, got %v", len(actions))
515         }
516         action := actions[0]
517         var match bool
518         for _, k := range codeActionKinds {
519                 if action.Kind == k {
520                         match = true
521                         break
522                 }
523         }
524         if !match {
525                 t.Fatalf("unexpected kind for code action %s, expected one of %v, got %v", action.Title, codeActionKinds, action.Kind)
526         }
527         var res map[span.URI]string
528         if cmd := action.Command; cmd != nil {
529                 edits, err := commandToEdits(r.ctx, snapshot, fh, rng, action.Command.Command)
530                 if err != nil {
531                         t.Fatal(err)
532                 }
533                 res, err = applyTextDocumentEdits(r, edits)
534                 if err != nil {
535                         t.Fatal(err)
536                 }
537         } else {
538                 res, err = applyTextDocumentEdits(r, action.Edit.DocumentChanges)
539                 if err != nil {
540                         t.Fatal(err)
541                 }
542         }
543         for u, got := range res {
544                 want := string(r.data.Golden("suggestedfix_"+tests.SpanName(spn), u.Filename(), func() ([]byte, error) {
545                         return []byte(got), nil
546                 }))
547                 if want != got {
548                         t.Errorf("suggested fixes failed for %s:\n%s", u.Filename(), tests.Diff(want, got))
549                 }
550         }
551 }
552
553 func commandToEdits(ctx context.Context, snapshot source.Snapshot, fh source.VersionedFileHandle, rng protocol.Range, cmd string) ([]protocol.TextDocumentEdit, error) {
554         var command *source.Command
555         for _, c := range source.Commands {
556                 if c.ID() == cmd {
557                         command = c
558                         break
559                 }
560         }
561         if command == nil {
562                 return nil, fmt.Errorf("no known command for %s", cmd)
563         }
564         if !command.Applies(ctx, snapshot, fh, rng) {
565                 return nil, fmt.Errorf("cannot apply %v", command.ID())
566         }
567         return command.SuggestedFix(ctx, snapshot, fh, rng)
568 }
569
570 func (r *runner) FunctionExtraction(t *testing.T, start span.Span, end span.Span) {
571         uri := start.URI()
572         view, err := r.server.session.ViewOf(uri)
573         if err != nil {
574                 t.Fatal(err)
575         }
576
577         snapshot, release := view.Snapshot(r.ctx)
578         defer release()
579
580         fh, err := snapshot.GetVersionedFile(r.ctx, uri)
581         if err != nil {
582                 t.Fatal(err)
583         }
584         m, err := r.data.Mapper(uri)
585         if err != nil {
586                 t.Fatal(err)
587         }
588         spn := span.New(start.URI(), start.Start(), end.End())
589         rng, err := m.Range(spn)
590         if err != nil {
591                 t.Fatal(err)
592         }
593         actions, err := r.server.CodeAction(r.ctx, &protocol.CodeActionParams{
594                 TextDocument: protocol.TextDocumentIdentifier{
595                         URI: protocol.URIFromSpanURI(uri),
596                 },
597                 Range: rng,
598                 Context: protocol.CodeActionContext{
599                         Only: []protocol.CodeActionKind{"refactor.extract"},
600                 },
601         })
602         if err != nil {
603                 t.Fatal(err)
604         }
605         // Hack: We assume that we only get one code action per range.
606         // TODO(rstambler): Support multiple code actions per test.
607         if len(actions) == 0 || len(actions) > 1 {
608                 t.Fatalf("unexpected number of code actions, want 1, got %v", len(actions))
609         }
610         edits, err := commandToEdits(r.ctx, snapshot, fh, rng, actions[0].Command.Command)
611         if err != nil {
612                 t.Fatal(err)
613         }
614         res, err := applyTextDocumentEdits(r, edits)
615         if err != nil {
616                 t.Fatal(err)
617         }
618         for u, got := range res {
619                 want := string(r.data.Golden("functionextraction_"+tests.SpanName(spn), u.Filename(), func() ([]byte, error) {
620                         return []byte(got), nil
621                 }))
622                 if want != got {
623                         t.Errorf("function extraction failed for %s:\n%s", u.Filename(), tests.Diff(want, got))
624                 }
625         }
626 }
627
628 func (r *runner) Definition(t *testing.T, spn span.Span, d tests.Definition) {
629         sm, err := r.data.Mapper(d.Src.URI())
630         if err != nil {
631                 t.Fatal(err)
632         }
633         loc, err := sm.Location(d.Src)
634         if err != nil {
635                 t.Fatalf("failed for %v: %v", d.Src, err)
636         }
637         tdpp := protocol.TextDocumentPositionParams{
638                 TextDocument: protocol.TextDocumentIdentifier{URI: loc.URI},
639                 Position:     loc.Range.Start,
640         }
641         var locs []protocol.Location
642         var hover *protocol.Hover
643         if d.IsType {
644                 params := &protocol.TypeDefinitionParams{
645                         TextDocumentPositionParams: tdpp,
646                 }
647                 locs, err = r.server.TypeDefinition(r.ctx, params)
648         } else {
649                 params := &protocol.DefinitionParams{
650                         TextDocumentPositionParams: tdpp,
651                 }
652                 locs, err = r.server.Definition(r.ctx, params)
653                 if err != nil {
654                         t.Fatalf("failed for %v: %+v", d.Src, err)
655                 }
656                 v := &protocol.HoverParams{
657                         TextDocumentPositionParams: tdpp,
658                 }
659                 hover, err = r.server.Hover(r.ctx, v)
660         }
661         if err != nil {
662                 t.Fatalf("failed for %v: %v", d.Src, err)
663         }
664         if len(locs) != 1 {
665                 t.Errorf("got %d locations for definition, expected 1", len(locs))
666         }
667         didSomething := false
668         if hover != nil {
669                 didSomething = true
670                 tag := fmt.Sprintf("%s-hover", d.Name)
671                 expectHover := string(r.data.Golden(tag, d.Src.URI().Filename(), func() ([]byte, error) {
672                         return []byte(hover.Contents.Value), nil
673                 }))
674                 if hover.Contents.Value != expectHover {
675                         t.Errorf("%s:\n%s", d.Src, tests.Diff(expectHover, hover.Contents.Value))
676                 }
677         }
678         if !d.OnlyHover {
679                 didSomething = true
680                 locURI := locs[0].URI.SpanURI()
681                 lm, err := r.data.Mapper(locURI)
682                 if err != nil {
683                         t.Fatal(err)
684                 }
685                 if def, err := lm.Span(locs[0]); err != nil {
686                         t.Fatalf("failed for %v: %v", locs[0], err)
687                 } else if def != d.Def {
688                         t.Errorf("for %v got %v want %v", d.Src, def, d.Def)
689                 }
690         }
691         if !didSomething {
692                 t.Errorf("no tests ran for %s", d.Src.URI())
693         }
694 }
695
696 func (r *runner) Implementation(t *testing.T, spn span.Span, impls []span.Span) {
697         sm, err := r.data.Mapper(spn.URI())
698         if err != nil {
699                 t.Fatal(err)
700         }
701         loc, err := sm.Location(spn)
702         if err != nil {
703                 t.Fatalf("failed for %v: %v", spn, err)
704         }
705         tdpp := protocol.TextDocumentPositionParams{
706                 TextDocument: protocol.TextDocumentIdentifier{URI: loc.URI},
707                 Position:     loc.Range.Start,
708         }
709         var locs []protocol.Location
710         params := &protocol.ImplementationParams{
711                 TextDocumentPositionParams: tdpp,
712         }
713         locs, err = r.server.Implementation(r.ctx, params)
714         if err != nil {
715                 t.Fatalf("failed for %v: %v", spn, err)
716         }
717         if len(locs) != len(impls) {
718                 t.Fatalf("got %d locations for implementation, expected %d", len(locs), len(impls))
719         }
720
721         var results []span.Span
722         for i := range locs {
723                 locURI := locs[i].URI.SpanURI()
724                 lm, err := r.data.Mapper(locURI)
725                 if err != nil {
726                         t.Fatal(err)
727                 }
728                 imp, err := lm.Span(locs[i])
729                 if err != nil {
730                         t.Fatalf("failed for %v: %v", locs[i], err)
731                 }
732                 results = append(results, imp)
733         }
734         // Sort results and expected to make tests deterministic.
735         sort.SliceStable(results, func(i, j int) bool {
736                 return span.Compare(results[i], results[j]) == -1
737         })
738         sort.SliceStable(impls, func(i, j int) bool {
739                 return span.Compare(impls[i], impls[j]) == -1
740         })
741         for i := range results {
742                 if results[i] != impls[i] {
743                         t.Errorf("for %dth implementation of %v got %v want %v", i, spn, results[i], impls[i])
744                 }
745         }
746 }
747
748 func (r *runner) Highlight(t *testing.T, src span.Span, locations []span.Span) {
749         m, err := r.data.Mapper(src.URI())
750         if err != nil {
751                 t.Fatal(err)
752         }
753         loc, err := m.Location(src)
754         if err != nil {
755                 t.Fatalf("failed for %v: %v", locations[0], err)
756         }
757         tdpp := protocol.TextDocumentPositionParams{
758                 TextDocument: protocol.TextDocumentIdentifier{URI: loc.URI},
759                 Position:     loc.Range.Start,
760         }
761         params := &protocol.DocumentHighlightParams{
762                 TextDocumentPositionParams: tdpp,
763         }
764         highlights, err := r.server.DocumentHighlight(r.ctx, params)
765         if err != nil {
766                 t.Fatal(err)
767         }
768         if len(highlights) != len(locations) {
769                 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))
770         }
771         // Check to make sure highlights have a valid range.
772         var results []span.Span
773         for i := range highlights {
774                 h, err := m.RangeSpan(highlights[i].Range)
775                 if err != nil {
776                         t.Fatalf("failed for %v: %v", highlights[i], err)
777                 }
778                 results = append(results, h)
779         }
780         // Sort results to make tests deterministic since DocumentHighlight uses a map.
781         sort.SliceStable(results, func(i, j int) bool {
782                 return span.Compare(results[i], results[j]) == -1
783         })
784         // Check to make sure all the expected highlights are found.
785         for i := range results {
786                 if results[i] != locations[i] {
787                         t.Errorf("want %v, got %v\n", locations[i], results[i])
788                 }
789         }
790 }
791
792 func (r *runner) References(t *testing.T, src span.Span, itemList []span.Span) {
793         sm, err := r.data.Mapper(src.URI())
794         if err != nil {
795                 t.Fatal(err)
796         }
797         loc, err := sm.Location(src)
798         if err != nil {
799                 t.Fatalf("failed for %v: %v", src, err)
800         }
801         for _, includeDeclaration := range []bool{true, false} {
802                 t.Run(fmt.Sprintf("refs-declaration-%v", includeDeclaration), func(t *testing.T) {
803                         want := make(map[protocol.Location]bool)
804                         for i, pos := range itemList {
805                                 // We don't want the first result if we aren't including the declaration.
806                                 if i == 0 && !includeDeclaration {
807                                         continue
808                                 }
809                                 m, err := r.data.Mapper(pos.URI())
810                                 if err != nil {
811                                         t.Fatal(err)
812                                 }
813                                 loc, err := m.Location(pos)
814                                 if err != nil {
815                                         t.Fatalf("failed for %v: %v", src, err)
816                                 }
817                                 want[loc] = true
818                         }
819                         params := &protocol.ReferenceParams{
820                                 TextDocumentPositionParams: protocol.TextDocumentPositionParams{
821                                         TextDocument: protocol.TextDocumentIdentifier{URI: loc.URI},
822                                         Position:     loc.Range.Start,
823                                 },
824                                 Context: protocol.ReferenceContext{
825                                         IncludeDeclaration: includeDeclaration,
826                                 },
827                         }
828                         got, err := r.server.References(r.ctx, params)
829                         if err != nil {
830                                 t.Fatalf("failed for %v: %v", src, err)
831                         }
832                         if len(got) != len(want) {
833                                 t.Errorf("references failed: different lengths got %v want %v", len(got), len(want))
834                         }
835                         for _, loc := range got {
836                                 if !want[loc] {
837                                         t.Errorf("references failed: incorrect references got %v want %v", loc, want)
838                                 }
839                         }
840                 })
841         }
842 }
843
844 func (r *runner) Rename(t *testing.T, spn span.Span, newText string) {
845         tag := fmt.Sprintf("%s-rename", newText)
846
847         uri := spn.URI()
848         filename := uri.Filename()
849         sm, err := r.data.Mapper(uri)
850         if err != nil {
851                 t.Fatal(err)
852         }
853         loc, err := sm.Location(spn)
854         if err != nil {
855                 t.Fatalf("failed for %v: %v", spn, err)
856         }
857
858         wedit, err := r.server.Rename(r.ctx, &protocol.RenameParams{
859                 TextDocument: protocol.TextDocumentIdentifier{
860                         URI: protocol.URIFromSpanURI(uri),
861                 },
862                 Position: loc.Range.Start,
863                 NewName:  newText,
864         })
865         if err != nil {
866                 renamed := string(r.data.Golden(tag, filename, func() ([]byte, error) {
867                         return []byte(err.Error()), nil
868                 }))
869                 if err.Error() != renamed {
870                         t.Errorf("rename failed for %s, expected:\n%v\ngot:\n%v\n", newText, renamed, err)
871                 }
872                 return
873         }
874         res, err := applyTextDocumentEdits(r, wedit.DocumentChanges)
875         if err != nil {
876                 t.Fatal(err)
877         }
878         var orderedURIs []string
879         for uri := range res {
880                 orderedURIs = append(orderedURIs, string(uri))
881         }
882         sort.Strings(orderedURIs)
883
884         var got string
885         for i := 0; i < len(res); i++ {
886                 if i != 0 {
887                         got += "\n"
888                 }
889                 uri := span.URIFromURI(orderedURIs[i])
890                 if len(res) > 1 {
891                         got += filepath.Base(uri.Filename()) + ":\n"
892                 }
893                 val := res[uri]
894                 got += val
895         }
896         want := string(r.data.Golden(tag, filename, func() ([]byte, error) {
897                 return []byte(got), nil
898         }))
899         if want != got {
900                 t.Errorf("rename failed for %s:\n%s", newText, tests.Diff(want, got))
901         }
902 }
903
904 func (r *runner) PrepareRename(t *testing.T, src span.Span, want *source.PrepareItem) {
905         m, err := r.data.Mapper(src.URI())
906         if err != nil {
907                 t.Fatal(err)
908         }
909         loc, err := m.Location(src)
910         if err != nil {
911                 t.Fatalf("failed for %v: %v", src, err)
912         }
913         tdpp := protocol.TextDocumentPositionParams{
914                 TextDocument: protocol.TextDocumentIdentifier{URI: loc.URI},
915                 Position:     loc.Range.Start,
916         }
917         params := &protocol.PrepareRenameParams{
918                 TextDocumentPositionParams: tdpp,
919         }
920         got, err := r.server.PrepareRename(context.Background(), params)
921         if err != nil {
922                 t.Errorf("prepare rename failed for %v: got error: %v", src, err)
923                 return
924         }
925         // we all love typed nils
926         if got == nil {
927                 if want.Text != "" { // expected an ident.
928                         t.Errorf("prepare rename failed for %v: got nil", src)
929                 }
930                 return
931         }
932         if got.Start == got.End {
933                 // Special case for 0-length ranges. Marks can't specify a 0-length range,
934                 // so just compare the start.
935                 if got.Start != want.Range.Start {
936                         t.Errorf("prepare rename failed: incorrect point, got %v want %v", got.Start, want.Range.Start)
937                 }
938         } else {
939                 if protocol.CompareRange(*got, want.Range) != 0 {
940                         t.Errorf("prepare rename failed: incorrect range got %v want %v", *got, want.Range)
941                 }
942         }
943 }
944
945 func applyTextDocumentEdits(r *runner, edits []protocol.TextDocumentEdit) (map[span.URI]string, error) {
946         res := map[span.URI]string{}
947         for _, docEdits := range edits {
948                 uri := docEdits.TextDocument.URI.SpanURI()
949                 var m *protocol.ColumnMapper
950                 // If we have already edited this file, we use the edited version (rather than the
951                 // file in its original state) so that we preserve our initial changes.
952                 if content, ok := res[uri]; ok {
953                         m = &protocol.ColumnMapper{
954                                 URI: uri,
955                                 Converter: span.NewContentConverter(
956                                         uri.Filename(), []byte(content)),
957                                 Content: []byte(content),
958                         }
959                 } else {
960                         var err error
961                         if m, err = r.data.Mapper(uri); err != nil {
962                                 return nil, err
963                         }
964                 }
965                 res[uri] = string(m.Content)
966                 sedits, err := source.FromProtocolEdits(m, docEdits.Edits)
967                 if err != nil {
968                         return nil, err
969                 }
970                 res[uri] = applyEdits(res[uri], sedits)
971         }
972         return res, nil
973 }
974
975 func applyEdits(contents string, edits []diff.TextEdit) string {
976         res := contents
977
978         // Apply the edits from the end of the file forward
979         // to preserve the offsets
980         for i := len(edits) - 1; i >= 0; i-- {
981                 edit := edits[i]
982                 start := edit.Span.Start().Offset()
983                 end := edit.Span.End().Offset()
984                 tmp := res[0:start] + edit.NewText
985                 res = tmp + res[end:]
986         }
987         return res
988 }
989
990 func (r *runner) Symbols(t *testing.T, uri span.URI, expectedSymbols []protocol.DocumentSymbol) {
991         params := &protocol.DocumentSymbolParams{
992                 TextDocument: protocol.TextDocumentIdentifier{
993                         URI: protocol.URIFromSpanURI(uri),
994                 },
995         }
996         got, err := r.server.DocumentSymbol(r.ctx, params)
997         if err != nil {
998                 t.Fatal(err)
999         }
1000         if len(got) != len(expectedSymbols) {
1001                 t.Errorf("want %d top-level symbols in %v, got %d", len(expectedSymbols), uri, len(got))
1002                 return
1003         }
1004         symbols := make([]protocol.DocumentSymbol, len(got))
1005         for i, s := range got {
1006                 s, ok := s.(protocol.DocumentSymbol)
1007                 if !ok {
1008                         t.Fatalf("%v: wanted []DocumentSymbols but got %v", uri, got)
1009                 }
1010                 symbols[i] = s
1011         }
1012         if diff := tests.DiffSymbols(t, uri, expectedSymbols, symbols); diff != "" {
1013                 t.Error(diff)
1014         }
1015 }
1016
1017 func (r *runner) WorkspaceSymbols(t *testing.T, query string, expectedSymbols []protocol.SymbolInformation, dirs map[string]struct{}) {
1018         r.callWorkspaceSymbols(t, query, source.SymbolCaseInsensitive, dirs, expectedSymbols)
1019 }
1020
1021 func (r *runner) FuzzyWorkspaceSymbols(t *testing.T, query string, expectedSymbols []protocol.SymbolInformation, dirs map[string]struct{}) {
1022         r.callWorkspaceSymbols(t, query, source.SymbolFuzzy, dirs, expectedSymbols)
1023 }
1024
1025 func (r *runner) CaseSensitiveWorkspaceSymbols(t *testing.T, query string, expectedSymbols []protocol.SymbolInformation, dirs map[string]struct{}) {
1026         r.callWorkspaceSymbols(t, query, source.SymbolCaseSensitive, dirs, expectedSymbols)
1027 }
1028
1029 func (r *runner) callWorkspaceSymbols(t *testing.T, query string, matcher source.SymbolMatcher, dirs map[string]struct{}, expectedSymbols []protocol.SymbolInformation) {
1030         t.Helper()
1031
1032         original := r.server.session.Options()
1033         modified := original
1034         modified.SymbolMatcher = matcher
1035         r.server.session.SetOptions(modified)
1036         defer r.server.session.SetOptions(original)
1037
1038         params := &protocol.WorkspaceSymbolParams{
1039                 Query: query,
1040         }
1041         got, err := r.server.Symbol(r.ctx, params)
1042         if err != nil {
1043                 t.Fatal(err)
1044         }
1045         got = tests.FilterWorkspaceSymbols(got, dirs)
1046         if diff := tests.DiffWorkspaceSymbols(expectedSymbols, got); diff != "" {
1047                 t.Error(diff)
1048         }
1049 }
1050
1051 func (r *runner) SignatureHelp(t *testing.T, spn span.Span, want *protocol.SignatureHelp) {
1052         m, err := r.data.Mapper(spn.URI())
1053         if err != nil {
1054                 t.Fatal(err)
1055         }
1056         loc, err := m.Location(spn)
1057         if err != nil {
1058                 t.Fatalf("failed for %v: %v", loc, err)
1059         }
1060         tdpp := protocol.TextDocumentPositionParams{
1061                 TextDocument: protocol.TextDocumentIdentifier{
1062                         URI: protocol.URIFromSpanURI(spn.URI()),
1063                 },
1064                 Position: loc.Range.Start,
1065         }
1066         params := &protocol.SignatureHelpParams{
1067                 TextDocumentPositionParams: tdpp,
1068         }
1069         got, err := r.server.SignatureHelp(r.ctx, params)
1070         if err != nil {
1071                 // Only fail if we got an error we did not expect.
1072                 if want != nil {
1073                         t.Fatal(err)
1074                 }
1075                 return
1076         }
1077         if want == nil {
1078                 if got != nil {
1079                         t.Errorf("expected no signature, got %v", got)
1080                 }
1081                 return
1082         }
1083         if got == nil {
1084                 t.Fatalf("expected %v, got nil", want)
1085         }
1086         if diff := tests.DiffSignatures(spn, want, got); diff != "" {
1087                 t.Error(diff)
1088         }
1089 }
1090
1091 func (r *runner) Link(t *testing.T, uri span.URI, wantLinks []tests.Link) {
1092         m, err := r.data.Mapper(uri)
1093         if err != nil {
1094                 t.Fatal(err)
1095         }
1096         got, err := r.server.DocumentLink(r.ctx, &protocol.DocumentLinkParams{
1097                 TextDocument: protocol.TextDocumentIdentifier{
1098                         URI: protocol.URIFromSpanURI(uri),
1099                 },
1100         })
1101         if err != nil {
1102                 t.Fatal(err)
1103         }
1104         if diff := tests.DiffLinks(m, wantLinks, got); diff != "" {
1105                 t.Error(diff)
1106         }
1107 }
1108
1109 func TestBytesOffset(t *testing.T) {
1110         tests := []struct {
1111                 text string
1112                 pos  protocol.Position
1113                 want int
1114         }{
1115                 {text: `a𐐀b`, pos: protocol.Position{Line: 0, Character: 0}, want: 0},
1116                 {text: `a𐐀b`, pos: protocol.Position{Line: 0, Character: 1}, want: 1},
1117                 {text: `a𐐀b`, pos: protocol.Position{Line: 0, Character: 2}, want: 1},
1118                 {text: `a𐐀b`, pos: protocol.Position{Line: 0, Character: 3}, want: 5},
1119                 {text: `a𐐀b`, pos: protocol.Position{Line: 0, Character: 4}, want: 6},
1120                 {text: `a𐐀b`, pos: protocol.Position{Line: 0, Character: 5}, want: -1},
1121                 {text: "aaa\nbbb\n", pos: protocol.Position{Line: 0, Character: 3}, want: 3},
1122                 {text: "aaa\nbbb\n", pos: protocol.Position{Line: 0, Character: 4}, want: 3},
1123                 {text: "aaa\nbbb\n", pos: protocol.Position{Line: 1, Character: 0}, want: 4},
1124                 {text: "aaa\nbbb\n", pos: protocol.Position{Line: 1, Character: 3}, want: 7},
1125                 {text: "aaa\nbbb\n", pos: protocol.Position{Line: 1, Character: 4}, want: 7},
1126                 {text: "aaa\nbbb\n", pos: protocol.Position{Line: 2, Character: 0}, want: 8},
1127                 {text: "aaa\nbbb\n", pos: protocol.Position{Line: 2, Character: 1}, want: -1},
1128                 {text: "aaa\nbbb\n\n", pos: protocol.Position{Line: 2, Character: 0}, want: 8},
1129         }
1130
1131         for i, test := range tests {
1132                 fname := fmt.Sprintf("test %d", i)
1133                 fset := token.NewFileSet()
1134                 f := fset.AddFile(fname, -1, len(test.text))
1135                 f.SetLinesForContent([]byte(test.text))
1136                 uri := span.URIFromPath(fname)
1137                 converter := span.NewContentConverter(fname, []byte(test.text))
1138                 mapper := &protocol.ColumnMapper{
1139                         URI:       uri,
1140                         Converter: converter,
1141                         Content:   []byte(test.text),
1142                 }
1143                 got, err := mapper.Point(test.pos)
1144                 if err != nil && test.want != -1 {
1145                         t.Errorf("unexpected error: %v", err)
1146                 }
1147                 if err == nil && got.Offset() != test.want {
1148                         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())
1149                 }
1150         }
1151 }
1152
1153 func (r *runner) collectDiagnostics(view source.View) {
1154         if r.diagnostics != nil {
1155                 return
1156         }
1157         r.diagnostics = make(map[span.URI][]*source.Diagnostic)
1158
1159         snapshot, release := view.Snapshot(r.ctx)
1160         defer release()
1161
1162         // Always run diagnostics with analysis.
1163         reports, _ := r.server.diagnose(r.ctx, snapshot, true)
1164         r.server.publishReports(r.ctx, snapshot, reports)
1165         for uri, sent := range r.server.delivered {
1166                 var diagnostics []*source.Diagnostic
1167                 for _, d := range sent.sorted {
1168                         diagnostics = append(diagnostics, &source.Diagnostic{
1169                                 Range:    d.Range,
1170                                 Message:  d.Message,
1171                                 Related:  d.Related,
1172                                 Severity: d.Severity,
1173                                 Source:   d.Source,
1174                                 Tags:     d.Tags,
1175                         })
1176                 }
1177                 r.diagnostics[uri] = diagnostics
1178         }
1179 }