Open koonix opened 1 month ago
Related Issues and Documentation
(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)
Thanks for the bug report; I can reproduce it with your test case. (The functions fn
and main
are superfluous, BTW.)
According to the documentation, implementation(A) should indeed include B, since B implements A, so this is definitely a bug. However, implementation(B) is correct not to include A, since A does not implement B.
type A interface { // impls={X} (should include B)
Read([]byte) (int, error)
}
type B interface { // impls={} (correct)
Read([]byte) (int, error)
Extra()
}
type X int // impls={A} (correct)
func (x X) Read([]byte) (int, error) { return 0, nil }
Thanks for your reply.
implementation(B) is correct not to include A, since A does not implement B
I understand that this is true according to the documentation, but could we change this?
What I was trying to show in main()
was that the relationship between B and A is practically similar to that of X and A, and if implementation(X) contains A, perhaps implementation(B) should too.
In other words, concrete types can only implement, but interfaces can both implement and be implemented.
I understand that the textDocument/implementation
request might not be a good place for this (backwards compatibility? not matching Microsoft's LSP spec?), but it would be nice to somehow be able to see what other interfaces an interface implements.
@adonovan Is this a problem I can try to fix? I've been idle lately. 0.0
[@koonix] I understand that this is true according to the documentation, but could we change this? [...] concrete types can only implement, but interfaces can both implement and be implemented.
Certainly we could, and potentially even within the existing textDocument/implementation
operation, if there's a consensus that this is the right change. The LSP spec gives very little guidance on what the implementation
of a symbol actually means and what the operation is supposed to do. For example, the wording is loose enough to permit reporting the definition (as opposed to declaration) of a function symbol in C++, which distinguishes them. But I'm not yet convinced it is the right change: when I ask for all implementations of an interface, I would be surprised to see all interfaces that it implements as well. Unfortunately the LSP does not provide a pair of "upwards" and "downwards" operations for the respective halves of the query, and there is currently no way to generalize references
-like queries. Perhaps this warrants a separate discussion.
[@tttoad] Is this a problem I can try to fix?
If you're referring to the bug portion of the issue (that implementations(A) should include B), then yes, we would welcome a fix. Thanks.
Change https://go.dev/cl/603075 mentions this issue: gopls/implementation: Fix the query function when the implementation on the interface is a different interface.
Too much noise? Maybe we should sort, local.struct
>other.struct
>local.interface
>local.interface
? @adonovan
Based on the description of the Go to Implementation feature in the VS Code manual, and despite my comment in the source, I'm no longer convinced that the gopls behavior is wrong. The manual says that when triggered by an interface type, it reports concrete types (subtypes), and when triggered by a concrete type, it reports interfaces (supertypes). That's what gopls does.
Of course, in Go, interfaces may have supertypes (also interfaces). And in languages like Java, concrete types may have subtypes (also concrete types). It would be useful in both these languages for LSP to separate the queries for supertypes and subtypes. But they do need to be separate queries: it makes no sense to report a mixture within a single operation.
One way for LSP to do this would be to support generalized reference queries.
Change https://go.dev/cl/605395 mentions this issue: gopls/references: Fix the query function when the implementation on the interface is a different interface.
gopls version
go env
What did you do?
What did you see happen?
implementation
behaves like so:What did you expect to see?
Much like the relationship between A and X, A is implemented by B, and B implements A. Therefore:
Editor and settings
No response
Logs
No response