internal/lsp: add definition tests from godef

This makes our internal version of go to definition be tested with the
same test data that godef now uses

Change-Id: I04e488b6b9b2d891181f202ea1125b823a079c50
Reviewed-on: https://go-review.googlesource.com/c/150045
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
Ian Cottrell 2018-11-14 21:11:16 -05:00
Родитель 1aef897494
Коммит 6dfe7efaa9
10 изменённых файлов: 134 добавлений и 6 удалений

Просмотреть файл

@ -164,6 +164,7 @@ var (
posType = reflect.TypeOf(token.Pos(0))
positionType = reflect.TypeOf(token.Position{})
rangeType = reflect.TypeOf(Range{})
fsetType = reflect.TypeOf((*token.FileSet)(nil))
)
// converter converts from a marker's argument parsed from the comment to
@ -190,6 +191,10 @@ func (e *Exported) buildConverter(pt reflect.Type) (converter, error) {
return func(n *expect.Note, args []interface{}) (reflect.Value, []interface{}, error) {
return reflect.ValueOf(n), args, nil
}, nil
case pt == fsetType:
return func(n *expect.Note, args []interface{}) (reflect.Value, []interface{}, error) {
return reflect.ValueOf(e.fset), args, nil
}, nil
case pt == posType:
return func(n *expect.Note, args []interface{}) (reflect.Value, []interface{}, error) {
r, remains, err := e.rangeConverter(n, args)

Просмотреть файл

@ -29,8 +29,9 @@ func TestLSP(t *testing.T) {
func testLSP(t *testing.T, exporter packagestest.Exporter) {
const dir = "testdata"
const expectedCompletionsCount = 4
const expectedDiagnosticsCount = 7
const expectedDiagnosticsCount = 9
const expectedFormatCount = 3
const expectedDefinitionsCount = 16
files := packagestest.MustCopyFileTree(dir)
for fragment, operation := range files {
@ -55,6 +56,7 @@ func testLSP(t *testing.T, exporter packagestest.Exporter) {
completionItems := make(completionItems)
expectedCompletions := make(completions)
expectedFormat := make(formats)
expectedDefinitions := make(definitions)
s := &server{
view: source.NewView(),
@ -89,6 +91,7 @@ func testLSP(t *testing.T, exporter packagestest.Exporter) {
"item": completionItems.collect,
"complete": expectedCompletions.collect,
"format": expectedFormat.collect,
"godef": expectedDefinitions.collect,
}); err != nil {
t.Fatal(err)
}
@ -116,12 +119,21 @@ func testLSP(t *testing.T, exporter packagestest.Exporter) {
}
expectedFormat.test(t, s)
})
t.Run("Definitions", func(t *testing.T) {
t.Helper()
if len(expectedDefinitions) != expectedDefinitionsCount {
t.Errorf("got %v definitions expected %v", len(expectedDefinitions), expectedDefinitionsCount)
}
expectedDefinitions.test(t, s)
})
}
type diagnostics map[string][]protocol.Diagnostic
type completionItems map[token.Pos]*protocol.CompletionItem
type completions map[token.Position][]token.Pos
type formats map[string]string
type definitions map[protocol.Location]protocol.Location
func (c completions) test(t *testing.T, exported *packagestest.Exported, s *server, items completionItems) {
for src, itemList := range c {
@ -272,3 +284,31 @@ func (f formats) collect(pos token.Position) {
cmd.Run() // ignore error, sometimes we have intentionally ungofmt-able files
f[pos.Filename] = stdout.String()
}
func (d definitions) test(t *testing.T, s *server) {
for src, target := range d {
locs, err := s.Definition(context.Background(), &protocol.TextDocumentPositionParams{
TextDocument: protocol.TextDocumentIdentifier{
URI: src.URI,
},
Position: src.Range.Start,
})
if err != nil {
t.Fatal(err)
}
if len(locs) != 1 {
t.Errorf("got %d locations for definition, expected 1", len(locs))
}
if locs[0] != target {
t.Errorf("for %v got %v want %v", src, locs[0], target)
}
}
}
func (d definitions) collect(fset *token.FileSet, src, target packagestest.Range) {
sRange := source.Range{Start: src.Start, End: src.End}
sLoc := toProtocolLocation(fset, sRange)
tRange := source.Range{Start: target.Start, End: target.End}
tLoc := toProtocolLocation(fset, tRange)
d[sLoc] = tLoc
}

Просмотреть файл

@ -24,11 +24,11 @@ func fromProtocolLocation(v *source.View, loc protocol.Location) (source.Range,
}
// toProtocolLocation converts from a source range back to a protocol location.
func toProtocolLocation(v *source.View, r source.Range) protocol.Location {
tokFile := v.Config.Fset.File(r.Start)
file := v.GetFile(source.ToURI(tokFile.Name()))
func toProtocolLocation(fset *token.FileSet, r source.Range) protocol.Location {
tokFile := fset.File(r.Start)
uri := source.ToURI(tokFile.Name())
return protocol.Location{
URI: protocol.DocumentURI(file.URI),
URI: protocol.DocumentURI(uri),
Range: toProtocolRange(tokFile, r),
}
}

Просмотреть файл

@ -204,7 +204,7 @@ func (s *server) Definition(ctx context.Context, params *protocol.TextDocumentPo
if err != nil {
return nil, err
}
return []protocol.Location{toProtocolLocation(s.view, r)}, nil
return []protocol.Location{toProtocolLocation(s.view.Config.Fset, r)}, nil
}
func (s *server) TypeDefinition(context.Context, *protocol.TextDocumentPositionParams) ([]protocol.Location, error) {

11
internal/lsp/testdata/godef/a/a.go поставляемый Normal file
Просмотреть файл

@ -0,0 +1,11 @@
// A comment just to push the positions out
package a
type A string //@A
func Stuff() { //@Stuff
x := 5
Random2(x) //@godef("dom2", Random2)
Random() //@godef("()", Random)
}

23
internal/lsp/testdata/godef/a/random.go поставляемый Normal file
Просмотреть файл

@ -0,0 +1,23 @@
package a
func Random() int { //@Random
y := 6 + 7
return y
}
func Random2(y int) int { //@Random2,mark(RandomParamY, "y")
return y //@godef("y", RandomParamY)
}
type Pos struct {
x, y int //@mark(PosX, "x"),mark(PosY, "y")
}
func (p *Pos) Sum() int { //@mark(PosSum, "Sum")
return p.x + p.y //@godef("x", PosX)
}
func _() {
var p Pos
_ = p.Sum() //@godef("()", PosSum)
}

23
internal/lsp/testdata/godef/b/b.go поставляемый Normal file
Просмотреть файл

@ -0,0 +1,23 @@
package b
import "golang.org/x/tools/internal/lsp/godef/a"
type S1 struct { //@S1
F1 int //@mark(S1F1, "F1")
S2 //@godef("S2", S2), mark(S1S2, "S2")
a.A //@godef("A", A)
}
type S2 struct { //@S2
F1 string //@mark(S2F1, "F1")
F2 int //@mark(S2F2, "F2")
}
func Bar() {
a.Stuff() //@godef("Stuff", Stuff)
var x S1 //@godef("S1", S1)
_ = x.S2 //@godef("S2", S1S2)
_ = x.F1 //@godef("F1", S1F1)
_ = x.F2 //@godef("F2", S2F2)
_ = x.S2.F1 //@godef("F1", S2F1)
}

8
internal/lsp/testdata/godef/b/c.go поставляемый Normal file
Просмотреть файл

@ -0,0 +1,8 @@
package b
// This is the in-editor version of the file.
// The on-disk version is in c.go.saved.
var _ = S1{ //@godef("S1", S1)
F1: 99, //@godef("F1", S1F1)
}

7
internal/lsp/testdata/godef/b/c.go.saved поставляемый Normal file
Просмотреть файл

@ -0,0 +1,7 @@
package b
// This is the on-disk version of c.go, which represents
// the in-editor version of the file.
}

11
internal/lsp/testdata/godef/broken/unclosedIf.go.in поставляемый Normal file
Просмотреть файл

@ -0,0 +1,11 @@
package broken
import "fmt"
func unclosedIf() {
if false {
var myUnclosedIf string //@myUnclosedIf
fmt.Printf("s = %v\n", myUnclosedIf) //@godef("my", myUnclosedIf)
}
//@diag(EOF, "expected ';', found 'EOF'")
//@diag(EOF, "expected '}', found 'EOF'")