diff options
Diffstat (limited to 'test')
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() +]=] +-- |