internal/lsp: make interface methods children of the interface symbol
Change-Id: I8dbb1400a228ea077f63c2c48f8dba0ac93990d2
Reviewed-on: https://go-review.googlesource.com/c/tools/+/173237
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/lsp/source/symbols.go b/internal/lsp/source/symbols.go
index 1ce5fc8..c0dcaf4 100644
--- a/internal/lsp/source/symbols.go
+++ b/internal/lsp/source/symbols.go
@@ -68,7 +68,7 @@
switch spec := spec.(type) {
case *ast.TypeSpec:
if obj := info.ObjectOf(spec.Name); obj != nil {
- ts := typeSymbol(spec, obj, fset, q)
+ ts := typeSymbol(info, spec, obj, fset, q)
symbols = append(symbols, ts)
symbolsToReceiver[obj.Type()] = len(symbols) - 1
}
@@ -161,7 +161,7 @@
}
}
-func typeSymbol(spec *ast.TypeSpec, obj types.Object, fset *token.FileSet, q types.Qualifier) Symbol {
+func typeSymbol(info *types.Info, spec *ast.TypeSpec, obj types.Object, fset *token.FileSet, q types.Qualifier) Symbol {
s := Symbol{Name: obj.Name()}
s.Detail, _ = formatType(obj.Type(), q)
setKind(&s, obj.Type(), q)
@@ -193,6 +193,66 @@
}
}
+ ti, objIsInterface := obj.Type().Underlying().(*types.Interface)
+ ai, specIsInterface := spec.Type.(*ast.InterfaceType)
+ if objIsInterface && specIsInterface {
+ for i := 0; i < ti.NumExplicitMethods(); i++ {
+ method := ti.ExplicitMethod(i)
+ child := Symbol{
+ Name: method.Name(),
+ Kind: MethodSymbol,
+ }
+
+ var spanNode, selectionNode ast.Node
+ Methods:
+ for _, f := range ai.Methods.List {
+ for _, id := range f.Names {
+ if id.Name == method.Name() {
+ spanNode, selectionNode = f, id
+ break Methods
+ }
+ }
+ }
+ if span, err := nodeSpan(spanNode, fset); err == nil {
+ child.Span = span
+ }
+ if span, err := nodeSpan(selectionNode, fset); err == nil {
+ child.SelectionSpan = span
+ }
+ s.Children = append(s.Children, child)
+ }
+
+ for i := 0; i < ti.NumEmbeddeds(); i++ {
+ embedded := ti.EmbeddedType(i)
+ nt, isNamed := embedded.(*types.Named)
+ if !isNamed {
+ continue
+ }
+
+ child := Symbol{Name: types.TypeString(embedded, q)}
+ setKind(&child, embedded, q)
+ var spanNode, selectionNode ast.Node
+ Embeddeds:
+ for _, f := range ai.Methods.List {
+ if len(f.Names) > 0 {
+ continue
+ }
+
+ if t := info.TypeOf(f.Type); types.Identical(nt, t) {
+ spanNode, selectionNode = f, f.Type
+ break Embeddeds
+ }
+ }
+
+ if span, err := nodeSpan(spanNode, fset); err == nil {
+ child.Span = span
+ }
+ if span, err := nodeSpan(selectionNode, fset); err == nil {
+ child.SelectionSpan = span
+ }
+ s.Children = append(s.Children, child)
+ }
+ }
return s
}
diff --git a/internal/lsp/testdata/symbols/main.go b/internal/lsp/testdata/symbols/main.go
index 032f74e..b9ee77c 100644
--- a/internal/lsp/testdata/symbols/main.go
+++ b/internal/lsp/testdata/symbols/main.go
@@ -1,6 +1,8 @@
package main
-import "io"
+import (
+ "io"
+)
var x = 42 //@symbol("x", "x", "Variable", "")
@@ -39,5 +41,16 @@
}
type Stringer interface { //@symbol("Stringer", "Stringer", "Interface", "")
- String() string
+ String() string //@symbol("String", "String", "Method", "Stringer")
+}
+
+type ABer interface { //@symbol("ABer", "ABer", "Interface", "")
+ B() //@symbol("B", "B", "Method", "ABer")
+ A() string //@symbol("A", "A", "Method", "ABer")
+}
+
+type WithEmbeddeds interface { //@symbol("WithEmbeddeds", "WithEmbeddeds", "Interface", "")
+ Do() //@symbol("Do", "Do", "Method", "WithEmbeddeds")
+ ABer //@symbol("ABer", "ABer", "Interface", "WithEmbeddeds")
+ io.Writer //@symbol("io.Writer", "io.Writer", "Interface", "WithEmbeddeds")
}