zigtools / zls

A Zig language server supporting Zig developers with features like autocomplete and goto definition
MIT License
2.81k stars 282 forks source link

Saving file multiple times creates a redeclaration error #1537

Closed Sirius902 closed 2 weeks ago

Sirius902 commented 10 months ago

Zig Version

0.12.0-dev.1101+25400fadf

Zig Language Server Version

e4593da63e57f83d40507b958c0b81125dd55156

Steps to Reproduce

I am unsure if this is an issue with ZLS directly or the Neovim LSP implementation, but I thought I'd try here first since ZLS outputs an error.

Expected Behavior

No errors will be generated and the diagnostics will be exactly the same.

Actual Behavior

Upon saving a second time, there will be a redeclaration of 'ArrayList' diagnostic displayed in the editor and an error is outputted from ZLS (shown in the logs below). Every time the file is saved after that point it will generate an extra error and diagnostic.

image

Raw LspLog from Neovim

[START][2023-10-19 17:14:18] LSP logging initiated
[ERROR][2023-10-19 17:14:18] .../vim/lsp/rpc.lua:734    "rpc"   "/usr/sbin/zls" "stderr"    "info : ( main ): Starting ZLS 0.12.0-dev.156+e4593da @ '/usr/sbin/zls'\ninfo : ( main ): No config file zls.json found.\ninfo : (server): set config option 'builtin_path' to '/home/blah/.cache/zls/builtin.zig'\ninfo : (server): set config option 'zig_lib_path' to '/usr/lib/zig/lib'\ninfo : (server): set config option 'zig_exe_path' to '/usr/sbin/zig'\ninfo : (server): set config option 'build_runner_path' to '/home/blah/.cache/zls/build_runner_master.zig'\ninfo : (server): set config option 'global_cache_path' to '/home/blah/.cache/zls'\ninfo : (server): set config option 'build_runner_global_cache_path' to '/home/blah/.cache/zig'\ninfo : (server): client is 'Neovim-0.9.4'\ninfo : (server): Server.ClientCapabilities{ .supports_snippets = true, .supports_apply_edits = true, .supports_will_save = true, .supports_will_save_wait_until = true, .supports_publish_diagnostics = true, .supports_code_action_fixall = false, .hover_supports_md = true, .completion_doc_supports_md = true, .label_details_support = true, .supports_configuration = true, .supports_workspace_did_change_configuration_dynamic_registration = false, .supports_textDocument_definition_linkSupport = true, .include_at_in_builtins = false, .max_detail_length = 1048576, .workspace_folders = {  } }\ninfo : (server): offset encoding: utf-16\n"
[ERROR][2023-10-19 17:15:04] .../vim/lsp/rpc.lua:734    "rpc"   "/usr/sbin/zls" "stderr"    "error: (server): failed to process notification-textDocument/didChange: error.InvalidParams\n???:?:?: 0x30fe41 in ??? (exe)\n???:?:?: 0x30fe4c in ??? (exe)\n???:?:?: 0x30fe54 in ??? (exe)\n???:?:?: 0x30ff3b in ??? (exe)\n"
[ERROR][2023-10-19 17:15:05] .../vim/lsp/rpc.lua:734    "rpc"   "/usr/sbin/zls" "stderr"    "error: (server): failed to process notification-textDocument/didChange: error.InvalidParams\n???:?:?: 0x30fe41 in ??? (exe)\n???:?:?: 0x30fe4c in ??? (exe)\n???:?:?: 0x30fe54 in ??? (exe)\n???:?:?: 0x30ff3b in ??? (exe)\n"

Cleaned up ZLS output from the above log:

