From 3ce73bee997a385b5f31cd7e7474eb3e23cbbd64 Mon Sep 17 00:00:00 2001 From: NeOzay Date: Mon, 22 Jul 2024 22:47:20 +0200 Subject: [PATCH 1/7] improve diagnosis (missing-fields). --- script/core/diagnostics/missing-fields.lua | 91 +++++++++++++--------- 1 file changed, 54 insertions(+), 37 deletions(-) diff --git a/script/core/diagnostics/missing-fields.lua b/script/core/diagnostics/missing-fields.lua index 210920fd9..e6b48ffdc 100644 --- a/script/core/diagnostics/missing-fields.lua +++ b/script/core/diagnostics/missing-fields.lua @@ -16,69 +16,86 @@ return function (uri, callback) await.delay() local defs = vm.getDefs(src) + local sortedDefs = {} for _, def in ipairs(defs) do - if def.type == 'doc.class' and def.bindSource then - if guide.isInRange(def.bindSource, src.start) then + if def.type == 'doc.class' then + if def.bindSource and guide.isInRange(def.bindSource, src.start) then return end + if not sortedDefs[def.class[1]] then + sortedDefs[def.class[1]] = {} + end + local samedefs = sortedDefs[def.class[1]] + samedefs[#samedefs+1] = def end if def.type == 'doc.type.array' or def.type == 'doc.type.table' then return end end - local warnings = {} - for _, def in ipairs(defs) do - if def.type == 'doc.class' then - if not def.fields then - return - end + + local Allwarnings = {} + for _, samedefs in pairs(sortedDefs) do + local warnings = {} + for _, def in ipairs(samedefs) do + if def.type == 'doc.class' then + if not def.fields then + goto continue + end - local requiresKeys = {} - for _, field in ipairs(def.fields) do - if not field.optional - and not vm.compileNode(field):isNullable() then + local requiresKeys = {} + for _, field in ipairs(def.fields) do + if not field.optional + and not vm.compileNode(field):isNullable() then + local key = vm.getKeyName(field) + if key and not requiresKeys[key] then + requiresKeys[key] = true + requiresKeys[#requiresKeys+1] = key + end + end + end + + if #requiresKeys == 0 then + goto continue + end + local myKeys = {} + for _, field in ipairs(src) do local key = vm.getKeyName(field) - if key and not requiresKeys[key] then - requiresKeys[key] = true - requiresKeys[#requiresKeys+1] = key + if key then + myKeys[key] = true end end - end - if #requiresKeys == 0 then - return - end - local myKeys = {} - for _, field in ipairs(src) do - local key = vm.getKeyName(field) - if key then - myKeys[key] = true + local missedKeys = {} + for _, key in ipairs(requiresKeys) do + if not myKeys[key] then + missedKeys[#missedKeys+1] = ('`%s`'):format(key) + end end - end - local missedKeys = {} - for _, key in ipairs(requiresKeys) do - if not myKeys[key] then - missedKeys[#missedKeys+1] = ('`%s`'):format(key) + if #missedKeys == 0 then + goto continue end - end - if #missedKeys == 0 then - return + warnings[#warnings+1] = lang.script('DIAG_MISSING_FIELDS', def.class[1], table.concat(missedKeys, ', ')) + end + ::continue:: + end + if #warnings == 0 then + return + else + for i = 1, #warnings do + Allwarnings[#Allwarnings+1] = warnings[i] end - - warnings[#warnings+1] = lang.script('DIAG_MISSING_FIELDS', def.class[1], table.concat(missedKeys, ', ')) end end - - if #warnings == 0 then + if #Allwarnings == 0 then return end callback { start = src.start, finish = src.finish, - message = table.concat(warnings, '\n') + message = table.concat(Allwarnings, '\n') } end) end From 0b8102e3774b74db5557a494567fb0a9c5da9013 Mon Sep 17 00:00:00 2001 From: NeOzay Date: Tue, 23 Jul 2024 17:14:37 +0200 Subject: [PATCH 2/7] improve missing-fields diagnostic logic --- script/core/diagnostics/missing-fields.lua | 75 ++++++--------- test/diagnostics/missing-fields.lua | 104 +++++++++++++++++++++ 2 files changed, 133 insertions(+), 46 deletions(-) diff --git a/script/core/diagnostics/missing-fields.lua b/script/core/diagnostics/missing-fields.lua index e6b48ffdc..746f7b00f 100644 --- a/script/core/diagnostics/missing-fields.lua +++ b/script/core/diagnostics/missing-fields.lua @@ -22,10 +22,11 @@ return function (uri, callback) if def.bindSource and guide.isInRange(def.bindSource, src.start) then return end - if not sortedDefs[def.class[1]] then - sortedDefs[def.class[1]] = {} + local className = def.class[1] + if not sortedDefs[className] then + sortedDefs[className] = {} end - local samedefs = sortedDefs[def.class[1]] + local samedefs = sortedDefs[className] samedefs[#samedefs+1] = def end if def.type == 'doc.type.array' @@ -33,69 +34,51 @@ return function (uri, callback) return end end - - local Allwarnings = {} + + local warnings = {} for _, samedefs in pairs(sortedDefs) do - local warnings = {} + local missedKeys = {} + local className for _, def in ipairs(samedefs) do - if def.type == 'doc.class' then - if not def.fields then - goto continue - end + className = def.class[1] + if not def.fields or #def.fields == 0 then + goto continue + end - local requiresKeys = {} - for _, field in ipairs(def.fields) do - if not field.optional - and not vm.compileNode(field):isNullable() then - local key = vm.getKeyName(field) - if key and not requiresKeys[key] then - requiresKeys[key] = true - requiresKeys[#requiresKeys+1] = key - end - end + local myKeys = {} + for _, field in ipairs(src) do + local key = vm.getKeyName(field) + if key then + myKeys[key] = true end + end - if #requiresKeys == 0 then - goto continue - end - local myKeys = {} - for _, field in ipairs(src) do + for _, field in ipairs(def.fields) do + if not field.optional + and not vm.compileNode(field):isNullable() then local key = vm.getKeyName(field) - if key then - myKeys[key] = true - end - end - - local missedKeys = {} - for _, key in ipairs(requiresKeys) do - if not myKeys[key] then + if key and not myKeys[key] then missedKeys[#missedKeys+1] = ('`%s`'):format(key) end end - - if #missedKeys == 0 then - goto continue - end - - warnings[#warnings+1] = lang.script('DIAG_MISSING_FIELDS', def.class[1], table.concat(missedKeys, ', ')) - end + end ::continue:: end - if #warnings == 0 then + + if #missedKeys == 0 then return else - for i = 1, #warnings do - Allwarnings[#Allwarnings+1] = warnings[i] - end + warnings[#warnings+1] = lang.script('DIAG_MISSING_FIELDS', className, table.concat(missedKeys, ', ')) end end - if #Allwarnings == 0 then + + if #warnings == 0 then return end callback { start = src.start, finish = src.finish, - message = table.concat(Allwarnings, '\n') + message = table.concat(warnings, '\n') } end) end diff --git a/test/diagnostics/missing-fields.lua b/test/diagnostics/missing-fields.lua index ab87f81d8..f1b1beae0 100644 --- a/test/diagnostics/missing-fields.lua +++ b/test/diagnostics/missing-fields.lua @@ -231,3 +231,107 @@ local t = { y = 1, } ]] + +TEST [[ +---@diagnostic disable: unused-local + +---@class Foo +---@field a number +---@field b number +---@field c number + +---@class Foo + +---@class Bar +---@field ba number +---@field bb number +---@field bc number + +---@class Bar +---@field bd number + +---@type Foo|Bar +local x = { + ba = 1, + bb = 2, + bc = 3, + bd = 4, +} +]] + +TEST [[ +---@diagnostic disable: unused-local + +---@class Foo +---@field a number +---@field b number +---@field c number + +---@class Foo + +---@class Bar +---@field ba number +---@field bb number +---@field bc number + +---@class Bar +---@field bd number + +---@type Foo|Bar +local x = { + a = 1, + b = 2, + c = 3, +} +]] + +TEST [[ +---@diagnostic disable: unused-local + +---@class Foo +---@field a number +---@field b number +---@field c number + +---@class Foo + +---@class Bar +---@field ba number +---@field bb number +---@field bc number + +---@class Bar +---@field bd number + +---@type Foo|Bar +local x = +]] + +TEST [[ +---@diagnostic disable: unused-local + +---@class Foo +---@field a number +---@field b number +---@field c number + +---@class Foo + +---@class Bar +---@field ba number +---@field bb number +---@field bc number + +---@class Bar +---@field bd number + +---@type Foo|Bar +local x = +]] \ No newline at end of file From 7582afd72f6d111f81b9c48caf43bafa80a1887a Mon Sep 17 00:00:00 2001 From: NeOzay Date: Tue, 23 Jul 2024 17:55:39 +0200 Subject: [PATCH 3/7] update changelog --- changelog.md | 3 ++- script/core/diagnostics/missing-fields.lua | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index a82efdabe..8efe3ce14 100644 --- a/changelog.md +++ b/changelog.md @@ -9,9 +9,10 @@ * `CHG` Improve performance of multithreaded `--check` and `undefined-field` diagnostic * `FIX` Addons can now self-recommend as expected. Fixed by correcting the `wholeMatch` function * `FIX` Now correctly evaluates the visibility of fields in a class when they are defined directly in the object. use for completion and invisible dianostic. [#2752](https://github.com/LuaLS/lua-language-server/issues/2752) -* `NEW` added lua regular expression support for Lua.doc.Name [#2753](https://github.com/LuaLS/lua-language-server/pull/2753) +* `NEW` Added lua regular expression support for Lua.doc.\Name [#2753](https://github.com/LuaLS/lua-language-server/pull/2753) * `FIX` Bad triggering of the `inject-field` diagnostic, when the fields are declared at the creation of the object [#2746](https://github.com/LuaLS/lua-language-server/issues/2746) * `CHG` Change spacing of parameter inlay hints to match other LSPs, like `rust-analyzer` +* `FIX` Improve the `missing-fields` logic to be able to correctly handle classes defined several times [#22770](https://github.com/LuaLS/lua-language-server/pull/2770) ## 3.9.3 `2024-6-11` diff --git a/script/core/diagnostics/missing-fields.lua b/script/core/diagnostics/missing-fields.lua index 746f7b00f..b901461aa 100644 --- a/script/core/diagnostics/missing-fields.lua +++ b/script/core/diagnostics/missing-fields.lua @@ -67,9 +67,9 @@ return function (uri, callback) if #missedKeys == 0 then return - else - warnings[#warnings+1] = lang.script('DIAG_MISSING_FIELDS', className, table.concat(missedKeys, ', ')) end + + warnings[#warnings+1] = lang.script('DIAG_MISSING_FIELDS', className, table.concat(missedKeys, ', ')) end if #warnings == 0 then From c08410458924a9f3c604e662cfce80f0f86d5b78 Mon Sep 17 00:00:00 2001 From: NeOzay Date: Wed, 24 Jul 2024 18:01:44 +0200 Subject: [PATCH 4/7] also detects missing index fields --- script/core/diagnostics/missing-fields.lua | 17 +++++++++++++++-- test/diagnostics/missing-fields.lua | 18 ++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/script/core/diagnostics/missing-fields.lua b/script/core/diagnostics/missing-fields.lua index b901461aa..18b561ee7 100644 --- a/script/core/diagnostics/missing-fields.lua +++ b/script/core/diagnostics/missing-fields.lua @@ -15,6 +15,7 @@ return function (uri, callback) guide.eachSourceType(state.ast, 'table', function (src) await.delay() + vm.removeNode(src) -- the node is not updated correctly, reason still unknown local defs = vm.getDefs(src) local sortedDefs = {} for _, def in ipairs(defs) do @@ -47,7 +48,7 @@ return function (uri, callback) local myKeys = {} for _, field in ipairs(src) do - local key = vm.getKeyName(field) + local key = vm.getKeyName(field) or field.tindex if key then myKeys[key] = true end @@ -57,8 +58,20 @@ return function (uri, callback) if not field.optional and not vm.compileNode(field):isNullable() then local key = vm.getKeyName(field) + if not key then + local fieldnode = vm.compileNode(field.field)[1] + if fieldnode and fieldnode.type == 'doc.type.integer' then + ---@cast fieldnode parser.object + key = vm.getKeyName(fieldnode) + end + end + if key and not myKeys[key] then - missedKeys[#missedKeys+1] = ('`%s`'):format(key) + if type(key) == "number" then + missedKeys[#missedKeys+1] = ('`[%s]`'):format(key) + else + missedKeys[#missedKeys+1] = ('`%s`'):format(key) + end end end end diff --git a/test/diagnostics/missing-fields.lua b/test/diagnostics/missing-fields.lua index f1b1beae0..8c1ffbbb1 100644 --- a/test/diagnostics/missing-fields.lua +++ b/test/diagnostics/missing-fields.lua @@ -334,4 +334,22 @@ local x = +]] + +TEST[[ +---@class A +---@field [1] string +---@field x number + +---@type A +local t = {x = 1, ""} +]] + +TEST[[ +---@class A +---@field [1] string +---@field x number + +---@type A +local t = ]] \ No newline at end of file From f706a965515b4a155deabf2c3f506a2ccf444718 Mon Sep 17 00:00:00 2001 From: NeOzay <69015205+NeOzay@users.noreply.github.com> Date: Sun, 28 Jul 2024 18:19:50 +0200 Subject: [PATCH 5/7] Update changelog.md Co-authored-by: Tom Lau --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index f91a27f48..783a75354 100644 --- a/changelog.md +++ b/changelog.md @@ -9,7 +9,7 @@ * `CHG` Improve performance of multithreaded `--check` and `undefined-field` diagnostic * `FIX` Addons can now self-recommend as expected. Fixed by correcting the `wholeMatch` function * `FIX` Now correctly evaluates the visibility of fields in a class when they are defined directly in the object. use for completion and invisible dianostic. [#2752](https://github.com/LuaLS/lua-language-server/issues/2752) -* `NEW` Added lua regular expression support for Lua.doc.\Name [#2753](https://github.com/LuaLS/lua-language-server/pull/2753) +* `NEW` Added lua regular expression support for `Lua.doc.Name` [#2753](https://github.com/LuaLS/lua-language-server/pull/2753) * `FIX` Bad triggering of the `inject-field` diagnostic, when the fields are declared at the creation of the object [#2746](https://github.com/LuaLS/lua-language-server/issues/2746) * `CHG` Change spacing of parameter inlay hints to match other LSPs, like `rust-analyzer` * `FIX` Inconsistent type narrow behavior of function call args [#2758](https://github.com/LuaLS/lua-language-server/issues/2758) From 9035afd4b4fb548c2d88c0a4e3ec566cb0c2cc11 Mon Sep 17 00:00:00 2001 From: NeOzay <69015205+NeOzay@users.noreply.github.com> Date: Sun, 28 Jul 2024 18:20:49 +0200 Subject: [PATCH 6/7] Update script/core/diagnostics/missing-fields.lua Co-authored-by: Tom Lau --- script/core/diagnostics/missing-fields.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/script/core/diagnostics/missing-fields.lua b/script/core/diagnostics/missing-fields.lua index 18b561ee7..d332b0912 100644 --- a/script/core/diagnostics/missing-fields.lua +++ b/script/core/diagnostics/missing-fields.lua @@ -37,11 +37,9 @@ return function (uri, callback) end local warnings = {} - for _, samedefs in pairs(sortedDefs) do + for className, samedefs in pairs(sortedDefs) do local missedKeys = {} - local className for _, def in ipairs(samedefs) do - className = def.class[1] if not def.fields or #def.fields == 0 then goto continue end From df9db56b148f8ab242d35f7d4e2b91cd2ba02668 Mon Sep 17 00:00:00 2001 From: NeOzay Date: Sun, 28 Jul 2024 18:30:02 +0200 Subject: [PATCH 7/7] compute table keys once --- script/core/diagnostics/missing-fields.lua | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/script/core/diagnostics/missing-fields.lua b/script/core/diagnostics/missing-fields.lua index d332b0912..5ce650ec6 100644 --- a/script/core/diagnostics/missing-fields.lua +++ b/script/core/diagnostics/missing-fields.lua @@ -35,7 +35,8 @@ return function (uri, callback) return end end - + + local myKeys local warnings = {} for className, samedefs in pairs(sortedDefs) do local missedKeys = {} @@ -43,12 +44,14 @@ return function (uri, callback) if not def.fields or #def.fields == 0 then goto continue end - - local myKeys = {} - for _, field in ipairs(src) do - local key = vm.getKeyName(field) or field.tindex - if key then - myKeys[key] = true + + if not myKeys then + myKeys = {} + for _, field in ipairs(src) do + local key = vm.getKeyName(field) or field.tindex + if key then + myKeys[key] = true + end end end