diff options
author | unknown <luoxingyue@cn.net.ntes> | 2021-09-23 12:10:01 +0800 |
---|---|---|
committer | unknown <luoxingyue@cn.net.ntes> | 2021-09-23 13:05:08 +0800 |
commit | f777e326716005c65aab50dfb2b974cf983021e2 (patch) | |
tree | 4d683eff97fcb781c585bf52183c44994d510f0c | |
parent | 0c8c6bbf23082d0b858646846a47a3001f718ae2 (diff) | |
download | lua-language-server-f777e326716005c65aab50dfb2b974cf983021e2.zip |
add typecheck
-rw-r--r-- | script/core/diagnostics/type-check.lua | 132 | ||||
-rw-r--r-- | script/proto/define.lua | 1 | ||||
-rw-r--r-- | test/diagnostics/init.lua | 67 |
3 files changed, 199 insertions, 1 deletions
diff --git a/script/core/diagnostics/type-check.lua b/script/core/diagnostics/type-check.lua new file mode 100644 index 00000000..99b40303 --- /dev/null +++ b/script/core/diagnostics/type-check.lua @@ -0,0 +1,132 @@ +local files = require 'files' +local guide = require 'parser.guide' +local vm = require 'vm' + +local function hasTypeDoc(obj) + if obj.type == 'getlocal' + and obj.node + and obj.node.type == 'local' + and obj.node.bindDocs + and obj.node.bindDocs[1] + and obj.node.bindDocs[1].type == 'doc.type' then + return true + end + return false +end +local function inTypes(param, args) + for _, v in ipairs(args.type) do + if param[1] == v[1] then + return true + end + end + return false +end +return function (uri, callback) + local ast = files.getState(uri) + if not ast then + return + end + guide.eachSourceType(ast.ast, 'call', function (source) + if not source.args then + return + end + local callArgs = source.args + local callArgsType = {} + for _, arg in ipairs(callArgs) do + ---检查字面值类型的参数调用 + if guide.isLiteral(arg) then + callArgsType[#callArgsType+1] = { + type = { + [1] = { + [1] = arg.type, + ['type'] = arg.type, + } + }, + start = arg.start, + finish = arg.finish, + } + ---检查传入参数有完整信息的情况 + elseif hasTypeDoc(arg) then + callArgsType[#callArgsType+1] = { + type = arg.node.bindDocs[1].types, + start = arg.start, + finish = arg.finish, + } + else + return + end + end + local func = source.node + local defs = vm.getDefs(func) + local funcArgsType = {} + ---只检查有emmy注释定义的函数 + ---获取函数定义的参数信息时,遇到一个定义就停止获取 + for _, def in ipairs(defs) do + if def.value then + def = def.value + end + if def.type == 'function' then + if def.args then + for _, arg in ipairs(def.args) do + if arg.docParam and arg.docParam.extends then + ---如果是很复杂的type,比如泛型什么的,先不检查 + if not arg.docParam.extends.types[1][1] + or arg.docParam.extends.types[1].typeGeneric then + return + end + funcArgsType[#funcArgsType+1] = arg.docParam.extends.types + else + funcArgsType = {} + end + end + end + break + end + end + if #funcArgsType == 0 then + return + end + local message = '' + ---先遍历实参 + for i, arg in ipairs(callArgsType) do + if not funcArgsType[i] then + ---由另一个检查来处理 + -- message = 'too many call args' + -- callback{ + -- start = arg.start, + -- finish = arg.finish, + -- message = message + -- } + return + end + local flag = '' + ---遍历形参 + for _, tp in ipairs(funcArgsType[i]) do + ---如果形参的类型在实参里面 + if inTypes(tp, arg) + or tp[1] == 'any' + or arg.type == 'any' then + flag = '' + break + else + flag = flag ..' ' .. tp[1] + end + end + if flag ~= '' then + local argm = '[ ' + for _, v in ipairs(arg.type) do + argm = argm .. v[1]..' ' + end + argm = argm .. ']' + message = 'callArg: '..argm..' has no type belong to ['..flag..' ]' + callback{ + start = arg.start, + finish = arg.finish, + message = message + } + return + end + end + end) + +end diff --git a/script/proto/define.lua b/script/proto/define.lua index 2e61bc3e..2e4116fd 100644 --- a/script/proto/define.lua +++ b/script/proto/define.lua @@ -49,6 +49,7 @@ m.DiagnosticSeverity = { --- 诊断类型与默认等级 ---@type table<string, DiagnosticDefaultSeverity> m.DiagnosticDefaultSeverity = { + ['type-check'] = "Hint", ['unused-local'] = 'Hint', ['unused-function'] = 'Hint', ['undefined-global'] = 'Warning', diff --git a/test/diagnostics/init.lua b/test/diagnostics/init.lua index 54ac73ef..79b267c3 100644 --- a/test/diagnostics/init.lua +++ b/test/diagnostics/init.lua @@ -491,7 +491,7 @@ f(1, 2, 3) ]] TEST [[ -<!unpack!>(1) +<!unpack!>(<!1!>) ]] TEST [[ @@ -1164,3 +1164,68 @@ return { [<!2!>] = 4, } ]] + +TEST [[ +---@param table table +---@param metatable table +---@return table +function Setmetatable(table, metatable) end + +Setmetatable(<!1!>, {}) +]] + +TEST [[ +---@param table table +---@param metatable table +---@return table +function Setmetatable(table, metatable) end + +Setmetatable(<!'name'!>, {}) + +]] + +TEST [[ +---@param table table +---@param metatable table +---@return table +function Setmetatable(table, metatable) end + +---@type table +local name +---@type function +local mt +---err +Setmetatable(name, <!mt!>) +]] + +TEST [[ +---@param p1 string +---@param p2 number +---@return table +local function func1(p1, p2) end + +---@type string +local s +---@type table +local t +---err +func1(s, <!t!>) +]] + +TEST [[ +---@class bird +---@field wing string + +---@class eagle +---@field family bird + +---@class chicken +---@field family bird + +---@param bd eagle +local function fly(bd) end + +---@type chicken +local h +fly(<!h!>) +]] |