haskell-language-server
haskell-language-server copied to clipboard
incoming / outgoing call hierarchy for local identifiers
Your environment
Which OS do you use?
Fedora/Linux
Which version of GHC do you use and how did you install it?
ghc-9.6.2
How is your project built (alternative: link to the project)?
I think it's irrelevant, I've seen this in various projects.
Which LSP client (editor/plugin) do you use?
Neovim with the build in lsp
Which version of HLS do you use and how did you install it?
HLS 2.3.0.0 installed with ghcup
Have you configured HLS in any way (especially: a hie.yaml file)?
no
Steps to reproduce
Ask for the incoming (or outgoing) call hierarchy of a local identifier, e.g. one defined in a where clause.
Expected behaviour
Return call hierarchy.
Actual behaviour
HLS fails with:
2023-10-08T09:54:19.072739Z | Error | LSP: incoming message parse error:
Error in $.params: parsing CallHierarchyIncomingCallsParams failed, expected Object, but encountered Array
when processing
{"method":"callHierarchy\/incomingCalls","id":7,"params":[],"jsonrpc":"2.0"}
Debug information
The ~callHierarchy/incomingCalls~ callHierarchy/prepareCallHierarchy returns an empty array {} . It is send back to callHierarchy/incomingCalls as params. I think that there's a bug in neovim lsp which encodes the empty array {} as an empty list instead of an empty object (the neovim lsp is written in lua, which doesn't make a distinction between lists & objects); but also a hls problem: why callHierarchy/prepareCallHierarchy returns {} in the first place.
The client side log:
[DEBUG][2023-10-08 11:54:14] .../lua/vim/lsp.lua:1391 "LSP[hls]" "client.request" 1 "textDocument/prepareCallHierarchy" { position = { character = 4, line = 864 }, textDocument = { uri = "file:///home/coot/src/haskell/ghc-tags-plugin/ghc-tags-core/lib/GhcTags/Ghc.hs" }} <function 1> 1
[DEBUG][2023-10-08 11:54:14] .../vim/lsp/rpc.lua:284 "rpc.send" { id = 6, jsonrpc = "2.0", method = "textDocument/prepareCallHierarchy", params = { position = { character = 4, line = 864 }, textDocument = { uri = "file:///home/coot/src/haskell/ghc-tags-plugin/ghc-tags-core/lib/GhcTags/Ghc.hs" } }}
[DEBUG][2023-10-08 11:54:14] .../vim/lsp/rpc.lua:387 "rpc.receive" { id = 6, jsonrpc = "2.0", result = {}}
[ERROR][2023-10-08 11:54:14] .../vim/lsp/rpc.lua:734 "rpc" "/home/coot/.ghcup/bin/haskell-language-server-wrapper" "stderr" "2023-10-08T09:54:14.889113Z | Debug | Finished: CallHierarchy.prepareHierarchy Took: 0.00s\n"
[DEBUG][2023-10-08 11:54:19] .../lua/vim/lsp.lua:1391 "LSP[hls]" "client.request" 1 "callHierarchy/incomingCalls" {} <function 1> 1
[DEBUG][2023-10-08 11:54:19] .../vim/lsp/rpc.lua:284 "rpc.send" { id = 7, jsonrpc = "2.0", method = "callHierarchy/incomingCalls", params = {}}
[ERROR][2023-10-08 11:54:19] .../vim/lsp/rpc.lua:734 "rpc" "/home/coot/.ghcup/bin/haskell-language-server-wrapper" "stderr" '2023-10-08T09:54:19.072997Z | Error | LSP: incoming message parse error:\nError in $.params: parsing CallHierarchyIncomingCallsParams failed, expected Object, but encountered Array\nwhen processing\n{"method":"callHierarchy\\/incomingCalls","id":7,"params":[],"jsonrpc":"2.0"}\n'
[DEBUG][2023-10-08 11:54:19] .../vim/lsp/rpc.lua:387 "rpc.receive" { jsonrpc = "2.0", method = "window/showMessage", params = { message = 'Error condition, please check your setup and/or the [issue tracker](https://github.com/haskell/haskell-language-server/issues): \nLSP: incoming message parse error:\nError in $.params: parsing CallHierarchyIncomingCallsParams failed, expected Object, but encountered Array\nwhen processing\n{"method":"callHierarchy\\/incomingCalls","id":7,"params":[],"jsonrpc":"2.0"}', type = 1 }}
I asked phind if hie files contain information about local identifiers. I don't know how close to truth the AI bot is, but it would explain why I am getting the error.
The callHierarchy/incomingCalls returns an empty array {} . It is send back to callHierarchy/incomingCalls as params
No, it's the other way around. The client sends the callHierarchy/incomingCalls request. It's sending an empty array as the params which is definitely wrong, it should be a CallHierarchyIncomingCallsParams object. So that's a bug in the client.
The HLS issue is that the textDocument/prepareCallHierarchy request returns nothing, which means there are no items to ask for incoming/outgoing calls to, and certainly none for local identifiers, probably indeed due to the fact that this works off HIE files.
Just to check that your setup isn't totally broken: does it work on non-local identifiers?
No, it's the other way around.
I made a mistake when writing the message. It's indeed the callHerirarchy/prepareCallHierarchy that returns something that is interpreted by the client as an empty array (which indeed seems like a bug on the client side).
Just to check that your setup isn't totally broken: does it work on non-local identifiers?
Yes, it works just fine.
It's indeed the callHerirarchy/prepareCallHierarchy that returns something that is interpreted by the client as an empty array (which indeed seems like a bug on the client side).
Hmm. Looking at the log you posted:
"rpc.receive" { id = 6, jsonrpc = "2.0", result = {}}
There are two weird things about this:
- There are no results!
- It's showing
resultas an empty object when it should be an array
So either:
- We're returning some results, and the client is messing up somehow and ending up with an empty object
- We're just not returning any results (weird?) and maybe also we're sending the results back as an object?
It would be helpful to know if that client log is showing the actual JSON that was received.
It's showing result as an empty object when it should be an array.
noevim lsp client is written in lua, which only has associative arrays. Empty object and empty list are both represented as {}.
The problem is that neovim's lsp tries to call callHierarchy/IncomingCalls with params being null (or something of that sort) when no item was resolved with callHierarchy/prepareCallHierarchy.
The question is why sometimes callHierarchy/prepareCallHierarchy returns no results; after restarting the editor this can go away - it could be a caching problem.
The problem is that neovim's lsp tries to call callHierarchy/IncomingCalls with params being null (or something of that sort) when no item was resolved with callHierarchy/prepareCallHierarchy.
In theory JSON-RPC says params can be null, but in practice it's basically always nonsense, so I'm pretty happy with us returning an error if this happens. I don't think neovim should do this.
The question is why sometimes callHierarchy/prepareCallHierarchy returns no results; after restarting the editor this can go away - it could be a caching problem.
If you can find any kinds of programs where this happens repeatably that would be a great help. As a random guess, we are maybe able to do something for top-level identifiers in more cases since they appear in HIE files. There might be some things we can only handle if we have a typechecked module, which is easier to break.