summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author最萌小汐 <sumneko@hotmail.com>2023-08-23 15:13:51 +0800
committer最萌小汐 <sumneko@hotmail.com>2023-08-23 15:13:51 +0800
commitc2018e05de0e9eebadbe094357d22883a608fdf5 (patch)
tree9a95a05a88f0e828c37c973732ef15fc80ce216b
parentcb16010fbb4128e2a5a31013e02b8cc4a4318be8 (diff)
downloadlua-language-server-c2018e05de0e9eebadbe094357d22883a608fdf5.zip
support `---@class (exact)`
#1990
-rw-r--r--changelog.md10
-rw-r--r--script/core/diagnostics/inject-field.lua81
-rw-r--r--script/parser/luadoc.lua1
-rw-r--r--test/diagnostics/inject-field.lua20
4 files changed, 99 insertions, 13 deletions
diff --git a/changelog.md b/changelog.md
index a105d276..474625a8 100644
--- a/changelog.md
+++ b/changelog.md
@@ -21,6 +21,16 @@
assert(isAnimalType(animal, 'Cat'))
```
+* `NEW` `---@class` supports attribute `exact`
+ ```lua
+ ---@class (exact) Point
+ ---@field x number
+ ---@field y number
+ local m = {}
+ m.x = 1 -- OK
+ m.y = 2 -- OK
+ m.z = 3 -- Warning
+ ```
* `FIX` wrong hover and signature for method with varargs and overloads
* `FIX` [#2155]
diff --git a/script/core/diagnostics/inject-field.lua b/script/core/diagnostics/inject-field.lua
index 570ca270..2866eef8 100644
--- a/script/core/diagnostics/inject-field.lua
+++ b/script/core/diagnostics/inject-field.lua
@@ -37,14 +37,29 @@ return function (uri, callback)
return
end
+ local isExact
local class = vm.getDefinedClass(uri, node)
if class then
- return
+ for _, doc in ipairs(class:getSets(uri)) do
+ if vm.docHasAttr(doc, 'exact') then
+ isExact = true
+ break
+ end
+ end
+ if not isExact then
+ return
+ end
+ if src.type == 'setmethod'
+ and not guide.getSelfNode(node) then
+ return
+ end
end
for _, def in ipairs(vm.getDefs(src)) do
local dnode = def.node
- if dnode and vm.getDefinedClass(uri, dnode) then
+ if dnode
+ and not isExact
+ and vm.getDefinedClass(uri, dnode) then
return
end
if def.type == 'doc.type.field' then
@@ -55,16 +70,19 @@ return function (uri, callback)
end
end
- local howToFix = lang.script('DIAG_INJECT_FIELD_FIX_CLASS', {
- node = hname(node),
- fix = '---@class',
- })
- for _, ndef in ipairs(vm.getDefs(node)) do
- if ndef.type == 'doc.type.table' then
- howToFix = lang.script('DIAG_INJECT_FIELD_FIX_TABLE', {
- fix = '[any]: any',
- })
- break
+ local howToFix = ''
+ if not isExact then
+ howToFix = lang.script('DIAG_INJECT_FIELD_FIX_CLASS', {
+ node = hname(node),
+ fix = '---@class',
+ })
+ for _, ndef in ipairs(vm.getDefs(node)) do
+ if ndef.type == 'doc.type.table' then
+ howToFix = lang.script('DIAG_INJECT_FIELD_FIX_TABLE', {
+ fix = '[any]: any',
+ })
+ break
+ end
end
end
@@ -79,7 +97,7 @@ return function (uri, callback)
finish = src.field.finish,
message = message,
}
- elseif src.type == 'setfield' and src.method then
+ elseif src.type == 'setmethod' and src.method then
callback {
start = src.method.start,
finish = src.method.finish,
@@ -89,4 +107,41 @@ return function (uri, callback)
end
guide.eachSourceType(ast.ast, 'setfield', checkInjectField)
guide.eachSourceType(ast.ast, 'setmethod', checkInjectField)
+
+ ---@async
+ local function checkExtraTableField(src)
+ await.delay()
+
+ if not src.bindSource then
+ return
+ end
+ if not vm.docHasAttr(src, 'exact') then
+ return
+ end
+ local value = src.bindSource.value
+ if not value or value.type ~= 'table' then
+ return
+ end
+ for _, field in ipairs(value) do
+ local defs = vm.getDefs(field)
+ for _, def in ipairs(defs) do
+ if def.type == 'doc.field' then
+ goto nextField
+ end
+ end
+ local message = lang.script('DIAG_INJECT_FIELD', {
+ class = vm.getInfer(src):view(uri),
+ field = guide.getKeyName(src),
+ fix = '',
+ })
+ callback {
+ start = field.start,
+ finish = field.finish,
+ message = message,
+ }
+ ::nextField::
+ end
+ end
+
+ guide.eachSourceType(ast.ast, 'doc.class', checkExtraTableField)
end
diff --git a/script/parser/luadoc.lua b/script/parser/luadoc.lua
index 81869638..d7338918 100644
--- a/script/parser/luadoc.lua
+++ b/script/parser/luadoc.lua
@@ -841,6 +841,7 @@ local docSwitch = util.switch()
operators = {},
calls = {},
}
+ result.docAttr = parseDocAttr(result)
result.class = parseName('doc.class.name', result)
if not result.class then
pushWarning {
diff --git a/test/diagnostics/inject-field.lua b/test/diagnostics/inject-field.lua
index f4d847e9..9bb0f8fc 100644
--- a/test/diagnostics/inject-field.lua
+++ b/test/diagnostics/inject-field.lua
@@ -62,3 +62,23 @@ local t
t.x = 1 -- OK
t.y = 2 -- OK
]]
+
+
+TEST [[
+---@class (exact) Class
+---@field x number
+local m = {
+ x = 1, -- OK
+ <!y!> = 2, -- Warning
+}
+
+m.x = 1 -- OK
+m.<!y!> = 2 -- Warning
+
+function m:init() -- OK
+ self.x = 1 -- OK
+ self.<!y!> = 2 -- Warning
+ function self:<!xx!>() -- Warning
+ end
+end
+]]