diff --git a/internal/index/helper.go b/internal/index/helper.go index db9419ce..81e90fdf 100644 --- a/internal/index/helper.go +++ b/internal/index/helper.go @@ -14,6 +14,46 @@ import ( "golang.org/x/tools/go/packages" ) +// monikerIdentifier constructs a moniker identifier from an ident. We can't just use the +// ident name itself as this causes non-unique moniker generations (such as a String function +// and a struct that conforms to the Stringer interface). +func monikerIdentifier(f *ast.File, pkgPath string, ident *ast.Ident, obj types.Object) string { + // See if the ident is a signature with a receiver. If so, then we want to trim + // any leading * indicating a pointer receiver, then try to trim the package path. + // This constructs something like `{pkg}:{struct}.{method}`. + if signature, ok := obj.Type().(*types.Signature); ok { + if recv := signature.Recv(); recv != nil { + return fmt.Sprintf( + "%s:%s.%s", + pkgPath, + safeTrim(safeTrim(recv.Type().String(), "*"), pkgPath+"."), + ident.String(), + ) + } + } + + // See if the ident is a field of an outer type. If so, then we need to find the + // type spec that encloses it. This construct something like `{pkg}:{struct}.{field}`. + if v, ok := obj.(*types.Var); ok && v.IsField() { + if path, exact := astutil.PathEnclosingInterval(f, ident.Pos(), ident.Pos()); exact { + for _, node := range path { + if spec, ok := node.(*ast.TypeSpec); ok { + return fmt.Sprintf("%s:%s.%s", pkgPath, spec.Name.String(), ident.String()) + } + } + } + } + + return fmt.Sprintf("%s:%s", pkgPath, ident.String()) +} + +func safeTrim(s, prefix string) string { + if strings.HasPrefix(s, prefix) { + return s[len(prefix):] + } + return s +} + // lspRange transforms go/token.Position (1-based) to LSP start and end ranges (0-based) // which takes in consideration of identifier's name length. If the token is a quoted // package name, we'll create a range that covers only the string contents, not the quotes. diff --git a/internal/index/indexer.go b/internal/index/indexer.go index fd3eccbd..a76bf429 100644 --- a/internal/index/indexer.go +++ b/internal/index/indexer.go @@ -315,7 +315,7 @@ func (i *indexer) indexPkgUses(pkgs []*packages.Package, p *packages.Package, pr } i.usesIndexed[fpos.Filename] = true - if err := i.indexUses(pkgs, p, fi, fpos.Filename); err != nil { + if err := i.indexUses(pkgs, p, f, fi, fpos.Filename); err != nil { return fmt.Errorf("error indexing uses of %q: %v", p.PkgPath, err) } } @@ -461,7 +461,7 @@ func (i *indexer) indexDefs(pkgs []*packages.Package, p *packages.Package, f *as } if ident.IsExported() { - err := i.emitExportMoniker(refResult.resultSetID, fmt.Sprintf("%s:%s", p.PkgPath, ident.String())) + err := i.emitExportMoniker(refResult.resultSetID, monikerIdentifier(f, p.PkgPath, ident, obj)) if err != nil { return fmt.Errorf(`emit moniker": %v`, err) } @@ -484,7 +484,7 @@ func (i *indexer) indexDefs(pkgs []*packages.Package, p *packages.Package, f *as return nil } -func (i *indexer) indexUses(pkgs []*packages.Package, p *packages.Package, fi *fileInfo, filename string) error { +func (i *indexer) indexUses(pkgs []*packages.Package, p *packages.Package, f *ast.File, fi *fileInfo, filename string) error { var rangeIDs []string for ident, obj := range p.TypesInfo.Uses { // Only emit if the object belongs to current file @@ -550,7 +550,7 @@ func (i *indexer) indexUses(pkgs []*packages.Package, p *packages.Package, fi *f // If we don't have a definition in this package, emit an import moniker // so that we can correlate it with another dump's LSIF data. - err = i.emitImportMoniker(rangeID, fmt.Sprintf("%s:%s", pkg.Path(), obj.Id())) + err = i.emitImportMoniker(rangeID, monikerIdentifier(f, pkg.Path(), ident, obj)) if err != nil { return fmt.Errorf(`emit moniker": %v`, err) }