Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / internal / lsp / source / source_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 source_test
6
7 import (
8         "context"
9         "fmt"
10         "os"
11         "os/exec"
12         "path/filepath"
13         "sort"
14         "strings"
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/fuzzy"
21         "golang.org/x/tools/internal/lsp/protocol"
22         "golang.org/x/tools/internal/lsp/source"
23         "golang.org/x/tools/internal/lsp/source/completion"
24         "golang.org/x/tools/internal/lsp/tests"
25         "golang.org/x/tools/internal/span"
26         "golang.org/x/tools/internal/testenv"
27         errors "golang.org/x/xerrors"
28 )
29
30 func TestMain(m *testing.M) {
31         testenv.ExitIfSmallMachine()
32         os.Exit(m.Run())
33 }
34
35 func TestSource(t *testing.T) {
36         tests.RunTests(t, "../testdata", true, testSource)
37 }
38
39 type runner struct {
40         snapshot source.Snapshot
41         view     source.View
42         data     *tests.Data
43         ctx      context.Context
44 }
45
46 func testSource(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         options.SetEnvSlice(datum.Config.Env)
54         view, _, release, err := session.NewView(ctx, "source_test", span.URIFromPath(datum.Config.Dir), "", options)
55         release()
56         if err != nil {
57                 t.Fatal(err)
58         }
59         defer view.Shutdown(ctx)
60
61         // Enable type error analyses for tests.
62         // TODO(golang/go#38212): Delete this once they are enabled by default.
63         tests.EnableAllAnalyzers(view, options)
64         view.SetOptions(ctx, options)
65         var modifications []source.FileModification
66         for filename, content := range datum.Config.Overlay {
67                 kind := source.DetectLanguage("", filename)
68                 if kind != source.Go {
69                         continue
70                 }
71                 modifications = append(modifications, source.FileModification{
72                         URI:        span.URIFromPath(filename),
73                         Action:     source.Open,
74                         Version:    -1,
75                         Text:       content,
76                         LanguageID: "go",
77                 })
78         }
79         if err := session.ModifyFiles(ctx, modifications); err != nil {
80                 t.Fatal(err)
81         }
82         snapshot, release := view.Snapshot(ctx)
83         defer release()
84         r := &runner{
85                 view:     view,
86                 snapshot: snapshot,
87                 data:     datum,
88                 ctx:      ctx,
89         }
90         tests.Run(t, r, datum)
91 }
92
93 func (r *runner) CallHierarchy(t *testing.T, spn span.Span, expectedCalls *tests.CallHierarchyResult) {
94         mapper, err := r.data.Mapper(spn.URI())
95         if err != nil {
96                 t.Fatal(err)
97         }
98         loc, err := mapper.Location(spn)
99         if err != nil {
100                 t.Fatalf("failed for %v: %v", spn, err)
101         }
102         fh, err := r.snapshot.GetFile(r.ctx, spn.URI())
103         if err != nil {
104                 t.Fatal(err)
105         }
106
107         items, err := source.PrepareCallHierarchy(r.ctx, r.snapshot, fh, loc.Range.Start)
108         if err != nil {
109                 t.Fatal(err)
110         }
111         if len(items) == 0 {
112                 t.Fatalf("expected call hierarchy item to be returned for identifier at %v\n", loc.Range)
113         }
114
115         callLocation := protocol.Location{
116                 URI:   items[0].URI,
117                 Range: items[0].Range,
118         }
119         if callLocation != loc {
120                 t.Fatalf("expected source.PrepareCallHierarchy to return identifier at %v but got %v\n", loc, callLocation)
121         }
122
123         incomingCalls, err := source.IncomingCalls(r.ctx, r.snapshot, fh, loc.Range.Start)
124         if err != nil {
125                 t.Error(err)
126         }
127         var incomingCallItems []protocol.CallHierarchyItem
128         for _, item := range incomingCalls {
129                 incomingCallItems = append(incomingCallItems, item.From)
130         }
131         msg := tests.DiffCallHierarchyItems(incomingCallItems, expectedCalls.IncomingCalls)
132         if msg != "" {
133                 t.Error(fmt.Sprintf("incoming calls differ: %s", msg))
134         }
135
136         outgoingCalls, err := source.OutgoingCalls(r.ctx, r.snapshot, fh, loc.Range.Start)
137         if err != nil {
138                 t.Error(err)
139         }
140         var outgoingCallItems []protocol.CallHierarchyItem
141         for _, item := range outgoingCalls {
142                 outgoingCallItems = append(outgoingCallItems, item.To)
143         }
144         msg = tests.DiffCallHierarchyItems(outgoingCallItems, expectedCalls.OutgoingCalls)
145         if msg != "" {
146                 t.Error(fmt.Sprintf("outgoing calls differ: %s", msg))
147         }
148 }
149
150 func (r *runner) Diagnostics(t *testing.T, uri span.URI, want []*source.Diagnostic) {
151         fileID, got, err := source.FileDiagnostics(r.ctx, r.snapshot, uri)
152         if err != nil {
153                 t.Fatal(err)
154         }
155         // A special case to test that there are no diagnostics for a file.
156         if len(want) == 1 && want[0].Source == "no_diagnostics" {
157                 if len(got) != 0 {
158                         t.Errorf("expected no diagnostics for %s, got %v", uri, got)
159                 }
160                 return
161         }
162         if diff := tests.DiffDiagnostics(fileID.URI, want, got); diff != "" {
163                 t.Error(diff)
164         }
165 }
166
167 func (r *runner) Completion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
168         var want []protocol.CompletionItem
169         for _, pos := range test.CompletionItems {
170                 want = append(want, tests.ToProtocolCompletionItem(*items[pos]))
171         }
172         _, got := r.callCompletion(t, src, func(opts *source.Options) {
173                 opts.Matcher = source.CaseInsensitive
174                 opts.DeepCompletion = false
175                 opts.CompleteUnimported = false
176                 opts.InsertTextFormat = protocol.SnippetTextFormat
177                 if !strings.Contains(string(src.URI()), "literal") {
178                         opts.LiteralCompletions = false
179                 }
180         })
181         got = tests.FilterBuiltins(src, got)
182         if diff := tests.DiffCompletionItems(want, got); diff != "" {
183                 t.Errorf("%s: %s", src, diff)
184         }
185 }
186
187 func (r *runner) CompletionSnippet(t *testing.T, src span.Span, expected tests.CompletionSnippet, placeholders bool, items tests.CompletionItems) {
188         _, list := r.callCompletion(t, src, func(opts *source.Options) {
189                 opts.UsePlaceholders = placeholders
190                 opts.DeepCompletion = true
191                 opts.CompleteUnimported = false
192         })
193         got := tests.FindItem(list, *items[expected.CompletionItem])
194         want := expected.PlainSnippet
195         if placeholders {
196                 want = expected.PlaceholderSnippet
197         }
198         if diff := tests.DiffSnippets(want, got); diff != "" {
199                 t.Errorf("%s: %s", src, diff)
200         }
201 }
202
203 func (r *runner) UnimportedCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
204         var want []protocol.CompletionItem
205         for _, pos := range test.CompletionItems {
206                 want = append(want, tests.ToProtocolCompletionItem(*items[pos]))
207         }
208         _, got := r.callCompletion(t, src, func(opts *source.Options) {})
209         got = tests.FilterBuiltins(src, got)
210         if diff := tests.CheckCompletionOrder(want, got, false); diff != "" {
211                 t.Errorf("%s: %s", src, diff)
212         }
213 }
214
215 func (r *runner) DeepCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
216         var want []protocol.CompletionItem
217         for _, pos := range test.CompletionItems {
218                 want = append(want, tests.ToProtocolCompletionItem(*items[pos]))
219         }
220         prefix, list := r.callCompletion(t, src, func(opts *source.Options) {
221                 opts.DeepCompletion = true
222                 opts.Matcher = source.CaseInsensitive
223                 opts.CompleteUnimported = false
224         })
225         list = tests.FilterBuiltins(src, list)
226         fuzzyMatcher := fuzzy.NewMatcher(prefix)
227         var got []protocol.CompletionItem
228         for _, item := range list {
229                 if fuzzyMatcher.Score(item.Label) <= 0 {
230                         continue
231                 }
232                 got = append(got, item)
233         }
234         if msg := tests.DiffCompletionItems(want, got); msg != "" {
235                 t.Errorf("%s: %s", src, msg)
236         }
237 }
238
239 func (r *runner) FuzzyCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
240         var want []protocol.CompletionItem
241         for _, pos := range test.CompletionItems {
242                 want = append(want, tests.ToProtocolCompletionItem(*items[pos]))
243         }
244         _, got := r.callCompletion(t, src, func(opts *source.Options) {
245                 opts.DeepCompletion = true
246                 opts.Matcher = source.Fuzzy
247                 opts.CompleteUnimported = false
248         })
249         got = tests.FilterBuiltins(src, got)
250         if msg := tests.DiffCompletionItems(want, got); msg != "" {
251                 t.Errorf("%s: %s", src, msg)
252         }
253 }
254
255 func (r *runner) CaseSensitiveCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
256         var want []protocol.CompletionItem
257         for _, pos := range test.CompletionItems {
258                 want = append(want, tests.ToProtocolCompletionItem(*items[pos]))
259         }
260         _, list := r.callCompletion(t, src, func(opts *source.Options) {
261                 opts.Matcher = source.CaseSensitive
262                 opts.CompleteUnimported = false
263         })
264         list = tests.FilterBuiltins(src, list)
265         if diff := tests.DiffCompletionItems(want, list); diff != "" {
266                 t.Errorf("%s: %s", src, diff)
267         }
268 }
269
270 func (r *runner) RankCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) {
271         var want []protocol.CompletionItem
272         for _, pos := range test.CompletionItems {
273                 want = append(want, tests.ToProtocolCompletionItem(*items[pos]))
274         }
275         _, got := r.callCompletion(t, src, func(opts *source.Options) {
276                 opts.DeepCompletion = true
277                 opts.Matcher = source.Fuzzy
278         })
279         if msg := tests.CheckCompletionOrder(want, got, true); msg != "" {
280                 t.Errorf("%s: %s", src, msg)
281         }
282 }
283
284 func (r *runner) callCompletion(t *testing.T, src span.Span, options func(*source.Options)) (string, []protocol.CompletionItem) {
285         fh, err := r.snapshot.GetFile(r.ctx, src.URI())
286         if err != nil {
287                 t.Fatal(err)
288         }
289         original := r.view.Options()
290         modified := original.Clone()
291         options(modified)
292         newView, err := r.view.SetOptions(r.ctx, modified)
293         if newView != r.view {
294                 t.Fatalf("options change unexpectedly created new view")
295         }
296         if err != nil {
297                 t.Fatal(err)
298         }
299         defer r.view.SetOptions(r.ctx, original)
300
301         list, surrounding, err := completion.Completion(r.ctx, r.snapshot, fh, protocol.Position{
302                 Line:      float64(src.Start().Line() - 1),
303                 Character: float64(src.Start().Column() - 1),
304         }, protocol.CompletionContext{})
305         if err != nil && !errors.As(err, &completion.ErrIsDefinition{}) {
306                 t.Fatalf("failed for %v: %v", src, err)
307         }
308         var prefix string
309         if surrounding != nil {
310                 prefix = strings.ToLower(surrounding.Prefix())
311         }
312
313         var numDeepCompletionsSeen int
314         var items []completion.CompletionItem
315         // Apply deep completion filtering.
316         for _, item := range list {
317                 if item.Depth > 0 {
318                         if !modified.DeepCompletion {
319                                 continue
320                         }
321                         if numDeepCompletionsSeen >= completion.MaxDeepCompletions {
322                                 continue
323                         }
324                         numDeepCompletionsSeen++
325                 }
326                 items = append(items, item)
327         }
328         return prefix, tests.ToProtocolCompletionItems(items)
329 }
330
331 func (r *runner) FoldingRanges(t *testing.T, spn span.Span) {
332         uri := spn.URI()
333
334         fh, err := r.snapshot.GetFile(r.ctx, spn.URI())
335         if err != nil {
336                 t.Fatal(err)
337         }
338         data, err := fh.Read()
339         if err != nil {
340                 t.Error(err)
341                 return
342         }
343
344         // Test all folding ranges.
345         ranges, err := source.FoldingRange(r.ctx, r.snapshot, fh, false)
346         if err != nil {
347                 t.Error(err)
348                 return
349         }
350         r.foldingRanges(t, "foldingRange", uri, string(data), ranges)
351
352         // Test folding ranges with lineFoldingOnly
353         ranges, err = source.FoldingRange(r.ctx, r.snapshot, fh, true)
354         if err != nil {
355                 t.Error(err)
356                 return
357         }
358         r.foldingRanges(t, "foldingRange-lineFolding", uri, string(data), ranges)
359 }
360
361 func (r *runner) foldingRanges(t *testing.T, prefix string, uri span.URI, data string, ranges []*source.FoldingRangeInfo) {
362         t.Helper()
363         // Fold all ranges.
364         nonOverlapping := nonOverlappingRanges(t, ranges)
365         for i, rngs := range nonOverlapping {
366                 got, err := foldRanges(string(data), rngs)
367                 if err != nil {
368                         t.Error(err)
369                         continue
370                 }
371                 tag := fmt.Sprintf("%s-%d", prefix, i)
372                 want := string(r.data.Golden(tag, uri.Filename(), func() ([]byte, error) {
373                         return []byte(got), nil
374                 }))
375
376                 if want != got {
377                         t.Errorf("%s: foldingRanges failed for %s, expected:\n%v\ngot:\n%v", tag, uri.Filename(), want, got)
378                 }
379         }
380
381         // Filter by kind.
382         kinds := []protocol.FoldingRangeKind{protocol.Imports, protocol.Comment}
383         for _, kind := range kinds {
384                 var kindOnly []*source.FoldingRangeInfo
385                 for _, fRng := range ranges {
386                         if fRng.Kind == kind {
387                                 kindOnly = append(kindOnly, fRng)
388                         }
389                 }
390
391                 nonOverlapping := nonOverlappingRanges(t, kindOnly)
392                 for i, rngs := range nonOverlapping {
393                         got, err := foldRanges(string(data), rngs)
394                         if err != nil {
395                                 t.Error(err)
396                                 continue
397                         }
398                         tag := fmt.Sprintf("%s-%s-%d", prefix, kind, i)
399                         want := string(r.data.Golden(tag, uri.Filename(), func() ([]byte, error) {
400                                 return []byte(got), nil
401                         }))
402
403                         if want != got {
404                                 t.Errorf("%s: failed for %s, expected:\n%v\ngot:\n%v", tag, uri.Filename(), want, got)
405                         }
406                 }
407
408         }
409 }
410
411 func nonOverlappingRanges(t *testing.T, ranges []*source.FoldingRangeInfo) (res [][]*source.FoldingRangeInfo) {
412         for _, fRng := range ranges {
413                 setNum := len(res)
414                 for i := 0; i < len(res); i++ {
415                         canInsert := true
416                         for _, rng := range res[i] {
417                                 if conflict(t, rng, fRng) {
418                                         canInsert = false
419                                         break
420                                 }
421                         }
422                         if canInsert {
423                                 setNum = i
424                                 break
425                         }
426                 }
427                 if setNum == len(res) {
428                         res = append(res, []*source.FoldingRangeInfo{})
429                 }
430                 res[setNum] = append(res[setNum], fRng)
431         }
432         return res
433 }
434
435 func conflict(t *testing.T, a, b *source.FoldingRangeInfo) bool {
436         arng, err := a.Range()
437         if err != nil {
438                 t.Fatal(err)
439         }
440         brng, err := b.Range()
441         if err != nil {
442                 t.Fatal(err)
443         }
444         // a start position is <= b start positions
445         return protocol.ComparePosition(arng.Start, brng.Start) <= 0 && protocol.ComparePosition(arng.End, brng.Start) > 0
446 }
447
448 func foldRanges(contents string, ranges []*source.FoldingRangeInfo) (string, error) {
449         foldedText := "<>"
450         res := contents
451         // Apply the folds from the end of the file forward
452         // to preserve the offsets.
453         for i := len(ranges) - 1; i >= 0; i-- {
454                 fRange := ranges[i]
455                 spn, err := fRange.Span()
456                 if err != nil {
457                         return "", err
458                 }
459                 start := spn.Start().Offset()
460                 end := spn.End().Offset()
461
462                 tmp := res[0:start] + foldedText
463                 res = tmp + res[end:]
464         }
465         return res, nil
466 }
467
468 func (r *runner) Format(t *testing.T, spn span.Span) {
469         gofmted := string(r.data.Golden("gofmt", spn.URI().Filename(), func() ([]byte, error) {
470                 cmd := exec.Command("gofmt", spn.URI().Filename())
471                 out, _ := cmd.Output() // ignore error, sometimes we have intentionally ungofmt-able files
472                 return out, nil
473         }))
474         fh, err := r.snapshot.GetFile(r.ctx, spn.URI())
475         if err != nil {
476                 t.Fatal(err)
477         }
478         edits, err := source.Format(r.ctx, r.snapshot, fh)
479         if err != nil {
480                 if gofmted != "" {
481                         t.Error(err)
482                 }
483                 return
484         }
485         data, err := fh.Read()
486         if err != nil {
487                 t.Fatal(err)
488         }
489         m, err := r.data.Mapper(spn.URI())
490         if err != nil {
491                 t.Fatal(err)
492         }
493         diffEdits, err := source.FromProtocolEdits(m, edits)
494         if err != nil {
495                 t.Error(err)
496         }
497         got := diff.ApplyEdits(string(data), diffEdits)
498         if gofmted != got {
499                 t.Errorf("format failed for %s, expected:\n%v\ngot:\n%v", spn.URI().Filename(), gofmted, got)
500         }
501 }
502
503 func (r *runner) SemanticTokens(t *testing.T, spn span.Span) {
504         t.Skip("nothing to test in source")
505 }
506
507 func (r *runner) Import(t *testing.T, spn span.Span) {
508         fh, err := r.snapshot.GetFile(r.ctx, spn.URI())
509         if err != nil {
510                 t.Fatal(err)
511         }
512         edits, _, err := source.AllImportsFixes(r.ctx, r.snapshot, fh)
513         if err != nil {
514                 t.Error(err)
515         }
516         data, err := fh.Read()
517         if err != nil {
518                 t.Fatal(err)
519         }
520         m, err := r.data.Mapper(fh.URI())
521         if err != nil {
522                 t.Fatal(err)
523         }
524         diffEdits, err := source.FromProtocolEdits(m, edits)
525         if err != nil {
526                 t.Error(err)
527         }
528         got := diff.ApplyEdits(string(data), diffEdits)
529         want := string(r.data.Golden("goimports", spn.URI().Filename(), func() ([]byte, error) {
530                 return []byte(got), nil
531         }))
532         if want != got {
533                 d := myers.ComputeEdits(spn.URI(), want, got)
534                 t.Errorf("import failed for %s: %s", spn.URI().Filename(), diff.ToUnified("want", "got", want, d))
535         }
536 }
537
538 func (r *runner) Definition(t *testing.T, spn span.Span, d tests.Definition) {
539         _, srcRng, err := spanToRange(r.data, d.Src)
540         if err != nil {
541                 t.Fatal(err)
542         }
543         fh, err := r.snapshot.GetFile(r.ctx, spn.URI())
544         if err != nil {
545                 t.Fatal(err)
546         }
547         ident, err := source.Identifier(r.ctx, r.snapshot, fh, srcRng.Start)
548         if err != nil {
549                 t.Fatalf("failed for %v: %v", d.Src, err)
550         }
551         h, err := source.HoverIdentifier(r.ctx, ident)
552         if err != nil {
553                 t.Fatalf("failed for %v: %v", d.Src, err)
554         }
555         hover, err := source.FormatHover(h, r.view.Options())
556         if err != nil {
557                 t.Fatal(err)
558         }
559         rng, err := ident.Declaration.MappedRange[0].Range()
560         if err != nil {
561                 t.Fatal(err)
562         }
563         if d.IsType {
564                 rng, err = ident.Type.Range()
565                 if err != nil {
566                         t.Fatal(err)
567                 }
568                 hover = ""
569         }
570         didSomething := false
571         if hover != "" {
572                 didSomething = true
573                 tag := fmt.Sprintf("%s-hover", d.Name)
574                 expectHover := string(r.data.Golden(tag, d.Src.URI().Filename(), func() ([]byte, error) {
575                         return []byte(hover), nil
576                 }))
577                 if hover != expectHover {
578                         t.Errorf("hover for %s failed:\n%s", d.Src, tests.Diff(expectHover, hover))
579                 }
580         }
581         if !d.OnlyHover {
582                 didSomething = true
583                 if _, defRng, err := spanToRange(r.data, d.Def); err != nil {
584                         t.Fatal(err)
585                 } else if rng != defRng {
586                         t.Errorf("for %v got %v want %v", d.Src, rng, defRng)
587                 }
588         }
589         if !didSomething {
590                 t.Errorf("no tests ran for %s", d.Src.URI())
591         }
592 }
593
594 func (r *runner) Implementation(t *testing.T, spn span.Span, impls []span.Span) {
595         sm, err := r.data.Mapper(spn.URI())
596         if err != nil {
597                 t.Fatal(err)
598         }
599         loc, err := sm.Location(spn)
600         if err != nil {
601                 t.Fatalf("failed for %v: %v", spn, err)
602         }
603         fh, err := r.snapshot.GetFile(r.ctx, spn.URI())
604         if err != nil {
605                 t.Fatal(err)
606         }
607         locs, err := source.Implementation(r.ctx, r.snapshot, fh, loc.Range.Start)
608         if err != nil {
609                 t.Fatalf("failed for %v: %v", spn, err)
610         }
611         if len(locs) != len(impls) {
612                 t.Fatalf("got %d locations for implementation, expected %d", len(locs), len(impls))
613         }
614         var results []span.Span
615         for i := range locs {
616                 locURI := locs[i].URI.SpanURI()
617                 lm, err := r.data.Mapper(locURI)
618                 if err != nil {
619                         t.Fatal(err)
620                 }
621                 imp, err := lm.Span(locs[i])
622                 if err != nil {
623                         t.Fatalf("failed for %v: %v", locs[i], err)
624                 }
625                 results = append(results, imp)
626         }
627         // Sort results and expected to make tests deterministic.
628         sort.SliceStable(results, func(i, j int) bool {
629                 return span.Compare(results[i], results[j]) == -1
630         })
631         sort.SliceStable(impls, func(i, j int) bool {
632                 return span.Compare(impls[i], impls[j]) == -1
633         })
634         for i := range results {
635                 if results[i] != impls[i] {
636                         t.Errorf("for %dth implementation of %v got %v want %v", i, spn, results[i], impls[i])
637                 }
638         }
639 }
640
641 func (r *runner) Highlight(t *testing.T, src span.Span, locations []span.Span) {
642         ctx := r.ctx
643         m, srcRng, err := spanToRange(r.data, src)
644         if err != nil {
645                 t.Fatal(err)
646         }
647         fh, err := r.snapshot.GetFile(r.ctx, src.URI())
648         if err != nil {
649                 t.Fatal(err)
650         }
651         highlights, err := source.Highlight(ctx, r.snapshot, fh, srcRng.Start)
652         if err != nil {
653                 t.Errorf("highlight failed for %s: %v", src.URI(), err)
654         }
655         if len(highlights) != len(locations) {
656                 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))
657         }
658         // Check to make sure highlights have a valid range.
659         var results []span.Span
660         for i := range highlights {
661                 h, err := m.RangeSpan(highlights[i])
662                 if err != nil {
663                         t.Fatalf("failed for %v: %v", highlights[i], err)
664                 }
665                 results = append(results, h)
666         }
667         // Sort results to make tests deterministic since DocumentHighlight uses a map.
668         sort.SliceStable(results, func(i, j int) bool {
669                 return span.Compare(results[i], results[j]) == -1
670         })
671         // Check to make sure all the expected highlights are found.
672         for i := range results {
673                 if results[i] != locations[i] {
674                         t.Errorf("want %v, got %v\n", locations[i], results[i])
675                 }
676         }
677 }
678
679 func (r *runner) References(t *testing.T, src span.Span, itemList []span.Span) {
680         ctx := r.ctx
681         _, srcRng, err := spanToRange(r.data, src)
682         if err != nil {
683                 t.Fatal(err)
684         }
685         snapshot := r.snapshot
686         fh, err := snapshot.GetFile(r.ctx, src.URI())
687         if err != nil {
688                 t.Fatal(err)
689         }
690         for _, includeDeclaration := range []bool{true, false} {
691                 t.Run(fmt.Sprintf("refs-declaration-%v", includeDeclaration), func(t *testing.T) {
692                         want := make(map[span.Span]bool)
693                         for i, pos := range itemList {
694                                 // We don't want the first result if we aren't including the declaration.
695                                 if i == 0 && !includeDeclaration {
696                                         continue
697                                 }
698                                 want[pos] = true
699                         }
700                         refs, err := source.References(ctx, snapshot, fh, srcRng.Start, includeDeclaration)
701                         if err != nil {
702                                 t.Fatalf("failed for %s: %v", src, err)
703                         }
704                         got := make(map[span.Span]bool)
705                         for _, refInfo := range refs {
706                                 refSpan, err := refInfo.Span()
707                                 if err != nil {
708                                         t.Fatal(err)
709                                 }
710                                 got[refSpan] = true
711                         }
712                         if len(got) != len(want) {
713                                 t.Errorf("references failed: different lengths got %v want %v", len(got), len(want))
714                         }
715                         for spn := range got {
716                                 if !want[spn] {
717                                         t.Errorf("references failed: incorrect references got %v want locations %v", got, want)
718                                 }
719                         }
720                 })
721         }
722 }
723
724 func (r *runner) Rename(t *testing.T, spn span.Span, newText string) {
725         tag := fmt.Sprintf("%s-rename", newText)
726
727         _, srcRng, err := spanToRange(r.data, spn)
728         if err != nil {
729                 t.Fatal(err)
730         }
731         fh, err := r.snapshot.GetFile(r.ctx, spn.URI())
732         if err != nil {
733                 t.Fatal(err)
734         }
735         changes, err := source.Rename(r.ctx, r.snapshot, fh, srcRng.Start, newText)
736         if err != nil {
737                 renamed := string(r.data.Golden(tag, spn.URI().Filename(), func() ([]byte, error) {
738                         return []byte(err.Error()), nil
739                 }))
740                 if err.Error() != renamed {
741                         t.Errorf("rename failed for %s, expected:\n%v\ngot:\n%v\n", newText, renamed, err)
742                 }
743                 return
744         }
745
746         var res []string
747         for editURI, edits := range changes {
748                 fh, err := r.snapshot.GetFile(r.ctx, editURI)
749                 if err != nil {
750                         t.Fatal(err)
751                 }
752                 data, err := fh.Read()
753                 if err != nil {
754                         t.Fatal(err)
755                 }
756                 m, err := r.data.Mapper(fh.URI())
757                 if err != nil {
758                         t.Fatal(err)
759                 }
760                 diffEdits, err := source.FromProtocolEdits(m, edits)
761                 if err != nil {
762                         t.Fatal(err)
763                 }
764                 contents := applyEdits(string(data), diffEdits)
765                 if len(changes) > 1 {
766                         filename := filepath.Base(editURI.Filename())
767                         contents = fmt.Sprintf("%s:\n%s", filename, contents)
768                 }
769                 res = append(res, contents)
770         }
771
772         // Sort on filename
773         sort.Strings(res)
774
775         var got string
776         for i, val := range res {
777                 if i != 0 {
778                         got += "\n"
779                 }
780                 got += val
781         }
782
783         renamed := string(r.data.Golden(tag, spn.URI().Filename(), func() ([]byte, error) {
784                 return []byte(got), nil
785         }))
786
787         if renamed != got {
788                 t.Errorf("rename failed for %s, expected:\n%v\ngot:\n%v", newText, renamed, got)
789         }
790 }
791
792 func applyEdits(contents string, edits []diff.TextEdit) string {
793         res := contents
794
795         // Apply the edits from the end of the file forward
796         // to preserve the offsets
797         for i := len(edits) - 1; i >= 0; i-- {
798                 edit := edits[i]
799                 start := edit.Span.Start().Offset()
800                 end := edit.Span.End().Offset()
801                 tmp := res[0:start] + edit.NewText
802                 res = tmp + res[end:]
803         }
804         return res
805 }
806
807 func (r *runner) PrepareRename(t *testing.T, src span.Span, want *source.PrepareItem) {
808         _, srcRng, err := spanToRange(r.data, src)
809         if err != nil {
810                 t.Fatal(err)
811         }
812         // Find the identifier at the position.
813         fh, err := r.snapshot.GetFile(r.ctx, src.URI())
814         if err != nil {
815                 t.Fatal(err)
816         }
817         item, err := source.PrepareRename(r.ctx, r.snapshot, fh, srcRng.Start)
818         if err != nil {
819                 if want.Text != "" { // expected an ident.
820                         t.Errorf("prepare rename failed for %v: got error: %v", src, err)
821                 }
822                 return
823         }
824         if item == nil {
825                 if want.Text != "" {
826                         t.Errorf("prepare rename failed for %v: got nil", src)
827                 }
828                 return
829         }
830         if want.Text == "" {
831                 t.Errorf("prepare rename failed for %v: expected nil, got %v", src, item)
832                 return
833         }
834         if item.Range.Start == item.Range.End {
835                 // Special case for 0-length ranges. Marks can't specify a 0-length range,
836                 // so just compare the start.
837                 if item.Range.Start != want.Range.Start {
838                         t.Errorf("prepare rename failed: incorrect point, got %v want %v", item.Range.Start, want.Range.Start)
839                 }
840         } else {
841                 if protocol.CompareRange(item.Range, want.Range) != 0 {
842                         t.Errorf("prepare rename failed: incorrect range got %v want %v", item.Range, want.Range)
843                 }
844         }
845 }
846
847 func (r *runner) Symbols(t *testing.T, uri span.URI, expectedSymbols []protocol.DocumentSymbol) {
848         fh, err := r.snapshot.GetFile(r.ctx, uri)
849         if err != nil {
850                 t.Fatal(err)
851         }
852         symbols, err := source.DocumentSymbols(r.ctx, r.snapshot, fh)
853         if err != nil {
854                 t.Errorf("symbols failed for %s: %v", uri, err)
855         }
856         if len(symbols) != len(expectedSymbols) {
857                 t.Errorf("want %d top-level symbols in %v, got %d", len(expectedSymbols), uri, len(symbols))
858                 return
859         }
860         if diff := tests.DiffSymbols(t, uri, expectedSymbols, symbols); diff != "" {
861                 t.Error(diff)
862         }
863 }
864
865 func (r *runner) WorkspaceSymbols(t *testing.T, query string, expectedSymbols []protocol.SymbolInformation, dirs map[string]struct{}) {
866         r.callWorkspaceSymbols(t, query, source.SymbolCaseInsensitive, dirs, expectedSymbols)
867 }
868
869 func (r *runner) FuzzyWorkspaceSymbols(t *testing.T, query string, expectedSymbols []protocol.SymbolInformation, dirs map[string]struct{}) {
870         r.callWorkspaceSymbols(t, query, source.SymbolFuzzy, dirs, expectedSymbols)
871 }
872
873 func (r *runner) CaseSensitiveWorkspaceSymbols(t *testing.T, query string, expectedSymbols []protocol.SymbolInformation, dirs map[string]struct{}) {
874         r.callWorkspaceSymbols(t, query, source.SymbolCaseSensitive, dirs, expectedSymbols)
875 }
876
877 func (r *runner) callWorkspaceSymbols(t *testing.T, query string, matcher source.SymbolMatcher, dirs map[string]struct{}, expectedSymbols []protocol.SymbolInformation) {
878         t.Helper()
879         got, err := source.WorkspaceSymbols(r.ctx, matcher, source.PackageQualifiedSymbols, []source.View{r.view}, query)
880         if err != nil {
881                 t.Fatal(err)
882         }
883         got = tests.FilterWorkspaceSymbols(got, dirs)
884         if diff := tests.DiffWorkspaceSymbols(expectedSymbols, got); diff != "" {
885                 t.Error(diff)
886         }
887 }
888
889 func (r *runner) SignatureHelp(t *testing.T, spn span.Span, want *protocol.SignatureHelp) {
890         _, rng, err := spanToRange(r.data, spn)
891         if err != nil {
892                 t.Fatal(err)
893         }
894         fh, err := r.snapshot.GetFile(r.ctx, spn.URI())
895         if err != nil {
896                 t.Fatal(err)
897         }
898         gotSignature, gotActiveParameter, err := source.SignatureHelp(r.ctx, r.snapshot, fh, rng.Start)
899         if err != nil {
900                 // Only fail if we got an error we did not expect.
901                 if want != nil {
902                         t.Fatalf("failed for %v: %v", spn, err)
903                 }
904                 return
905         }
906         if gotSignature == nil {
907                 if want != nil {
908                         t.Fatalf("got nil signature, but expected %v", want)
909                 }
910                 return
911         }
912         got := &protocol.SignatureHelp{
913                 Signatures:      []protocol.SignatureInformation{*gotSignature},
914                 ActiveParameter: float64(gotActiveParameter),
915         }
916         if diff := tests.DiffSignatures(spn, want, got); diff != "" {
917                 t.Error(diff)
918         }
919 }
920
921 // These are pure LSP features, no source level functionality to be tested.
922 func (r *runner) Link(t *testing.T, uri span.URI, wantLinks []tests.Link)         {}
923 func (r *runner) SuggestedFix(t *testing.T, spn span.Span, actionKinds []string)  {}
924 func (r *runner) FunctionExtraction(t *testing.T, start span.Span, end span.Span) {}
925 func (r *runner) CodeLens(t *testing.T, uri span.URI, want []protocol.CodeLens)   {}
926
927 func spanToRange(data *tests.Data, spn span.Span) (*protocol.ColumnMapper, protocol.Range, error) {
928         m, err := data.Mapper(spn.URI())
929         if err != nil {
930                 return nil, protocol.Range{}, err
931         }
932         srcRng, err := m.Range(spn)
933         if err != nil {
934                 return nil, protocol.Range{}, err
935         }
936         return m, srcRng, nil
937 }