summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author最萌小汐 <sumneko@hotmail.com>2022-04-23 23:24:23 +0800
committer最萌小汐 <sumneko@hotmail.com>2022-04-23 23:24:23 +0800
commit3f815c836beb054a13f8d250f6f05a54778b72ba (patch)
tree04e39933feecbc34cd8ef36c86f838ea8181c635
parenta7afda10d2ea044a8ea3c83d4656736a6969a101 (diff)
downloadlua-language-server-3f815c836beb054a13f8d250f6f05a54778b72ba.zip
new diagnostic: `need-check-nil`
-rw-r--r--changelog.md1
-rw-r--r--script/core/diagnostics/need-check-nil.lua39
-rw-r--r--script/proto/define.lua16
-rw-r--r--script/vm/compiler.lua3
-rw-r--r--script/vm/node.lua2
-rw-r--r--script/vm/sign.lua2
-rw-r--r--test/diagnostics/common.lua34
7 files changed, 86 insertions, 11 deletions
diff --git a/changelog.md b/changelog.md
index eb1aaf78..ff2d2c1e 100644
--- a/changelog.md
+++ b/changelog.md
@@ -26,6 +26,7 @@
* `NEW` generic: resolve `T[]` by `table<integer, type>` or `---@field [integer] type`
* `NEW` resolve `class[1]` by `---@field [integer] type`
* `NEW` diagnostic: `missing-parameter`
+* `NEW` diagnostic: `need-check-nil`
* `CHG` diagnostic: no longer mark `redundant-parameter` as `Unnecessary`
* `FIX` diagnostic: `unused-function` does not recognize recursion
* `FIX` [#1051](https://github.com/sumneko/lua-language-server/issues/1051)
diff --git a/script/core/diagnostics/need-check-nil.lua b/script/core/diagnostics/need-check-nil.lua
new file mode 100644
index 00000000..8654a7a6
--- /dev/null
+++ b/script/core/diagnostics/need-check-nil.lua
@@ -0,0 +1,39 @@
+local files = require 'files'
+local guide = require 'parser.guide'
+local vm = require 'vm'
+local lang = require 'language'
+
+return function (uri, callback)
+ local state = files.getState(uri)
+ if not state then
+ return
+ end
+
+ guide.eachSourceType(state.ast, 'getlocal', function (src)
+ local checkNil
+ local nxt = src.next
+ if nxt then
+ if nxt.type == 'getfield'
+ or nxt.type == 'getmethod'
+ or nxt.type == 'getindex'
+ or nxt.type == 'call' then
+ checkNil = true
+ end
+ end
+ local call = src.parent
+ if call and call.type == 'call' and call.node == src then
+ checkNil = true
+ end
+ if not checkNil then
+ return
+ end
+ local node = vm.compileNode(src)
+ if node:hasFalsy() then
+ callback {
+ start = src.start,
+ finish = src.finish,
+ message = lang.script('DIAG_MISS_NEED_CHECK_NIL'),
+ }
+ end
+ end)
+end
diff --git a/script/proto/define.lua b/script/proto/define.lua
index 9da67039..fb60c56c 100644
--- a/script/proto/define.lua
+++ b/script/proto/define.lua
@@ -9,10 +9,10 @@ m.DiagnosticSeverity = {
}
---@alias DiagnosticDefaultSeverity
----| '"Hint"'
----| '"Information"'
----| '"Warning"'
----| '"Error"'
+---| 'Hint'
+---| 'Information'
+---| 'Warning'
+---| 'Error'
--- 诊断类型与默认等级
---@type table<string, DiagnosticDefaultSeverity>
@@ -48,6 +48,7 @@ m.DiagnosticDefaultSeverity = {
['await-in-sync'] = 'Warning',
['not-yieldable'] = 'Warning',
['discard-returns'] = 'Warning',
+ ['need-check-nil'] = 'Warning',
['type-check'] = 'Warning',
['duplicate-doc-alias'] = 'Warning',
@@ -64,9 +65,9 @@ m.DiagnosticDefaultSeverity = {
}
---@alias DiagnosticDefaultNeededFileStatus
----| '"Any"'
----| '"Opened"'
----| '"None"'
+---| 'Any'
+---| 'Opened'
+---| 'None'
-- 文件状态
m.FileStatus = {
@@ -108,6 +109,7 @@ m.DiagnosticDefaultNeededFileStatus = {
['await-in-sync'] = 'None',
['not-yieldable'] = 'None',
['discard-returns'] = 'Opened',
+ ['need-check-nil'] = 'Opened',
['type-check'] = 'None',
['duplicate-doc-alias'] = 'Any',
diff --git a/script/vm/compiler.lua b/script/vm/compiler.lua
index 4917d33a..1f33a784 100644
--- a/script/vm/compiler.lua
+++ b/script/vm/compiler.lua
@@ -1435,9 +1435,8 @@ local compilerSwitch = util.switch()
elseif r1 == false then
vm.setNode(source, node1)
else
- vm.getNode(source):merge(node1)
- vm.getNode(source):setTruly()
vm.getNode(source):merge(node2)
+ vm.getNode(source):addOptional()
end
end
if source.op.type == 'or' then
diff --git a/script/vm/node.lua b/script/vm/node.lua
index d52c2a84..06023ca5 100644
--- a/script/vm/node.lua
+++ b/script/vm/node.lua
@@ -80,7 +80,7 @@ function mt:isOptional()
end
---@return boolean
-function mt:isFalsy()
+function mt:hasFalsy()
if self.optional then
return true
end
diff --git a/script/vm/sign.lua b/script/vm/sign.lua
index 78d61022..795916fa 100644
--- a/script/vm/sign.lua
+++ b/script/vm/sign.lua
@@ -128,7 +128,7 @@ function mt:resolve(uri, args, removeGeneric)
local function buildArgNode(argNode, knownTypes)
local newArgNode = vm.createNode()
for n in argNode:eachObject() do
- if argNode:isFalsy() then
+ if argNode:hasFalsy() then
goto CONTINUE
end
local view = infer.viewObject(n)
diff --git a/test/diagnostics/common.lua b/test/diagnostics/common.lua
index dca826a1..72b2db6c 100644
--- a/test/diagnostics/common.lua
+++ b/test/diagnostics/common.lua
@@ -1460,3 +1460,37 @@ end
<!T:ff!>()
]]
+
+TEST [[
+---@type string?
+local x
+
+S = <!x!>:upper()
+]]
+
+TEST [[
+---@type string?
+local x
+
+if x then
+ S = x:upper()
+end
+]]
+
+TEST [[
+---@type string?
+local x
+
+if not x then
+ x = ''
+end
+
+S = x:upper()
+]]
+
+TEST [[
+---@type fun()?
+local x
+
+S = <!x!>()
+]]