// Copyright 2019 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 source import ( "context" "go/types" "golang.org/x/tools/internal/lsp/protocol" "golang.org/x/tools/internal/lsp/telemetry" "golang.org/x/tools/internal/telemetry/log" errors "golang.org/x/xerrors" ) func (i *IdentifierInfo) Implementation(ctx context.Context) ([]protocol.Location, error) { ctx = telemetry.Package.With(ctx, i.pkg.ID()) impls, err := i.implementations(ctx) if err != nil { return nil, err } var locations []protocol.Location for _, impl := range impls { if impl.pkg == nil || len(impl.pkg.CompiledGoFiles()) == 0 { continue } file, _, err := i.Snapshot.View().FindPosInPackage(impl.pkg, impl.obj.Pos()) if err != nil { log.Error(ctx, "Error getting file for object", err) continue } ident, err := findIdentifier(i.Snapshot, impl.pkg, file, impl.obj.Pos()) if err != nil { log.Error(ctx, "Error getting ident for object", err) continue } decRange, err := ident.Declaration.Range() if err != nil { log.Error(ctx, "Error getting range for object", err) continue } locations = append(locations, protocol.Location{ URI: protocol.NewURI(ident.Declaration.URI()), Range: decRange, }) } return locations, nil } var ErrNotAnInterface = errors.New("not an interface or interface method") func (i *IdentifierInfo) implementations(ctx context.Context) ([]implementation, error) { var ( T *types.Interface method *types.Func ) switch obj := i.Declaration.obj.(type) { case *types.Func: method = obj if recv := obj.Type().(*types.Signature).Recv(); recv != nil { T, _ = recv.Type().Underlying().(*types.Interface) } case *types.TypeName: T, _ = obj.Type().Underlying().(*types.Interface) } if T == nil { return nil, ErrNotAnInterface } if T.NumMethods() == 0 { return nil, nil } // Find all named types, even local types (which can have methods // due to promotion). var ( allNamed []*types.Named pkgs = make(map[*types.Package]Package) ) for _, pkg := range i.Snapshot.KnownPackages(ctx) { pkgs[pkg.GetTypes()] = pkg info := pkg.GetTypesInfo() for _, obj := range info.Defs { // We ignore aliases 'type M = N' to avoid duplicate reporting // of the Named type N. if obj, ok := obj.(*types.TypeName); ok && !obj.IsAlias() { // We skip interface types since we only want concrete // implementations. if named, ok := obj.Type().(*types.Named); ok && !isInterface(named) { allNamed = append(allNamed, named) } } } } var ( impls []implementation seen = make(map[types.Object]bool) ) // Find all the named types that implement our interface. for _, U := range allNamed { var concrete types.Type = U if !types.AssignableTo(concrete, T) { // We also accept T if *T implements our interface. concrete = types.NewPointer(concrete) if !types.AssignableTo(concrete, T) { continue } } var obj types.Object = U.Obj() if method != nil { obj = types.NewMethodSet(concrete).Lookup(method.Pkg(), method.Name()).Obj() } if obj == method || seen[obj] { continue } seen[obj] = true impls = append(impls, implementation{ obj: obj, pkg: pkgs[obj.Pkg()], }) } return impls, nil } type implementation struct { // obj is the implementation, either a *types.TypeName or *types.Func. obj types.Object // pkg is the Package that contains obj's definition. pkg Package }