diff options
-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..010764ba --- /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 = 'Argument of type in '..argm..' is not assignable to parameter of type in ['..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 699bcd15..fe7d171b 100644 --- a/script/proto/define.lua +++ b/script/proto/define.lua @@ -17,6 +17,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 5f69bdc9..e3bf9516 100644 --- a/test/diagnostics/init.lua +++ b/test/diagnostics/init.lua @@ -460,7 +460,7 @@ f(1, 2, 3) ]] TEST [[ -<!unpack!>(1) +<!unpack!>(<!1!>) ]] TEST [[ @@ -1133,3 +1133,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!>) +]] |