info : ( main ): Starting ZLS 0.12.0-dev.156+e4593da @ '/usr/sbin/zls'
info : ( main ): No config file zls.json found.
info : (server): set config option 'builtin_path' to '/home/blah/.cache/zls/builtin.zig'
info : (server): set config option 'zig_lib_path' to '/usr/lib/zig/lib'
info : (server): set config option 'zig_exe_path' to '/usr/sbin/zig'
info : (server): set config option 'build_runner_path' to '/home/blah/.cache/zls/build_runner_master.zig'
info : (server): set config option 'global_cache_path' to '/home/blah/.cache/zls'
info : (server): set config option 'build_runner_global_cache_path' to '/home/blah/.cache/zig'
info : (server): client is 'Neovim-0.9.4'
info : (server): Server.ClientCapabilities{ .supports_snippets = true, .supports_apply_edits = true, .supports_will_save = true, .supports_will_save_wait_until = true, .supports_publish_diagnostics = true, .supports_code_action_fixall = false, .hover_supports_md = true, .completion_doc_supports_md = true, .label_details_support = true, .supports_configuration = true, .supports_workspace_did_change_configuration_dynamic_registration = false, .supports_textDocument_definition_linkSupport = true, .include_at_in_builtins = false, .max_detail_length = 1048576, .workspace_folders = {  } }
info : (server): offset encoding: utf-16
error: (server): failed to process notification-textDocument/didChange: error.InvalidParams
???:?:?: 0x30fe41 in ??? (exe)
???:?:?: 0x30fe4c in ??? (exe)
???:?:?: 0x30fe54 in ??? (exe)
???:?:?: 0x30ff3b in ??? (exe)
error: (server): failed to process notification-textDocument/didChange: error.InvalidParams
???:?:?: 0x30fe41 in ??? (exe)
???:?:?: 0x30fe4c in ??? (exe)
???:?:?: 0x30fe54 in ??? (exe)
???:?:?: 0x30ff3b in ??? (exe)
Techatrix commented 10 months ago

Could you use a debug build of ZLS so that we get the error return trace. Could you also check if your zig file has been saved as LF instead of CRLF? If you happen to be using the ziglang/zig.vim plugin, could you also check if let g:zig_fmt_autosave = 0 prevents the issue from occurring?

Sirius902 commented 10 months ago

Sure! I'll have the log from a debug build below. My editor is configured to save as LF and I used xxd to verify it is indeed saving as LF. It seems like the issue does not occur with let g:zig_fmt_autosave = 0.

