summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorfesily <fesil@foxmail.com>2024-01-10 11:03:21 +0800
committerGitHub <noreply@github.com>2024-01-10 11:03:21 +0800
commit1e7bb72ad3ff2b75a1c55ee4bc53004cb7fe30f7 (patch)
treedd94cf09b9e3a675a73a9f1d41248d1538165997 /test
parentbb6e172d6166190bd4edd3bb56230a7d60ebcb93 (diff)
parent37779f9b2493e51e59e1e4366bf7dcb8350e69bd (diff)
downloadlua-language-server-1e7bb72ad3ff2b75a1c55ee4bc53004cb7fe30f7.zip
Merge branch 'LuaLS:master' into plugin-add-OnTransformAst
Diffstat (limited to 'test')
-rw-r--r--test/code_action/init.lua144
-rw-r--r--test/completion/common.lua299
-rw-r--r--test/crossfile/completion.lua14
-rw-r--r--test/crossfile/diagnostic.lua24
-rw-r--r--test/crossfile/hover.lua57
-rw-r--r--test/crossfile/references.lua4
-rw-r--r--test/diagnostics/ambiguity-1.lua29
-rw-r--r--test/diagnostics/assign-type-mismatch.lua474
-rw-r--r--test/diagnostics/await-in-sync.lua132
-rw-r--r--test/diagnostics/cast-local-type.lua334
-rw-r--r--test/diagnostics/cast-type-mismatch.lua13
-rw-r--r--test/diagnostics/circle-doc-class.lua14
-rw-r--r--test/diagnostics/close-non-object.lua18
-rw-r--r--test/diagnostics/code-after-break.lua7
-rw-r--r--test/diagnostics/common.lua2270
-rw-r--r--test/diagnostics/count-down-loop.lua29
-rw-r--r--test/diagnostics/deprecated.lua21
-rw-r--r--test/diagnostics/discard-returns.lua17
-rw-r--r--test/diagnostics/doc-field-no-class.lua16
-rw-r--r--test/diagnostics/duplicate-doc-alias.lua10
-rw-r--r--test/diagnostics/duplicate-doc-field.lua38
-rw-r--r--test/diagnostics/duplicate-doc-param.lua12
-rw-r--r--test/diagnostics/duplicate-index.lua24
-rw-r--r--test/diagnostics/duplicate-set-field.lua74
-rw-r--r--test/diagnostics/empty-block.lua32
-rw-r--r--test/diagnostics/global-element.lua34
-rw-r--r--test/diagnostics/global-in-nil-env.lua44
-rw-r--r--test/diagnostics/incomplete-signature-doc.lua167
-rw-r--r--test/diagnostics/init.lua116
-rw-r--r--test/diagnostics/inject-field.lua84
-rw-r--r--test/diagnostics/invisible.lua145
-rw-r--r--test/diagnostics/lowercase-global.lua39
-rw-r--r--test/diagnostics/missing-fields.lua233
-rw-r--r--test/diagnostics/missing-global-doc.lua36
-rw-r--r--test/diagnostics/missing-local-export-doc.lua36
-rw-r--r--test/diagnostics/missing-parameter.lua90
-rw-r--r--test/diagnostics/missing-return-value.lua34
-rw-r--r--test/diagnostics/missing-return.lua158
-rw-r--r--test/diagnostics/need-check-nil.lua68
-rw-r--r--test/diagnostics/newfield-call.lua15
-rw-r--r--test/diagnostics/newline-call.lua34
-rw-r--r--test/diagnostics/not-yieldable.lua48
-rw-r--r--test/diagnostics/param-type-mismatch.lua248
-rw-r--r--test/diagnostics/redefined-local.lua22
-rw-r--r--test/diagnostics/redundant-parameter.lua214
-rw-r--r--test/diagnostics/redundant-return-value.lua32
-rw-r--r--test/diagnostics/redundant-return.lua34
-rw-r--r--test/diagnostics/redundant-value.lua7
-rw-r--r--test/diagnostics/return-type-mismatch.lua167
-rw-r--r--test/diagnostics/trailing-space.lua32
-rw-r--r--test/diagnostics/type-check.lua1262
-rw-r--r--test/diagnostics/unbalanced-assignments.lua34
-rw-r--r--test/diagnostics/undefined-doc-class.lua3
-rw-r--r--test/diagnostics/undefined-doc-name.lua19
-rw-r--r--test/diagnostics/undefined-doc-param.lua21
-rw-r--r--test/diagnostics/undefined-env-child.lua36
-rw-r--r--test/diagnostics/undefined-field.lua148
-rw-r--r--test/diagnostics/undefined-global.lua36
-rw-r--r--test/diagnostics/unknown-cast-variable.lua8
-rw-r--r--test/diagnostics/unknown-diag-code.lua3
-rw-r--r--test/diagnostics/unknown-operator.lua4
-rw-r--r--test/diagnostics/unreachable-code.lua71
-rw-r--r--test/diagnostics/unused-function.lua48
-rw-r--r--test/diagnostics/unused-label.lua3
-rw-r--r--test/diagnostics/unused-local.lua99
-rw-r--r--test/diagnostics/unused-vararg.lua6
-rw-r--r--test/full/init.lua2
-rw-r--r--test/full/projects.lua2
-rw-r--r--test/full/self.lua4
-rw-r--r--test/hover/init.lua41
-rw-r--r--test/plugins/ffi/test.lua9
-rw-r--r--test/signature/init.lua42
-rw-r--r--test/type_inference/init.lua23
73 files changed, 4411 insertions, 3756 deletions
diff --git a/test/code_action/init.lua b/test/code_action/init.lua
index 2329ce4c..264cfacf 100644
--- a/test/code_action/init.lua
+++ b/test/code_action/init.lua
@@ -2,38 +2,40 @@ local core = require 'core.code-action'
local files = require 'files'
local lang = require 'language'
local catch = require 'catch'
+local furi = require 'file-uri'
rawset(_G, 'TEST', true)
local EXISTS = {}
-local function eq(a, b)
- if a == EXISTS and b ~= nil then
+local function eq(expected, result)
+ if expected == EXISTS and result ~= nil then
return true
end
- if b == EXISTS and a ~= nil then
+ if result == EXISTS and expected ~= nil then
return true
end
- local tp1, tp2 = type(a), type(b)
+ local tp1, tp2 = type(expected), type(result)
if tp1 ~= tp2 then
- return false
+ return false, string.format(": expected type %s, got %s", tp1, tp2)
end
if tp1 == 'table' then
local mark = {}
- for k in pairs(a) do
- if not eq(a[k], b[k]) then
- return false
+ for k in pairs(expected) do
+ local ok, err = eq(expected[k], result[k])
+ if not ok then
+ return false, string.format(".%s%s", k, err)
end
mark[k] = true
end
- for k in pairs(b) do
+ for k in pairs(result) do
if not mark[k] then
- return false
+ return false, string.format(".%s: missing key in result", k)
end
end
return true
end
- return a == b
+ return expected == result, string.format(": expected %s, got %s", expected, result)
end
function TEST(script)
@@ -47,6 +49,32 @@ function TEST(script)
end
end
+local function TEST_CROSSFILE(testfiles)
+ local mainscript = table.remove(testfiles, 1)
+ return function(expected)
+ for _, data in ipairs(testfiles) do
+ local uri = furi.encode(TESTROOT .. data.path)
+ files.setText(uri, data.content)
+ files.compileState(uri)
+ end
+
+ local newScript, catched = catch(mainscript, '?')
+ files.setText(TESTURI, newScript)
+ files.compileState(TESTURI)
+
+ local _ <close> = function ()
+ for _, info in ipairs(testfiles) do
+ files.remove(furi.encode(TESTROOT .. info.path))
+ end
+ files.remove(TESTURI)
+ end
+
+ local results = core(TESTURI, catched['?'][1][1], catched['?'][1][2])
+ assert(results)
+ assert(eq(expected, results))
+ end
+end
+
TEST [[
print(<?a?>, b, c)
]]
@@ -154,3 +182,97 @@ local t = {
-- edit = EXISTS,
-- },
--}
+
+TEST_CROSSFILE {
+[[
+ <?unrequiredModule?>.myFunction()
+]],
+ {
+ path = 'unrequiredModule.lua',
+ content = [[
+ local m = {}
+ m.myFunction = print
+ return m
+ ]]
+ }
+} {
+ {
+ title = lang.script('ACTION_AUTOREQUIRE', 'unrequiredModule', 'unrequiredModule'),
+ kind = 'refactor.rewrite',
+ command = {
+ title = 'autoRequire',
+ command = 'lua.autoRequire',
+ arguments = {
+ {
+ uri = TESTURI,
+ target = furi.encode(TESTROOT .. 'unrequiredModule.lua'),
+ name = 'unrequiredModule',
+ requireName = 'unrequiredModule'
+ },
+ },
+ }
+ }
+}
+
+TEST_CROSSFILE {
+[[
+ <?myModule?>.myFunction()
+]],
+ {
+ path = 'myModule/init.lua',
+ content = [[
+ local m = {}
+ m.myFunction = print
+ return m
+ ]]
+ }
+} {
+ {
+ title = lang.script('ACTION_AUTOREQUIRE', 'myModule.init', 'myModule'),
+ kind = 'refactor.rewrite',
+ command = {
+ title = 'autoRequire',
+ command = 'lua.autoRequire',
+ arguments = {
+ {
+ uri = TESTURI,
+ target = furi.encode(TESTROOT .. 'myModule/init.lua'),
+ name = 'myModule',
+ requireName = 'myModule.init'
+ },
+ },
+ }
+ },
+ {
+ title = lang.script('ACTION_AUTOREQUIRE', 'init', 'myModule'),
+ kind = 'refactor.rewrite',
+ command = {
+ title = 'autoRequire',
+ command = 'lua.autoRequire',
+ arguments = {
+ {
+ uri = TESTURI,
+ target = furi.encode(TESTROOT .. 'myModule/init.lua'),
+ name = 'myModule',
+ requireName = 'init'
+ },
+ },
+ }
+ },
+ {
+ title = lang.script('ACTION_AUTOREQUIRE', 'myModule', 'myModule'),
+ kind = 'refactor.rewrite',
+ command = {
+ title = 'autoRequire',
+ command = 'lua.autoRequire',
+ arguments = {
+ {
+ uri = TESTURI,
+ target = furi.encode(TESTROOT .. 'myModule/init.lua'),
+ name = 'myModule',
+ requireName = 'myModule'
+ },
+ },
+ }
+ },
+}
diff --git a/test/completion/common.lua b/test/completion/common.lua
index 8d23822a..90037c27 100644
--- a/test/completion/common.lua
+++ b/test/completion/common.lua
@@ -3816,6 +3816,32 @@ f(<??>)
}
TEST [[
+local x = 1
+local y = 2
+
+---@enum(key) Enum
+local t = {
+ x = x,
+ y = y,
+}
+
+---@param p Enum
+local function f(p) end
+
+f(<??>)
+]]
+{
+ {
+ label = '"x"',
+ kind = define.CompletionItemKind.EnumMember,
+ },
+ {
+ label = '"y"',
+ kind = define.CompletionItemKind.EnumMember,
+ },
+}
+
+TEST [[
--
<??>
]]
@@ -3838,6 +3864,12 @@ local x = function (x, y) end
(EXISTS)
TEST [[
+--- <??>
+local x = function (x, y) end
+]]
+(EXISTS)
+
+TEST [[
local x = {
<??>
})
@@ -4114,3 +4146,270 @@ f({
kind = define.CompletionItemKind.Text,
},
}
+
+TEST [[
+while true do
+ continue<??>
+end
+]]
+{
+ {
+ label = 'continue',
+ kind = define.CompletionItemKind.Keyword,
+ },
+ {
+ label = 'goto continue ..',
+ kind = define.CompletionItemKind.Snippet,
+ additionalTextEdits = {
+ {
+ start = 10004,
+ finish = 10004,
+ newText = 'goto ',
+ },
+ {
+ start = 20000,
+ finish = 20000,
+ newText = ' ::continue::\n',
+ },
+ }
+ },
+}
+
+TEST [[
+while true do
+ goto continue<??>
+end
+]]
+{
+ {
+ label = 'continue',
+ kind = define.CompletionItemKind.Keyword,
+ },
+ {
+ label = 'goto continue ..',
+ kind = define.CompletionItemKind.Snippet,
+ additionalTextEdits = {
+ {
+ start = 20000,
+ finish = 20000,
+ newText = ' ::continue::\n',
+ }
+ }
+ },
+}
+
+TEST [[
+while true do
+ goto continue<??>
+ ::continue::
+end
+]]
+{
+ {
+ label = 'continue',
+ kind = define.CompletionItemKind.Keyword,
+ },
+ {
+ label = 'goto continue ..',
+ kind = define.CompletionItemKind.Snippet,
+ additionalTextEdits = {
+ }
+ },
+}
+
+Cared['description'] = true
+TEST [[
+---@class Foo
+---@field ['with quotes'] integer
+---@field without_quotes integer
+
+---@type Foo
+local bar = {}
+
+bar.<??>
+]]
+{
+ {
+ label = "'with quotes'",
+ kind = define.CompletionItemKind.Field,
+ textEdit = {
+ start = 70004,
+ finish = 70004,
+ newText = "['with quotes']"
+ },
+ additionalTextEdits = {
+ {
+ start = 70003,
+ finish = 70004,
+ newText = '',
+ }
+ },
+ description = [[
+```lua
+(field) Foo['with quotes']: integer
+```]]
+ },
+ {
+ label = 'without_quotes',
+ kind = define.CompletionItemKind.Field,
+ description = [[
+```lua
+(field) Foo.without_quotes: integer
+```]]
+ },
+}
+Cared['description'] = false
+
+TEST [[
+---@class A
+local M = {}
+
+function M:method1()
+end
+
+function M.static1(tt)
+end
+
+function M:method2()
+end
+
+function M.static2(tt)
+end
+
+---@type A
+local a
+
+a.<??>
+]]
+{
+ {
+ label ='static1(tt)',
+ kind = define.CompletionItemKind.Function,
+ },
+ {
+ label ='static2(tt)',
+ kind = define.CompletionItemKind.Function,
+ },
+ {
+ label ='method1(self)',
+ kind = define.CompletionItemKind.Method,
+ },
+ {
+ label ='method2(self)',
+ kind = define.CompletionItemKind.Method,
+ },
+}
+
+TEST [[
+---@class A
+local M = {}
+
+function M:method1()
+end
+
+function M.static1(tt)
+end
+
+function M:method2()
+end
+
+function M.static2(tt)
+end
+
+---@type A
+local a
+
+a:<??>
+]]
+{
+ {
+ label ='method1()',
+ kind = define.CompletionItemKind.Method,
+ },
+ {
+ label ='method2()',
+ kind = define.CompletionItemKind.Method,
+ },
+ {
+ label ='static1()',
+ kind = define.CompletionItemKind.Function,
+ },
+ {
+ label ='static2()',
+ kind = define.CompletionItemKind.Function,
+ },
+}
+
+TEST [[
+---@class A
+---@field x number
+---@field y? number
+---@field z number
+
+---@type A
+local t = {
+ <??>
+}
+]]
+{
+ {
+ label = 'x',
+ kind = define.CompletionItemKind.Property,
+ },
+ {
+ label = 'z',
+ kind = define.CompletionItemKind.Property,
+ },
+ {
+ label = 'y?',
+ kind = define.CompletionItemKind.Property,
+ },
+}
+
+TEST [[
+---@class A
+---@field x number
+---@field y? number
+---@field z number
+
+---@param t A
+local function f(t) end
+
+f {
+ <??>
+}
+]]
+{
+ {
+ label = 'x',
+ kind = define.CompletionItemKind.Property,
+ },
+ {
+ label = 'z',
+ kind = define.CompletionItemKind.Property,
+ },
+ {
+ label = 'y?',
+ kind = define.CompletionItemKind.Property,
+ },
+}
+
+TEST [[
+---@class A
+---@overload fun(x: {id: string})
+
+---@generic T
+---@param t `T`
+---@return T
+local function new(t) end
+
+new 'A' {
+ <??>
+}
+]]
+{
+ {
+ label = 'id',
+ kind = define.CompletionItemKind.Property,
+ }
+}
diff --git a/test/crossfile/completion.lua b/test/crossfile/completion.lua
index 113b0327..227350cb 100644
--- a/test/crossfile/completion.lua
+++ b/test/crossfile/completion.lua
@@ -61,7 +61,7 @@ function TEST(data)
local mainUri
local pos
for _, info in ipairs(data) do
- local uri = furi.encode(info.path)
+ local uri = furi.encode(TESTROOT .. info.path)
local script = info.content or ''
if info.main then
local newScript, catched = catch(script, '?')
@@ -75,7 +75,7 @@ function TEST(data)
local _ <close> = function ()
for _, info in ipairs(data) do
- files.remove(furi.encode(info.path))
+ files.remove(furi.encode(TESTROOT .. info.path))
end
end
@@ -108,6 +108,12 @@ function TEST(data)
: gsub('\r\n', '\n')
end
end
+ for _, eitem in ipairs(expect) do
+ if eitem['description'] then
+ eitem['description'] = eitem['description']
+ : gsub('%$(.-)%$', _G)
+ end
+ end
assert(result)
assert(eq(expect, result))
end
@@ -936,7 +942,7 @@ TEST {
kind = CompletionItemKind.Variable,
detail = 'function',
description = [[
-从 [myfunc.lua](file:///myfunc.lua) 中导入
+从 [myfunc.lua]($TESTROOTURI$myfunc.lua) 中导入
```lua
function (a: any, b: any)
@@ -967,7 +973,7 @@ TEST {
kind = CompletionItemKind.Variable,
detail = 'function',
description = [[
-从 [dir\myfunc.lua](file:///dir/myfunc.lua) 中导入
+从 [dir\myfunc.lua]($TESTROOTURI$dir/myfunc.lua) 中导入
```lua
function (a: any, b: any)
diff --git a/test/crossfile/diagnostic.lua b/test/crossfile/diagnostic.lua
index e270d3da..1acf3fe2 100644
--- a/test/crossfile/diagnostic.lua
+++ b/test/crossfile/diagnostic.lua
@@ -58,20 +58,26 @@ function TEST(datas)
local results = {}
+ local origins = {}
for _, data in ipairs(datas) do
local uri = furi.encode(data.path)
core(uri, false, function (result)
- results[#results+1] = {
- result.start,
- result.finish,
- uri,
- }
+ if result.code == datas.code then
+ results[#results+1] = {
+ result.start,
+ result.finish,
+ uri,
+ }
+ end
+ origins[#origins+1] = result
end)
end
+ assert(datas.code, 'Need code')
assert(founded(targetList, results))
end
TEST {
+ code = 'different-requires',
{
path = 'f/a.lua',
content = '',
@@ -87,6 +93,7 @@ TEST {
}
TEST {
+ code = 'different-requires',
{
path = 'f/a.lua',
content = '',
@@ -106,6 +113,7 @@ TEST {
}
TEST {
+ code = 'different-requires',
{
path = 'a.lua',
content = '',
@@ -125,6 +133,7 @@ TEST {
}
TEST {
+ code = 'different-requires',
{
path = 'a/init.lua',
content = '',
@@ -144,6 +153,7 @@ TEST {
}
TEST {
+ code = 'invisible',
{ path = 'a.lua', content = [[
---@class A
---@field package x string
@@ -156,6 +166,7 @@ TEST {
}
TEST {
+ code = 'invisible',
{ path = 'a.lua', content = [[
---@class A
---@field package x string
@@ -169,6 +180,7 @@ TEST {
}
TEST {
+ code = 'duplicate-doc-field',
{ path = 'a.lua', content = [[
---@class A
---@field <!x!> number
@@ -180,6 +192,7 @@ TEST {
}
TEST {
+ code = 'duplicate-set-field',
{ path = 'a.lua', content = [[
---@class A
local mt
@@ -197,6 +210,7 @@ TEST {
}
TEST {
+ code = 'duplicate-set-field',
{ path = 'a.lua', content = [[
---@class A
local mt
diff --git a/test/crossfile/hover.lua b/test/crossfile/hover.lua
index d013af98..a18c714b 100644
--- a/test/crossfile/hover.lua
+++ b/test/crossfile/hover.lua
@@ -1637,6 +1637,30 @@ TEST {
{
path = 'a.lua',
content = [[
+ ---@enum(key) <?A?>
+ local t = {
+ x = 1 << 0,
+ y = 1 << 1,
+ z = 1 << 2,
+ }
+ ]]
+ },
+ hover = [[
+```lua
+(enum) A
+```
+
+---
+
+```lua
+"x" | "y" | "z"
+```]]
+}
+
+TEST {
+ {
+ path = 'a.lua',
+ content = [[
---@alias someType
---| "#" # description
@@ -1695,6 +1719,26 @@ function f(x: number, y: number)
```]]
}
+TEST { { path = 'a.lua', content = [[
+---@overload fun(self: self, x: number)
+---@overload fun(self: self, x: number, y: number)
+function M:f(...)
+end
+
+M:<?f?>
+]] },
+hover = [[
+```lua
+(method) M:f(x: number)
+```
+
+---
+
+```lua
+(method) M:f(x: number, y: number)
+```]]
+}
+
TEST { {path = 'a.lua', content = [[
---@class A
@@ -1795,3 +1839,16 @@ local x: integer = 1
comment1]]
}
+
+TEST { {path = 'a.lua', content = [[
+local t = {}
+
+print(<?t?>['a b'])
+]]},
+hover = [[
+```lua
+local t: {
+ ['a b']: unknown,
+}
+```]]
+}
diff --git a/test/crossfile/references.lua b/test/crossfile/references.lua
index faa9dba9..36c08170 100644
--- a/test/crossfile/references.lua
+++ b/test/crossfile/references.lua
@@ -58,7 +58,7 @@ function TEST(datas)
local sourceList
local sourceUri
for i, data in ipairs(datas) do
- local uri = furi.encode(data.path)
+ local uri = furi.encode(TESTROOT .. data.path)
local newScript, catched = catch(data.content, '!?~')
if catched['!'] or catched['~'] then
for _, position in ipairs(catched['!'] + catched['~']) do
@@ -79,7 +79,7 @@ function TEST(datas)
local _ <close> = function ()
for _, info in ipairs(datas) do
- files.remove(furi.encode(info.path))
+ files.remove(furi.encode(TESTROOT .. info.path))
end
end
diff --git a/test/diagnostics/ambiguity-1.lua b/test/diagnostics/ambiguity-1.lua
new file mode 100644
index 00000000..6b8e41da
--- /dev/null
+++ b/test/diagnostics/ambiguity-1.lua
@@ -0,0 +1,29 @@
+TEST [[
+local x
+x = <!x or 0 + 1!>
+]]
+
+TEST [[
+local x, y
+x = <!x + y or 0!>
+]]
+
+TEST [[
+local x, y, z
+x = x and y or '' .. z
+]]
+
+TEST [[
+local x
+x = x or -1
+]]
+
+TEST [[
+local x
+x = x or (0 + 1)
+]]
+
+TEST [[
+local x, y
+x = (x + y) or 0
+]]
diff --git a/test/diagnostics/assign-type-mismatch.lua b/test/diagnostics/assign-type-mismatch.lua
new file mode 100644
index 00000000..d4632563
--- /dev/null
+++ b/test/diagnostics/assign-type-mismatch.lua
@@ -0,0 +1,474 @@
+local config = require 'config'
+
+TEST [[
+local m = {}
+
+---@type integer[]
+m.ints = {}
+]]
+
+TEST [[
+---@class A
+---@field x A
+
+---@type A
+local t
+
+t.x = {}
+]]
+
+TEST [[
+---@class A
+---@field x integer
+
+---@type A
+local t
+
+<!t.x!> = true
+]]
+
+TEST [[
+---@class A
+---@field x integer
+
+---@type A
+local t
+
+---@type boolean
+local y
+
+<!t.x!> = y
+]]
+
+TEST [[
+---@class A
+local m
+
+m.x = 1
+
+---@type A
+local t
+
+<!t.x!> = true
+]]
+
+TEST [[
+---@class A
+local m
+
+---@type integer
+m.x = 1
+
+<!m.x!> = true
+]]
+
+TEST [[
+---@class A
+local mt
+
+---@type integer
+mt.x = 1
+
+function mt:init()
+ <!self.x!> = true
+end
+]]
+
+TEST [[
+---@class A
+---@field x integer
+
+---@type A
+local t = {
+ <!x!> = true
+}
+]]
+
+TEST [[
+---@type boolean[]
+local t = {}
+
+t[5] = nil
+]]
+
+TEST [[
+---@type table<string, true>
+local t = {}
+
+t['x'] = nil
+]]
+
+TEST [[
+local t = { true }
+
+t[1] = nil
+]]
+
+TEST [[
+---@class A
+local t = {
+ x = 1
+}
+
+<!t.x!> = true
+]]
+
+TEST [[
+---@type number
+local t
+
+t = 1
+]]
+
+TEST [[
+---@type number
+local t
+
+---@type integer
+local y
+
+t = y
+]]
+
+TEST [[
+---@class A
+local m
+
+---@type number
+m.x = 1
+
+<!m.x!> = {}
+]]
+
+TEST [[
+local n
+
+if G then
+ n = {}
+else
+ n = nil
+end
+
+local t = {
+ x = n,
+}
+]]
+
+TEST [[
+---@type boolean[]
+local t = {}
+
+---@type boolean?
+local x
+
+t[#t+1] = x
+]]
+
+TEST [[
+---@type number
+local n
+---@type integer
+local i
+
+<?i?> = n
+]]
+
+config.set(nil, 'Lua.type.castNumberToInteger', true)
+TEST [[
+---@type number
+local n
+---@type integer
+local i
+
+i = n
+]]
+
+config.set(nil, 'Lua.type.castNumberToInteger', false)
+TEST [[
+---@type number|boolean
+local nb
+
+---@type number
+local n
+
+<?n?> = nb
+]]
+
+config.set(nil, 'Lua.type.weakUnionCheck', true)
+TEST [[
+---@type number|boolean
+local nb
+
+---@type number
+local n
+
+n = nb
+]]
+
+config.set(nil, 'Lua.type.weakUnionCheck', false)
+TEST [[
+---@class Option: string
+
+---@param x Option
+local function f(x) end
+
+---@type Option
+local x = 'aaa'
+
+f(x)
+]]
+config.set(nil, 'Lua.type.weakUnionCheck', true)
+
+TEST [[
+---@type number
+local <!x!> = 'aaa'
+]]
+TEST [[
+---@class X
+
+---@class A
+local mt = G
+
+---@type X
+mt._x = nil
+]]
+config.set(nil, 'Lua.type.weakUnionCheck', false)
+
+config.set(nil, 'Lua.type.weakNilCheck', true)
+TEST [[
+---@type number?
+local nb
+
+---@type number
+local n
+
+n = nb
+]]
+
+TEST [[
+---@type number|nil
+local nb
+
+---@type number
+local n
+
+n = nb
+]]
+config.set(nil, 'Lua.type.weakNilCheck', false)
+
+TEST [[
+---@class A
+local a = {}
+
+---@class B
+local <!b!> = a
+]]
+
+TEST [[
+---@class A
+local a = {}
+
+---@class B: A
+local b = a
+]]
+
+TEST [[
+---@class A
+local a = {}
+a.__index = a
+
+---@class B: A
+local b = setmetatable({}, a)
+]]
+
+TEST [[
+---@class A
+local a = {}
+
+---@class B: A
+local b = setmetatable({}, {__index = a})
+]]
+
+TEST [[
+---@class A
+local a = {}
+
+---@class B
+local <!b!> = setmetatable({}, {__index = a})
+]]
+
+TEST [[
+---@class A
+---@field x number?
+local a
+
+---@class B
+---@field x number
+local b
+
+b.x = a.x
+]]
+
+TEST [[
+
+---@class A
+---@field x number?
+local a
+
+---@type number
+local t
+
+t = a.x
+]]
+
+TEST [[
+local mt = {}
+mt.x = 1
+mt.x = nil
+]]
+
+config.set(nil, 'Lua.type.weakUnionCheck', true)
+TEST [[
+---@type number
+local x = G
+]]
+
+TEST [[
+---@generic T
+---@param x T
+---@return T
+local function f(x)
+ return x
+end
+]]
+config.set(nil, 'Lua.type.weakUnionCheck', false)
+
+
+TEST [[
+---@alias test boolean
+
+---@type test
+local <!test!> = 4
+]]
+
+TEST [[
+---@class MyClass
+local MyClass = {}
+
+function MyClass:new()
+ ---@class MyClass
+ local myObject = setmetatable({
+ initialField = true
+ }, self)
+
+ print(myObject.initialField)
+end
+]]
+
+TEST [[
+---@class T
+local t = {
+ x = nil
+}
+
+t.x = 1
+]]
+
+TEST [[
+---@type {[1]: string, [10]: number, xx: boolean}
+local t = {
+ <!true!>,
+ <![10]!> = 's',
+ <!xx!> = 1,
+}
+]]
+
+TEST [[
+---@type boolean[]
+local t = { <!1!>, <!2!>, <!3!> }
+]]
+
+TEST [[
+---@type boolean[]
+local t = { true, false, nil }
+]]
+
+TEST [[
+---@type boolean|nil
+local x
+
+---@type boolean[]
+local t = { true, false, x }
+]]
+
+TEST [[
+---@enum Enum
+local t = {
+ x = 1,
+ y = 2,
+}
+
+---@type Enum
+local y
+
+---@type integer
+local x = y
+]]
+
+TEST [[
+---@type string|string[]|string[][]
+local t = {{'a'}}
+]]
+
+TEST [[
+local A = "Hello"
+local B = "World"
+
+---@alias myLiteralAliases `A` | `B`
+
+---@type myLiteralAliases
+local x = A
+]]
+
+TEST [[
+local enum = { a = 1, b = 2 }
+
+---@type { [integer] : boolean }
+local t = {
+ <![enum.a]!> = 1,
+ <![enum.b]!> = 2,
+ <![3]!> = 3,
+}
+]]
+
+TEST [[
+---@class SomeClass
+---@field [1] string
+-- ...
+
+---@param some_param SomeClass|SomeClass[]
+local function some_fn(some_param) return end
+
+some_fn { { "test" } } -- <- diagnostic: "Cannot assign `table` to `string`."
+]]
+
+TEST [[
+---@type string[]
+local arr = {
+ <!3!>,
+}
+]]
+
+TEST [[
+---@type (string|boolean)[]
+local arr2 = {
+ <!3!>, -- no warnings
+}
+]]
+
+TEST [[
+local t = {}
+t.a = 1
+t.a = 2
+return t
+]]
diff --git a/test/diagnostics/await-in-sync.lua b/test/diagnostics/await-in-sync.lua
new file mode 100644
index 00000000..323c1113
--- /dev/null
+++ b/test/diagnostics/await-in-sync.lua
@@ -0,0 +1,132 @@
+TEST [[
+function F()
+ <!coroutine.yield!>()
+end
+]]
+
+TEST [[
+---@async
+function F()
+ coroutine.yield()
+end
+]]
+
+TEST [[
+---@type async fun()
+local f
+
+function F()
+ <!f!>()
+end
+]]
+
+TEST [[
+---@type async fun()
+local f
+
+---@async
+function F()
+ f()
+end
+]]
+
+TEST [[
+local function f(cb)
+ cb()
+end
+
+return function()
+ <!f>(function () ---@async
+ return nil
+ end)
+end
+]]
+
+TEST [[
+local function f(cb)
+ pcall(cb)
+end
+
+return function()
+ <!f!>(function () ---@async
+ return nil
+ end)
+end
+]]
+
+TEST [[
+---@param c any
+local function f(c)
+ return c
+end
+
+return function ()
+ f(function () ---@async
+ return nil
+ end)
+end
+]]
+
+TEST [[
+---@param ... any
+local function f(...)
+ return ...
+end
+
+return function ()
+ f(function () ---@async
+ return nil
+ end)
+end
+]]
+
+TEST [[
+---@vararg any
+local function f(...)
+ return ...
+end
+
+return function ()
+ f(function () ---@async
+ return nil
+ end)
+end
+]]
+
+TEST [[
+local function f(...)
+ return ...
+end
+
+return function ()
+ f(function () ---@async
+ return nil
+ end)
+end
+]]
+
+TEST [[
+local function f(...)
+ return ...
+end
+
+return function ()
+ f(function () ---@async
+ return nil
+ end)
+end
+]]
+
+TEST [[
+local function f(cb)
+ cb()
+end
+
+local function af()
+ <!f!>(function () ---@async
+ return nil
+ end)
+end
+
+return af
+]]
diff --git a/test/diagnostics/cast-local-type.lua b/test/diagnostics/cast-local-type.lua
new file mode 100644
index 00000000..f79bf48d
--- /dev/null
+++ b/test/diagnostics/cast-local-type.lua
@@ -0,0 +1,334 @@
+TEST [[
+local x = 0
+
+<!x!> = true
+]]
+
+TEST [[
+---@type integer
+local x
+
+<!x!> = true
+]]
+
+TEST [[
+---@type unknown
+local x
+
+x = nil
+]]
+
+TEST [[
+---@type unknown
+local x
+
+x = 1
+]]
+
+TEST [[
+---@type unknown|nil
+local x
+
+x = 1
+]]
+
+TEST [[
+local x = {}
+
+x = nil
+]]
+
+TEST [[
+---@type string
+local x
+
+<?x?> = nil
+]]
+
+TEST [[
+---@type string?
+local x
+
+x = nil
+]]
+
+TEST [[
+---@type table
+local x
+
+<!x!> = nil
+]]
+
+TEST [[
+local x
+
+x = nil
+]]
+
+TEST [[
+---@type integer
+local x
+
+---@type number
+<!x!> = f()
+]]
+
+TEST [[
+---@type number
+local x
+
+---@type integer
+x = f()
+]]
+
+TEST [[
+---@type number|boolean
+local x
+
+---@type string
+<!x!> = f()
+]]
+
+TEST [[
+---@type number|boolean
+local x
+
+---@type boolean
+x = f()
+]]
+
+TEST [[
+---@type number|boolean
+local x
+
+---@type boolean|string
+<!x!> = f()
+]]
+
+TEST [[
+---@type boolean
+local x
+
+if not x then
+ return
+end
+
+x = f()
+]]
+
+TEST [[
+---@type boolean
+local x
+
+---@type integer
+local y
+
+<!x!> = y
+]]
+
+TEST [[
+local y = true
+
+local x
+x = 1
+x = y
+]]
+
+TEST [[
+local t = {}
+
+local x = 0
+x = x + #t
+]]
+
+TEST [[
+local x = 0
+
+x = 1.0
+]]
+
+TEST [[
+---@class A
+
+local t = {}
+
+---@type A
+local a
+
+t = a
+]]
+
+TEST [[
+---@type integer
+local x
+
+x = 1.0
+]]
+
+TEST [[
+---@type integer
+local x
+
+<!x!> = 1.5
+]]
+
+TEST [[
+---@type integer
+local x
+
+x = 1 + G
+]]
+
+TEST [[
+---@type integer
+local x
+
+x = 1 + G
+]]
+
+TEST [[
+---@alias A integer
+
+---@type A
+local a
+
+---@type integer
+local b
+
+b = a
+]]
+
+TEST [[
+---@type string[]
+local t
+
+<!t!> = 'xxx'
+]]
+
+TEST [[
+---@type 1|2
+local x
+
+x = 1
+x = 2
+<!x!> = 3
+]]
+
+TEST [[
+---@type 'x'|'y'
+local x
+
+x = 'x'
+x = 'y'
+<!x!> = 'z'
+]]
+
+TEST [[
+local t = {
+ x = 1,
+}
+
+local x
+t[x] = true
+]]
+
+TEST [[
+---@type table<string, string>
+local x
+
+---@type table<number, string>
+local y
+
+<!x!> = y
+]]
+
+TEST [[
+---@type table<string, string>
+local x
+
+---@type table<string, number>
+local y
+
+<!x!> = y
+]]
+
+TEST [[
+---@type table<string, string>
+local x
+
+---@type table<string, string>
+local y
+
+x = y
+]]
+
+TEST [[
+---@type { x: number, y: number }
+local t1
+
+---@type { x: number }
+local t2
+
+<!t1!> = t2
+]]
+
+TEST [[
+---@type { x: number, [integer]: number }
+local t1
+
+---@type { x: number }
+local t2
+
+<!t1!> = t2
+]]
+
+TEST [[
+local x
+
+if X then
+ x = 'A'
+elseif X then
+ x = 'B'
+else
+ x = 'C'
+end
+
+local y = x
+
+<!y!> = nil
+]]
+(function (diags)
+ local diag = diags[1]
+ assert(diag.message == [[
+已显式定义变量的类型为 `string` ,不能再将其类型转换为 `nil`。
+- `nil` 无法匹配 `string`
+- 类型 `nil` 无法匹配 `string`]])
+end)
+
+TEST [[
+---@type 'A'|'B'|'C'|'D'|'E'|'F'|'G'|'H'|'I'|'J'|'K'|'L'|'M'|'N'|'O'|'P'|'Q'|'R'|'S'|'T'|'U'|'V'|'W'|'X'|'Y'|'Z'
+local x
+
+<!x!> = nil
+]]
+(function (diags)
+ local diag = diags[1]
+ assert(diag.message == [[
+已显式定义变量的类型为 `'A'|'B'|'C'|'D'|'E'...(+21)` ,不能再将其类型转换为 `nil`。
+- `nil` 无法匹配 `'A'|'B'|'C'|'D'|'E'...(+21)`
+- `nil` 无法匹配 `'A'|'B'|'C'|'D'|'E'...(+21)` 中的任何子类
+- 类型 `nil` 无法匹配 `'Z'`
+- 类型 `nil` 无法匹配 `'Y'`
+- 类型 `nil` 无法匹配 `'X'`
+- 类型 `nil` 无法匹配 `'W'`
+- 类型 `nil` 无法匹配 `'V'`
+- 类型 `nil` 无法匹配 `'U'`
+- 类型 `nil` 无法匹配 `'T'`
+- 类型 `nil` 无法匹配 `'S'`
+- 类型 `nil` 无法匹配 `'R'`
+- 类型 `nil` 无法匹配 `'Q'`
+...(+13)
+- 类型 `nil` 无法匹配 `'C'`
+- 类型 `nil` 无法匹配 `'B'`
+- 类型 `nil` 无法匹配 `'A'`]])
+end)
diff --git a/test/diagnostics/cast-type-mismatch.lua b/test/diagnostics/cast-type-mismatch.lua
new file mode 100644
index 00000000..9fba58f6
--- /dev/null
+++ b/test/diagnostics/cast-type-mismatch.lua
@@ -0,0 +1,13 @@
+TEST [[
+---@type string|boolean
+local t
+
+---@cast t string
+]]
+
+TEST [[
+---@type string|boolean
+local t
+
+---@cast t <!number!>
+]]
diff --git a/test/diagnostics/circle-doc-class.lua b/test/diagnostics/circle-doc-class.lua
new file mode 100644
index 00000000..089b0c9b
--- /dev/null
+++ b/test/diagnostics/circle-doc-class.lua
@@ -0,0 +1,14 @@
+TEST [[
+---@class <!A : B!>
+---@class <!B : C!>
+---@class <!C : D!>
+---@class <!D : A!>
+]]
+
+TEST [[
+---@class A : B
+---@class B : C
+---@class C : D
+---@class D
+]]
+
diff --git a/test/diagnostics/close-non-object.lua b/test/diagnostics/close-non-object.lua
new file mode 100644
index 00000000..11b882b7
--- /dev/null
+++ b/test/diagnostics/close-non-object.lua
@@ -0,0 +1,18 @@
+TEST [[
+local _ <close> = <!1!>
+]]
+
+TEST [[
+local _ <close> = <!''!>
+]]
+
+TEST [[
+local c <close> = <!(function () return 1 end)()!>
+]]
+
+TEST [[
+---@type unknown
+local t
+
+local _ <close> = t
+]]
diff --git a/test/diagnostics/code-after-break.lua b/test/diagnostics/code-after-break.lua
new file mode 100644
index 00000000..a150948b
--- /dev/null
+++ b/test/diagnostics/code-after-break.lua
@@ -0,0 +1,7 @@
+TEST [[
+while true do
+ break
+ <!print()
+ print()!>
+end
+]]
diff --git a/test/diagnostics/common.lua b/test/diagnostics/common.lua
deleted file mode 100644
index a1dbe819..00000000
--- a/test/diagnostics/common.lua
+++ /dev/null
@@ -1,2270 +0,0 @@
-local config = require 'config'
-local util = require 'utility'
-
-local disables = config.get(nil, 'Lua.diagnostics.disable')
-
-TEST [[
-local <!x!>
-]]
-
-TEST [[
-local y
-local x <close> = y
-]]
-
-TEST [[
-local function x()
-end
-x()
-]]
-
-TEST [[
-return function (x)
- x.a = 1
-end
-]]
-
-TEST [[
-local <!t!> = {}
-<!t!>.a = 1
-]]
-
-TEST [[
-local <!function <!x!>()
-end!>
-]]
-
-
-TEST [[
-local <!x!> = <!function () end!>
-]]
-
-TEST [[
-local <!x!>
-<!x!> = <!function () end!>
-]]
-
-TEST [[
-local <!function x()
-end!>
-local <!function <!y!>()
- x()
-end!>
-]]
-
-TEST [[
-local print, _G
-print(<!x!>)
-print(<!log!>)
-print(<!X!>)
-print(<!Log!>)
-print(<!y!>)
-print(Z)
-print(_G)
-Z = 1
-]]
-
-TEST [[
-::<!LABEL!>::
-]]
-
-TEST [[
-<! !>
-]]
-
-TEST [[
-
-<! !>
-]]
-
-TEST [[
-X = 1<! !>
-]]
-
-TEST [[
-X = [=[
- ]=]
-]]
-
-TEST [[
--- xxxx
-]]
-
-TEST [[
--- [=[
- ]=]
-]]
-
-TEST [[
-local x
-print(x)
-local <!x!>
-print(x)
-]]
-
-TEST [[
-local x
-print(x)
-local <!x!>
-print(x)
-local <!x!>
-print(x)
-]]
-
-TEST [[
-local _
-print(_)
-local _
-print(_)
-local _ENV
-<!print!>(_ENV) -- 由于重定义了_ENV,因此print变为了未定义全局变量
-]]
-
-TEST [[
-local x
-return x, function (<!x!>)
- return x
-end
-]]
-
-TEST [[
-print(1)
-_ENV = nil
-]]
-
-TEST [[
----@diagnostic disable: undefined-global
-_ENV = nil
-<!print!>(<!A!>) -- `print` and `A` should warning
-]]
-
-TEST [[
----@diagnostic disable: undefined-global
-local _ENV = nil
-<!print!>(<!A!>) -- `print` and `A` should warning
-]]
-
-TEST [[
-_ENV = {}
-print(A) -- no warning
-]]
-
-TEST [[
-local _ENV = {}
-print(A) -- no warning
-]]
-
-TEST [[
----@type iolib
-_ENV = {}
-<!print!>(stderr) -- `print` is warning but `stderr` is not
-]]
-
-TEST [[
----@type iolib
-local _ENV = {}
-<!print!>(stderr) -- `print` is warning but `stderr` is not
-]]
-
-TEST [[
-local _ENV = { print = print }
-print(1)
-]]
-
-util.arrayInsert(disables, 'undefined-env-child')
-TEST [[
-_ENV = nil
-<!GLOBAL!> = 1 --> _ENV.GLOBAL = 1
-]]
-
-TEST [[
-_ENV = nil
-local _ = <!print!> --> local _ = _ENV.print
-]]
-
-TEST [[
-_ENV = {}
-GLOBAL = 1 --> _ENV.GLOBAL = 1
-]]
-
-TEST [[
-_ENV = {}
-local _ = print --> local _ = _ENV.print
-]]
-
-TEST [[
-GLOBAL = 1
-_ENV = nil
-]]
-
-util.arrayRemove(disables, 'undefined-env-child')
-TEST [[
-<!print()
-('string')!>:sub(1, 1)
-]]
-
-TEST [[
-print()
-('string')
-]]
-
-TEST [[
-print
-{}
-{}
-]]
-
-TEST [[
-local x
-return x
- : f(1)
- : f(1)
-]]
-
-TEST [[
-return {
- <!print
- 'string'!>
-}
-]]
-
-TEST [[
-return {
- <!print
- {
- x = 1,
- }!>
-}
-]]
-
-TEST [[
-print()
-'string'
-]]
-
-TEST [[
-print
-{
- x = 1,
-}
-]]
-
-TEST [[
-local function x(a, b)
- return a, b
-end
-x(1, 2, <!3!>)
-]]
-
-TEST [[
-local function x(a, b, ...)
- return a, b, ...
-end
-x(1, 2, 3, 4, 5)
-]]
-
-TEST [[
----@type fun(a, b, ...)
-local x
-x(1, 2, 3, 4, 5)
-]]
-
-TEST [[
-local m = {}
-function m:x(a, b)
- return a, b
-end
-m:x(1, 2, <!3!>)
-]]
-
-TEST [[
-local m = {}
-function m:x(a, b)
- return a, b
-end
-m.x(m, 2, 3, <!4!>)
-]]
-
-TEST [[
-local m = {}
-function m.x(a, b)
- return a, b
-end
-m:x(1, <!2!>, <!3!>, <!4!>)
-]]
-
-TEST [[
-local function x(a, b)
- return a, b
-end
-x(1)
-]]
-
-TEST [[
----@param a integer
----@param b integer
-local function x(a, b)
- return a, b
-end
-<!x(1)!>
-]]
-
-TEST [[
----@param a integer
----@param b integer
-local function x(a, b)
- return a, b
-end
-<!x()!>
-]]
-
-TEST [[
----@param a integer
----@param b integer
----@param ... integer
-local function x(a, b, ...)
- return a, b, ...
-end
-x(1, 2)
-]]
-
-TEST [[
----@diagnostic disable: unused-local
-
----@param a integer
----@param b integer
-local function f(a, b)
-end
-
-f(...)
-]]
-
-TEST [[
----@diagnostic disable: unused-local
-
----@param a integer
----@param b integer
-local function f(a, b)
-end
-
-local function return2Numbers()
- return 1, 2
-end
-
-f(return2Numbers())
-]]
-
-TEST [[
----@param a integer
----@param b? integer
-local function x(a, b)
- return a, b
-end
-x(1)
-]]
-
-TEST [[
----@param b integer?
-local function x(a, b)
- return a, b
-end
-x(1)
-]]
-
-TEST [[
----@param b integer|nil
-local function x(a, b)
- return a, b
-end
-x(1)
-]]
-
-TEST [[
-local m = {}
-function m.x()
-end
-m:x()
-]]
-
-TEST [[
-InstanceName = 1
-Instance = _G[InstanceName]
-]]
-
-TEST [[
-local _ = (''):sub(1, 2)
-]]
-
-TEST [=[
-return [[
-
-]]
-]=]
-
-util.arrayInsert(disables, 'close-non-object')
-TEST [[
-local _ <close> = function () end
-]]
-util.arrayRemove(disables, 'close-non-object')
-
-TEST [[
-local _ <close> = <!1!>
-]]
-
-TEST [[
-local _ <close> = <!''!>
-]]
-
-TEST [[
-local c <close> = <!(function () return 1 end)()!>
-]]
-
-util.arrayInsert(disables, 'unused-local')
-TEST [[
-local f = <!function () end!>
-]]
-
-TEST [[
-local f;f = <!function () end!>
-]]
-
-TEST [[
-local <!function f() end!>
-]]
-
-TEST [[
-local <!function f()
- f()
-end!>
-]]
-
-
-TEST [[
-local <!function test()
-end!>
-
-local <!function foo ()
-end!>
-]]
-
-util.arrayRemove(disables, 'unused-local')
-TEST [[
-local mt, x
-function mt:m()
- function x:m()
- end
-end
-return mt, x
-]]
-
-TEST [[
-local mt = {}
-function mt:f()
-end
-return mt
-]]
-
-TEST [[
-local <!mt!> = {}
-function <!mt!>:f()
-end
-]]
-
-TEST [[
-local <!x!> = {}
-<!x!>.a = 1
-]]
-
-TEST [[
-local <!x!> = {}
-<!x!>['a'] = 1
-]]
-
-TEST [[
-local function f(<!self!>)
- return 'something'
-end
-f()
-]]
-
-TEST [[
-local function f(<!...!>)
- return 'something'
-end
-f()
-]]
-
-TEST [[
-local function f(var)
- print(var)
-end
-local var
-f(var)
-]]
-
-TEST [[
-local function f(a, b)
- return a, b
-end
-f(1, 2, <!3!>, <!4!>)
-]]
-
-TEST [[
-local mt = {}
-function mt:f(a, b)
- return a, b
-end
-mt.f(mt, 2, 3, <!4!>)
-]]
-
-
-TEST [[
-local mt = {}
-function mt.f(a, b)
- return a, b
-end
-mt:f(1, <!2!>, <!3!>, <!4!>)
-]]
-
-TEST [[
-local mt = {}
-function mt:f(a, b)
- return a, b
-end
-mt:f(1, 2, <!3!>, <!4!>)
-]]
-
-TEST [[
-local function f(a, b, ...)
- return a, b, ...
-end
-f(1, 2, 3, 4)
-]]
-
-TEST [[
-local _ = next({}, 1, <!2!>)
-print(1, 2, 3, 4, 5)
-]]
-
-TEST [[
-local function f(callback)
- callback(1, 2, 3)
-end
-f(function () end)
-]]
-
---TEST [[
---local realTostring = tostring
---tostring = function () end
---tostring(<!1!>)
---tostring = realTostring
---tostring(1)
---]]
-
-TEST [[
-<!aa!> = 1
-tostring = 1
-ROOT = 1
-_G.bb = 1
-]]
-
-TEST [[
-local f = load('')
-if f then
- f(1, 2, 3)
-end
-]]
-
-TEST [[
-local _ = <!unpack!>
-]]
-
-TEST [[
-X = table[<!x!>]
-]]
-
-TEST [[
-return {
- <!x!> = 1,
- y = 2,
- <!x!> = 3,
-}
-]]
-
-TEST [[
-return {
- x = 1,
- y = 2,
-}, {
- x = 1,
- y = 2,
-}
-]]
-
-TEST [[
-local m = {}
-function m.open()
-end
-
-m:open()
-]]
-
-TEST [[
-local m = {}
-function m:open()
-end
-
-m.open(m)
-]]
-
-TEST [[
-<!if true then
-end!>
-]]
-
-TEST [[
-<!if true then
-else
-end!>
-]]
-
-TEST [[
-if true then
-else
- return
-end
-]]
-
-TEST [[
-while true do
-end
-]]
-
-TEST [[
-<!for _ = 1, 10 do
-end!>
-]]
-
-TEST [[
-<!for _ in pairs({}) do
-end!>
-]]
-
-TEST [[
-local _ = 1, <!2!>
-]]
-
-TEST [[
-_ = 1, <!2!>
-]]
-
-TEST [[
-function X()
- do
- local k
- print(k)
- end
- local k = 1
- print(k)
-end
-]]
-
-TEST [[
-function X()
- local loc
- print(loc)
-end
-]]
-
-TEST [[
-local <!t!> = {}
-<!t!>[1] = 1
-]]
-
-TEST [[
-T1 = 1
-_ENV.T2 = 1
-_G.T3 = 1
-_ENV._G.T4 = 1
-_G._G._G.T5 = 1
-rawset(_G, 'T6', 1)
-rawset(_ENV, 'T7', 1)
-print(T1)
-print(T2)
-print(T3)
-print(T4)
-print(T5)
-print(T6)
-print(T7)
-]]
-
-TEST [[
-local x
-x = <!x or 0 + 1!>
-]]
-
-TEST [[
-local x, y
-x = <!x + y or 0!>
-]]
-
-TEST [[
-local x, y, z
-x = x and y or '' .. z
-]]
-
-TEST [[
-local x
-x = x or -1
-]]
-
-TEST [[
-local x
-x = x or (0 + 1)
-]]
-
-TEST [[
-local x, y
-x = (x + y) or 0
-]]
-
-TEST [[
-local t = {}
-t.a = 1
-t.a = 2
-return t
-]]
-
-TEST [[
-table.insert({}, 1, 2, <!3!>)
-]]
-
-TEST [[
-while true do
- break
- <!print()
- print()!>
-end
-]]
-
-TEST [[
-local x, <!y!>, <!z!> = 1
-print(x, y, z)
-]]
-
-TEST [[
-local x, y, <!z!> = 1, 2
-print(x, y, z)
-]]
-
-TEST [[
-local x, y, z = print()
-print(x, y, z)
-]]
-
-TEST [[
-local x, y, z
-print(x, y, z)
-]]
-
-TEST [[
-local x, y, z
-x, <!y!>, <!z!> = 1
-print(x, y, z)
-]]
-
-TEST [[
-X, <!Y!>, <!Z!> = 1
-]]
-
-TEST [[
-T = {}
-T.x, <!T.y!>, <!T.z!> = 1
-]]
-
-TEST [[
-T = {}
-T['x'], <!T['y']!>, <!T['z']!> = 1
-]]
-
---TEST [[
------@class <!Class!>
------@class <!Class!>
---]]
-
-TEST [[
----@alias <!A!> integer
----@alias <!A!> integer
-]]
-
-TEST [[
----@class A : <!B!>
-]]
-
-TEST [[
----@class <!A : B!>
----@class <!B : C!>
----@class <!C : D!>
----@class <!D : A!>
-]]
-
-TEST [[
----@class A : B
----@class B : C
----@class C : D
----@class D
-]]
-
-TEST [[
----@type <!A!>
-]]
-
-TEST [[
----@class A
----@type A|<!B!>|<!C!>
-]]
-
-TEST [[
----@class AAA
----@alias B AAA
-
----@type B
-]]
-
-TEST [[
----@alias B <!AAA!>
-]]
-
-TEST [[
----@class A
----@class B
----@alias <!A!> B
-]]
-
-TEST [[
----@param <!x!> <!Class!>
-]]
-
-TEST [[
----@class Class
----@param <!y!> Class
-local function f(x)
- return x
-end
-f()
-]]
-
-TEST [[
----@class Class
----@param <!y!> Class
-function F(x)
- return x
-end
-F()
-]]
-
-TEST [[
----@class Class
----@param <!x!> Class
----@param y Class
----@param <!x!> Class
-local function f(x, y)
- return x, y
-end
-
-local _
-f(_, _)
-]]
-
-TEST [[
----@field <!x Class!>
----@class Class
-]]
-
-TEST [[
----@class Class
-
----@field <!x Class!>
-]]
-
-TEST [[
----@class Class
----
----@field x Class
-]]
-
-TEST [[
----@class Class
----@field <!x!> Class
----@field <!x!> Class
-]]
-
-TEST [[
----@class Class : any
-]]
-
-TEST [[
----@type fun(a: integer)
-local f
-f(1)
-]]
-
-TEST [[
----@class c
-c = {}
-]]
-
-TEST [[
----@generic T: any
----@param v T
----@param message any
----@return T
----@return any message
-function assert(v, message)
- return v, message
-end
-]]
-
-TEST [[
----@type string
----|
-]]
-
-TEST [[
----@type
----| 'xx'
-]]
-
-TEST [[
----@class class
-local t
-]]
----[==[
--- checkUndefinedField 通用
-TEST [[
----@class Foo
----@field field1 integer
-local mt = {}
-function mt:Constructor()
- self.field2 = 1
-end
-function mt:method1() return 1 end
-function mt.method2() return 2 end
-
----@class Bar: Foo
----@field field4 integer
-local mt2 = {}
-
----@type Foo
-local v
-print(v.field1 + 1)
-print(v.field2 + 1)
-print(v.<!field3!> + 1)
-print(v:method1())
-print(v.method2())
-print(v:<!method3!>())
-
----@type Bar
-local v2
-print(v2.field1 + 1)
-print(v2.field2 + 1)
-print(v2.<!field3!> + 1)
-print(v2.field4 + 1)
-print(v2:method1())
-print(v2.method2())
-print(v2:<!method3!>())
-
-local v3 = {}
-print(v3.abc)
-
----@class Bar2
-local mt3
-function mt3:method() return 1 end
-print(mt3:method())
-]]
-
--- checkUndefinedField 通过type找到class
-TEST [[
----@class Foo
-local Foo
-function Foo:method1() end
-
----@type Foo
-local v
-v:method1()
-v:<!method2!>() -- doc.class.name
-]]
-
--- checkUndefinedField 通过type找到class,涉及到 class 继承版
-TEST [[
----@class Foo
-local Foo
-function Foo:method1() end
----@class Bar: Foo
-local Bar
-function Bar:method3() end
-
----@type Bar
-local v
-v:method1()
-v:<!method2!>() -- doc.class.name
-v:method3()
-]]
-
--- checkUndefinedField 类名和类变量同名,类变量被直接使用
-TEST [[
----@class Foo
-local Foo
-function Foo:method1() end
-Foo:<!method2!>() -- doc.class
-Foo:<!method2!>() -- doc.class
-]]
-
--- checkUndefinedField 没有@class的不检测
-TEST [[
-local Foo
-function Foo:method1()
- return Foo:method2() -- table
-end
-]]
-
--- checkUndefinedField 类名和类变量不同名,类变量被直接使用、使用self
-TEST [[
----@class Foo
-local mt
-function mt:method1()
- mt.<!method2!>() -- doc.class
- self:method1()
- return self.<!method2!>() -- doc.class.name
-end
-]]
-
--- checkUndefinedField 当会推导成多个class类型时
-TEST [[
----@class Foo
-local mt
-function mt:method1() end
-
----@class Bar
-local mt2
-function mt2:method2() end
-
----@type Foo
-local v
----@type Bar
-local v2
-<!v2!> = v
-v2:method1()
-v2:<!method2!>()
-]]
-
-TEST [[
----@type table
-T1 = {}
-print(T1.f1)
----@type tablelib
-T2 = {}
-print(T2.<!f2!>)
-]]
---]==]
-TEST [[
----@overload fun(...)
-local function f() end
-
-f(1)
-]]
-
-TEST [[
-for i = <!10, 1!> do
- print(i)
-end
-]]
-
-TEST [[
-for i = <!10, 1, 5!> do
- print(i)
-end
-]]
-
-TEST [[
-for i = <!100, 10, 1!> do
- print(i)
-end
-]]
-
-TEST [[
-for i = <!1, -10!> do
- print(i)
-end
-]]
-
-TEST [[
-for i = 1, 1 do
- print(i)
-end
-]]
-
-TEST [[
-local m = {}
-
-function <!m:fff!>()
-end
-
-function <!m:fff!>()
-end
-
-return m
-]]
-
-TEST [[
-local m = {}
-
-function <!m:fff!>()
-end
-
-do
- function <!m:fff!>()
- end
-end
-
-return m
-]]
-
-TEST [[
-local m = {}
-
-m.x = true
-m.x = false
-
-return m
-]]
-
-TEST [[
-local m = {}
-
-m.x = io.open('')
-m.x = nil
-
-return m
-]]
-
-TEST [[
----@meta
-
----@class A
----@field a boolean
-
----@return A
-local function f() end
-
-local r = f()
-r.x = 1
-
-return r.x
-]]
-
-TEST [[
----@diagnostic disable-next-line
-x = 1
-]]
-
-TEST [[
----@diagnostic disable-next-line: lowercase-global
-x = 1
-]]
-
-TEST [[
----@diagnostic disable-next-line: unused-local
-<!x!> = 1
-]]
-
-TEST [[
----@diagnostic disable
-x = 1
-]]
-
-TEST [[
----@diagnostic disable
----@diagnostic enable
-<!x!> = 1
-]]
-
-TEST [[
----@diagnostic disable
----@diagnostic disable
----@diagnostic enable
-x = 1
-]]
-
-TEST [[
----@diagnostic disable-next-line: <!xxx!>
-]]
-
-TEST [[
-local mt = {}
-
-function mt:a(x)
- return self, x
-end
-
-function mt:b(y)
- self:a(1):b(2)
- return y
-end
-
-return mt
-]]
-
-TEST [[
-local function each()
- return function ()
- end
-end
-
-for x in each() do
- print(x)
-end
-]]
-
-TEST [[
----@type string
-local s
-
-print(s:upper())
-]]
-
-TEST [[
-local t = ().
-return t
-]]
-
-TEST [[
-return {
- [1] = 1,
- ['1'] = 1,
-}
-]]
-
-TEST [[
-return {
- [print()] = 1,
- [print()] = 1,
-}
-]]
-
-TEST [[
----@type { x: number, y: number}
----| "'resume'"
-]]
-
-TEST [[
-return {
- 1, <!2!>, 3,
- [<!2!>] = 4,
-}
-]]
-
-TEST [[
---- @class Emit
---- @field on fun(eventName: string, cb: function)
---- @field on fun(eventName: '"died"', cb: fun(i: integer))
---- @field on fun(eventName: '"won"', cb: fun(s: string))
-local emit = {}
-]]
-
-TEST [[
---- @class Emit
---- @field on fun(eventName: string, cb: function)
---- @field <!on!> fun(eventName: '"died"', cb: fun(i: integer))
---- @field on fun(eventName: '"won"', cb: fun(s: string))
---- @field <!on!> fun(eventName: '"died"', cb: fun(i: integer))
-local emit = {}
-]]
-
--- redundant-return
-TEST [[
-local function f()
- <!return!>
-end
-f()
-]]
-
-TEST [[
-local function f()
- return nil
-end
-f()
-]]
-
-TEST [[
-local function f()
- local function x()
- <!return!>
- end
- x()
- return true
-end
-f()
-]]
-
-TEST [[
-local function f()
- local function x()
- return true
- end
- return x()
-end
-f()
-]]
-
-TEST [[
----@type file*
-local f
-local _ = f:read '*a'
-local _ = f:read('*a')
-]]
-
-TEST [[
-function F()
- <!coroutine.yield!>()
-end
-]]
-
-TEST [[
----@async
-function F()
- coroutine.yield()
-end
-]]
-
-TEST [[
----@type async fun()
-local f
-
-function F()
- <!f!>()
-end
-]]
-
-TEST [[
----@type async fun()
-local f
-
----@async
-function F()
- f()
-end
-]]
-
-TEST [[
-local function f(cb)
- cb()
-end
-
-return function()
- <!f>(function () ---@async
- return nil
- end)
-end
-]]
-
-TEST [[
-local function f(cb)
- pcall(cb)
-end
-
-return function()
- <!f!>(function () ---@async
- return nil
- end)
-end
-]]
-
-TEST [[
----@param c any
-local function f(c)
- return c
-end
-
-return function ()
- f(function () ---@async
- return nil
- end)
-end
-]]
-
-TEST [[
----@param ... any
-local function f(...)
- return ...
-end
-
-return function ()
- f(function () ---@async
- return nil
- end)
-end
-]]
-
-TEST [[
----@vararg any
-local function f(...)
- return ...
-end
-
-return function ()
- f(function () ---@async
- return nil
- end)
-end
-]]
-
-TEST [[
-local function f(...)
- return ...
-end
-
-return function ()
- f(function () ---@async
- return nil
- end)
-end
-]]
-
-TEST [[
-local function f(...)
- return ...
-end
-
-return function ()
- f(function () ---@async
- return nil
- end)
-end
-]]
-
-TEST [[
----@nodiscard
-local function f()
- return 1
-end
-
-<!f()!>
-]]
-
-TEST [[
----@nodiscard
-local function f()
- return 1
-end
-
-X = f()
-]]
-
-config.get(nil, 'Lua.diagnostics.neededFileStatus')['not-yieldable'] = 'Any'
-TEST [[
----@param cb fun()
-local function f(cb)
- return cb
-end
-
----@async
-local function af()
- return nil
-end
-
-f(<!af!>)
-]]
-
-TEST [[
----@param cb async fun()
-local function f(cb)
- return cb
-end
-
----@async
-local function af()
- return nil
-end
-
-f(af)
-]]
-
-TEST [[
-local function f(cb)
- cb()
-end
-
-local function af()
- <!f!>(function () ---@async
- return nil
- end)
-end
-
-return af
-]]
-
-TEST [[
-local function f(cb)
- cb()
-end
-
----@async
-local function af()
- f(function () ---@async
- return nil
- end)
-end
-
-return af
-]]
-
-TEST [[
-local _ = type(function () ---@async
- return nil
-end)
-]]
-
-TEST [[
----@param ... number
-local function f(...)
- return ...
-end
-
-return f
-]]
-
-TEST [[
----@type fun(...: string)
-]]
-
-TEST [[
----@type fun(xxx, yyy, ...): boolean
-]]
-
-TEST [[
-local <!x!>
-
-return {
- x = 1,
-}
-]]
-
-TEST [[
----@class A #1
-]]
-
-TEST [[
----@class A 1
-]]
-
-TEST [[
-return ('1'):upper()
-]]
-
-TEST [[
-local value
-value = '1'
-value = value:upper()
-]]
-
-TEST [[
-T = {}
----@deprecated # comment
-T.x = 1
-
-print(<!T.x!>)
-]]
-
-TEST [[
-T = {}
-
----@deprecated
-function T:ff()
-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!>()
-]]
-
-TEST [[
----@type integer?
-local x
-
-T = {}
-T[<!x!>] = 1
-]]
-
-TEST [[
-local x, y
-local z = x and y
-
-print(z.y)
-]]
-
-TEST [[
-local x, y
-function x()
- y()
-end
-
-function y()
- x()
-end
-
-x()
-]]
-
-TEST [[
----@meta
-
----@param x fun()
-local function f1(x)
-end
-
----@return fun()
-local function f2()
-end
-
-f1(f2())
-]]
-
-TEST [[
----@meta
-
----@type fun():integer
-local f
-
----@param x integer
-local function foo(x) end
-
-foo(f())
-]]
-
-TEST [[
----@type string|table
-local n
-
-print(n.x)
-]]
-
-TEST [[
----@diagnostic disable: unused-local, unused-function, undefined-global
-
-function F() end
-
----@param x boolean
-function F(x) end
-
-F(k())
-]]
-
-TEST [[
-local function f()
- return 1, 2, 3
-end
-
-local function k()
-end
-
-k(<!f()!>)
-]]
-
-TEST [[
----@diagnostic disable: unused-local
-local function f()
- return 1, 2, 3
-end
-
----@param x integer
-local function k(x)
-end
-
-k(f())
-]]
-
-TEST [[
----@cast <!x!> integer
-]]
-
-TEST [[
----@diagnostic disable: unused-local
-local x, y
----@cast y number
-]]
-
-TEST [[
----@class A
-
----@class B
----@field [integer] A
----@field [A] true
-]]
-
-TEST [[
----@class A
-
----@class B
----@field [<!A!>] A
----@field [<!A!>] true
-]]
-
-TEST [[
----@diagnostic disable: unused-local
-
----@type 'x'
-local t
-
-local n = t:upper()
-]]
-
-TEST [[
----@diagnostic disable: unused-local
-
----@alias A 'x'
-
----@type A
-local t
-
-local n = t:upper()
-]]
-
-TEST [[
-local t = {}
-
-function t:init() end
-
-<!t.init()!>
-]]
-
-TEST [[
----@meta
-
-return function f(x, y, z) end
-]]
-
-util.arrayInsert(disables, 'redundant-return')
-TEST [[
----@return number
-function F()
- <!return!>
-end
-]]
-
-TEST [[
----@return number, number
-function F()
- <!return!> 1
-end
-]]
-
-TEST [[
----@return number, number?
-function F()
- return 1
-end
-]]
-
-TEST [[
----@return ...
-function F()
- return
-end
-]]
-
-TEST [[
----@return number, number?
-function F()
- return 1, 1, <!1!>
-end
-]]
-
-TEST [[
----@return number, number?
-function F()
- return 1, 1, <!1!>, <!2!>, <!3!>
-end
-]]
-
-TEST [[
----@meta
-
----@return number, number
-local function r2() end
-
----@return number, number?
-function F()
- return 1, <!r2()!>
-end
-]]
-
-TEST [[
----@return number
-function F()
- X = 1<!!>
-end
-]]
-
-TEST [[
-local A
----@return number
-function F()
- if A then
- return 1
- end<!!>
-end
-]]
-
-TEST [[
-local A, B
----@return number
-function F()
- if A then
- return 1
- elseif B then
- return 2
- end<!!>
-end
-]]
-
-TEST [[
-local A, B
----@return number
-function F()
- if A then
- return 1
- elseif B then
- return 2
- else
- return 3
- end
-end
-]]
-
-TEST [[
-local A, B
----@return number
-function F()
- if A then
- elseif B then
- return 2
- else
- return 3
- end<!!>
-end
-]]
-
-TEST [[
----@return any
-function F()
- X = 1
-end
-]]
-
-TEST [[
----@return any, number
-function F()
- X = 1<!!>
-end
-]]
-
-TEST [[
----@return number, any
-function F()
- X = 1<!!>
-end
-]]
-
-TEST [[
----@return any, any
-function F()
- X = 1
-end
-]]
-
-TEST [[
-local A
----@return number
-function F()
- for _ = 1, 10 do
- if A then
- return 1
- end
- end
- error('should not be here')
-end
-]]
-
-TEST [[
-local A
----@return number
-function F()
- while true do
- if A then
- return 1
- end
- end
-end
-]]
-
-TEST [[
-local A
----@return number
-function F()
- while A do
- if A then
- return 1
- end
- end<!!>
-end
-]]
-
-TEST [[
-local A
----@return number
-function F()
- while A do
- if A then
- return 1
- else
- return 2
- end
- end
-end
-]]
-
-TEST [[
----@return number?
-function F()
-
-end
-]]
-
-util.arrayRemove(disables, 'redundant-return')
-
-TEST [[
----@class A
----@operator <!xxx!>: A
-]]
-
-config.add(nil, 'Lua.diagnostics.unusedLocalExclude', 'll_*')
-
-TEST [[
-local <!xx!>
-local ll_1
-local ll_2
-local <!ll!>
-]]
-
-config.remove(nil, 'Lua.diagnostics.unusedLocalExclude', 'll_*')
-
-TEST [[
----@diagnostic disable: undefined-global
-
-if X then
- return false
-elseif X then
- return false
-else
- return false
-end
-<!return true!>
-]]
-
-TEST [[
----@diagnostic disable: undefined-global
-
-function X()
- if X then
- return false
- elseif X then
- return false
- else
- return false
- end
- <!return true!>
-end
-]]
-
-TEST [[
-while true do
-end
-
-<!print(1)!>
-]]
-
-TEST [[
-while true do
-end
-
-<!print(1)!>
-]]
-
-TEST [[
-while X do
- X = 1
-end
-
-print(1)
-]]
-
-TEST [[
----@diagnostic disable: undefined-global
-
-while true do
- if not X then
- break
- end
-end
-
-print(1)
-
-do return end
-]]
-
-TEST [[
----@type unknown
-local t
-
-local _ <close> = t
-]]
-
-TEST [[
----@meta
----@diagnostic disable: duplicate-set-field
----@class A
-local m = {}
-
-function m.ff() end
-
-function m.ff(x) end
-
-m.ff(1)
-]]
-
-TEST [[
-local done = false
-
-local function set_done()
- done = true
-end
-
-while not done do
- set_done()
-end
-
-print(1)
-]]
-
-TEST [[
----@class A
----@field private x number
----@field protected y number
----@field public z number
-local t
-print(t.x)
-]]
-
-TEST [[
----@class A
----@field private x number
----@field protected y number
----@field public z number
-
----@type A
-local t
-
-print(t.<!x!>)
-]]
-
-TEST [[
----@class A
----@field private x number
----@field protected y number
----@field public z number
-
----@class B: A
-local t
-
-print(t.y)
-]]
-
-TEST [[
----@class A
----@field private x number
----@field protected y number
----@field public z number
-
----@class B: A
-
----@type B
-local t
-
-print(t.<!y!>)
-]]
-
-TEST [[
----@class A
----@field private x number
----@field protected y number
----@field public z number
-
----@class B: A
-
----@type B
-local t
-
-print(t.z)
-]]
-
-TEST [[
----@class A
----@field _id number
-
----@type A
-local t
-
-print(t._id)
-]]
-
-config.set(nil, 'Lua.doc.privateName', { '_*' })
-TEST [[
----@class A
----@field _id number
-
----@type A
-local t
-
-print(t.<!_id!>)
-
----@class B: A
-local t2
-
-print(t2.<!_id!>)
-]]
-config.set(nil, 'Lua.doc.privateName', nil)
-
-config.set(nil, 'Lua.doc.protectedName', { '_*' })
-TEST [[
----@class A
----@field _id number
-
----@type A
-local t
-
-print(t.<!_id!>)
-
----@class B: A
-local t2
-
-print(t2._id)
-]]
-config.set(nil, 'Lua.doc.protectedName', nil)
-
-TEST [[
----@class A
----@field private x number
-local mt = {}
-
-function mt:init()
- print(self.x)
-end
-]]
-
-TEST [[
----@diagnostic disable: unused-local
----@class A
----@field private x number
-local mt = {}
-
-function mt:init()
- ---@type A
- local obj = {}
-
- obj.x = 1
-end
-]]
-
-TEST [[
----@diagnostic disable: unused-local
----@class A
----@field private x number
-local mt = {}
-
-mt.init = function ()
- ---@type A
- local obj = {}
-
- obj.x = 1
-end
-]]
-
-TEST [[
----@class A
-X = {}
-
-function <!X.f!>() end
-
-function <!X.f!>() end
-]]
-
-TEST [[
----@meta
-
----@class A
-X = {}
-
-function X.f() end
-
-function X.f() end
-]]
-
-TEST [[
----@class A
-X = {}
-
-if true then
- function X.f() end
-else
- function X.f() end
-end
-]]
-
-TESTWITH 'global-in-nil-env' [[
-local function foo(_ENV)
- Joe = "human"
-end
-]]
diff --git a/test/diagnostics/count-down-loop.lua b/test/diagnostics/count-down-loop.lua
new file mode 100644
index 00000000..f4e385f5
--- /dev/null
+++ b/test/diagnostics/count-down-loop.lua
@@ -0,0 +1,29 @@
+TEST [[
+for i = <!10, 1!> do
+ print(i)
+end
+]]
+
+TEST [[
+for i = <!10, 1, 5!> do
+ print(i)
+end
+]]
+
+TEST [[
+for i = <!100, 10, 1!> do
+ print(i)
+end
+]]
+
+TEST [[
+for i = <!1, -10!> do
+ print(i)
+end
+]]
+
+TEST [[
+for i = 1, 1 do
+ print(i)
+end
+]]
diff --git a/test/diagnostics/deprecated.lua b/test/diagnostics/deprecated.lua
new file mode 100644
index 00000000..c5486752
--- /dev/null
+++ b/test/diagnostics/deprecated.lua
@@ -0,0 +1,21 @@
+TEST [[
+local _ = <!unpack!>
+]]
+
+TEST [[
+T = {}
+---@deprecated # comment
+T.x = 1
+
+print(<!T.x!>)
+]]
+
+TEST [[
+T = {}
+
+---@deprecated
+function T:ff()
+end
+
+<!T:ff!>()
+]]
diff --git a/test/diagnostics/discard-returns.lua b/test/diagnostics/discard-returns.lua
new file mode 100644
index 00000000..2e348390
--- /dev/null
+++ b/test/diagnostics/discard-returns.lua
@@ -0,0 +1,17 @@
+TEST [[
+---@nodiscard
+local function f()
+ return 1
+end
+
+<!f()!>
+]]
+
+TEST [[
+---@nodiscard
+local function f()
+ return 1
+end
+
+X = f()
+]]
diff --git a/test/diagnostics/doc-field-no-class.lua b/test/diagnostics/doc-field-no-class.lua
new file mode 100644
index 00000000..87db518c
--- /dev/null
+++ b/test/diagnostics/doc-field-no-class.lua
@@ -0,0 +1,16 @@
+TEST [[
+---@field <!x Class!>
+---@class Class
+]]
+
+TEST [[
+---@class Class
+
+---@field <!x Class!>
+]]
+
+TEST [[
+---@class Class
+---
+---@field x Class
+]]
diff --git a/test/diagnostics/duplicate-doc-alias.lua b/test/diagnostics/duplicate-doc-alias.lua
new file mode 100644
index 00000000..0373fee9
--- /dev/null
+++ b/test/diagnostics/duplicate-doc-alias.lua
@@ -0,0 +1,10 @@
+TEST [[
+---@alias <!A!> integer
+---@alias <!A!> integer
+]]
+
+TEST [[
+---@class A
+---@class B
+---@alias <!A!> B
+]]
diff --git a/test/diagnostics/duplicate-doc-field.lua b/test/diagnostics/duplicate-doc-field.lua
new file mode 100644
index 00000000..8f385335
--- /dev/null
+++ b/test/diagnostics/duplicate-doc-field.lua
@@ -0,0 +1,38 @@
+TEST [[
+---@class Class
+---@field <!x!> Class
+---@field <!x!> Class
+]]
+
+TEST [[
+--- @class Emit
+--- @field on fun(eventName: string, cb: function)
+--- @field on fun(eventName: '"died"', cb: fun(i: integer))
+--- @field on fun(eventName: '"won"', cb: fun(s: string))
+local emit = {}
+]]
+
+TEST [[
+--- @class Emit
+--- @field on fun(eventName: string, cb: function)
+--- @field <!on!> fun(eventName: '"died"', cb: fun(i: integer))
+--- @field on fun(eventName: '"won"', cb: fun(s: string))
+--- @field <!on!> fun(eventName: '"died"', cb: fun(i: integer))
+local emit = {}
+]]
+
+TEST [[
+---@class A
+
+---@class B
+---@field [integer] A
+---@field [A] true
+]]
+
+TEST [[
+---@class A
+
+---@class B
+---@field [<!A!>] A
+---@field [<!A!>] true
+]]
diff --git a/test/diagnostics/duplicate-doc-param.lua b/test/diagnostics/duplicate-doc-param.lua
new file mode 100644
index 00000000..42eb73d3
--- /dev/null
+++ b/test/diagnostics/duplicate-doc-param.lua
@@ -0,0 +1,12 @@
+TEST [[
+---@class Class
+---@param <!x!> Class
+---@param y Class
+---@param <!x!> Class
+local function f(x, y)
+ return x, y
+end
+
+local _
+f(_, _)
+]]
diff --git a/test/diagnostics/duplicate-index.lua b/test/diagnostics/duplicate-index.lua
new file mode 100644
index 00000000..3289c736
--- /dev/null
+++ b/test/diagnostics/duplicate-index.lua
@@ -0,0 +1,24 @@
+TEST [[
+return {
+ <!x!> = 1,
+ y = 2,
+ <!x!> = 3,
+}
+]]
+
+TEST [[
+return {
+ x = 1,
+ y = 2,
+}, {
+ x = 1,
+ y = 2,
+}
+]]
+
+TEST [[
+return {
+ 1, <!2!>, 3,
+ [<!2!>] = 4,
+}
+]]
diff --git a/test/diagnostics/duplicate-set-field.lua b/test/diagnostics/duplicate-set-field.lua
new file mode 100644
index 00000000..469bc3ea
--- /dev/null
+++ b/test/diagnostics/duplicate-set-field.lua
@@ -0,0 +1,74 @@
+TEST [[
+local m = {}
+
+function <!m:fff!>()
+end
+
+function <!m:fff!>()
+end
+
+return m
+]]
+
+TEST [[
+local m = {}
+
+function <!m:fff!>()
+end
+
+do
+ function <!m:fff!>()
+ end
+end
+
+return m
+]]
+
+TEST [[
+local m = {}
+
+m.x = true
+m.x = false
+
+return m
+]]
+
+TEST [[
+local m = {}
+
+m.x = io.open('')
+m.x = nil
+
+return m
+]]
+
+TEST [[
+---@class A
+X = {}
+
+function <!X.f!>() end
+
+function <!X.f!>() end
+]]
+
+TEST [[
+---@meta
+
+---@class A
+X = {}
+
+function X.f() end
+
+function X.f() end
+]]
+
+TEST [[
+---@class A
+X = {}
+
+if true then
+ function X.f() end
+else
+ function X.f() end
+end
+]]
diff --git a/test/diagnostics/empty-block.lua b/test/diagnostics/empty-block.lua
new file mode 100644
index 00000000..750397a4
--- /dev/null
+++ b/test/diagnostics/empty-block.lua
@@ -0,0 +1,32 @@
+TEST [[
+<!if true then
+end!>
+]]
+
+TEST [[
+<!if true then
+else
+end!>
+]]
+
+TEST [[
+if true then
+else
+ return
+end
+]]
+
+TEST [[
+while true do
+end
+]]
+
+TEST [[
+<!for _ = 1, 10 do
+end!>
+]]
+
+TEST [[
+<!for _ in pairs({}) do
+end!>
+]]
diff --git a/test/diagnostics/global-element.lua b/test/diagnostics/global-element.lua
index 0e4cdd61..0c31bade 100644
--- a/test/diagnostics/global-element.lua
+++ b/test/diagnostics/global-element.lua
@@ -1,31 +1,5 @@
local config = require 'config'
-local util = require 'utility'
--- disable all default groups to make isolated tests
-config.set(nil, 'Lua.diagnostics.groupFileStatus',
-{
- ['ambiguity'] = 'None',
- ['await'] = 'None',
- ['codestyle'] = 'None',
- ['conventions'] = 'None',
- ['duplicate'] = 'None',
- ['global'] = 'None',
- ['luadoc'] = 'None',
- ['redefined'] = 'None',
- ['strict'] = 'None',
- ['strong'] = 'None',
- ['type-check'] = 'None',
- ['unbalanced'] = 'None',
- ['unused'] = 'None'
-})
-
--- enable single diagnostic that is to be tested
-config.set(nil, 'Lua.diagnostics.neededFileStatus',
-{
- ['global-element'] = 'Any!' -- override groupFileStatus
-})
-
--- check that local elements are not warned about
TEST [[
local x = 123
x = 321
@@ -87,11 +61,3 @@ function GLOBAL_CLOSURE()
<!elem2!> = 2
end
]]
-
--- reset configurations
-config.set(nil, 'Lua.diagnostics.groupFileStatus',
-{})
-config.set(nil, 'Lua.diagnostics.neededFileStatus',
-{})
-config.set(nil, 'Lua.diagnostics.globals',
-{})
diff --git a/test/diagnostics/global-in-nil-env.lua b/test/diagnostics/global-in-nil-env.lua
new file mode 100644
index 00000000..a0b8cd3e
--- /dev/null
+++ b/test/diagnostics/global-in-nil-env.lua
@@ -0,0 +1,44 @@
+TEST [[
+local _
+print(_)
+local _
+print(_)
+local _ENV
+<!print!>(_ENV) -- 由于重定义了_ENV,因此print变为了未定义全局变量
+]]
+
+TEST [[
+_ENV = nil
+<!print!>(<!A!>) -- `print` and `A` should warning
+]]
+
+TEST [[
+local _ENV = nil
+<!print!>(<!A!>) -- `print` and `A` should warning
+]]
+
+TEST [[
+_ENV = {}
+print(A) -- no warning
+]]
+
+TEST [[
+local _ENV = {}
+print(A) -- no warning
+]]
+
+TEST [[
+_ENV = nil
+<!GLOBAL!> = 1 --> _ENV.GLOBAL = 1
+]]
+
+TEST [[
+_ENV = nil
+local _ = <!print!> --> local _ = _ENV.print
+]]
+
+TEST [[
+local function foo(_ENV)
+ Joe = "human"
+end
+]]
diff --git a/test/diagnostics/incomplete-signature-doc.lua b/test/diagnostics/incomplete-signature-doc.lua
index c3099cd2..7cd144c0 100644
--- a/test/diagnostics/incomplete-signature-doc.lua
+++ b/test/diagnostics/incomplete-signature-doc.lua
@@ -1,31 +1,21 @@
-local config = require 'config'
-local util = require 'utility'
-
--- disable all default groups to make isolated tests
-config.set(nil, 'Lua.diagnostics.groupFileStatus',
-{
- ['ambiguity'] = 'None',
- ['await'] = 'None',
- ['codestyle'] = 'None',
- ['conventions'] = 'None',
- ['duplicate'] = 'None',
- ['global'] = 'None',
- ['luadoc'] = 'None',
- ['redefined'] = 'None',
- ['strict'] = 'None',
- ['strong'] = 'None',
- ['type-check'] = 'None',
- ['unbalanced'] = 'None',
- ['unused'] = 'None'
-})
-
--- enable single diagnostic that is to be tested
-config.set(nil, 'Lua.diagnostics.neededFileStatus',
-{
- ['incomplete-signature-doc'] = 'Any!' -- override groupFileStatus
-})
-
--- check global functions
+-- -------------------------------------
+-- about the structure of these test cases
+--
+-- the following test cases are grouped by the number of parameters and return values of the functions
+-- so first global functions with:
+-- no parameter and return value (FG), one parameter (FGP), two parameters (FGPP),
+-- one return value (FGR), two return values (FGRR) and parameter and return value (FGPR)
+-- after that, these groups are also done for local functions (FL, FLP, ...)
+--
+-- in these groups, different versions of documentation are tested:
+-- no comment, simple comment, @async annotation (which is no signature doc),
+-- incomplete signature doc (only part of the necessary @param or @return annotations, if possible) - the only cases that should generating warnings
+-- and complete signature docs (all necessary @param and @return annotations)
+-- -------------------------------------
+
+-- global functions no parameter, no return value
+-- no incomplete signature docs possible
+
TEST [[
function FG0()
end
@@ -33,15 +23,26 @@ end
---comment
function FG1()
end
+
+---@async
+function FG1_()
+end
]]
+-- global functions with single parameter, no return value
+-- no incomplete signature docs possible
TEST [[
function FGP0(p)
print(p)
end
---comment
-function FGP1(<!p!>)
+function FGP1(p)
+ print(p)
+end
+
+---@async
+function FGP1_(p)
print(p)
end
@@ -52,13 +53,20 @@ function FGP2(p)
end
]]
+-- global functions with two parameters, no return value
+-- incomplete signature docs when exactly one of the parameters is documented
TEST [[
function FGPP0(p0, p1)
print(p0, p1)
end
---comment
-function FGPP1(<!p0!>, <!p1!>)
+function FGPP1(p0, p1)
+ print(p0, p1)
+end
+
+---@async
+function FGPP1_(p0, p1)
print(p0, p1)
end
@@ -69,6 +77,12 @@ function FGPP2(p0, <!p1!>)
end
---comment
+---@param p1 any
+function FGPP2_(<!p0!>, p1)
+ print(p0, p1)
+end
+
+---comment
---@param p0 any
---@param p1 any
function FGPP3(p0, p1)
@@ -76,6 +90,8 @@ function FGPP3(p0, p1)
end
]]
+-- global functions with no parameter, single return value
+-- no incomplete signature docs possible
TEST [[
function FGR0()
return 0
@@ -83,7 +99,12 @@ end
---comment
function FGR1()
- return <!0!>
+ return 0
+end
+
+---@async
+function FGR1_()
+ return 0
end
---comment
@@ -93,6 +114,8 @@ function FGR2()
end
]]
+-- global functions with no parameter, two return values
+-- incomplete signature docs when exactly one of the return values is documented
TEST [[
function FGRR0()
return 0, 1
@@ -100,7 +123,12 @@ end
---comment
function FGRR1()
- return <!0!>, <!1!>
+ return 0, 1
+end
+
+---@async
+function FGRR1_()
+ return 0, 1
end
---comment
@@ -117,6 +145,8 @@ function FGRR3()
end
]]
+-- global functions with one parameter, one return value
+-- incomplete signature docs when exactly one of parameter or return value is documented
TEST [[
function FGPR0(p)
print(p)
@@ -124,9 +154,15 @@ function FGPR0(p)
end
---comment
-function FGPR1(<!p!>)
+function FGPR1(p)
print(p)
- return <!0!>
+ return 0
+end
+
+---@async
+function FGPR1_(p)
+ print(p)
+ return 0
end
---comment
@@ -152,8 +188,8 @@ function FGPR4(p)
end
]]
--- check local functions
-
+-- local functions with no parameter, no return value
+-- no incomplete signature docs possible
TEST [[
local function FL0()
end
@@ -165,8 +201,13 @@ local function FL1()
end
FL1()
+
+---@async
+local function FL1_()
]]
+-- local functions with single parameter, no return value
+-- no incomplete signature docs possible
TEST [[
local function FLP0(p)
print(p)
@@ -175,12 +216,17 @@ end
FLP0(0)
---comment
-local function FLP1(<!p!>)
+local function FLP1(p)
print(p)
end
FLP1(0)
+---@async
+local function FLP1_(p)
+ print(p)
+end
+
---comment
---@param p any
local function FLP2(p)
@@ -190,6 +236,8 @@ end
FLP2(0)
]]
+-- local functions with two parameters, no return value
+-- incomplete signature docs when exactly one of the parameters is documented
TEST [[
local function FLPP0(p0, p1)
print(p0, p1)
@@ -198,12 +246,17 @@ end
FLPP0(0, 1)
---comment
-local function FLPP1(<!p0!>, <!p1!>)
+local function FLPP1(p0, p1)
print(p0, p1)
end
FLPP1(0, 1)
+---@async
+local function FLPP1_(p0, p1)
+ print(p0, p1)
+end
+
---comment
---@param p0 any
local function FLPP2(p0, <!p1!>)
@@ -222,6 +275,8 @@ end
FLPP3(0, 1)
]]
+-- local functions with no parameter, single return value
+-- no incomplete signature docs possible
TEST [[
local function FLR0()
return 0
@@ -231,7 +286,12 @@ local vr0 = FLR0()
---comment
local function FLR1()
- return <!0!>
+ return 0
+end
+
+---@async
+local function FLR1_()
+ return 0
end
local vr1 = FLR1()
@@ -245,6 +305,8 @@ end
local vr2 = FLR2()
]]
+-- local functions with no parameter, two return values
+-- incomplete signature docs when exactly one of the return values is documented
TEST [[
local function FLRR0()
return 0, 1
@@ -254,11 +316,16 @@ local vrr0, _ = FLRR0()
---comment
local function FLRR1()
- return <!0!>, <!1!>
+ return 0, 1
end
local vrr1, _ = FLRR1()
+---@async
+local function FLRR1_()
+ return 0, 1
+end
+
---comment
---@return integer
local function FLRR2()
@@ -277,6 +344,8 @@ end
local vrr3, _ = FLRR3()
]]
+-- local functions with one parameter, one return value
+-- incomplete signature docs when exactly one of parameter or return value is documented
TEST [[
local function FLPR0(p)
print(p)
@@ -286,9 +355,15 @@ end
local vpr0 = FLPR0(0)
---comment
-local function FLPR1(<!p!>)
+local function FLPR1(p)
print(p)
- return <!0!>
+ return 0
+end
+
+---@async
+local function FLPR1_(p)
+ print(p)
+ return 0
end
local vpr1 = FLPR1(0)
@@ -321,11 +396,3 @@ end
local vpr4 = FLPR4(0)
]]
-
--- reset configurations
-config.set(nil, 'Lua.diagnostics.groupFileStatus',
-{})
-config.set(nil, 'Lua.diagnostics.neededFileStatus',
-{})
-config.set(nil, 'Lua.diagnostics.globals',
-{}) \ No newline at end of file
diff --git a/test/diagnostics/init.lua b/test/diagnostics/init.lua
index c2fe720d..99a5dc24 100644
--- a/test/diagnostics/init.lua
+++ b/test/diagnostics/init.lua
@@ -4,9 +4,15 @@ local config = require 'config'
local util = require 'utility'
local catch = require 'catch'
-config.get(nil, 'Lua.diagnostics.neededFileStatus')['deprecated'] = 'Any'
-config.get(nil, 'Lua.diagnostics.neededFileStatus')['type-check'] = 'Any'
-config.get(nil, 'Lua.diagnostics.neededFileStatus')['await-in-sync'] = 'Any'
+local status = config.get(nil, 'Lua.diagnostics.neededFileStatus')
+
+for key in pairs(status) do
+ status[key] = 'Any!'
+end
+
+config.set('nil', 'Lua.type.castNumberToInteger', false)
+config.set('nil', 'Lua.type.weakUnionCheck', false)
+config.set('nil', 'Lua.type.weakNilCheck', false)
rawset(_G, 'TEST', true)
@@ -32,9 +38,14 @@ function TEST(script)
files.setText(TESTURI, newScript)
files.open(TESTURI)
local origins = {}
+ local filteds = {}
local results = {}
core(TESTURI, false, function (result)
- results[#results+1] = { result.start, result.finish }
+ if DIAG_CARE == result.code
+ or DIAG_CARE == '*' then
+ results[#results+1] = { result.start, result.finish }
+ filteds[#filteds+1] = result
+ end
origins[#origins+1] = result
end)
@@ -49,44 +60,69 @@ function TEST(script)
files.remove(TESTURI)
return function (callback)
- callback(origins)
+ callback(filteds)
end
end
-function TESTWITH(code)
- return function (script)
- local newScript, catched = catch(script, '!')
- files.setText(TESTURI, newScript)
- files.open(TESTURI)
- local origins = {}
- local results = {}
- core(TESTURI, false, function (result)
- if code ~= result.code then
- return
- end
- results[#results+1] = { result.start, result.finish }
- origins[#origins+1] = result
- end)
-
- if results[1] then
- if not founded(catched['!'] or {}, results) then
- error(('%s\n%s'):format(util.dump(catched['!']), util.dump(results)))
- end
- else
- assert(#catched['!'] == 0)
- end
-
- files.remove(TESTURI)
-
- return function (callback)
- callback(origins)
- end
- end
+local function check(name)
+ DIAG_CARE = name
+ require('diagnostics.' .. name)
end
-require 'diagnostics.common'
-require 'diagnostics.type-check'
-require 'diagnostics.incomplete-signature-doc'
-require 'diagnostics.missing-global-doc'
-require 'diagnostics.missing-local-export-doc'
-require 'diagnostics.global-element'
+check 'ambiguity-1'
+check 'assign-type-mismatch'
+check 'await-in-sync'
+check 'cast-local-type'
+check 'cast-type-mismatch'
+check 'circle-doc-class'
+check 'close-non-object'
+check 'code-after-break'
+check 'count-down-loop'
+check 'deprecated'
+check 'discard-returns'
+check 'doc-field-no-class'
+check 'duplicate-doc-alias'
+check 'duplicate-doc-field'
+check 'duplicate-doc-param'
+check 'duplicate-index'
+check 'duplicate-set-field'
+check 'empty-block'
+check 'global-element'
+check 'global-in-nil-env'
+check 'incomplete-signature-doc'
+check 'inject-field'
+check 'invisible'
+check 'lowercase-global'
+check 'missing-fields'
+check 'missing-global-doc'
+check 'missing-local-export-doc'
+check 'missing-parameter'
+check 'missing-return-value'
+check 'missing-return'
+check 'need-check-nil'
+check 'newfield-call'
+check 'newline-call'
+check 'not-yieldable'
+check 'param-type-mismatch'
+check 'redefined-local'
+check 'redundant-parameter'
+check 'redundant-return-value'
+check 'redundant-return'
+check 'redundant-value'
+check 'return-type-mismatch'
+check 'trailing-space'
+check 'unbalanced-assignments'
+check 'undefined-doc-class'
+check 'undefined-doc-name'
+check 'undefined-doc-param'
+check 'undefined-env-child'
+check 'undefined-field'
+check 'undefined-global'
+check 'unknown-cast-variable'
+check 'unknown-diag-code'
+check 'unknown-operator'
+check 'unreachable-code'
+check 'unused-function'
+check 'unused-label'
+check 'unused-local'
+check 'unused-vararg'
diff --git a/test/diagnostics/inject-field.lua b/test/diagnostics/inject-field.lua
new file mode 100644
index 00000000..9bb0f8fc
--- /dev/null
+++ b/test/diagnostics/inject-field.lua
@@ -0,0 +1,84 @@
+TEST [[
+---@class Class
+local m = {}
+
+m.xx = 1 -- OK
+
+---@type Class
+local m
+
+m.xx = 1 -- OK
+m.<!yy!> = 1 -- Warning
+]]
+
+TEST [[
+---@class Class
+local m = {}
+
+m.xx = 1 -- OK
+
+---@class Class
+local m
+
+m.xx = 1 -- OK
+m.yy = 1 -- OK
+]]
+
+TEST [[
+---@type { xx: number }
+local m
+
+m.xx = 1 -- OK
+m.<!yy!> = 1 -- Warning
+]]
+
+TEST [[
+---@type { xx: number, [any]: any }
+local m
+
+m.xx = 1 -- OK
+m.yy = 1 -- OK
+]]
+
+TEST [[
+---@class Class
+---@field x number
+
+---@type Class
+local t
+
+t.x = 1 -- OK
+t.<!y!> = 2 -- Warning
+]]
+
+TEST [[
+---@class Class
+---@field x number
+---@field [any] any
+
+---@type Class
+local t
+
+t.x = 1 -- OK
+t.y = 2 -- OK
+]]
+
+
+TEST [[
+---@class (exact) Class
+---@field x number
+local m = {
+ x = 1, -- OK
+ <!y!> = 2, -- Warning
+}
+
+m.x = 1 -- OK
+m.<!y!> = 2 -- Warning
+
+function m:init() -- OK
+ self.x = 1 -- OK
+ self.<!y!> = 2 -- Warning
+ function self:<!xx!>() -- Warning
+ end
+end
+]]
diff --git a/test/diagnostics/invisible.lua b/test/diagnostics/invisible.lua
new file mode 100644
index 00000000..2fc6791e
--- /dev/null
+++ b/test/diagnostics/invisible.lua
@@ -0,0 +1,145 @@
+local config = require 'config'
+
+TEST [[
+---@class A
+---@field private x number
+---@field protected y number
+---@field public z number
+local t
+print(t.x)
+]]
+
+TEST [[
+---@class A
+---@field private x number
+---@field protected y number
+---@field public z number
+
+---@type A
+local t
+
+print(t.<!x!>)
+]]
+
+TEST [[
+---@class A
+---@field private x number
+---@field protected y number
+---@field public z number
+
+---@class B: A
+local t
+
+print(t.y)
+]]
+
+TEST [[
+---@class A
+---@field private x number
+---@field protected y number
+---@field public z number
+
+---@class B: A
+
+---@type B
+local t
+
+print(t.<!y!>)
+]]
+
+TEST [[
+---@class A
+---@field private x number
+---@field protected y number
+---@field public z number
+
+---@class B: A
+
+---@type B
+local t
+
+print(t.z)
+]]
+TEST [[
+---@class A
+---@field _id number
+
+---@type A
+local t
+
+print(t._id)
+]]
+
+config.set(nil, 'Lua.doc.privateName', { '_*' })
+TEST [[
+---@class A
+---@field _id number
+
+---@type A
+local t
+
+print(t.<!_id!>)
+
+---@class B: A
+local t2
+
+print(t2.<!_id!>)
+]]
+config.set(nil, 'Lua.doc.privateName', nil)
+
+config.set(nil, 'Lua.doc.protectedName', { '_*' })
+TEST [[
+---@class A
+---@field _id number
+
+---@type A
+local t
+
+print(t.<!_id!>)
+
+---@class B: A
+local t2
+
+print(t2._id)
+]]
+config.set(nil, 'Lua.doc.protectedName', nil)
+
+TEST [[
+---@class A
+---@field private x number
+local mt = {}
+
+function mt:init()
+ print(self.x)
+end
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+---@diagnostic disable: missing-fields
+---@class A
+---@field private x number
+local mt = {}
+
+function mt:init()
+ ---@type A
+ local obj = {}
+
+ obj.x = 1
+end
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+---@diagnostic disable: missing-fields
+---@class A
+---@field private x number
+local mt = {}
+
+mt.init = function ()
+ ---@type A
+ local obj = {}
+
+ obj.x = 1
+end
+]]
diff --git a/test/diagnostics/lowercase-global.lua b/test/diagnostics/lowercase-global.lua
new file mode 100644
index 00000000..bf73b8c8
--- /dev/null
+++ b/test/diagnostics/lowercase-global.lua
@@ -0,0 +1,39 @@
+TEST [[
+<!aa!> = 1
+tostring = 1
+ROOT = 1
+_G.bb = 1
+]]
+
+TEST [[
+---@diagnostic disable-next-line
+x = 1
+]]
+
+TEST [[
+---@diagnostic disable-next-line: lowercase-global
+x = 1
+]]
+
+TEST [[
+---@diagnostic disable-next-line: unused-local
+<!x!> = 1
+]]
+
+TEST [[
+---@diagnostic disable
+x = 1
+]]
+
+TEST [[
+---@diagnostic disable
+---@diagnostic enable
+<!x!> = 1
+]]
+
+TEST [[
+---@diagnostic disable
+---@diagnostic disable
+---@diagnostic enable
+x = 1
+]]
diff --git a/test/diagnostics/missing-fields.lua b/test/diagnostics/missing-fields.lua
new file mode 100644
index 00000000..ab87f81d
--- /dev/null
+++ b/test/diagnostics/missing-fields.lua
@@ -0,0 +1,233 @@
+TEST [[
+---@diagnostic disable: unused-local
+---@class A
+---@field x number
+---@field y? number
+---@field z number
+
+---@type A
+local t = <!{}!>
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+---@class A
+---@field x number
+---@field y? number
+---@field z number
+
+---@type A
+local t = <!{
+ x = 1,
+}!>
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+---@class A
+---@field x number
+---@field y? number
+---@field z number
+
+---@type A
+local t = <!{
+ x = 1,
+ y = 2,
+}!>
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+---@class A
+---@field x number
+---@field y? number
+---@field z number
+
+---@type A
+local t = {
+ x = 1,
+ y = 2,
+ z = 3,
+}
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+---@class A
+---@field x number
+---@field y? number
+---@field z number
+
+---@type A
+local t = {
+ x = 1,
+ z = 3,
+}
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+---@class A
+---@field x number
+---@field y? number
+---@field z number
+
+---@param a A
+local function f(a) end
+
+f <!{}!>
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+---@class A
+---@field x number
+---@field y? number
+---@field z number
+
+---@param a A
+local function f(a) end
+
+f <!{
+ x = 1,
+}!>
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+---@class A
+---@field x number
+---@field y? number
+---@field z number
+
+---@param a A
+local function f(a) end
+
+f <!{
+ x = 1,
+ y = 2,
+}!>
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+---@class A
+---@field x number
+---@field y? number
+---@field z number
+
+---@param a A
+local function f(a) end
+
+f {
+ x = 1,
+ y = 2,
+ z = 3,
+}
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+---@class A
+---@field x number
+---@field y? number
+---@field z number
+
+---@param a A
+local function f(a) end
+
+f {
+ x = 1,
+ z = 3,
+}
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+---@class A
+---@field x number
+local t = {}
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+
+---@class A
+---@field x number
+
+---@class A
+local t = {}
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+
+---@class Foo
+---@field a number
+---@field b number
+---@field c number
+
+---@type Foo|Foo[]
+local a = {
+ {
+ a = 1,
+ b = 2,
+ c = 3,
+ }
+}
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+
+---@class Foo
+---@field a number
+---@field b number
+---@field c number
+
+---@class Bar
+---@field ba number
+---@field bb number
+---@field bc number
+
+---@type Foo|Bar
+local b = {
+ a = 1,
+ b = 2,
+ c = 3,
+}
+]]
+
+TEST [[
+---@class A
+---@field x integer
+
+---@type A
+return <!{}!>
+]]
+
+TEST [[
+---@class A
+---@field x number
+
+---@class B
+---@field y number
+
+---@type A|B
+local t = <!{
+ z = 1,
+}!>
+]]
+
+TEST [[
+---@class A
+---@field x number
+
+---@class B
+---@field y number
+
+---@type A|B
+local t = {
+ y = 1,
+}
+]]
diff --git a/test/diagnostics/missing-global-doc.lua b/test/diagnostics/missing-global-doc.lua
index 2ebc237a..de5250fd 100644
--- a/test/diagnostics/missing-global-doc.lua
+++ b/test/diagnostics/missing-global-doc.lua
@@ -1,31 +1,3 @@
-local config = require 'config'
-local util = require 'utility'
-
--- disable all default groups to make isolated tests
-config.set(nil, 'Lua.diagnostics.groupFileStatus',
-{
- ['ambiguity'] = 'None',
- ['await'] = 'None',
- ['codestyle'] = 'None',
- ['conventions'] = 'None',
- ['duplicate'] = 'None',
- ['global'] = 'None',
- ['luadoc'] = 'None',
- ['redefined'] = 'None',
- ['strict'] = 'None',
- ['strong'] = 'None',
- ['type-check'] = 'None',
- ['unbalanced'] = 'None',
- ['unused'] = 'None'
-})
-
--- enable single diagnostic that is to be tested
-config.set(nil, 'Lua.diagnostics.neededFileStatus',
-{
- ['missing-global-doc'] = 'Any!' -- override groupFileStatus
-})
-
-
-- check global functions
TEST [[
<!function FG0()
@@ -323,11 +295,3 @@ end
local vpr4 = FLPR4(0)
]]
-
--- reset configurations
-config.set(nil, 'Lua.diagnostics.groupFileStatus',
-{})
-config.set(nil, 'Lua.diagnostics.neededFileStatus',
-{})
-config.set(nil, 'Lua.diagnostics.globals',
-{}) \ No newline at end of file
diff --git a/test/diagnostics/missing-local-export-doc.lua b/test/diagnostics/missing-local-export-doc.lua
index bdbf1cfb..3d942216 100644
--- a/test/diagnostics/missing-local-export-doc.lua
+++ b/test/diagnostics/missing-local-export-doc.lua
@@ -1,31 +1,3 @@
-local config = require 'config'
-local util = require 'utility'
-
--- disable all default groups to make isolated tests
-config.set(nil, 'Lua.diagnostics.groupFileStatus',
-{
- ['ambiguity'] = 'None',
- ['await'] = 'None',
- ['codestyle'] = 'None',
- ['conventions'] = 'None',
- ['duplicate'] = 'None',
- ['global'] = 'None',
- ['luadoc'] = 'None',
- ['redefined'] = 'None',
- ['strict'] = 'None',
- ['strong'] = 'None',
- ['type-check'] = 'None',
- ['unbalanced'] = 'None',
- ['unused'] = 'None'
-})
-
--- enable single diagnostic that is to be tested
-config.set(nil, 'Lua.diagnostics.neededFileStatus',
-{
- ['missing-local-export-doc'] = 'Any!' -- override groupFileStatus
-})
-
-
-- check global functions
TEST [[
local mod = {}
@@ -201,11 +173,3 @@ mod.flpr3 = flpr3
mod.flpr4 = flpr4
return mod
]]
-
--- reset configurations
-config.set(nil, 'Lua.diagnostics.groupFileStatus',
-{})
-config.set(nil, 'Lua.diagnostics.neededFileStatus',
-{})
-config.set(nil, 'Lua.diagnostics.globals',
-{}) \ No newline at end of file
diff --git a/test/diagnostics/missing-parameter.lua b/test/diagnostics/missing-parameter.lua
new file mode 100644
index 00000000..154d630b
--- /dev/null
+++ b/test/diagnostics/missing-parameter.lua
@@ -0,0 +1,90 @@
+
+TEST [[
+local function x(a, b)
+ return a, b
+end
+x(1)
+]]
+
+TEST [[
+---@param a integer
+---@param b integer
+local function x(a, b)
+ return a, b
+end
+<!x(1)!>
+]]
+
+TEST [[
+---@param a integer
+---@param b integer
+local function x(a, b)
+ return a, b
+end
+<!x()!>
+]]
+
+TEST [[
+---@param a integer
+---@param b integer
+---@param ... integer
+local function x(a, b, ...)
+ return a, b, ...
+end
+x(1, 2)
+]]
+
+TEST [[
+---@param a integer
+---@param b integer
+local function f(a, b)
+end
+
+f(...)
+]]
+
+TEST [[
+---@param a integer
+---@param b integer
+local function f(a, b)
+end
+
+local function return2Numbers()
+ return 1, 2
+end
+
+f(return2Numbers())
+]]
+
+TEST [[
+---@param a integer
+---@param b? integer
+local function x(a, b)
+ return a, b
+end
+x(1)
+]]
+
+TEST [[
+---@param b integer?
+local function x(a, b)
+ return a, b
+end
+x(1)
+]]
+
+TEST [[
+---@param b integer|nil
+local function x(a, b)
+ return a, b
+end
+x(1)
+]]
+
+TEST [[
+local t = {}
+
+function t:init() end
+
+<!t.init()!>
+]]
diff --git a/test/diagnostics/missing-return-value.lua b/test/diagnostics/missing-return-value.lua
new file mode 100644
index 00000000..3bad7974
--- /dev/null
+++ b/test/diagnostics/missing-return-value.lua
@@ -0,0 +1,34 @@
+TEST [[
+---@type fun():number
+local function f()
+ <!return!>
+end
+]]
+
+TEST [[
+---@return number
+function F()
+ <!return!>
+end
+]]
+
+TEST [[
+---@return number, number
+function F()
+ <!return!> 1
+end
+]]
+
+TEST [[
+---@return number, number?
+function F()
+ return 1
+end
+]]
+
+TEST [[
+---@return ...
+function F()
+ return
+end
+]]
diff --git a/test/diagnostics/missing-return.lua b/test/diagnostics/missing-return.lua
new file mode 100644
index 00000000..b8c1e7d3
--- /dev/null
+++ b/test/diagnostics/missing-return.lua
@@ -0,0 +1,158 @@
+TEST [[
+---@type fun():number
+local function f()
+<!!>end
+]]
+
+TEST [[
+---@type fun():number?
+local function f()
+end
+]]
+
+TEST [[
+---@type fun():...
+local function f()
+end
+]]
+
+TEST [[
+---@return number
+function F()
+ X = 1<!!>
+end
+]]
+TEST [[
+local A
+---@return number
+function F()
+ if A then
+ return 1
+ end<!!>
+end
+]]
+
+TEST [[
+local A, B
+---@return number
+function F()
+ if A then
+ return 1
+ elseif B then
+ return 2
+ end<!!>
+end
+]]
+
+TEST [[
+local A, B
+---@return number
+function F()
+ if A then
+ return 1
+ elseif B then
+ return 2
+ else
+ return 3
+ end
+end
+]]
+
+TEST [[
+local A, B
+---@return number
+function F()
+ if A then
+ elseif B then
+ return 2
+ else
+ return 3
+ end<!!>
+end
+]]
+
+TEST [[
+---@return any
+function F()
+ X = 1
+end
+]]
+
+TEST [[
+---@return any, number
+function F()
+ X = 1<!!>
+end
+]]
+
+TEST [[
+---@return number, any
+function F()
+ X = 1<!!>
+end
+]]
+
+TEST [[
+---@return any, any
+function F()
+ X = 1
+end
+]]
+
+TEST [[
+local A
+---@return number
+function F()
+ for _ = 1, 10 do
+ if A then
+ return 1
+ end
+ end
+ error('should not be here')
+end
+]]
+
+TEST [[
+local A
+---@return number
+function F()
+ while true do
+ if A then
+ return 1
+ end
+ end
+end
+]]
+
+TEST [[
+local A
+---@return number
+function F()
+ while A do
+ if A then
+ return 1
+ end
+ end<!!>
+end
+]]
+
+TEST [[
+local A
+---@return number
+function F()
+ while A do
+ if A then
+ return 1
+ else
+ return 2
+ end
+ end
+end
+]]
+
+TEST [[
+---@return number?
+function F()
+
+end
+]]
diff --git a/test/diagnostics/need-check-nil.lua b/test/diagnostics/need-check-nil.lua
new file mode 100644
index 00000000..c4e3bba6
--- /dev/null
+++ b/test/diagnostics/need-check-nil.lua
@@ -0,0 +1,68 @@
+TEST [[
+---@type string?
+local x
+
+local s = <!x!>:upper()
+]]
+
+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!>()
+]]
+
+TEST [[
+---@type integer?
+local x
+
+T = {}
+T[<!x!>] = 1
+]]
+
+TEST [[
+local x, y
+local z = x and y
+
+print(z.y)
+]]
+
+TEST [[
+local x, y
+function x()
+ y()
+end
+
+function y()
+ x()
+end
+
+x()
+]]
diff --git a/test/diagnostics/newfield-call.lua b/test/diagnostics/newfield-call.lua
new file mode 100644
index 00000000..63de9db9
--- /dev/null
+++ b/test/diagnostics/newfield-call.lua
@@ -0,0 +1,15 @@
+TEST [[
+return {
+ <!print
+ 'string'!>
+}
+]]
+
+TEST [[
+return {
+ <!print
+ {
+ x = 1,
+ }!>
+}
+]]
diff --git a/test/diagnostics/newline-call.lua b/test/diagnostics/newline-call.lua
new file mode 100644
index 00000000..ca160aa3
--- /dev/null
+++ b/test/diagnostics/newline-call.lua
@@ -0,0 +1,34 @@
+TEST [[
+<!print()
+('string')!>:sub(1, 1)
+]]
+
+TEST [[
+print()
+('string')
+]]
+
+TEST [[
+print
+{}
+{}
+]]
+
+TEST [[
+local x
+return x
+ : f(1)
+ : f(1)
+]]
+
+TEST [[
+print()
+'string'
+]]
+
+TEST [[
+print
+{
+ x = 1,
+}
+]]
diff --git a/test/diagnostics/not-yieldable.lua b/test/diagnostics/not-yieldable.lua
new file mode 100644
index 00000000..81e972ee
--- /dev/null
+++ b/test/diagnostics/not-yieldable.lua
@@ -0,0 +1,48 @@
+TEST [[
+---@param cb fun()
+local function f(cb)
+ return cb
+end
+
+---@async
+local function af()
+ return nil
+end
+
+f(<!af!>)
+]]
+
+TEST [[
+---@param cb async fun()
+local function f(cb)
+ return cb
+end
+
+---@async
+local function af()
+ return nil
+end
+
+f(af)
+]]
+
+TEST [[
+local function f(cb)
+ cb()
+end
+
+---@async
+local function af()
+ f(function () ---@async
+ return nil
+ end)
+end
+
+return af
+]]
+
+TEST [[
+local _ = type(function () ---@async
+ return nil
+end)
+]]
diff --git a/test/diagnostics/param-type-mismatch.lua b/test/diagnostics/param-type-mismatch.lua
new file mode 100644
index 00000000..e31e9933
--- /dev/null
+++ b/test/diagnostics/param-type-mismatch.lua
@@ -0,0 +1,248 @@
+TEST [[
+---@param x number
+local function f(x) end
+
+f(<!true!>)
+]]
+
+TEST [[
+---@class A
+
+---@param n A
+local function f(n)
+end
+
+---@class B
+local a = {}
+
+---@type A?
+a.x = XX
+
+f(a.x)
+]]
+TEST [[
+---@alias A string|boolean
+
+---@param x string|boolean
+local function f(x) end
+
+---@type A
+local x
+
+f(x)
+]]
+
+TEST [[
+---@alias A string|boolean
+
+---@param x A
+local function f(x) end
+
+---@type string|boolean
+local x
+
+f(x)
+]]
+
+TEST [[
+---@param b boolean
+local function f(b)
+end
+
+---@type boolean
+local t
+
+if t then
+ f(t)
+end
+]]
+
+TEST [[
+---@enum A
+local t = {
+ x = 1,
+ y = 2,
+}
+
+---@param x A
+local function f(x)
+end
+
+f(<!t!>)
+f(t.x)
+f(1)
+f(<!3!>)
+]]
+
+TEST [[
+---@enum A
+local t = {
+ x = { h = 1 },
+ y = { h = 2 },
+}
+
+---@param x A
+local function f(x)
+end
+
+f(t.x)
+f(t.y)
+f(<!{ h = 1 }!>)
+]]
+
+TEST [[
+---@enum(key) A
+local t = {
+ x = 1,
+ ['y'] = 2,
+}
+
+---@param x A
+local function f(x)
+end
+
+f('x')
+f('y')
+f(<!'z'!>)
+]]
+
+TEST [[
+---@generic T: string | boolean | table
+---@param x T
+---@return T
+local function f(x)
+ return x
+end
+
+f(<!1!>)
+]]
+
+TEST [[
+---@param opts {a:number, b:number}
+local function foo(opts)
+
+end
+
+---@param opts {a:number, b:number}
+local function bar(opts)
+ foo(opts)
+end
+]]
+
+TEST [[
+---@param opts {a:number, b:number}
+local function foo(opts)
+
+end
+
+---@param opts {c:number, d:number}
+local function bar(opts)
+ foo(<!opts!>) -- this should raise linting error
+end
+]]
+
+TEST [[
+---@param opts {[number]: boolean}
+local function foo(opts)
+
+end
+
+---@param opts {[1]: boolean}
+local function bar(opts)
+ foo(opts)
+end
+]]
+
+TEST [[
+---@generic T
+---@param v1 T
+---@param v2 T|table
+local function func(v1, v2)
+end
+
+func('hello', 'world')
+]]
+
+TEST [[
+---@generic T1, T2, T3, T4, T5
+---@param f fun(): T1?, T2?, T3?, T4?, T5?
+---@return T1?, T2?, T3?, T4?, T5?
+local function foo(f)
+ return f()
+end
+
+local a, b = foo(function()
+ return 1
+end)
+]]
+
+TEST [[
+---@generic T1, T2, T3, T4, T5
+---@param f fun(): T1|nil, T2|nil, T3|nil, T4|nil, T5|nil
+---@return T1?, T2?, T3?, T4?, T5?
+local function foo(f)
+ return f()
+end
+
+local a, b = foo(function()
+ return 1
+end)
+]]
+
+TEST [[
+---@param v integer
+---@return boolean
+local function is_string(v)
+ return type(v) == 'string'
+end
+
+print(is_string(3))
+]]
+
+TEST [[
+---@param p integer|string
+local function get_val(p)
+ local is_number = type(p) == 'number'
+ return is_number and p or p
+end
+
+get_val('hi')
+]]
+
+TEST [[
+---@class Class
+local Class = {}
+
+---@param source string
+function Class.staticCreator(source)
+
+end
+
+Class.staticCreator(<!true!>)
+Class<!:!>staticCreator() -- Expecting a waring
+]]
+
+TEST [[
+---@class A
+
+---@class B : A
+
+---@class C : B
+
+---@class D : B
+
+---@param x A
+local function func(x) end
+
+---@type C|D
+local var
+func(var)
+]]
+
+TEST [[
+---@class MyClass
+---@overload fun(x : string) : MyClass
+local MyClass = {}
+
+local w = MyClass(<!1!>)
+]]
diff --git a/test/diagnostics/redefined-local.lua b/test/diagnostics/redefined-local.lua
new file mode 100644
index 00000000..c594ed2c
--- /dev/null
+++ b/test/diagnostics/redefined-local.lua
@@ -0,0 +1,22 @@
+TEST [[
+local x
+print(x)
+local <!x!>
+print(x)
+]]
+
+TEST [[
+local x
+print(x)
+local <!x!>
+print(x)
+local <!x!>
+print(x)
+]]
+
+TEST [[
+local x
+return x, function (<!x!>)
+ return x
+end
+]]
diff --git a/test/diagnostics/redundant-parameter.lua b/test/diagnostics/redundant-parameter.lua
new file mode 100644
index 00000000..fabe3340
--- /dev/null
+++ b/test/diagnostics/redundant-parameter.lua
@@ -0,0 +1,214 @@
+TEST [[
+local function x(a, b)
+ return a, b
+end
+x(1, 2, <!3!>)
+]]
+
+TEST [[
+local function x(a, b, ...)
+ return a, b, ...
+end
+x(1, 2, 3, 4, 5)
+]]
+
+TEST [[
+---@type fun(a, b, ...)
+local x
+x(1, 2, 3, 4, 5)
+]]
+
+TEST [[
+local m = {}
+function m:x(a, b)
+ return a, b
+end
+m:x(1, 2, <!3!>)
+]]
+
+TEST [[
+local m = {}
+function m:x(a, b)
+ return a, b
+end
+m.x(m, 2, 3, <!4!>)
+]]
+
+TEST [[
+local m = {}
+function m.x(a, b)
+ return a, b
+end
+m:x(1, <!2!>, <!3!>, <!4!>)
+]]
+
+TEST [[
+local m = {}
+function m.x()
+end
+m:x()
+]]
+
+TEST [[
+local function f(a, b)
+ return a, b
+end
+f(1, 2, <!3!>, <!4!>)
+]]
+
+TEST [[
+local mt = {}
+function mt:f(a, b)
+ return a, b
+end
+mt.f(mt, 2, 3, <!4!>)
+]]
+
+TEST [[
+local mt = {}
+function mt.f(a, b)
+ return a, b
+end
+mt:f(1, <!2!>, <!3!>, <!4!>)
+]]
+
+TEST [[
+local mt = {}
+function mt:f(a, b)
+ return a, b
+end
+mt:f(1, 2, <!3!>, <!4!>)
+]]
+
+TEST [[
+local function f(a, b, ...)
+ return a, b, ...
+end
+f(1, 2, 3, 4)
+]]
+
+TEST [[
+local _ = next({}, 1, <!2!>)
+print(1, 2, 3, 4, 5)
+]]
+
+TEST [[
+local function f(callback)
+ callback(1, 2, 3)
+end
+f(function () end)
+]]
+
+--TEST [[
+--local realTostring = tostring
+--tostring = function () end
+--tostring(<!1!>)
+--tostring = realTostring
+--tostring(1)
+--]]
+
+TEST [[
+local f = load('')
+if f then
+ f(1, 2, 3)
+end
+]]
+
+TEST [[
+local m = {}
+function m.open()
+end
+
+m:open()
+]]
+
+TEST [[
+local m = {}
+function m:open()
+end
+
+m.open(m)
+]]
+
+TEST [[
+table.insert({}, 1, 2, <!3!>)
+]]
+
+TEST [[
+---@overload fun(...)
+local function f() end
+
+f(1)
+]]
+
+TEST [[
+function F() end
+
+---@param x boolean
+function F(x) end
+
+F(k())
+]]
+
+TEST [[
+local function f()
+ return 1, 2, 3
+end
+
+local function k()
+end
+
+k(<!f()!>)
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+local function f()
+ return 1, 2, 3
+end
+
+---@param x integer
+local function k(x)
+end
+
+k(f())
+]]
+
+TEST [[
+---@meta
+
+---@param x fun()
+local function f1(x)
+end
+
+---@return fun()
+local function f2()
+end
+
+f1(f2())
+]]
+
+TEST [[
+---@meta
+
+---@type fun():integer
+local f
+
+---@param x integer
+local function foo(x) end
+
+foo(f())
+]]
+
+TEST [[
+---@meta
+---@diagnostic disable: duplicate-set-field
+---@class A
+local m = {}
+
+function m.ff() end
+
+function m.ff(x) end
+
+m.ff(1)
+]]
diff --git a/test/diagnostics/redundant-return-value.lua b/test/diagnostics/redundant-return-value.lua
new file mode 100644
index 00000000..f3e2c584
--- /dev/null
+++ b/test/diagnostics/redundant-return-value.lua
@@ -0,0 +1,32 @@
+TEST [[
+---@type fun():number
+local function f()
+ return 1, <!true!>
+end
+]]
+
+TEST [[
+---@return number, number?
+function F()
+ return 1, 1, <!1!>
+end
+]]
+
+TEST [[
+---@return number, number?
+function F()
+ return 1, 1, <!1!>, <!2!>, <!3!>
+end
+]]
+
+TEST [[
+---@meta
+
+---@return number, number
+local function r2() end
+
+---@return number, number?
+function F()
+ return 1, <!r2()!>
+end
+]]
diff --git a/test/diagnostics/redundant-return.lua b/test/diagnostics/redundant-return.lua
new file mode 100644
index 00000000..11214aab
--- /dev/null
+++ b/test/diagnostics/redundant-return.lua
@@ -0,0 +1,34 @@
+TEST [[
+local function f()
+ <!return!>
+end
+f()
+]]
+
+TEST [[
+local function f()
+ return nil
+end
+f()
+]]
+
+TEST [[
+local function f()
+ local function x()
+ <!return!>
+ end
+ x()
+ return true
+end
+f()
+]]
+
+TEST [[
+local function f()
+ local function x()
+ return true
+ end
+ return x()
+end
+f()
+]]
diff --git a/test/diagnostics/redundant-value.lua b/test/diagnostics/redundant-value.lua
new file mode 100644
index 00000000..a63544df
--- /dev/null
+++ b/test/diagnostics/redundant-value.lua
@@ -0,0 +1,7 @@
+TEST [[
+local _ = 1, <!2!>
+]]
+
+TEST [[
+_ = 1, <!2!>
+]]
diff --git a/test/diagnostics/return-type-mismatch.lua b/test/diagnostics/return-type-mismatch.lua
new file mode 100644
index 00000000..aaf7807f
--- /dev/null
+++ b/test/diagnostics/return-type-mismatch.lua
@@ -0,0 +1,167 @@
+TEST [[
+---@return number
+function F()
+ return <!true!>
+end
+]]
+
+TEST [[
+---@return number?
+function F()
+ return 1
+end
+]]
+
+TEST [[
+---@return number?
+function F()
+ return nil
+end
+]]
+
+TEST [[
+---@return number, number
+local function f()
+ return 1, 1
+end
+
+---@return number, boolean
+function F()
+ return <!f()!>
+end
+]]
+
+TEST [[
+---@return boolean, number
+local function f()
+ return true, 1
+end
+
+---@return number, boolean
+function F()
+ return <!f()!>
+end
+]]
+
+TEST [[
+---@return boolean, number?
+local function f()
+ return true, 1
+end
+
+---@return number, boolean
+function F()
+ return 1, f()
+end
+]]
+
+TEST [[
+---@return number, number?
+local function f()
+ return 1, 1
+end
+
+---@return number, boolean, number
+function F()
+ return 1, <!f()!>
+end
+]]
+
+TEST [[
+---@class A
+---@field x number?
+
+---@return number
+function F()
+ ---@type A
+ local t
+ return t.x
+end
+]]
+
+TEST [[
+---@class A
+---@field x number?
+local t = {}
+
+---@return number
+function F()
+ return t.x
+end
+]]
+
+TEST [[
+---@param ... number
+local function f(...)
+end
+
+f(nil)
+]]
+
+TEST [[
+---@return number
+function F()
+ local n = 0
+ if true then
+ n = 1
+ end
+ return n
+end
+]]
+
+TEST [[
+---@param x boolean
+---@return number
+---@overload fun(): boolean
+local function f(x)
+ if x then
+ return 1
+ else
+ return false
+ end
+end
+]]
+
+TEST [[
+---@param x boolean
+---@return number
+---@overload fun()
+local function f(x)
+ if x then
+ return 1
+ else
+ return
+ end
+end
+]]
+
+TEST [[
+---@param x boolean
+---@return number
+---@overload fun()
+local function f(x)
+ if x then
+ return 1
+ end
+end
+]]
+
+TEST [[
+---@param x boolean
+---@return number
+---@overload fun(): boolean, boolean
+local function f(x)
+ if x then
+ return 1
+ else
+ return false, false
+ end
+end
+]]
+
+TEST [[
+---@type fun():number
+local function f()
+ return <!true!>
+end
+]]
diff --git a/test/diagnostics/trailing-space.lua b/test/diagnostics/trailing-space.lua
new file mode 100644
index 00000000..ff794714
--- /dev/null
+++ b/test/diagnostics/trailing-space.lua
@@ -0,0 +1,32 @@
+TEST [[
+<! !>
+]]
+
+TEST [[
+
+<! !>
+]]
+
+TEST [[
+X = 1<! !>
+]]
+
+TEST [[
+X = [=[
+ ]=]
+]]
+
+TEST [[
+-- xxxx
+]]
+
+TEST [[
+-- [=[
+ ]=]
+]]
+
+TEST [=[
+return [[
+
+]]
+]=]
diff --git a/test/diagnostics/type-check.lua b/test/diagnostics/type-check.lua
deleted file mode 100644
index 18e7190d..00000000
--- a/test/diagnostics/type-check.lua
+++ /dev/null
@@ -1,1262 +0,0 @@
-local config = require 'config'
-
-config.add(nil, 'Lua.diagnostics.disable', 'unused-local')
-config.add(nil, 'Lua.diagnostics.disable', 'unused-function')
-config.add(nil, 'Lua.diagnostics.disable', 'undefined-global')
-config.add(nil, 'Lua.diagnostics.disable', 'redundant-return')
-config.set(nil, 'Lua.type.castNumberToInteger', false)
-
-TEST [[
-local x = 0
-
-<!x!> = true
-]]
-
-TEST [[
----@type integer
-local x
-
-<!x!> = true
-]]
-
-TEST [[
----@type unknown
-local x
-
-x = nil
-]]
-
-TEST [[
----@type unknown
-local x
-
-x = 1
-]]
-
-TEST [[
----@type unknown|nil
-local x
-
-x = 1
-]]
-
-TEST [[
-local x = {}
-
-x = nil
-]]
-
-TEST [[
----@type string
-local x
-
-<?x?> = nil
-]]
-
-TEST [[
----@type string?
-local x
-
-x = nil
-]]
-
-TEST [[
----@type table
-local x
-
-<!x!> = nil
-]]
-
-TEST [[
-local x
-
-x = nil
-]]
-
-TEST [[
----@type integer
-local x
-
----@type number
-<!x!> = f()
-]]
-
-TEST [[
----@type number
-local x
-
----@type integer
-x = f()
-]]
-
-TEST [[
----@type number|boolean
-local x
-
----@type string
-<!x!> = f()
-]]
-
-TEST [[
----@type number|boolean
-local x
-
----@type boolean
-x = f()
-]]
-
-TEST [[
----@type number|boolean
-local x
-
----@type boolean|string
-<!x!> = f()
-]]
-
-TEST [[
----@type boolean
-local x
-
-if not x then
- return
-end
-
-x = f()
-]]
-
-TEST [[
----@type boolean
-local x
-
----@type integer
-local y
-
-<!x!> = y
-]]
-
-TEST [[
-local y = true
-
-local x
-x = 1
-x = y
-]]
-
-TEST [[
-local t = {}
-
-local x = 0
-x = x + #t
-]]
-
-TEST [[
-local x = 0
-
-x = 1.0
-]]
-
-TEST [[
----@class A
-
-local t = {}
-
----@type A
-local a
-
-t = a
-]]
-
-TEST [[
-local m = {}
-
----@type integer[]
-m.ints = {}
-]]
-
-TEST [[
----@class A
----@field x A
-
----@type A
-local t
-
-t.x = {}
-]]
-
-TEST [[
----@class A
----@field x integer
-
----@type A
-local t
-
-<!t.x!> = true
-]]
-
-TEST [[
----@class A
----@field x integer
-
----@type A
-local t
-
----@type boolean
-local y
-
-<!t.x!> = y
-]]
-
-TEST [[
----@class A
-local m
-
-m.x = 1
-
----@type A
-local t
-
-<!t.x!> = true
-]]
-
-TEST [[
----@class A
-local m
-
----@type integer
-m.x = 1
-
-<!m.x!> = true
-]]
-
-TEST [[
----@class A
-local mt
-
----@type integer
-mt.x = 1
-
-function mt:init()
- <!self.x!> = true
-end
-]]
-
-TEST [[
----@class A
----@field x integer
-
----@type A
-local t = {
- <!x!> = true
-}
-]]
-
-TEST [[
----@type boolean[]
-local t = {}
-
-t[5] = nil
-]]
-
-TEST [[
----@type table<string, true>
-local t = {}
-
-t['x'] = nil
-]]
-
-TEST [[
-local t = { true }
-
-t[1] = nil
-]]
-
-TEST [[
----@class A
-local t = {
- x = 1
-}
-
-<!t.x!> = true
-]]
-
-TEST [[
----@type number
-local t
-
-t = 1
-]]
-
-TEST [[
----@type number
-local t
-
----@type integer
-local y
-
-t = y
-]]
-
-TEST [[
----@class A
-local m
-
----@type number
-m.x = 1
-
-<!m.x!> = {}
-]]
-
-TEST [[
----@param x number
-local function f(x) end
-
-f(<!true!>)
-]]
-
-TEST [[
----@type integer
-local x
-
-x = 1.0
-]]
-
-TEST [[
----@type integer
-local x
-
-<!x!> = 1.5
-]]
-
-TEST [[
----@diagnostic disable:undefined-global
----@type integer
-local x
-
-x = 1 + G
-]]
-
-TEST [[
----@diagnostic disable:undefined-global
----@type integer
-local x
-
-x = 1 + G
-]]
-
-TEST [[
----@alias A integer
-
----@type A
-local a
-
----@type integer
-local b
-
-b = a
-]]
-
-TEST [[
----@type string|boolean
-local t
-
----@cast t string
-]]
-
-TEST [[
----@type string|boolean
-local t
-
----@cast t <!number!>
-]]
-
-TEST [[
-local n
-
-if G then
- n = {}
-else
- n = nil
-end
-
-local t = {
- x = n,
-}
-]]
-
-TEST [[
----@class A
-
----@param n A
-local function f(n)
-end
-
----@class B
-local a = {}
-
----@type A?
-a.x = XX
-
-f(a.x)
-]]
-
-TEST [[
----@type string?
-local x
-
-local s = <!x!>:upper()
-]]
-
-TEST [[
----@alias A string|boolean
-
----@param x string|boolean
-local function f(x) end
-
----@type A
-local x
-
-f(x)
-]]
-
-TEST [[
----@alias A string|boolean
-
----@param x A
-local function f(x) end
-
----@type string|boolean
-local x
-
-f(x)
-]]
-
-TEST [[
----@type boolean[]
-local t = {}
-
----@type boolean?
-local x
-
-t[#t+1] = x
-]]
-
-TEST [[
----@type number
-local n
----@type integer
-local i
-
-<?i?> = n
-]]
-
-config.set(nil, 'Lua.type.castNumberToInteger', true)
-TEST [[
----@type number
-local n
----@type integer
-local i
-
-i = n
-]]
-config.set(nil, 'Lua.type.castNumberToInteger', false)
-
-TEST [[
----@type number|boolean
-local nb
-
----@type number
-local n
-
-<?n?> = nb
-]]
-
-config.set(nil, 'Lua.type.weakUnionCheck', true)
-TEST [[
----@type number|boolean
-local nb
-
----@type number
-local n
-
-n = nb
-]]
-config.set(nil, 'Lua.type.weakUnionCheck', false)
-
-TEST [[
----@class Option: string
-
----@param x Option
-local function f(x) end
-
----@type Option
-local x = 'aaa'
-
-f(x)
-]]
-
-TEST [[
----@type number
-local <!x!> = 'aaa'
-]]
-
-TEST [[
----@return number
-function F()
- return <!true!>
-end
-]]
-
-TEST [[
----@return number?
-function F()
- return 1
-end
-]]
-
-TEST [[
----@return number?
-function F()
- return nil
-end
-]]
-
-TEST [[
----@return number, number
-local function f()
- return 1, 1
-end
-
----@return number, boolean
-function F()
- return <!f()!>
-end
-]]
-
-TEST [[
----@return boolean, number
-local function f()
- return true, 1
-end
-
----@return number, boolean
-function F()
- return <!f()!>
-end
-]]
-
-TEST [[
----@return boolean, number?
-local function f()
- return true, 1
-end
-
----@return number, boolean
-function F()
- return 1, f()
-end
-]]
-
-TEST [[
----@return number, number?
-local function f()
- return 1, 1
-end
-
----@return number, boolean, number
-function F()
- return 1, <!f()!>
-end
-]]
-
-TEST [[
----@class A
----@field x number?
-
----@return number
-function F()
- ---@type A
- local t
- return t.x
-end
-]]
-
-TEST [[
----@class A
----@field x number?
-local t = {}
-
----@return number
-function F()
- return t.x
-end
-]]
-
-TEST [[
----@param ... number
-local function f(...)
-end
-
-f(nil)
-]]
-
-TEST [[
----@return number
-function F()
- local n = 0
- if true then
- n = 1
- end
- return n
-end
-]]
-
-TEST [[
----@class X
-
----@class A
-local mt = G
-
----@type X
-mt._x = nil
-]]
-
-config.set(nil, 'Lua.type.weakNilCheck', true)
-TEST [[
----@type number?
-local nb
-
----@type number
-local n
-
-n = nb
-]]
-
-TEST [[
----@type number|nil
-local nb
-
----@type number
-local n
-
-n = nb
-]]
-config.set(nil, 'Lua.type.weakNilCheck', false)
-
-TEST [[
----@class A
-local a = {}
-
----@class B
-local <!b!> = a
-]]
-
-TEST [[
----@class A
-local a = {}
-
----@class B: A
-local b = a
-]]
-
-TEST [[
----@class A
-local a = {}
-a.__index = a
-
----@class B: A
-local b = setmetatable({}, a)
-]]
-
-TEST [[
----@class A
-local a = {}
-
----@class B: A
-local b = setmetatable({}, {__index = a})
-]]
-
-TEST [[
----@class A
-local a = {}
-
----@class B
-local <!b!> = setmetatable({}, {__index = a})
-]]
-
-TEST [[
----@class A
----@field x number?
-local a
-
----@class B
----@field x number
-local b
-
-b.x = a.x
-]]
-
-TEST [[
-
----@class A
----@field x number?
-local a
-
----@type number
-local t
-
-t = a.x
-]]
-
-TEST [[
-local mt = {}
-mt.x = 1
-mt.x = nil
-]]
-
-TEST [[
----@type string[]
-local t
-
-<!t!> = 'xxx'
-]]
-
-TEST [[
----@param b boolean
-local function f(b)
-end
-
----@type boolean
-local t
-
-if t then
- f(t)
-end
-]]
-
-config.set(nil, 'Lua.type.weakUnionCheck', true)
-TEST [[
----@type number
-local x = G
-]]
-
-TEST [[
----@generic T
----@param x T
----@return T
-local function f(x)
- return x
-end
-]]
-config.set(nil, 'Lua.type.weakUnionCheck', false)
-
-TEST [[
----@type 1|2
-local x
-
-x = 1
-x = 2
-<!x!> = 3
-]]
-
-TEST [[
----@type 'x'|'y'
-local x
-
-x = 'x'
-x = 'y'
-<!x!> = 'z'
-]]
-
-TEST [[
----@enum A
-local t = {
- x = 1,
- y = 2,
-}
-
----@param x A
-local function f(x)
-end
-
-f(<!t!>)
-f(t.x)
-f(1)
-f(<!3!>)
-]]
-
-TEST [[
----@enum A
-local t = {
- x = { h = 1 },
- y = { h = 2 },
-}
-
----@param x A
-local function f(x)
-end
-
-f(t.x)
-f(t.y)
-f(<!{ h = 1 }!>)
-]]
-
-TEST [[
-local t = {
- x = 1,
-}
-
-local x
-t[x] = true
-]]
-
-TEST [[
----@param x boolean
----@return number
----@overload fun(): boolean
-local function f(x)
- if x then
- return 1
- else
- return false
- end
-end
-]]
-
-TEST [[
----@param x boolean
----@return number
----@overload fun()
-local function f(x)
- if x then
- return 1
- else
- return
- end
-end
-]]
-
-TEST [[
----@param x boolean
----@return number
----@overload fun()
-local function f(x)
- if x then
- return 1
- end
-end
-]]
-
-TEST [[
----@param x boolean
----@return number
----@overload fun(): boolean, boolean
-local function f(x)
- if x then
- return 1
- else
- return false, false
- end
-end
-]]
-
-TEST [[
----@alias test boolean
-
----@type test
-local <!test!> = 4
-]]
-
-TEST [[
----@class MyClass
-local MyClass = {}
-
-function MyClass:new()
- ---@class MyClass
- local myObject = setmetatable({
- initialField = true
- }, self)
-
- print(myObject.initialField)
-end
-]]
-
-TEST [[
----@class T
-local t = {
- x = nil
-}
-
-t.x = 1
-]]
-
-TEST [[
----@generic T: string | boolean | table
----@param x T
----@return T
-local function f(x)
- return x
-end
-
-f(<!1!>)
-]]
-
-TEST [[
----@type table<string, string>
-local x
-
----@type table<number, string>
-local y
-
-<!x!> = y
-]]
-
-TEST [[
----@type table<string, string>
-local x
-
----@type table<string, number>
-local y
-
-<!x!> = y
-]]
-
-TEST [[
----@type table<string, string>
-local x
-
----@type table<string, string>
-local y
-
-x = y
-]]
-
-TEST [[
----@param opts {a:number, b:number}
-local function foo(opts)
-
-end
-
----@param opts {a:number, b:number}
-local function bar(opts)
- foo(opts)
-end
-]]
-
-TEST [[
----@param opts {a:number, b:number}
-local function foo(opts)
-
-end
-
----@param opts {c:number, d:number}
-local function bar(opts)
- foo(<!opts!>) -- this should raise linting error
-end
-]]
-
-TEST [[
----@param opts {[number]: boolean}
-local function foo(opts)
-
-end
-
----@param opts {[1]: boolean}
-local function bar(opts)
- foo(opts)
-end
-]]
-
-TEST [[
----@type {[1]: string, [10]: number, xx: boolean}
-local t = {
- <!true!>,
- <![10]!> = 's',
- <!xx!> = 1,
-}
-]]
-
-TEST [[
----@type { x: number, y: number }
-local t1
-
----@type { x: number }
-local t2
-
-<!t1!> = t2
-]]
-
-TEST [[
----@type { x: number, [integer]: number }
-local t1
-
----@type { x: number }
-local t2
-
-<!t1!> = t2
-]]
-
-TEST [[
----@type boolean[]
-local t = { <!1!>, <!2!>, <!3!> }
-]]
-
-TEST [[
----@type boolean[]
-local t = { true, false, nil }
-]]
-
-TEST [[
----@type boolean|nil
-local x
-
----@type boolean[]
-local t = { true, false, x }
-]]
-
-TEST [[
----@type fun():number
-local function f()
-<!!>end
-]]
-
-TEST [[
----@type fun():number
-local function f()
- <!return!>
-end
-]]
-
-TEST [[
----@type fun():number?
-local function f()
-end
-]]
-
-TEST [[
----@type fun():...
-local function f()
-end
-]]
-
-TEST [[
----@type fun():number
-local function f()
- return 1, <!true!>
-end
-]]
-
-TEST [[
----@type fun():number
-local function f()
- return <!true!>
-end
-]]
-
-TEST [[
----@enum Enum
-local t = {
- x = 1,
- y = 2,
-}
-
----@type Enum
-local y
-
----@type integer
-local x = y
-]]
-
-TEST [[
----@generic T
----@param v1 T
----@param v2 T|table
-local function func(v1, v2)
-end
-
-func('hello', 'world')
-]]
-
-TEST [[
----@generic T1, T2, T3, T4, T5
----@param f fun(): T1?, T2?, T3?, T4?, T5?
----@return T1?, T2?, T3?, T4?, T5?
-local function foo(f)
- return f()
-end
-
-local a, b = foo(function()
- return 1
-end)
-]]
-
-TEST [[
----@generic T1, T2, T3, T4, T5
----@param f fun(): T1|nil, T2|nil, T3|nil, T4|nil, T5|nil
----@return T1?, T2?, T3?, T4?, T5?
-local function foo(f)
- return f()
-end
-
-local a, b = foo(function()
- return 1
-end)
-]]
-
-TEST [[
----@type string|string[]|string[][]
-local t = {{'a'}}
-]]
-
-TEST [[
-local A = "Hello"
-local B = "World"
-
----@alias myLiteralAliases `A` | `B`
-
----@type myLiteralAliases
-local x = A
-]]
-
-TEST [[
-local enum = { a = 1, b = 2 }
-
----@type { [integer] : boolean }
-local t = {
- <![enum.a]!> = 1,
- <![enum.b]!> = 2,
- <![3]!> = 3,
-}
-]]
-
-TEST [[
-local x
-
-if X then
- x = 'A'
-elseif X then
- x = 'B'
-else
- x = 'C'
-end
-
-local y = x
-
-<!y!> = nil
-]]
-(function (diags)
- local diag = diags[1]
- assert(diag.message == [[
-已显式定义变量的类型为 `string` ,不能再将其类型转换为 `nil`。
-- `nil` 无法匹配 `string`
-- 类型 `nil` 无法匹配 `string`]])
-end)
-
-
-TEST [[
----@type 'A'|'B'|'C'|'D'|'E'|'F'|'G'|'H'|'I'|'J'|'K'|'L'|'M'|'N'|'O'|'P'|'Q'|'R'|'S'|'T'|'U'|'V'|'W'|'X'|'Y'|'Z'
-local x
-
-<!x!> = nil
-]]
-(function (diags)
- local diag = diags[1]
- assert(diag.message == [[
-已显式定义变量的类型为 `'A'|'B'|'C'|'D'|'E'...(+21)` ,不能再将其类型转换为 `nil`。
-- `nil` 无法匹配 `'A'|'B'|'C'|'D'|'E'...(+21)`
-- `nil` 无法匹配 `'A'|'B'|'C'|'D'|'E'...(+21)` 中的任何子类
-- 类型 `nil` 无法匹配 `'Z'`
-- 类型 `nil` 无法匹配 `'Y'`
-- 类型 `nil` 无法匹配 `'X'`
-- 类型 `nil` 无法匹配 `'W'`
-- 类型 `nil` 无法匹配 `'V'`
-- 类型 `nil` 无法匹配 `'U'`
-- 类型 `nil` 无法匹配 `'T'`
-- 类型 `nil` 无法匹配 `'S'`
-- 类型 `nil` 无法匹配 `'R'`
-- 类型 `nil` 无法匹配 `'Q'`
-...(+13)
-- 类型 `nil` 无法匹配 `'C'`
-- 类型 `nil` 无法匹配 `'B'`
-- 类型 `nil` 无法匹配 `'A'`]])
-end)
-
-TEST [[
----@param v integer
----@return boolean
-local function is_string(v)
- return type(v) == 'string'
-end
-
-print(is_string(3))
-]]
-
-TEST [[
----@class SomeClass
----@field [1] string
--- ...
-
----@param some_param SomeClass|SomeClass[]
-local function some_fn(some_param) return end
-
-some_fn { { "test" } } -- <- diagnostic: "Cannot assign `table` to `string`."
-]]
-
-TEST [[
----@param p integer|string
-local function get_val(p)
- local is_number = type(p) == 'number'
- return is_number and p or p
-end
-
-get_val('hi')
-]]
-
-TESTWITH 'param-type-mismatch' [[
----@class Class
-local Class = {}
-
----@param source string
-function Class.staticCreator(source)
-
-end
-
-Class.staticCreator(<!true!>)
-Class<!:!>staticCreator() -- Expecting a waring
-]]
-
-TESTWITH 'assign-type-mismatch' [[
----@type string[]
-local arr = {
- <!3!>,
-}
-]]
-
-TESTWITH 'assign-type-mismatch' [[
----@type (string|boolean)[]
-local arr2 = {
- <!3!>, -- no warnings
-}
-]]
-
-TEST [[
----@class A
-
----@class B : A
-
----@class C : B
-
----@class D : B
-
----@param x A
-local function func(x) end
-
----@type C|D
-local var
-func(var)
-]]
-
-config.remove(nil, 'Lua.diagnostics.disable', 'unused-local')
-config.remove(nil, 'Lua.diagnostics.disable', 'unused-function')
-config.remove(nil, 'Lua.diagnostics.disable', 'undefined-global')
-config.remove(nil, 'Lua.diagnostics.disable', 'redundant-return')
-config.set(nil, 'Lua.type.castNumberToInteger', true)
diff --git a/test/diagnostics/unbalanced-assignments.lua b/test/diagnostics/unbalanced-assignments.lua
new file mode 100644
index 00000000..a0d47f56
--- /dev/null
+++ b/test/diagnostics/unbalanced-assignments.lua
@@ -0,0 +1,34 @@
+TEST [[
+local x, <!y!>, <!z!> = 1
+]]
+
+TEST [[
+local x, y, <!z!> = 1, 2
+]]
+
+TEST [[
+local x, y, z = print()
+]]
+
+TEST [[
+local x, y, z
+]]
+
+TEST [[
+local x, y, z
+x, <!y!>, <!z!> = 1
+]]
+
+TEST [[
+X, <!Y!>, <!Z!> = 1
+]]
+
+TEST [[
+T = {}
+T.x, <!T.y!>, <!T.z!> = 1
+]]
+
+TEST [[
+T = {}
+T['x'], <!T['y']!>, <!T['z']!> = 1
+]]
diff --git a/test/diagnostics/undefined-doc-class.lua b/test/diagnostics/undefined-doc-class.lua
new file mode 100644
index 00000000..d10b89d8
--- /dev/null
+++ b/test/diagnostics/undefined-doc-class.lua
@@ -0,0 +1,3 @@
+TEST [[
+---@class A : <!B!>
+]]
diff --git a/test/diagnostics/undefined-doc-name.lua b/test/diagnostics/undefined-doc-name.lua
new file mode 100644
index 00000000..9a55108a
--- /dev/null
+++ b/test/diagnostics/undefined-doc-name.lua
@@ -0,0 +1,19 @@
+TEST [[
+---@type <!A!>
+]]
+
+TEST [[
+---@class A
+---@type A|<!B!>|<!C!>
+]]
+
+TEST [[
+---@class AAA
+---@alias B AAA
+
+---@type B
+]]
+
+TEST [[
+---@alias B <!AAA!>
+]]
diff --git a/test/diagnostics/undefined-doc-param.lua b/test/diagnostics/undefined-doc-param.lua
new file mode 100644
index 00000000..7bfbafa5
--- /dev/null
+++ b/test/diagnostics/undefined-doc-param.lua
@@ -0,0 +1,21 @@
+TEST [[
+---@param <!x!> Class
+]]
+
+TEST [[
+---@class Class
+---@param <!y!> Class
+local function f(x)
+ return x
+end
+f()
+]]
+
+TEST [[
+---@class Class
+---@param <!y!> Class
+function F(x)
+ return x
+end
+F()
+]]
diff --git a/test/diagnostics/undefined-env-child.lua b/test/diagnostics/undefined-env-child.lua
new file mode 100644
index 00000000..73904a74
--- /dev/null
+++ b/test/diagnostics/undefined-env-child.lua
@@ -0,0 +1,36 @@
+TEST [[
+---@type iolib
+_ENV = io
+<!print!>(stderr) -- `print` is warning but `stderr` is not
+]]
+
+TEST [[
+---@type iolib
+local _ENV = io
+<!print!>(stderr) -- `print` is warning but `stderr` is not
+]]
+
+TEST [[
+local _ENV = { print = print }
+print(1)
+]]
+
+TEST [[
+_ENV = {}
+GLOBAL = 1 --> _ENV.GLOBAL = 1
+]]
+
+TEST [[
+_ENV = {}
+local _ = print --> local _ = _ENV.print
+]]
+
+TEST [[
+GLOBAL = 1
+_ENV = nil
+]]
+
+TEST [[
+print(1)
+_ENV = nil
+]]
diff --git a/test/diagnostics/undefined-field.lua b/test/diagnostics/undefined-field.lua
new file mode 100644
index 00000000..aff329fb
--- /dev/null
+++ b/test/diagnostics/undefined-field.lua
@@ -0,0 +1,148 @@
+TEST [[
+---@class Foo
+---@field field1 integer
+local mt = {}
+function mt:Constructor()
+ self.field2 = 1
+end
+function mt:method1() return 1 end
+function mt.method2() return 2 end
+
+---@class Bar: Foo
+---@field field4 integer
+local mt2 = {}
+
+---@type Foo
+local v
+print(v.field1 + 1)
+print(v.field2 + 1)
+print(v.<!field3!> + 1)
+print(v:method1())
+print(v.method2())
+print(v:<!method3!>())
+
+---@type Bar
+local v2
+print(v2.field1 + 1)
+print(v2.field2 + 1)
+print(v2.<!field3!> + 1)
+print(v2.field4 + 1)
+print(v2:method1())
+print(v2.method2())
+print(v2:<!method3!>())
+
+local v3 = {}
+print(v3.abc)
+
+---@class Bar2
+local mt3
+function mt3:method() return 1 end
+print(mt3:method())
+]]
+
+-- checkUndefinedField 通过type找到class
+TEST [[
+---@class Foo
+local Foo
+function Foo:method1() end
+
+---@type Foo
+local v
+v:method1()
+v:<!method2!>() -- doc.class.name
+]]
+
+-- checkUndefinedField 通过type找到class,涉及到 class 继承版
+TEST [[
+---@class Foo
+local Foo
+function Foo:method1() end
+---@class Bar: Foo
+local Bar
+function Bar:method3() end
+
+---@type Bar
+local v
+v:method1()
+v:<!method2!>() -- doc.class.name
+v:method3()
+]]
+
+-- checkUndefinedField 类名和类变量同名,类变量被直接使用
+TEST [[
+---@class Foo
+local Foo
+function Foo:method1() end
+Foo:<!method2!>() -- doc.class
+Foo:<!method2!>() -- doc.class
+]]
+
+-- checkUndefinedField 没有@class的不检测
+TEST [[
+local Foo
+function Foo:method1()
+ return Foo:method2() -- table
+end
+]]
+
+-- checkUndefinedField 类名和类变量不同名,类变量被直接使用、使用self
+TEST [[
+---@class Foo
+local mt
+function mt:method1()
+ mt.<!method2!>() -- doc.class
+ self:method1()
+ return self.<!method2!>() -- doc.class.name
+end
+]]
+
+-- checkUndefinedField 当会推导成多个class类型时
+TEST [[
+---@class Foo
+local mt
+function mt:method1() end
+
+---@class Bar
+local mt2
+function mt2:method2() end
+
+---@type Foo
+local v
+---@type Bar
+local v2
+v2 = v
+v2:method1()
+v2:<!method2!>()
+]]
+
+TEST [[
+---@type table
+T1 = {}
+print(T1.f1)
+---@type tablelib
+T2 = {}
+print(T2.<!f2!>)
+]]
+
+TEST [[
+---@type string|table
+local n
+
+print(n.x)
+]]
+
+TEST [[
+---@type 'x'
+local t
+
+local n = t:upper()
+]]
+
+TEST [[
+---@alias A 'x'
+
+---@type A
+local t
+
+local n = t:upper()
+]]
diff --git a/test/diagnostics/undefined-global.lua b/test/diagnostics/undefined-global.lua
new file mode 100644
index 00000000..f5a6396c
--- /dev/null
+++ b/test/diagnostics/undefined-global.lua
@@ -0,0 +1,36 @@
+TEST [[
+local print, _G
+print(<!x!>)
+print(<!log!>)
+print(<!X!>)
+print(<!Log!>)
+print(<!y!>)
+print(Z)
+print(_G)
+Z = 1
+]]
+
+TEST [[
+X = table[<!x!>]
+]]
+TEST [[
+T1 = 1
+_ENV.T2 = 1
+_G.T3 = 1
+_ENV._G.T4 = 1
+_G._G._G.T5 = 1
+rawset(_G, 'T6', 1)
+rawset(_ENV, 'T7', 1)
+print(T1)
+print(T2)
+print(T3)
+print(T4)
+print(T5)
+print(T6)
+print(T7)
+]]
+
+TEST [[
+---@class c
+c = {}
+]]
diff --git a/test/diagnostics/unknown-cast-variable.lua b/test/diagnostics/unknown-cast-variable.lua
new file mode 100644
index 00000000..a347083f
--- /dev/null
+++ b/test/diagnostics/unknown-cast-variable.lua
@@ -0,0 +1,8 @@
+TEST [[
+---@cast <!x!> integer
+]]
+
+TEST [[
+local x, y
+---@cast y number
+]]
diff --git a/test/diagnostics/unknown-diag-code.lua b/test/diagnostics/unknown-diag-code.lua
new file mode 100644
index 00000000..33b41886
--- /dev/null
+++ b/test/diagnostics/unknown-diag-code.lua
@@ -0,0 +1,3 @@
+TEST [[
+---@diagnostic disable-next-line: <!xxx!>
+]]
diff --git a/test/diagnostics/unknown-operator.lua b/test/diagnostics/unknown-operator.lua
new file mode 100644
index 00000000..bb193f6a
--- /dev/null
+++ b/test/diagnostics/unknown-operator.lua
@@ -0,0 +1,4 @@
+TEST [[
+---@class A
+---@operator <!xxx!>: A
+]]
diff --git a/test/diagnostics/unreachable-code.lua b/test/diagnostics/unreachable-code.lua
new file mode 100644
index 00000000..4444252f
--- /dev/null
+++ b/test/diagnostics/unreachable-code.lua
@@ -0,0 +1,71 @@
+TEST [[
+if X then
+ return false
+elseif X then
+ return false
+else
+ return false
+end
+<!return true!>
+]]
+
+TEST [[
+function X()
+ if X then
+ return false
+ elseif X then
+ return false
+ else
+ return false
+ end
+ <!return true!>
+end
+]]
+
+TEST [[
+while true do
+end
+
+<!print(1)!>
+]]
+
+TEST [[
+while true do
+end
+
+<!print(1)!>
+]]
+
+TEST [[
+while X do
+ X = 1
+end
+
+print(1)
+]]
+
+TEST [[
+while true do
+ if not X then
+ break
+ end
+end
+
+print(1)
+
+do return end
+]]
+
+TEST [[
+local done = false
+
+local function set_done()
+ done = true
+end
+
+while not done do
+ set_done()
+end
+
+print(1)
+]]
diff --git a/test/diagnostics/unused-function.lua b/test/diagnostics/unused-function.lua
new file mode 100644
index 00000000..c2cea23a
--- /dev/null
+++ b/test/diagnostics/unused-function.lua
@@ -0,0 +1,48 @@
+TEST [[
+local <!function x()
+end!>
+]]
+
+TEST [[
+local x = <!function () end!>
+]]
+
+TEST [[
+local x
+x = <!function () end!>
+]]
+
+TEST [[
+local <!function x()
+end!>
+local <!function y()
+ x()
+end!>
+]]
+
+TEST [[
+local f = <!function () end!>
+]]
+
+TEST [[
+local f;f = <!function () end!>
+]]
+
+TEST [[
+local <!function f() end!>
+]]
+
+TEST [[
+local <!function f()
+ f()
+end!>
+]]
+
+
+TEST [[
+local <!function test()
+end!>
+
+local <!function foo ()
+end!>
+]]
diff --git a/test/diagnostics/unused-label.lua b/test/diagnostics/unused-label.lua
new file mode 100644
index 00000000..3a89a147
--- /dev/null
+++ b/test/diagnostics/unused-label.lua
@@ -0,0 +1,3 @@
+TEST [[
+::<!LABEL!>::
+]]
diff --git a/test/diagnostics/unused-local.lua b/test/diagnostics/unused-local.lua
new file mode 100644
index 00000000..425f3e1e
--- /dev/null
+++ b/test/diagnostics/unused-local.lua
@@ -0,0 +1,99 @@
+local config = require 'config'
+
+TEST [[
+local <!x!>
+]]
+
+TEST [[
+local y
+local x <close> = y
+]]
+
+TEST [[
+local function x()
+end
+x()
+]]
+
+TEST [[
+return function (x)
+ x.a = 1
+end
+]]
+
+TEST [[
+local <!t!> = {}
+<!t!>.a = 1
+]]
+
+TEST [[
+InstanceName = 1
+Instance = _G[InstanceName]
+]]
+
+TEST [[
+local _ = (''):sub(1, 2)
+]]
+
+TEST [[
+local mt, x
+function mt:m()
+ function x:m()
+ end
+end
+return mt, x
+]]
+
+TEST [[
+local mt = {}
+function mt:f()
+end
+return mt
+]]
+
+TEST [[
+local <!mt!> = {}
+function <!mt!>:f()
+end
+]]
+
+TEST [[
+local <!x!> = {}
+<!x!>.a = 1
+]]
+
+TEST [[
+local <!x!> = {}
+<!x!>['a'] = 1
+]]
+
+TEST [[
+local function f(<!self!>)
+ return 'something'
+end
+f()
+]]
+
+TEST [[
+local function f(var)
+ print(var)
+end
+local var
+f(var)
+]]
+
+TEST [[
+local <!t!> = {}
+<!t!>[1] = 1
+]]
+
+config.add(nil, 'Lua.diagnostics.unusedLocalExclude', 'll_*')
+
+TEST [[
+local <!xx!>
+local ll_1
+local ll_2
+local <!ll!>
+]]
+
+config.remove(nil, 'Lua.diagnostics.unusedLocalExclude', 'll_*')
diff --git a/test/diagnostics/unused-vararg.lua b/test/diagnostics/unused-vararg.lua
new file mode 100644
index 00000000..2bed4aab
--- /dev/null
+++ b/test/diagnostics/unused-vararg.lua
@@ -0,0 +1,6 @@
+TEST [[
+local function f(<!...!>)
+ return 'something'
+end
+f()
+]]
diff --git a/test/full/init.lua b/test/full/init.lua
index ac78f134..a6496a99 100644
--- a/test/full/init.lua
+++ b/test/full/init.lua
@@ -37,7 +37,7 @@ end) do
end
end
-util.revertTable(times)
+util.revertArray(times)
for _, time in ipairs(times) do
print(time)
end
diff --git a/test/full/projects.lua b/test/full/projects.lua
index dacc101c..20ef6724 100644
--- a/test/full/projects.lua
+++ b/test/full/projects.lua
@@ -41,7 +41,7 @@ local function doProjects(pathname)
print('开始诊断...')
- ws.ready = true
+ furi.encode(path:string())
diag.diagnosticsScope(furi.encode(path:string()))
local clock = os.clock()
diff --git a/test/full/self.lua b/test/full/self.lua
index 09d2d154..d118e034 100644
--- a/test/full/self.lua
+++ b/test/full/self.lua
@@ -32,7 +32,7 @@ end
print('基准诊断目录:', path)
-ws.ready = true
+ws.awaitReady(furi.encode(path:string()))
diag.diagnosticsScope(furi.encode(path:string()))
local clock = os.clock()
@@ -86,7 +86,7 @@ end) do
end
end
-util.revertTable(printTexts)
+util.revertArray(printTexts)
for _, text in ipairs(printTexts) do
print(text)
diff --git a/test/hover/init.lua b/test/hover/init.lua
index e77d8c98..63220a59 100644
--- a/test/hover/init.lua
+++ b/test/hover/init.lua
@@ -2383,6 +2383,25 @@ local obj: B {
TEST [[
---@class A
+local M = {}
+
+---@private
+M.x = 0
+
+---@private
+function M:init()
+ self.x = 1
+end
+
+---@type A
+local <?a?>
+]]
+[[
+local a: A
+]]
+
+TEST [[
+---@class A
---@field x fun(): string
---@type table<string, A>
@@ -2447,3 +2466,25 @@ local t: {
[3]: table,
}
]]
+
+TEST [[
+---@class A
+---@overload fun(x: number): boolean
+local <?x?>
+]]
+[[
+local x: A
+]]
+
+TEST [[
+---@type A
+local <?f?>
+
+---@enum A
+local t = {
+ x = f,
+}
+]]
+[[
+local f: A
+]]
diff --git a/test/plugins/ffi/test.lua b/test/plugins/ffi/test.lua
index b46fe962..93be2ff5 100644
--- a/test/plugins/ffi/test.lua
+++ b/test/plugins/ffi/test.lua
@@ -2,13 +2,14 @@ local lclient = require 'lclient'
local ws = require 'workspace'
local furi = require 'file-uri'
local files = require 'files'
+local diagnostic = require 'provider.diagnostic'
--TODO how to changed the runtime version?
local template = require 'config.template'
template['Lua.runtime.version'].default = 'LuaJIT'
-TESTURI = furi.encode('/unittest.ffi.lua')
+TESTURI = furi.encode(TESTROOT .. 'unittest.ffi.lua')
---@async
local function TestBuilder()
@@ -24,15 +25,19 @@ end
---@async
lclient():start(function (languageClient)
languageClient:registerFakers()
- local rootUri = furi.encode '/'
+ local rootUri = TESTURI
languageClient:initialize {
rootUri = rootUri,
}
+ diagnostic.pause()
+
ws.awaitReady(rootUri)
require 'plugins.ffi.cdef'
require 'plugins.ffi.parser'
require 'plugins.ffi.builder'
TestBuilder()
+
+ diagnostic.resume()
end)
diff --git a/test/signature/init.lua b/test/signature/init.lua
index 147f93e1..f46ce017 100644
--- a/test/signature/init.lua
+++ b/test/signature/init.lua
@@ -330,3 +330,45 @@ t(<??>)
{
'function (<!x: number!>)',
}
+
+TEST [[
+---@class 😅
+
+---@param a 😅
+---@param b integer
+local function f(a, b)
+end
+
+f(1, 2<??>)
+]]
+{
+'function f(a: 😅, <!b: integer!>)',
+}
+
+TEST [[
+---@class A
+---@field event fun(self: self, ev: "onChat", c: string)
+---@field event fun(self: self, ev: "onTimer", t: integer)
+
+---@type A
+local t
+
+t:event("onChat", <??>)
+]]
+{
+'(method) (ev: "onChat", <!c: string!>)',
+}
+
+TEST [[
+---@class A
+---@field event fun(self: self, ev: "onChat", c: string)
+---@field event fun(self: self, ev: "onTimer", t: integer)
+
+---@type A
+local t
+
+t:event("onTimer", <??>)
+]]
+{
+'(method) (ev: "onTimer", <!t: integer!>)',
+}
diff --git a/test/type_inference/init.lua b/test/type_inference/init.lua
index 8797cfdd..98a07ca3 100644
--- a/test/type_inference/init.lua
+++ b/test/type_inference/init.lua
@@ -44,6 +44,10 @@ function TEST(wanted)
end
end
+TEST 'nil' [[
+local <?t?> = nil
+]]
+
TEST 'string' [[
local <?var?> = '111'
]]
@@ -4313,3 +4317,22 @@ local x = 1
repeat
until <?x?>
]]
+
+-- #2144
+TEST 'A' [=[
+local function f()
+ return {} --[[@as A]]
+end
+
+local <?x?> = f()
+]=]
+
+TEST 'A' [=[
+local function f()
+ ---@type A
+ return {}
+end
+
+local <?x?> = f()
+]=]
+--