zls icon indicating copy to clipboard operation
zls copied to clipboard

zls stuck with 100% CPU on a bad `const bar = if (...) else null;` statement (textDocument_hover)

Open AloisMahdal opened this issue 11 months ago • 2 comments

Zig Version

0.13.0

ZLS Version

0.13.0

Client / Code Editor / Extensions

NVIM v0.10.3

Steps to Reproduce and Observed Behavior

Following code snippet will make zls hang, consuming 100% CPU:

const OPTIONAL_STR = ?[]const u8;
const OPTIONAL_USIZE = ?usize;
const AN_USIZE = usize;

const FOO = OPTIONAL_USIZE orelse @max(1, AN_USIZE);

//       ,-- Gets stuck here.
//    vvv
const BAR = if (OPTIONAL_STR == null) {
    (OPTIONAL_USIZE orelse (FOO + 1) / 2);
} else null;

When I try to use Hover Documentation, neovim just gets stuck, and in htop I can see zls using 100% of CPU cycles.

Expected Behavior

Documentation shows up in expected time. (The type is most probably unknown in this case.)

Relevant log output

[START][2025-02-07 15:43:38] LSP logging initiated
[INFO][2025-02-07 15:43:38] .../vim/lsp/rpc.lua:731	"Starting RPC client"	{  cmd = { "/home/netvor/.local/share/nvim/mason/bin/zls" },  extra = {    cwd = "/home/netvor"  }}
[DEBUG][2025-02-07 15:43:38] .../vim/lsp/rpc.lua:286	"rpc.send"	{  id = 1,  jsonrpc = "2.0",  method = "initialize",  params = {    capabilities = {      general = {        positionEncodings = { "utf-16" }      },      textDocument = {        callHierarchy = {          dynamicRegistration = false        },        codeAction = {          codeActionLiteralSupport = {            codeActionKind = {              valueSet = { "", "quickfix", "refactor", "refactor.extract", "refactor.inline", "refactor.rewrite", "source", "source.organizeImports" }            }          },          dataSupport = true,          dynamicRegistration = true,          isPreferredSupport = true,          resolveSupport = {            properties = { "edit" }          }        },        completion = {          completionItem = {            commitCharactersSupport = true,            deprecatedSupport = true,            documentationFormat = { "markdown", "plaintext" },            insertReplaceSupport = true,            insertTextModeSupport = {              valueSet = { 1, 2 }            },            labelDetailsSupport = true,            preselectSupport = true,            resolveSupport = {              properties = { "documentation", "detail", "additionalTextEdits", "sortText", "filterText", "insertText", "textEdit", "insertTextFormat", "insertTextMode" }            },            snippetSupport = true,            tagSupport = {              valueSet = { 1 }            }          },          completionItemKind = {            valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }          },          completionList = {            itemDefaults = { "commitCharacters", "editRange", "insertTextFormat", "insertTextMode", "data" }          },          contextSupport = true,          dynamicRegistration = false,          insertTextMode = 1        },        declaration = {          linkSupport = true        },        definition = {          dynamicRegistration = true,          linkSupport = true        },        diagnostic = {          dynamicRegistration = false        },        documentHighlight = {          dynamicRegistration = false        },        documentSymbol = {          dynamicRegistration = false,          hierarchicalDocumentSymbolSupport = true,          symbolKind = {            valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }          }        },        formatting = {          dynamicRegistration = true        },        hover = {          contentFormat = { "markdown", "plaintext" },          dynamicRegistration = true        },        implementation = {          linkSupport = true        },        inlayHint = {          dynamicRegistration = true,          resolveSupport = {            properties = { "textEdits", "tooltip", "location", "command" }          }        },        publishDiagnostics = {          dataSupport = true,          relatedInformation = true,          tagSupport = {            valueSet = { 1, 2 }          }        },        rangeFormatting = {          dynamicRegistration = true        },        references = {          dynamicRegistration = false        },        rename = {          dynamicRegistration = true,          prepareSupport = true        },        semanticTokens = {          augmentsSyntaxTokens = true,          dynamicRegistration = false,          formats = { "relative" },          multilineTokenSupport = false,          overlappingTokenSupport = true,          requests = {            full = {              delta = true            },            range = false          },          serverCancelSupport = false,          tokenModifiers = { "declaration", "definition", "readonly", "static", "deprecated", "abstract", "async", "modification", "documentation", "defaultLibrary" },          tokenTypes = { "namespace", "type", "class", "enum", "interface", "struct", "typeParameter", "parameter", "variable", "property", "enumMember", "event", "function", "method", "macro", "keyword", "modifier", "comment", "string", "number", "regexp", "operator", "decorator" }        },        signatureHelp = {          dynamicRegistration = false,          signatureInformation = {            activeParameterSupport = true,            documentationFormat = { "markdown", "plaintext" },            parameterInformation = {              labelOffsetSupport = true            }          }        },        synchronization = {          didSave = true,          dynamicRegistration = false,          willSave = true,          willSaveWaitUntil = true        },        typeDefinition = {          linkSupport = true        }      },      window = {        showDocument = {          support = true        },        showMessage = {          messageActionItem = {            additionalPropertiesSupport = false          }        },        workDoneProgress = true      },      workspace = {        applyEdit = true,        configuration = true,        didChangeConfiguration = {          dynamicRegistration = false        },        didChangeWatchedFiles = {          dynamicRegistration = false,          relativePatternSupport = true        },        inlayHint = {          refreshSupport = true        },        semanticTokens = {          refreshSupport = true        },        symbol = {          dynamicRegistration = false,          symbolKind = {            valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }          }        },        workspaceEdit = {          resourceOperations = { "rename", "create", "delete" }        },        workspaceFolders = true      }    },    clientInfo = {      name = "Neovim",      version = "0.10.3+g9b5ee7df4e"    },    initializationOptions = vim.empty_dict(),    processId = 3698395,    rootPath = vim.NIL,    rootUri = vim.NIL,    trace = "off",    workDoneToken = "1",    workspaceFolders = vim.NIL  }}
[ERROR][2025-02-07 15:43:38] .../vim/lsp/rpc.lua:770	"rpc"	"/home/netvor/.local/share/nvim/mason/bin/zls"	"stderr""info : ( main ): Starting ZLS 0.13.0 @ '/home/netvor/.local/share/nvim/mason/bin/zls'\ninfo : (server): Client is 'Neovim-0.10.3+g9b5ee7df4e'\ninfo : (server): No config file zls.json found. This is not an error.\n"
[ERROR][2025-02-07 15:43:38] .../vim/lsp/rpc.lua:770	"rpc"	"/home/netvor/.local/share/nvim/mason/bin/zls"	"stderr""info : (server): Set config option 'builtin_path' to '/home/netvor/.cache/zls/builtin.zig'\ninfo : (server): Set config option 'zig_lib_path' to '/opt/zigdev/zig-linux-x86_64/lib'\ninfo : (server): Set config option 'zig_exe_path' to '/usr/bin/zig'\ninfo : (server): Set config option 'build_runner_path' to '/home/netvor/.cache/zls/build_runner/21872970afd69e48a0847077e5196711/build_runner.zig'\ninfo : (server): Set config option 'global_cache_path' to '/home/netvor/.cache/zls'\n"
[DEBUG][2025-02-07 15:43:38] .../vim/lsp/rpc.lua:408	"rpc.receive"	{  id = 1,  jsonrpc = "2.0",  result = {    capabilities = {      codeActionProvider = true,      colorProvider = false,      completionProvider = {        completionItem = {          labelDetailsSupport = true        },        resolveProvider = false,        triggerCharacters = { ".", ":", "@", "]", "/" }      },      declarationProvider = true,      definitionProvider = true,      documentFormattingProvider = true,      documentHighlightProvider = true,      documentRangeFormattingProvider = false,      documentSymbolProvider = true,      foldingRangeProvider = true,      hoverProvider = true,      implementationProvider = false,      inlayHintProvider = true,      positionEncoding = "utf-16",      referencesProvider = true,      renameProvider = true,      selectionRangeProvider = true,      semanticTokensProvider = {        full = true,        legend = {          tokenModifiers = { "declaration", "definition", "readonly", "static", "deprecated", "abstract", "async", "modification", "documentation", "defaultLibrary", "generic", "_" },          tokenTypes = { "namespace", "type", "class", "enum", "interface", "struct", "typeParameter", "parameter", "variable", "property", "enumMember", "event", "function", "method", "macro", "keyword", "modifier", "comment", "string", "number", "regexp", "operator", "decorator", "errorTag", "builtin", "label", "keywordLiteral", "union", "opaque" }        },        range = true      },      signatureHelpProvider = {        retriggerCharacters = { "," },        triggerCharacters = { "(" }      },      textDocumentSync = {        change = 2,        openClose = true,        save = true,        willSave = true,        willSaveWaitUntil = true      },      typeDefinitionProvider = true,      workspace = {        workspaceFolders = {          changeNotifications = true,          supported = true        }      },      workspaceSymbolProvider = false    },    serverInfo = {      name = "zls",      version = "0.13.0"    }  }}
[DEBUG][2025-02-07 15:43:38] .../vim/lsp/rpc.lua:286	"rpc.send"	{  jsonrpc = "2.0",  method = "initialized",  params = vim.empty_dict()}
[DEBUG][2025-02-07 15:43:38] .../vim/lsp/rpc.lua:286	"rpc.send"	{  jsonrpc = "2.0",  method = "textDocument/didOpen",  params = {    textDocument = {      languageId = "zig",      text = "const OPTIONAL_STR = ?[]const u8;\nconst OPTIONAL_USIZE = ?usize;\nconst AN_USIZE = usize;\n\nconst FOO = OPTIONAL_USIZE orelse @max(1, AN_USIZE);\n\n//       ,-- Gets stuck here.\n//    vvv\nconst BAR = if (OPTIONAL_STR == null) {\n    (OPTIONAL_USIZE orelse (FOO + 1) / 2);\n} else null;\n",      uri = "file:///home/netvor/killzls.zig",      version = 0    }  }}
[INFO][2025-02-07 15:43:38] ...m/lsp/client.lua:620	"LSP[zls]"	"server_capabilities"	{  server_capabilities = {    codeActionProvider = true,    colorProvider = false,    completionProvider = {      completionItem = {        labelDetailsSupport = true      },      resolveProvider = false,      triggerCharacters = { ".", ":", "@", "]", "/" }    },    declarationProvider = true,    definitionProvider = true,    documentFormattingProvider = true,    documentHighlightProvider = true,    documentRangeFormattingProvider = false,    documentSymbolProvider = true,    foldingRangeProvider = true,    hoverProvider = true,    implementationProvider = false,    inlayHintProvider = true,    positionEncoding = "utf-16",    referencesProvider = true,    renameProvider = true,    selectionRangeProvider = true,    semanticTokensProvider = {      full = true,      legend = {        tokenModifiers = { "declaration", "definition", "readonly", "static", "deprecated", "abstract", "async", "modification", "documentation", "defaultLibrary", "generic", "_" },        tokenTypes = { "namespace", "type", "class", "enum", "interface", "struct", "typeParameter", "parameter", "variable", "property", "enumMember", "event", "function", "method", "macro", "keyword", "modifier", "comment", "string", "number", "regexp", "operator", "decorator", "errorTag", "builtin", "label", "keywordLiteral", "union", "opaque" }      },      range = true    },    signatureHelpProvider = {      retriggerCharacters = { "," },      triggerCharacters = { "(" }    },    textDocumentSync = {      change = 2,      openClose = true,      save = true,      willSave = true,      willSaveWaitUntil = true    },    typeDefinitionProvider = true,    workspace = {      workspaceFolders = {        changeNotifications = true,        supported = true      }    },    workspaceSymbolProvider = false  }}
[DEBUG][2025-02-07 15:43:38] .../vim/lsp/rpc.lua:408	"rpc.receive"	{  id = "i_haz_configuration",  jsonrpc = "2.0",  method = "workspace/configuration",  params = {    items = { {        section = "zls.enable_snippets"      }, {        section = "zls.enable_argument_placeholders"      }, {        section = "zls.enable_build_on_save"      }, {        section = "zls.build_on_save_step"      }, {        section = "zls.enable_autofix"      }, {        section = "zls.semantic_tokens"      }, {        section = "zls.enable_inlay_hints"      }, {        section = "zls.inlay_hints_show_variable_type_hints"      }, {        section = "zls.inlay_hints_show_struct_literal_field_type"      }, {        section = "zls.inlay_hints_show_parameter_name"      }, {        section = "zls.inlay_hints_show_builtin"      }, {        section = "zls.inlay_hints_exclude_single_argument"      }, {        section = "zls.inlay_hints_hide_redundant_param_names"      }, {        section = "zls.inlay_hints_hide_redundant_param_names_last_token"      }, {        section = "zls.warn_style"      }, {        section = "zls.highlight_global_var_declarations"      }, {        section = "zls.dangerous_comptime_experiments_do_not_enable"      }, {        section = "zls.skip_std_references"      }, {        section = "zls.prefer_ast_check_as_child_process"      }, {        section = "zls.builtin_path"      }, {        section = "zls.zig_lib_path"      }, {        section = "zls.zig_exe_path"      }, {        section = "zls.build_runner_path"      }, {        section = "zls.global_cache_path"      }, {        section = "zls.completion_label_details"      } }  }}
[DEBUG][2025-02-07 15:43:38] .../vim/lsp/rpc.lua:408	"rpc.receive"	{  jsonrpc = "2.0",  method = "textDocument/publishDiagnostics",  params = {    diagnostics = {},    uri = "file:///home/netvor/killzls.zig"  }}
[DEBUG][2025-02-07 15:43:38] ...m/lsp/client.lua:678	"LSP[zls]"	"client.request"	1	"textDocument/semanticTokens/full"	{  textDocument = {    uri = "file:///home/netvor/killzls.zig"  }}	<function 1>	1
[DEBUG][2025-02-07 15:43:38] .../vim/lsp/rpc.lua:286	"rpc.send"	{  id = 2,  jsonrpc = "2.0",  method = "textDocument/semanticTokens/full",  params = {    textDocument = {      uri = "file:///home/netvor/killzls.zig"    }  }}
[DEBUG][2025-02-07 15:43:38] .../vim/lsp/rpc.lua:423	"server_request: callback result"	{  result = { vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL },  status = true}
[DEBUG][2025-02-07 15:43:38] .../vim/lsp/rpc.lua:286	"rpc.send"	{  id = "i_haz_configuration",  jsonrpc = "2.0",  result = { vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL, vim.NIL }}
[DEBUG][2025-02-07 15:43:38] .../vim/lsp/rpc.lua:408	"rpc.receive"	{  id = 2,  jsonrpc = "2.0",  result = {    data = { 0, 0, 5, 15, 0, 0, 6, 12, 1, 1, 0, 13, 1, 21, 0, 0, 2, 1, 21, 0, 0, 3, 5, 15, 0, 0, 6, 2, 1, 0, 1, 0, 5, 15, 0, 0, 6, 14, 1, 1, 0, 15, 1, 21, 0, 0, 2, 1, 21, 0, 0, 1, 5, 1, 0, 1, 0, 5, 15, 0, 0, 6, 8, 1, 1, 0, 9, 1, 21, 0, 0, 2, 5, 1, 0, 2, 0, 5, 15, 0, 0, 6, 3, 8, 1, 0, 4, 1, 21, 0, 0, 2, 14, 1, 0, 0, 15, 6, 15, 0, 0, 7, 4, 24, 0, 0, 5, 1, 19, 0, 0, 3, 8, 1, 0, 2, 0, 29, 17, 0, 1, 0, 9, 17, 0, 1, 0, 5, 15, 0, 0, 6, 3, 8, 1, 0, 4, 1, 21, 0, 0, 2, 2, 15, 0, 0, 4, 12, 1, 0, 0, 13, 2, 21, 0, 0, 3, 4, 26, 0, 1, 5, 14, 1, 0, 0, 15, 6, 15, 0, 0, 8, 3, 8, 0, 0, 4, 1, 21, 0, 0, 2, 1, 19, 0, 0, 3, 1, 21, 0, 0, 2, 1, 19, 0, 1, 2, 4, 15, 0, 0, 5, 4, 26, 0 }  }}
[DEBUG][2025-02-07 15:43:38] .../vim/lsp/rpc.lua:408	"rpc.receive"	{  id = "semantic_tokens_refresh",  jsonrpc = "2.0",  method = "workspace/semanticTokens/refresh"}
[DEBUG][2025-02-07 15:43:38] ...m/lsp/client.lua:678	"LSP[zls]"	"client.request"	1	"textDocument/semanticTokens/full"	{  textDocument = {    uri = "file:///home/netvor/killzls.zig"  }}	<function 1>	1
[DEBUG][2025-02-07 15:43:38] .../vim/lsp/rpc.lua:286	"rpc.send"	{  id = 3,  jsonrpc = "2.0",  method = "textDocument/semanticTokens/full",  params = {    textDocument = {      uri = "file:///home/netvor/killzls.zig"    }  }}
[DEBUG][2025-02-07 15:43:38] .../vim/lsp/rpc.lua:423	"server_request: callback result"	{  result = vim.NIL,  status = true}
[DEBUG][2025-02-07 15:43:38] .../vim/lsp/rpc.lua:286	"rpc.send"	{  id = "semantic_tokens_refresh",  jsonrpc = "2.0",  result = vim.NIL}
[DEBUG][2025-02-07 15:43:38] .../vim/lsp/rpc.lua:408	"rpc.receive"	{  id = 3,  jsonrpc = "2.0",  result = {    data = { 0, 0, 5, 15, 0, 0, 6, 12, 1, 1, 0, 13, 1, 21, 0, 0, 2, 1, 21, 0, 0, 3, 5, 15, 0, 0, 6, 2, 1, 0, 1, 0, 5, 15, 0, 0, 6, 14, 1, 1, 0, 15, 1, 21, 0, 0, 2, 1, 21, 0, 0, 1, 5, 1, 0, 1, 0, 5, 15, 0, 0, 6, 8, 1, 1, 0, 9, 1, 21, 0, 0, 2, 5, 1, 0, 2, 0, 5, 15, 0, 0, 6, 3, 8, 1, 0, 4, 1, 21, 0, 0, 2, 14, 1, 0, 0, 15, 6, 15, 0, 0, 7, 4, 24, 0, 0, 5, 1, 19, 0, 0, 3, 8, 1, 0, 2, 0, 29, 17, 0, 1, 0, 9, 17, 0, 1, 0, 5, 15, 0, 0, 6, 3, 8, 1, 0, 4, 1, 21, 0, 0, 2, 2, 15, 0, 0, 4, 12, 1, 0, 0, 13, 2, 21, 0, 0, 3, 4, 26, 0, 1, 5, 14, 1, 0, 0, 15, 6, 15, 0, 0, 8, 3, 8, 0, 0, 4, 1, 21, 0, 0, 2, 1, 19, 0, 0, 3, 1, 21, 0, 0, 2, 1, 19, 0, 1, 2, 4, 15, 0, 0, 5, 4, 26, 0 }  }}
[DEBUG][2025-02-07 15:43:41] ...m/lsp/client.lua:678	"LSP[zls]"	"client.request"	1	"textDocument/hover"	{  position = {    character = 6,    line = 8  },  textDocument = {    uri = "file:///home/netvor/killzls.zig"  }}	<function 1>	1
[DEBUG][2025-02-07 15:43:41] .../vim/lsp/rpc.lua:286	"rpc.send"	{  id = 4,  jsonrpc = "2.0",  method = "textDocument/hover",  params = {    position = {      character = 6,      line = 8    },    textDocument = {      uri = "file:///home/netvor/killzls.zig"    }  }}
[DEBUG][2025-02-07 15:43:41] .../vim/lsp/rpc.lua:408	"rpc.receive"	{  id = 4,  jsonrpc = "2.0",  result = {    contents = {      kind = "markdown",      value = "\nconst BAR = if (OPTIONAL_STR == null) {\n    (OPTIONAL_USIZE orelse (FOO + 1) / 2);\n} else null\n\n\n(@TypeOf(null))\n"    },    range = {      ["end"] = {        character = 9,        line = 8      },      start = {        character = 6,        line = 8      }    }  }}

