diff options
author | 最萌小汐 <sumneko@hotmail.com> | 2022-06-16 20:37:00 +0800 |
---|---|---|
committer | 最萌小汐 <sumneko@hotmail.com> | 2022-06-16 20:37:00 +0800 |
commit | 8a9e8d9cfd12d43496c2bf16f70874af550d4e7e (patch) | |
tree | 5dfdec3988e74a8b4eae33849870051a96420d94 | |
parent | ea1c5197c469779ba7cd18f1a8da5da9908f2149 (diff) | |
download | lua-language-server-8a9e8d9cfd12d43496c2bf16f70874af550d4e7e.zip |
type-check: `cast-local-type`
-rw-r--r-- | script/core/diagnostics/cast-local-type.lua | 40 | ||||
-rw-r--r-- | script/core/diagnostics/type-check-assign.lua | 20 | ||||
-rw-r--r-- | script/core/diagnostics/type-check.lua | 3 | ||||
-rw-r--r-- | script/proto/define.lua | 6 | ||||
-rw-r--r-- | script/vm/type.lua | 104 | ||||
-rw-r--r-- | test/diagnostics/common.lua | 7 |
6 files changed, 141 insertions, 39 deletions
diff --git a/script/core/diagnostics/cast-local-type.lua b/script/core/diagnostics/cast-local-type.lua new file mode 100644 index 00000000..1284e934 --- /dev/null +++ b/script/core/diagnostics/cast-local-type.lua @@ -0,0 +1,40 @@ +local files = require 'files' +local lang = require 'language' +local guide = require 'parser.guide' +local vm = require 'vm' +local await = require 'await' + +---@async +return function (uri, callback) + local state = files.getState(uri) + if not state then + return + end + + ---@async + guide.eachSourceType(state.ast, 'local', function (loc) + if not loc.ref then + return + end + local locNode = vm.compileNode(loc) + if not locNode:getData 'hasDefined' then + return + end + for _, ref in ipairs(loc.ref) do + if ref.type == 'setlocal' then + await.delay() + local refNode = vm.compileNode(ref) + if not vm.isSubType(uri, refNode, locNode) then + callback { + start = ref.start, + finish = ref.finish, + message = lang.script('DIAG_CAST_LOCAL_TYPE', { + loc = vm.getInfer(locNode):view(uri), + ref = vm.getInfer(refNode):view(uri), + }), + } + end + end + end + end) +end diff --git a/script/core/diagnostics/type-check-assign.lua b/script/core/diagnostics/type-check-assign.lua new file mode 100644 index 00000000..6b3c73b3 --- /dev/null +++ b/script/core/diagnostics/type-check-assign.lua @@ -0,0 +1,20 @@ +local files = require 'files' +local lang = require 'language' +local guide = require 'parser.guide' +local vm = require 'vm' + +return function (uri, callback) + local state = files.getState(uri) + if not state then + return + end + + guide.eachSourceType(state.ast, 'setlocal', function (source) + local value = source.value + if not value then + return + end + local locNode = vm.compileNode(source) + local valueNode = vm.compileNode(value) + end) +end diff --git a/script/core/diagnostics/type-check.lua b/script/core/diagnostics/type-check.lua deleted file mode 100644 index cc2b3228..00000000 --- a/script/core/diagnostics/type-check.lua +++ /dev/null @@ -1,3 +0,0 @@ ----@async -return function(uri, callback) -end diff --git a/script/proto/define.lua b/script/proto/define.lua index 52006992..2ea1fd90 100644 --- a/script/proto/define.lua +++ b/script/proto/define.lua @@ -49,7 +49,8 @@ m.DiagnosticDefaultSeverity = { ['not-yieldable'] = 'Warning', ['discard-returns'] = 'Warning', ['need-check-nil'] = 'Warning', - ['type-check'] = 'Warning', + ['cast-local-type'] = 'Warning', + ['type-check-assign'] = 'Warning', ['duplicate-doc-alias'] = 'Warning', ['undefined-doc-class'] = 'Warning', @@ -111,7 +112,8 @@ m.DiagnosticDefaultNeededFileStatus = { ['not-yieldable'] = 'None', ['discard-returns'] = 'Opened', ['need-check-nil'] = 'Opened', - ['type-check'] = 'None', + ['cast-local-type'] = 'Any', + ['type-check-assign'] = 'Any', ['duplicate-doc-alias'] = 'Any', ['undefined-doc-class'] = 'Any', diff --git a/script/vm/type.lua b/script/vm/type.lua index c3264993..8027e933 100644 --- a/script/vm/type.lua +++ b/script/vm/type.lua @@ -1,6 +1,55 @@ ---@class vm local vm = require 'vm.vm' +---@param object vm.object +---@return string? +local function getNodeName(object) + if object.type == 'global' and object.cate == 'type' then + return object.name + end + if object.type == 'nil' + or object.type == 'boolean' + or object.type == 'number' + or object.type == 'string' + or object.type == 'table' + or object.type == 'function' + or object.type == 'integer' then + return object.type + end + if object.type == 'doc.type.boolean' then + return 'boolean' + end + if object.type == 'doc.type.integer' then + return 'integer' + end + if object.type == 'doc.type.function' then + return 'function' + end + if object.type == 'doc.type.table' then + return 'table' + end + return nil +end + +---@param child vm.object +---@param parent vm.object +---@param mark? table +---@return boolean +function vm.isSubNode(child, parent, mark) + mark = mark or {} + local childName = getNodeName(child) + local parentName = getNodeName(parent) + if childName == 'any' or parentName == 'any' then + return true + end + + if childName == parentName then + return true + end + + return false +end + ---@param uri uri ---@param child vm.node|string ---@param parent vm.node|string @@ -19,49 +68,36 @@ function vm.isSubType(uri, child, parent, mark) end mark = mark or {} - for obj in child:eachObject() do - if obj.type ~= 'global' - or obj.cate ~= 'type' then - goto CONTINUE_CHILD - end - if mark[obj.name] then - return false - end - mark[obj.name] = true - for parentNode in parent:eachObject() do - if parentNode.type ~= 'global' - or parentNode.cate ~= 'type' then - goto CONTINUE_PARENT - end - if parentNode.name == 'any' or obj.name == 'any' then - return true - end - - if parentNode.name == obj.name then - return true + for childNode in child:eachObject() do + if not mark[childNode] then + mark[childNode] = true + for parentNode in parent:eachObject() do + if vm.isSubNode(childNode, parentNode, mark) then + return true + end end - for _, set in ipairs(obj:getSets(uri)) do - if set.type == 'doc.class' and set.extends then - for _, ext in ipairs(set.extends) do - if ext.type == 'doc.extends.name' - and vm.isSubType(uri, ext[1], parentNode.name, mark) then - return true + if childNode.type == 'global' and childNode.cate == 'type' then + for _, set in ipairs(childNode:getSets(uri)) do + if set.type == 'doc.class' and set.extends then + for _, ext in ipairs(set.extends) do + if ext.type == 'doc.extends.name' + and vm.isSubType(uri, ext[1], parent, mark) then + return true + end end end - end - if set.type == 'doc.alias' and set.extends then - for _, ext in ipairs(set.extends.types) do - if ext.type == 'doc.type.name' - and vm.isSubType(uri, ext[1], parentNode.name, mark) then - return true + if set.type == 'doc.alias' and set.extends then + for _, ext in ipairs(set.extends.types) do + if ext.type == 'doc.type.name' + and vm.isSubType(uri, ext[1], parent, mark) then + return true + end end end end end - ::CONTINUE_PARENT:: end - ::CONTINUE_CHILD:: end return false diff --git a/test/diagnostics/common.lua b/test/diagnostics/common.lua index 428d8a45..d9dfb0b9 100644 --- a/test/diagnostics/common.lua +++ b/test/diagnostics/common.lua @@ -1601,3 +1601,10 @@ end f1(f2()) ]] + +TEST [[ +---@diagnostic disable: unused-local +local x = 0 + +<!x!> = true +]] |