diff options
Diffstat (limited to 'gopls/internal/regtest/misc/hover_test.go')
-rw-r--r-- | gopls/internal/regtest/misc/hover_test.go | 272 |
1 files changed, 257 insertions, 15 deletions
diff --git a/gopls/internal/regtest/misc/hover_test.go b/gopls/internal/regtest/misc/hover_test.go index 04dc740b8..72a6e23eb 100644 --- a/gopls/internal/regtest/misc/hover_test.go +++ b/gopls/internal/regtest/misc/hover_test.go @@ -5,11 +5,13 @@ package misc import ( + "fmt" "strings" "testing" - "golang.org/x/tools/internal/lsp/fake" - . "golang.org/x/tools/internal/lsp/regtest" + "golang.org/x/tools/gopls/internal/lsp/fake" + "golang.org/x/tools/gopls/internal/lsp/protocol" + . "golang.org/x/tools/gopls/internal/lsp/regtest" "golang.org/x/tools/internal/testenv" ) @@ -59,21 +61,22 @@ func main() { ProxyFiles(proxy), ).Run(t, mod, func(t *testing.T, env *Env) { env.OpenFile("main.go") - mixedPos := env.RegexpSearch("main.go", "Mixed") - got, _ := env.Hover("main.go", mixedPos) + mixedLoc := env.RegexpSearch("main.go", "Mixed") + got, _ := env.Hover(mixedLoc) if !strings.Contains(got.Value, "unexported") { t.Errorf("Workspace hover: missing expected field 'unexported'. Got:\n%q", got.Value) } - cacheFile, _ := env.GoToDefinition("main.go", mixedPos) - argPos := env.RegexpSearch(cacheFile, "printMixed.*(Mixed)") - got, _ = env.Hover(cacheFile, argPos) + cacheLoc := env.GoToDefinition(mixedLoc) + cacheFile := env.Sandbox.Workdir.URIToPath(cacheLoc.URI) + argLoc := env.RegexpSearch(cacheFile, "printMixed.*(Mixed)") + got, _ = env.Hover(argLoc) if !strings.Contains(got.Value, "unexported") { t.Errorf("Non-workspace hover: missing expected field 'unexported'. Got:\n%q", got.Value) } - exportedFieldPos := env.RegexpSearch("main.go", "Exported") - got, _ = env.Hover("main.go", exportedFieldPos) + exportedFieldLoc := env.RegexpSearch("main.go", "Exported") + got, _ = env.Hover(exportedFieldLoc) if !strings.Contains(got.Value, "comment") { t.Errorf("Workspace hover: missing comment for field 'Exported'. Got:\n%q", got.Value) } @@ -81,7 +84,12 @@ func main() { } func TestHoverIntLiteral(t *testing.T) { - testenv.NeedsGo1Point(t, 13) + // TODO(rfindley): this behavior doesn't actually make sense for vars. It is + // misleading to format their value when it is (of course) variable. + // + // Instead, we should allow hovering on numeric literals. + t.Skip("golang/go#58220: broken due to new hover logic") + const source = ` -- main.go -- package main @@ -98,13 +106,13 @@ func main() { Run(t, source, func(t *testing.T, env *Env) { env.OpenFile("main.go") hexExpected := "58190" - got, _ := env.Hover("main.go", env.RegexpSearch("main.go", "hex")) + got, _ := env.Hover(env.RegexpSearch("main.go", "hex")) if got != nil && !strings.Contains(got.Value, hexExpected) { t.Errorf("Hover: missing expected field '%s'. Got:\n%q", hexExpected, got.Value) } binExpected := "73" - got, _ = env.Hover("main.go", env.RegexpSearch("main.go", "bigBin")) + got, _ = env.Hover(env.RegexpSearch("main.go", "bigBin")) if got != nil && !strings.Contains(got.Value, binExpected) { t.Errorf("Hover: missing expected field '%s'. Got:\n%q", binExpected, got.Value) } @@ -113,7 +121,8 @@ func main() { // Tests that hovering does not trigger the panic in golang/go#48249. func TestPanicInHoverBrokenCode(t *testing.T) { - testenv.NeedsGo1Point(t, 13) + // Note: this test can not be expressed as a marker test, as it must use + // content without a trailing newline. const source = ` -- main.go -- package main @@ -121,7 +130,7 @@ package main type Example struct` Run(t, source, func(t *testing.T, env *Env) { env.OpenFile("main.go") - env.Editor.Hover(env.Ctx, "main.go", env.RegexpSearch("main.go", "Example")) + env.Editor.Hover(env.Ctx, env.RegexpSearch("main.go", "Example")) }) } @@ -137,6 +146,239 @@ package main Run(t, files, func(t *testing.T, env *Env) { env.OpenFile("main.go") env.EditBuffer("main.go", fake.NewEdit(0, 0, 1, 0, "package main\nfunc main() {\nconst x = `\nfoo\n`\n}")) - env.Editor.Hover(env.Ctx, "main.go", env.RegexpSearch("main.go", "foo")) + env.Editor.Hover(env.Ctx, env.RegexpSearch("main.go", "foo")) + }) +} + +func TestHoverImport(t *testing.T) { + const packageDoc1 = "Package lib1 hover documentation" + const packageDoc2 = "Package lib2 hover documentation" + tests := []struct { + hoverPackage string + want string + wantError bool + }{ + { + "mod.com/lib1", + packageDoc1, + false, + }, + { + "mod.com/lib2", + packageDoc2, + false, + }, + { + "mod.com/lib3", + "", + false, + }, + { + "mod.com/lib4", + "", + true, + }, + } + source := fmt.Sprintf(` +-- go.mod -- +module mod.com + +go 1.12 +-- lib1/a.go -- +// %s +package lib1 + +const C = 1 + +-- lib1/b.go -- +package lib1 + +const D = 1 + +-- lib2/a.go -- +// %s +package lib2 + +const E = 1 + +-- lib3/a.go -- +package lib3 + +const F = 1 + +-- main.go -- +package main + +import ( + "mod.com/lib1" + "mod.com/lib2" + "mod.com/lib3" + "mod.com/lib4" +) + +func main() { + println("Hello") +} + `, packageDoc1, packageDoc2) + Run(t, source, func(t *testing.T, env *Env) { + env.OpenFile("main.go") + for _, test := range tests { + got, _, err := env.Editor.Hover(env.Ctx, env.RegexpSearch("main.go", test.hoverPackage)) + if test.wantError { + if err == nil { + t.Errorf("Hover(%q) succeeded unexpectedly", test.hoverPackage) + } + } else if !strings.Contains(got.Value, test.want) { + t.Errorf("Hover(%q): got:\n%q\nwant:\n%q", test.hoverPackage, got.Value, test.want) + } + } + }) +} + +// for x/tools/gopls: unhandled named anchor on the hover #57048 +func TestHoverTags(t *testing.T) { + const source = ` +-- go.mod -- +module mod.com + +go 1.19 + +-- lib/a.go -- + +// variety of execution modes. +// +// # Test package setup +// +// The regression test package uses a couple of uncommon patterns to reduce +package lib + +-- a.go -- + package main + import "mod.com/lib" + + const A = 1 + +} +` + Run(t, source, func(t *testing.T, env *Env) { + t.Run("tags", func(t *testing.T) { + env.OpenFile("a.go") + z := env.RegexpSearch("a.go", "lib") + t.Logf("%#v", z) + got, _ := env.Hover(env.RegexpSearch("a.go", "lib")) + if strings.Contains(got.Value, "{#hdr-") { + t.Errorf("Hover: got {#hdr- tag:\n%q", got) + } + }) + }) +} + +// This is a regression test for Go issue #57625. +func TestHoverModMissingModuleStmt(t *testing.T) { + const source = ` +-- go.mod -- +go 1.16 +` + Run(t, source, func(t *testing.T, env *Env) { + env.OpenFile("go.mod") + env.Hover(env.RegexpSearch("go.mod", "go")) // no panic + }) +} + +func TestHoverCompletionMarkdown(t *testing.T) { + testenv.NeedsGo1Point(t, 19) + const source = ` +-- go.mod -- +module mod.com +go 1.19 +-- main.go -- +package main +// Just says [hello]. +// +// [hello]: https://en.wikipedia.org/wiki/Hello +func Hello() string { + Hello() //Here + return "hello" +} +` + Run(t, source, func(t *testing.T, env *Env) { + // Hover, Completion, and SignatureHelp should all produce markdown + // check that the markdown for SignatureHelp and Completion are + // the same, and contained in that for Hover (up to trailing \n) + env.OpenFile("main.go") + loc := env.RegexpSearch("main.go", "func (Hello)") + hover, _ := env.Hover(loc) + hoverContent := hover.Value + + loc = env.RegexpSearch("main.go", "//Here") + loc.Range.Start.Character -= 3 // Hello(_) //Here + completions := env.Completion(loc) + signatures := env.SignatureHelp(loc) + + if len(completions.Items) != 1 { + t.Errorf("got %d completions, expected 1", len(completions.Items)) + } + if len(signatures.Signatures) != 1 { + t.Errorf("got %d signatures, expected 1", len(signatures.Signatures)) + } + item := completions.Items[0].Documentation.Value + var itemContent string + if x, ok := item.(protocol.MarkupContent); !ok || x.Kind != protocol.Markdown { + t.Fatalf("%#v is not markdown", item) + } else { + itemContent = strings.Trim(x.Value, "\n") + } + sig := signatures.Signatures[0].Documentation.Value + var sigContent string + if x, ok := sig.(protocol.MarkupContent); !ok || x.Kind != protocol.Markdown { + t.Fatalf("%#v is not markdown", item) + } else { + sigContent = x.Value + } + if itemContent != sigContent { + t.Errorf("item:%q not sig:%q", itemContent, sigContent) + } + if !strings.Contains(hoverContent, itemContent) { + t.Errorf("hover:%q does not containt sig;%q", hoverContent, sigContent) + } }) } + +// Test that the generated markdown contains links for Go references. +// https://github.com/golang/go/issues/58352 +func TestHoverLinks(t *testing.T) { + testenv.NeedsGo1Point(t, 19) + const input = ` +-- go.mod -- +go 1.19 +module mod.com +-- main.go -- +package main +// [fmt] +var A int +// [fmt.Println] +var B int +// [golang.org/x/tools/go/packages.Package.String] +var C int +` + var tests = []struct { + pat string + ans string + }{ + {"A", "fmt"}, + {"B", "fmt#Println"}, + {"C", "golang.org/x/tools/go/packages#Package.String"}, + } + for _, test := range tests { + Run(t, input, func(t *testing.T, env *Env) { + env.OpenFile("main.go") + loc := env.RegexpSearch("main.go", test.pat) + hover, _ := env.Hover(loc) + hoverContent := hover.Value + want := fmt.Sprintf("%s/%s", "https://pkg.go.dev", test.ans) + if !strings.Contains(hoverContent, want) { + t.Errorf("hover:%q does not contain link %q", hoverContent, want) + } + }) + } +} |