summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author最萌小汐 <sumneko@hotmail.com>2022-06-15 17:40:43 +0800
committer最萌小汐 <sumneko@hotmail.com>2022-06-15 17:40:43 +0800
commitb22505b90c6df5fadfcad08bee82ec2f6d91bbaa (patch)
treecb45d1189a2b87d11f98d05ae22d51b45ce388a5
parente6b82117616e5d055399aa1568fee90a99ba8d64 (diff)
downloadlua-language-server-b22505b90c6df5fadfcad08bee82ec2f6d91bbaa.zip
resolve #1105 infer type by `type(x)`
-rw-r--r--changelog.md8
-rw-r--r--script/vm/node.lua26
-rw-r--r--script/vm/runner.lua58
-rw-r--r--test/type_inference/init.lua34
4 files changed, 106 insertions, 20 deletions
diff --git a/changelog.md b/changelog.md
index 01cb797f..15042aa8 100644
--- a/changelog.md
+++ b/changelog.md
@@ -26,6 +26,14 @@
local s = t and t.x or 1 -- `t` in `t.x` is `table`
```
+* `CHG` infer type by `type(x)`
+ ```lua
+ local x
+
+ if type(x) == 'string' then
+ print(x) -- `x` is `string` here
+ end
+ ```
* `FIX` with clients that support LSP 3.17 (VSCode), workspace diagnostics are triggered every time when opening a file.
* `FIX` [#1204](https://github.com/sumneko/lua-language-server/issues/1204)
* `FIX` [#1208](https://github.com/sumneko/lua-language-server/issues/1208)
diff --git a/script/vm/node.lua b/script/vm/node.lua
index 5086f66d..1b3ea45c 100644
--- a/script/vm/node.lua
+++ b/script/vm/node.lua
@@ -224,6 +224,32 @@ function mt:remove(name)
return self
end
+---@param name string
+function mt:narrow(name)
+ if name ~= 'nil' and self.optional == true then
+ self.optional = nil
+ end
+ for index = #self, 1, -1 do
+ local c = self[index]
+ if (c.type == 'global' and c.cate == 'type' and c.name == name)
+ or (c.type == name)
+ or (c.type == 'doc.type.integer' and (name == 'number' or name == 'integer'))
+ or (c.type == 'doc.type.boolean' and name == 'boolean')
+ or (c.type == 'doc.type.table' and name == 'table')
+ or (c.type == 'doc.type.array' and name == 'table')
+ or (c.type == 'doc.type.function' and name == 'function') then
+ goto CONTINUE
+ end
+ table.remove(self, index)
+ self[c] = nil
+ ::CONTINUE::
+ end
+ if #self == 0 then
+ self[#self+1] = vm.getGlobal('type', name)
+ end
+ return self
+end
+
---@param node vm.node
function mt:removeNode(node)
for _, c in ipairs(node) do
diff --git a/script/vm/runner.lua b/script/vm/runner.lua
index 2e362871..e6f34656 100644
--- a/script/vm/runner.lua
+++ b/script/vm/runner.lua
@@ -208,30 +208,48 @@ function mt:_lookInto(action, topNode, outNode)
outNode = outNode2
elseif action.op.type == '=='
or action.op.type == '~=' then
- local loc, checker
+ local exp, checker
for i = 1, 2 do
- if action[i].type == 'getlocal' and action[i].node == self._loc then
- loc = action[i]
- checker = action[3-i] -- Copilot tells me use `3-i` instead of `i%2+1`
- elseif action[2].type == 'getlocal' and action[2].node == self._loc then
- loc = action[3-i]
+ if guide.isLiteral(action[i]) then
checker = action[i]
+ exp = action[3-i] -- Copilot tells me use `3-i` instead of `i%2+1`
end
end
- if loc then
- self:_fastWard(loc.finish, topNode:copy())
- if guide.isLiteral(checker) then
- local checkerNode = vm.compileNode(checker)
- if action.op.type == '==' then
- topNode = checkerNode
- if outNode then
- outNode:removeNode(topNode)
- end
- else
- topNode:removeNode(checkerNode)
- if outNode then
- outNode = checkerNode
- end
+ if not exp then
+ goto RETURN
+ end
+ if exp.type == 'getlocal'
+ and exp.node == self._loc then
+ self:_fastWard(exp.finish, topNode:copy())
+ local checkerNode = vm.compileNode(checker)
+ if action.op.type == '==' then
+ topNode = checkerNode
+ if outNode then
+ outNode:removeNode(topNode)
+ end
+ else
+ topNode:removeNode(checkerNode)
+ if outNode then
+ outNode = checkerNode
+ end
+ end
+ elseif exp.type == 'call'
+ and checker.type == 'string'
+ and exp.node.special == 'type'
+ and exp.args
+ and exp.args[1]
+ and exp.args[1].type == 'getlocal'
+ and exp.args[1].node == self._loc then
+ self:_fastWard(exp.finish, topNode:copy())
+ if action.op.type == '==' then
+ topNode:narrow(checker[1])
+ if outNode then
+ outNode:remove(checker[1])
+ end
+ else
+ topNode:remove(checker[1])
+ if outNode then
+ outNode:narrow(checker[1])
end
end
end
diff --git a/test/type_inference/init.lua b/test/type_inference/init.lua
index a12e702f..20d2c6e9 100644
--- a/test/type_inference/init.lua
+++ b/test/type_inference/init.lua
@@ -2580,3 +2580,37 @@ local n
n = ff[n and <?n?>.x]
]]
+
+TEST 'integer' [[
+local x
+
+if type(x) == 'integer' then
+ print(<?x?>)
+end
+]]
+
+TEST 'boolean|integer' [[
+local x
+
+if type(x) == 'integer'
+or type(x) == 'boolean' then
+ print(<?x?>)
+end
+]]
+
+TEST 'fun()' [[
+---@type fun()?
+local x
+
+if type(x) == 'function' then
+ print(<?x?>)
+end
+]]
+
+TEST 'function' [[
+local x
+
+if type(x) == 'function' then
+ print(<?x?>)
+end
+]]