-
Notifications
You must be signed in to change notification settings - Fork 2.3k
/
Copy pathtype_hierarchy.go
157 lines (141 loc) · 4.83 KB
/
type_hierarchy.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package golang
import (
"context"
"fmt"
"go/token"
"go/types"
"slices"
"strings"
"sync"
"golang.org/x/tools/gopls/internal/cache"
"golang.org/x/tools/gopls/internal/cache/metadata"
"golang.org/x/tools/gopls/internal/cache/methodsets"
"golang.org/x/tools/gopls/internal/file"
"golang.org/x/tools/gopls/internal/protocol"
)
// Type hierarchy support (using method sets)
//
// TODO(adonovan):
// - Support type hierarchy by signatures (using Kind=Function).
// As with Implementations by signature matching, needs more UX thought.
//
// - Allow methods too (using Kind=Method)? It's not exactly in the
// spirit of TypeHierarchy but it would be useful and it's easy
// enough to support.
//
// FIXME: fix pkg=command-line-arguments problem with query initiated at "error" in builtins.go
// PrepareTypeHierarchy returns the TypeHierarchyItems for the types at the selected position.
func PrepareTypeHierarchy(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp protocol.Position) ([]protocol.TypeHierarchyItem, error) {
pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI())
if err != nil {
return nil, err
}
pos, err := pgf.PositionPos(pp)
if err != nil {
return nil, err
}
// For now, we require that the selection be a type name.
_, obj, _ := referencedObject(pkg, pgf, pos)
if obj == nil {
return nil, fmt.Errorf("not a symbol")
}
tname, ok := obj.(*types.TypeName)
if !ok {
return nil, fmt.Errorf("not a type name")
}
// Find declaration.
var declLoc protocol.Location
if isBuiltin(obj) {
pgf, id, err := builtinDecl(ctx, snapshot, obj)
if err != nil {
return nil, err
}
declLoc, err = pgf.NodeLocation(id)
if err != nil {
return nil, err
}
} else {
declLoc, err = mapPosition(ctx, pkg.FileSet(), snapshot, tname.Pos(), tname.Pos()+token.Pos(len(tname.Name())))
if err != nil {
return nil, err
}
}
pkgpath := "builtin"
if tname.Pkg() != nil {
pkgpath = tname.Pkg().Path()
}
return []protocol.TypeHierarchyItem{{
Name: tname.Name(),
Kind: cond(types.IsInterface(tname.Type()), protocol.Interface, protocol.Class),
Detail: pkgpath,
URI: declLoc.URI,
Range: declLoc.Range, // (in theory this should be the entire declaration)
SelectionRange: declLoc.Range,
}}, nil
}
// Subtypes reports information about subtypes of the selected type.
func Subtypes(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, item protocol.TypeHierarchyItem) ([]protocol.TypeHierarchyItem, error) {
return relatedTypes(ctx, snapshot, fh, item, methodsets.Subtype)
}
// Subtypes reports information about supertypes of the selected type.
func Supertypes(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, item protocol.TypeHierarchyItem) ([]protocol.TypeHierarchyItem, error) {
return relatedTypes(ctx, snapshot, fh, item, methodsets.Supertype)
}
// relatedTypes is the common implementation of {Super,Sub}types.
func relatedTypes(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, item protocol.TypeHierarchyItem, rel methodsets.TypeRelation) ([]protocol.TypeHierarchyItem, error) {
pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI())
if err != nil {
return nil, err
}
pos, err := pgf.PositionPos(item.Range.Start)
if err != nil {
return nil, err
}
var (
itemsMu sync.Mutex
items []protocol.TypeHierarchyItem
)
err = implementationsMsets(ctx, snapshot, pkg, pgf, pos, rel, func(pkgpath metadata.PackagePath, name string, abstract bool, loc protocol.Location) {
if pkgpath == "" {
pkgpath = "builtin"
}
itemsMu.Lock()
defer itemsMu.Unlock()
items = append(items, protocol.TypeHierarchyItem{
Name: name,
Kind: cond(abstract, protocol.Interface, protocol.Class),
Detail: string(pkgpath),
URI: loc.URI,
Range: loc.Range, // (in theory this should be the entire declaration)
SelectionRange: loc.Range,
})
})
if err != nil {
return nil, err
}
// Sort by (package, name, URI, range) then
// de-duplicate based on the same 4-tuple
cmp := func(x, y protocol.TypeHierarchyItem) int {
if d := strings.Compare(x.Detail, y.Detail); d != 0 {
// Rank the original item's package first.
if d := boolCompare(x.Detail == item.Detail, y.Detail == item.Detail); d != 0 {
return -d
}
return d
}
if d := strings.Compare(x.Name, y.Name); d != 0 {
return d
}
if d := strings.Compare(string(x.URI), string(y.URI)); d != 0 {
return d
}
return protocol.CompareRange(x.SelectionRange, y.Range)
}
slices.SortFunc(items, cmp)
eq := func(x, y protocol.TypeHierarchyItem) bool { return cmp(x, y) == 0 }
items = slices.CompactFunc(items, eq)
return items, nil
}