diff options
author | arthur <1348392749@qq.com> | 2021-10-08 15:10:23 +0800 |
---|---|---|
committer | arthur <1348392749@qq.com> | 2021-10-08 15:10:23 +0800 |
commit | 21d92f73c1236ffce188efa043f54184714cafa4 (patch) | |
tree | cac2218b65c1d20c21e173e1f57aa4c9dafa2752 /script | |
parent | 842f738db5ccdce6f470a9c729287cb81796594d (diff) | |
download | lua-language-server-21d92f73c1236ffce188efa043f54184714cafa4.zip |
优化代码结构,修复了一些bug
Diffstat (limited to 'script')
-rw-r--r-- | script/core/diagnostics/type-check.lua | 422 |
1 files changed, 349 insertions, 73 deletions
diff --git a/script/core/diagnostics/type-check.lua b/script/core/diagnostics/type-check.lua index ff3a9124..11678b38 100644 --- a/script/core/diagnostics/type-check.lua +++ b/script/core/diagnostics/type-check.lua @@ -3,27 +3,211 @@ local guide = require 'parser.guide' local vm = require 'vm' local infer = require 'core.infer' local await = require 'await' -local hasVarargs +local hasVarargs, errType -local function inTypes(param, args) +local tableMap = { + ['table'] = true, + ['array'] = true, + ['ltable'] = true, + ['[]'] = true, +} + +local typeNameMap = { + ['doc.extends.name'] = true, + ['doc.class.name'] = true, + ['doc.alias.name'] = true, + ['doc.type.name'] = true, + ['doc.type.enum'] = true, + ['doc.resume'] = true, + +} + +local function isTable(name) + if tableMap[name] + ---table<K: number, V: string> table + or tableMap[name:sub(1, 5)] + ---string[] + or tableMap[name:sub(-2, -1)] then + return true + end + return false +end + +local function isUserDefineClass(name) + if vm.isBuiltinType(name) then + return false + else + local defs = vm.getDocDefines(name) + for _, v in ipairs(defs) do + if v.type == 'doc.class.name' then + return true + end + end + end + return false +end + +local function isClassOralias(typeName) + if not typeName then + return false + elseif typeNameMap[typeName] + or vm.isBuiltinType(typeName) then + return true + else + return false + end +end + +local function isGeneric(type) + if type.typeGeneric then + return true + end + return false +end + +local function compatibleType(param, args) + if string.sub(param.type, 1, 9) == 'doc.type.' + and not param[1] then + param[1] = string.sub(param.type, 10) + end for _, v in ipairs(args) do - if param[1] == v[1] then + if v[1] == 'any' then return true - elseif param[1] == 'number' - and v[1] == 'integer' then + elseif param[1] == v[1] then + return true + elseif (param[1] == 'number' or param[1] == 'integer') + and (v[1] == 'integer' or v[1] == 'number') then + return true + elseif v[1] == 'string' then + ---处理alias + --@alias searchmode '"ref"'|'"def"' + if param[1] and param[1]:sub(1,1) == '"' then + return true + end + elseif isTable(v.type or v[1]) and isTable(param[1] or param.type) then return true end end return false end -local function excludeSituations(types) - if not types[1] - or not types[1][1] - or types[1].typeGeneric then - return true +-- local function addFatherClass(types, type) +-- if not type[1] then +-- return +-- end +-- local docDefs = vm.getDocDefines(type[1]) +-- for _, doc in ipairs(docDefs) do +-- if doc.parent +-- and doc.parent.type == 'doc.class' +-- and doc.parent.extends then +-- for _, tp in ipairs(doc.parent.extends) do +-- if tp.type == 'doc.extends.name' then +-- types[#types+1] = { +-- [1] = tp[1], +-- type = 'doc.class.name' +-- } +-- end +-- end +-- end +-- end +-- end + +local function addFatherClass(infers) + for k in pairs(infers) do + local docDefs = vm.getDocDefines(k) + for _, doc in ipairs(docDefs) do + if doc.parent + and doc.parent.type == 'doc.class' + and doc.parent.extends then + for _, tp in ipairs(doc.parent.extends) do + if tp.type == 'doc.extends.name' then + infers[tp[1]] = true + end + end + end + end + end +end + +local function getParamTypes(arg) + if not arg then + return false + end + local types + ---处理doc.type.function + if arg.type == 'doc.type.arg' then + if arg.name and arg.name[1] == '...' then + types = { + [1] = { + [1] = '...', + type = 'varargs' + } + } + return true, types + end + types = arg.extends.types + return true, types + ---处理function + elseif arg.type == 'local' then + local argDefs = vm.getDefs(arg) + if #argDefs == 0 then + return false + end + types = {} + ---method, 如果self没有定义为一个class或者type,则认为它为any + if arg.tag == 'self' then + for _, argDef in ipairs(argDefs) do + if argDef.type == 'doc.class.name' + or argDef.type == 'doc.type.name' + or argDef.type == 'doc.type.enum' + or argDef.type == 'doc.type.ltable' then + types[#types+1] = argDef + end + end + if #types == 0 then + return false + end + return true, types + else + for _, argDef in ipairs(argDefs) do + if argDef.type == 'doc.param' and argDef.extends then + types = argDef.extends.types + if argDef.optional then + types[#types+1] = { + [1] = 'nil', + type = 'nil' + } + end + elseif argDef.type == 'doc.type.enum' + or argDef.type == 'doc.type.ltable' then + types[#types+1] = argDef + ---变长参数 + elseif argDef.name and argDef.name[1] == '...' then + types = { + [1] = { + [1] = '...', + type = 'varargs' + } + } + break + end + end + if #types == 0 then + return false + else + return true, types + end + end + ---处理只有一个可变参数 + elseif arg.type == '...' then + types = { + [1] = { + [1] = '...', + type = 'varargs' + } + } + return true, types end - return false end local function getInfoFromDefs(defs) @@ -35,66 +219,177 @@ local function getInfoFromDefs(defs) if def.value then def = def.value end - if mark[def] then - goto CONTINUE + if not mark[def] then + mark[def] = true + if def.type == 'function' + or def.type == 'doc.type.function' then + if def.args then + for _, arg in ipairs(def.args) do + local suc, types = getParamTypes(arg) + if suc then + local plusAlias = {} + for i, tp in ipairs(types) do + local aliasDefs = vm.getDefs(tp) + for _, v in ipairs(aliasDefs) do + ---TODO(arthur) + -- if not v.type then + -- end + if v[1] ~= tp[1] + and isClassOralias(v.type) then + plusAlias[#plusAlias+1] = v + end + if not v[1] or not v.type then + log.warn('type-check: if not v[1] or not v.type') + end + end + plusAlias[#plusAlias+1] = types[i] + end + funcArgsType[#funcArgsType+1] = plusAlias + else + ---如果有一个参数没有定义type,都会跳过检查 + funcArgsType = {} + break + end + end + end + if #funcArgsType > 0 then + paramsTypes[#paramsTypes+1] = funcArgsType + end + end end - mark[def] = true + end + return paramsTypes +end - if def.type == 'function' - or def.type == 'doc.type.function' then - if def.args then - for _, arg in ipairs(def.args) do - local types - if arg.docParam and arg.docParam.extends then - types = arg.docParam.extends.types - ---变长参数 - elseif arg.name and arg.name[1] == '...' then - types = { - [1] = { - [1] = '...', - type = 'varargs' - } - } - elseif arg.type == 'doc.type.arg' then - types = arg.extends.types - else - goto CONTINUE - end - ---如果是很复杂的type,比如泛型什么的,先不检查 - if excludeSituations(types) then - goto CONTINUE - end - funcArgsType[#funcArgsType+1] = types +local function getArgsInfo(callArgs) + local callArgsType = {} + for _, arg in ipairs(callArgs) do + -- local defs = vm.getDefs(arg) + local infers = infer.searchInfers(arg) + if infers['_G'] or infer['_ENV'] then + infers['_G'] = nil + infers['_ENV'] = nil + infers['table'] = true + end + local hasAny = infers['any'] + ---处理继承 + addFatherClass(infers) + if not hasAny then + infers['any'] = nil + infers['unknown'] = nil + end + local types = {} + if not infers['table'] then + for k in pairs(infers) do + if not vm.isBuiltinType(k) + and isUserDefineClass(k) then + infers['table'] = true + break end end - if #funcArgsType > 0 then - paramsTypes[#paramsTypes+1] = funcArgsType + end + for k in pairs(infers) do + if k then + types[#types+1] = { + [1] = k, + type = k + } end end - ::CONTINUE:: + if #types < 1 then + return false + end + types.start = arg.start + types.finish = arg.finish + callArgsType[#callArgsType+1] = types + -- local defs = vm.getDefs(arg) + -- local types = {} + -- types.typeMap = {} + -- for _, def in ipairs(defs) do + -- if vm.isBuiltinType(def.type) then + -- types[#types+1] = { + -- [1] = def.type, + -- type = def.type + -- } + -- elseif def.type == 'doc.class.name' + -- or def.type == 'doc.type.name' + -- or def.type == 'doc.type.enum' + -- or def.type == 'doc.type.ltable' then + -- if def[1] then + -- if not types.typeMap[def[1]] then + -- types[#types+1] = def + -- types.typeMap[def[1]] = true + -- end + -- else + -- types[#types+1] = def + -- end + -- elseif def.type == 'doc.type' then + -- print(1) + -- elseif def.type == 'doc.type.arg' then + -- for _, tp in ipairs(arg.extends.types) do + -- types[#types+1] = arg.extends.types[1] + -- end + -- elseif def.type == 'doc.param' then + -- for i, tp in ipairs(def.extends.types) do + -- types[#types+1] = def.extends.types[i] + -- end + -- if def.optional then + -- types[#types+1] = { + -- [1] = 'nil', + -- type = 'nil' + -- } + -- end + -- end + -- end + -- for _, tp in ipairs(types) do + -- if not vm.isBuiltinType(tp.type) then + -- addFatherClass(types, tp) + -- end + -- end + -- types.start = arg.start + -- types.finish = arg.finish + -- if #types == 0 then + -- types = { + -- [1] = { + -- [1] = 'any', + -- type = 'any', + -- } + -- } + -- end + -- callArgsType[#callArgsType+1] = types end - return paramsTypes + return true, callArgsType end local function matchParams(paramsTypes, i, arg) local flag = '' local messages = {} + ---paramsTypes 存的是多个定义的参数信息 + ---paramTypes 存的是单独一个定义的参数信息 + ---param 是某一个定义中的第i个参数的信息 for _, paramTypes in ipairs(paramsTypes) do if not paramTypes[i] then goto CONTINUE end + flag = '' for _, param in ipairs(paramTypes[i]) do + if param[1] == '...' then + hasVarargs = true + return true ---如果形参的类型在实参里面 - if inTypes(param, arg) - or param[1] == 'any' - or arg.type == 'any' then + elseif compatibleType(param, arg) + or param[1] == 'any' then flag = '' return true - elseif param[1] == '...' then - hasVarargs = true + ---如果是泛型,不检查 + elseif isGeneric(param) then return true else - flag = flag ..' ' .. param[1] + ---TODO(arthur) 什么时候param[1]是nil? + if param[1] and not errType[param[1]] then + errType[param[1]] = true + flag = flag ..' ' .. (param[1] or '') + end end end if flag ~= '' then @@ -113,6 +408,7 @@ local function matchParams(paramsTypes, i, arg) end return false, messages end + return function (uri, callback) local ast = files.getState(uri) if not ast then @@ -124,29 +420,9 @@ return function (uri, callback) end await.delay() local callArgs = source.args - local callArgsType = {} - for _, arg in ipairs(callArgs) do - local infers = infer.searchInfers(arg) - if infers['_G'] or infer['_ENV'] then - infers['_G'] = nil - infers['_ENV'] = nil - infers['table'] = true - end - local types = {} - for k in pairs(infers) do - if k then - types[#types+1] = { - [1] = k, - type = k - } - end - end - if #types < 1 then - return - end - types.start = arg.start - types.finish = arg.finish - callArgsType[#callArgsType+1] = types + local suc, callArgsType = getArgsInfo(callArgs) + if not suc then + return end local func = source.node local defs = vm.getDefs(func) @@ -156,6 +432,7 @@ return function (uri, callback) for i, arg in ipairs(callArgsType) do ---遍历形参 hasVarargs = false + errType = {} local match, messages = matchParams(paramsTypes, i, arg) if hasVarargs then return @@ -173,5 +450,4 @@ return function (uri, callback) end ---所有参数都匹配了 end) - end |