1 // Copyright 2019 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
16 "golang.org/x/tools/internal/event"
17 "golang.org/x/tools/internal/lsp/protocol"
18 "golang.org/x/xerrors"
21 func Implementation(ctx context.Context, snapshot Snapshot, f FileHandle, pp protocol.Position) ([]protocol.Location, error) {
22 ctx, done := event.Start(ctx, "source.Implementation")
25 impls, err := implementations(ctx, snapshot, f, pp)
29 var locations []protocol.Location
30 for _, impl := range impls {
31 if impl.pkg == nil || len(impl.pkg.CompiledGoFiles()) == 0 {
34 rng, err := objToMappedRange(snapshot, impl.pkg, impl.obj)
38 pr, err := rng.Range()
42 locations = append(locations, protocol.Location{
43 URI: protocol.URIFromSpanURI(rng.URI()),
47 sort.Slice(locations, func(i, j int) bool {
48 li, lj := locations[i], locations[j]
50 return protocol.CompareRange(li.Range, lj.Range) < 0
52 return li.URI < lj.URI
57 var ErrNotAType = errors.New("not a type name or method")
59 // implementations returns the concrete implementations of the specified
60 // interface, or the interfaces implemented by the specified concrete type.
61 func implementations(ctx context.Context, s Snapshot, f FileHandle, pp protocol.Position) ([]qualifiedObject, error) {
63 impls []qualifiedObject
64 seen = make(map[token.Position]bool)
68 qos, err := qualifiedObjsAtProtocolPos(ctx, s, f, pp)
72 for _, qo := range qos {
75 queryMethod *types.Func
78 switch obj := qo.obj.(type) {
81 if recv := obj.Type().(*types.Signature).Recv(); recv != nil {
82 queryType = ensurePointer(recv.Type())
85 queryType = ensurePointer(obj.Type())
89 return nil, ErrNotAType
92 if types.NewMethodSet(queryType).Len() == 0 {
96 // Find all named types, even local types (which can have methods
99 allNamed []*types.Named
100 pkgs = make(map[*types.Package]Package)
102 knownPkgs, err := s.KnownPackages(ctx)
106 for _, pkg := range knownPkgs {
107 pkgs[pkg.GetTypes()] = pkg
108 info := pkg.GetTypesInfo()
109 for _, obj := range info.Defs {
110 obj, ok := obj.(*types.TypeName)
111 // We ignore aliases 'type M = N' to avoid duplicate reporting
112 // of the Named type N.
113 if !ok || obj.IsAlias() {
116 if named, ok := obj.Type().(*types.Named); ok {
117 allNamed = append(allNamed, named)
122 // Find all the named types that match our query.
123 for _, named := range allNamed {
125 candObj types.Object = named.Obj()
126 candType = ensurePointer(named)
129 if !concreteImplementsIntf(candType, queryType) {
133 ms := types.NewMethodSet(candType)
135 // Skip empty interfaces.
139 // If client queried a method, look up corresponding candType method.
140 if queryMethod != nil {
141 sel := ms.Lookup(queryMethod.Pkg(), queryMethod.Name())
148 pos := fset.Position(candObj.Pos())
149 if candObj == queryMethod || seen[pos] {
155 impls = append(impls, qualifiedObject{
157 pkg: pkgs[candObj.Pkg()],
165 // concreteImplementsIntf returns true if a is an interface type implemented by
166 // concrete type b, or vice versa.
167 func concreteImplementsIntf(a, b types.Type) bool {
168 aIsIntf, bIsIntf := IsInterface(a), IsInterface(b)
170 // Make sure exactly one is an interface type.
171 if aIsIntf == bIsIntf {
175 // Rearrange if needed so "a" is the concrete type.
180 return types.AssignableTo(a, b)
183 // ensurePointer wraps T in a *types.Pointer if T is a named, non-interface
184 // type. This is useful to make sure you consider a named type's full method
186 func ensurePointer(T types.Type) types.Type {
187 if _, ok := T.(*types.Named); ok && !IsInterface(T) {
188 return types.NewPointer(T)
194 type qualifiedObject struct {
197 // pkg is the Package that contains obj's definition.
200 // node is the *ast.Ident or *ast.ImportSpec we followed to find obj, if any.
203 // sourcePkg is the Package that contains node, if any.
208 errBuiltin = errors.New("builtin object")
209 errNoObjectFound = errors.New("no object found")
212 // qualifiedObjsAtProtocolPos returns info for all the type.Objects
213 // referenced at the given position. An object will be returned for
214 // every package that the file belongs to, in every typechecking mode
216 func qualifiedObjsAtProtocolPos(ctx context.Context, s Snapshot, fh FileHandle, pp protocol.Position) ([]qualifiedObject, error) {
217 pkgs, err := s.PackagesForFile(ctx, fh.URI(), TypecheckAll)
221 // Check all the packages that the file belongs to.
222 var qualifiedObjs []qualifiedObject
223 for _, searchpkg := range pkgs {
224 astFile, pos, err := getASTFile(searchpkg, fh, pp)
228 path := pathEnclosingObjNode(astFile, pos)
232 var objs []types.Object
233 switch leaf := path[0].(type) {
235 // If leaf represents an implicit type switch object or the type
236 // switch "assign" variable, expand to all of the type switch's
238 if implicits, _ := typeSwitchImplicits(searchpkg, path); len(implicits) > 0 {
239 objs = append(objs, implicits...)
241 obj := searchpkg.GetTypesInfo().ObjectOf(leaf)
243 return nil, xerrors.Errorf("%w for %q", errNoObjectFound, leaf.Name)
245 objs = append(objs, obj)
247 case *ast.ImportSpec:
248 // Look up the implicit *types.PkgName.
249 obj := searchpkg.GetTypesInfo().Implicits[leaf]
251 return nil, xerrors.Errorf("%w for import %q", errNoObjectFound, ImportPath(leaf))
253 objs = append(objs, obj)
255 // Get all of the transitive dependencies of the search package.
256 pkgs := make(map[*types.Package]Package)
257 var addPkg func(pkg Package)
258 addPkg = func(pkg Package) {
259 pkgs[pkg.GetTypes()] = pkg
260 for _, imp := range pkg.Imports() {
261 if _, ok := pkgs[imp.GetTypes()]; !ok {
267 for _, obj := range objs {
268 if obj.Parent() == types.Universe {
269 return nil, xerrors.Errorf("%q: %w", obj.Name(), errBuiltin)
271 pkg, ok := pkgs[obj.Pkg()]
273 event.Error(ctx, fmt.Sprintf("no package for obj %s: %v", obj, obj.Pkg()), err)
276 qualifiedObjs = append(qualifiedObjs, qualifiedObject{
279 sourcePkg: searchpkg,
284 // Return an error if no objects were found since callers will assume that
285 // the slice has at least 1 element.
286 if len(qualifiedObjs) == 0 {
287 return nil, errNoObjectFound
289 return qualifiedObjs, nil
292 func getASTFile(pkg Package, f FileHandle, pos protocol.Position) (*ast.File, token.Pos, error) {
293 pgf, err := pkg.File(f.URI())
297 spn, err := pgf.Mapper.PointSpan(pos)
301 rng, err := spn.Range(pgf.Mapper.Converter)
305 return pgf.File, rng.Start, nil
308 // pathEnclosingObjNode returns the AST path to the object-defining
309 // node associated with pos. "Object-defining" means either an
310 // *ast.Ident mapped directly to a types.Object or an ast.Node mapped
311 // implicitly to a types.Object.
312 func pathEnclosingObjNode(f *ast.File, pos token.Pos) []ast.Node {
318 ast.Inspect(f, func(n ast.Node) bool {
324 path = path[:len(path)-1]
328 path = append(path, n)
330 switch n := n.(type) {
332 // Include the position directly after identifier. This handles
333 // the common case where the cursor is right after the
334 // identifier the user is currently typing. Previously we
335 // handled this by calling astutil.PathEnclosingInterval twice,
336 // once for "pos" and once for "pos-1".
337 found = n.Pos() <= pos && pos <= n.End()
338 case *ast.ImportSpec:
339 if n.Path.Pos() <= pos && pos < n.Path.End() {
341 // If import spec has a name, add name to path even though
342 // position isn't in the name.
344 path = append(path, n.Name)
348 // Follow star expressions to the inner identifier.
352 case *ast.SelectorExpr:
353 // If pos is on the ".", move it into the selector.
354 if pos == n.X.End() {
366 // Reverse path so leaf is first element.
367 for i := 0; i < len(path)/2; i++ {
368 path[i], path[len(path)-1-i] = path[len(path)-1-i], path[i]