local files = require 'files' local guide = require 'parser.guide' local config = require 'config' local catch = require 'catch' local vm = require 'vm' rawset(_G, 'TEST', true) local function getSource(pos) local state = files.getState(TESTURI) if not state then return end local result guide.eachSourceContain(state.ast, pos, function (source) if source.type == 'local' or source.type == 'getlocal' or source.type == 'setlocal' or source.type == 'setglobal' or source.type == 'getglobal' or source.type == 'field' or source.type == 'method' or source.type == 'function' or source.type == 'table' or source.type == 'doc.type.name' then result = source end end) return result end function TEST(wanted) return function (script) local newScript, catched = catch(script, '?') files.setText(TESTURI, newScript) local source = getSource(catched['?'][1][1]) assert(source) local result = vm.getInfer(source):view(TESTURI) if wanted ~= result then vm.getInfer(source):view(TESTURI) end assert(wanted == result) files.remove(TESTURI) end end TEST 'string' [[ local = '111' ]] TEST 'boolean' [[ local = true ]] TEST 'integer' [[ local = 1 ]] TEST 'number' [[ local = 1.0 ]] TEST 'unknown' [[ local ]] TEST 'unknown' [[ local var = y ]] TEST 'any' [[ function f() end ]] TEST 'any' [[ function f() x = 1 end ]] TEST 'string' [[ local var = '111' t. = var ]] TEST 'string' [[ local var = '111' ]] TEST 'string' [[ local var = '111' ]] TEST 'string' [[ local var var = '111' print() ]] TEST 'function' [[ function () end ]] TEST 'function' [[ local function () end ]] TEST 'function' [[ local xx = function () end ]] TEST 'table' [[ local = {} ]] TEST 'unknown' [[ () ]] TEST 'boolean' [[ = not y ]] TEST 'integer' [[ = #y ]] TEST 'integer' [[ = #'aaaa' ]] TEST 'integer' [[ = #{} ]] TEST 'number' [[ = - y ]] TEST 'number' [[ = - 1.0 ]] TEST 'integer' [[ = ~ y ]] TEST 'integer' [[ = ~ 1 ]] TEST 'boolean' [[ = 1 < 2 ]] TEST 'integer' [[ local a = true local b = 1 = a and b ]] TEST 'integer' [[ local a = false local b = 1 = a or b ]] TEST 'boolean' [[ = a == b ]] TEST 'integer' [[ = a << b ]] TEST 'integer' [[ = 1 << 2 ]] TEST 'string' [[ = a .. b ]] TEST 'string' [[ = 'a' .. 'b' ]] TEST 'string' [[ = 'a' .. 1 ]] TEST 'string' [[ = 'a' .. 1.0 ]] TEST 'number' [[ = a + b ]] TEST 'number' [[ = 1 + 2.0 ]] TEST 'integer' [[ = 1 + 2 ]] TEST 'integer' [[ ---@type integer local a = - a ]] TEST 'number' [[ local a = - a ]] TEST 'integer' [[ = 1 + X ]] TEST 'number' [[ = 1.0 + X ]] TEST 'tablelib' [[ ---@class tablelib table = {} () ]] TEST 'string' [[ _VERSION = 'Lua 5.4' = _VERSION ]] TEST 'function' [[ ---@class stringlib local string string.xxx = function () end return ('x'). ]] TEST 'function' [[ ---@class stringlib String = {} String.xxx = function () end return ('x'). ]] TEST 'function' [[ ---@class stringlib local string string.xxx = function () end = ('x').xxx ]] TEST 'function' [[ ---@class stringlib local string string.xxx = function () end _VERSION = 'Lua 5.4' = _VERSION.xxx ]] TEST 'table' [[ = setmetatable({}) ]] TEST 'integer' [[ local function x() return 1 end = x() ]] TEST 'integer|nil' [[ local function x() return 1 return nil end = x() ]] TEST 'unknown|nil' [[ local function x() return a return nil end = x() ]] TEST 'unknown|nil' [[ local function x() return nil return f() end = x() ]] TEST 'unknown|nil' [[ local function x() return nil return f() end _, = x() ]] TEST 'integer' [[ local function x() return 1 end _, = pcall(x) ]] TEST 'integer' [[ function x() return 1 end _, = pcall(x) ]] TEST 'integer' [[ local function x() return 1 end _, = xpcall(x) ]] TEST 'A' [[ ---@class A ---@return A local function f2() end local function f() return f2() end local = f() ]] -- 不根据调用者的输入参数来推测 --TEST 'number' [[ --local function x(a) -- return --end --x(1) --]] --TEST 'table' [[ --setmetatable() --]] -- 不根据对方函数内的使用情况来推测 TEST 'unknown' [[ local function x(a) _ = a + 1 end local b x() ]] TEST 'unknown' [[ local function x(a, ...) local _, , _ = ... end x(nil, 'xx', 1, true) ]] -- 引用不跨越参数 TEST 'unknown' [[ local function x(a, ...) return true, 'ss', ... end local _, _, _, , _ = x(nil, true, 1, 'yy') ]] TEST 'unknown' [[ local = next() ]] TEST 'unknown' [[ local a, b function a() return b() end function b() return a() end local = a() ]] TEST 'class' [[ ---@class class local ]] TEST 'string' [[ ---@class string ---@type string local ]] TEST '1' [[ ---@type 1 local ]] TEST 'string[]' [[ ---@class string ---@type string[] local ]] TEST 'string|table' [[ ---@class string ---@class table ---@type string | table local ]] TEST [['enum1'|'enum2']] [[ ---@type 'enum1' | 'enum2' local ]] TEST [["enum1"|"enum2"]] [[ ---@type "enum1" | "enum2" local ]] config.set(nil, 'Lua.hover.expandAlias', false) TEST 'A' [[ ---@alias A 'enum1' | 'enum2' ---@type A local ]] TEST 'A' [[ ---@alias A 'enum1' | 'enum2' | A ---@type A local ]] TEST 'A' [[ ---@alias A 'enum1' | 'enum2' | B ---@type A local ]] config.set(nil, 'Lua.hover.expandAlias', true) TEST [['enum1'|'enum2']] [[ ---@alias A 'enum1' | 'enum2' ---@type A local ]] TEST [['enum1'|'enum2']] [[ ---@alias A 'enum1' | 'enum2' | A ---@type A local ]] TEST [['enum1'|'enum2'|B]] [[ ---@alias A 'enum1' | 'enum2' | B ---@type A local ]] TEST '1|true' [[ ---@alias A 1 | true ---@type A local ]] TEST 'fun()' [[ ---@type fun() local ]] TEST 'fun(a: string, b: any, ...any)' [[ ---@type fun(a: string, b, ...) local ]] TEST 'fun(a: string, b: any, c?: boolean, ...any):c, d?, ...unknown' [[ ---@type fun(a: string, b, c?: boolean, ...):c, d?, ... local ]] TEST '{ [string]: string }' [[ ---@type { [string]: string } local ]] TEST 'table' [[ ---@class string ---@class number ---@type table local ]] TEST 'A' [[ ---@class A ---@type A local ]] TEST 'string' [[ ---@class string ---@type string[] local x local = x[1] ]] TEST 'string' [[ ---@class string ---@return string[] local function f() end local x = f() local = x[1] ]] TEST 'table' [[ local t = {} local = setmetatable(t) ]] TEST 'CCC' [[ ---@class CCC ---@type table local t = {} print(t.) ]] TEST [['aaa'|'bbb']] [[ ---@type table local t = {} print(t.) ]] TEST 'integer' [[ ---@generic K ---@type fun(a?: K):K local f local = f(1) ]] TEST 'unknown' [[ ---@generic K ---@type fun(a?: K):K local f local = f(nil) ]] TEST 'unknown' [[ ---@generic K ---@type fun(a: K|integer):K local f local = f(1) ]] TEST 'integer' [[ ---@class integer ---@generic T: table, V ---@param t T ---@return fun(table: V[], i?: integer):integer, V ---@return T ---@return integer i local function ipairs() end for in ipairs() do end ]] TEST 'table' [[ ---@generic K, V ---@param t table ---@return K ---@return V local function next(t) end ---@type table local t local k, v = next() ]] TEST 'string' [[ ---@class string ---@generic K, V ---@param t table ---@return K ---@return V local function next(t) end ---@type table local t local , v = next(t) ]] TEST 'boolean' [[ ---@class boolean ---@generic K, V ---@param t table ---@return K ---@return V local function next(t) end ---@type table local t local k, = next(t) ]] TEST 'boolean' [[ ---@generic K ---@type fun(arg: K):K local f local = f(true) ]] TEST 'string' [[ ---@class string ---@generic K, V ---@type fun(arg: table):K, V local f ---@type table local t local , v = f(t) ]] TEST 'boolean' [[ ---@class boolean ---@generic K, V ---@type fun(arg: table):K, V local f ---@type table local t local k, = f(t) ]] TEST 'fun()' [[ ---@return fun() local function f() end local = f() ]] TEST 'table' [[ ---@return table local function f() end local = f() ]] TEST 'string' [[ ---@class string ---@generic K, V ---@return fun(arg: table):K, V local function f() end local f2 = f() ---@type table local t local , v = f2(t) ]] TEST 'fun(a: ):integer, ' [[ ---@generic K, V ---@param a K ---@return fun(a: V):K, V local function f(a) end local = f(1) ]] TEST 'integer' [[ ---@generic K, V ---@param a K ---@return fun(a: V):K, V local function f(a) end local f2 = f(1) local , v = f2(true) ]] TEST 'boolean' [[ ---@generic K, V ---@param a K ---@return fun(a: V):K, V local function f(a) end local f2 = f(1) local i, = f2(true) ]] TEST 'fun(table: table<, >, index?: ):, ' [[ ---@generic T: table, K, V ---@param t T ---@return fun(table: table, index?: K):K, V ---@return T ---@return nil local function pairs(t) end local = pairs(dummy) ]] TEST 'string' [[ ---@generic T: table, K, V ---@param t T ---@return fun(table: table, index?: K):K, V ---@return T ---@return nil local function pairs(t) end local next = pairs(dummy) ---@type table local t local , v = next(t) ]] TEST 'boolean' [[ ---@generic T: table, K, V ---@param t T ---@return fun(table: table, index?: K):K, V ---@return T ---@return nil local function pairs(t) end local next = pairs(dummy) ---@type table local t local k, = next(t) ]] TEST 'string' [[ ---@generic T: table, K, V ---@param t T ---@return fun(table: table, index?: K):K, V ---@return T ---@return nil local function pairs(t) end local next = pairs(dummy) ---@type table local t local , v = next(t, nil) ]] TEST 'boolean' [[ ---@generic T: table, K, V ---@param t T ---@return fun(table: table, index?: K):K, V ---@return T ---@return nil local function pairs(t) end local next = pairs(dummy) ---@type table local t local k, = next(t, nil) ]] TEST 'string' [[ ---@generic T: table, K, V ---@param t T ---@return fun(table: table, index?: K):K, V ---@return T ---@return nil local function pairs(t) end local next = pairs(dummy) ---@type table local t for , v in next, t do end ]] TEST 'boolean' [[ ---@class boolean ---@generic T: table, K, V ---@param t T ---@return fun(table: table, index?: K):K, V ---@return T local function pairs(t) end local f = pairs(t) ---@type table local t for k, in f, t do end ]] TEST 'string' [[ ---@generic T: table, K, V ---@param t T ---@return fun(table: table, index?: K):K, V ---@return T local function pairs(t) end ---@type table local t for , v in pairs(t) do end ]] TEST 'boolean' [[ ---@generic T: table, K, V ---@param t T ---@return fun(table: table, index: K):K, V ---@return T ---@return nil local function pairs(t) end ---@type table local t for k, in pairs(t) do end ]] TEST 'boolean' [[ ---@generic T: table, V ---@param t T ---@return fun(table: V[], i?: integer):integer, V ---@return T ---@return integer i local function ipairs(t) end ---@type boolean[] local t for _, in ipairs(t) do end ]] TEST 'boolean' [[ ---@generic T: table, V ---@param t T ---@return fun(table: V[], i?: integer):integer, V ---@return T ---@return integer i local function ipairs(t) end ---@type table local t for _, in ipairs(t) do end ]] TEST 'boolean' [[ ---@generic T: table, V ---@param t T ---@return fun(table: V[], i?: integer):integer, V ---@return T ---@return integer i local function ipairs(t) end ---@class MyClass ---@field [integer] boolean local t for _, in ipairs(t) do end ]] TEST 'boolean' [[ ---@generic T: table, K, V ---@param t T ---@return fun(table: table, index: K):K, V ---@return T ---@return nil local function pairs(t) end ---@type boolean[] local t for k, in pairs(t) do end ]] TEST 'integer' [[ ---@generic T: table, K, V ---@param t T ---@return fun(table: table, index?: K):K, V ---@return T local function pairs(t) end ---@type boolean[] local t for , v in pairs(t) do end ]] TEST 'E' [[ ---@class A ---@class B: A ---@class C: B ---@class D: C ---@class E: D local m function m:f() return end ]] TEST 'Cls' [[ ---@class Cls local Cls = {} ---@generic T ---@param self T ---@return T function Cls.new(self) return self end local = Cls:new() ]] TEST 'Cls' [[ ---@class Cls local Cls = {} ---@generic T ---@param self T ---@return T function Cls:new() return self end local = Cls:new() ]] TEST 'Cls' [[ ---@class Cls local Cls = {} ---@generic T ---@param self T ---@return T function Cls.new(self) return self end local = Cls.new(Cls) ]] TEST 'Cls' [[ ---@class Cls local Cls = {} ---@generic T ---@param self T ---@return T function Cls:new() return self end local = Cls.new(Cls) ]] TEST 'Rct' [[ ---@class Obj local Obj = {} ---@generic T ---@param self T ---@return T function Obj.new(self) return self end ---@class Pnt:Obj local Pnt = {x = 0, y = 0} ---@class Rct:Pnt local Rct = {w = 0, h = 0} local = Rct.new(Rct) -- local test = Rct:new() return test ]] TEST 'function' [[ string.gsub():gsub():() ]] config.set(nil, 'Lua.hover.enumsLimit', 5) TEST [['a'|'b'|'c'|'d'|'e'...(+5)]] [[ ---@type 'a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'i'|'j' local ]] config.set(nil, 'Lua.hover.enumsLimit', 1) TEST [['a'...(+9)]] [[ ---@type 'a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'i'|'j' local ]] config.set(nil, 'Lua.hover.enumsLimit', 0) TEST '...(+10)' [[ ---@type 'a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'i'|'j' local ]] config.set(nil, 'Lua.hover.enumsLimit', 5) TEST 'string|fun():string' [[ ---@type string | fun(): string local ]] TEST 'string' [[ local valids = { ['Lua 5.1'] = false, ['Lua 5.2'] = false, ['Lua 5.3'] = false, ['Lua 5.4'] = false, ['LuaJIT'] = false, } for , v in pairs(valids) do end ]] TEST 'boolean' [[ local valids = { ['Lua 5.1'] = false, ['Lua 5.2'] = false, ['Lua 5.3'] = false, ['Lua 5.4'] = false, ['LuaJIT'] = false, } for k, in pairs(valids) do end ]] TEST 'string' [[ local t = { a = 1, b = 1, } for , v in pairs(t) do end ]] TEST 'integer' [[ local t = {'a', 'b'} for , v in pairs(t) do end ]] TEST 'string' [[ local t = {'a', 'b'} for k, in pairs(t) do end ]] TEST 'fun():number, boolean' [[ ---@type fun():number, boolean local ]] TEST 'fun(value: Class)' [[ ---@class Class ---@param callback fun(value: Class) function work(callback) end work( (value) end) ]] TEST 'Class' [[ ---@class Class ---@param callback fun(value: Class) function work(callback) end work(function () end) ]] TEST 'fun(value: Class)' [[ ---@class Class ---@param callback fun(value: Class) function work(callback) end pcall(work, (value) end) ]] TEST 'Class' [[ ---@class Class ---@param callback fun(value: Class) function work(callback) end xpcall(work, debug.traceback, function () end) ]] TEST 'string' [[ ---@generic T ---@param x T ---@return { x: T } local function f(x) end local t = f('') print(t.) ]] TEST 'string' [[ ---@generic T ---@param t T[] ---@param callback fun(v: T) local function f(t, callback) end ---@type string[] local t f(t, function () end) ]] TEST 'unknown' [[ ---@generic T ---@param t T[] ---@param callback fun(v: T) local function f(t, callback) end local t = {} f(t, function () end) ]] TEST 'table' [[ local = setmetatable({}, { __index = function () end }) ]] TEST 'player' [[ ---@class player local t :getOwner() ]] TEST 'string[][]' [[ ---@type string[][] local ]] TEST 'table' [[ ---@type {}[] local t local = t[1] ]] TEST 'string' [[ ---@type string[][] local v = {} for _, a in ipairs(v) do for i, in ipairs(a) do end end ]] --TEST 'number' [[ -----@param x number --local f -- --f = function () end --]] TEST 'fun(i: integer)' [[ --- @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 = {} emit.on("died", (i) end) ]] TEST 'integer' [[ --- @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 = {} emit.on("died", function () end) ]] TEST 'integer' [[ --- @class Emit --- @field on fun(self: Emit, eventName: string, cb: function) --- @field on fun(self: Emit, eventName: 'died', cb: fun(i: integer)) --- @field on fun(self: Emit, eventName: 'won', cb: fun(s: string)) local emit = {} emit:on("died", function () end) ]] TEST 'integer' [[ --- @class Emit --- @field on fun(self: Emit, eventName: string, cb: function) --- @field on fun(self: Emit, eventName: '"died"', cb: fun(i: integer)) --- @field on fun(self: Emit, eventName: '"won"', cb: fun(s: string)) local emit = {} emit.on(self, "died", function () end) ]] TEST '👍' [[ ---@class 👍 local ]] TEST 'integer' [[ ---@type boolean local x = 1 ]] TEST 'integer' [[ ---@class Class local x = 1 ]] TEST 'unknown' [[ ---@return number local function f(x) local = x() end ]] TEST 'unknown' [[ local mt ---@return number function mt:f() end local = mt() ]] TEST 'unknown' [[ local ---@class X function mt:f(x) end ]] TEST 'any' [[ local mt ---@class X function mt:f() end ]] TEST 'unknown' [[ local ---@type number function mt:f(x) end ]] TEST 'any' [[ local mt ---@type number function mt:f() end ]] TEST 'Test' [[ ---@class Test _G. = {} ]] TEST 'integer' [[ local mt = {} ---@param callback fun(i: integer) function mt:loop(callback) end mt:loop(function () end) ]] TEST 'C' [[ ---@class D ---@field y integer # D comment ---@class C ---@field x integer # C comment ---@field d D ---@param c C local function f(c) end f x = , } ]] TEST 'integer' [[ ---@class D ---@field y integer # D comment ---@class C ---@field x integer # C comment ---@field d D ---@param c C local function f(c) end f { = , } ]] TEST 'integer' [[ ---@class D ---@field y integer # D comment ---@class C ---@field x integer # C comment ---@field d D ---@param c C local function f(c) end f { d = { = , } } ]] TEST 'integer' [[ for = a, b, c do end ]] TEST 'number' [[ ---@param x number function F() end ---@param x boolean function F(x) end ]] TEST 'B' [[ ---@class A local A ---@return A function A:x() end ---@class B: A local B ---@return B function B:x() end ---@type B local t local = t.x() ]] TEST 'function' [[ ---@overload fun() function () end ]] TEST 'integer' [[ ---@type table local t t. ]] TEST '"a"|"b"|"c"' [[ ---@type table local t t. ]] TEST 'integer' [[ ---@class A ---@field x integer ---@type A local t t. ]] TEST 'boolean' [[ local = true var = 1 var = 1.0 ]] TEST 'unknown' [[ ---@return ... local function f() end local = f() ]] TEST 'unknown' [[ ---@return ... local function f() end local _, = f() ]] TEST 'unknown' [[ local t = { x = 1, y = 2, } local = t[#t] ]] TEST 'string' [[ local t = { x = 1, [1] = 'x', } local = t[#t] ]] TEST 'string' [[ local t = { 'x' } local = t[#t] ]] TEST '(string|integer)[]' [[ ---@type (string|integer)[] local ]] TEST 'boolean' [[ ---@type table local t ---@alias uri string ---@type string local uri local = t[uri] ]] TEST 'A' [[ ---@class A G = {} :A() ]] TEST 'A' [[ ---@type A local = nil ]] TEST 'A' [[ ---@class A ---@field b B local mt function mt:f() self.b:x() print() end ]] TEST 'string?' [[ ---@return string? local function f() end local = f() ]] TEST 'AA' [[ ---@class AA ---@overload fun():AA local AAA local = AAA() ]] TEST 'AA' [[ ---@class AA ---@overload fun():AA AAA = {} local = AAA() ]] TEST 'string' [[ local x = '1' x = 1 ]] TEST 'string' [[ local x = '1' x = 1 ]] TEST 'integer' [[ local x x = '1' = 1 ]] TEST 'unknown' [[ local x print() x = '1' x = 1 ]] TEST 'string' [[ local x x = '1' print() x = 1 ]] TEST 'integer' [[ local x x = '1' x = 1 print() ]] TEST 'unknown' [[ local x function A() print() end ]] TEST 'string' [[ local x function A() print() end x = '1' x = 1 ]] TEST 'string' [[ local x x = '1' function A() print() end x = 1 ]] TEST 'integer' [[ local x x = '1' x = 1 function A() print() end ]] TEST 'boolean' [[ local x function A() x = true print() end x = '1' x = 1 ]] TEST 'unknown' [[ local x function A() x = true end print() x = '1' x = 1 ]] TEST 'boolean' [[ local x function A() x = true function B() print() end end x = '1' x = 1 ]] TEST 'table' [[ local x function A() x = true function B() x = {} print() end end x = '1' x = 1 ]] TEST 'boolean' [[ local x function A() x = true function B() x = {} end print() end x = '1' x = 1 ]] TEST 'unknown' [[ local x function A() x = true function B() x = {} end end function C() print() end x = '1' x = 1 ]] TEST 'integer' [[ local x x = true do x = 1 end print() ]] TEST 'boolean' [[ local x x = true function XX() do x = 1 end end print() ]] TEST 'integer?' [[ ---@type integer? local ]] TEST 'integer?' [[ ---@type integer? local x if then print(x) end ]] --[[ context 0 integer? save copy 'block' save copy 'out' push 'block' get push copy truthy falsy ref 'out' get save HEAD 'final' push 'out' push copy HEAD merge 'final' ]] TEST 'integer' [[ ---@type integer? local x if x then print() end ]] TEST 'integer?' [[ ---@type integer? local x if x then print(x) end print() ]] TEST 'nil' [[ ---@type integer? local x if not x then print() end print(x) ]] TEST 'integer' [[ ---@type integer? local x if not x then x = 1 end print() ]] TEST 'integer' [[ ---@type integer? local x if not x then return end print() ]] TEST 'integer' [[ ---@type integer? local x if xxx and x then print() end ]] TEST 'unknown' [[ ---@type integer? local x if not x and x then print() end ]] TEST 'integer' [[ ---@type integer? local x if x and not mark[x] then print() end ]] TEST 'integer?' [[ ---@type integer? local x if xxx and x then end print() ]] TEST 'integer?' [[ ---@type integer? local x if xxx and x then return end print() ]] TEST 'integer' [[ ---@type integer? local x if x ~= nil then print() end print(x) ]] TEST 'integer|nil' [[ ---@type integer? local x if x ~= nil then print(x) end print() ]] TEST 'nil' [[ ---@type integer? local x if x == nil then print() end print(x) ]] TEST 'integer|nil' [[ ---@type integer? local x if x == nil then print(x) end print() ]] TEST 'integer' [[ ---@type integer? local x = x or 1 ]] TEST 'integer' [[ ---@type integer? local x = x or y ]] TEST 'integer' [[ ---@type integer? local x if not x then return end print() ]] TEST 'integer' [[ ---@type integer? local x if not x then goto ANYWHERE end print() ]] TEST 'integer' [=[ local x print(--[[@as integer]]) ]=] TEST 'integer' [=[ print(--[[@as integer]]) ]=] TEST 'integer' [=[ print(io.--[[@as integer]]) ]=] TEST 'integer' [=[ local = io['open']--[[@as integer]]) ]=] TEST 'integer' [=[ local = 1 + 1--[[@as integer]]) ]=] TEST 'integer' [=[ local = not 1--[[@as integer]]) ]=] TEST 'integer' [=[ local = ()--[[@as integer]]) ]=] TEST 'integer?' [[ ---@param x? integer local function f() end ]] TEST 'integer' [[ local x = 1 x = ]] TEST 'integer?' [[ ---@class A ---@field x? integer local t t. ]] TEST 'integer?' [[ ---@type { x?: integer } local t t. ]] TEST 'boolean' [[ ---@class A ---@field [integer] boolean local t local = t[1] ]] TEST 'unknown' [[ local = y and z ]] TEST 'integer' [[ ---@type integer? local x assert(x) print() ]] TEST 'integer' [[ ---@type integer? local x assert(x ~= nil) print() ]] TEST 'integer' [[ ---@type integer | nil local x assert(x) print() ]] TEST 'integer' [[ ---@type integer | nil local x assert(x ~= nil) print() ]] TEST 'integer' [[ local x assert(x == 1) print() ]] TEST 'integer' [[ ---@type integer? local x if x and .y then end ]] TEST 'integer?' [[ ---@type integer? local x if x and x.y then end print() ]] TEST 'integer?' [[ ---@type integer? local x if x and x.y then return end print() ]] TEST 'integer' [[ ---@type integer? local x if not x or .y then end ]] TEST 'integer?' [[ ---@type integer? local x if not x or x.y then print() end ]] TEST 'integer?' [[ ---@type integer? local x if x or x.y then print() end ]] TEST 'integer?' [[ ---@type integer? local x if x.y or x then print() end ]] TEST 'integer?' [[ ---@type integer? local x if x.y or not x then print() end ]] TEST 'integer' [[ ---@type integer? local x if not x or not y then return end print() ]] TEST 'integer' [[ ---@type integer? local x if not y or not x then return end print() ]] TEST 'integer' [[ ---@type integer? local x while true do if not x then break end print() end ]] TEST 'integer?' [[ ---@type integer? local x while true do if not x then break end end print() ]] TEST 'integer' [[ ---@type integer? local x while x do print() end ]] TEST 'integer' [[ ---@type fun():integer? local iter for in iter do end ]] TEST 'integer' [[ local x ---@type integer = XXX ]] TEST 'unknown' [[ for _ = 1, 999 do local end ]] TEST 'integer' [[ local x ---@cast x integer print() ]] TEST 'unknown' [[ local x ---@cast x integer local x print() ]] TEST 'unknown' [[ local x if true then local x ---@cast x integer print(x) end print() ]] TEST 'boolean|integer' [[ local x = 1 ---@cast x +boolean print() ]] TEST 'boolean' [[ ---@type integer|boolean local x ---@cast x -integer print() ]] TEST 'boolean?' [[ ---@type boolean local x ---@cast x +? print() ]] TEST 'boolean' [[ ---@type boolean? local x ---@cast x -? print() ]] TEST 'nil' [[ ---@type string? local x if x then return else print() end print(x) ]] TEST 'string' [[ ---@type string? local x if not x then return else print() end print(x) ]] TEST 'string' [[ ---@type string? local x if not x then return else print(x) end print() ]] TEST 'true' [[ ---@type boolean | nil local x if not x then return end print() ]] TEST 'true' [[ ---@type boolean local t if t then print() return end print(t) ]] TEST 'false' [[ ---@type boolean local t if t then print(t) return end print() ]] TEST 'nil' [[ ---@type integer? local t if t then else print() end print(t) ]] TEST 'table' [[ local function f() if x then return y end return {} end local = f() ]] TEST 'integer|table' [[ local function returnI() return a + 1 end local function f() if x then return returnI() end return {} end local = f() ]] TEST 'number' [[ for _ in _ do ---@type number local end ]] TEST 'unknown' [[ for _ in _ do ---@param x number local end ]] TEST 'unknown' [[ ---@type number for in _ do end ]] TEST 'number' [[ ---@param x number for in _ do end ]] TEST 'table' [[ ---@alias tp table ---@type tp local ]] TEST '{ name: boolean }' [[ ---@alias tp {name: boolean} ---@type tp local ]] TEST 'boolean|{ name: boolean }' [[ ---@alias tp boolean | {name: boolean} ---@type tp local ]] TEST '`1`|`true`' [[ ---@type `1` | `true` local ]] TEST 'function' [[ local x function x() end print() ]] TEST 'unknown' [[ local x if x.field == 'haha' then print() end ]] TEST 'string' [[ ---@type string? local t if not t or xxx then return end print() ]] TEST 'table' [[ ---@type table|nil local t return function () if not t then return end print() end ]] TEST 'table' [[ ---@type table|nil local t f(function () if not t then return end print() end) ]] TEST 'table' [[ ---@type table? local t t = t or {} print() ]] TEST 'unknown|nil' [[ local x if x == nil then end print() ]] TEST 'table' [[ ---@alias xxx table ---@type xxx local ]] TEST 'xxx[][]' [[ ---@alias xxx xxx[] ---@type xxx local ]] TEST 'fun(x: fun(x: xxx))' [[ ---@alias xxx fun(x: xxx) ---@type xxx local ]] TEST 'table' [[ ---@type table|nil local t while t do print() end ]] TEST 'table|nil' [[ ---@type table|nil local t while do print(t) end ]] TEST 'table' [[ ---@type table|nil local t while t ~= nil do print() end ]] TEST 'table|nil' [[ ---@type table|nil local t while ~= nil do print(t) end ]] TEST 'integer' [[ ---@type integer? local n if not n then error('n is nil') end print() ]] TEST 'table' [[ ---@type table? local n print((n and .x)) ]] TEST 'table' [[ ---@type table? local n n = n and .x or 1 ]] TEST 'table' [[ ---@type table? local n n = ff[n and .x] ]] TEST 'integer' [[ local x if type(x) == 'integer' then print() end ]] TEST 'boolean|integer' [[ local x if type(x) == 'integer' or type(x) == 'boolean' then print() end ]] TEST 'fun()' [[ ---@type fun()? local x if type(x) == 'function' then print() end ]] TEST 'function' [[ local x if type(x) == 'function' then print() end ]] TEST 'integer' [[ local x local tp = type(x) if tp == 'integer' then print() end ]] TEST 'integer' [[ ---@type integer? local x if (x == nil) then else print() end ]] TEST 'B' [[ ---@class A ---@class B ---@type A local x ---@type B x = call(x) print() ]] TEST 'nil' [[ local function f() end local = f() ]] TEST 'integer[]' [[ ---@type integer[] local x if not x then return end print() ]] TEST 'unknown' [[ ---@type string[] local t local = t.x ]] TEST 'integer|unknown' [[ local function f() return GG end local t t.x = 1 t.x = f() print(t.) ]] TEST 'integer' [[ local function f() if X then return X else return 1 end end local = f() ]] TEST 'unknown' [[ local function f() return t[k] end local = f() ]] TEST 'integer|nil' [[ local function f() if x then return else return 1 end end local = f() ]] TEST 'integer' [[ ---@class A ---@field x integer local m m. = true print(m.x) ]] TEST 'integer' [[ ---@class A ---@field x integer local m m.x = true print(m.) ]] TEST 'integer' [[ ---@class A ---@field x integer --> 1st local m = { x = '' --> 2nd } ---@type boolean m.x = true --> 3rd (with ---@type above) m.x = {} --> 4th print(m.) ]] TEST 'string' [[ ---@class A ----@field x integer --> 1st local m = { x = '' --> 2nd } ---@type boolean m.x = true --> 3rd (with ---@type above) m.x = {} --> 4th print(m.) ]] TEST 'boolean' [[ ---@class A ----@field x integer --> 1st local m = { --x = '' --> 2nd } ---@type boolean m.x = true --> 3rd (with ---@type above) m.x = {} --> 4th print(m.) ]] TEST 'table' [[ ---@class A ----@field x integer --> 1st local m = { --x = '' --> 2nd } ---@type boolean --m.x = true --> 3rd (with ---@type above) m.x = {} --> 4th print(m.) ]] TEST 'boolean?' [[ ---@generic T ---@param x T ---@return T local function echo(x) end ---@type boolean? local b local = echo(b) ]] TEST 'boolean' [[ ---@generic T ---@param x T? ---@return T local function echo(x) end ---@type boolean? local b local = echo(b) ]] TEST 'boolean' [[ ---@generic T ---@param x? T ---@return T local function echo(x) end ---@type boolean? local b local = echo(b) ]] TEST 'boolean' [[ ---@overload fun():boolean ---@param x integer ---@return number function f(x) end local = f() ]] TEST 'number' [[ ---@overload fun():boolean ---@param x integer ---@return number function f(x) end local = f(1) ]] TEST 'boolean' [[ ---@overload fun():boolean ---@param x integer ---@return number function f(x) end function r0() return end local = f(r0()) ]] TEST 'number' [[ ---@overload fun():boolean ---@param x integer ---@return number function f(x) end function r1() return 1 end local = f(r1()) ]] TEST 'boolean' [[ ---@overload fun():boolean ---@param x integer ---@return number function f(x) end ---@type fun() local r0 local = f(r0()) ]] TEST 'number' [[ ---@overload fun():boolean ---@param x integer ---@return number function f(x) end ---@type fun():integer local r1 local = f(r1()) ]] TEST 'boolean' [[ ---@overload fun(x: number, y: number):string ---@overload fun(x: number):number ---@return boolean local function f() end local = f() local n2 = f(0) local n3 = f(0, 0) ]] TEST 'number' [[ ---@overload fun(x: number, y: number):string ---@overload fun(x: number):number ---@return boolean local function f() end local n1 = f() local = f(0) local n3 = f(0, 0) ]] TEST 'string' [[ ---@overload fun(x: number, y: number):string ---@overload fun(x: number):number ---@return boolean local function f() end local n1 = f() local n2 = f(0) local = f(0, 0) ]] TEST 'boolean' [[ ---@type {[integer]: boolean, xx: integer} local t local = t[1] ]] TEST 'boolean' [[ ---@type integer local i ---@type {[integer]: boolean, xx: integer} local t local = t[i] ]] TEST 'string' [=[ local x = true local y = x--[[@as integer]] --is `integer` here local z = --[[@as string]] --is `true` here ]=] TEST 'integer' [[ ---@type integer local x if type(x) == 'number' then print() end ]] TEST 'boolean' [[ ---@class A ---@field [integer] boolean local mt function mt:f() ---@type integer local index local = self[index] end ]] TEST 'boolean' [[ ---@class A ---@field [B] boolean ---@class B ---@type A local a ---@type B local b local = a[b] ]] TEST 'number' [[ ---@type {x: string ; y: boolean; z: number} local t local = t.z ]] TEST 'fun():number, boolean' [[ ---@type {f: fun():number, boolean} local t local = t.f ]] TEST 'fun():number' [[ ---@type {(f: fun():number), x: boolean} local t local = t.f ]] TEST 'boolean' [[ ---@param ... boolean local function f(...) local = ... end ]] TEST 'boolean' [[ ---@param ... boolean local function f(...) local _, = ... end ]] TEST 'boolean' [[ ---@return boolean ... local function f() end local = f() ]] TEST 'boolean' [[ ---@return boolean ... local function f() end local _, = f() ]] TEST 'boolean' [[ ---@type fun():name1: boolean, name2:number local f local = f() ]] TEST 'number' [[ ---@type fun():name1: boolean, name2:number local f local _, = f() ]] TEST 'boolean' [[ ---@type fun():(name1: boolean, name2:number) local f local = f() ]] TEST 'number' [[ ---@type fun():(name1: boolean, name2:number) local f local _, = f() ]] TEST 'boolean' [[ ---@type fun():...: boolean local f local _, = f() ]] TEST 'string' [[ local s while true do s = '' end print() ]] TEST 'string' [[ local s for _ in _ do s = '' end print() ]] TEST 'A' [[ ---@class A: string ---@type A local = '' ]] TEST 'number' [[ ---@return number local function f() end local x, = 1, f() ]] TEST 'boolean' [[ ---@return number, boolean local function f() end local x, y, = 1, f() ]] TEST 'number' [[ ---@return number, boolean local function f() end local x, y, = 1, 2, f() ]] TEST 'unknown' [[ local f print() function f() end ]] TEST 'unknown' [[ local f do print() end function f() end ]] TEST 'function' [[ local f function A() print() end function f() end ]] TEST 'number' [[ ---@type number|nil local n local t = { x = n and , } ]] TEST 'table' [[ ---@type table? local n if not n or not .x then end ]] TEST 'table' [[ ---@type table? local n if not n or not [1] then end ]] TEST 'number' [[ ---@type number|false local n ---@cast n -false print() ]] TEST 'table' [[ ---@type number|table local n if n ---@cast n table and .type == 'xxx' then end ]] TEST 'integer' [[ ---@type integer? local n if true then n = 0 end local = n or 0 ]] TEST 'number' [=[ local = F()--[[@as number]] ]=] TEST 'number' [=[ local function f() return F()--[[@as number]] end local = f() ]=] TEST 'number' [=[ local = X --[[@as number]] ]=] TEST 'number' [[ ---@return number?, number? local function f() end for , y in f do end ]] TEST 'number' [[ ---@return number?, number? local function f() end for x, in f do end ]] TEST 'number|nil' [[ ---@type table|nil local a ---@type number|nil local b local = a and b ]] TEST 'number|table|nil' [[ ---@type table|nil local a ---@type number|nil local b local = a or b ]] TEST 'number|table|nil' [[ ---@type table|nil local a ---@type number|nil local b local c = a and b local = a or b ]] TEST 'number' [[ local x ---@return number local function f() end x = f() print() ]] TEST 'number' [[ local x ---@return number local function f() end _, x = pcall(f) print() ]] TEST 'string' [[ ---@type table local t ---@type number local n ---@type string local s local = t[n] local test2 = t[s] --test and test2 are unknow ]] TEST 'string' [[ ---@type table local t ---@type number local n ---@type string local s local test = t[n] local = t[s] --test and test2 are unknow ]] TEST 'table' [[ ---@type table local t = {} ]] TEST 'integer' [[ ---@type integer[]|A local t local = t[1] ]] TEST 'integer' [[ ---@type integer ---@diagnostic disable local ]] TEST 'A' [[ ---@class A ---@diagnostic disable local ]] TEST '{ [string]: number, [true]: string, [1]: boolean, tag: integer }' [[ ---@type {[string]: number, [true]: string, [1]: boolean, tag: integer} local ]] TEST 'unknown' [[ local mt = {} mt. = nil ]] TEST 'unknown' [[ mt = {} mt. = nil ]] TEST 'A' [[ ---@class A ---@operator unm: A ---@type A local a local = -a ]] TEST 'A' [[ ---@class A ---@operator bnot: A ---@type A local a local = ~a ]] TEST 'A' [[ ---@class A ---@operator len: A ---@type A local a local = #a ]] TEST 'A' [[ ---@class A ---@operator add: A ---@type A local a local = a + 1 ]] TEST 'A' [[ ---@class A ---@operator sub: A ---@type A local a local = a - 1 ]] TEST 'A' [[ ---@class A ---@operator mul: A ---@type A local a local = a * 1 ]] TEST 'A' [[ ---@class A ---@operator div: A ---@type A local a local = a / 1 ]] TEST 'A' [[ ---@class A ---@operator mod: A ---@type A local a local = a % 1 ]] TEST 'A' [[ ---@class A ---@operator pow: A ---@type A local a local = a ^ 1 ]] TEST 'A' [[ ---@class A ---@operator idiv: A ---@type A local a local = a // 1 ]] TEST 'A' [[ ---@class A ---@operator band: A ---@type A local a local = a & 1 ]] TEST 'A' [[ ---@class A ---@operator bor: A ---@type A local a local = a | 1 ]] TEST 'A' [[ ---@class A ---@operator bxor: A ---@type A local a local = a ~ 1 ]] TEST 'A' [[ ---@class A ---@operator shl: A ---@type A local a local = a << 1 ]] TEST 'A' [[ ---@class A ---@operator shr: A ---@type A local a local = a >> 1 ]] TEST 'A' [[ ---@class A ---@operator concat: A ---@type A local a local = a .. 1 ]] TEST 'A' [[ ---@class A ---@operator add(boolean): boolean ---@operator add(integer): A ---@type A local a local = a + 1 ]] TEST 'boolean' [[ ---@class A ---@operator add(boolean): boolean ---@operator add(integer): A ---@type A local a local = a + true ]] TEST 'A' [[ ---@class A ---@operator call: A ---@type A local a local = a() ]] TEST 'A' [[ ---@class A ---@operator call: A ---@type A local a local t = { = a(), } ]] TEST 'boolean' [[ ---@class A ---@field n number ---@field [string] boolean local t local = t.xx ]] TEST 'number' [[ ---@class A ---@field n number ---@field [string] boolean local t local = t.n ]] TEST 'string' [[ ---@class string ---@operator mod: string local = '' % 1 ]] TEST 'string|integer' [[ ---@type boolean local bool local = bool and '' or 0 ]] TEST 'string|integer' [[ local bool if X then bool = true else bool = false end local = bool and '' or 0 ]] TEST 'boolean' [[ ---@type boolean|true|false local ]] TEST 'integer|false' [[ local = X == 1 and X == 1 and 1 ]] TEST 'unknown|nil' [[ local function f() if X then return ({})[1] end return nil end local = f() ]] TEST 'integer' [[ ---@generic T ---@vararg T # ERROR ---@return T local function test(...) return ... end local = test(1) ]] TEST 'boolean' [[ ---@type boolean, number local , y ]] TEST 'number' [[ ---@type boolean, number local x, ]] TEST 'unknown' [[ ---@type _, number local , y ]] TEST 'number[]' [[ local t ---@cast t number[]? local x = t and [i] ]] TEST 'number?' [[ ---@type number[]? local t local = t and t[i] ]] TEST 'number' [[ ---@type number local x if not .y then x = nil end ]] TEST 'number' [[ ---@type number|nil local x while x == nil do if x == nil then return end x = nil end print() ]] TEST 'integer' [[ local A = { ---@class XXX B = {} } A.B.C = 1 print(A.B.) ]] TEST '-2|-3|1' [[ ---@type 1|-2|-3 local ]] TEST 'table' [[ ---@enum A local m = {} print() ]] TEST 'A' [[ ---@class A ---@overload fun():A local m = {} ---@return A function m:init() return end ]] TEST 'string' [[ ---@vararg string function F(...) local t = {...} for k, in pairs(t) do end end ]] TEST 'string' [[ ---@vararg string function F(...) local t = {...} for k, in ipairs(t) do end end ]] TEST 'integerA' [[ ---@type integerA for = 1, 10 do end ]] TEST 'string' [[ ---@class A ---@field x string ---@class B : A local t = {} t.x = t.x print(t.) ]] TEST 'unknown' [[ local t = { x = 1, } local x local = t[x] ]] TEST 'A|B' [[ ---@class A ---@class B: A ---@type A|B local ]] TEST 'function' [[ ---@class myClass local myClass = { has = { nested = {} } } function myClass.has.nested.fn() end ---@type myClass local class class.has.nested.() ]] TEST 'integer[]' [[ ---@generic T ---@param f fun(x: T) ---@return T[] local function x(f) end ---@param x integer local = x(function (x) end) ]] TEST 'integer[]' [[ ---@generic T ---@param f fun():T ---@return T[] local function x(f) end local = x(function () return 1 end) ]] TEST 'integer[]' [[ ---@generic T ---@param f fun():T ---@return T[] local function x(f) end ---@return integer local = x(function () end) ]] TEST 'integer[]' [[ ---@generic T ---@param f fun(x: T) ---@return T[] local function x(f) end ---@type fun(x: integer) local cb local = x(cb) ]] TEST 'integer[]' [[ ---@generic T ---@param f fun():T ---@return T[] local function x(f) end ---@type fun(): integer local cb local = x(cb) ]] TEST 'integer' [[ ---@return fun(x: integer) local function f() return function () end end ]] TEST 'string' [[ ---@class A ---@field f fun(x: string) ---@type A local t = { f = function () end } ]] config.set(nil, 'Lua.runtime.special', { ['xx.assert'] = 'assert' }) TEST 'number' [[ ---@type number? local t xx.assert(t) print() ]] config.set(nil, 'Lua.runtime.special', nil) TEST 'A' [[ ---@class A local mt ---@return function mt:init() end ]] TEST 'A' [[ ---@class A local mt ---@return self function mt:init() end local = mt:init() ]] TEST 'A' [[ ---@class A ---@field x ]] TEST 'A' [[ ---@class A ---@field x self ---@type A local o print(o.) ]] TEST 'A' [[ ---@class A ---@overload fun(): self local A local = A() ]] TEST 'number' [[ ---@type table<'Test1', fun(x: number)> local t = { ["Test1"] = function() end, } ]] TEST 'number' [[ ---@type table<5, fun(x: number)> local t = { [5] = function() end, } ]] TEST 'number' [[ ---@type fun(x: number) local function f() end ]] TEST 'boolean' [[ ---@generic T: string | boolean | table ---@param x T ---@return T local function f(x) return x end local = f(true) ]] TEST 'number' [[ ---@class A ---@field [1] number ---@field [2] boolean local t local = t[1] ]] TEST 'boolean' [[ ---@class A ---@field [1] number ---@field [2] boolean local t local = t[2] ]] TEST 'N' [[ ---@class N: number local x if x == 0.1 then print() end ]] TEST 'vec3' [[ ---@class mat4 ---@operator mul(vec3): vec3 -- matrix * vector ---@operator mul(number): mat4 -- matrix * constant ---@class vec3: number ---@type mat4, vec3 local m, v local = m * v ]] TEST 'mat4' [[ ---@class mat4 ---@operator mul(number): mat4 -- matrix * constant ---@operator mul(vec3): vec3 -- matrix * vector ---@class vec3: number ---@type mat4, vec3 local m, v local = m * v ]] TEST 'A|B' [[ ---@class A ---@class B ---@type A|B local t if x then ---@cast t A else print() end ]] TEST 'A|B' [[ ---@class A ---@class B ---@type A|B local t if x then ---@cast t A elseif then end ]] TEST 'A|B' [[ ---@class A ---@class B ---@type A|B local t if x then ---@cast t A print(t) elseif then end ]] TEST 'A|B' [[ ---@class A ---@class B ---@type A|B local t if x then ---@cast t A print(t) elseif then ---@cast t A print(t) end ]] TEST 'function' [[ local function x() print() end ]] TEST 'number' [[ ---@type number? local x do if not x then return end end print() ]] TEST 'number' [[ ---@type number[] local xs ---@type fun(x): number? local f for _, in ipairs(xs) do x = f(x) end ]] TEST 'number' [[ ---@type number? X = Y if X then print() end ]] TEST 'number' [[ ---@type number|boolean X = Y if type(X) == 'number' then print() end ]] TEST 'boolean' [[ ---@type number|boolean X = Y if type(X) ~= 'number' then print() end ]] TEST 'boolean' [[ ---@type number X = Y ---@cast X boolean print() ]] TEST 'number' [[ ---@type number local t if xxx == then print(t) end ]] TEST 'V' [[ ---@class V X = 1 print() ]] TEST 'V' [[ ---@class V X.Y = 1 print(X.) ]] TEST 'integer' [[ local x = {} x.y = 1 local y = x.y x.y = nil print() ]] TEST 'function' [[ function X() () end function Y() end ]]