diff --git a/changelog.md b/changelog.md index ef4a40179..d777fdc05 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,7 @@ * `NEW` Add postfix snippet for `unpack` * `FIX` `diagnostics.severity` defaulting to "Warning" when run using `--check` [#2730](https://github.com/LuaLS/lua-language-server/issues/2730) * `NEW` Add support for lambda style functions, `|paramList| expr` is syntactic sugar for `function(paramList) return expr end` +* `CHG` Improve performance of multithreaded `--check` and `undefined-field` diagnostic ## 3.9.3 `2024-6-11` diff --git a/script/cli/check_worker.lua b/script/cli/check_worker.lua index 23c34b2c5..822c83dc6 100644 --- a/script/cli/check_worker.lua +++ b/script/cli/check_worker.lua @@ -43,15 +43,6 @@ local checkLevel = define.DiagnosticSeverity[CHECKLEVEL] or define.DiagnosticSev util.enableCloseFunction() --- Hash function used to distribute work. -local function hashString(str) - local hash = 0 - for i = 1, #str do - hash = (hash * 37 & 0xFFFFFFFF) + str:byte(i, i) - end - return hash -end - local lastClock = os.clock() local results = {} @@ -109,9 +100,9 @@ xpcall(lclient.start, errorhandler, lclient, function (client) local uris = files.getChildFiles(rootUri) local max = #uris + table.sort(uris) -- sort file list to ensure the work distribution order across multiple threads for i, uri in ipairs(uris) do - local hash = hashString(uri) % numThreads + 1 - if hash == threadId then + if (i % numThreads + 1) == threadId then files.open(uri) diag.doDiagnostic(uri, true) -- Print regularly but always print the last entry to ensure that logs written to files don't look incomplete. diff --git a/script/core/diagnostics/undefined-field.lua b/script/core/diagnostics/undefined-field.lua index 4fd55966d..ab5d4d544 100644 --- a/script/core/diagnostics/undefined-field.lua +++ b/script/core/diagnostics/undefined-field.lua @@ -21,7 +21,7 @@ return function (uri, callback) local function checkUndefinedField(src) await.delay() - if #vm.getDefs(src) > 0 then + if vm.hasDef(src) then return end local node = src.node diff --git a/script/vm/def.lua b/script/vm/def.lua index 5388a3849..669d39c25 100644 --- a/script/vm/def.lua +++ b/script/vm/def.lua @@ -92,3 +92,42 @@ function vm.getDefs(source) return results end + +local HAS_DEF_ERR = {} -- the error object for comparing +local function checkHasDef(checkFunc, source, pushResult) + local _, err = pcall(checkFunc, source, pushResult) + return err == HAS_DEF_ERR +end + +---@param source parser.object +function vm.hasDef(source) + local mark = {} + local hasLocal + local function pushResult(src) + if src.type == 'local' then + if hasLocal then + return + end + hasLocal = true + if source.type ~= 'local' + and source.type ~= 'getlocal' + and source.type ~= 'setlocal' + and source.type ~= 'doc.cast.name' then + return + end + end + if not mark[src] then + mark[src] = true + if guide.isAssign(src) + or guide.isLiteral(src) then + -- break out on 1st result using error() with a unique error object + error(HAS_DEF_ERR) + end + end + end + + return checkHasDef(searchBySimple, source, pushResult) + or checkHasDef(searchByLocalID, source, pushResult) + or checkHasDef(vm.compileByNodeChain, source, pushResult) + or checkHasDef(searchByNode, source, pushResult) +end