internal/lsp: add more testdata for completion and diagnostics
Change-Id: I2a73e51b60f76a2af0f8ff4d34220b551e0cd378 Reviewed-on: https://go-review.googlesource.com/c/150041 Run-TryBot: Rebecca Stambler <rstambler@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Cottrell <iancottrell@google.com>
This commit is contained in:
parent
89e258047f
commit
fc4f04983f
|
@ -7,11 +7,12 @@ package packagestest
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/token"
|
"go/token"
|
||||||
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"golang.org/x/tools/go/expect"
|
"golang.org/x/tools/go/expect"
|
||||||
|
"golang.org/x/tools/go/packages"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -131,11 +132,18 @@ func (e *Exported) getNotes() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
notes := []*expect.Note{}
|
notes := []*expect.Note{}
|
||||||
|
var dirs []string
|
||||||
for _, module := range e.written {
|
for _, module := range e.written {
|
||||||
for _, filename := range module {
|
for _, filename := range module {
|
||||||
if !strings.HasSuffix(filename, ".go") {
|
dirs = append(dirs, filepath.Dir(filename))
|
||||||
continue
|
}
|
||||||
}
|
}
|
||||||
|
pkgs, err := packages.Load(e.Config, dirs...)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to load packages for directories %s: %v", dirs, err)
|
||||||
|
}
|
||||||
|
for _, pkg := range pkgs {
|
||||||
|
for _, filename := range pkg.GoFiles {
|
||||||
l, err := expect.Parse(e.fset, filename, nil)
|
l, err := expect.Parse(e.fset, filename, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to extract expectations: %v", err)
|
return fmt.Errorf("Failed to extract expectations: %v", err)
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
package lsp
|
package lsp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sort"
|
||||||
|
|
||||||
"golang.org/x/tools/internal/lsp/protocol"
|
"golang.org/x/tools/internal/lsp/protocol"
|
||||||
"golang.org/x/tools/internal/lsp/source"
|
"golang.org/x/tools/internal/lsp/source"
|
||||||
)
|
)
|
||||||
|
@ -36,3 +38,15 @@ func toProtocolSeverity(severity source.DiagnosticSeverity) protocol.DiagnosticS
|
||||||
}
|
}
|
||||||
return protocol.SeverityError // default
|
return protocol.SeverityError // default
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sorted(d []protocol.Diagnostic) {
|
||||||
|
sort.Slice(d, func(i int, j int) bool {
|
||||||
|
if d[i].Range.Start.Line == d[j].Range.Start.Line {
|
||||||
|
if d[i].Range.Start.Character == d[j].Range.End.Character {
|
||||||
|
return d[i].Message < d[j].Message
|
||||||
|
}
|
||||||
|
return d[i].Range.Start.Character < d[j].Range.End.Character
|
||||||
|
}
|
||||||
|
return d[i].Range.Start.Line < d[j].Range.Start.Line
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
//+build !go1.11
|
||||||
|
|
||||||
|
package lsp
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
goVersion111 = false
|
||||||
|
}
|
|
@ -12,7 +12,6 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -22,14 +21,18 @@ import (
|
||||||
"golang.org/x/tools/internal/lsp/source"
|
"golang.org/x/tools/internal/lsp/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO(rstambler): Remove this once Go 1.12 is released as we will end support
|
||||||
|
// for versions of Go <= 1.10.
|
||||||
|
var goVersion111 = true
|
||||||
|
|
||||||
func TestLSP(t *testing.T) {
|
func TestLSP(t *testing.T) {
|
||||||
packagestest.TestAll(t, testLSP)
|
packagestest.TestAll(t, testLSP)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testLSP(t *testing.T, exporter packagestest.Exporter) {
|
func testLSP(t *testing.T, exporter packagestest.Exporter) {
|
||||||
const dir = "testdata"
|
const dir = "testdata"
|
||||||
const expectedCompletionsCount = 4
|
const expectedCompletionsCount = 43
|
||||||
const expectedDiagnosticsCount = 9
|
const expectedDiagnosticsCount = 14
|
||||||
const expectedFormatCount = 3
|
const expectedFormatCount = 3
|
||||||
const expectedDefinitionsCount = 16
|
const expectedDefinitionsCount = 16
|
||||||
|
|
||||||
|
@ -49,8 +52,6 @@ func testLSP(t *testing.T, exporter packagestest.Exporter) {
|
||||||
exported := packagestest.Export(t, exporter, modules)
|
exported := packagestest.Export(t, exporter, modules)
|
||||||
defer exported.Cleanup()
|
defer exported.Cleanup()
|
||||||
|
|
||||||
dirs := make(map[string]bool)
|
|
||||||
|
|
||||||
// collect results for certain tests
|
// collect results for certain tests
|
||||||
expectedDiagnostics := make(diagnostics)
|
expectedDiagnostics := make(diagnostics)
|
||||||
completionItems := make(completionItems)
|
completionItems := make(completionItems)
|
||||||
|
@ -67,16 +68,6 @@ func testLSP(t *testing.T, exporter packagestest.Exporter) {
|
||||||
cfg.Mode = packages.LoadSyntax
|
cfg.Mode = packages.LoadSyntax
|
||||||
s.view.Config = &cfg
|
s.view.Config = &cfg
|
||||||
|
|
||||||
for _, module := range modules {
|
|
||||||
for fragment := range module.Files {
|
|
||||||
if !strings.HasSuffix(fragment, ".go") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
filename := exporter.Filename(exported, module.Name, fragment)
|
|
||||||
expectedDiagnostics[filename] = []protocol.Diagnostic{}
|
|
||||||
dirs[filepath.Dir(filename)] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Do a first pass to collect special markers
|
// Do a first pass to collect special markers
|
||||||
if err := exported.Expect(map[string]interface{}{
|
if err := exported.Expect(map[string]interface{}{
|
||||||
"item": func(name string, r packagestest.Range, _, _ string) {
|
"item": func(name string, r packagestest.Range, _, _ string) {
|
||||||
|
@ -98,32 +89,40 @@ func testLSP(t *testing.T, exporter packagestest.Exporter) {
|
||||||
|
|
||||||
t.Run("Completion", func(t *testing.T) {
|
t.Run("Completion", func(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
if len(expectedCompletions) != expectedCompletionsCount {
|
if goVersion111 { // TODO(rstambler): Remove this when we no longer support Go 1.10.
|
||||||
t.Errorf("got %v completions expected %v", len(expectedCompletions), expectedCompletionsCount)
|
if len(expectedCompletions) != expectedCompletionsCount {
|
||||||
|
t.Errorf("got %v completions expected %v", len(expectedCompletions), expectedCompletionsCount)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
expectedCompletions.test(t, exported, s, completionItems)
|
expectedCompletions.test(t, exported, s, completionItems)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Diagnostics", func(t *testing.T) {
|
t.Run("Diagnostics", func(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
diagnosticsCount := expectedDiagnostics.test(t, exported, s.view, dirs)
|
diagnosticsCount := expectedDiagnostics.test(t, exported, s.view)
|
||||||
if diagnosticsCount != expectedDiagnosticsCount {
|
if goVersion111 { // TODO(rstambler): Remove this when we no longer support Go 1.10.
|
||||||
t.Errorf("got %v diagnostics expected %v", diagnosticsCount, expectedDiagnosticsCount)
|
if diagnosticsCount != expectedDiagnosticsCount {
|
||||||
|
t.Errorf("got %v diagnostics expected %v", diagnosticsCount, expectedDiagnosticsCount)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Format", func(t *testing.T) {
|
t.Run("Format", func(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
if len(expectedFormat) != expectedFormatCount {
|
if goVersion111 { // TODO(rstambler): Remove this when we no longer support Go 1.10.
|
||||||
t.Errorf("got %v formats expected %v", len(expectedFormat), expectedFormatCount)
|
if len(expectedFormat) != expectedFormatCount {
|
||||||
|
t.Errorf("got %v formats expected %v", len(expectedFormat), expectedFormatCount)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
expectedFormat.test(t, s)
|
expectedFormat.test(t, s)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Definitions", func(t *testing.T) {
|
t.Run("Definitions", func(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
if len(expectedDefinitions) != expectedDefinitionsCount {
|
if goVersion111 { // TODO(rstambler): Remove this when we no longer support Go 1.10.
|
||||||
t.Errorf("got %v definitions expected %v", len(expectedDefinitions), expectedDefinitionsCount)
|
if len(expectedDefinitions) != expectedDefinitionsCount {
|
||||||
|
t.Errorf("got %v definitions expected %v", len(expectedDefinitions), expectedDefinitionsCount)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
expectedDefinitions.test(t, s)
|
expectedDefinitions.test(t, s)
|
||||||
})
|
})
|
||||||
|
@ -153,11 +152,11 @@ func (c completions) test(t *testing.T, exported *packagestest.Exported, s *serv
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatalf("completion failed for %s:%v:%v: %v", filepath.Base(src.Filename), src.Line, src.Column, err)
|
||||||
}
|
}
|
||||||
got := list.Items
|
got := list.Items
|
||||||
if equal := reflect.DeepEqual(want, got); !equal {
|
if equal := reflect.DeepEqual(want, got); !equal {
|
||||||
t.Errorf("completion failed for %s:%v:%v: (expected: %v), (got: %v)", filepath.Base(src.Filename), src.Line, src.Column, want, got)
|
t.Errorf(diffC(src, want, got))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -185,6 +184,8 @@ func (i completionItems) collect(pos token.Pos, label, detail, kind string) {
|
||||||
k = protocol.ConstantCompletion
|
k = protocol.ConstantCompletion
|
||||||
case "method":
|
case "method":
|
||||||
k = protocol.MethodCompletion
|
k = protocol.MethodCompletion
|
||||||
|
case "package":
|
||||||
|
k = protocol.ModuleCompletion
|
||||||
}
|
}
|
||||||
i[pos] = &protocol.CompletionItem{
|
i[pos] = &protocol.CompletionItem{
|
||||||
Label: label,
|
Label: label,
|
||||||
|
@ -193,50 +194,33 @@ func (i completionItems) collect(pos token.Pos, label, detail, kind string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d diagnostics) test(t *testing.T, exported *packagestest.Exported, v *source.View, dirs map[string]bool) int {
|
func (d diagnostics) test(t *testing.T, exported *packagestest.Exported, v *source.View) int {
|
||||||
// first trigger a load to get the diagnostics
|
|
||||||
var dirList []string
|
|
||||||
for dir := range dirs {
|
|
||||||
dirList = append(dirList, dir)
|
|
||||||
}
|
|
||||||
exported.Config.Mode = packages.LoadFiles
|
|
||||||
pkgs, err := packages.Load(exported.Config, dirList...)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
// and now see if they match the expected ones
|
|
||||||
count := 0
|
count := 0
|
||||||
for _, pkg := range pkgs {
|
for filename, want := range d {
|
||||||
for _, filename := range pkg.GoFiles {
|
f := v.GetFile(source.ToURI(filename))
|
||||||
f := v.GetFile(source.ToURI(filename))
|
sourceDiagnostics, err := source.Diagnostics(context.Background(), v, f)
|
||||||
diagnostics, err := source.Diagnostics(context.Background(), v, f)
|
if err != nil {
|
||||||
if err != nil {
|
t.Fatal(err)
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
got := toProtocolDiagnostics(v, diagnostics[filename])
|
|
||||||
sort.Slice(got, func(i int, j int) bool {
|
|
||||||
return got[i].Range.Start.Line < got[j].Range.Start.Line
|
|
||||||
})
|
|
||||||
want := d[filename]
|
|
||||||
if equal := reflect.DeepEqual(want, got); !equal {
|
|
||||||
msg := &bytes.Buffer{}
|
|
||||||
fmt.Fprintf(msg, "diagnostics failed for %s: expected:\n", filepath.Base(filename))
|
|
||||||
for _, d := range want {
|
|
||||||
fmt.Fprintf(msg, " %v\n", d)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(msg, "got:\n")
|
|
||||||
for _, d := range got {
|
|
||||||
fmt.Fprintf(msg, " %v\n", d)
|
|
||||||
}
|
|
||||||
t.Error(msg.String())
|
|
||||||
}
|
|
||||||
count += len(want)
|
|
||||||
}
|
}
|
||||||
|
got := toProtocolDiagnostics(v, sourceDiagnostics[filename])
|
||||||
|
sorted(got)
|
||||||
|
if equal := reflect.DeepEqual(want, got); !equal {
|
||||||
|
t.Error(diffD(filename, want, got))
|
||||||
|
}
|
||||||
|
count += len(want)
|
||||||
}
|
}
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d diagnostics) collect(pos token.Position, msg string) {
|
func (d diagnostics) collect(pos token.Position, msg string) {
|
||||||
|
if _, ok := d[pos.Filename]; !ok {
|
||||||
|
d[pos.Filename] = []protocol.Diagnostic{}
|
||||||
|
}
|
||||||
|
// If a file has an empty diagnostics, mark that and return. This allows us
|
||||||
|
// to avoid testing diagnostics in files that may have a lot of them.
|
||||||
|
if msg == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
line := float64(pos.Line - 1)
|
line := float64(pos.Line - 1)
|
||||||
col := float64(pos.Column - 1)
|
col := float64(pos.Column - 1)
|
||||||
want := protocol.Diagnostic{
|
want := protocol.Diagnostic{
|
||||||
|
@ -268,7 +252,7 @@ func (f formats) test(t *testing.T, s *server) {
|
||||||
if gofmted != "" {
|
if gofmted != "" {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
return
|
continue
|
||||||
}
|
}
|
||||||
edit := edits[0]
|
edit := edits[0]
|
||||||
if edit.NewText != gofmted {
|
if edit.NewText != gofmted {
|
||||||
|
@ -312,3 +296,31 @@ func (d definitions) collect(fset *token.FileSet, src, target packagestest.Range
|
||||||
tLoc := toProtocolLocation(fset, tRange)
|
tLoc := toProtocolLocation(fset, tRange)
|
||||||
d[sLoc] = tLoc
|
d[sLoc] = tLoc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// diffD prints the diff between expected and actual diagnostics test results.
|
||||||
|
func diffD(filename string, want, got []protocol.Diagnostic) string {
|
||||||
|
msg := &bytes.Buffer{}
|
||||||
|
fmt.Fprintf(msg, "diagnostics failed for %s:\nexpected:\n", filename)
|
||||||
|
for _, d := range want {
|
||||||
|
fmt.Fprintf(msg, " %v\n", d)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(msg, "got:\n")
|
||||||
|
for _, d := range got {
|
||||||
|
fmt.Fprintf(msg, " %v\n", d)
|
||||||
|
}
|
||||||
|
return msg.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// diffC prints the diff between expected and actual completion test results.
|
||||||
|
func diffC(pos token.Position, want, got []protocol.CompletionItem) string {
|
||||||
|
msg := &bytes.Buffer{}
|
||||||
|
fmt.Fprintf(msg, "completion failed for %s:%v:%v:\nexpected:\n", filepath.Base(pos.Filename), pos.Line, pos.Column)
|
||||||
|
for _, d := range want {
|
||||||
|
fmt.Fprintf(msg, " %v\n", d)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(msg, "got:\n")
|
||||||
|
for _, d := range got {
|
||||||
|
fmt.Fprintf(msg, " %v\n", d)
|
||||||
|
}
|
||||||
|
return msg.String()
|
||||||
|
}
|
||||||
|
|
|
@ -54,3 +54,30 @@ func (s DiagnosticSeverity) Format(f fmt.State, c rune) {
|
||||||
func (d Diagnostic) Format(f fmt.State, c rune) {
|
func (d Diagnostic) Format(f fmt.State, c rune) {
|
||||||
fmt.Fprintf(f, "%v:%v from %v at %v: %v", d.Severity, d.Code, d.Source, d.Range, d.Message)
|
fmt.Fprintf(f, "%v:%v from %v at %v: %v", d.Severity, d.Code, d.Source, d.Range, d.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i CompletionItem) Format(f fmt.State, c rune) {
|
||||||
|
fmt.Fprintf(f, "%v %v %v", i.Label, i.Detail, CompletionItemKind(i.Kind))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k CompletionItemKind) Format(f fmt.State, c rune) {
|
||||||
|
switch k {
|
||||||
|
case StructCompletion:
|
||||||
|
fmt.Fprintf(f, "struct")
|
||||||
|
case FunctionCompletion:
|
||||||
|
fmt.Fprintf(f, "func")
|
||||||
|
case VariableCompletion:
|
||||||
|
fmt.Fprintf(f, "var")
|
||||||
|
case TypeParameterCompletion:
|
||||||
|
fmt.Fprintf(f, "type")
|
||||||
|
case FieldCompletion:
|
||||||
|
fmt.Fprintf(f, "field")
|
||||||
|
case InterfaceCompletion:
|
||||||
|
fmt.Fprintf(f, "interface")
|
||||||
|
case ConstantCompletion:
|
||||||
|
fmt.Fprintf(f, "const")
|
||||||
|
case MethodCompletion:
|
||||||
|
fmt.Fprintf(f, "method")
|
||||||
|
case ModuleCompletion:
|
||||||
|
fmt.Fprintf(f, "package")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
type CompletionItem struct {
|
type CompletionItem struct {
|
||||||
Label, Detail string
|
Label, Detail string
|
||||||
Kind CompletionItemKind
|
Kind CompletionItemKind
|
||||||
Score int
|
Score float64
|
||||||
}
|
}
|
||||||
|
|
||||||
type CompletionItemKind int
|
type CompletionItemKind int
|
||||||
|
@ -47,6 +47,8 @@ func Completion(ctx context.Context, f *File, pos token.Pos) ([]CompletionItem,
|
||||||
return items, err
|
return items, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const stdScore float64 = 1.0
|
||||||
|
|
||||||
type finder func(types.Object, float64, []CompletionItem) []CompletionItem
|
type finder func(types.Object, float64, []CompletionItem) []CompletionItem
|
||||||
|
|
||||||
// completions returns the map of possible candidates for completion, given a
|
// completions returns the map of possible candidates for completion, given a
|
||||||
|
@ -89,7 +91,7 @@ func completions(file *ast.File, pos token.Pos, fset *token.FileSet, pkg *types.
|
||||||
if !seen[obj] {
|
if !seen[obj] {
|
||||||
seen[obj] = true
|
seen[obj] = true
|
||||||
if typ != nil && matchingTypes(typ, obj.Type()) {
|
if typ != nil && matchingTypes(typ, obj.Type()) {
|
||||||
weight *= 10
|
weight *= 10.0
|
||||||
}
|
}
|
||||||
if !strings.HasPrefix(obj.Name(), prefix) {
|
if !strings.HasPrefix(obj.Name(), prefix) {
|
||||||
return items
|
return items
|
||||||
|
@ -160,7 +162,7 @@ func selector(sel *ast.SelectorExpr, info *types.Info, found finder) (items []Co
|
||||||
scope := pkgname.Imported().Scope()
|
scope := pkgname.Imported().Scope()
|
||||||
// TODO testcase: bad import
|
// TODO testcase: bad import
|
||||||
for _, name := range scope.Names() {
|
for _, name := range scope.Names() {
|
||||||
items = found(scope.Lookup(name), 1, items)
|
items = found(scope.Lookup(name), stdScore, items)
|
||||||
}
|
}
|
||||||
return items, prefix, nil
|
return items, prefix, nil
|
||||||
}
|
}
|
||||||
|
@ -175,20 +177,20 @@ func selector(sel *ast.SelectorExpr, info *types.Info, found finder) (items []Co
|
||||||
// methods of T
|
// methods of T
|
||||||
mset := types.NewMethodSet(tv.Type)
|
mset := types.NewMethodSet(tv.Type)
|
||||||
for i := 0; i < mset.Len(); i++ {
|
for i := 0; i < mset.Len(); i++ {
|
||||||
items = found(mset.At(i).Obj(), 1, items)
|
items = found(mset.At(i).Obj(), stdScore, items)
|
||||||
}
|
}
|
||||||
|
|
||||||
// methods of *T
|
// methods of *T
|
||||||
if tv.Addressable() && !types.IsInterface(tv.Type) && !isPointer(tv.Type) {
|
if tv.Addressable() && !types.IsInterface(tv.Type) && !isPointer(tv.Type) {
|
||||||
mset := types.NewMethodSet(types.NewPointer(tv.Type))
|
mset := types.NewMethodSet(types.NewPointer(tv.Type))
|
||||||
for i := 0; i < mset.Len(); i++ {
|
for i := 0; i < mset.Len(); i++ {
|
||||||
items = found(mset.At(i).Obj(), 1, items)
|
items = found(mset.At(i).Obj(), stdScore, items)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fields of T
|
// fields of T
|
||||||
for _, f := range fieldSelections(tv.Type) {
|
for _, f := range fieldSelections(tv.Type) {
|
||||||
items = found(f, 1, items)
|
items = found(f, stdScore, items)
|
||||||
}
|
}
|
||||||
|
|
||||||
return items, prefix, nil
|
return items, prefix, nil
|
||||||
|
@ -236,7 +238,7 @@ func lexical(path []ast.Node, pos token.Pos, pkg *types.Package, info *types.Inf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
score := 1.0
|
score := stdScore
|
||||||
// Rank builtins significantly lower than other results.
|
// Rank builtins significantly lower than other results.
|
||||||
if scope == types.Universe {
|
if scope == types.Universe {
|
||||||
score *= 0.1
|
score *= 0.1
|
||||||
|
@ -342,7 +344,7 @@ func complit(path []ast.Node, pos token.Pos, pkg *types.Package, info *types.Inf
|
||||||
structPkg = field.Pkg()
|
structPkg = field.Pkg()
|
||||||
}
|
}
|
||||||
if !addedFields[field] {
|
if !addedFields[field] {
|
||||||
items = found(field, 10, items)
|
items = found(field, 10.0, items)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Add lexical completions if the user hasn't typed a key value expression
|
// Add lexical completions if the user hasn't typed a key value expression
|
||||||
|
@ -418,6 +420,7 @@ func formatCompletion(obj types.Object, qualifier types.Qualifier, score float64
|
||||||
Label: label,
|
Label: label,
|
||||||
Detail: detail,
|
Detail: detail,
|
||||||
Kind: kind,
|
Kind: kind,
|
||||||
|
Score: score,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package assign_rank
|
||||||
|
|
||||||
|
var (
|
||||||
|
apple int = 3 //@item(apple, "apple", "int", "var")
|
||||||
|
pear string = "hello" //@item(pear, "pear", "string", "var")
|
||||||
|
)
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
orange := 1 //@item(orange, "orange", "int", "var")
|
||||||
|
grape := "hello" //@item(grape, "grape", "string", "var")
|
||||||
|
orange, grape = 2, "hello" //@complete(" \"", grape, pear, orange, apple)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
var pineapple int //@item(pineapple, "pineapple", "int", "var")
|
||||||
|
pineapple = //@complete(" /", pineapple, apple, pear)
|
||||||
|
|
||||||
|
y := //@complete(" /", pineapple, apple, pear)
|
||||||
|
}
|
|
@ -1,18 +1,21 @@
|
||||||
|
// +build go1.11
|
||||||
|
|
||||||
package bad
|
package bad
|
||||||
|
|
||||||
func stuff() {
|
func stuff() { //@item(stuff, "stuff()", "", "func")
|
||||||
x := "heeeeyyyy"
|
x := "heeeeyyyy"
|
||||||
random2(x) //@diag("x", "cannot use x (variable of type string) as int value in argument to random2")
|
random2(x) //@diag("x", "cannot use x (variable of type string) as int value in argument to random2")
|
||||||
random2(1)
|
random2(1) //@complete("dom", random, random2, random3)
|
||||||
y := 3 //@diag("y", "y declared but not used")
|
y := 3 //@diag("y", "y declared but not used")
|
||||||
}
|
}
|
||||||
|
|
||||||
type bob struct {
|
type bob struct { //@item(bob, "bob", "struct{...}", "struct")
|
||||||
x int
|
x int
|
||||||
}
|
}
|
||||||
|
|
||||||
func _() {
|
func _() {
|
||||||
|
var q int
|
||||||
_ = &bob{
|
_ = &bob{
|
||||||
f: 0, //@diag("f", "unknown field f in struct literal")
|
f: q, //@diag("f", "unknown field f in struct literal")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,27 @@
|
||||||
|
// +build go1.11
|
||||||
|
|
||||||
package bad
|
package bad
|
||||||
|
|
||||||
func random2(y int) int {
|
// import (
|
||||||
x := 6 //@diag("x", "x declared but not used")
|
// "github.com/bob/pkg" //@diag("\"github.com/bob/pkg\"", "unable to import "\"github.com/bob/pkg\"")
|
||||||
|
// )
|
||||||
|
|
||||||
|
var a unknown //@item(global_a, "a", "unknown", "var"),diag("unknown", "undeclared name: unknown")
|
||||||
|
|
||||||
|
func random() int { //@item(random, "random()", "int", "func")
|
||||||
|
//@complete("", global_a, bob, random, random2, random3, stuff)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func random2(y int) int { //@item(random2, "random2(y int)", "int", "func"),item(bad_y_param, "y", "int", "var")
|
||||||
|
x := 6 //@item(x, "x", "int", "var"),diag("x", "x declared but not used")
|
||||||
|
var q blah //@item(q, "q", "blah", "var"),diag("q", "q declared but not used"),diag("blah", "undeclared name: blah")
|
||||||
|
var t blob //@item(t, "t", "blob", "var"),diag("t", "t declared but not used"),diag("blob", "undeclared name: blob")
|
||||||
|
//@complete("", q, t, x, bad_y_param, global_a, bob, random, random2, random3, stuff)
|
||||||
|
|
||||||
return y
|
return y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func random3(y ...int) { //@item(random3, "random3(y ...int)", "", "func"),item(y_variadic_param, "y", "[]int", "var")
|
||||||
|
//@complete("", y_variadic_param, global_a, bob, random, random2, random3, stuff)
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
package bar
|
|
||||||
|
|
||||||
import "golang.org/x/tools/internal/lsp/foo"
|
|
||||||
|
|
||||||
func Bar() {
|
|
||||||
foo.Foo()
|
|
||||||
}
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
// +build go1.11
|
||||||
|
|
||||||
|
package bar
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/tools/internal/lsp/foo" //@item(foo, "foo", "\"golang.org/x/tools/internal/lsp/foo\"", "package")
|
||||||
|
)
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
_ = foo.StructFoo{} //@complete("S", Foo, IntFoo, StructFoo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Bar() { //@item(Bar, "Bar()", "", "func")
|
||||||
|
foo.Foo() //@complete("F", Foo, IntFoo, StructFoo)
|
||||||
|
var _ foo.IntFoo //@complete("I", Foo, IntFoo, StructFoo)
|
||||||
|
foo.() //@complete("(", Foo, IntFoo, StructFoo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
var Valentine int //@item(Valentine, "Valentine", "int", "var")
|
||||||
|
|
||||||
|
_ = foo.StructFoo{
|
||||||
|
Val //@complete("l", Value)
|
||||||
|
}
|
||||||
|
_ = foo.StructFoo{
|
||||||
|
Va //@complete("a", Value)
|
||||||
|
}
|
||||||
|
_ = foo.StructFoo{
|
||||||
|
Value: 5, //@complete("a", Value)
|
||||||
|
}
|
||||||
|
_ = foo.StructFoo{
|
||||||
|
//@complete("", Value)
|
||||||
|
}
|
||||||
|
_ = foo.StructFoo{
|
||||||
|
Value: Valen //@complete("le", Valentine)
|
||||||
|
}
|
||||||
|
_ = foo.StructFoo{
|
||||||
|
Value: //@complete(re"$", Valentine, foo, Bar)
|
||||||
|
}
|
||||||
|
_ = foo.StructFoo{
|
||||||
|
Value: //@complete(" ", Valentine, foo, Bar)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +0,0 @@
|
||||||
package baz
|
|
||||||
|
|
||||||
import "golang.org/x/tools/internal/lsp/bar"
|
|
||||||
|
|
||||||
func Baz() {
|
|
||||||
bar.Bar()
|
|
||||||
}
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
// +build go1.11
|
||||||
|
|
||||||
|
package baz
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/tools/internal/lsp/bar"
|
||||||
|
|
||||||
|
f "golang.org/x/tools/internal/lsp/foo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Baz() {
|
||||||
|
defer bar.Bar() //@complete("B", Bar)
|
||||||
|
// TODO(rstambler): Test completion here.
|
||||||
|
defer bar.B
|
||||||
|
var _ f.IntFoo //@complete("n", IntFoo)
|
||||||
|
bar.Bar() //@complete("B", Bar)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
bob := f.StructFoo{Value: 5}
|
||||||
|
if x := bob. //@complete(re"$", Value)
|
||||||
|
switch true == false {
|
||||||
|
case true:
|
||||||
|
if x := bob. //@complete(re"$", Value)
|
||||||
|
case false:
|
||||||
|
}
|
||||||
|
if x := bob.Va //@complete("a", Value)
|
||||||
|
switch true == true {
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package cast
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
foo := struct{x int}{x: 1} //@item(x_field, "x", "int", "field")
|
||||||
|
_ = float64(foo.x) //@complete("x", x_field)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
foo := struct{x int}{x: 1}
|
||||||
|
_ = float64(foo. //@complete(" /", x_field)
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
package good
|
package good //@diag("package", "")
|
||||||
|
|
||||||
func stuff() {
|
func stuff() { //@item(good_stuff, "stuff()", "", "func")
|
||||||
x := 5
|
x := 5
|
||||||
random2(x)
|
random2(x)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,19 @@
|
||||||
package good
|
package good //@diag("package", "")
|
||||||
|
|
||||||
func random() int {
|
import (
|
||||||
|
"golang.org/x/tools/internal/lsp/types" //@item(types_import, "types", "\"golang.org/x/tools/internal/lsp/types\"", "package")
|
||||||
|
)
|
||||||
|
|
||||||
|
func random() int { //@item(good_random, "random()", "int", "func")
|
||||||
y := 6 + 7
|
y := 6 + 7
|
||||||
return y
|
return y
|
||||||
}
|
}
|
||||||
|
|
||||||
func random2(y int) int {
|
func random2(y int) int { //@item(good_random2, "random2(y int)", "int", "func"),item(good_y_param, "y", "int", "var")
|
||||||
|
//@complete("", good_y_param, types_import, good_random, good_random2, good_stuff)
|
||||||
|
var b types.Bob = &types.X{}
|
||||||
|
if _, ok := b.(*types.X); ok { //@complete("X", Bob_interface, X_struct, Y_struct)
|
||||||
|
}
|
||||||
|
|
||||||
return y
|
return y
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
// +build !go1.11
|
|
||||||
|
|
||||||
// This file does not actually test anything
|
|
||||||
// on 1.10 the errors are different
|
|
||||||
package noparse_format
|
|
||||||
|
|
||||||
func what() {
|
|
||||||
// we need a diagnostic below so we have the same count as the main file
|
|
||||||
var b int //@diag("b", "b declared but not used")
|
|
||||||
if true {}
|
|
||||||
}
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
// +build go1.11
|
||||||
|
|
||||||
|
package selector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/tools/internal/lsp/bar"
|
||||||
|
)
|
||||||
|
|
||||||
|
type S struct {
|
||||||
|
B, A, C int //@item(Bf, "B", "int", "field"),item(Af, "A", "int", "field"),item(Cf, "C", "int", "field")
|
||||||
|
}
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
_ = S{}.; //@complete(";", Bf, Af, Cf)
|
||||||
|
}
|
||||||
|
|
||||||
|
type bob struct { a int } //@item(a, "a", "int", "field")
|
||||||
|
type george struct { b int }
|
||||||
|
type jack struct { c int } //@item(c, "c", "int", "field")
|
||||||
|
type jill struct { d int }
|
||||||
|
|
||||||
|
func (b *bob) george() *george {} //@item(george, "george()", "*george", "method")
|
||||||
|
func (g *george) jack() *jack {}
|
||||||
|
func (j *jack) jill() *jill {} //@item(jill, "jill()", "*jill", "method")
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
b := &bob{}
|
||||||
|
y := b.george().
|
||||||
|
jack();
|
||||||
|
y.; //@complete(";", jill, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
bar. //@complete(" /", Bar)
|
||||||
|
x := 5
|
||||||
|
|
||||||
|
var b *bob
|
||||||
|
b. //@complete(" /", george, a)
|
||||||
|
y, z := 5, 6
|
||||||
|
|
||||||
|
b. //@complete(" /", george, a)
|
||||||
|
y, z, a, b, c := 5, 6
|
||||||
|
}
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
bar. //@complete(" /", Bar)
|
||||||
|
bar.Bar()
|
||||||
|
|
||||||
|
bar. //@complete(" /", Bar)
|
||||||
|
go f()
|
||||||
|
}
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
var b *bob
|
||||||
|
if y != b. //@complete(" /", george, a)
|
||||||
|
z := 5
|
||||||
|
|
||||||
|
if z + y + 1 + b. //@complete(" /", george, a)
|
||||||
|
r, s, t := 4, 5
|
||||||
|
|
||||||
|
if y != b. //@complete(" /", george, a)
|
||||||
|
z = 5
|
||||||
|
|
||||||
|
if z + y + 1 + b. //@complete(" /", george, a)
|
||||||
|
r = 4
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
type X struct { //@item(X_struct, "X", "struct{...}", "struct")
|
||||||
|
x int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Y struct { //@item(Y_struct, "Y", "struct{...}", "struct")
|
||||||
|
y int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bob interface { //@item(Bob_interface, "Bob", "interface{...}", "interface")
|
||||||
|
Bobby()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*X) Bobby() {}
|
||||||
|
func (*Y) Bobby() {}
|
Loading…
Reference in New Issue