diff options
author | CppCXY <812125110@qq.com> | 2024-02-22 20:29:13 +0800 |
---|---|---|
committer | CppCXY <812125110@qq.com> | 2024-02-22 20:29:13 +0800 |
commit | 9b6df71d97a70ee7179949ef9f15368cbf29dcbd (patch) | |
tree | bf7a7e62ed7c164a12bdce437c17262a5235bcec /script/core/diagnostics/inject-field.lua | |
parent | 483fe246b6ae8c25d433aa15e43f04f0e71a74d5 (diff) | |
parent | 3e6fd3ce1f2f0528336ded939d776a29bbfaf2eb (diff) | |
download | lua-language-server-9b6df71d97a70ee7179949ef9f15368cbf29dcbd.zip |
Merge branch 'master' of github.com:CppCXY/lua-language-server
Diffstat (limited to 'script/core/diagnostics/inject-field.lua')
-rw-r--r-- | script/core/diagnostics/inject-field.lua | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/script/core/diagnostics/inject-field.lua b/script/core/diagnostics/inject-field.lua new file mode 100644 index 00000000..2866eef8 --- /dev/null +++ b/script/core/diagnostics/inject-field.lua @@ -0,0 +1,147 @@ +local files = require 'files' +local vm = require 'vm' +local lang = require 'language' +local guide = require 'parser.guide' +local await = require 'await' +local hname = require 'core.hover.name' + +local skipCheckClass = { + ['unknown'] = true, + ['any'] = true, + ['table'] = true, +} + +---@async +return function (uri, callback) + local ast = files.getState(uri) + if not ast then + return + end + + ---@async + local function checkInjectField(src) + await.delay() + + local node = src.node + if not node then + return + end + local ok + for view in vm.getInfer(node):eachView(uri) do + if skipCheckClass[view] then + return + end + ok = true + end + if not ok then + return + end + + local isExact + local class = vm.getDefinedClass(uri, node) + if class then + 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 not isExact + and vm.getDefinedClass(uri, dnode) then + return + end + if def.type == 'doc.type.field' then + return + end + if def.type == 'doc.field' then + return + end + end + + 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 + + local message = lang.script('DIAG_INJECT_FIELD', { + class = vm.getInfer(node):view(uri), + field = guide.getKeyName(src), + fix = howToFix, + }) + if src.type == 'setfield' and src.field then + callback { + start = src.field.start, + finish = src.field.finish, + message = message, + } + elseif src.type == 'setmethod' and src.method then + callback { + start = src.method.start, + finish = src.method.finish, + message = message, + } + end + 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 |