golang.org/x/tools/gopls@v0.15.3/internal/test/integration/workspace/standalone_test.go (about)

     1  // Copyright 2022 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 workspace
     6  
     7  import (
     8  	"sort"
     9  	"testing"
    10  
    11  	"github.com/google/go-cmp/cmp"
    12  	"golang.org/x/tools/gopls/internal/protocol"
    13  	. "golang.org/x/tools/gopls/internal/test/integration"
    14  )
    15  
    16  func TestStandaloneFiles(t *testing.T) {
    17  	const files = `
    18  -- go.mod --
    19  module mod.test
    20  
    21  go 1.16
    22  -- lib/lib.go --
    23  package lib
    24  
    25  const K = 0
    26  
    27  type I interface {
    28  	M()
    29  }
    30  -- lib/ignore.go --
    31  //go:build ignore
    32  // +build ignore
    33  
    34  package main
    35  
    36  import (
    37  	"mod.test/lib"
    38  )
    39  
    40  const K = 1
    41  
    42  type Mer struct{}
    43  func (Mer) M()
    44  
    45  func main() {
    46  	println(lib.K + K)
    47  }
    48  `
    49  	WithOptions(
    50  		// On Go 1.17 and earlier, this test fails with
    51  		// experimentalWorkspaceModule. Not investigated, as
    52  		// experimentalWorkspaceModule will be removed.
    53  		Modes(Default),
    54  	).Run(t, files, func(t *testing.T, env *Env) {
    55  		// Initially, gopls should not know about the standalone file as it hasn't
    56  		// been opened. Therefore, we should only find one symbol 'K'.
    57  		//
    58  		// (The choice of "K" is a little sleazy: it was originally "C" until
    59  		// we started adding "unsafe" to the workspace unconditionally, which
    60  		// caused a spurious match of "unsafe.Slice". But in practice every
    61  		// workspace depends on unsafe.)
    62  		syms := env.Symbol("K")
    63  		if got, want := len(syms), 1; got != want {
    64  			t.Errorf("got %d symbols, want %d (%+v)", got, want, syms)
    65  		}
    66  
    67  		// Similarly, we should only find one reference to "K", and no
    68  		// implementations of I.
    69  		checkLocations := func(method string, gotLocations []protocol.Location, wantFiles ...string) {
    70  			var gotFiles []string
    71  			for _, l := range gotLocations {
    72  				gotFiles = append(gotFiles, env.Sandbox.Workdir.URIToPath(l.URI))
    73  			}
    74  			sort.Strings(gotFiles)
    75  			sort.Strings(wantFiles)
    76  			if diff := cmp.Diff(wantFiles, gotFiles); diff != "" {
    77  				t.Errorf("%s(...): unexpected locations (-want +got):\n%s", method, diff)
    78  			}
    79  		}
    80  
    81  		env.OpenFile("lib/lib.go")
    82  		env.AfterChange(NoDiagnostics())
    83  
    84  		// Replacing K with D should not cause any workspace diagnostics, since we
    85  		// haven't yet opened the standalone file.
    86  		env.RegexpReplace("lib/lib.go", "K", "D")
    87  		env.AfterChange(NoDiagnostics())
    88  		env.RegexpReplace("lib/lib.go", "D", "K")
    89  		env.AfterChange(NoDiagnostics())
    90  
    91  		refs := env.References(env.RegexpSearch("lib/lib.go", "K"))
    92  		checkLocations("References", refs, "lib/lib.go")
    93  
    94  		impls := env.Implementations(env.RegexpSearch("lib/lib.go", "I"))
    95  		checkLocations("Implementations", impls) // no implementations
    96  
    97  		// Opening the standalone file should not result in any diagnostics.
    98  		env.OpenFile("lib/ignore.go")
    99  		env.AfterChange(NoDiagnostics())
   100  
   101  		// Having opened the standalone file, we should find its symbols in the
   102  		// workspace.
   103  		syms = env.Symbol("K")
   104  		if got, want := len(syms), 2; got != want {
   105  			t.Fatalf("got %d symbols, want %d", got, want)
   106  		}
   107  
   108  		foundMainK := false
   109  		var symNames []string
   110  		for _, sym := range syms {
   111  			symNames = append(symNames, sym.Name)
   112  			if sym.Name == "main.K" {
   113  				foundMainK = true
   114  			}
   115  		}
   116  		if !foundMainK {
   117  			t.Errorf("WorkspaceSymbol(\"K\") = %v, want containing main.K", symNames)
   118  		}
   119  
   120  		// We should resolve workspace definitions in the standalone file.
   121  		fileLoc := env.GoToDefinition(env.RegexpSearch("lib/ignore.go", "lib.(K)"))
   122  		file := env.Sandbox.Workdir.URIToPath(fileLoc.URI)
   123  		if got, want := file, "lib/lib.go"; got != want {
   124  			t.Errorf("GoToDefinition(lib.K) = %v, want %v", got, want)
   125  		}
   126  
   127  		// ...as well as intra-file definitions
   128  		loc := env.GoToDefinition(env.RegexpSearch("lib/ignore.go", "\\+ (K)"))
   129  		wantLoc := env.RegexpSearch("lib/ignore.go", "const (K)")
   130  		if loc != wantLoc {
   131  			t.Errorf("GoToDefinition(K) = %v, want %v", loc, wantLoc)
   132  		}
   133  
   134  		// Renaming "lib.K" to "lib.D" should cause a diagnostic in the standalone
   135  		// file.
   136  		env.RegexpReplace("lib/lib.go", "K", "D")
   137  		env.AfterChange(Diagnostics(env.AtRegexp("lib/ignore.go", "lib.(K)")))
   138  
   139  		// Undoing the replacement should fix diagnostics
   140  		env.RegexpReplace("lib/lib.go", "D", "K")
   141  		env.AfterChange(NoDiagnostics())
   142  
   143  		// Now that our workspace has no errors, we should be able to find
   144  		// references and rename.
   145  		refs = env.References(env.RegexpSearch("lib/lib.go", "K"))
   146  		checkLocations("References", refs, "lib/lib.go", "lib/ignore.go")
   147  
   148  		impls = env.Implementations(env.RegexpSearch("lib/lib.go", "I"))
   149  		checkLocations("Implementations", impls, "lib/ignore.go")
   150  
   151  		// Renaming should rename in the standalone package.
   152  		env.Rename(env.RegexpSearch("lib/lib.go", "K"), "D")
   153  		env.RegexpSearch("lib/ignore.go", "lib.D")
   154  	})
   155  }
   156  
   157  func TestStandaloneFiles_Configuration(t *testing.T) {
   158  	const files = `
   159  -- go.mod --
   160  module mod.test
   161  
   162  go 1.18
   163  -- lib.go --
   164  package lib // without this package, files are loaded as command-line-arguments
   165  -- ignore.go --
   166  //go:build ignore
   167  // +build ignore
   168  
   169  package main
   170  
   171  // An arbitrary comment.
   172  
   173  func main() {}
   174  -- standalone.go --
   175  //go:build standalone
   176  // +build standalone
   177  
   178  package main
   179  
   180  func main() {}
   181  `
   182  
   183  	WithOptions(
   184  		Settings{
   185  			"standaloneTags": []string{"standalone", "script"},
   186  		},
   187  	).Run(t, files, func(t *testing.T, env *Env) {
   188  		env.OpenFile("ignore.go")
   189  		env.OpenFile("standalone.go")
   190  
   191  		env.AfterChange(
   192  			Diagnostics(env.AtRegexp("ignore.go", "package (main)")),
   193  			NoDiagnostics(ForFile("standalone.go")),
   194  		)
   195  
   196  		cfg := env.Editor.Config()
   197  		cfg.Settings = map[string]interface{}{
   198  			"standaloneTags": []string{"ignore"},
   199  		}
   200  		env.ChangeConfiguration(cfg)
   201  		env.AfterChange(
   202  			NoDiagnostics(ForFile("ignore.go")),
   203  			Diagnostics(env.AtRegexp("standalone.go", "package (main)")),
   204  		)
   205  	})
   206  }