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 / tests / tests.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/internal/lsp/tests/tests.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/internal/lsp/tests/tests.go
new file mode 100644 (file)
index 0000000..3d173b4
--- /dev/null
@@ -0,0 +1,1381 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package tests exports functionality to be used across a variety of gopls tests.
+package tests
+
+import (
+       "bytes"
+       "context"
+       "flag"
+       "fmt"
+       "go/ast"
+       "go/token"
+       "io/ioutil"
+       "os"
+       "path/filepath"
+       "regexp"
+       "sort"
+       "strconv"
+       "strings"
+       "sync"
+       "testing"
+       "time"
+
+       "golang.org/x/tools/go/expect"
+       "golang.org/x/tools/go/packages"
+       "golang.org/x/tools/go/packages/packagestest"
+       "golang.org/x/tools/internal/lsp/protocol"
+       "golang.org/x/tools/internal/lsp/source"
+       "golang.org/x/tools/internal/lsp/source/completion"
+       "golang.org/x/tools/internal/span"
+       "golang.org/x/tools/internal/testenv"
+       "golang.org/x/tools/txtar"
+)
+
+const (
+       overlayFileSuffix = ".overlay"
+       goldenFileSuffix  = ".golden"
+       inFileSuffix      = ".in"
+       summaryFile       = "summary.txt"
+       testModule        = "golang.org/x/tools/internal/lsp"
+)
+
+var UpdateGolden = flag.Bool("golden", false, "Update golden files")
+
+type CallHierarchy map[span.Span]*CallHierarchyResult
+type CodeLens map[span.URI][]protocol.CodeLens
+type Diagnostics map[span.URI][]*source.Diagnostic
+type CompletionItems map[token.Pos]*completion.CompletionItem
+type Completions map[span.Span][]Completion
+type CompletionSnippets map[span.Span][]CompletionSnippet
+type UnimportedCompletions map[span.Span][]Completion
+type DeepCompletions map[span.Span][]Completion
+type FuzzyCompletions map[span.Span][]Completion
+type CaseSensitiveCompletions map[span.Span][]Completion
+type RankCompletions map[span.Span][]Completion
+type FoldingRanges []span.Span
+type Formats []span.Span
+type Imports []span.Span
+type SemanticTokens []span.Span
+type SuggestedFixes map[span.Span][]string
+type FunctionExtractions map[span.Span]span.Span
+type Definitions map[span.Span]Definition
+type Implementations map[span.Span][]span.Span
+type Highlights map[span.Span][]span.Span
+type References map[span.Span][]span.Span
+type Renames map[span.Span]string
+type PrepareRenames map[span.Span]*source.PrepareItem
+type Symbols map[span.URI][]protocol.DocumentSymbol
+type SymbolsChildren map[string][]protocol.DocumentSymbol
+type SymbolInformation map[span.Span]protocol.SymbolInformation
+type WorkspaceSymbols map[string][]protocol.SymbolInformation
+type Signatures map[span.Span]*protocol.SignatureHelp
+type Links map[span.URI][]Link
+
+type Data struct {
+       Config                        packages.Config
+       Exported                      *packagestest.Exported
+       CallHierarchy                 CallHierarchy
+       CodeLens                      CodeLens
+       Diagnostics                   Diagnostics
+       CompletionItems               CompletionItems
+       Completions                   Completions
+       CompletionSnippets            CompletionSnippets
+       UnimportedCompletions         UnimportedCompletions
+       DeepCompletions               DeepCompletions
+       FuzzyCompletions              FuzzyCompletions
+       CaseSensitiveCompletions      CaseSensitiveCompletions
+       RankCompletions               RankCompletions
+       FoldingRanges                 FoldingRanges
+       Formats                       Formats
+       Imports                       Imports
+       SemanticTokens                SemanticTokens
+       SuggestedFixes                SuggestedFixes
+       FunctionExtractions           FunctionExtractions
+       Definitions                   Definitions
+       Implementations               Implementations
+       Highlights                    Highlights
+       References                    References
+       Renames                       Renames
+       PrepareRenames                PrepareRenames
+       Symbols                       Symbols
+       symbolsChildren               SymbolsChildren
+       symbolInformation             SymbolInformation
+       WorkspaceSymbols              WorkspaceSymbols
+       FuzzyWorkspaceSymbols         WorkspaceSymbols
+       CaseSensitiveWorkspaceSymbols WorkspaceSymbols
+       Signatures                    Signatures
+       Links                         Links
+
+       t         testing.TB
+       fragments map[string]string
+       dir       string
+       golden    map[string]*Golden
+       mode      string
+
+       ModfileFlagAvailable bool
+
+       mappersMu sync.Mutex
+       mappers   map[span.URI]*protocol.ColumnMapper
+}
+
+type Tests interface {
+       CallHierarchy(*testing.T, span.Span, *CallHierarchyResult)
+       CodeLens(*testing.T, span.URI, []protocol.CodeLens)
+       Diagnostics(*testing.T, span.URI, []*source.Diagnostic)
+       Completion(*testing.T, span.Span, Completion, CompletionItems)
+       CompletionSnippet(*testing.T, span.Span, CompletionSnippet, bool, CompletionItems)
+       UnimportedCompletion(*testing.T, span.Span, Completion, CompletionItems)
+       DeepCompletion(*testing.T, span.Span, Completion, CompletionItems)
+       FuzzyCompletion(*testing.T, span.Span, Completion, CompletionItems)
+       CaseSensitiveCompletion(*testing.T, span.Span, Completion, CompletionItems)
+       RankCompletion(*testing.T, span.Span, Completion, CompletionItems)
+       FoldingRanges(*testing.T, span.Span)
+       Format(*testing.T, span.Span)
+       Import(*testing.T, span.Span)
+       SemanticTokens(*testing.T, span.Span)
+       SuggestedFix(*testing.T, span.Span, []string)
+       FunctionExtraction(*testing.T, span.Span, span.Span)
+       Definition(*testing.T, span.Span, Definition)
+       Implementation(*testing.T, span.Span, []span.Span)
+       Highlight(*testing.T, span.Span, []span.Span)
+       References(*testing.T, span.Span, []span.Span)
+       Rename(*testing.T, span.Span, string)
+       PrepareRename(*testing.T, span.Span, *source.PrepareItem)
+       Symbols(*testing.T, span.URI, []protocol.DocumentSymbol)
+       WorkspaceSymbols(*testing.T, string, []protocol.SymbolInformation, map[string]struct{})
+       FuzzyWorkspaceSymbols(*testing.T, string, []protocol.SymbolInformation, map[string]struct{})
+       CaseSensitiveWorkspaceSymbols(*testing.T, string, []protocol.SymbolInformation, map[string]struct{})
+       SignatureHelp(*testing.T, span.Span, *protocol.SignatureHelp)
+       Link(*testing.T, span.URI, []Link)
+}
+
+type Definition struct {
+       Name      string
+       IsType    bool
+       OnlyHover bool
+       Src, Def  span.Span
+}
+
+type CompletionTestType int
+
+const (
+       // Default runs the standard completion tests.
+       CompletionDefault = CompletionTestType(iota)
+
+       // Unimported tests the autocompletion of unimported packages.
+       CompletionUnimported
+
+       // Deep tests deep completion.
+       CompletionDeep
+
+       // Fuzzy tests deep completion and fuzzy matching.
+       CompletionFuzzy
+
+       // CaseSensitive tests case sensitive completion.
+       CompletionCaseSensitive
+
+       // CompletionRank candidates in test must be valid and in the right relative order.
+       CompletionRank
+)
+
+type WorkspaceSymbolsTestType int
+
+const (
+       // Default runs the standard workspace symbols tests.
+       WorkspaceSymbolsDefault = WorkspaceSymbolsTestType(iota)
+
+       // Fuzzy tests workspace symbols with fuzzy matching.
+       WorkspaceSymbolsFuzzy
+
+       // CaseSensitive tests workspace symbols with case sensitive.
+       WorkspaceSymbolsCaseSensitive
+)
+
+type Completion struct {
+       CompletionItems []token.Pos
+}
+
+type CompletionSnippet struct {
+       CompletionItem     token.Pos
+       PlainSnippet       string
+       PlaceholderSnippet string
+}
+
+type CallHierarchyResult struct {
+       IncomingCalls, OutgoingCalls []protocol.CallHierarchyItem
+}
+
+type Link struct {
+       Src          span.Span
+       Target       string
+       NotePosition token.Position
+}
+
+type Golden struct {
+       Filename string
+       Archive  *txtar.Archive
+       Modified bool
+}
+
+func Context(t testing.TB) context.Context {
+       return context.Background()
+}
+
+func DefaultOptions(o *source.Options) {
+       o.SupportedCodeActions = map[source.FileKind]map[protocol.CodeActionKind]bool{
+               source.Go: {
+                       protocol.SourceOrganizeImports: true,
+                       protocol.QuickFix:              true,
+                       protocol.RefactorRewrite:       true,
+                       protocol.RefactorExtract:       true,
+                       protocol.SourceFixAll:          true,
+               },
+               source.Mod: {
+                       protocol.SourceOrganizeImports: true,
+               },
+               source.Sum: {},
+       }
+       o.ExperimentalOptions.Codelens[source.CommandTest.Name] = true
+       o.HoverKind = source.SynopsisDocumentation
+       o.InsertTextFormat = protocol.SnippetTextFormat
+       o.CompletionBudget = time.Minute
+       o.HierarchicalDocumentSymbolSupport = true
+       o.ExperimentalWorkspaceModule = true
+       o.SemanticTokens = true
+}
+
+func RunTests(t *testing.T, dataDir string, includeMultiModule bool, f func(*testing.T, *Data)) {
+       t.Helper()
+       modes := []string{"Modules", "GOPATH"}
+       if includeMultiModule {
+               modes = append(modes, "MultiModule")
+       }
+       for _, mode := range modes {
+               t.Run(mode, func(t *testing.T) {
+                       t.Helper()
+                       if mode == "MultiModule" {
+                               // Some bug in 1.12 breaks reading markers, and it's not worth figuring out.
+                               testenv.NeedsGo1Point(t, 13)
+                       }
+                       datum := load(t, mode, dataDir)
+                       f(t, datum)
+               })
+       }
+}
+
+func load(t testing.TB, mode string, dir string) *Data {
+       t.Helper()
+
+       datum := &Data{
+               CallHierarchy:                 make(CallHierarchy),
+               CodeLens:                      make(CodeLens),
+               Diagnostics:                   make(Diagnostics),
+               CompletionItems:               make(CompletionItems),
+               Completions:                   make(Completions),
+               CompletionSnippets:            make(CompletionSnippets),
+               UnimportedCompletions:         make(UnimportedCompletions),
+               DeepCompletions:               make(DeepCompletions),
+               FuzzyCompletions:              make(FuzzyCompletions),
+               RankCompletions:               make(RankCompletions),
+               CaseSensitiveCompletions:      make(CaseSensitiveCompletions),
+               Definitions:                   make(Definitions),
+               Implementations:               make(Implementations),
+               Highlights:                    make(Highlights),
+               References:                    make(References),
+               Renames:                       make(Renames),
+               PrepareRenames:                make(PrepareRenames),
+               SuggestedFixes:                make(SuggestedFixes),
+               FunctionExtractions:           make(FunctionExtractions),
+               Symbols:                       make(Symbols),
+               symbolsChildren:               make(SymbolsChildren),
+               symbolInformation:             make(SymbolInformation),
+               WorkspaceSymbols:              make(WorkspaceSymbols),
+               FuzzyWorkspaceSymbols:         make(WorkspaceSymbols),
+               CaseSensitiveWorkspaceSymbols: make(WorkspaceSymbols),
+               Signatures:                    make(Signatures),
+               Links:                         make(Links),
+
+               t:         t,
+               dir:       dir,
+               fragments: map[string]string{},
+               golden:    map[string]*Golden{},
+               mode:      mode,
+               mappers:   map[span.URI]*protocol.ColumnMapper{},
+       }
+
+       if !*UpdateGolden {
+               summary := filepath.Join(filepath.FromSlash(dir), summaryFile+goldenFileSuffix)
+               if _, err := os.Stat(summary); os.IsNotExist(err) {
+                       t.Fatalf("could not find golden file summary.txt in %#v", dir)
+               }
+               archive, err := txtar.ParseFile(summary)
+               if err != nil {
+                       t.Fatalf("could not read golden file %v/%v: %v", dir, summary, err)
+               }
+               datum.golden[summaryFile] = &Golden{
+                       Filename: summary,
+                       Archive:  archive,
+               }
+       }
+
+       files := packagestest.MustCopyFileTree(dir)
+       overlays := map[string][]byte{}
+       for fragment, operation := range files {
+               if trimmed := strings.TrimSuffix(fragment, goldenFileSuffix); trimmed != fragment {
+                       delete(files, fragment)
+                       goldFile := filepath.Join(dir, fragment)
+                       archive, err := txtar.ParseFile(goldFile)
+                       if err != nil {
+                               t.Fatalf("could not read golden file %v: %v", fragment, err)
+                       }
+                       datum.golden[trimmed] = &Golden{
+                               Filename: goldFile,
+                               Archive:  archive,
+                       }
+               } else if trimmed := strings.TrimSuffix(fragment, inFileSuffix); trimmed != fragment {
+                       delete(files, fragment)
+                       files[trimmed] = operation
+               } else if index := strings.Index(fragment, overlayFileSuffix); index >= 0 {
+                       delete(files, fragment)
+                       partial := fragment[:index] + fragment[index+len(overlayFileSuffix):]
+                       contents, err := ioutil.ReadFile(filepath.Join(dir, fragment))
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       overlays[partial] = contents
+               }
+       }
+
+       modules := []packagestest.Module{
+               {
+                       Name:    testModule,
+                       Files:   files,
+                       Overlay: overlays,
+               },
+       }
+       switch mode {
+       case "Modules":
+               datum.Exported = packagestest.Export(t, packagestest.Modules, modules)
+       case "GOPATH":
+               datum.Exported = packagestest.Export(t, packagestest.GOPATH, modules)
+       case "MultiModule":
+               files := map[string]interface{}{}
+               for k, v := range modules[0].Files {
+                       files[filepath.Join("testmodule", k)] = v
+               }
+               modules[0].Files = files
+
+               overlays := map[string][]byte{}
+               for k, v := range modules[0].Overlay {
+                       overlays[filepath.Join("testmodule", k)] = v
+               }
+               modules[0].Overlay = overlays
+
+               golden := map[string]*Golden{}
+               for k, v := range datum.golden {
+                       if k == summaryFile {
+                               golden[k] = v
+                       } else {
+                               golden[filepath.Join("testmodule", k)] = v
+                       }
+               }
+               datum.golden = golden
+
+               datum.Exported = packagestest.Export(t, packagestest.Modules, modules)
+       default:
+               panic("unknown mode " + mode)
+       }
+
+       for _, m := range modules {
+               for fragment := range m.Files {
+                       filename := datum.Exported.File(m.Name, fragment)
+                       datum.fragments[filename] = fragment
+               }
+       }
+
+       // Turn off go/packages debug logging.
+       datum.Exported.Config.Logf = nil
+       datum.Config.Logf = nil
+
+       // Merge the exported.Config with the view.Config.
+       datum.Config = *datum.Exported.Config
+       datum.Config.Fset = token.NewFileSet()
+       datum.Config.Context = Context(nil)
+       datum.Config.ParseFile = func(fset *token.FileSet, filename string, src []byte) (*ast.File, error) {
+               panic("ParseFile should not be called")
+       }
+
+       // Do a first pass to collect special markers for completion and workspace symbols.
+       if err := datum.Exported.Expect(map[string]interface{}{
+               "item": func(name string, r packagestest.Range, _ []string) {
+                       datum.Exported.Mark(name, r)
+               },
+               "symbol": func(name string, r packagestest.Range, _ []string) {
+                       datum.Exported.Mark(name, r)
+               },
+       }); err != nil {
+               t.Fatal(err)
+       }
+
+       // Collect any data that needs to be used by subsequent tests.
+       if err := datum.Exported.Expect(map[string]interface{}{
+               "codelens":        datum.collectCodeLens,
+               "diag":            datum.collectDiagnostics,
+               "item":            datum.collectCompletionItems,
+               "complete":        datum.collectCompletions(CompletionDefault),
+               "unimported":      datum.collectCompletions(CompletionUnimported),
+               "deep":            datum.collectCompletions(CompletionDeep),
+               "fuzzy":           datum.collectCompletions(CompletionFuzzy),
+               "casesensitive":   datum.collectCompletions(CompletionCaseSensitive),
+               "rank":            datum.collectCompletions(CompletionRank),
+               "snippet":         datum.collectCompletionSnippets,
+               "fold":            datum.collectFoldingRanges,
+               "format":          datum.collectFormats,
+               "import":          datum.collectImports,
+               "semantic":        datum.collectSemanticTokens,
+               "godef":           datum.collectDefinitions,
+               "implementations": datum.collectImplementations,
+               "typdef":          datum.collectTypeDefinitions,
+               "hover":           datum.collectHoverDefinitions,
+               "highlight":       datum.collectHighlights,
+               "refs":            datum.collectReferences,
+               "rename":          datum.collectRenames,
+               "prepare":         datum.collectPrepareRenames,
+               "symbol":          datum.collectSymbols,
+               "signature":       datum.collectSignatures,
+               "link":            datum.collectLinks,
+               "suggestedfix":    datum.collectSuggestedFixes,
+               "extractfunc":     datum.collectFunctionExtractions,
+               "incomingcalls":   datum.collectIncomingCalls,
+               "outgoingcalls":   datum.collectOutgoingCalls,
+       }); err != nil {
+               t.Fatal(err)
+       }
+       for _, symbols := range datum.Symbols {
+               for i := range symbols {
+                       children := datum.symbolsChildren[symbols[i].Name]
+                       symbols[i].Children = children
+               }
+       }
+       // Collect names for the entries that require golden files.
+       if err := datum.Exported.Expect(map[string]interface{}{
+               "godef":                        datum.collectDefinitionNames,
+               "hover":                        datum.collectDefinitionNames,
+               "workspacesymbol":              datum.collectWorkspaceSymbols(WorkspaceSymbolsDefault),
+               "workspacesymbolfuzzy":         datum.collectWorkspaceSymbols(WorkspaceSymbolsFuzzy),
+               "workspacesymbolcasesensitive": datum.collectWorkspaceSymbols(WorkspaceSymbolsCaseSensitive),
+       }); err != nil {
+               t.Fatal(err)
+       }
+       if mode == "MultiModule" {
+               if err := os.Rename(filepath.Join(datum.Config.Dir, "go.mod"), filepath.Join(datum.Config.Dir, "testmodule/go.mod")); err != nil {
+                       t.Fatal(err)
+               }
+       }
+
+       return datum
+}
+
+func Run(t *testing.T, tests Tests, data *Data) {
+       t.Helper()
+       checkData(t, data)
+
+       eachCompletion := func(t *testing.T, cases map[span.Span][]Completion, test func(*testing.T, span.Span, Completion, CompletionItems)) {
+               t.Helper()
+
+               for src, exp := range cases {
+                       for i, e := range exp {
+                               t.Run(SpanName(src)+"_"+strconv.Itoa(i), func(t *testing.T) {
+                                       t.Helper()
+                                       if strings.Contains(t.Name(), "complit") || strings.Contains(t.Name(), "UnimportedCompletion") {
+                                               if data.mode == "MultiModule" {
+                                                       t.Skip("Unimported completions are broken in multi-module mode")
+                                               }
+                                       }
+                                       if strings.Contains(t.Name(), "cgo") {
+                                               testenv.NeedsTool(t, "cgo")
+                                       }
+                                       if strings.Contains(t.Name(), "declarecgo") {
+                                               testenv.NeedsGo1Point(t, 15)
+                                       }
+                                       test(t, src, e, data.CompletionItems)
+                               })
+                       }
+
+               }
+       }
+
+       eachWorkspaceSymbols := func(t *testing.T, cases map[string][]protocol.SymbolInformation, test func(*testing.T, string, []protocol.SymbolInformation, map[string]struct{})) {
+               t.Helper()
+
+               for query, expectedSymbols := range cases {
+                       name := query
+                       if name == "" {
+                               name = "EmptyQuery"
+                       }
+                       t.Run(name, func(t *testing.T) {
+                               t.Helper()
+                               dirs := make(map[string]struct{})
+                               for _, si := range expectedSymbols {
+                                       d := filepath.Dir(si.Location.URI.SpanURI().Filename())
+                                       if _, ok := dirs[d]; !ok {
+                                               dirs[d] = struct{}{}
+                                       }
+                               }
+                               test(t, query, expectedSymbols, dirs)
+                       })
+               }
+       }
+
+       t.Run("CallHierarchy", func(t *testing.T) {
+               t.Helper()
+               for spn, callHierarchyResult := range data.CallHierarchy {
+                       t.Run(SpanName(spn), func(t *testing.T) {
+                               t.Helper()
+                               tests.CallHierarchy(t, spn, callHierarchyResult)
+                       })
+               }
+       })
+
+       t.Run("Completion", func(t *testing.T) {
+               t.Helper()
+               eachCompletion(t, data.Completions, tests.Completion)
+       })
+
+       t.Run("CompletionSnippets", func(t *testing.T) {
+               t.Helper()
+               for _, placeholders := range []bool{true, false} {
+                       for src, expecteds := range data.CompletionSnippets {
+                               for i, expected := range expecteds {
+                                       name := SpanName(src) + "_" + strconv.Itoa(i+1)
+                                       if placeholders {
+                                               name += "_placeholders"
+                                       }
+
+                                       t.Run(name, func(t *testing.T) {
+                                               t.Helper()
+                                               tests.CompletionSnippet(t, src, expected, placeholders, data.CompletionItems)
+                                       })
+                               }
+                       }
+               }
+       })
+
+       t.Run("UnimportedCompletion", func(t *testing.T) {
+               t.Helper()
+               eachCompletion(t, data.UnimportedCompletions, tests.UnimportedCompletion)
+       })
+
+       t.Run("DeepCompletion", func(t *testing.T) {
+               t.Helper()
+               eachCompletion(t, data.DeepCompletions, tests.DeepCompletion)
+       })
+
+       t.Run("FuzzyCompletion", func(t *testing.T) {
+               t.Helper()
+               eachCompletion(t, data.FuzzyCompletions, tests.FuzzyCompletion)
+       })
+
+       t.Run("CaseSensitiveCompletion", func(t *testing.T) {
+               t.Helper()
+               eachCompletion(t, data.CaseSensitiveCompletions, tests.CaseSensitiveCompletion)
+       })
+
+       t.Run("RankCompletions", func(t *testing.T) {
+               t.Helper()
+               eachCompletion(t, data.RankCompletions, tests.RankCompletion)
+       })
+
+       t.Run("CodeLens", func(t *testing.T) {
+               t.Helper()
+               for uri, want := range data.CodeLens {
+                       // Check if we should skip this URI if the -modfile flag is not available.
+                       if shouldSkip(data, uri) {
+                               continue
+                       }
+                       t.Run(uriName(uri), func(t *testing.T) {
+                               t.Helper()
+                               tests.CodeLens(t, uri, want)
+                       })
+               }
+       })
+
+       t.Run("Diagnostics", func(t *testing.T) {
+               t.Helper()
+               for uri, want := range data.Diagnostics {
+                       // Check if we should skip this URI if the -modfile flag is not available.
+                       if shouldSkip(data, uri) {
+                               continue
+                       }
+                       t.Run(uriName(uri), func(t *testing.T) {
+                               t.Helper()
+                               tests.Diagnostics(t, uri, want)
+                       })
+               }
+       })
+
+       t.Run("FoldingRange", func(t *testing.T) {
+               t.Helper()
+               for _, spn := range data.FoldingRanges {
+                       t.Run(uriName(spn.URI()), func(t *testing.T) {
+                               t.Helper()
+                               tests.FoldingRanges(t, spn)
+                       })
+               }
+       })
+
+       t.Run("Format", func(t *testing.T) {
+               t.Helper()
+               for _, spn := range data.Formats {
+                       t.Run(uriName(spn.URI()), func(t *testing.T) {
+                               t.Helper()
+                               tests.Format(t, spn)
+                       })
+               }
+       })
+
+       t.Run("Import", func(t *testing.T) {
+               t.Helper()
+               for _, spn := range data.Imports {
+                       t.Run(uriName(spn.URI()), func(t *testing.T) {
+                               t.Helper()
+                               tests.Import(t, spn)
+                       })
+               }
+       })
+
+       t.Run("SemanticTokens", func(t *testing.T) {
+               t.Helper()
+               for _, spn := range data.SemanticTokens {
+                       t.Run(uriName(spn.URI()), func(t *testing.T) {
+                               t.Helper()
+                               tests.SemanticTokens(t, spn)
+                       })
+               }
+       })
+
+       t.Run("SuggestedFix", func(t *testing.T) {
+               t.Helper()
+               for spn, actionKinds := range data.SuggestedFixes {
+                       // Check if we should skip this spn if the -modfile flag is not available.
+                       if shouldSkip(data, spn.URI()) {
+                               continue
+                       }
+                       t.Run(SpanName(spn), func(t *testing.T) {
+                               t.Helper()
+                               tests.SuggestedFix(t, spn, actionKinds)
+                       })
+               }
+       })
+
+       t.Run("FunctionExtraction", func(t *testing.T) {
+               t.Helper()
+               for start, end := range data.FunctionExtractions {
+                       // Check if we should skip this spn if the -modfile flag is not available.
+                       if shouldSkip(data, start.URI()) {
+                               continue
+                       }
+                       t.Run(SpanName(start), func(t *testing.T) {
+                               t.Helper()
+                               tests.FunctionExtraction(t, start, end)
+                       })
+               }
+       })
+
+       t.Run("Definition", func(t *testing.T) {
+               t.Helper()
+               for spn, d := range data.Definitions {
+                       t.Run(SpanName(spn), func(t *testing.T) {
+                               t.Helper()
+                               if strings.Contains(t.Name(), "cgo") {
+                                       testenv.NeedsTool(t, "cgo")
+                               }
+                               if strings.Contains(t.Name(), "declarecgo") {
+                                       testenv.NeedsGo1Point(t, 15)
+                               }
+                               tests.Definition(t, spn, d)
+                       })
+               }
+       })
+
+       t.Run("Implementation", func(t *testing.T) {
+               t.Helper()
+               for spn, m := range data.Implementations {
+                       t.Run(SpanName(spn), func(t *testing.T) {
+                               t.Helper()
+                               tests.Implementation(t, spn, m)
+                       })
+               }
+       })
+
+       t.Run("Highlight", func(t *testing.T) {
+               t.Helper()
+               for pos, locations := range data.Highlights {
+                       t.Run(SpanName(pos), func(t *testing.T) {
+                               t.Helper()
+                               tests.Highlight(t, pos, locations)
+                       })
+               }
+       })
+
+       t.Run("References", func(t *testing.T) {
+               t.Helper()
+               for src, itemList := range data.References {
+                       t.Run(SpanName(src), func(t *testing.T) {
+                               t.Helper()
+                               tests.References(t, src, itemList)
+                       })
+               }
+       })
+
+       t.Run("Renames", func(t *testing.T) {
+               t.Helper()
+               for spn, newText := range data.Renames {
+                       t.Run(uriName(spn.URI())+"_"+newText, func(t *testing.T) {
+                               t.Helper()
+                               tests.Rename(t, spn, newText)
+                       })
+               }
+       })
+
+       t.Run("PrepareRenames", func(t *testing.T) {
+               t.Helper()
+               for src, want := range data.PrepareRenames {
+                       t.Run(SpanName(src), func(t *testing.T) {
+                               t.Helper()
+                               tests.PrepareRename(t, src, want)
+                       })
+               }
+       })
+
+       t.Run("Symbols", func(t *testing.T) {
+               t.Helper()
+               for uri, expectedSymbols := range data.Symbols {
+                       t.Run(uriName(uri), func(t *testing.T) {
+                               t.Helper()
+                               tests.Symbols(t, uri, expectedSymbols)
+                       })
+               }
+       })
+
+       t.Run("WorkspaceSymbols", func(t *testing.T) {
+               t.Helper()
+               eachWorkspaceSymbols(t, data.WorkspaceSymbols, tests.WorkspaceSymbols)
+       })
+
+       t.Run("FuzzyWorkspaceSymbols", func(t *testing.T) {
+               t.Helper()
+               eachWorkspaceSymbols(t, data.FuzzyWorkspaceSymbols, tests.FuzzyWorkspaceSymbols)
+       })
+
+       t.Run("CaseSensitiveWorkspaceSymbols", func(t *testing.T) {
+               t.Helper()
+               eachWorkspaceSymbols(t, data.CaseSensitiveWorkspaceSymbols, tests.CaseSensitiveWorkspaceSymbols)
+       })
+
+       t.Run("SignatureHelp", func(t *testing.T) {
+               t.Helper()
+               for spn, expectedSignature := range data.Signatures {
+                       t.Run(SpanName(spn), func(t *testing.T) {
+                               t.Helper()
+                               tests.SignatureHelp(t, spn, expectedSignature)
+                       })
+               }
+       })
+
+       t.Run("Link", func(t *testing.T) {
+               t.Helper()
+               for uri, wantLinks := range data.Links {
+                       // If we are testing GOPATH, then we do not want links with
+                       // the versions attached (pkg.go.dev/repoa/moda@v1.1.0/pkg),
+                       // unless the file is a go.mod, then we can skip it alltogether.
+                       if data.Exported.Exporter == packagestest.GOPATH {
+                               if strings.HasSuffix(uri.Filename(), ".mod") {
+                                       continue
+                               }
+                               re := regexp.MustCompile(`@v\d+\.\d+\.[\w-]+`)
+                               for i, link := range wantLinks {
+                                       wantLinks[i].Target = re.ReplaceAllString(link.Target, "")
+                               }
+                       }
+                       t.Run(uriName(uri), func(t *testing.T) {
+                               t.Helper()
+                               tests.Link(t, uri, wantLinks)
+                       })
+               }
+       })
+
+       if *UpdateGolden {
+               for _, golden := range data.golden {
+                       if !golden.Modified {
+                               continue
+                       }
+                       sort.Slice(golden.Archive.Files, func(i, j int) bool {
+                               return golden.Archive.Files[i].Name < golden.Archive.Files[j].Name
+                       })
+                       if err := ioutil.WriteFile(golden.Filename, txtar.Format(golden.Archive), 0666); err != nil {
+                               t.Fatal(err)
+                       }
+               }
+       }
+}
+
+func checkData(t *testing.T, data *Data) {
+       buf := &bytes.Buffer{}
+       diagnosticsCount := 0
+       for _, want := range data.Diagnostics {
+               diagnosticsCount += len(want)
+       }
+       linksCount := 0
+       for _, want := range data.Links {
+               linksCount += len(want)
+       }
+       definitionCount := 0
+       typeDefinitionCount := 0
+       for _, d := range data.Definitions {
+               if d.IsType {
+                       typeDefinitionCount++
+               } else {
+                       definitionCount++
+               }
+       }
+
+       snippetCount := 0
+       for _, want := range data.CompletionSnippets {
+               snippetCount += len(want)
+       }
+
+       countCompletions := func(c map[span.Span][]Completion) (count int) {
+               for _, want := range c {
+                       count += len(want)
+               }
+               return count
+       }
+
+       countCodeLens := func(c map[span.URI][]protocol.CodeLens) (count int) {
+               for _, want := range c {
+                       count += len(want)
+               }
+               return count
+       }
+
+       fmt.Fprintf(buf, "CallHierarchyCount = %v\n", len(data.CallHierarchy))
+       fmt.Fprintf(buf, "CodeLensCount = %v\n", countCodeLens(data.CodeLens))
+       fmt.Fprintf(buf, "CompletionsCount = %v\n", countCompletions(data.Completions))
+       fmt.Fprintf(buf, "CompletionSnippetCount = %v\n", snippetCount)
+       fmt.Fprintf(buf, "UnimportedCompletionsCount = %v\n", countCompletions(data.UnimportedCompletions))
+       fmt.Fprintf(buf, "DeepCompletionsCount = %v\n", countCompletions(data.DeepCompletions))
+       fmt.Fprintf(buf, "FuzzyCompletionsCount = %v\n", countCompletions(data.FuzzyCompletions))
+       fmt.Fprintf(buf, "RankedCompletionsCount = %v\n", countCompletions(data.RankCompletions))
+       fmt.Fprintf(buf, "CaseSensitiveCompletionsCount = %v\n", countCompletions(data.CaseSensitiveCompletions))
+       fmt.Fprintf(buf, "DiagnosticsCount = %v\n", diagnosticsCount)
+       fmt.Fprintf(buf, "FoldingRangesCount = %v\n", len(data.FoldingRanges))
+       fmt.Fprintf(buf, "FormatCount = %v\n", len(data.Formats))
+       fmt.Fprintf(buf, "ImportCount = %v\n", len(data.Imports))
+       fmt.Fprintf(buf, "SemanticTokenCount = %v\n", len(data.SemanticTokens))
+       fmt.Fprintf(buf, "SuggestedFixCount = %v\n", len(data.SuggestedFixes))
+       fmt.Fprintf(buf, "FunctionExtractionCount = %v\n", len(data.FunctionExtractions))
+       fmt.Fprintf(buf, "DefinitionsCount = %v\n", definitionCount)
+       fmt.Fprintf(buf, "TypeDefinitionsCount = %v\n", typeDefinitionCount)
+       fmt.Fprintf(buf, "HighlightsCount = %v\n", len(data.Highlights))
+       fmt.Fprintf(buf, "ReferencesCount = %v\n", len(data.References))
+       fmt.Fprintf(buf, "RenamesCount = %v\n", len(data.Renames))
+       fmt.Fprintf(buf, "PrepareRenamesCount = %v\n", len(data.PrepareRenames))
+       fmt.Fprintf(buf, "SymbolsCount = %v\n", len(data.Symbols))
+       fmt.Fprintf(buf, "WorkspaceSymbolsCount = %v\n", len(data.WorkspaceSymbols))
+       fmt.Fprintf(buf, "FuzzyWorkspaceSymbolsCount = %v\n", len(data.FuzzyWorkspaceSymbols))
+       fmt.Fprintf(buf, "CaseSensitiveWorkspaceSymbolsCount = %v\n", len(data.CaseSensitiveWorkspaceSymbols))
+       fmt.Fprintf(buf, "SignaturesCount = %v\n", len(data.Signatures))
+       fmt.Fprintf(buf, "LinksCount = %v\n", linksCount)
+       fmt.Fprintf(buf, "ImplementationsCount = %v\n", len(data.Implementations))
+
+       want := string(data.Golden("summary", summaryFile, func() ([]byte, error) {
+               return buf.Bytes(), nil
+       }))
+       got := buf.String()
+       if want != got {
+               t.Errorf("test summary does not match:\n%s", Diff(want, got))
+       }
+}
+
+func (data *Data) Mapper(uri span.URI) (*protocol.ColumnMapper, error) {
+       data.mappersMu.Lock()
+       defer data.mappersMu.Unlock()
+
+       if _, ok := data.mappers[uri]; !ok {
+               content, err := data.Exported.FileContents(uri.Filename())
+               if err != nil {
+                       return nil, err
+               }
+               converter := span.NewContentConverter(uri.Filename(), content)
+               data.mappers[uri] = &protocol.ColumnMapper{
+                       URI:       uri,
+                       Converter: converter,
+                       Content:   content,
+               }
+       }
+       return data.mappers[uri], nil
+}
+
+func (data *Data) Golden(tag string, target string, update func() ([]byte, error)) []byte {
+       data.t.Helper()
+       fragment, found := data.fragments[target]
+       if !found {
+               if filepath.IsAbs(target) {
+                       data.t.Fatalf("invalid golden file fragment %v", target)
+               }
+               fragment = target
+       }
+       golden := data.golden[fragment]
+       if golden == nil {
+               if !*UpdateGolden {
+                       data.t.Fatalf("could not find golden file %v: %v", fragment, tag)
+               }
+               var subdir string
+               if fragment != summaryFile {
+                       subdir = "primarymod"
+               }
+               golden = &Golden{
+                       Filename: filepath.Join(data.dir, subdir, fragment+goldenFileSuffix),
+                       Archive:  &txtar.Archive{},
+                       Modified: true,
+               }
+               data.golden[fragment] = golden
+       }
+       var file *txtar.File
+       for i := range golden.Archive.Files {
+               f := &golden.Archive.Files[i]
+               if f.Name == tag {
+                       file = f
+                       break
+               }
+       }
+       if *UpdateGolden {
+               if file == nil {
+                       golden.Archive.Files = append(golden.Archive.Files, txtar.File{
+                               Name: tag,
+                       })
+                       file = &golden.Archive.Files[len(golden.Archive.Files)-1]
+               }
+               contents, err := update()
+               if err != nil {
+                       data.t.Fatalf("could not update golden file %v: %v", fragment, err)
+               }
+               file.Data = append(contents, '\n') // add trailing \n for txtar
+               golden.Modified = true
+
+       }
+       if file == nil {
+               data.t.Fatalf("could not find golden contents %v: %v", fragment, tag)
+       }
+       if len(file.Data) == 0 {
+               return file.Data
+       }
+       return file.Data[:len(file.Data)-1] // drop the trailing \n
+}
+
+func (data *Data) collectCodeLens(spn span.Span, title, cmd string) {
+       if _, ok := data.CodeLens[spn.URI()]; !ok {
+               data.CodeLens[spn.URI()] = []protocol.CodeLens{}
+       }
+       m, err := data.Mapper(spn.URI())
+       if err != nil {
+               return
+       }
+       rng, err := m.Range(spn)
+       if err != nil {
+               return
+       }
+       data.CodeLens[spn.URI()] = append(data.CodeLens[spn.URI()], protocol.CodeLens{
+               Range: rng,
+               Command: protocol.Command{
+                       Title:   title,
+                       Command: cmd,
+               },
+       })
+}
+
+func (data *Data) collectDiagnostics(spn span.Span, msgSource, msg, msgSeverity string) {
+       if _, ok := data.Diagnostics[spn.URI()]; !ok {
+               data.Diagnostics[spn.URI()] = []*source.Diagnostic{}
+       }
+       m, err := data.Mapper(spn.URI())
+       if err != nil {
+               return
+       }
+       rng, err := m.Range(spn)
+       if err != nil {
+               return
+       }
+       severity := protocol.SeverityError
+       switch msgSeverity {
+       case "error":
+               severity = protocol.SeverityError
+       case "warning":
+               severity = protocol.SeverityWarning
+       case "hint":
+               severity = protocol.SeverityHint
+       case "information":
+               severity = protocol.SeverityInformation
+       }
+       // This is not the correct way to do this, but it seems excessive to do the full conversion here.
+       want := &source.Diagnostic{
+               Range:    rng,
+               Severity: severity,
+               Source:   msgSource,
+               Message:  msg,
+       }
+       data.Diagnostics[spn.URI()] = append(data.Diagnostics[spn.URI()], want)
+}
+
+func (data *Data) collectCompletions(typ CompletionTestType) func(span.Span, []token.Pos) {
+       result := func(m map[span.Span][]Completion, src span.Span, expected []token.Pos) {
+               m[src] = append(m[src], Completion{
+                       CompletionItems: expected,
+               })
+       }
+       switch typ {
+       case CompletionDeep:
+               return func(src span.Span, expected []token.Pos) {
+                       result(data.DeepCompletions, src, expected)
+               }
+       case CompletionUnimported:
+               return func(src span.Span, expected []token.Pos) {
+                       result(data.UnimportedCompletions, src, expected)
+               }
+       case CompletionFuzzy:
+               return func(src span.Span, expected []token.Pos) {
+                       result(data.FuzzyCompletions, src, expected)
+               }
+       case CompletionRank:
+               return func(src span.Span, expected []token.Pos) {
+                       result(data.RankCompletions, src, expected)
+               }
+       case CompletionCaseSensitive:
+               return func(src span.Span, expected []token.Pos) {
+                       result(data.CaseSensitiveCompletions, src, expected)
+               }
+       default:
+               return func(src span.Span, expected []token.Pos) {
+                       result(data.Completions, src, expected)
+               }
+       }
+}
+
+func (data *Data) collectCompletionItems(pos token.Pos, args []string) {
+       if len(args) < 3 {
+               loc := data.Exported.ExpectFileSet.Position(pos)
+               data.t.Fatalf("%s:%d: @item expects at least 3 args, got %d",
+                       loc.Filename, loc.Line, len(args))
+       }
+       label, detail, kind := args[0], args[1], args[2]
+       var documentation string
+       if len(args) == 4 {
+               documentation = args[3]
+       }
+       data.CompletionItems[pos] = &completion.CompletionItem{
+               Label:         label,
+               Detail:        detail,
+               Kind:          protocol.ParseCompletionItemKind(kind),
+               Documentation: documentation,
+       }
+}
+
+func (data *Data) collectFoldingRanges(spn span.Span) {
+       data.FoldingRanges = append(data.FoldingRanges, spn)
+}
+
+func (data *Data) collectFormats(spn span.Span) {
+       data.Formats = append(data.Formats, spn)
+}
+
+func (data *Data) collectImports(spn span.Span) {
+       data.Imports = append(data.Imports, spn)
+}
+
+func (data *Data) collectSemanticTokens(spn span.Span) {
+       data.SemanticTokens = append(data.SemanticTokens, spn)
+}
+
+func (data *Data) collectSuggestedFixes(spn span.Span, actionKind string) {
+       if _, ok := data.SuggestedFixes[spn]; !ok {
+               data.SuggestedFixes[spn] = []string{}
+       }
+       data.SuggestedFixes[spn] = append(data.SuggestedFixes[spn], actionKind)
+}
+
+func (data *Data) collectFunctionExtractions(start span.Span, end span.Span) {
+       if _, ok := data.FunctionExtractions[start]; !ok {
+               data.FunctionExtractions[start] = end
+       }
+}
+
+func (data *Data) collectDefinitions(src, target span.Span) {
+       data.Definitions[src] = Definition{
+               Src: src,
+               Def: target,
+       }
+}
+
+func (data *Data) collectImplementations(src span.Span, targets []span.Span) {
+       data.Implementations[src] = targets
+}
+
+func (data *Data) collectIncomingCalls(src span.Span, calls []span.Span) {
+       for _, call := range calls {
+               m, err := data.Mapper(call.URI())
+               if err != nil {
+                       data.t.Fatal(err)
+               }
+               rng, err := m.Range(call)
+               if err != nil {
+                       data.t.Fatal(err)
+               }
+               // we're only comparing protocol.range
+               if data.CallHierarchy[src] != nil {
+                       data.CallHierarchy[src].IncomingCalls = append(data.CallHierarchy[src].IncomingCalls,
+                               protocol.CallHierarchyItem{
+                                       URI:   protocol.DocumentURI(call.URI()),
+                                       Range: rng,
+                               })
+               } else {
+                       data.CallHierarchy[src] = &CallHierarchyResult{
+                               IncomingCalls: []protocol.CallHierarchyItem{
+                                       {URI: protocol.DocumentURI(call.URI()), Range: rng},
+                               },
+                       }
+               }
+       }
+}
+
+func (data *Data) collectOutgoingCalls(src span.Span, calls []span.Span) {
+       for _, call := range calls {
+               m, err := data.Mapper(call.URI())
+               if err != nil {
+                       data.t.Fatal(err)
+               }
+               rng, err := m.Range(call)
+               if err != nil {
+                       data.t.Fatal(err)
+               }
+               // we're only comparing protocol.range
+               if data.CallHierarchy[src] != nil {
+                       data.CallHierarchy[src].OutgoingCalls = append(data.CallHierarchy[src].OutgoingCalls,
+                               protocol.CallHierarchyItem{
+                                       URI:   protocol.DocumentURI(call.URI()),
+                                       Range: rng,
+                               })
+               } else {
+                       data.CallHierarchy[src] = &CallHierarchyResult{
+                               OutgoingCalls: []protocol.CallHierarchyItem{
+                                       {URI: protocol.DocumentURI(call.URI()), Range: rng},
+                               },
+                       }
+               }
+       }
+}
+
+func (data *Data) collectHoverDefinitions(src, target span.Span) {
+       data.Definitions[src] = Definition{
+               Src:       src,
+               Def:       target,
+               OnlyHover: true,
+       }
+}
+
+func (data *Data) collectTypeDefinitions(src, target span.Span) {
+       data.Definitions[src] = Definition{
+               Src:    src,
+               Def:    target,
+               IsType: true,
+       }
+}
+
+func (data *Data) collectDefinitionNames(src span.Span, name string) {
+       d := data.Definitions[src]
+       d.Name = name
+       data.Definitions[src] = d
+}
+
+func (data *Data) collectHighlights(src span.Span, expected []span.Span) {
+       // Declaring a highlight in a test file: @highlight(src, expected1, expected2)
+       data.Highlights[src] = append(data.Highlights[src], expected...)
+}
+
+func (data *Data) collectReferences(src span.Span, expected []span.Span) {
+       data.References[src] = expected
+}
+
+func (data *Data) collectRenames(src span.Span, newText string) {
+       data.Renames[src] = newText
+}
+
+func (data *Data) collectPrepareRenames(src span.Span, rng span.Range, placeholder string) {
+       m, err := data.Mapper(src.URI())
+       if err != nil {
+               data.t.Fatal(err)
+       }
+       // Convert range to span and then to protocol.Range.
+       spn, err := rng.Span()
+       if err != nil {
+               data.t.Fatal(err)
+       }
+       prng, err := m.Range(spn)
+       if err != nil {
+               data.t.Fatal(err)
+       }
+       data.PrepareRenames[src] = &source.PrepareItem{
+               Range: prng,
+               Text:  placeholder,
+       }
+}
+
+// collectSymbols is responsible for collecting @symbol annotations.
+func (data *Data) collectSymbols(name string, spn span.Span, kind string, parentName string, siName string) {
+       m, err := data.Mapper(spn.URI())
+       if err != nil {
+               data.t.Fatal(err)
+       }
+       rng, err := m.Range(spn)
+       if err != nil {
+               data.t.Fatal(err)
+       }
+       sym := protocol.DocumentSymbol{
+               Name:           name,
+               Kind:           protocol.ParseSymbolKind(kind),
+               SelectionRange: rng,
+       }
+       if parentName == "" {
+               data.Symbols[spn.URI()] = append(data.Symbols[spn.URI()], sym)
+       } else {
+               data.symbolsChildren[parentName] = append(data.symbolsChildren[parentName], sym)
+       }
+
+       // Reuse @symbol in the workspace symbols tests.
+       si := protocol.SymbolInformation{
+               Name: siName,
+               Kind: sym.Kind,
+               Location: protocol.Location{
+                       URI:   protocol.URIFromSpanURI(spn.URI()),
+                       Range: sym.SelectionRange,
+               },
+       }
+       data.symbolInformation[spn] = si
+}
+
+func (data *Data) collectWorkspaceSymbols(typ WorkspaceSymbolsTestType) func(string, []span.Span) {
+       switch typ {
+       case WorkspaceSymbolsFuzzy:
+               return func(query string, targets []span.Span) {
+                       data.FuzzyWorkspaceSymbols[query] = make([]protocol.SymbolInformation, 0, len(targets))
+                       for _, target := range targets {
+                               data.FuzzyWorkspaceSymbols[query] = append(data.FuzzyWorkspaceSymbols[query], data.symbolInformation[target])
+                       }
+               }
+       case WorkspaceSymbolsCaseSensitive:
+               return func(query string, targets []span.Span) {
+                       data.CaseSensitiveWorkspaceSymbols[query] = make([]protocol.SymbolInformation, 0, len(targets))
+                       for _, target := range targets {
+                               data.CaseSensitiveWorkspaceSymbols[query] = append(data.CaseSensitiveWorkspaceSymbols[query], data.symbolInformation[target])
+                       }
+               }
+       default:
+               return func(query string, targets []span.Span) {
+                       data.WorkspaceSymbols[query] = make([]protocol.SymbolInformation, 0, len(targets))
+                       for _, target := range targets {
+                               data.WorkspaceSymbols[query] = append(data.WorkspaceSymbols[query], data.symbolInformation[target])
+                       }
+               }
+       }
+}
+
+func (data *Data) collectSignatures(spn span.Span, signature string, activeParam int64) {
+       data.Signatures[spn] = &protocol.SignatureHelp{
+               Signatures: []protocol.SignatureInformation{
+                       {
+                               Label: signature,
+                       },
+               },
+               ActiveParameter: float64(activeParam),
+       }
+       // Hardcode special case to test the lack of a signature.
+       if signature == "" && activeParam == 0 {
+               data.Signatures[spn] = nil
+       }
+}
+
+func (data *Data) collectCompletionSnippets(spn span.Span, item token.Pos, plain, placeholder string) {
+       data.CompletionSnippets[spn] = append(data.CompletionSnippets[spn], CompletionSnippet{
+               CompletionItem:     item,
+               PlainSnippet:       plain,
+               PlaceholderSnippet: placeholder,
+       })
+}
+
+func (data *Data) collectLinks(spn span.Span, link string, note *expect.Note, fset *token.FileSet) {
+       position := fset.Position(note.Pos)
+       uri := spn.URI()
+       data.Links[uri] = append(data.Links[uri], Link{
+               Src:          spn,
+               Target:       link,
+               NotePosition: position,
+       })
+}
+
+func uriName(uri span.URI) string {
+       return filepath.Base(strings.TrimSuffix(uri.Filename(), ".go"))
+}
+
+func SpanName(spn span.Span) string {
+       return fmt.Sprintf("%v_%v_%v", uriName(spn.URI()), spn.Start().Line(), spn.Start().Column())
+}
+
+func CopyFolderToTempDir(folder string) (string, error) {
+       if _, err := os.Stat(folder); err != nil {
+               return "", err
+       }
+       dst, err := ioutil.TempDir("", "modfile_test")
+       if err != nil {
+               return "", err
+       }
+       fds, err := ioutil.ReadDir(folder)
+       if err != nil {
+               return "", err
+       }
+       for _, fd := range fds {
+               srcfp := filepath.Join(folder, fd.Name())
+               stat, err := os.Stat(srcfp)
+               if err != nil {
+                       return "", err
+               }
+               if !stat.Mode().IsRegular() {
+                       return "", fmt.Errorf("cannot copy non regular file %s", srcfp)
+               }
+               contents, err := ioutil.ReadFile(srcfp)
+               if err != nil {
+                       return "", err
+               }
+               if err := ioutil.WriteFile(filepath.Join(dst, fd.Name()), contents, stat.Mode()); err != nil {
+                       return "", err
+               }
+       }
+       return dst, nil
+}
+
+func shouldSkip(data *Data, uri span.URI) bool {
+       if data.ModfileFlagAvailable {
+               return false
+       }
+       // If the -modfile flag is not available, then we do not want to run
+       // any tests on the go.mod file.
+       if strings.HasSuffix(uri.Filename(), ".mod") {
+               return true
+       }
+       // If the -modfile flag is not available, then we do not want to test any
+       // uri that contains "go mod tidy".
+       m, err := data.Mapper(uri)
+       return err == nil && strings.Contains(string(m.Content), ", \"go mod tidy\",")
+}