diff options
-rw-r--r-- | changelog.md | 1 | ||||
-rw-r--r-- | locale/en-us/script.lua | 2 | ||||
-rw-r--r-- | locale/pt-br/script.lua | 2 | ||||
-rw-r--r-- | locale/zh-cn/script.lua | 2 | ||||
-rw-r--r-- | locale/zh-tw/script.lua | 2 | ||||
-rw-r--r-- | script/core/diagnostics/assign-type-mismatch.lua | 6 | ||||
-rw-r--r-- | script/core/diagnostics/cast-local-type.lua | 6 | ||||
-rw-r--r-- | script/core/diagnostics/cast-type-mismatch.lua | 4 | ||||
-rw-r--r-- | script/core/diagnostics/param-type-mismatch.lua | 4 | ||||
-rw-r--r-- | script/core/diagnostics/return-type-mismatch.lua | 4 | ||||
-rw-r--r-- | script/vm/type.lua | 224 |
11 files changed, 141 insertions, 116 deletions
diff --git a/changelog.md b/changelog.md index a1a8825f..88df6e35 100644 --- a/changelog.md +++ b/changelog.md @@ -168,6 +168,7 @@ server will generate `doc.json` and `doc.md` in `LOGPATH`. [#1153]: https://github.com/sumneko/lua-language-server/issues/1153 [#1177]: https://github.com/sumneko/lua-language-server/issues/1177 +[#1201]: https://github.com/sumneko/lua-language-server/issues/1201 [#1202]: https://github.com/sumneko/lua-language-server/issues/1202 [#1332]: https://github.com/sumneko/lua-language-server/issues/1332 [#1344]: https://github.com/sumneko/lua-language-server/issues/1344 diff --git a/locale/en-us/script.lua b/locale/en-us/script.lua index 9fceac00..51f1c4eb 100644 --- a/locale/en-us/script.lua +++ b/locale/en-us/script.lua @@ -139,7 +139,7 @@ DIAG_REDUNDANT_RETURN_VALUE_RANGE = DIAG_MISSING_RETURN = 'Return value is required here.' DIAG_RETURN_TYPE_MISMATCH = -'The type of the {index} return value is `{def}`, but the actual return is `{ref}`.' +'The type of the {index} return value is `{def}`, but the actual return is `{ref}`.\n{err}' DIAG_UNKNOWN_OPERATOR = 'Unknown operator `{}`.' DIAG_UNREACHABLE_CODE = diff --git a/locale/pt-br/script.lua b/locale/pt-br/script.lua index a6adc651..2e0b0aa6 100644 --- a/locale/pt-br/script.lua +++ b/locale/pt-br/script.lua @@ -139,7 +139,7 @@ DIAG_REDUNDANT_RETURN_VALUE_RANGE = -- TODO: need translate! DIAG_MISSING_RETURN = -- TODO: need translate! 'Return value is required here.' DIAG_RETURN_TYPE_MISMATCH = -- TODO: need translate! -'The type of the {index} return value is `{def}`, but the actual return is `{ref}`.' +'The type of the {index} return value is `{def}`, but the actual return is `{ref}`.\n{err}' DIAG_UNKNOWN_OPERATOR = -- TODO: need translate! 'Unknown operator `{}`.' DIAG_UNREACHABLE_CODE = -- TODO: need translate! diff --git a/locale/zh-cn/script.lua b/locale/zh-cn/script.lua index 86815da3..9a7a76d8 100644 --- a/locale/zh-cn/script.lua +++ b/locale/zh-cn/script.lua @@ -139,7 +139,7 @@ DIAG_REDUNDANT_RETURN_VALUE_RANGE = DIAG_MISSING_RETURN = '此处需要返回值。' DIAG_RETURN_TYPE_MISMATCH = -'第 {index} 个返回值的类型为 `{def}` ,但实际返回的是 `{ref}`。' +'第 {index} 个返回值的类型为 `{def}` ,但实际返回的是 `{ref}`。\n{err}' DIAG_UNKNOWN_OPERATOR = '未知的运算符 `{}`。' DIAG_UNREACHABLE_CODE = diff --git a/locale/zh-tw/script.lua b/locale/zh-tw/script.lua index 060ae8ab..a31c0d67 100644 --- a/locale/zh-tw/script.lua +++ b/locale/zh-tw/script.lua @@ -139,7 +139,7 @@ DIAG_REDUNDANT_RETURN_VALUE_RANGE = DIAG_MISSING_RETURN = '此處需要回傳值。' DIAG_RETURN_TYPE_MISMATCH = -'第 {index} 個回傳值的類型為 `{def}` ,但實際回傳的是 `{ref}`。' +'第 {index} 個回傳值的類型為 `{def}` ,但實際回傳的是 `{ref}`。\n{err}' DIAG_UNKNOWN_OPERATOR = -- TODO: need translate! 'Unknown operator `{}`.' DIAG_UNREACHABLE_CODE = -- TODO: need translate! diff --git a/script/core/diagnostics/assign-type-mismatch.lua b/script/core/diagnostics/assign-type-mismatch.lua index 2bbceebb..6f68a543 100644 --- a/script/core/diagnostics/assign-type-mismatch.lua +++ b/script/core/diagnostics/assign-type-mismatch.lua @@ -96,8 +96,8 @@ return function (uri, callback) end local varNode = vm.compileNode(source) - local suc, errs = vm.canCastType(uri, varNode, valueNode) - if suc then + local errs = {} + if vm.canCastType(uri, varNode, valueNode, errs) then return end @@ -108,8 +108,6 @@ return function (uri, callback) end end - assert(errs) - callback { start = source.start, finish = source.finish, diff --git a/script/core/diagnostics/cast-local-type.lua b/script/core/diagnostics/cast-local-type.lua index 42271e91..cde4ac40 100644 --- a/script/core/diagnostics/cast-local-type.lua +++ b/script/core/diagnostics/cast-local-type.lua @@ -34,10 +34,8 @@ return function (uri, callback) refNode = refNode:copy():setTruthy() end - local suc, errs = vm.canCastType(uri, locNode, refNode) - - if not suc then - assert(errs) + local errs = {} + if not vm.canCastType(uri, locNode, refNode, errs) then callback { start = ref.start, finish = ref.finish, diff --git a/script/core/diagnostics/cast-type-mismatch.lua b/script/core/diagnostics/cast-type-mismatch.lua index 34b12559..c4809338 100644 --- a/script/core/diagnostics/cast-type-mismatch.lua +++ b/script/core/diagnostics/cast-type-mismatch.lua @@ -26,8 +26,8 @@ return function (uri, callback) for _, cast in ipairs(doc.casts) do if not cast.mode and cast.extends then local refNode = vm.compileNode(cast.extends) - local suc, errs = vm.canCastType(uri, defNode, refNode) - if not suc then + local errs = {} + if not vm.canCastType(uri, defNode, refNode, errs) then assert(errs) callback { start = cast.extends.start, diff --git a/script/core/diagnostics/param-type-mismatch.lua b/script/core/diagnostics/param-type-mismatch.lua index 9b2fbc6a..d3ff350d 100644 --- a/script/core/diagnostics/param-type-mismatch.lua +++ b/script/core/diagnostics/param-type-mismatch.lua @@ -100,8 +100,8 @@ return function (uri, callback) -- 因此将假值移除再进行检查 refNode = refNode:copy():setTruthy() end - local suc, errs = vm.canCastType(uri, defNode, refNode) - if not suc then + local errs = {} + if not vm.canCastType(uri, defNode, refNode, errs) then local rawDefNode = getRawDefNode(funcNode, i) assert(errs) callback { diff --git a/script/core/diagnostics/return-type-mismatch.lua b/script/core/diagnostics/return-type-mismatch.lua index 1f335e9d..092b44fc 100644 --- a/script/core/diagnostics/return-type-mismatch.lua +++ b/script/core/diagnostics/return-type-mismatch.lua @@ -61,13 +61,15 @@ return function (uri, callback) retNode = retNode:copy():removeOptional() end end - if not vm.canCastType(uri, docRet, retNode) then + local errs = {} + if not vm.canCastType(uri, docRet, retNode, errs) then callback { start = exp.start, finish = exp.finish, message = lang.script('DIAG_RETURN_TYPE_MISMATCH', { def = vm.getInfer(docRet):view(uri), ref = vm.getInfer(retNode):view(uri), + err = vm.viewTypeErrorMessage(uri, errs), index = i, }), } diff --git a/script/vm/type.lua b/script/vm/type.lua index c60978e5..05b76fc3 100644 --- a/script/vm/type.lua +++ b/script/vm/type.lua @@ -50,9 +50,9 @@ end ---@param parentName string ---@param child vm.node.object ---@param uri uri ----@param err typecheck.err[] +---@param errs? typecheck.err[] ---@return boolean? -local function checkEnum(parentName, child, uri, err) +local function checkEnum(parentName, child, uri, errs) local parentClass = vm.getGlobal('type', parentName) if not parentClass then return nil @@ -74,14 +74,18 @@ local function checkEnum(parentName, child, uri, err) return true end end - err[#err+1] = 'TYPE_ERROR_ENUM_GLOBAL_DISMATCH' - err[#err+1] = child - err[#err+1] = parentClass + if errs then + errs[#errs+1] = 'TYPE_ERROR_ENUM_GLOBAL_DISMATCH' + errs[#errs+1] = child + errs[#errs+1] = parentClass + end return false elseif child.type == 'generic' then ---@cast child vm.generic - err[#err+1] = 'TYPE_ERROR_ENUM_GENERIC_UNSUPPORTED' - err[#err+1] = child + if errs then + errs[#errs+1] = 'TYPE_ERROR_ENUM_GENERIC_UNSUPPORTED' + errs[#errs+1] = child + end return false else ---@cast child parser.object @@ -97,9 +101,11 @@ local function checkEnum(parentName, child, uri, err) end end end - err[#err+1] = 'TYPE_ERROR_ENUM_LITERAL_DISMATCH' - err[#err+1] = child[1] - err[#err+1] = parentClass + if errs then + errs[#errs+1] = 'TYPE_ERROR_ENUM_LITERAL_DISMATCH' + errs[#errs+1] = child[1] + errs[#errs+1] = parentClass + end return false elseif childName == 'function' or childName == 'table' then @@ -110,13 +116,17 @@ local function checkEnum(parentName, child, uri, err) end end end - err[#err+1] = 'TYPE_ERROR_ENUM_OBJECT_DISMATCH' - err[#err+1] = child - err[#err+1] = parentClass + if errs then + errs[#errs+1] = 'TYPE_ERROR_ENUM_OBJECT_DISMATCH' + errs[#errs+1] = child + errs[#errs+1] = parentClass + end return false end - err[#err+1] = 'TYPE_ERROR_ENUM_NO_OBJECT' - err[#err+1] = child + if errs then + errs[#errs+1] = 'TYPE_ERROR_ENUM_NO_OBJECT' + errs[#errs+1] = child + end return false end end @@ -124,17 +134,19 @@ end ---@param parent vm.node.object ---@param child vm.node.object ---@param mark table ----@param err typecheck.err[] +---@param errs? typecheck.err[] ---@return boolean -local function checkValue(parent, child, mark, err) +local function checkValue(parent, child, mark, errs) if parent.type == 'doc.type.integer' then if child.type == 'integer' or child.type == 'doc.type.integer' or child.type == 'number' then if parent[1] ~= child[1] then - err[#err+1] = 'TYPE_ERROR_INTEGER_DISMATCH' - err[#err+1] = child[1] - err[#err+1] = parent[1] + if errs then + errs[#errs+1] = 'TYPE_ERROR_INTEGER_DISMATCH' + errs[#errs+1] = child[1] + errs[#errs+1] = parent[1] + end return false end end @@ -147,9 +159,11 @@ local function checkValue(parent, child, mark, err) or child.type == 'doc.type.string' or child.type == 'doc.field.name' then if parent[1] ~= child[1] then - err[#err+1] = 'TYPE_ERROR_STRING_DISMATCH' - err[#err+1] = child[1] - err[#err+1] = parent[1] + if errs then + errs[#errs+1] = 'TYPE_ERROR_STRING_DISMATCH' + errs[#errs+1] = child[1] + errs[#errs+1] = parent[1] + end return false end end @@ -160,9 +174,11 @@ local function checkValue(parent, child, mark, err) if child.type == 'boolean' or child.type == 'doc.type.boolean' then if parent[1] ~= child[1] then - err[#err+1] = 'TYPE_ERROR_BOOLEAN_DISMATCH' - err[#err+1] = child[1] - err[#err+1] = parent[1] + if errs then + errs[#errs+1] = 'TYPE_ERROR_BOOLEAN_DISMATCH' + errs[#errs+1] = child[1] + errs[#errs+1] = parent[1] + end return false end end @@ -179,16 +195,20 @@ local function checkValue(parent, child, mark, err) local knode = vm.compileNode(pfield.name) local cvalues = vm.getTableValue(uri, tnode, knode, true) if not cvalues then - err[#err+1] = 'TYPE_ERROR_TABLE_NO_FIELD' - err[#err+1] = pfield.name + if errs then + errs[#errs+1] = 'TYPE_ERROR_TABLE_NO_FIELD' + errs[#errs+1] = pfield.name + end return false end local pvalues = vm.compileNode(pfield.extends) - if vm.isSubType(uri, cvalues, pvalues, mark, err) == false then - err[#err+1] = 'TYPE_ERROR_TABLE_FIELD_DISMATCH' - err[#err+1] = pfield.name - err[#err+1] = cvalues - err[#err+1] = pvalues + if vm.isSubType(uri, cvalues, pvalues, mark, errs) == false then + if errs then + errs[#errs+1] = 'TYPE_ERROR_TABLE_FIELD_DISMATCH' + errs[#errs+1] = pfield.name + errs[#errs+1] = cvalues + errs[#errs+1] = pvalues + end return false end end @@ -219,17 +239,15 @@ end ---@param child vm.node|string|vm.node.object ---@param parent vm.node|string|vm.node.object ---@param mark? table ----@param err? typecheck.err[] +---@param errs? typecheck.err[] ---@return boolean|nil ----@return typecheck.err[] # errors -function vm.isSubType(uri, child, parent, mark, err) +function vm.isSubType(uri, child, parent, mark, errs) mark = mark or {} - err = err or {} if type(child) == 'string' then local global = vm.getGlobal('type', child) if not global then - return nil, err + return nil end child = global elseif child.type == 'vm.node' then @@ -238,41 +256,45 @@ function vm.isSubType(uri, child, parent, mark, err) for n in child:eachObject() do if getNodeName(n) then hasKnownType = hasKnownType + 1 - if vm.isSubType(uri, n, parent, mark, err) == true then - return true, err + if vm.isSubType(uri, n, parent, mark, errs) == true then + return true end end end if hasKnownType > 0 then - if hasKnownType > 1 then - err[#err+1] = 'TYPE_ERROR_CHILD_ALL_DISMATCH' - err[#err+1] = child - err[#err+1] = parent + if errs and hasKnownType > 1 then + errs[#errs+1] = 'TYPE_ERROR_CHILD_ALL_DISMATCH' + errs[#errs+1] = child + errs[#errs+1] = parent end - return false, err + return false end - return true, err + return true else local weakNil = config.get(uri, 'Lua.type.weakNilCheck') for n in child:eachObject() do local nodeName = getNodeName(n) if nodeName and not (nodeName == 'nil' and weakNil) - and vm.isSubType(uri, n, parent, mark, err) == false then - err[#err+1] = 'TYPE_ERROR_UNION_DISMATCH' - err[#err+1] = n - err[#err+1] = parent - return false, err + and vm.isSubType(uri, n, parent, mark, errs) == false then + if errs then + errs[#errs+1] = 'TYPE_ERROR_UNION_DISMATCH' + errs[#errs+1] = n + errs[#errs+1] = parent + end + return false end end if not weakNil and child:isOptional() then - if vm.isSubType(uri, 'nil', parent, mark, err) == false then - err[#err+1] = 'TYPE_ERROR_OPTIONAL_DISMATCH' - err[#err+1] = parent - return false, err + if vm.isSubType(uri, 'nil', parent, mark, errs) == false then + if errs then + errs[#errs+1] = 'TYPE_ERROR_OPTIONAL_DISMATCH' + errs[#errs+1] = parent + end + return false end end - return true, err + return true end end @@ -280,18 +302,18 @@ function vm.isSubType(uri, child, parent, mark, err) local childName = getNodeName(child) if childName == 'any' or childName == 'unknown' then - return true, err + return true end if not childName or isAlias(childName, uri) then - return nil, err + return nil end if type(parent) == 'string' then local global = vm.getGlobal('type', parent) if not global then - return false, err + return false end parent = global elseif parent.type == 'vm.node' then @@ -299,25 +321,25 @@ function vm.isSubType(uri, child, parent, mark, err) for n in parent:eachObject() do if getNodeName(n) then hasKnownType = hasKnownType + 1 - if vm.isSubType(uri, child, n, mark, err) == true then - return true, err + if vm.isSubType(uri, child, n, mark, errs) == true then + return true end end end if parent:isOptional() then - if vm.isSubType(uri, child, 'nil', mark, err) == true then - return true, err + if vm.isSubType(uri, child, 'nil', mark, errs) == true then + return true end end if hasKnownType > 0 then - if hasKnownType > 1 then - err[#err+1] = 'TYPE_ERROR_PARENT_ALL_DISMATCH' - err[#err+1] = child - err[#err+1] = parent + if errs and hasKnownType > 1 then + errs[#errs+1] = 'TYPE_ERROR_PARENT_ALL_DISMATCH' + errs[#errs+1] = child + errs[#errs+1] = parent end - return false, err + return false end - return true, err + return true end ---@cast parent vm.node.object @@ -325,54 +347,58 @@ function vm.isSubType(uri, child, parent, mark, err) local parentName = getNodeName(parent) if parentName == 'any' or parentName == 'unknown' then - return true, err + return true end if not parentName or isAlias(parentName, uri) then - return nil, err + return nil end if childName == parentName then - if not checkValue(parent, child, mark, err) then - return false, err + if not checkValue(parent, child, mark, errs) then + return false end - return true, err + return true end if parentName == 'number' and childName == 'integer' then - return true, err + return true end if parentName == 'integer' and childName == 'number' then if config.get(uri, 'Lua.type.castNumberToInteger') then - return true, err + return true end if child.type == 'number' and child[1] and not math.tointeger(child[1]) then - err[#err+1] = 'TYPE_ERROR_NUMBER_LITERAL_TO_INTEGER' - err[#err+1] = child[1] - return false, err + if errs then + errs[#errs+1] = 'TYPE_ERROR_NUMBER_LITERAL_TO_INTEGER' + errs[#errs+1] = child[1] + end + return false end if child.type == 'global' and child.cate == 'type' then - err[#err+1] = 'TYPE_ERROR_NUMBER_TYPE_TO_INTEGER' - return false, err + if errs then + errs[#errs+1] = 'TYPE_ERROR_NUMBER_TYPE_TO_INTEGER' + end + return false end - return true, err + return true end - local isEnum = checkEnum(parentName, child, uri, err) + local isEnum = checkEnum(parentName, child, uri, errs) if isEnum ~= nil then - return isEnum, err + return isEnum end if parentName == 'table' and not guide.isBasicType(childName) then - return true, err + return true end if childName == 'table' and not guide.isBasicType(parentName) then - return true, err + return true end -- check class parent @@ -386,8 +412,8 @@ function vm.isSubType(uri, child, parent, mark, err) for _, ext in ipairs(set.extends) do if ext.type == 'doc.extends.name' and (not isBasicType or guide.isBasicType(ext[1])) - and vm.isSubType(uri, ext[1], parent, mark, err) == true then - return true, err + and vm.isSubType(uri, ext[1], parent, mark, errs) == true then + return true end end end @@ -405,13 +431,15 @@ function vm.isSubType(uri, child, parent, mark, err) if guide.isBasicType(childName) and guide.isLiteral(child) and vm.isSubType(uri, parentName, childName, mark) then - return true, err + return true end - err[#err+1] = 'TYPE_ERROR_DISMATCH' - err[#err+1] = child - err[#err+1] = parent - return false, err + if errs then + errs[#errs+1] = 'TYPE_ERROR_DISMATCH' + errs[#errs+1] = child + errs[#errs+1] = parent + end + return false end ---@param node string|vm.node|vm.object @@ -554,9 +582,9 @@ end ---@param uri uri ---@param defNode vm.node ---@param refNode vm.node +---@param errs typecheck.err[]? ---@return boolean ----@return typecheck.err[]? -function vm.canCastType(uri, defNode, refNode) +function vm.canCastType(uri, defNode, refNode, errs) local defInfer = vm.getInfer(defNode) local refInfer = vm.getInfer(refNode) @@ -594,13 +622,11 @@ function vm.canCastType(uri, defNode, refNode) end end - local suc, err = vm.isSubType(uri, refNode, defNode) - - if suc then + if vm.isSubType(uri, refNode, defNode, {}, errs) then return true end - return false, err + return false end local ErrorMessageMap = { |