-
Notifications
You must be signed in to change notification settings - Fork 20
Conversation
internal/indexer/indexer.go
Outdated
name := dep.Name | ||
name = strings.TrimPrefix(name, "https://") | ||
name = strings.TrimPrefix(name, "https:/") | ||
pkgs, err := packages.Load(config, name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lol @tjdevries you were right
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
of course, but about what? 😆 We saw this while we were testing stuff out today and figured we had to do something like this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I gave you crap about us only calling package.Load
once and future proofing with that cache 😃
internal/indexer/indexer.go
Outdated
|
||
start1 := time.Now() | ||
localInterfaces, localConcreteTypes := extractTypes(i.packages) | ||
remoteInterfaces, remoteConcreteTypes := extractTypes(deps) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'd still be somewhat interested in us seeing if we can calculate the implementations on a per-dependency basis (and then parallelize that via go funcs) and see if we get any noticeable performance improvements on large repos.
internal/indexer/indexer.go
Outdated
if shouldInvert { | ||
relation = invert(relation) | ||
leftDefs, rightDefs = rightDefs, leftDefs | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have been wondering a bit about whether we should teach the backend that this language is reciprocal or whether we should actually emit both ways.
For example, if we say "struct X implements interface Y", is it always true that "interface Y is implemented by X"?
If so, then we do not need to emit everything for both directions. We can simply have:
X --textDocument/implement --> implementationResult --item--> Y
And the backend must handle the face that the reverse relationship is true as well.
cc @efritz so we can chat about this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The relationship should be symmetric (imo, just by intuition). I was chatting with @chrismwendt about this and I think it might have some tendrils in the fact that we need to emit relationships for both:
- interfaces defined in a package and a conforming struct defined in a dependency, and
- structs defined in a package and a descriptive interface of that struct defined in a dependency
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are defs/refs symmetric, too? (I think so) If so, should indexers only emit one direction and the backend fill in the other? If not, there are probably some practical or principled reason(s) why that can help us decide whether or not implementations should follow the same model.
This question applies to implementation relationships within an LSIF dump, but not across dumps because cross-dump find-implementations uses monikers rather than textDocument/implementation
et al.
58a2d9f
to
a17e464
Compare
internal/indexer/indexer.go
Outdated
if err != nil { | ||
errs <- errors.Wrap(err, "packages.Load") | ||
return | ||
load := func(cache map[string][]*packages.Package, patterns []string) ([]*packages.Package, bool) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
load := func(cache map[string][]*packages.Package, patterns []string) ([]*packages.Package, bool) { | |
load := func(cache map[string][]*packages.Package, patterns ...string) ([]*packages.Package, bool) { |
this would be way cooler 😎
internal/indexer/indexer.go
Outdated
|
||
// listProjectDependencies finds any packages from "$ go list all" that are NOT declared | ||
// as part of the current project. | ||
func (i *Indexer) listProjectDependencies() ([]string, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couple comments related to this method (but not exactly about this method):
- this might be able to go away if
dependencies
fits your use-case here (it should) - this should be called pre-indexing if it stays because all shell commands should happen in main; indexer shouldn't have to know much about the OS
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did anything here change regarding the dependencies thing from Chris's other comment?
I can move this to the pre-indexing stage regardless
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah so it looks like we shouldn't use -m
for these in particular, but it would be cool to have this moved into the gomod
package and called from main and passed to the indexer instead.
// universeParent := toMethod.Obj().(*types.Func).Type().(*types.Signature).Recv().Type().String() | ||
|
||
pkg := obj.Pkg() | ||
if pkg == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comments here would be real neat :)
internal/indexer/indexer.go
Outdated
errs <- errors.Wrap(err, "failed to list dependencies") | ||
return | ||
} | ||
i.depPackages, hasError = load(cachedDepPackages, deps) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this implicitly done by loading ./...
?
internal/indexer/implementation.go
Outdated
|
||
// filterToExported removes any nonExported types or identifiers from a list of []implDef | ||
func filterToExported(defs []implDef) []implDef { | ||
filtered := []implDef{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
filtered := []implDef{} | |
filtered := defs[:0] |
If this can be done in place:
internal/indexer/implementation.go
Outdated
localTypesToRemoteInterfaces := i.buildImplementationRelation(localConcreteTypes, filterToExported(remoteInterfaces)) | ||
localTypesToRemoteInterfaces.forEachImplementation(i.emitRemoteImplementation) | ||
|
||
localInterfacesToRemoteTypes := invert(i.buildImplementationRelation(filterToExported(remoteConcreteTypes), localInterfaces)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can each of these blocks have a short comment?
internal/indexer/implementation.go
Outdated
// Find all the concrete types that implement this interface. | ||
// Types that implement this interface are the intersection | ||
// of all sets of receivers of all methods in this interface. | ||
candidateTypes := &intsets.Sparse{} | ||
|
||
if initialReceivers, ok := methodToReceivers[canonical(methods[0])]; !ok { | ||
continue | ||
} else { | ||
candidateTypes.Copy(initialReceivers) | ||
} | ||
|
||
for _, method := range methods[1:] { | ||
receivers, ok := methodToReceivers[canonical(method)] | ||
if !ok { | ||
continue interfaceLoop | ||
} | ||
|
||
candidateTypes.IntersectionWith(receivers) | ||
if candidateTypes.IsEmpty() { | ||
continue interfaceLoop | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wish there was a cleaner way to write this as an obvious fold. I think the underlying logic here is really beautiful but the syntax makes it a bit messy and really obscures the intent and simplicity behind it 😞
So this is one thing I'm not sure about -- putting here to talk about w/ @efritz and/or @chrismwendt at some point. |
This PR creates the issue: #208 |
This makes lsif-go emit interface/type implementation data necessary to support find-implementations.
textDocument/implementation
for each pair (interface, concrete type) that matchestextDocumentation/implementation
s for methods + cross repoBenchmarks
Indexing sourcegraph/sourcegraph:
lsif-go@master
: took 20s wall 45s user 336MB dumplsif-go@implementations
: took 29s wall 69s user 339MB dump (8s to load deps, 0.5s to index implementations)Indexing aws/aws-sdk-go:
lsif-go@master
: took 38s wall 106s user 2.347GB dumplsif-go@implementations
: took 45s wall 123s user 2.386GB dump (1.5s to load deps, 2.7s to index implementations)Conclusion: ~25% slower, ~1% bigger dumps