summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/command/auto-require.lua5
-rw-r--r--test/completion/common.lua37
-rw-r--r--test/definition/luadoc.lua6
-rw-r--r--test/definition/method.lua26
-rw-r--r--test/diagnostics/common.lua185
-rw-r--r--test/full/init.lua13
-rw-r--r--test/full/self.lua39
-rw-r--r--test/hover/init.lua55
-rw-r--r--test/tclient/init.lua1
-rw-r--r--test/tclient/tests/resolve-completion.lua58
-rw-r--r--test/type_inference/init.lua837
11 files changed, 1214 insertions, 48 deletions
diff --git a/test/command/auto-require.lua b/test/command/auto-require.lua
index 9dd19bd2..38cc0012 100644
--- a/test/command/auto-require.lua
+++ b/test/command/auto-require.lua
@@ -3,9 +3,12 @@ local files = require 'files'
local autoRequire = require 'core.command.autoRequire'
local client = require 'client'
-local findInsertRow = util.getUpvalue(autoRequire, 'findInsertRow')
+local findInsertRow = util.getUpvalue(autoRequire, 'findInsertRow')
local applyAutoRequire = util.getUpvalue(autoRequire, 'applyAutoRequire')
+assert(findInsertRow)
+assert(applyAutoRequire)
+
local originEditText = client.editText
local EditResult
diff --git a/test/completion/common.lua b/test/completion/common.lua
index 538ac401..613f9b0c 100644
--- a/test/completion/common.lua
+++ b/test/completion/common.lua
@@ -3311,3 +3311,40 @@ TEST [[
end
assert(count == 1)
end)
+
+TEST [[
+local x
+x.y.z = xxx
+
+x.y.<??>
+]]
+{
+ {
+ label = 'z',
+ kind = define.CompletionItemKind.Field,
+ }
+}
+
+TEST [[
+local xyz
+
+---@cast <??>
+]]
+{
+ {
+ label = 'xyz',
+ kind = define.CompletionItemKind.Variable,
+ },
+}
+
+TEST [[
+local xyz
+
+---@cast x<??>
+]]
+{
+ {
+ label = 'xyz',
+ kind = define.CompletionItemKind.Variable,
+ }
+}
diff --git a/test/definition/luadoc.lua b/test/definition/luadoc.lua
index 19b4421b..47859b15 100644
--- a/test/definition/luadoc.lua
+++ b/test/definition/luadoc.lua
@@ -893,3 +893,9 @@ TEST [[
---@type XXX<<?YYY?>>
]]
+
+TEST [[
+local <!x!>
+
+---@cast <?x?> integer
+]]
diff --git a/test/definition/method.lua b/test/definition/method.lua
index aa7aacdc..e4368edb 100644
--- a/test/definition/method.lua
+++ b/test/definition/method.lua
@@ -29,3 +29,29 @@ end
function mt:<!m4!>()
end
]]
+
+TEST [[
+local mt
+
+function mt:f()
+ self.<!x!> = 1
+end
+
+mt.<?x?>
+]]
+
+TEST [[
+function G:f()
+ self.<!x!> = 1
+end
+
+G.<?x?>
+]]
+
+TEST [[
+function G.H:f()
+ self.<!x!> = 1
+end
+
+G.H.<?x?>
+]]
diff --git a/test/diagnostics/common.lua b/test/diagnostics/common.lua
index 56ad0d59..56d1cbcc 100644
--- a/test/diagnostics/common.lua
+++ b/test/diagnostics/common.lua
@@ -163,7 +163,7 @@ print()
]]
TEST [[
-pairs
+print
{}
{}
]]
@@ -218,6 +218,12 @@ 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
@@ -242,6 +248,66 @@ 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 [[
+---@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
@@ -273,6 +339,14 @@ TEST [[
local _ <close> = <!1!>
]]
+TEST [[
+local _ <close> = <!''!>
+]]
+
+TEST [[
+local c <close> = <!(function () return 1 end)()!>
+]]
+
config.get(nil, 'Lua.diagnostics.disable')['unused-local'] = true
TEST [[
local f = <!function () end!>
@@ -286,6 +360,21 @@ TEST [[
local <!function f() end!>
]]
+TEST [[
+local <!function f()
+ f()
+end!>
+]]
+
+
+TEST [[
+local <!function test()
+end!>
+
+local <!function foo ()
+end!>
+]]
+
config.get(nil, 'Lua.diagnostics.disable')['unused-local'] = nil
TEST [[
local mt, x
@@ -407,11 +496,13 @@ _G.bb = 1
TEST [[
local f = load('')
-f(1, 2, 3)
+if f then
+ f(1, 2, 3)
+end
]]
TEST [[
-local _ = <!unpack!>()
+local _ = <!unpack!>
]]
TEST [[
@@ -494,11 +585,10 @@ _ = 1, <!2!>
]]
TEST [[
-local function x()
+function X()
do
local k
print(k)
- x()
end
local k = 1
print(k)
@@ -506,9 +596,8 @@ end
]]
TEST [[
-local function x()
+function X()
local loc
- x()
print(loc)
end
]]
@@ -708,7 +797,9 @@ TEST [[
local function f(x, y)
return x, y
end
-f()
+
+local _
+f(_, _)
]]
TEST [[
@@ -741,7 +832,7 @@ TEST [[
TEST [[
---@type fun(a: integer)
local f
-f()
+f(1)
]]
TEST [[
@@ -792,7 +883,7 @@ local mt2 = {}
---@type Foo
local v
print(v.field1 + 1)
-print(v.<!field2!> + 1)
+print(v.field2 + 1)
print(v.<!field3!> + 1)
print(v:method1())
print(v.method2())
@@ -801,7 +892,7 @@ print(v:<!method3!>())
---@type Bar
local v2
print(v2.field1 + 1)
-print(v2.<!field2!> + 1)
+print(v2.field2 + 1)
print(v2.<!field3!> + 1)
print(v2.field4 + 1)
print(v2:method1())
@@ -868,7 +959,7 @@ TEST [[
local mt
function mt:method1()
mt.<!method2!>() -- doc.class
- self.method1()
+ self:method1()
return self.<!method2!>() -- doc.class.name
end
]]
@@ -978,7 +1069,7 @@ return m
TEST [[
local m = {}
-m.x = io.open()
+m.x = io.open('')
m.x = nil
return m
@@ -1379,13 +1470,13 @@ TEST [[
]]
TEST [[
-return ('1'):gsub()
+return ('1'):upper()
]]
TEST [[
local value
value = '1'
-value = value:gsub()
+value = value:upper()
]]
TEST [[
@@ -1395,3 +1486,67 @@ 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 [[
+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/full/init.lua b/test/full/init.lua
index e83a7d6d..3b1d2fe2 100644
--- a/test/full/init.lua
+++ b/test/full/init.lua
@@ -27,8 +27,17 @@ require 'full.dirty'
require 'full.projects'
require 'full.self'
+local times = {}
for name, time in util.sortPairs(DIAGTIMES, function (k1, k2)
- return DIAGTIMES[k1] < DIAGTIMES[k2]
+ return DIAGTIMES[k1] > DIAGTIMES[k2]
end) do
- print('诊断任务耗时:', name, time)
+ times[#times+1] = ('诊断任务耗时:%05.3f [%s]'):format(time, name)
+ if #times >= 10 then
+ break
+ end
+end
+
+util.revertTable(times)
+for _, time in ipairs(times) do
+ print(time)
end
diff --git a/test/full/self.lua b/test/full/self.lua
index 93cfe715..cfa6b710 100644
--- a/test/full/self.lua
+++ b/test/full/self.lua
@@ -5,11 +5,14 @@ local diag = require 'provider.diagnostic'
local config = require 'config'
local ws = require 'workspace'
local guide = require 'parser.guide'
+local vm = require 'vm'
+local util = require 'utility'
local path = ROOT / 'script'
local uris = {}
+files.reset()
fsu.scanDirectory(path, function (path)
if path:extension():string() ~= '.lua' then
return
@@ -47,3 +50,39 @@ end
local passed = os.clock() - clock
print('基准全量诊断用时:', passed)
+
+vm.clearNodeCache()
+
+local clock = os.clock()
+local compileDatas = {}
+
+for uri in files.eachFile() do
+ local state = files.getState(uri)
+ local clock = os.clock()
+ guide.eachSource(state.ast, function (src)
+ vm.compileNode(src)
+ end)
+ compileDatas[uri] = {
+ passed = os.clock() - clock,
+ uri = uri,
+ }
+end
+
+local printTexts = {}
+for uri, data in util.sortPairs(compileDatas, function (a, b)
+ return compileDatas[a].passed > compileDatas[b].passed
+end) do
+ printTexts[#printTexts+1] = ('全量编译耗时:%05.3f [%s]'):format(data.passed, uri)
+ if #printTexts >= 10 then
+ break
+ end
+end
+
+util.revertTable(printTexts)
+
+for _, text in ipairs(printTexts) do
+ print(text)
+end
+
+local passed = os.clock() - clock
+print('基准全量编译用时:', passed)
diff --git a/test/hover/init.lua b/test/hover/init.lua
index ee66ef2b..dc725f6c 100644
--- a/test/hover/init.lua
+++ b/test/hover/init.lua
@@ -280,8 +280,8 @@ TEST [[
]]
[=[
function load(chunk: string|function, chunkname?: string, mode?: "b"|"bt"|"t", env?: table)
- -> function
- 2. error_message: string
+ -> function?
+ 2. error_message: string?
]=]
TEST [[
@@ -504,10 +504,10 @@ local <?self?> = setmetatable({
]]
[[
local self: {
- __index: table,
- __name: string = "obj",
id: integer = 1,
remove: function,
+ __index: table,
+ __name: string = "obj",
}
]]
@@ -772,7 +772,7 @@ local <?t?> = {
]]
[[
local t: {
- f: file*,
+ f?: file*,
}
]]
@@ -790,8 +790,6 @@ TEST [[
]]
[[
(global) _G: _G {
- _G: _G,
- _VERSION: string = "Lua 5.4",
arg: string[],
assert: function,
collectgarbage: function,
@@ -810,6 +808,8 @@ TEST [[
module: function,
newproxy: function,
next: function,
+ os: oslib,
+ package: packagelib,
...(+22)
}
]]
@@ -1733,18 +1733,18 @@ t.<?x?>()
(field) t.x: unknown
]]
-TEST [[
----@class A
-local a
-
-local b
-b = a
-
-print(b.<?x?>)
-]]
-[[
-(field) A.x: unknown
-]]
+--TEST [[
+-----@class A
+--local a
+--
+--local b
+--b = a
+--
+--print(b.<?x?>)
+--]]
+--[[
+--(field) A.x: unknown
+--]]
TEST [[
---@return nil
@@ -1851,6 +1851,23 @@ local x: {
]]
TEST [[
+local <?x?> = {
+ _x = '',
+ _y = '',
+ x = '',
+ y = '',
+}
+]]
+[[
+local x: {
+ x: string = "",
+ y: string = "",
+ _x: string = "",
+ _y: string = "",
+}
+]]
+
+TEST [[
---@class A
---@field x string
diff --git a/test/tclient/init.lua b/test/tclient/init.lua
index 7c8d70ef..9e1db8d4 100644
--- a/test/tclient/init.lua
+++ b/test/tclient/init.lua
@@ -4,3 +4,4 @@ require 'tclient.tests.multi-workspace'
require 'tclient.tests.folders-with-single-file'
require 'tclient.tests.load-library'
require 'tclient.tests.files-associations'
+require 'tclient.tests.resolve-completion'
diff --git a/test/tclient/tests/resolve-completion.lua b/test/tclient/tests/resolve-completion.lua
new file mode 100644
index 00000000..a7cf2c2f
--- /dev/null
+++ b/test/tclient/tests/resolve-completion.lua
@@ -0,0 +1,58 @@
+local lclient = require 'lclient'
+local ws = require 'workspace'
+local util = require 'utility'
+
+---@async
+lclient():start(function (client)
+ client:registerFakers()
+ client:initialize()
+
+ client:notify('textDocument/didOpen', {
+ textDocument = {
+ uri = 'file://test.lua',
+ languageId = 'lua',
+ version = 0,
+ text = [[
+---@type integer
+local xxxx
+
+x
+]]
+ }
+ })
+
+ ws.awaitReady()
+
+ local completions = client:awaitRequest('textDocument/completion', {
+ textDocument = { uri = 'file://test.lua' },
+ position = { line = 3, character = 1 },
+ })
+
+ client:awaitRequest('textDocument/didChange',
+ {
+ textDocument = { uri = 'file://test.lua' },
+ contentChanges = {
+ {
+ range = {
+ start = { line = 3, character = 1 },
+ ['end'] = { line = 3, character = 1 },
+ },
+ text = 'x'
+ }
+ }
+ })
+
+ local targetItem
+ for _, item in ipairs(completions.items) do
+ if item.label == 'xxxx' then
+ targetItem = item
+ break
+ end
+ end
+
+ assert(targetItem ~= nil)
+
+ local newItem = client:awaitRequest('completionItem/resolve', targetItem)
+
+ assert(newItem.detail == 'integer')
+end)
diff --git a/test/type_inference/init.lua b/test/type_inference/init.lua
index 9ead2861..b71cf987 100644
--- a/test/type_inference/init.lua
+++ b/test/type_inference/init.lua
@@ -1,8 +1,8 @@
local files = require 'files'
local guide = require 'parser.guide'
-local infer = require 'vm.infer'
local config = require 'config'
local catch = require 'catch'
+local vm = require 'vm'
rawset(_G, 'TEST', true)
@@ -31,9 +31,9 @@ function TEST(wanted)
files.setText('', newScript)
local source = getSource(catched['?'][1][1])
assert(source)
- local result = infer.getInfer(source):view()
+ local result = vm.getInfer(source):view()
if wanted ~= result then
- infer.getInfer(source):view()
+ vm.getInfer(source):view()
end
assert(wanted == result)
files.remove('')
@@ -77,12 +77,6 @@ function f(<?x?>)
end
]]
-TEST 'number' [[
-local <?var?>
-var = 1
-var = 1.0
-]]
-
TEST 'string' [[
local var = '111'
t.<?x?> = var
@@ -95,6 +89,11 @@ var = '111'
TEST 'string' [[
local var
+<?var?> = '111'
+]]
+
+TEST 'string' [[
+local var
var = '111'
print(<?var?>)
]]
@@ -872,6 +871,37 @@ 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<integer, boolean>
+local t
+
+for _, <?v?> 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 _, <?v?> in ipairs(t) do
+end
+]]
+
+TEST 'boolean' [[
---@generic T: table, K, V
---@param t T
---@return fun(table: table<K, V>, index: K):K, V
@@ -1127,6 +1157,29 @@ local t = f('')
print(t.<?x?>)
]]
+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 (<?v?>) end)
+]]
+
+TEST 'unknown' [[
+---@generic T
+---@param t T[]
+---@param callback fun(v: T)
+local function f(t, callback) end
+
+local t = {}
+
+f(t, function (<?v?>) end)
+]]
+
TEST 'table' [[
local <?t?> = setmetatable({}, { __index = function () end })
]]
@@ -1216,14 +1269,14 @@ TEST '👍' [[
local <?x?>
]]
-TEST 'boolean' [[
+TEST 'integer' [[
---@type boolean
local x
<?x?> = 1
]]
-TEST 'Class' [[
+TEST 'integer' [[
---@class Class
local x
@@ -1482,3 +1535,765 @@ function mt:f()
print(<?self?>)
end
]]
+
+TEST 'string?' [[
+---@return string?
+local function f() end
+
+local <?x?> = f()
+]]
+
+TEST 'AA' [[
+---@class AA
+---@overload fun():AA
+local AAA
+
+
+local <?x?> = AAA()
+]]
+
+TEST 'AA' [[
+---@class AA
+---@overload fun():AA
+AAA = {}
+
+
+local <?x?> = AAA()
+]]
+
+TEST 'AA' [[
+---@overload fun():AA
+AAA.BBB = {}
+
+
+local <?x?> = AAA.BBB()
+]]
+
+TEST 'AA' [[
+local AAA
+
+---@overload fun():AA
+AAA.BBB = {}
+
+
+local <?x?> = AAA.BBB()
+]]
+
+TEST 'string|integer' [[
+local <?x?>
+x = '1'
+x = 1
+]]
+
+TEST 'string' [[
+local x
+<?x?> = '1'
+x = 1
+]]
+
+TEST 'integer' [[
+local x
+x = '1'
+<?x?> = 1
+]]
+
+TEST 'unknown' [[
+local x
+print(<?x?>)
+x = '1'
+x = 1
+]]
+
+TEST 'string' [[
+local x
+x = '1'
+print(<?x?>)
+x = 1
+]]
+
+TEST 'integer' [[
+local x
+x = '1'
+x = 1
+print(<?x?>)
+]]
+
+TEST 'unknown' [[
+local x
+
+function A()
+ print(<?x?>)
+end
+]]
+
+TEST 'unknown' [[
+local x
+
+function A()
+ print(<?x?>)
+end
+
+x = '1'
+x = 1
+]]
+
+TEST 'string' [[
+local x
+
+x = '1'
+
+function A()
+ print(<?x?>)
+end
+
+x = 1
+]]
+
+TEST 'integer' [[
+local x
+
+x = '1'
+x = 1
+
+function A()
+ print(<?x?>)
+end
+
+]]
+
+TEST 'boolean' [[
+local x
+
+function A()
+ x = true
+ print(<?x?>)
+end
+
+x = '1'
+x = 1
+]]
+
+TEST 'unknown' [[
+local x
+
+function A()
+ x = true
+end
+
+print(<?x?>)
+x = '1'
+x = 1
+]]
+
+TEST 'boolean' [[
+local x
+
+function A()
+ x = true
+ function B()
+ print(<?x?>)
+ end
+end
+
+x = '1'
+x = 1
+]]
+
+TEST 'table' [[
+local x
+
+function A()
+ x = true
+ function B()
+ x = {}
+ print(<?x?>)
+ end
+end
+
+x = '1'
+x = 1
+]]
+
+TEST 'boolean' [[
+local x
+
+function A()
+ x = true
+ function B()
+ x = {}
+ end
+ print(<?x?>)
+end
+
+x = '1'
+x = 1
+]]
+
+TEST 'unknown' [[
+local x
+
+function A()
+ x = true
+ function B()
+ x = {}
+ end
+end
+
+function C()
+ print(<?x?>)
+end
+
+x = '1'
+x = 1
+]]
+
+TEST 'integer?' [[
+---@type integer?
+local <?x?>
+]]
+
+TEST 'integer?' [[
+---@type integer?
+local x
+
+if <?x?> 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(<?x?>)
+end
+]]
+
+TEST 'integer?' [[
+---@type integer?
+local x
+
+if x then
+ print(x)
+end
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+if not x then
+ x = 1
+end
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+if xxx and x then
+ print(<?x?>)
+end
+]]
+
+TEST 'integer?' [[
+---@type integer?
+local x
+
+if xxx and x then
+end
+
+print(<?x?>)
+]]
+
+TEST 'integer?' [[
+---@type integer?
+local x
+
+if xxx and x then
+ return
+end
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+if x ~= nil then
+ print(<?x?>)
+end
+
+print(x)
+]]
+
+TEST 'integer|nil' [[
+---@type integer?
+local x
+
+if x ~= nil then
+ print(x)
+end
+
+print(<?x?>)
+]]
+
+TEST 'nil' [[
+---@type integer?
+local x
+
+if x == nil then
+ print(<?x?>)
+end
+
+print(x)
+]]
+
+TEST 'integer|nil' [[
+---@type integer?
+local x
+
+if x == nil then
+ print(x)
+end
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+<?x?> = x or 1
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+<?x?> = x or y
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+if not x then
+ return
+end
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+if not x then
+ goto ANYWHERE
+end
+
+print(<?x?>)
+]]
+
+TEST 'integer' [=[
+local x
+
+print(<?x?>--[[@as integer]])
+]=]
+
+TEST 'integer' [=[
+print(<?io?>--[[@as integer]])
+]=]
+
+TEST 'integer' [=[
+print(io.<?open?>--[[@as integer]])
+]=]
+
+TEST 'integer' [=[
+local <?x?> = io['open']--[[@as integer]])
+]=]
+
+TEST 'integer' [=[
+local <?x?> = 1 + 1--[[@as integer]])
+]=]
+
+TEST 'integer' [=[
+local <?x?> = not 1--[[@as integer]])
+]=]
+
+TEST 'integer' [=[
+local <?x?> = ()--[[@as integer]])
+]=]
+
+TEST 'integer?' [[
+---@param x? integer
+local function f(<?x?>)
+
+end
+]]
+
+TEST 'integer' [[
+local x = 1
+x = <?x?>
+]]
+
+TEST 'integer?' [[
+---@class A
+---@field x? integer
+local t
+
+t.<?x?>
+]]
+
+TEST 'integer?' [[
+---@type { x?: integer }
+local t
+
+t.<?x?>
+]]
+
+TEST 'boolean' [[
+---@class A
+---@field [integer] boolean
+local t
+
+local <?x?> = t[1]
+]]
+
+TEST 'unknown' [[
+local <?x?> = y and z
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+assert(x)
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+assert(x ~= nil)
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+---@type integer | nil
+local x
+
+assert(x)
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+---@type integer | nil
+local x
+
+assert(x ~= nil)
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+local x
+
+assert(x == 1)
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+if x and <?x?>.y then
+end
+]]
+
+TEST 'integer?' [[
+---@type integer?
+local x
+
+if x and x.y then
+end
+
+print(<?x?>)
+]]
+
+TEST 'integer?' [[
+---@type integer?
+local x
+
+if x and x.y then
+ return
+end
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+if not x or <?x?>.y then
+end
+]]
+
+TEST 'integer?' [[
+---@type integer?
+local x
+
+if not x or x.y then
+ print(<?x?>)
+end
+]]
+
+TEST 'integer?' [[
+---@type integer?
+local x
+
+if x or x.y then
+ print(<?x?>)
+end
+]]
+
+TEST 'integer?' [[
+---@type integer?
+local x
+
+if x.y or x then
+ print(<?x?>)
+end
+]]
+
+TEST 'integer?' [[
+---@type integer?
+local x
+
+if x.y or not x then
+ print(<?x?>)
+end
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+if not x or not y then
+ return
+end
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+if not y or not x then
+ return
+end
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+while true do
+ if not x then
+ break
+ end
+ print(<?x?>)
+end
+]]
+
+TEST 'integer?' [[
+---@type integer?
+local x
+
+while true do
+ if not x then
+ break
+ end
+end
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+---@type fun():integer?
+local iter
+
+for <?x?> in iter do
+end
+]]
+
+TEST 'integer' [[
+local x
+
+---@type integer
+<?x?> = XXX
+]]
+
+TEST 'unknown' [[
+for _ = 1, 999 do
+ local <?x?>
+end
+]]
+
+TEST 'integer' [[
+local x
+
+---@cast x integer
+
+print(<?x?>)
+]]
+
+TEST 'unknown' [[
+local x
+
+---@cast x integer
+
+local x
+print(<?x?>)
+]]
+
+TEST 'unknown' [[
+local x
+
+if true then
+ local x
+ ---@cast x integer
+ print(x)
+end
+
+print(<?x?>)
+]]
+
+TEST 'boolean|integer' [[
+local x = 1
+
+---@cast x +boolean
+
+print(<?x?>)
+]]
+
+TEST 'boolean' [[
+---@type integer|boolean
+local x
+
+---@cast x -integer
+
+print(<?x?>)
+]]
+
+TEST 'boolean?' [[
+---@type boolean
+local x
+
+---@cast x +?
+
+print(<?x?>)
+]]
+
+TEST 'boolean' [[
+---@type boolean?
+local x
+
+---@cast x -?
+
+print(<?x?>)
+]]
+
+TEST 'string' [[
+---@type string?
+local x
+
+if not x then
+ return
+else
+ print(<?x?>)
+end
+
+print(x)
+]]
+
+TEST 'string' [[
+---@type string?
+local x
+
+if not x then
+ return
+else
+ print(x)
+end
+
+print(<?x?>)
+]]
+
+TEST 'true' [[
+---@type boolean | nil
+local x
+
+if not x then
+ return
+end
+
+print(<?x?>)
+]]
+
+TEST 'true' [[
+---@type boolean
+local t
+
+if t then
+ print(<?t?>)
+ return
+end
+
+print(t)
+]]
+
+TEST 'false' [[
+---@type boolean
+local t
+
+if t then
+ print(t)
+ return
+end
+
+print(<?t?>)
+]]