summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--script/core/diagnostics/type-check.lua132
-rw-r--r--script/proto/define.lua1
-rw-r--r--test/diagnostics/init.lua67
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!>)
+]]