AloisMahdal avatar Feb 07 '25 14:02 AloisMahdal

I have similar issue, but not for hover doc. I'm using blink.cmp for completions and it randomly gets stuck, using 100% cpu. Weirdly, this only happens for zls and it consistently fails on one specific code snippet

fn parse(filename: []const u8) !i32 {
    var fd = try std.fs.cwd().openFile(filename, .{});
    defer fd.cl
    //         ^ 
    // cursor is here
}

as soon as close method becomes best match, nvim crashes. I tried your provided snipped but that worked fine for me, so I think this might be blink.cmp issue, but if so, then why does it only happen to zls

Kaikacy avatar Feb 08 '25 07:02 Kaikacy

related #2139

NicoElbers avatar Feb 13 '25 12:02 NicoElbers

Is this issue still happening? If so, please provide more information on how the editor has been configured (plugins, config files, etc.). I had previously tested this with relatively bare bones neovim config and could not reproduce this issue.

Techatrix avatar May 30 '25 08:05 Techatrix

I just tried on the same machine with nvim-0.11.0 and zls-0.14.0 and it works fine. (It even gets the type right.)

AloisMahdal avatar May 30 '25 16:05 AloisMahdal

iirc, this was likely an issue in treesitter. Then after a month or so I tried to track the bug down again and it didn't repro anymore

NicoElbers avatar May 30 '25 19:05 NicoElbers

So I guess there is nothing to do here. Closing.

Techatrix avatar Jun 02 '25 18:06 Techatrix