00000000: 7075 6220 636f 6e73 7420 4172 7261 794c  pub const ArrayL
00000010: 6973 7420 3d20 4069 6d70 6f72 7428 2273  ist = @import("s
00000020: 7464 2229 2e41 7272 6179 4c69 7374 3b0a  td").ArrayList;.
info : ( main ): Starting ZLS 0.12.0-dev.156+e4593da @ '/home/blah/dev/zig/zls/zig-out/bin/zls'
info : ( main ): No config file zls.json found.
info : (server): set config option 'builtin_path' to '/home/blah/.cache/zls/builtin.zig'
info : (server): set config option 'zig_lib_path' to '/usr/lib/zig/lib'
info : (server): set config option 'zig_exe_path' to '/usr/sbin/zig'
info : (server): set config option 'build_runner_path' to '/home/blah/.cache/zls/build_runner_master.zig'
info : (server): set config option 'global_cache_path' to '/home/blah/.cache/zls'
info : (server): set config option 'build_runner_global_cache_path' to '/home/blah/.cache/zig'
info : (server): client is 'Neovim-0.9.4'
info : (server): Server.ClientCapabilities{ .supports_snippets = true, .supports_apply_edits = true, .supports_will_save = true, .supports_will_save_wait_until = true, .supports_publish_diagnostics = true, .supports_code_action_fixall = false, .hover_supports_md = true, .completion_doc_supports_md = true, .label_details_support = true, .supports_configuration = true, .supports_workspace_did_change_configuration_dynamic_registration = false, .supports_textDocument_definition_linkSupport = true, .include_at_in_builtins = false, .max_detail_length = 1048576, .workspace_folders = {  } }
info : (server): offset encoding: utf-16
debug: (server): Took 3ms to process request-1-initialize on Thread 2078
debug: (server): Took 0ms to process notification-initialized on Thread 2078
debug: (server): Took 1ms to process notification-workspace/didChangeConfiguration on Thread 2078
debug: (server): Took 1ms to process notification-workspace/didChangeConfiguration on Thread 2078
debug: (server): Took 0ms to process notification-textDocument/didOpen on Thread 2078
debug: (store ): Opened document `file:///usr/lib/zig/lib/std/std.zig`
debug: (store ): Opened document `file:///usr/lib/zig/lib/std/array_list.zig`
debug: (server): Took 5ms to process request-2-textDocument/semanticTokens/full on Thread 2081
debug: (server): Took 1ms to process response-i_haz_configuration on Thread 2078
debug: (server): Took 0ms to process notification-$/cancelRequest on Thread 2082
debug: (server): Took 0ms to process request-3-textDocument/semanticTokens/full on Thread 2079
debug: (server): Took 0ms to process response-semantic_tokens_refresh on Thread 2078
debug: (server): Took 0ms to process notification-$/cancelRequest on Thread 2080
debug: (server): Took 0ms to process request-4-textDocument/semanticTokens/full on Thread 2081
debug: (server): Took 0ms to process response-semantic_tokens_refresh on Thread 2078
debug: (server): Took 0ms to process request-5-textDocument/semanticTokens/full on Thread 2082
debug: (server): Took 0ms to process response-semantic_tokens_refresh on Thread 2078
debug: (server): Took 0ms to process notification-textDocument/didChange on Thread 2078
debug: (server): Took 0ms to process notification-textDocument/willSave on Thread 2080
debug: (server): Took 2ms to process request-6-textDocument/willSaveWaitUntil on Thread 2081
debug: (server): Took 0ms to process notification-textDocument/didSave on Thread 2078
debug: (server): Took 0ms to process request-7-textDocument/semanticTokens/full on Thread 2082
debug: (server): Took 0ms to process notification-textDocument/didChange on Thread 2078
error: (server): failed to process notification-textDocument/didChange: error.InvalidParams
/home/blah/dev/zig/zls/src/diff.zig:102:96: 0x4e8a42 in applyContentChanges (zls)
        const end = offsets.maybePositionToIndex(text_array.items, range.end, encoding) orelse return error.InvalidParams;
                                                                                               ^
/home/blah/dev/zig/zls/src/Server.zig:1150:22: 0x498b0a in changeDocumentHandler (zls)
    const new_text = try diff.applyContentChanges(server.allocator, handle.text, notification.contentChanges, server.offset_encoding);
                     ^
/home/blah/dev/zig/zls/src/Server.zig:2031:39: 0x4604cf in sendNotificationSync__anon_26313 (zls)
        .@"textDocument/didChange" => try server.changeDocumentHandler(arena, params),
                                      ^
/home/blah/dev/zig/zls/src/Server.zig:2081:17: 0x422b46 in processMessage (zls)
                try server.sendNotificationSync(arena_allocator.allocator(), @tagName(method), params);
                ^
debug: (server): Took 0ms to process notification-textDocument/didChange on Thread 2078
debug: (server): Took 0ms to process notification-textDocument/willSave on Thread 2079
debug: (server): Took 2ms to process request-8-textDocument/willSaveWaitUntil on Thread 2081
debug: (server): Took 0ms to process notification-textDocument/didSave on Thread 2078
debug: (server): Took 0ms to process request-9-textDocument/semanticTokens/full on Thread 2082
Sirius902 commented 10 months ago

Oops I didn't mean to close.

Sirius902 commented 10 months ago

I've also just noticed the issue does not occur if you add something else after the declaration like shown below.

pub const ArrayList = @import("std").ArrayList;

comptime {
    _ = 4;
}
Techatrix commented 10 months ago

Here is how I reproduced the issue:

# ~/.config/nvim/init.vim
call plug#begin('~/.config/nvim/plugged')
  Plug 'neovim/nvim-lspconfig'
  Plug 'ziglang/zig.vim'
call plug#end()

:lua << EOF
  local lspconfig = require('lspconfig')
  lspconfig.zls.setup {}
  vim.lsp.set_log_level("debug")
EOF

create a file with the following content (no trailing new line)

pub const ArrayList = @import("std").ArrayList;

Save the file twice and then run :LspLog to see ZLS's log output and error return trace. The issue is caused by the textDocument/didChange request which contains invalid contentChanges.

{
    "jsonrpc": "2.0",
    "params": {
        "textDocument": {
            "uri": "file:\/\/\/run\/media\/techatrix\/UserFiles\/zls\/file.zig",
            "version": 5
        },
        "contentChanges": [
            {
                "text": "",
                "range": {
                    "start": {
                        "character": 0,
                        "line": 0
                    },
                    "end": {
                        "character": 0,
                        "line": 1
                    }
                },
                "rangeLength": 48
            },
            {
                "text": "pub const ArrayList = @import(\"std\").ArrayList;",
                "range": {
                    "start": {
                        "character": 0,
                        "line": 0
                    },
                    "end": {
                        "character": 0,
                        "line": 0
                    }
                },
                "rangeLength": 0
            }
        ]
    },
    "method": "textDocument\/didChange"
}

The first content change has a range from the start of the document to the start of the second line. But there is no second line in the document. This appears to be related to the zig_fmt_autosave feature of ziglang/zig.vim because these contentChanges describe an edit that would remove a newline at the end of the document but there is no previous textDocument/didChange that notifies ZLS that a newline has been added (by zig_fmt_autosave).