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