Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve the missing-fields logic to be able to correctly handle classes defined several times #2770

Merged
merged 11 commits into from
Aug 1, 2024
3 changes: 2 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@
* `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.<scope>Name [#2753](https://github.com/LuaLS/lua-language-server/pull/2753)
* `NEW` Added lua regular expression support for `Lua.doc.<scope>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)
* `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)
* `FIX` Typos in annotation descriptions
* `NEW` You can now click on "References" in CodeLen to display the reference list
* `FIX` incorrect `CompletionItemKind` for postfix snippets [#2773](https://github.com/LuaLS/lua-language-server/pull/2773)
Expand Down
78 changes: 46 additions & 32 deletions script/core/diagnostics/missing-fields.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,61 +15,75 @@ 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
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
local className = def.class[1]
if not sortedDefs[className] then
sortedDefs[className] = {}
end
local samedefs = sortedDefs[className]
samedefs[#samedefs+1] = def
end
if def.type == 'doc.type.array'
or def.type == 'doc.type.table' then
return
end
end

local myKeys
local warnings = {}
for _, def in ipairs(defs) do
if def.type == 'doc.class' then
if not def.fields then
return
for className, samedefs in pairs(sortedDefs) do
local missedKeys = {}
for _, def in ipairs(samedefs) do
if not def.fields or #def.fields == 0 then
goto continue
end

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

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
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
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
end
end

local missedKeys = {}
for _, key in ipairs(requiresKeys) do
if not myKeys[key] then
missedKeys[#missedKeys+1] = ('`%s`'):format(key)
if key and not myKeys[key] then
if type(key) == "number" then
missedKeys[#missedKeys+1] = ('`[%s]`'):format(key)
else
missedKeys[#missedKeys+1] = ('`%s`'):format(key)
end
end
end
end
::continue::
end

if #missedKeys == 0 then
return
end

warnings[#warnings+1] = lang.script('DIAG_MISSING_FIELDS', def.class[1], table.concat(missedKeys, ', '))
if #missedKeys == 0 then
return
end

warnings[#warnings+1] = lang.script('DIAG_MISSING_FIELDS', className, table.concat(missedKeys, ', '))
end

if #warnings == 0 then
Expand Down
122 changes: 122 additions & 0 deletions test/diagnostics/missing-fields.lua
Original file line number Diff line number Diff line change
Expand Up @@ -231,3 +231,125 @@ 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 = <!{
a = 1,
b = 2,
}!>
]]

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,
bd = 4,
}!>
]]

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 = <!{x = 1}!>
]]
Loading