summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author最萌小汐 <sumneko@hotmail.com>2022-06-16 20:37:00 +0800
committer最萌小汐 <sumneko@hotmail.com>2022-06-16 20:37:00 +0800
commit8a9e8d9cfd12d43496c2bf16f70874af550d4e7e (patch)
tree5dfdec3988e74a8b4eae33849870051a96420d94
parentea1c5197c469779ba7cd18f1a8da5da9908f2149 (diff)
downloadlua-language-server-8a9e8d9cfd12d43496c2bf16f70874af550d4e7e.zip
type-check: `cast-local-type`
-rw-r--r--script/core/diagnostics/cast-local-type.lua40
-rw-r--r--script/core/diagnostics/type-check-assign.lua20
-rw-r--r--script/core/diagnostics/type-check.lua3
-rw-r--r--script/proto/define.lua6
-rw-r--r--script/vm/type.lua104
-rw-r--r--test/diagnostics/common.lua7
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
+]]