diff options
author | carsakiller <carsakiller@gmail.com> | 2024-05-23 02:22:29 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-23 02:22:29 -0400 |
commit | 459ac5c06ecca35d135217ca0892cc7787a91d33 (patch) | |
tree | 6b4891e4c6dae06dfdd10d24f6c213bed24bd8fe | |
parent | b9c9f9423ff31bb9160acc5d75feaec3c0bf777c (diff) | |
parent | 87abc4245f2a24e1cc35851b6464af9588934286 (diff) | |
download | lua-language-server-459ac5c06ecca35d135217ca0892cc7787a91d33.zip |
Merge branch 'master' into 2175-md-symbol-reference
51 files changed, 5319 insertions, 4511 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 01b377ca..bfedcd50 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,7 +25,7 @@ jobs: include: - { os: ubuntu-22.04, target: linux, platform: linux-x64, container: 'alpine:latest', libc: musl } - { os: ubuntu-20.04, target: linux, platform: linux-x64 } - #- { os: ubuntu-20.04, target: linux, platform: linux-arm64 } + - { os: ubuntu-20.04, target: linux, platform: linux-arm64 } - { os: macos-11, target: darwin, platform: darwin-x64 } - { os: macos-11, target: darwin, platform: darwin-arm64 } - { os: windows-latest, target: windows, platform: win32-ia32 } @@ -34,6 +34,12 @@ jobs: container: image: ${{ matrix.container }} steps: + - name: Install aarch64-linux-gnu + if: ${{ matrix.platform == 'linux-arm64' && matrix.libc != 'musl' }} + run: | + sudo apt-get update + sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu + - name: Prepare container for musl if: ${{ matrix.target == 'linux' && matrix.libc == 'musl' }} run: | @@ -45,17 +51,23 @@ jobs: submodules: recursive - name: Build for others step-1 - if: ${{ matrix.libc != 'musl' }} + if: ${{ matrix.platform != 'linux-x64' }} uses: actboy168/setup-luamake@master - name: Build for others step-2 - if: ${{ matrix.libc != 'musl' }} + if: ${{ matrix.platform != 'linux-x64' }} run: luamake -platform ${{ matrix.platform }} - name: Build for musl - if: ${{ matrix.target == 'linux' && matrix.libc == 'musl' }} + if: ${{ matrix.platform == 'linux-x64' && matrix.libc == 'musl' }} run: ./make.sh + - name: Build for x64 glibc + if: ${{ matrix.platform == 'linux-x64' && matrix.libc != 'musl' }} + run: | + docker build -t ubuntu-18.04 . + docker run --rm -v $(pwd):$(pwd) -w $(pwd) ubuntu-18.04 bash -c './make.sh' + - name: Setting up workflow variables id: vars shell: bash diff --git a/3rd/bee.lua b/3rd/bee.lua -Subproject de3f4c85856f31b795f91fe69c20f8ca82d5791 +Subproject b4fda4e0865d4369c845015ab2ad45573d6ba24 diff --git a/3rd/love-api b/3rd/love-api -Subproject 728ba001f3398fd11b0a3909b919a7caf3e329a +Subproject 853639288547618dece86c3a8e52348fe304eba diff --git a/3rd/luamake b/3rd/luamake -Subproject bc906eb2de88a6f169633023a227478da5e9f2a +Subproject 62d729a6eb72dbb89547538ff9ab1876a5daf0f diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..ec57d0fe --- /dev/null +++ b/Dockerfile @@ -0,0 +1,14 @@ +# Dockerfile + +FROM ubuntu:18.04 + +# Install necessary packages +RUN apt-get update && \ + apt-get install -y software-properties-common && \ + add-apt-repository -y ppa:ubuntu-toolchain-r/test && \ + add-apt-repository -y ppa:git-core/ppa && \ + apt-get install -y git gcc-9 g++-9 wget tar gzip rsync ninja-build + +# Use alternative gcc +RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 100 && \ + update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-9 100 diff --git a/changelog.md b/changelog.md index 8f19fd0a..85c073d7 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,18 @@ # changelog +## 3.9.0 +`2024-5-11` +* `NEW` goto implementation +* `NEW` narrow the function prototype based on the parameter type + ```lua + ---@overload fun(a: boolean): A + ---@overload fun(a: number): B + local function f(...) end + + local r1 = f(true) --> r1 is `A` + local r2 = f(10) --> r2 is `B` + ``` + ## 3.8.3 `2024-4-23` * `FIX` server may crash when the workspace is using a non-English path. diff --git a/doc/en-us/config.md b/doc/en-us/config.md index f57d4d64..8dd8c059 100644 --- a/doc/en-us/config.md +++ b/doc/en-us/config.md @@ -406,6 +406,22 @@ Array<string> [] ``` +# diagnostics.globalsRegex + +Find defined global variables using regex. + +## type + +```ts +Array<string> +``` + +## default + +```jsonc +[] +``` + # diagnostics.groupFileStatus Modify the diagnostic needed file status in a group. diff --git a/doc/pt-br/config.md b/doc/pt-br/config.md index 7add2c98..7ee6c2ea 100644 --- a/doc/pt-br/config.md +++ b/doc/pt-br/config.md @@ -406,6 +406,22 @@ Array<string> [] ``` +# diagnostics.globalsRegex + +Find defined global variables using regex. + +## type + +```ts +Array<string> +``` + +## default + +```jsonc +[] +``` + # diagnostics.groupFileStatus Modify the diagnostic needed file status in a group. diff --git a/doc/zh-cn/config.md b/doc/zh-cn/config.md index ebb8325f..e7292b92 100644 --- a/doc/zh-cn/config.md +++ b/doc/zh-cn/config.md @@ -406,6 +406,22 @@ Array<string> [] ``` +# diagnostics.globalsRegex + +Find defined global variables using regex. + +## type + +```ts +Array<string> +``` + +## default + +```jsonc +[] +``` + # diagnostics.groupFileStatus 批量修改一个组中的文件状态。 diff --git a/doc/zh-tw/config.md b/doc/zh-tw/config.md index 8b01d78c..5e491a51 100644 --- a/doc/zh-tw/config.md +++ b/doc/zh-tw/config.md @@ -406,6 +406,22 @@ Array<string> [] ``` +# diagnostics.globalsRegex + +Find defined global variables using regex. + +## type + +```ts +Array<string> +``` + +## default + +```jsonc +[] +``` + # diagnostics.groupFileStatus 批量修改一個組中的檔案狀態。 diff --git a/locale/en-us/script.lua b/locale/en-us/script.lua index 49751d52..6fc488d8 100644 --- a/locale/en-us/script.lua +++ b/locale/en-us/script.lua @@ -650,6 +650,8 @@ CLI_CHECK_PROGRESS = 'Found {} problems in {} files' CLI_CHECK_RESULTS = 'Diagnosis complete, {} problems found, see {}' +CLI_CHECK_MULTIPLE_WORKERS = +'Starting {} worker tasks, progress output will be disabled. This may take a few minutes.' CLI_DOC_INITING = 'Loading documents ...' CLI_DOC_DONE = diff --git a/locale/en-us/setting.lua b/locale/en-us/setting.lua index 9ef46b86..eae37eae 100644 --- a/locale/en-us/setting.lua +++ b/locale/en-us/setting.lua @@ -48,6 +48,8 @@ config.diagnostics.disable = "Disabled diagnostic (Use code in hover brackets)." config.diagnostics.globals = "Defined global variables." +config.diagnostics.globalsRegex = +"Find defined global variables using regex." config.diagnostics.severity = [[ Modify the diagnostic severity. @@ -440,3 +442,7 @@ command.addon_manager.open = 'Lua: Open Addon Manager ...' command.reloadFFIMeta = 'Lua: Reload luajit ffi meta' +command.startServer = +'Lua: (debug) Start Language Server' +command.stopServer = +'Lua: (debug) Stop Language Server' diff --git a/locale/pt-br/script.lua b/locale/pt-br/script.lua index 7a114fef..468812cc 100644 --- a/locale/pt-br/script.lua +++ b/locale/pt-br/script.lua @@ -650,6 +650,8 @@ CLI_CHECK_PROGRESS = -- TODO: need translate! 'Found {} problems in {} files' CLI_CHECK_RESULTS = 'Diagnóstico completo, {} problemas encontrados, veja {}' +CLI_CHECK_MULTIPLE_WORKERS = -- TODO: need translate! +'Starting {} worker tasks, progress output will be disabled. This may take a few minutes.' CLI_DOC_INITING = -- TODO: need translate! 'Loading documents ...' CLI_DOC_DONE = -- TODO: need translate! diff --git a/locale/pt-br/setting.lua b/locale/pt-br/setting.lua index 6ececcd3..3f7ae657 100644 --- a/locale/pt-br/setting.lua +++ b/locale/pt-br/setting.lua @@ -48,6 +48,8 @@ config.diagnostics.disable = -- TODO: need translate! "Disabled diagnostic (Use code in hover brackets)." config.diagnostics.globals = -- TODO: need translate! "Defined global variables." +config.diagnostics.globalsRegex = -- TODO: need translate! +"Find defined global variables using regex." config.diagnostics.severity = -- TODO: need translate! [[ Modify the diagnostic severity. @@ -440,3 +442,7 @@ command.addon_manager.open = -- TODO: need translate! 'Lua: Open Addon Manager ...' command.reloadFFIMeta = -- TODO: need translate! 'Lua: Reload luajit ffi meta' +command.startServer = -- TODO: need translate! +'Lua: (debug) Start Language Server' +command.stopServer = -- TODO: need translate! +'Lua: (debug) Stop Language Server' diff --git a/locale/zh-cn/script.lua b/locale/zh-cn/script.lua index c16a764f..a4d20628 100644 --- a/locale/zh-cn/script.lua +++ b/locale/zh-cn/script.lua @@ -650,6 +650,8 @@ CLI_CHECK_PROGRESS = -- TODO: need translate! 'Found {} problems in {} files' CLI_CHECK_RESULTS = '诊断完成,共有 {} 个问题,请查看 {}' +CLI_CHECK_MULTIPLE_WORKERS = -- TODO: need translate! +'Starting {} worker tasks, progress output will be disabled. This may take a few minutes.' CLI_DOC_INITING = '加载文档 ...' CLI_DOC_DONE = diff --git a/locale/zh-cn/setting.lua b/locale/zh-cn/setting.lua index 78e7fb68..9c6e9a25 100644 --- a/locale/zh-cn/setting.lua +++ b/locale/zh-cn/setting.lua @@ -48,6 +48,8 @@ config.diagnostics.disable = "禁用的诊断(使用浮框括号内的代码)。" config.diagnostics.globals = "已定义的全局变量。" +config.diagnostics.globalsRegex = -- TODO: need translate! +"Find defined global variables using regex." config.diagnostics.severity = [[ 修改诊断等级。 @@ -439,3 +441,7 @@ command.addon_manager.open = 'Lua: 打开插件管理器...' command.reloadFFIMeta = 'Lua: 重新生成luajit的FFI模块C语言元数据' +command.startServer = +'Lua: (debug) 启动服务器' +command.stopServer = +'Lua: (debug) 停止服务器' diff --git a/locale/zh-tw/script.lua b/locale/zh-tw/script.lua index 1deb9877..c17c41fb 100644 --- a/locale/zh-tw/script.lua +++ b/locale/zh-tw/script.lua @@ -650,6 +650,8 @@ CLI_CHECK_PROGRESS = -- TODO: need translate! 'Found {} problems in {} files' CLI_CHECK_RESULTS = '診斷完成,共有 {} 個問題,請查看 {}' +CLI_CHECK_MULTIPLE_WORKERS = -- TODO: need translate! +'Starting {} worker tasks, progress output will be disabled. This may take a few minutes.' CLI_DOC_INITING = -- TODO: need translate! 'Loading documents ...' CLI_DOC_DONE = -- TODO: need translate! diff --git a/locale/zh-tw/setting.lua b/locale/zh-tw/setting.lua index 2b43e954..f15e2b4f 100644 --- a/locale/zh-tw/setting.lua +++ b/locale/zh-tw/setting.lua @@ -48,6 +48,8 @@ config.diagnostics.disable = "停用的診斷(使用浮框括號內的程式碼)。" config.diagnostics.globals = "已定義的全域變數。" +config.diagnostics.globalsRegex = -- TODO: need translate! +"Find defined global variables using regex." config.diagnostics.severity = [[ 修改診斷等級。 @@ -439,3 +441,7 @@ command.addon_manager.open = -- TODO: need translate! 'Lua: Open Addon Manager ...' command.reloadFFIMeta = -- TODO: need translate! 'Lua: Reload luajit ffi meta' +command.startServer = -- TODO: need translate! +'Lua: (debug) Start Language Server' +command.stopServer = -- TODO: need translate! +'Lua: (debug) Stop Language Server' @@ -2,6 +2,8 @@ local fs = require 'bee.filesystem' local util = require 'utility' local version = require 'version' +require 'config.env' + local function getValue(value) if value == 'true' or value == nil then value = true diff --git a/script/brave/brave.lua b/script/brave/brave.lua index 5a15a6b2..34c92a72 100644 --- a/script/brave/brave.lua +++ b/script/brave/brave.lua @@ -1,5 +1,6 @@ local channel = require 'bee.channel' local select = require 'bee.select' +local thread = require 'bee.thread' local function channel_init(chan) local selector = select.create() @@ -9,11 +10,14 @@ end local function channel_bpop(ctx) local selector, chan = ctx[1], ctx[2] - for _ in selector:wait() do - local r = table.pack(chan:pop()) - if r[1] == true then - return table.unpack(r, 2) + while true do + for _ in selector:wait() do + local r = table.pack(chan:pop()) + if r[1] == true then + return table.unpack(r, 2) + end end + thread.sleep(10) end end diff --git a/script/cli/check.lua b/script/cli/check.lua index 3902c4aa..8b314f24 100644 --- a/script/cli/check.lua +++ b/script/cli/check.lua @@ -1,130 +1,96 @@ -local lclient = require 'lclient'() -local furi = require 'file-uri' -local ws = require 'workspace' -local files = require 'files' -local diag = require 'provider.diagnostic' -local util = require 'utility' -local jsonb = require 'json-beautify' -local lang = require 'language' -local define = require 'proto.define' -local config = require 'config.config' -local fs = require 'bee.filesystem' -local provider = require 'provider' +local lang = require 'language' +local platform = require 'bee.platform' +local subprocess = require 'bee.subprocess' +local json = require 'json' +local jsonb = require 'json-beautify' +local util = require 'utility' -require 'plugin' -require 'vm' -lang(LOCALE) +local numThreads = tonumber(NUM_THREADS or 1) -if type(CHECK) ~= 'string' then - print(lang.script('CLI_CHECK_ERROR_TYPE', type(CHECK))) - return +local exe = arg[-1] +-- TODO: is this necessary? got it from the shell.lua helper in bee.lua tests +if platform.os == 'windows' and not exe:match('%.[eE][xX][eE]$') then + exe = exe..'.exe' end -local rootPath = fs.absolute(fs.path(CHECK)):string() -local rootUri = furi.encode(rootPath) -if not rootUri then - print(lang.script('CLI_CHECK_ERROR_URI', rootPath)) - return +local function logFileForThread(threadId) + return LOGPATH .. '/check-partial-' .. threadId .. '.json' end -rootUri = rootUri:gsub("/$", "") -if CHECKLEVEL then - if not define.DiagnosticSeverity[CHECKLEVEL] then - print(lang.script('CLI_CHECK_ERROR_LEVEL', 'Error, Warning, Information, Hint')) - return +local function buildArgs(threadId) + local args = {exe} + local skipNext = false + for i = 1, #arg do + local arg = arg[i] + -- --check needs to be transformed into --check_worker + if arg:lower():match('^%-%-check$') or arg:lower():match('^%-%-check=') then + args[#args + 1] = arg:gsub('%-%-%w*', '--check_worker') + -- --check_out_path needs to be removed if we have more than one thread + elseif arg:lower():match('%-%-check_out_path') and numThreads > 1 then + if not arg:match('%-%-%w*=') then + skipNext = true + end + else + if skipNext then + skipNext = false + else + args[#args + 1] = arg + end + end + end + args[#args + 1] = '--thread_id' + args[#args + 1] = tostring(threadId) + if numThreads > 1 then + args[#args + 1] = '--quiet' + args[#args + 1] = '--check_out_path' + args[#args + 1] = logFileForThread(threadId) end + return args end -local checkLevel = define.DiagnosticSeverity[CHECKLEVEL] or define.DiagnosticSeverity.Warning - -util.enableCloseFunction() -local lastClock = os.clock() -local results = {} - -local function errorhandler(err) - print(err) - print(debug.traceback()) +if numThreads > 1 then + print(lang.script('CLI_CHECK_MULTIPLE_WORKERS', numThreads)) end ----@async -xpcall(lclient.start, errorhandler, lclient, function (client) - client:registerFakers() - - client:initialize { - rootUri = rootUri, - } - - client:register('textDocument/publishDiagnostics', function (params) - results[params.uri] = params.diagnostics - end) - - io.write(lang.script('CLI_CHECK_INITING')) - - provider.updateConfig(rootUri) - - ws.awaitReady(rootUri) - - local disables = util.arrayToHash(config.get(rootUri, 'Lua.diagnostics.disable')) - for name, serverity in pairs(define.DiagnosticDefaultSeverity) do - serverity = config.get(rootUri, 'Lua.diagnostics.severity')[name] or 'Warning' - if serverity:sub(-1) == '!' then - serverity = serverity:sub(1, -2) - end - if define.DiagnosticSeverity[serverity] > checkLevel then - disables[name] = true - end +local procs = {} +for i = 1, numThreads do + local process, err = subprocess.spawn({buildArgs(i)}) + if err then + print(err) end - config.set(rootUri, 'Lua.diagnostics.disable', util.getTableKeys(disables, true)) - - local uris = files.getChildFiles(rootUri) - local max = #uris - for i, uri in ipairs(uris) do - files.open(uri) - diag.doDiagnostic(uri, true) - -- Print regularly but always print the last entry to ensure that logs written to files don't look incomplete. - if os.clock() - lastClock > 0.2 or i == #uris then - lastClock = os.clock() - client:update() - local output = '\x0D' - .. ('>'):rep(math.ceil(i / max * 20)) - .. ('='):rep(20 - math.ceil(i / max * 20)) - .. ' ' - .. ('0'):rep(#tostring(max) - #tostring(i)) - .. tostring(i) .. '/' .. tostring(max) - io.write(output) - local filesWithErrors = 0 - local errors = 0 - for _, diags in pairs(results) do - filesWithErrors = filesWithErrors + 1 - errors = errors + #diags - end - if errors > 0 then - local errorDetails = ' [' .. lang.script('CLI_CHECK_PROGRESS', errors, filesWithErrors) .. ']' - io.write(errorDetails) - end - io.flush() - end + if process then + procs[#procs + 1] = process end - io.write('\x0D') -end) +end -local count = 0 -for uri, result in pairs(results) do - count = count + #result - if #result == 0 then - results[uri] = nil - end +for _, process in ipairs(procs) do + process:wait() end -if count == 0 then - print(lang.script('CLI_CHECK_SUCCESS')) -else - local outpath = CHECK_OUT_PATH - if outpath == nil then - outpath = LOGPATH .. '/check.json' - end - util.saveFile(outpath, jsonb.beautify(results)) +local outpath = CHECK_OUT_PATH +if outpath == nil then + outpath = LOGPATH .. '/check.json' +end - print(lang.script('CLI_CHECK_RESULTS', count, outpath)) +if numThreads > 1 then + local mergedResults = {} + local count = 0 + for i = 1, numThreads do + local result = json.decode(util.loadFile(logFileForThread(i)) or '[]') + for k, v in pairs(result) do + local entries = mergedResults[k] or {} + mergedResults[k] = entries + for _, entry in ipairs(v) do + entries[#entries + 1] = entry + count = count + 1 + end + end + end + util.saveFile(outpath, jsonb.beautify(mergedResults)) + if count == 0 then + print(lang.script('CLI_CHECK_SUCCESS')) + else + print(lang.script('CLI_CHECK_RESULTS', count, outpath)) + end end diff --git a/script/cli/check_worker.lua b/script/cli/check_worker.lua new file mode 100644 index 00000000..0a121344 --- /dev/null +++ b/script/cli/check_worker.lua @@ -0,0 +1,168 @@ +local lclient = require 'lclient'() +local furi = require 'file-uri' +local ws = require 'workspace' +local files = require 'files' +local diag = require 'provider.diagnostic' +local util = require 'utility' +local jsonb = require 'json-beautify' +local lang = require 'language' +local define = require 'proto.define' +local protoDiag = require 'proto.diagnostic' +local config = require 'config.config' +local fs = require 'bee.filesystem' +local provider = require 'provider' +local await = require 'await' +require 'plugin' +require 'vm' + +lang(LOCALE) + +local numThreads = tonumber(NUM_THREADS or 1) +local threadId = tonumber(THREAD_ID or 1) + +if type(CHECK_WORKER) ~= 'string' then + print(lang.script('CLI_CHECK_ERROR_TYPE', type(CHECK_WORKER))) + return +end + +local rootPath = fs.absolute(fs.path(CHECK_WORKER)):string() +local rootUri = furi.encode(rootPath) +if not rootUri then + print(lang.script('CLI_CHECK_ERROR_URI', rootPath)) + return +end +rootUri = rootUri:gsub("/$", "") + +if CHECKLEVEL then + if not define.DiagnosticSeverity[CHECKLEVEL] then + print(lang.script('CLI_CHECK_ERROR_LEVEL', 'Error, Warning, Information, Hint')) + return + end +end +local checkLevel = define.DiagnosticSeverity[CHECKLEVEL] or define.DiagnosticSeverity.Warning + +util.enableCloseFunction() + +-- Hash function used to distribute work. +local function hashString(str) + local hash = 0 + for i = 1, #str do + hash = (hash * 37 & 0xFFFFFFFF) + str:byte(i, i) + end + return hash +end + +local lastClock = os.clock() +local results = {} + +local function errorhandler(err) + print(err) + print(debug.traceback()) +end + +---@async +xpcall(lclient.start, errorhandler, lclient, function (client) + await.disable() + client:registerFakers() + + client:initialize { + rootUri = rootUri, + } + + client:register('textDocument/publishDiagnostics', function (params) + results[params.uri] = params.diagnostics + end) + + if not QUIET then + io.write(lang.script('CLI_CHECK_INITING')) + end + + provider.updateConfig(rootUri) + + ws.awaitReady(rootUri) + + local disables = util.arrayToHash(config.get(rootUri, 'Lua.diagnostics.disable')) + for name, serverity in pairs(define.DiagnosticDefaultSeverity) do + serverity = config.get(rootUri, 'Lua.diagnostics.severity')[name] or 'Warning' + if serverity:sub(-1) == '!' then + serverity = serverity:sub(1, -2) + end + if define.DiagnosticSeverity[serverity] > checkLevel then + disables[name] = true + end + end + config.set(rootUri, 'Lua.diagnostics.disable', util.getTableKeys(disables, true)) + + -- Downgrade file opened status to Opened for everything to avoid reporting during compilation on files that do not belong to this thread + local diagStatus = config.get(rootUri, 'Lua.diagnostics.neededFileStatus') + for diag, status in pairs(diagStatus) do + if status == 'Any' or status == 'Any!' then + diagStatus[diag] = 'Opened!' + end + end + for diag, status in pairs(protoDiag.getDefaultStatus()) do + if status == 'Any' or status == 'Any!' then + diagStatus[diag] = 'Opened!' + end + end + config.set(rootUri, 'Lua.diagnostics.neededFileStatus', diagStatus) + + local uris = files.getChildFiles(rootUri) + local max = #uris + for i, uri in ipairs(uris) do + local hash = hashString(uri) % numThreads + 1 + if hash == threadId then + files.open(uri) + diag.doDiagnostic(uri, true) + -- Print regularly but always print the last entry to ensure that logs written to files don't look incomplete. + if (os.clock() - lastClock > 0.2 or i == #uris) and not QUIET then + lastClock = os.clock() + client:update() + local output = '\x0D' + .. ('>'):rep(math.ceil(i / max * 20)) + .. ('='):rep(20 - math.ceil(i / max * 20)) + .. ' ' + .. ('0'):rep(#tostring(max) - #tostring(i)) + .. tostring(i) .. '/' .. tostring(max) + io.write(output) + local filesWithErrors = 0 + local errors = 0 + for _, diags in pairs(results) do + filesWithErrors = filesWithErrors + 1 + errors = errors + #diags + end + if errors > 0 then + local errorDetails = ' [' .. lang.script('CLI_CHECK_PROGRESS', errors, filesWithErrors) .. ']' + io.write(errorDetails) + end + io.flush() + end + end + end + if not QUIET then + io.write('\x0D') + end +end) + +local count = 0 +for uri, result in pairs(results) do + count = count + #result + if #result == 0 then + results[uri] = nil + end +end + +local outpath = CHECK_OUT_PATH +if outpath == nil then + outpath = LOGPATH .. '/check.json' +end +-- Always write result, even if it's empty to make sure no one accidentally looks at an old output after a successful run. +util.saveFile(outpath, jsonb.beautify(results)) + +if not QUIET then + if count == 0 then + print(lang.script('CLI_CHECK_SUCCESS')) + else + print(lang.script('CLI_CHECK_RESULTS', count, outpath)) + end +end diff --git a/script/cli/init.lua b/script/cli/init.lua index d37c50ae..65e7e102 100644 --- a/script/cli/init.lua +++ b/script/cli/init.lua @@ -8,6 +8,11 @@ if _G['CHECK'] then os.exit(0, true) end +if _G['CHECK_WORKER'] then + require 'cli.check_worker' + os.exit(0, true) +end + if _G['DOC_UPDATE'] then require 'cli.doc' .runCLI() os.exit(0, true) diff --git a/script/config/env.lua b/script/config/env.lua new file mode 100644 index 00000000..ef5b31f2 --- /dev/null +++ b/script/config/env.lua @@ -0,0 +1,67 @@ +-- Handles loading environment arguments + +---Convert a string to boolean +---@param v string +local function strToBool(v) + return v == "true" +end + +---ENV args are defined here. +---- `name` is the ENV arg name +---- `key` is the value used to index `_G` for setting the argument +---- `converter` if present, will be used to convert the string value into another type +---@type { name: string, key: string, converter: fun(value: string): any }[] +local vars = { + { + name = "LLS_CHECK_LEVEL", + key = "CHECKLEVEL", + }, + { + name = "LLS_CHECK_PATH", + key = "CHECK", + }, + { + name = "LLS_CONFIG_PATH", + key = "CONFIGPATH", + }, + { + name = "LLS_DOC_OUT_PATH", + key = "DOC_OUT_PATH", + }, + { + name = "LLS_DOC_PATH", + key = "DOC", + }, + { + name = "LLS_FORCE_ACCEPT_WORKSPACE", + key = "FORCE_ACCEPT_WORKSPACE", + converter = strToBool, + }, + { + name = "LLS_LOCALE", + key = "LOCALE", + }, + { + name = "LLS_LOG_LEVEL", + key = "LOGLEVEL", + }, + { + name = "LLS_LOG_PATH", + key = "LOGPATH", + }, + { + name = "LLS_META_PATH", + key = "METAPATH", + }, +} + +for _, var in ipairs(vars) do + local value = os.getenv(var.name) + if value then + if var.converter then + value = var.converter(value) + end + + _G[var.key] = value + end +end diff --git a/script/config/template.lua b/script/config/template.lua index 49907419..e74a9f9c 100644 --- a/script/config/template.lua +++ b/script/config/template.lua @@ -242,6 +242,7 @@ local template = { >> util.deepCopy(define.BuiltIn), ['Lua.diagnostics.enable'] = Type.Boolean >> true, ['Lua.diagnostics.globals'] = Type.Array(Type.String), + ['Lua.diagnostics.globalsRegex'] = Type.Array(Type.String), ['Lua.diagnostics.disable'] = Type.Array(Type.String << util.getTableKeys(diag.getDiagAndErrNameMap(), true)), ['Lua.diagnostics.severity'] = Type.Hash( Type.String << util.getTableKeys(define.DiagnosticDefaultNeededFileStatus, true), diff --git a/script/core/diagnostics/duplicate-doc-field.lua b/script/core/diagnostics/duplicate-doc-field.lua index 098b41a4..6066ae53 100644 --- a/script/core/diagnostics/duplicate-doc-field.lua +++ b/script/core/diagnostics/duplicate-doc-field.lua @@ -15,7 +15,7 @@ local function getFieldEventName(doc) if docFunc.type ~= 'doc.type.function' then return nil end - for i = 1, 2 do + for i = 1, #docFunc.args do local arg = docFunc.args[i] if arg and arg.extends diff --git a/script/core/diagnostics/global-element.lua b/script/core/diagnostics/global-element.lua index e9dd46ce..a30ebbc6 100644 --- a/script/core/diagnostics/global-element.lua +++ b/script/core/diagnostics/global-element.lua @@ -17,6 +17,20 @@ local function isDocClass(source) return false end +local function isGlobalRegex(name, definedGlobalRegex) + if not definedGlobalRegex then + return false + end + + for _, pattern in ipairs(definedGlobalRegex) do + if name:match(pattern) then + return true + end + end + + return false +end + -- If global elements are discouraged by coding convention, this diagnostic helps with reminding about that -- Exceptions may be added to Lua.diagnostics.globals return function (uri, callback) @@ -26,6 +40,7 @@ return function (uri, callback) end local definedGlobal = util.arrayToHash(config.get(uri, 'Lua.diagnostics.globals')) + local definedGlobalRegex = config.get(uri, 'Lua.diagnostics.globalsRegex') guide.eachSourceType(ast.ast, 'setglobal', function (source) local name = guide.getKeyName(source) @@ -36,6 +51,9 @@ return function (uri, callback) if isDocClass(source) then return end + if isGlobalRegex(name, definedGlobalRegex) then + return + end if definedGlobal[name] == nil then definedGlobal[name] = false local global = vm.getGlobal('variable', name) diff --git a/script/core/diagnostics/lowercase-global.lua b/script/core/diagnostics/lowercase-global.lua index 68bec234..c7e9294d 100644 --- a/script/core/diagnostics/lowercase-global.lua +++ b/script/core/diagnostics/lowercase-global.lua @@ -17,6 +17,20 @@ local function isDocClass(source) return false end +local function isGlobalRegex(name, definedGlobalRegex) + if not definedGlobalRegex then + return false + end + + for _, pattern in ipairs(definedGlobalRegex) do + if name:match(pattern) then + return true + end + end + + return false +end + -- 不允许定义首字母小写的全局变量(很可能是拼错或者漏删) return function (uri, callback) local ast = files.getState(uri) @@ -25,6 +39,7 @@ return function (uri, callback) end local definedGlobal = util.arrayToHash(config.get(uri, 'Lua.diagnostics.globals')) + local definedGlobalRegex = config.get(uri, 'Lua.diagnostics.globalsRegex') guide.eachSourceType(ast.ast, 'setglobal', function (source) local name = guide.getKeyName(source) @@ -42,6 +57,9 @@ return function (uri, callback) if isDocClass(source) then return end + if isGlobalRegex(name, definedGlobalRegex) then + return + end if definedGlobal[name] == nil then definedGlobal[name] = false local global = vm.getGlobal('variable', name) diff --git a/script/core/implementation.lua b/script/core/implementation.lua new file mode 100644 index 00000000..e48e2f73 --- /dev/null +++ b/script/core/implementation.lua @@ -0,0 +1,171 @@ +local workspace = require 'workspace' +local files = require 'files' +local vm = require 'vm' +local findSource = require 'core.find-source' +local guide = require 'parser.guide' +local rpath = require 'workspace.require-path' +local jumpSource = require 'core.jump-source' + +local function sortResults(results) + -- 先按照顺序排序 + table.sort(results, function (a, b) + local u1 = guide.getUri(a.target) + local u2 = guide.getUri(b.target) + if u1 == u2 then + return a.target.start < b.target.start + else + return u1 < u2 + end + end) + -- 如果2个结果处于嵌套状态,则取范围小的那个 + local lf, lu + for i = #results, 1, -1 do + local res = results[i].target + local f = res.finish + local uri = guide.getUri(res) + if lf and f > lf and uri == lu then + table.remove(results, i) + else + lu = uri + lf = f + end + end +end + +local accept = { + ['local'] = true, + ['setlocal'] = true, + ['getlocal'] = true, + ['label'] = true, + ['goto'] = true, + ['field'] = true, + ['method'] = true, + ['setglobal'] = true, + ['getglobal'] = true, + ['string'] = true, + ['boolean'] = true, + ['number'] = true, + ['integer'] = true, + ['...'] = true, + + ['doc.type.name'] = true, + ['doc.class.name'] = true, + ['doc.extends.name'] = true, + ['doc.alias.name'] = true, + ['doc.cast.name'] = true, + ['doc.enum.name'] = true, + ['doc.field.name'] = true, +} + +local function convertIndex(source) + if not source then + return + end + if source.type == 'string' + or source.type == 'boolean' + or source.type == 'number' + or source.type == 'integer' then + local parent = source.parent + if not parent then + return + end + if parent.type == 'setindex' + or parent.type == 'getindex' + or parent.type == 'tableindex' then + return parent + end + end + return source +end + +---@async +return function (uri, offset) + local ast = files.getState(uri) + if not ast then + return nil + end + + local source = convertIndex(findSource(ast, offset, accept)) + if not source then + return nil + end + + local results = {} + + local defs = vm.getRefs(source) + + for _, src in ipairs(defs) do + if not guide.isAssign(src) then + goto CONTINUE + end + if src.type == 'global' then + goto CONTINUE + end + local root = guide.getRoot(src) + if not root then + goto CONTINUE + end + if src.type == 'self' then + goto CONTINUE + end + src = src.field or src.method or src + if src.type == 'getindex' + or src.type == 'setindex' + or src.type == 'tableindex' then + src = src.index + if not src then + goto CONTINUE + end + if not guide.isLiteral(src) then + goto CONTINUE + end + end + if src.type == 'doc.type.function' + or src.type == 'doc.type.table' + or src.type == 'doc.type.boolean' + or src.type == 'doc.type.integer' + or src.type == 'doc.type.string' then + goto CONTINUE + end + if src.type == 'doc.class' then + goto CONTINUE + end + if src.type == 'doc.alias' then + goto CONTINUE + end + if src.type == 'doc.enum' then + goto CONTINUE + end + if src.type == 'doc.type.field' then + goto CONTINUE + end + if src.type == 'doc.class.name' + or src.type == 'doc.alias.name' + or src.type == 'doc.enum.name' + or src.type == 'doc.field.name' then + goto CONTINUE + end + if src.type == 'doc.generic.name' then + goto CONTINUE + end + if src.type == 'doc.param' then + goto CONTINUE + end + + results[#results+1] = { + target = src, + uri = root.uri, + source = source, + } + ::CONTINUE:: + end + + if #results == 0 then + return nil + end + + sortResults(results) + jumpSource(results) + + return results +end diff --git a/script/core/signature.lua b/script/core/signature.lua index 98018b21..c52dcff3 100644 --- a/script/core/signature.lua +++ b/script/core/signature.lua @@ -94,10 +94,7 @@ local function isEventNotMatch(call, src) return false end local literal, index - for i = 1, 2 do - if not call.args[i] then - break - end + for i = 1, #call.args do literal = guide.getLiteral(call.args[i]) if literal then index = i diff --git a/script/core/type-definition.lua b/script/core/type-definition.lua index 0a821f25..d9939eb0 100644 --- a/script/core/type-definition.lua +++ b/script/core/type-definition.lua @@ -52,8 +52,9 @@ local accept = { ['doc.class.name'] = true, ['doc.extends.name'] = true, ['doc.alias.name'] = true, + ['doc.cast.name'] = true, ['doc.enum.name'] = true, - ['doc.see.name'] = true, + ['doc.field.name'] = true, } local function checkRequire(source, offset) diff --git a/script/files.lua b/script/files.lua index b9df5695..68c3b8a5 100644 --- a/script/files.lua +++ b/script/files.lua @@ -1,5 +1,6 @@ local platform = require 'bee.platform' local fs = require 'bee.filesystem' +local sys = require 'bee.sys' local config = require 'config' local glob = require 'glob' local furi = require 'file-uri' @@ -70,7 +71,7 @@ local function getRealParent(path) == path :string():gsub('^%w+:', string.lower) then return path end - local res = fs.fullpath(path) + local res = sys.fullpath(path) return getRealParent(parent) / res:filename() end diff --git a/script/filewatch.lua b/script/filewatch.lua index 6520afe6..7fb3e605 100644 --- a/script/filewatch.lua +++ b/script/filewatch.lua @@ -1,6 +1,7 @@ local fw = require 'bee.filewatch' local fs = require 'bee.filesystem' local plat = require 'bee.platform' +local sys = require 'bee.sys' local await = require 'await' local files = require 'files' @@ -16,7 +17,7 @@ local function isExists(filename) if plat.os ~= 'windows' then return true end - local res = fs.fullpath(path) + local res = sys.fullpath(path) if not res then return false end diff --git a/script/global.d.lua b/script/global.d.lua index ead46ca9..daac5f6c 100644 --- a/script/global.d.lua +++ b/script/global.d.lua @@ -98,3 +98,11 @@ FORCE_ACCEPT_WORKSPACE = false -- Trust all plugins that are being loaded by workspace config files. -- This is potentially unsafe for normal use and meant for usage in CI environments only. TRUST_ALL_PLUGINS = false + +NUM_THREADS = 1 + +THREAD_ID = 1 + +CHECK_WORKER = '' + +QUIET = false diff --git a/script/meta/bee/filesystem.lua b/script/meta/bee/filesystem.lua index 0c7e41a8..2a353065 100644 --- a/script/meta/bee/filesystem.lua +++ b/script/meta/bee/filesystem.lua @@ -1,4 +1,4 @@ ----@meta +---@meta bee.filesystem ---@class fs.path ---@operator div: fs.path @@ -75,11 +75,6 @@ end ---@param path fs.path ---@return fs.path -function fs.fullpath(path) -end - ----@param path fs.path ----@return fs.path function fs.absolute(path) end diff --git a/script/meta/bee/filewatch.lua b/script/meta/bee/filewatch.lua index b5211355..813548b2 100644 --- a/script/meta/bee/filewatch.lua +++ b/script/meta/bee/filewatch.lua @@ -1,4 +1,4 @@ ----@meta +---@meta bee.filewatch ---@class bee.filewatch.instance local instance = {} diff --git a/script/meta/bee/socket.lua b/script/meta/bee/socket.lua index 55c349a6..b5e3a7b2 100644 --- a/script/meta/bee/socket.lua +++ b/script/meta/bee/socket.lua @@ -1,4 +1,4 @@ ----@meta +---@meta bee.socket ---@alias bee.socket.protocol ---| 'tcp' diff --git a/script/meta/bee/sys.lua b/script/meta/bee/sys.lua new file mode 100644 index 00000000..ad14702c --- /dev/null +++ b/script/meta/bee/sys.lua @@ -0,0 +1,13 @@ +---@meta bee.sys + +---@class bee.sys +local sys = {} + +---@param path fs.path +---@return fs.path +function sys.fullpath(path) end + +---@return fs.path +function sys.exe_path() end + +return sys diff --git a/script/meta/bee/thread.lua b/script/meta/bee/thread.lua index 6b4323a4..15955aff 100644 --- a/script/meta/bee/thread.lua +++ b/script/meta/bee/thread.lua @@ -1,9 +1,9 @@ ----@meta +---@meta bee.thread ---@class bee.thread local thread = {} ----@param time number +---@param time integer function thread.sleep(time) end ---@param name string @@ -15,7 +15,10 @@ function thread.channel(name) end ---@param script string ---@return bee.thread.thread -function thread.thread(script) end +function thread.create(script) end + +---@return string? +function thread.errlog() end ---@class bee.thread.channel local channel = {} diff --git a/script/provider/provider.lua b/script/provider/provider.lua index 69fb3263..15e78b9a 100644 --- a/script/provider/provider.lua +++ b/script/provider/provider.lua @@ -368,6 +368,39 @@ m.register 'textDocument/hover' { end } +local function convertDefinitionResult(state, result) + local response = {} + for i, info in ipairs(result) do + ---@type uri + local targetUri = info.uri + if targetUri then + local targetState = files.getState(targetUri) + if targetState then + if client.getAbility 'textDocument.definition.linkSupport' then + response[i] = converter.locationLink(targetUri + , converter.packRange(targetState, info.target.start, info.target.finish) + , converter.packRange(targetState, info.target.start, info.target.finish) + , converter.packRange(state, info.source.start, info.source.finish) + ) + else + response[i] = converter.location(targetUri + , converter.packRange(targetState, info.target.start, info.target.finish) + ) + end + else + response[i] = converter.location( + targetUri, + converter.range( + converter.position(guide.rowColOf(info.target.start)), + converter.position(guide.rowColOf(info.target.finish)) + ) + ) + end + end + end + return response +end + m.register 'textDocument/definition' { capability = { definitionProvider = true, @@ -388,35 +421,7 @@ m.register 'textDocument/definition' { if not result then return nil end - local response = {} - for i, info in ipairs(result) do - ---@type uri - local targetUri = info.uri - if targetUri then - local targetState = files.getState(targetUri) - if targetState then - if client.getAbility 'textDocument.definition.linkSupport' then - response[i] = converter.locationLink(targetUri - , converter.packRange(targetState, info.target.start, info.target.finish) - , converter.packRange(targetState, info.target.start, info.target.finish) - , converter.packRange(state, info.source.start, info.source.finish) - ) - else - response[i] = converter.location(targetUri - , converter.packRange(targetState, info.target.start, info.target.finish) - ) - end - else - response[i] = converter.location( - targetUri, - converter.range( - converter.position(guide.rowColOf(info.target.start)), - converter.position(guide.rowColOf(info.target.finish)) - ) - ) - end - end - end + local response = convertDefinitionResult(state, result) return response end } @@ -441,27 +446,32 @@ m.register 'textDocument/typeDefinition' { if not result then return nil end - local response = {} - for i, info in ipairs(result) do - ---@type uri - local targetUri = info.uri - if targetUri then - local targetState = files.getState(targetUri) - if targetState then - if client.getAbility 'textDocument.typeDefinition.linkSupport' then - response[i] = converter.locationLink(targetUri - , converter.packRange(targetState, info.target.start, info.target.finish) - , converter.packRange(targetState, info.target.start, info.target.finish) - , converter.packRange(state, info.source.start, info.source.finish) - ) - else - response[i] = converter.location(targetUri - , converter.packRange(targetState, info.target.start, info.target.finish) - ) - end - end - end + local response = convertDefinitionResult(state, result) + return response + end +} + +m.register 'textDocument/implementation' { + capability = { + implementationProvider = true, + }, + abortByFileUpdate = true, + ---@async + function (params) + local uri = files.getRealUri(params.textDocument.uri) + workspace.awaitReady(uri) + local _ <close> = progress.create(uri, lang.script.WINDOW_PROCESSING_TYPE_DEFINITION, 0.5) + local state = files.getState(uri) + if not state then + return + end + local core = require 'core.implementation' + local pos = converter.unpackPosition(state, params.position) + local result = core(uri, pos) + if not result then + return nil end + local response = convertDefinitionResult(state, result) return response end } diff --git a/script/pub/pub.lua b/script/pub/pub.lua index e8051d05..517c3fc7 100644 --- a/script/pub/pub.lua +++ b/script/pub/pub.lua @@ -12,11 +12,14 @@ end local function channel_bpop(ctx) local selector, chan = ctx[1], ctx[2] - for _ in selector:wait() do - local r = table.pack(chan:pop()) - if r[1] == true then - return table.unpack(r, 2) + while true do + for _ in selector:wait() do + local r = table.pack(chan:pop()) + if r[1] == true then + return table.unpack(r, 2) + end end + thread.sleep(10) end end diff --git a/script/vm/compiler.lua b/script/vm/compiler.lua index 11ba07ab..e1b1b43b 100644 --- a/script/vm/compiler.lua +++ b/script/vm/compiler.lua @@ -550,11 +550,14 @@ local function matchCall(source) or call.node ~= source then return end - local funcs = vm.getMatchedFunctions(source, call.args) local myNode = vm.getNode(source) if not myNode then return end + local funcs = vm.getExactMatchedFunctions(source, call.args) + if not funcs then + return + end local needRemove for n in myNode:eachObject() do if n.type == 'function' @@ -870,7 +873,7 @@ local function compileCallArgNode(arg, call, callNode, fixIndex, myIndex) ---@type integer?, table<any, boolean>? local eventIndex, eventMap if call.args then - for i = 1, 2 do + for i = 1, 10 do local eventArg = call.args[i + fixIndex] if not eventArg then break diff --git a/script/vm/function.lua b/script/vm/function.lua index c6df6349..1e308317 100644 --- a/script/vm/function.lua +++ b/script/vm/function.lua @@ -1,6 +1,7 @@ ---@class vm local vm = require 'vm.vm' local guide = require 'parser.guide' +local util = require 'utility' ---@param arg parser.object ---@return parser.object? @@ -267,6 +268,9 @@ end ---@return integer def function vm.countReturnsOfCall(func, args, mark) local funcs = vm.getMatchedFunctions(func, args, mark) + if not funcs then + return 0, math.huge, 0 + end ---@type integer?, number?, integer? local min, max, def for _, f in ipairs(funcs) do @@ -329,22 +333,71 @@ function vm.countList(list, mark) return min, max, def end +---@param uri uri +---@param args parser.object[] +---@return boolean +local function isAllParamMatched(uri, args, params) + if not params then + return false + end + for i = 1, #args do + if not params[i] then + break + end + local argNode = vm.compileNode(args[i]) + local defNode = vm.compileNode(params[i]) + if not vm.canCastType(uri, defNode, argNode) then + return false + end + end + return true +end + ---@param func parser.object ----@param args parser.object[]? +---@param args? parser.object[] +---@return parser.object[]? +function vm.getExactMatchedFunctions(func, args) + local funcs = vm.getMatchedFunctions(func, args) + if not args or not funcs then + return funcs + end + if #funcs == 1 then + return funcs + end + local uri = guide.getUri(func) + local needRemove + for i, n in ipairs(funcs) do + if vm.isVarargFunctionWithOverloads(n) + or not isAllParamMatched(uri, args, n.args) then + if not needRemove then + needRemove = {} + end + needRemove[#needRemove+1] = i + end + end + if not needRemove then + return funcs + end + if #needRemove == #funcs then + return nil + end + util.tableMultiRemove(funcs, needRemove) + return funcs +end + +---@param func parser.object +---@param args? parser.object[] ---@param mark? table ----@return parser.object[] +---@return parser.object[]? function vm.getMatchedFunctions(func, args, mark) local funcs = {} local node = vm.compileNode(func) for n in node:eachObject() do - if (n.type == 'function' and not vm.isVarargFunctionWithOverloads(n)) + if n.type == 'function' or n.type == 'doc.type.function' then funcs[#funcs+1] = n end end - if #funcs <= 1 then - return funcs - end local amin, amax = vm.countList(args, mark) @@ -357,7 +410,7 @@ function vm.getMatchedFunctions(func, args, mark) end if #matched == 0 then - return funcs + return nil else return matched end @@ -372,23 +425,31 @@ function vm.isVarargFunctionWithOverloads(func) if not func.args then return false end + if func._varargFunction ~= nil then + return func._varargFunction + end if func.args[1] and func.args[1].type == 'self' then if not func.args[2] or func.args[2].type ~= '...' then + func._varargFunction = false return false end else if not func.args[1] or func.args[1].type ~= '...' then + func._varargFunction = false return false end end if not func.bindDocs then + func._varargFunction = false return false end for _, doc in ipairs(func.bindDocs) do if doc.type == 'doc.overload' then + func._varargFunction = true return true end end + func._varargFunction = false return false end diff --git a/script/vm/global.lua b/script/vm/global.lua index e830f6d8..aa987cf4 100644 --- a/script/vm/global.lua +++ b/script/vm/global.lua @@ -539,21 +539,60 @@ function vm.hasGlobalSets(suri, cate, name) return true end +---@param uri uri +---@param key string +---@return boolean +local function checkIsGlobalRegex(uri, key) + local dglobalsregex = config.get(uri, 'Lua.diagnostics.globalsRegex') + if not dglobalsregex then + return false + end + + for _, pattern in ipairs(dglobalsregex) do + if key:match(pattern) then + return true + end + end + + return false +end + ---@param src parser.object local function checkIsUndefinedGlobal(src) + if src.type ~= 'getglobal' then + return false + end + local key = src[1] + if not key then + return false + end + + local node = src.node + if node.tag ~= '_ENV' then + return false + end local uri = guide.getUri(src) - local dglobals = util.arrayToHash(config.get(uri, 'Lua.diagnostics.globals')) local rspecial = config.get(uri, 'Lua.runtime.special') + if rspecial[key] then + return false + end - local node = src.node - return src.type == 'getglobal' and key and not ( - dglobals[key] or - rspecial[key] or - node.tag ~= '_ENV' or - vm.hasGlobalSets(uri, 'variable', key) - ) + if vm.hasGlobalSets(uri, 'variable', key) then + return false + end + + local dglobals = config.get(uri, 'Lua.diagnostics.globals') + if util.arrayHas(dglobals, key) then + return false + end + + if checkIsGlobalRegex(uri, key) then + return false + end + + return true end ---@param src parser.object diff --git a/script/vm/infer.lua b/script/vm/infer.lua index 3f3d0e3a..bb06ee3a 100644 --- a/script/vm/infer.lua +++ b/script/vm/infer.lua @@ -242,9 +242,6 @@ local viewNodeSwitch;viewNodeSwitch = util.switch() return vm.viewKey(source, uri) end) ----@class vm.node ----@field lastInfer? vm.infer - ---@param node? vm.node ---@return vm.infer local function createInfer(node) diff --git a/script/vm/node.lua b/script/vm/node.lua index bc1dfcb1..fae79cbc 100644 --- a/script/vm/node.lua +++ b/script/vm/node.lua @@ -16,6 +16,7 @@ vm.nodeCache = setmetatable({}, util.MODE_K) ---@field [vm.node.object] true ---@field fields? table<vm.node|string, vm.node> ---@field undefinedGlobal boolean? +---@field lastInfer? vm.infer local mt = {} mt.__index = mt mt.id = 0 @@ -31,6 +32,7 @@ function mt:merge(node) if not node then return self end + self.lastInfer = nil if node.type == 'vm.node' then if node == self then return self @@ -2,7 +2,8 @@ package.path = package.path .. ';./test/?.lua' .. ';./test/?/init.lua' local fs = require 'bee.filesystem' -local rootPath = fs.exe_path():parent_path():parent_path():string() +local sys = require 'bee.sys' +local rootPath = sys.exe_path():parent_path():parent_path():string() ROOT = fs.path(rootPath) TEST = true DEVELOP = true @@ -54,6 +55,7 @@ local function testAll() test 'basic' test 'definition' test 'type_inference' + test 'implementation' test 'references' test 'hover' test 'completion' diff --git a/test/implementation/init.lua b/test/implementation/init.lua new file mode 100644 index 00000000..678cb23b --- /dev/null +++ b/test/implementation/init.lua @@ -0,0 +1,81 @@ +local core = require 'core.implementation' +local files = require 'files' +local vm = require 'vm' +local catch = require 'catch' + +rawset(_G, 'TEST', true) + +local function founded(targets, results) + if #targets ~= #results then + return false + end + for _, target in ipairs(targets) do + for _, result in ipairs(results) do + if target[1] == result[1] and target[2] == result[2] then + goto NEXT + end + end + do return false end + ::NEXT:: + end + return true +end + +---@async +function TEST(script) + local newScript, catched = catch(script, '!?') + + files.setText(TESTURI, newScript) + + local results = core(TESTURI, catched['?'][1][1]) + if results then + local positions = {} + for i, result in ipairs(results) do + if not vm.isMetaFile(result.uri) then + positions[#positions+1] = { result.target.start, result.target.finish } + end + end + assert(founded(catched['!'], positions)) + else assert(#catched['!'] == 0) + end + + files.remove(TESTURI) +end + +TEST [[ +---@class A +---@field x number +local M + +M.<!x!> = 1 + + +print(M.<?x?>) +]] + +TEST [[ +---@class A +---@field f fun() +local M + +function M.<!f!>() end + + +print(M.<?f?>) +]] + +TEST [[ +---@class A +local M + +function M:<!event!>(name) end + +---@class A +---@field event fun(self, name: 'ev1') +---@field event fun(self, name: 'ev2') + +---@type A +local m + +m:<?event?>('ev1') +]] diff --git a/test/type_inference/common.lua b/test/type_inference/common.lua new file mode 100644 index 00000000..5922832b --- /dev/null +++ b/test/type_inference/common.lua @@ -0,0 +1,4194 @@ +local config = require 'config' + +TEST 'nil' [[ +local <?t?> = nil +]] + +TEST 'string' [[ +local <?var?> = '111' +]] + +TEST 'boolean' [[ +local <?var?> = true +]] + +TEST 'integer' [[ +local <?var?> = 1 +]] + +TEST 'number' [[ +local <?var?> = 1.0 +]] + +TEST 'unknown' [[ +local <?var?> +]] + +TEST 'unknown' [[ +local <?var?> +var = y +]] + +TEST 'any' [[ +function f(<?x?>) + +end +]] + +TEST 'any' [[ +function f(<?x?>) + x = 1 +end +]] + +TEST 'string' [[ +local var = '111' +t.<?x?> = var +]] + +TEST 'string' [[ +local <?var?> +var = '111' +]] + +TEST 'string' [[ +local var +<?var?> = '111' +]] + +TEST 'string' [[ +local var +var = '111' +print(<?var?>) +]] + +TEST 'function' [[ +function <?xx?>() +end +]] + +TEST 'function' [[ +local function <?xx?>() +end +]] + +TEST 'function' [[ +local xx +<?xx?> = function () +end +]] + +TEST 'table' [[ +local <?t?> = {} +]] + +TEST 'unknown' [[ +<?x?>() +]] + +TEST 'boolean' [[ +<?x?> = not y +]] + +TEST 'integer' [[ +<?x?> = #y +]] + +TEST 'integer' [[ +<?x?> = #'aaaa' +]] + +TEST 'integer' [[ +<?x?> = #{} +]] + +TEST 'number' [[ +<?x?> = - y +]] + +TEST 'number' [[ +<?x?> = - 1.0 +]] + +TEST 'integer' [[ +<?x?> = ~ y +]] + +TEST 'integer' [[ +<?x?> = ~ 1 +]] + +TEST 'boolean' [[ +<?x?> = 1 < 2 +]] + +TEST 'integer' [[ +local a = true +local b = 1 +<?x?> = a and b +]] + +TEST 'integer' [[ +local a = false +local b = 1 +<?x?> = a or b +]] + +TEST 'boolean' [[ +<?x?> = a == b +]] + +TEST 'unknown' [[ +<?x?> = a << b +]] + +TEST 'integer' [[ +<?x?> = 1 << 2 +]] + +TEST 'unknown' [[ +<?x?> = a .. b +]] + +TEST 'string' [[ +<?x?> = 'a' .. 'b' +]] + +TEST 'string' [[ +<?x?> = 'a' .. 1 +]] + +TEST 'string' [[ +<?x?> = 'a' .. 1.0 +]] + +TEST 'unknown' [[ +<?x?> = a + b +]] + +TEST 'number' [[ +<?x?> = 1 + 2.0 +]] + +TEST 'integer' [[ +<?x?> = 1 + 2 +]] + +TEST 'integer' [[ +---@type integer +local a + +<?x?> = - a +]] + +TEST 'number' [[ +local a + +<?x?> = - a +]] + +TEST 'unknown' [[ +<?x?> = 1 + X +]] + +TEST 'unknown' [[ +<?x?> = 1.0 + X +]] + +TEST 'tablelib' [[ +---@class tablelib +table = {} + +<?table?>() +]] + +TEST 'string' [[ +_VERSION = 'Lua 5.4' + +<?x?> = _VERSION +]] + +TEST 'function' [[ +---@class stringlib +local string + +string.xxx = function () end + +return ('x').<?xxx?> +]] + +TEST 'function' [[ +---@class stringlib +String = {} + +String.xxx = function () end + +return ('x').<?xxx?> +]] + +TEST 'function' [[ +---@class stringlib +local string + +string.xxx = function () end + +<?x?> = ('x').xxx +]] + +TEST 'function' [[ +---@class stringlib +local string + +string.xxx = function () end + +_VERSION = 'Lua 5.4' + +<?x?> = _VERSION.xxx +]] + +TEST 'table' [[ +<?x?> = setmetatable({}) +]] + +TEST 'integer' [[ +local function x() + return 1 +end +<?y?> = x() +]] + +TEST 'integer|nil' [[ +local function x() + return 1 + return nil +end +<?y?> = x() +]] + +TEST 'unknown|nil' [[ +local function x() + return a + return nil +end +<?y?> = x() +]] + +TEST 'unknown|nil' [[ +local function x() + return nil + return f() +end +<?y?> = x() +]] + +TEST 'unknown|nil' [[ +local function x() + return nil + return f() +end +_, <?y?> = x() +]] + +TEST 'integer' [[ +local function x() + return 1 +end +_, <?y?> = pcall(x) +]] + +TEST 'integer' [[ +function x() + return 1 +end +_, <?y?> = pcall(x) +]] + +TEST 'integer' [[ +local function x() + return 1 +end +_, <?y?> = xpcall(x) +]] + +TEST 'A' [[ +---@class A + +---@return A +local function f2() end + +local function f() + return f2() +end + +local <?x?> = f() +]] + +-- 不根据调用者的输入参数来推测 +--TEST 'number' [[ +--local function x(a) +-- return <?a?> +--end +--x(1) +--]] + +--TEST 'table' [[ +--setmetatable(<?b?>) +--]] + +-- 不根据对方函数内的使用情况来推测 +TEST 'unknown' [[ +local function x(a) + _ = a + 1 +end +local b +x(<?b?>) +]] + +TEST 'unknown' [[ +local function x(a, ...) + local _, <?b?>, _ = ... +end +x(nil, 'xx', 1, true) +]] + +-- 引用不跨越参数 +TEST 'unknown' [[ +local function x(a, ...) + return true, 'ss', ... +end +local _, _, _, <?b?>, _ = x(nil, true, 1, 'yy') +]] + +TEST 'unknown' [[ +local <?x?> = next() +]] + +TEST 'unknown' [[ +local a, b +function a() + return b() +end +function b() + return a() +end +local <?x?> = a() +]] + +TEST 'class' [[ +---@class class +local <?x?> +]] + +TEST 'string' [[ +---@class string + +---@type string +local <?x?> +]] + +TEST '1' [[ +---@type 1 +local <?v?> +]] + +TEST 'string[]' [[ +---@class string + +---@type string[] +local <?x?> +]] + +TEST 'string|table' [[ +---@class string +---@class table + +---@type string | table +local <?x?> +]] + +TEST [['enum1'|'enum2']] [[ +---@type 'enum1' | 'enum2' +local <?x?> +]] + +TEST [["enum1"|"enum2"]] [[ +---@type "enum1" | "enum2" +local <?x?> +]] + +config.set(nil, 'Lua.hover.expandAlias', false) +TEST 'A' [[ +---@alias A 'enum1' | 'enum2' + +---@type A +local <?x?> +]] + +TEST 'A' [[ +---@alias A 'enum1' | 'enum2' | A + +---@type A +local <?x?> +]] + +TEST 'A' [[ +---@alias A 'enum1' | 'enum2' | B + +---@type A +local <?x?> +]] +config.set(nil, 'Lua.hover.expandAlias', true) +TEST [['enum1'|'enum2']] [[ +---@alias A 'enum1' | 'enum2' + +---@type A +local <?x?> +]] + +TEST [['enum1'|'enum2']] [[ +---@alias A 'enum1' | 'enum2' | A + +---@type A +local <?x?> +]] + +TEST [['enum1'|'enum2'|B]] [[ +---@alias A 'enum1' | 'enum2' | B + +---@type A +local <?x?> +]] + +TEST '1|true' [[ +---@alias A 1 | true + +---@type A +local <?x?> +]] + +TEST 'fun()' [[ +---@type fun() +local <?x?> +]] + +TEST 'fun(a: string, b: any, ...any)' [[ +---@type fun(a: string, b, ...) +local <?x?> +]] + +TEST 'fun(a: string, b: any, c?: boolean, ...any):c, d?, ...unknown' [[ +---@type fun(a: string, b, c?: boolean, ...):c, d?, ... +local <?x?> +]] + +TEST '{ [string]: string }' [[ +---@type { [string]: string } +local <?x?> +]] + +TEST 'table<string, number>' [[ +---@class string +---@class number + +---@type table<string, number> +local <?x?> +]] + +TEST 'A<string, number>' [[ +---@class A + +---@type A<string, number> +local <?x?> +]] + +TEST 'string' [[ +---@class string + +---@type string[] +local x +local <?y?> = x[1] +]] + +TEST 'string' [[ +---@class string + +---@return string[] +local function f() end +local x = f() +local <?y?> = x[1] +]] + +TEST 'table' [[ +local t = {} +local <?v?> = setmetatable(t) +]] + +TEST 'CCC' [[ +---@class CCC + +---@type table<string, CCC> +local t = {} + +print(t.<?a?>) +]] + +TEST [['aaa'|'bbb']] [[ +---@type table<string, 'aaa'|'bbb'> +local t = {} + +print(t.<?a?>) +]] + +TEST 'integer' [[ +---@generic K +---@type fun(a?: K):K +local f + +local <?n?> = f(1) +]] + +TEST 'unknown' [[ +---@generic K +---@type fun(a?: K):K +local f + +local <?n?> = f(nil) +]] + +TEST 'unknown' [[ +---@generic K +---@type fun(a: K|integer):K +local f + +local <?n?> = 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 <?i?> in ipairs() do +end +]] + +TEST 'table<string, boolean>' [[ +---@generic K, V +---@param t table<K, V> +---@return K +---@return V +local function next(t) end + +---@type table<string, boolean> +local t +local k, v = next(<?t?>) +]] + +TEST 'string' [[ +---@class string + +---@generic K, V +---@param t table<K, V> +---@return K +---@return V +local function next(t) end + +---@type table<string, boolean> +local t +local <?k?>, v = next(t) +]] + +TEST 'boolean' [[ +---@class boolean + +---@generic K, V +---@param t table<K, V> +---@return K +---@return V +local function next(t) end + +---@type table<string, boolean> +local t +local k, <?v?> = next(t) +]] + +TEST 'boolean' [[ +---@generic K +---@type fun(arg: K):K +local f + +local <?r?> = f(true) +]] + +TEST 'string' [[ +---@class string + +---@generic K, V +---@type fun(arg: table<K, V>):K, V +local f + +---@type table<string, boolean> +local t + +local <?k?>, v = f(t) +]] + +TEST 'boolean' [[ +---@class boolean + +---@generic K, V +---@type fun(arg: table<K, V>):K, V +local f + +---@type table<string, boolean> +local t + +local k, <?v?> = f(t) +]] + +TEST 'fun()' [[ +---@return fun() +local function f() end + +local <?r?> = f() +]] + +TEST 'table<string, boolean>' [[ +---@return table<string, boolean> +local function f() end + +local <?r?> = f() +]] + +TEST 'string' [[ +---@class string + +---@generic K, V +---@return fun(arg: table<K, V>):K, V +local function f() end + +local f2 = f() + +---@type table<string, boolean> +local t + +local <?k?>, v = f2(t) +]] + +TEST 'fun(a: <V>):integer, <V>' [[ +---@generic K, V +---@param a K +---@return fun(a: V):K, V +local function f(a) end + +local <?f2?> = 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 <?i?>, 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, <?v?> = f2(true) +]] + +TEST 'fun(table: table<<K>, <V>>, index?: <K>):<K>, <V>' [[ +---@generic T: table, K, V +---@param t T +---@return fun(table: table<K, V>, index?: K):K, V +---@return T +---@return nil +local function pairs(t) end + +local <?next?> = pairs(dummy) +]] + +TEST 'string' [[ +---@generic T: table, K, V +---@param t T +---@return fun(table: table<K, V>, index?: K):K, V +---@return T +---@return nil +local function pairs(t) end + +local next = pairs(dummy) + +---@type table<string, boolean> +local t +local <?k?>, v = next(t) +]] + +TEST 'boolean' [[ +---@generic T: table, K, V +---@param t T +---@return fun(table: table<K, V>, index?: K):K, V +---@return T +---@return nil +local function pairs(t) end + +local next = pairs(dummy) + +---@type table<string, boolean> +local t +local k, <?v?> = next(t) +]] + +TEST 'string' [[ +---@generic T: table, K, V +---@param t T +---@return fun(table: table<K, V>, index?: K):K, V +---@return T +---@return nil +local function pairs(t) end + +local next = pairs(dummy) + +---@type table<string, boolean> +local t +local <?k?>, v = next(t, nil) +]] + +TEST 'boolean' [[ +---@generic T: table, K, V +---@param t T +---@return fun(table: table<K, V>, index?: K):K, V +---@return T +---@return nil +local function pairs(t) end + +local next = pairs(dummy) + +---@type table<string, boolean> +local t +local k, <?v?> = next(t, nil) +]] + +TEST 'string' [[ +---@generic T: table, K, V +---@param t T +---@return fun(table: table<K, V>, index?: K):K, V +---@return T +---@return nil +local function pairs(t) end + +local next = pairs(dummy) + +---@type table<string, boolean> +local t + +for <?k?>, v in next, t do +end +]] + +TEST 'boolean' [[ +---@class boolean + +---@generic T: table, K, V +---@param t T +---@return fun(table: table<K, V>, index?: K):K, V +---@return T +local function pairs(t) end + +local f = pairs(t) + +---@type table<string, boolean> +local t + +for k, <?v?> in f, t do +end +]] + +TEST 'string' [[ +---@generic T: table, K, V +---@param t T +---@return fun(table: table<K, V>, index?: K):K, V +---@return T +local function pairs(t) end + +---@type table<string, boolean> +local t + +for <?k?>, v in pairs(t) do +end +]] + +TEST 'boolean' [[ +---@generic T: table, K, V +---@param t T +---@return fun(table: table<K, V>, index: K):K, V +---@return T +---@return nil +local function pairs(t) end + +---@type table<string, boolean> +local t + +for k, <?v?> 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 _, <?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 + +---@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 +---@return T +---@return nil +local function pairs(t) end + +---@type boolean[] +local t + +for k, <?v?> in pairs(t) do +end +]] + +TEST 'integer' [[ +---@generic T: table, K, V +---@param t T +---@return fun(table: table<K, V>, index?: K):K, V +---@return T +local function pairs(t) end + +---@type boolean[] +local t + +for <?k?>, 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 <?self?> +end +]] + +TEST 'Cls' [[ +---@class Cls +local Cls = {} + +---@generic T +---@param self T +---@return T +function Cls.new(self) return self end + +local <?test?> = Cls:new() +]] + +TEST 'Cls' [[ +---@class Cls +local Cls = {} + +---@generic T +---@param self T +---@return T +function Cls:new() return self end + +local <?test?> = Cls:new() +]] + +TEST 'Cls' [[ +---@class Cls +local Cls = {} + +---@generic T +---@param self T +---@return T +function Cls.new(self) return self end + +local <?test?> = Cls.new(Cls) +]] + +TEST 'Cls' [[ +---@class Cls +local Cls = {} + +---@generic T +---@param self T +---@return T +function Cls:new() return self end + +local <?test?> = 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 <?test?> = Rct.new(Rct) + +-- local test = Rct:new() + +return test +]] + +TEST 'function' [[ +string.gsub():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 <?t?> +]] + +config.set(nil, 'Lua.hover.enumsLimit', 1) +TEST [['a'...(+9)]] [[ +---@type 'a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'i'|'j' +local <?t?> +]] + +config.set(nil, 'Lua.hover.enumsLimit', 0) +TEST '...(+10)' [[ +---@type 'a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'i'|'j' +local <?t?> +]] + +config.set(nil, 'Lua.hover.enumsLimit', 5) + +TEST 'string|fun():string' [[ +---@type string | fun(): string +local <?t?> +]] + +TEST 'string' [[ +local valids = { + ['Lua 5.1'] = false, + ['Lua 5.2'] = false, + ['Lua 5.3'] = false, + ['Lua 5.4'] = false, + ['LuaJIT'] = false, +} + +for <?k?>, 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, <?v?> in pairs(valids) do +end +]] + +TEST 'string' [[ +local t = { + a = 1, + b = 1, +} + +for <?k?>, v in pairs(t) do +end +]] + +TEST 'integer' [[ +local t = {'a', 'b'} + +for <?k?>, v in pairs(t) do +end +]] + +TEST 'string' [[ +local t = {'a', 'b'} + +for k, <?v?> in pairs(t) do +end +]] + +TEST 'fun():number, boolean' [[ +---@type fun():number, boolean +local <?t?> +]] + + +TEST 'fun(value: Class)' [[ +---@class Class + +---@param callback fun(value: Class) +function work(callback) +end + +work(<?function?> (value) +end) +]] + +TEST 'Class' [[ +---@class Class + +---@param callback fun(value: Class) +function work(callback) +end + +work(function (<?value?>) +end) +]] + +TEST 'fun(value: Class)' [[ +---@class Class + +---@param callback fun(value: Class) +function work(callback) +end + +pcall(work, <?function?> (value) +end) +]] + +TEST 'Class' [[ +---@class Class + +---@param callback fun(value: Class) +function work(callback) +end + +xpcall(work, debug.traceback, function (<?value?>) +end) +]] + +TEST 'string' [[ +---@generic T +---@param x T +---@return { x: T } +local function f(x) end + +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 }) +]] + +TEST 'player' [[ +---@class player +local t + +<?t?>:getOwner() +]] + +TEST 'string[][]' [[ +---@type string[][] +local <?t?> +]] + +TEST 'table' [[ +---@type {}[] +local t + +local <?v?> = t[1] +]] + +TEST 'string' [[ +---@type string[][] +local v = {} + +for _, a in ipairs(v) do + for i, <?b?> in ipairs(a) do + end +end +]] + +--TEST 'number' [[ +-----@param x number +--local f +-- +--f = function (<?x?>) 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", <?function?> (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 (<?i?>) +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 (<?i?>) +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 (<?i?>) +end) +]] + +TEST '👍' [[ +---@class 👍 +local <?x?> +]] + +TEST 'integer' [[ +---@type boolean +local x + +<?x?> = 1 +]] + +TEST 'integer' [[ +---@class Class +local x + +<?x?> = 1 +]] + +TEST 'unknown' [[ +---@return number +local function f(x) + local <?y?> = x() +end +]] + +TEST 'unknown' [[ +local mt + +---@return number +function mt:f() end + +local <?v?> = mt() +]] + +TEST 'unknown' [[ +local <?mt?> + +---@class X +function mt:f(x) end +]] + +TEST 'any' [[ +local mt + +---@class X +function mt:f(<?x?>) end +]] + +TEST 'unknown' [[ +local <?mt?> + +---@type number +function mt:f(x) end +]] + +TEST 'any' [[ +local mt + +---@type number +function mt:f(<?x?>) end +]] + +TEST 'Test' [[ +---@class Test +_G.<?Test?> = {} +]] + +TEST 'integer' [[ +local mt = {} + +---@param callback fun(i: integer) +function mt:loop(callback) end + +mt:loop(function (<?i?>) + +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 { + <?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 { + d = { + <?y?> = , + } +} +]] + +TEST 'integer' [[ +for <?i?> = a, b, c do end +]] + +TEST 'number' [[ +---@param x number +function F(<?x?>) 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 <?v?> = t.x() +]] + +TEST 'function' [[ +---@overload fun() +function <?f?>() end +]] + +TEST 'integer' [[ +---@type table<string, integer> +local t + +t.<?a?> +]] + +TEST '"a"|"b"|"c"' [[ +---@type table<string, "a"|"b"|"c"> +local t + +t.<?a?> +]] + +TEST 'integer' [[ +---@class A +---@field x integer + +---@type A +local t +t.<?x?> +]] + +TEST 'boolean' [[ +local <?var?> = true +var = 1 +var = 1.0 +]] + +TEST 'unknown' [[ +---@return ... +local function f() end + +local <?x?> = f() +]] + +TEST 'unknown' [[ +---@return ... +local function f() end + +local _, <?x?> = f() +]] + +TEST 'unknown' [[ +local t = { + x = 1, + y = 2, +} + +local <?x?> = t[#t] +]] + +TEST 'string' [[ +local t = { + x = 1, + [1] = 'x', +} + +local <?x?> = t[#t] +]] + +TEST 'string' [[ +local t = { 'x' } + +local <?x?> = t[#t] +]] + +TEST '(string|integer)[]' [[ +---@type (string|integer)[] +local <?x?> +]] + +TEST 'boolean' [[ +---@type table<string, boolean> +local t + +---@alias uri string + +---@type string +local uri + +local <?v?> = t[uri] +]] + +TEST 'A' [[ +---@class A +G = {} + +<?G?>:A() +]] + +TEST 'A' [[ +---@type A +local <?x?> = nil +]] + +TEST 'A' [[ +---@class A +---@field b B +local mt + +function mt:f() + self.b:x() + 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 'string' [[ +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 'string' [[ +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' [[ +local x +x = true +do + x = 1 +end +print(<?x?>) +]] + +TEST 'boolean' [[ +local x +x = true +function XX() + do + x = 1 + end +end +print(<?x?>) +]] + +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 'nil' [[ +---@type integer? +local x + +if not 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 not x then + return +end + +print(<?x?>) +]] + +TEST 'integer' [[ +---@type integer? +local x + +if xxx and x then + print(<?x?>) +end +]] + +TEST 'unknown' [[ +---@type integer? +local x + +if not x and x then + print(<?x?>) +end +]] + +TEST 'integer' [[ +---@type integer? +local x + +if x and not mark[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 integer? +local x + +while x do + print(<?x?>) +end +]] + +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 'nil' [[ +---@type string? +local x + +if 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 '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?>) +]] + +TEST 'nil' [[ +---@type integer? +local t + +if t then +else + print(<?t?>) +end + +print(t) +]] + +TEST 'table' [[ +local function f() + if x then + return y + end + return {} +end + +local <?z?> = f() +]] + +TEST 'integer|table' [[ +local function returnI() + return 1 +end + +local function f() + if x then + return returnI() + end + return {} +end + +local <?z?> = f() +]] + +TEST 'number' [[ +for _ in _ do + ---@type number + local <?x?> +end +]] + +TEST 'unknown' [[ +for _ in _ do + ---@param x number + local <?x?> +end +]] + +TEST 'unknown' [[ +---@type number +for <?x?> in _ do +end +]] + +TEST 'number' [[ +---@param x number +for <?x?> in _ do +end +]] + +TEST 'table' [[ +---@alias tp table + +---@type tp +local <?x?> +]] + +TEST '{ name: boolean }' [[ +---@alias tp {name: boolean} + +---@type tp +local <?x?> +]] + +TEST 'boolean|{ name: boolean }' [[ +---@alias tp boolean | {name: boolean} + +---@type tp +local <?x?> +]] + +TEST '`1`|`true`' [[ +---@type `1` | `true` +local <?x?> +]] + +TEST 'function' [[ +local x + +function x() end + +print(<?x?>) +]] + +TEST 'unknown' [[ +local x + +if x.field == 'haha' then + print(<?x?>) +end +]] + +TEST 'string' [[ +---@type string? +local t + +if not t or xxx then + return +end + +print(<?t?>) +]] + +TEST 'table' [[ +---@type table|nil +local t + +return function () + if not t then + return + end + + print(<?t?>) +end +]] + +TEST 'table' [[ +---@type table|nil +local t + +f(function () + if not t then + return + end + + print(<?t?>) +end) +]] + +TEST 'table' [[ +---@type table? +local t + +t = t or {} + +print(<?t?>) +]] + +TEST 'unknown|nil' [[ +local x + +if x == nil then +end + +print(<?x?>) +]] + +TEST 'table<xxx, true>' [[ +---@alias xxx table<xxx, true> + +---@type xxx +local <?t?> +]] + +TEST 'xxx[][]' [[ +---@alias xxx xxx[] + +---@type xxx +local <?t?> +]] + +TEST 'fun(x: fun(x: xxx))' [[ +---@alias xxx fun(x: xxx) + +---@type xxx +local <?t?> +]] + +TEST 'table' [[ +---@type table|nil +local t + +while t do + print(<?t?>) +end +]] + +TEST 'table|nil' [[ +---@type table|nil +local t + +while <?t?> do + print(t) +end +]] + +TEST 'table' [[ +---@type table|nil +local t + +while t ~= nil do + print(<?t?>) +end +]] + +TEST 'table|nil' [[ +---@type table|nil +local t + +while <?t?> ~= nil do + print(t) +end +]] + +TEST 'integer' [[ +---@type integer? +local n + +if not n then + error('n is nil') +end + +print(<?n?>) +]] + +TEST 'integer' [[ +---@type integer? +local n + +if not n then + os.exit() +end + +print(<?n?>) +]] + +TEST 'table' [[ +---@type table? +local n + +print((n and <?n?>.x)) +]] + +TEST 'table' [[ +---@type table? +local n + +n = n and <?n?>.x or 1 +]] + +TEST 'table' [[ +---@type table? +local n + +n = ff[n and <?n?>.x] +]] + +TEST 'integer' [[ +local x + +if type(x) == 'integer' then + print(<?x?>) +end +]] + +TEST 'boolean|integer' [[ +local x + +if type(x) == 'integer' +or type(x) == 'boolean' then + print(<?x?>) +end +]] + +TEST 'fun()' [[ +---@type fun()? +local x + +if type(x) == 'function' then + print(<?x?>) +end +]] + +TEST 'function' [[ +local x + +if type(x) == 'function' then + print(<?x?>) +end +]] + +TEST 'integer' [[ +local x +local tp = type(x) + +if tp == 'integer' then + print(<?x?>) +end +]] + +TEST 'integer' [[ +---@type integer? +local x + +if (x == nil) then +else + print(<?x?>) +end +]] + +TEST 'B' [[ +---@class A +---@class B + +---@type A +local x + +---@type B +x = call(x) + +print(<?x?>) +]] + +TEST 'nil' [[ +local function f() +end + +local <?x?> = f() +]] + +TEST 'integer[]' [[ +---@type integer[] +local x +if not x then + return +end + +print(<?x?>) +]] + +TEST 'unknown' [[ +---@type string[] +local t + +local <?x?> = t.x +]] + +TEST 'integer|unknown' [[ +local function f() + return GG +end + +local t + +t.x = 1 +t.x = f() + +print(t.<?x?>) +]] + +TEST 'integer' [[ +local function f() + if X then + return X + else + return 1 + end +end + +local <?n?> = f() +]] + +TEST 'unknown' [[ +local function f() + return t[k] +end + +local <?n?> = f() +]] + +TEST 'integer|nil' [[ +local function f() + if x then + return + else + return 1 + end +end + +local <?n?> = f() +]] + +TEST 'integer' [[ +---@class A +---@field x integer +local m + +m.<?x?> = true + +print(m.x) +]] + +TEST 'integer' [[ +---@class A +---@field x integer +local m + +m.x = true + +print(m.<?x?>) +]] + +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.<?x?>) +]] + +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.<?x?>) +]] + +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.<?x?>) +]] + +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.<?x?>) +]] + +TEST 'boolean?' [[ +---@generic T +---@param x T +---@return T +local function echo(x) end + +---@type boolean? +local b + +local <?x?> = echo(b) +]] + +TEST 'boolean' [[ +---@generic T +---@param x T? +---@return T +local function echo(x) end + +---@type boolean? +local b + +local <?x?> = echo(b) +]] + +TEST 'boolean' [[ +---@generic T +---@param x? T +---@return T +local function echo(x) end + +---@type boolean? +local b + +local <?x?> = echo(b) +]] + +TEST 'boolean' [[ +---@type {[integer]: boolean, xx: integer} +local t + +local <?n?> = t[1] +]] + +TEST 'boolean' [[ +---@type integer +local i + +---@type {[integer]: boolean, xx: integer} +local t + +local <?n?> = t[i] +]] + +TEST 'string' [=[ +local x = true +local y = x--[[@as integer]] --is `integer` here +local z = <?x?>--[[@as string]] --is `true` here +]=] + +TEST 'integer' [[ +---@type integer +local x + +if type(x) == 'number' then + print(<?x?>) +end +]] + +TEST 'boolean' [[ +---@class A +---@field [integer] boolean +local mt + +function mt:f() + ---@type integer + local index + local <?x?> = self[index] +end +]] + +TEST 'boolean' [[ +---@class A +---@field [B] boolean + +---@class B + +---@type A +local a + +---@type B +local b + +local <?x?> = a[b] +]] + +TEST 'number' [[ +---@type {x: string ; y: boolean; z: number} +local t + +local <?z?> = t.z +]] + +TEST 'fun():number, boolean' [[ +---@type {f: fun():number, boolean} +local t + +local <?f?> = t.f +]] + +TEST 'fun():number' [[ +---@type {(f: fun():number), x: boolean} +local t + +local <?f?> = t.f +]] + +TEST 'boolean' [[ +---@param ... boolean +local function f(...) + local <?n?> = ... +end +]] + +TEST 'boolean' [[ +---@param ... boolean +local function f(...) + local _, <?n?> = ... +end +]] + +TEST 'boolean' [[ +---@return boolean ... +local function f() end + +local <?n?> = f() +]] + +TEST 'boolean' [[ +---@return boolean ... +local function f() end + +local _, <?n?> = f() +]] + +TEST 'boolean' [[ +---@type fun():name1: boolean, name2:number +local f + +local <?n?> = f() +]] + +TEST 'number' [[ +---@type fun():name1: boolean, name2:number +local f + +local _, <?n?> = f() +]] +TEST 'boolean' [[ +---@type fun():(name1: boolean, name2:number) +local f + +local <?n?> = f() +]] + +TEST 'number' [[ +---@type fun():(name1: boolean, name2:number) +local f + +local _, <?n?> = f() +]] + +TEST 'boolean' [[ +---@type fun():...: boolean +local f + +local _, <?n?> = f() +]] + +TEST 'string' [[ +local s +while true do + s = '' +end +print(<?s?>) +]] + +TEST 'string' [[ +local s +for _ in _ do + s = '' +end +print(<?s?>) +]] + +TEST 'A' [[ +---@class A: string + +---@type A +local <?s?> = '' +]] + +TEST 'number' [[ +---@return number +local function f() end +local x, <?y?> = 1, f() +]] + +TEST 'boolean' [[ +---@return number, boolean +local function f() end +local x, y, <?z?> = 1, f() +]] + +TEST 'number' [[ +---@return number, boolean +local function f() end +local x, y, <?z?> = 1, 2, f() +]] + +TEST 'unknown' [[ +local f + +print(<?f?>) + +function f() end +]] + +TEST 'unknown' [[ +local f + +do + print(<?f?>) +end + +function f() end +]] + +TEST 'function' [[ +local f + +function A() + print(<?f?>) +end + +function f() end +]] + +TEST 'number' [[ +---@type number|nil +local n + +local t = { + x = n and <?n?>, +} +]] + +TEST 'table' [[ +---@type table? +local n + +if not n or not <?n?>.x then +end +]] + +TEST 'table' [[ +---@type table? +local n + +if not n or not <?n?>[1] then +end +]] + +TEST 'number' [[ +---@type number|false +local n + +---@cast n -false + +print(<?n?>) +]] + +TEST 'table' [[ +---@type number|table +local n + +if n +---@cast n table +and <?n?>.type == 'xxx' then +end +]] + +TEST 'integer' [[ +---@type integer? +local n +if true then + n = 0 +end +local <?x?> = n or 0 +]] + +TEST 'number' [=[ +local <?x?> = F()--[[@as number]] +]=] + +TEST 'number' [=[ +local function f() + return F()--[[@as number]] +end + +local <?x?> = f() +]=] + +TEST 'number' [=[ +local <?x?> = X --[[@as number]] +]=] + +TEST 'number' [[ +---@return number?, number? +local function f() end + +for <?x?>, y in f do +end +]] + +TEST 'number' [[ +---@return number?, number? +local function f() end + +for x, <?y?> in f do +end +]] + +TEST 'number|nil' [[ +---@type table|nil +local a + +---@type number|nil +local b + +local <?c?> = a and b +]] + +TEST 'number|table|nil' [[ +---@type table|nil +local a + +---@type number|nil +local b + +local <?c?> = a or b +]] + +TEST 'number|table|nil' [[ +---@type table|nil +local a + +---@type number|nil +local b + +local c = a and b +local <?d?> = a or b +]] + +TEST 'number' [[ +local x + +---@return number +local function f() +end + +x = f() + +print(<?x?>) +]] + +TEST 'number' [[ +local x + +---@return number +local function f() +end + +_, x = pcall(f) + +print(<?x?>) +]] + +TEST 'string' [[ +---@type table<string|number, string> +local t + +---@type number +local n +---@type string +local s + +local <?test?> = t[n] +local test2 = t[s] --test and test2 are unknow +]] + +TEST 'string' [[ +---@type table<string|number, string> +local t + +---@type number +local n +---@type string +local s + +local test = t[n] +local <?test2?> = t[s] --test and test2 are unknow +]] + +TEST 'table<number, boolean>' [[ +---@type table<number, boolean> +local t + +<?t?> = {} +]] + +TEST 'integer' [[ +---@type integer[]|A +local t + +local <?x?> = t[1] +]] + +TEST 'integer' [[ +---@type integer +---@diagnostic disable +local <?t?> +]] + +TEST 'A' [[ +---@class A +---@diagnostic disable +local <?t?> +]] + +TEST '{ [string]: number, [true]: string, [1]: boolean, tag: integer }' [[ +---@type {[string]: number, [true]: string, [1]: boolean, tag: integer} +local <?t?> +]] + +TEST 'unknown' [[ +local mt = {} +mt.<?x?> = nil +]] + +TEST 'unknown' [[ +mt = {} +mt.<?x?> = nil +]] + +TEST 'A' [[ +---@class A +---@operator unm: A + +---@type A +local a +local <?b?> = -a +]] + +TEST 'A' [[ +---@class A +---@operator bnot: A + +---@type A +local a +local <?b?> = ~a +]] + +TEST 'A' [[ +---@class A +---@operator len: A + +---@type A +local a +local <?b?> = #a +]] + +TEST 'A' [[ +---@class A +---@operator add: A + +---@type A +local a +local <?b?> = a + 1 +]] + +TEST 'A' [[ +---@class A +---@operator sub: A + +---@type A +local a +local <?b?> = a - 1 +]] + +TEST 'A' [[ +---@class A +---@operator mul: A + +---@type A +local a +local <?b?> = a * 1 +]] + +TEST 'A' [[ +---@class A +---@operator div: A + +---@type A +local a +local <?b?> = a / 1 +]] + +TEST 'A' [[ +---@class A +---@operator mod: A + +---@type A +local a +local <?b?> = a % 1 +]] + +TEST 'A' [[ +---@class A +---@operator pow: A + +---@type A +local a +local <?b?> = a ^ 1 +]] + +TEST 'A' [[ +---@class A +---@operator idiv: A + +---@type A +local a +local <?b?> = a // 1 +]] + +TEST 'A' [[ +---@class A +---@operator band: A + +---@type A +local a +local <?b?> = a & 1 +]] + +TEST 'A' [[ +---@class A +---@operator bor: A + +---@type A +local a +local <?b?> = a | 1 +]] + +TEST 'A' [[ +---@class A +---@operator bxor: A + +---@type A +local a +local <?b?> = a ~ 1 +]] + +TEST 'A' [[ +---@class A +---@operator shl: A + +---@type A +local a +local <?b?> = a << 1 +]] + +TEST 'A' [[ +---@class A +---@operator shr: A + +---@type A +local a +local <?b?> = a >> 1 +]] + +TEST 'A' [[ +---@class A +---@operator concat: A + +---@type A +local a +local <?b?> = a .. 1 +]] + +TEST 'A' [[ +---@class A +---@operator add(boolean): boolean +---@operator add(integer): A + +---@type A +local a +local <?b?> = a + 1 +]] + +TEST 'boolean' [[ +---@class A +---@operator add(boolean): boolean +---@operator add(integer): A + +---@type A +local a +local <?b?> = a + true +]] + +TEST 'A' [[ +---@class A +---@operator call: A + +---@type A +local a +local <?b?> = a() +]] + +TEST 'A' [[ +---@class A +---@operator call: A + +---@type A +local a + +local t = { + <?x?> = a(), +} +]] + +TEST 'boolean' [[ +---@class A +---@field n number +---@field [string] boolean +local t + +local <?x?> = t.xx +]] + +TEST 'number' [[ +---@class A +---@field n number +---@field [string] boolean +local t + +local <?x?> = t.n +]] + +TEST 'string' [[ +---@class string +---@operator mod: string + +local <?b?> = '' % 1 +]] + +TEST 'string|integer' [[ +---@type boolean +local bool + +local <?x?> = bool and '' or 0 +]] + +TEST 'string|integer' [[ +local bool + +if X then + bool = true +else + bool = false +end + +local <?x?> = bool and '' or 0 +]] + +TEST 'boolean' [[ +---@type boolean|true|false +local <?b?> +]] + +TEST 'integer|false' [[ +local <?b?> = X == 1 and X == 1 and 1 +]] + +TEST 'unknown|nil' [[ +local function f() + if X then + return ({})[1] + end + return nil +end + +local <?n?> = f() +]] + +TEST 'integer' [[ +---@generic T +---@vararg T # ERROR +---@return T +local function test(...) + return ... +end + +local <?n?> = test(1) +]] + +TEST 'boolean' [[ +---@type boolean, number +local <?x?>, y +]] + +TEST 'number' [[ +---@type boolean, number +local x, <?y?> +]] + +TEST 'unknown' [[ +---@type _, number +local <?x?>, y +]] + +TEST 'number[]' [[ +local t +---@cast t number[]? + +local x = t and <?t?>[i] +]] + +TEST 'number?' [[ +---@type number[]? +local t + +local <?x?> = t and t[i] +]] + +TEST 'number' [[ +---@type number +local x + +if not <?x?>.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(<?x?>) +]] + +TEST 'integer' [[ +local A = { + ---@class XXX + B = {} +} + +A.B.C = 1 + +print(A.B.<?C?>) +]] + +TEST '-2|-3|1' [[ +---@type 1|-2|-3 +local <?n?> +]] + +TEST 'table' [[ +---@enum A +local m = {} + +print(<?m?>) +]] + +TEST 'A' [[ +---@class A +---@overload fun():A +local m = {} + +---@return A +function m:init() + return <?self?> +end +]] + +TEST 'string' [[ +---@vararg string +function F(...) + local t = {...} + for k, <?v?> in pairs(t) do + end +end +]] + +TEST 'string' [[ +---@vararg string +function F(...) + local t = {...} + for k, <?v?> in ipairs(t) do + end +end +]] + +TEST 'integerA' [[ +---@type integerA +for <?i?> = 1, 10 do +end +]] + +TEST 'string' [[ +---@class A +---@field x string + +---@class B : A +local t = {} + +t.x = t.x + +print(t.<?x?>) +]] + +TEST 'unknown' [[ +local t = { + x = 1, +} + +local x + +local <?v?> = t[x] +]] + +TEST 'A|B' [[ +---@class A +---@class B: A + +---@type A|B +local <?t?> +]] + +TEST 'function' [[ +---@class myClass +local myClass = { has = { nested = {} } } + +function myClass.has.nested.fn() end + +---@type myClass +local class + +class.has.nested.<?fn?>() +]] + +TEST 'integer[]' [[ +---@generic T +---@param f fun(x: T) +---@return T[] +local function x(f) end + +---@param x integer +local <?arr?> = x(function (x) end) +]] + +TEST 'integer[]' [[ +---@generic T +---@param f fun():T +---@return T[] +local function x(f) end + +local <?arr?> = x(function () + return 1 +end) +]] + +TEST 'integer[]' [[ +---@generic T +---@param f fun():T +---@return T[] +local function x(f) end + +---@return integer +local <?arr?> = 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 <?arr?> = x(cb) +]] + +TEST 'integer[]' [[ +---@generic T +---@param f fun():T +---@return T[] +local function x(f) end + +---@type fun(): integer +local cb + +local <?arr?> = x(cb) +]] + +TEST 'integer' [[ +---@return fun(x: integer) +local function f() + return function (<?x?>) + end +end +]] + +TEST 'string' [[ +---@class A +---@field f fun(x: string) + +---@type A +local t = { + f = function (<?x?>) end +} +]] + +config.set(nil, 'Lua.runtime.special', { + ['xx.assert'] = 'assert' +}) + +TEST 'number' [[ +---@type number? +local t + +xx.assert(t) + +print(<?t?>) +]] + +config.set(nil, 'Lua.runtime.special', nil) + +TEST 'A' [[ +---@class A +local mt + +---@return <?self?> +function mt:init() +end +]] + +TEST 'A' [[ +---@class A +local mt + +---@return self +function mt:init() +end + +local <?o?> = mt:init() +]] + +TEST 'A' [[ +---@class A +---@field x <?self?> +]] + +TEST 'A' [[ +---@class A +---@field x self + +---@type A +local o + +print(o.<?x?>) +]] + +TEST 'A' [[ +---@class A +---@overload fun(): self +local A + +local <?o?> = A() +]] + +TEST 'number' [[ +---@type table<'Test1', fun(x: number)> +local t = { + ["Test1"] = function(<?x?>) end, +} +]] + +TEST 'number' [[ +---@type table<5, fun(x: number)> +local t = { + [5] = function(<?x?>) end, +} +]] + +TEST 'number' [[ +---@type fun(x: number) +local function f(<?x?>) end +]] + +TEST 'boolean' [[ +---@generic T: string | boolean | table +---@param x T +---@return T +local function f(x) + return x +end + +local <?x?> = f(true) +]] + +TEST 'number' [[ +---@class A +---@field [1] number +---@field [2] boolean +local t + +local <?n?> = t[1] +]] + +TEST 'boolean' [[ +---@class A +---@field [1] number +---@field [2] boolean +local t + +local <?n?> = t[2] +]] + +TEST 'N' [[ +---@class N: number +local x + +if x == 0.1 then + print(<?x?>) +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 <?r?> = 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 <?r?> = m * v +]] + +TEST 'A|B' [[ +---@class A +---@class B + +---@type A|B +local t + +if x then + ---@cast t A +else + print(<?t?>) +end +]] + +TEST 'A|B' [[ +---@class A +---@class B + +---@type A|B +local t + +if x then + ---@cast t A +elseif <?t?> then +end +]] + +TEST 'A|B' [[ +---@class A +---@class B + +---@type A|B +local t + +if x then + ---@cast t A + print(t) +elseif <?t?> then +end +]] + +TEST 'A|B' [[ +---@class A +---@class B + +---@type A|B +local t + +if x then + ---@cast t A + print(t) +elseif <?t?> then + ---@cast t A + print(t) +end +]] + +TEST 'function' [[ +local function x() + print(<?x?>) +end +]] + +TEST 'number' [[ +---@type number? +local x + +do + if not x then + return + end +end + +print(<?x?>) +]] + +TEST 'number' [[ +---@type number[] +local xs + +---@type fun(x): number? +local f + +for _, <?x?> in ipairs(xs) do + x = f(x) +end +]] + +TEST 'number' [[ +---@type number? +X = Y + +if X then + print(<?X?>) +end +]] + +TEST 'number' [[ +---@type number|boolean +X = Y + +if type(X) == 'number' then + print(<?X?>) +end +]] + +TEST 'boolean' [[ +---@type number|boolean +X = Y + +if type(X) ~= 'number' then + print(<?X?>) +end +]] + +TEST 'boolean' [[ +---@type number +X = Y + +---@cast X boolean + +print(<?X?>) +]] + +TEST 'number' [[ +---@type number +local t + +if xxx == <?t?> then + print(t) +end +]] + +TEST 'V' [[ +---@class V +X = 1 + +print(<?X?>) +]] + +TEST 'V' [[ +---@class V +X.Y = 1 + +print(X.<?Y?>) +]] + +TEST 'integer' [[ +local x = {} + +x.y = 1 +local y = x.y +x.y = nil + +print(<?y?>) +]] + +TEST 'function' [[ +function X() + <?Y?>() +end + +function Y() +end +]] + +TEST 'A_Class' [[ +---@class A_Class +local A = { x = 5 } + +function A:func() + for i = 1, <?self?>.x do + print(i) + end + + self.y = 3 + self.y = self.y + 3 +end +]] + +TEST 'number' [[ +---@type number? +local n +local <?v?> = n or error('') +]] + +TEST 'Foo' [[ +---@class Foo +---@operator mul(Foo): Foo +---@operator mul(Bar): Foo +---@class Bar + +---@type Foo +local foo + +---@type Foo|Bar +local fooOrBar + +local <?b?> = foo * fooOrBar +]] + +TEST 'number' [[ +local a = 4; +local b = 2; + +local <?c?> = a / b; +]] + +TEST 'string' [[ +local a = '4'; +local b = '2'; + +local <?c?> = a .. b; +]] + +TEST 'number|{ [1]: string }' [[ +---@alias Some +---| { [1]: string } +---| number + +local x ---@type Some + +print(<?x?>) +]] + +TEST 'integer' [[ +---@class metatable : table +---@field __index table + +---@param table table +---@param metatable? metatable +---@return table +function setmetatable(table, metatable) end + +local m = setmetatable({},{ __index = { a = 1 } }) + +m.<?a?> +]] + +TEST 'integer' [[ +---@class metatable : table +---@field __index table + +---@param table table +---@param metatable? metatable +---@return table +function setmetatable(table, metatable) end + +local mt = {a = 1 } +local m = setmetatable({},{ __index = mt }) + +m.<?a?> +]] + +TEST 'integer' [[ +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() +]=] + +TEST 'boolean|number' [[ +---@alias A number +---@alias(partial) A boolean + +---@type A +local <?x?> +]] diff --git a/test/type_inference/init.lua b/test/type_inference/init.lua index da4590a6..35900bc3 100644 --- a/test/type_inference/init.lua +++ b/test/type_inference/init.lua @@ -44,4302 +44,5 @@ function TEST(wanted) end end -TEST 'nil' [[ -local <?t?> = nil -]] - -TEST 'string' [[ -local <?var?> = '111' -]] - -TEST 'boolean' [[ -local <?var?> = true -]] - -TEST 'integer' [[ -local <?var?> = 1 -]] - -TEST 'number' [[ -local <?var?> = 1.0 -]] - -TEST 'unknown' [[ -local <?var?> -]] - -TEST 'unknown' [[ -local <?var?> -var = y -]] - -TEST 'any' [[ -function f(<?x?>) - -end -]] - -TEST 'any' [[ -function f(<?x?>) - x = 1 -end -]] - -TEST 'string' [[ -local var = '111' -t.<?x?> = var -]] - -TEST 'string' [[ -local <?var?> -var = '111' -]] - -TEST 'string' [[ -local var -<?var?> = '111' -]] - -TEST 'string' [[ -local var -var = '111' -print(<?var?>) -]] - -TEST 'function' [[ -function <?xx?>() -end -]] - -TEST 'function' [[ -local function <?xx?>() -end -]] - -TEST 'function' [[ -local xx -<?xx?> = function () -end -]] - -TEST 'table' [[ -local <?t?> = {} -]] - -TEST 'unknown' [[ -<?x?>() -]] - -TEST 'boolean' [[ -<?x?> = not y -]] - -TEST 'integer' [[ -<?x?> = #y -]] - -TEST 'integer' [[ -<?x?> = #'aaaa' -]] - -TEST 'integer' [[ -<?x?> = #{} -]] - -TEST 'number' [[ -<?x?> = - y -]] - -TEST 'number' [[ -<?x?> = - 1.0 -]] - -TEST 'integer' [[ -<?x?> = ~ y -]] - -TEST 'integer' [[ -<?x?> = ~ 1 -]] - -TEST 'boolean' [[ -<?x?> = 1 < 2 -]] - -TEST 'integer' [[ -local a = true -local b = 1 -<?x?> = a and b -]] - -TEST 'integer' [[ -local a = false -local b = 1 -<?x?> = a or b -]] - -TEST 'boolean' [[ -<?x?> = a == b -]] - -TEST 'unknown' [[ -<?x?> = a << b -]] - -TEST 'integer' [[ -<?x?> = 1 << 2 -]] - -TEST 'unknown' [[ -<?x?> = a .. b -]] - -TEST 'string' [[ -<?x?> = 'a' .. 'b' -]] - -TEST 'string' [[ -<?x?> = 'a' .. 1 -]] - -TEST 'string' [[ -<?x?> = 'a' .. 1.0 -]] - -TEST 'unknown' [[ -<?x?> = a + b -]] - -TEST 'number' [[ -<?x?> = 1 + 2.0 -]] - -TEST 'integer' [[ -<?x?> = 1 + 2 -]] - -TEST 'integer' [[ ----@type integer -local a - -<?x?> = - a -]] - -TEST 'number' [[ -local a - -<?x?> = - a -]] - -TEST 'unknown' [[ -<?x?> = 1 + X -]] - -TEST 'unknown' [[ -<?x?> = 1.0 + X -]] - -TEST 'tablelib' [[ ----@class tablelib -table = {} - -<?table?>() -]] - -TEST 'string' [[ -_VERSION = 'Lua 5.4' - -<?x?> = _VERSION -]] - -TEST 'function' [[ ----@class stringlib -local string - -string.xxx = function () end - -return ('x').<?xxx?> -]] - -TEST 'function' [[ ----@class stringlib -String = {} - -String.xxx = function () end - -return ('x').<?xxx?> -]] - -TEST 'function' [[ ----@class stringlib -local string - -string.xxx = function () end - -<?x?> = ('x').xxx -]] - -TEST 'function' [[ ----@class stringlib -local string - -string.xxx = function () end - -_VERSION = 'Lua 5.4' - -<?x?> = _VERSION.xxx -]] - -TEST 'table' [[ -<?x?> = setmetatable({}) -]] - -TEST 'integer' [[ -local function x() - return 1 -end -<?y?> = x() -]] - -TEST 'integer|nil' [[ -local function x() - return 1 - return nil -end -<?y?> = x() -]] - -TEST 'unknown|nil' [[ -local function x() - return a - return nil -end -<?y?> = x() -]] - -TEST 'unknown|nil' [[ -local function x() - return nil - return f() -end -<?y?> = x() -]] - -TEST 'unknown|nil' [[ -local function x() - return nil - return f() -end -_, <?y?> = x() -]] - -TEST 'integer' [[ -local function x() - return 1 -end -_, <?y?> = pcall(x) -]] - -TEST 'integer' [[ -function x() - return 1 -end -_, <?y?> = pcall(x) -]] - -TEST 'integer' [[ -local function x() - return 1 -end -_, <?y?> = xpcall(x) -]] - -TEST 'A' [[ ----@class A - ----@return A -local function f2() end - -local function f() - return f2() -end - -local <?x?> = f() -]] - --- 不根据调用者的输入参数来推测 ---TEST 'number' [[ ---local function x(a) --- return <?a?> ---end ---x(1) ---]] - ---TEST 'table' [[ ---setmetatable(<?b?>) ---]] - --- 不根据对方函数内的使用情况来推测 -TEST 'unknown' [[ -local function x(a) - _ = a + 1 -end -local b -x(<?b?>) -]] - -TEST 'unknown' [[ -local function x(a, ...) - local _, <?b?>, _ = ... -end -x(nil, 'xx', 1, true) -]] - --- 引用不跨越参数 -TEST 'unknown' [[ -local function x(a, ...) - return true, 'ss', ... -end -local _, _, _, <?b?>, _ = x(nil, true, 1, 'yy') -]] - -TEST 'unknown' [[ -local <?x?> = next() -]] - -TEST 'unknown' [[ -local a, b -function a() - return b() -end -function b() - return a() -end -local <?x?> = a() -]] - -TEST 'class' [[ ----@class class -local <?x?> -]] - -TEST 'string' [[ ----@class string - ----@type string -local <?x?> -]] - -TEST '1' [[ ----@type 1 -local <?v?> -]] - -TEST 'string[]' [[ ----@class string - ----@type string[] -local <?x?> -]] - -TEST 'string|table' [[ ----@class string ----@class table - ----@type string | table -local <?x?> -]] - -TEST [['enum1'|'enum2']] [[ ----@type 'enum1' | 'enum2' -local <?x?> -]] - -TEST [["enum1"|"enum2"]] [[ ----@type "enum1" | "enum2" -local <?x?> -]] - -config.set(nil, 'Lua.hover.expandAlias', false) -TEST 'A' [[ ----@alias A 'enum1' | 'enum2' - ----@type A -local <?x?> -]] - -TEST 'A' [[ ----@alias A 'enum1' | 'enum2' | A - ----@type A -local <?x?> -]] - -TEST 'A' [[ ----@alias A 'enum1' | 'enum2' | B - ----@type A -local <?x?> -]] -config.set(nil, 'Lua.hover.expandAlias', true) -TEST [['enum1'|'enum2']] [[ ----@alias A 'enum1' | 'enum2' - ----@type A -local <?x?> -]] - -TEST [['enum1'|'enum2']] [[ ----@alias A 'enum1' | 'enum2' | A - ----@type A -local <?x?> -]] - -TEST [['enum1'|'enum2'|B]] [[ ----@alias A 'enum1' | 'enum2' | B - ----@type A -local <?x?> -]] - -TEST '1|true' [[ ----@alias A 1 | true - ----@type A -local <?x?> -]] - -TEST 'fun()' [[ ----@type fun() -local <?x?> -]] - -TEST 'fun(a: string, b: any, ...any)' [[ ----@type fun(a: string, b, ...) -local <?x?> -]] - -TEST 'fun(a: string, b: any, c?: boolean, ...any):c, d?, ...unknown' [[ ----@type fun(a: string, b, c?: boolean, ...):c, d?, ... -local <?x?> -]] - -TEST '{ [string]: string }' [[ ----@type { [string]: string } -local <?x?> -]] - -TEST 'table<string, number>' [[ ----@class string ----@class number - ----@type table<string, number> -local <?x?> -]] - -TEST 'A<string, number>' [[ ----@class A - ----@type A<string, number> -local <?x?> -]] - -TEST 'string' [[ ----@class string - ----@type string[] -local x -local <?y?> = x[1] -]] - -TEST 'string' [[ ----@class string - ----@return string[] -local function f() end -local x = f() -local <?y?> = x[1] -]] - -TEST 'table' [[ -local t = {} -local <?v?> = setmetatable(t) -]] - -TEST 'CCC' [[ ----@class CCC - ----@type table<string, CCC> -local t = {} - -print(t.<?a?>) -]] - -TEST [['aaa'|'bbb']] [[ ----@type table<string, 'aaa'|'bbb'> -local t = {} - -print(t.<?a?>) -]] - -TEST 'integer' [[ ----@generic K ----@type fun(a?: K):K -local f - -local <?n?> = f(1) -]] - -TEST 'unknown' [[ ----@generic K ----@type fun(a?: K):K -local f - -local <?n?> = f(nil) -]] - -TEST 'unknown' [[ ----@generic K ----@type fun(a: K|integer):K -local f - -local <?n?> = 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 <?i?> in ipairs() do -end -]] - -TEST 'table<string, boolean>' [[ ----@generic K, V ----@param t table<K, V> ----@return K ----@return V -local function next(t) end - ----@type table<string, boolean> -local t -local k, v = next(<?t?>) -]] - -TEST 'string' [[ ----@class string - ----@generic K, V ----@param t table<K, V> ----@return K ----@return V -local function next(t) end - ----@type table<string, boolean> -local t -local <?k?>, v = next(t) -]] - -TEST 'boolean' [[ ----@class boolean - ----@generic K, V ----@param t table<K, V> ----@return K ----@return V -local function next(t) end - ----@type table<string, boolean> -local t -local k, <?v?> = next(t) -]] - -TEST 'boolean' [[ ----@generic K ----@type fun(arg: K):K -local f - -local <?r?> = f(true) -]] - -TEST 'string' [[ ----@class string - ----@generic K, V ----@type fun(arg: table<K, V>):K, V -local f - ----@type table<string, boolean> -local t - -local <?k?>, v = f(t) -]] - -TEST 'boolean' [[ ----@class boolean - ----@generic K, V ----@type fun(arg: table<K, V>):K, V -local f - ----@type table<string, boolean> -local t - -local k, <?v?> = f(t) -]] - -TEST 'fun()' [[ ----@return fun() -local function f() end - -local <?r?> = f() -]] - -TEST 'table<string, boolean>' [[ ----@return table<string, boolean> -local function f() end - -local <?r?> = f() -]] - -TEST 'string' [[ ----@class string - ----@generic K, V ----@return fun(arg: table<K, V>):K, V -local function f() end - -local f2 = f() - ----@type table<string, boolean> -local t - -local <?k?>, v = f2(t) -]] - -TEST 'fun(a: <V>):integer, <V>' [[ ----@generic K, V ----@param a K ----@return fun(a: V):K, V -local function f(a) end - -local <?f2?> = 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 <?i?>, 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, <?v?> = f2(true) -]] - -TEST 'fun(table: table<<K>, <V>>, index?: <K>):<K>, <V>' [[ ----@generic T: table, K, V ----@param t T ----@return fun(table: table<K, V>, index?: K):K, V ----@return T ----@return nil -local function pairs(t) end - -local <?next?> = pairs(dummy) -]] - -TEST 'string' [[ ----@generic T: table, K, V ----@param t T ----@return fun(table: table<K, V>, index?: K):K, V ----@return T ----@return nil -local function pairs(t) end - -local next = pairs(dummy) - ----@type table<string, boolean> -local t -local <?k?>, v = next(t) -]] - -TEST 'boolean' [[ ----@generic T: table, K, V ----@param t T ----@return fun(table: table<K, V>, index?: K):K, V ----@return T ----@return nil -local function pairs(t) end - -local next = pairs(dummy) - ----@type table<string, boolean> -local t -local k, <?v?> = next(t) -]] - -TEST 'string' [[ ----@generic T: table, K, V ----@param t T ----@return fun(table: table<K, V>, index?: K):K, V ----@return T ----@return nil -local function pairs(t) end - -local next = pairs(dummy) - ----@type table<string, boolean> -local t -local <?k?>, v = next(t, nil) -]] - -TEST 'boolean' [[ ----@generic T: table, K, V ----@param t T ----@return fun(table: table<K, V>, index?: K):K, V ----@return T ----@return nil -local function pairs(t) end - -local next = pairs(dummy) - ----@type table<string, boolean> -local t -local k, <?v?> = next(t, nil) -]] - -TEST 'string' [[ ----@generic T: table, K, V ----@param t T ----@return fun(table: table<K, V>, index?: K):K, V ----@return T ----@return nil -local function pairs(t) end - -local next = pairs(dummy) - ----@type table<string, boolean> -local t - -for <?k?>, v in next, t do -end -]] - -TEST 'boolean' [[ ----@class boolean - ----@generic T: table, K, V ----@param t T ----@return fun(table: table<K, V>, index?: K):K, V ----@return T -local function pairs(t) end - -local f = pairs(t) - ----@type table<string, boolean> -local t - -for k, <?v?> in f, t do -end -]] - -TEST 'string' [[ ----@generic T: table, K, V ----@param t T ----@return fun(table: table<K, V>, index?: K):K, V ----@return T -local function pairs(t) end - ----@type table<string, boolean> -local t - -for <?k?>, v in pairs(t) do -end -]] - -TEST 'boolean' [[ ----@generic T: table, K, V ----@param t T ----@return fun(table: table<K, V>, index: K):K, V ----@return T ----@return nil -local function pairs(t) end - ----@type table<string, boolean> -local t - -for k, <?v?> 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 _, <?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 - ----@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 ----@return T ----@return nil -local function pairs(t) end - ----@type boolean[] -local t - -for k, <?v?> in pairs(t) do -end -]] - -TEST 'integer' [[ ----@generic T: table, K, V ----@param t T ----@return fun(table: table<K, V>, index?: K):K, V ----@return T -local function pairs(t) end - ----@type boolean[] -local t - -for <?k?>, 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 <?self?> -end -]] - -TEST 'Cls' [[ ----@class Cls -local Cls = {} - ----@generic T ----@param self T ----@return T -function Cls.new(self) return self end - -local <?test?> = Cls:new() -]] - -TEST 'Cls' [[ ----@class Cls -local Cls = {} - ----@generic T ----@param self T ----@return T -function Cls:new() return self end - -local <?test?> = Cls:new() -]] - -TEST 'Cls' [[ ----@class Cls -local Cls = {} - ----@generic T ----@param self T ----@return T -function Cls.new(self) return self end - -local <?test?> = Cls.new(Cls) -]] - -TEST 'Cls' [[ ----@class Cls -local Cls = {} - ----@generic T ----@param self T ----@return T -function Cls:new() return self end - -local <?test?> = 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 <?test?> = Rct.new(Rct) - --- local test = Rct:new() - -return test -]] - -TEST 'function' [[ -string.gsub():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 <?t?> -]] - -config.set(nil, 'Lua.hover.enumsLimit', 1) -TEST [['a'...(+9)]] [[ ----@type 'a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'i'|'j' -local <?t?> -]] - -config.set(nil, 'Lua.hover.enumsLimit', 0) -TEST '...(+10)' [[ ----@type 'a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'i'|'j' -local <?t?> -]] - -config.set(nil, 'Lua.hover.enumsLimit', 5) - -TEST 'string|fun():string' [[ ----@type string | fun(): string -local <?t?> -]] - -TEST 'string' [[ -local valids = { - ['Lua 5.1'] = false, - ['Lua 5.2'] = false, - ['Lua 5.3'] = false, - ['Lua 5.4'] = false, - ['LuaJIT'] = false, -} - -for <?k?>, 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, <?v?> in pairs(valids) do -end -]] - -TEST 'string' [[ -local t = { - a = 1, - b = 1, -} - -for <?k?>, v in pairs(t) do -end -]] - -TEST 'integer' [[ -local t = {'a', 'b'} - -for <?k?>, v in pairs(t) do -end -]] - -TEST 'string' [[ -local t = {'a', 'b'} - -for k, <?v?> in pairs(t) do -end -]] - -TEST 'fun():number, boolean' [[ ----@type fun():number, boolean -local <?t?> -]] - - -TEST 'fun(value: Class)' [[ ----@class Class - ----@param callback fun(value: Class) -function work(callback) -end - -work(<?function?> (value) -end) -]] - -TEST 'Class' [[ ----@class Class - ----@param callback fun(value: Class) -function work(callback) -end - -work(function (<?value?>) -end) -]] - -TEST 'fun(value: Class)' [[ ----@class Class - ----@param callback fun(value: Class) -function work(callback) -end - -pcall(work, <?function?> (value) -end) -]] - -TEST 'Class' [[ ----@class Class - ----@param callback fun(value: Class) -function work(callback) -end - -xpcall(work, debug.traceback, function (<?value?>) -end) -]] - -TEST 'string' [[ ----@generic T ----@param x T ----@return { x: T } -local function f(x) end - -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 }) -]] - -TEST 'player' [[ ----@class player -local t - -<?t?>:getOwner() -]] - -TEST 'string[][]' [[ ----@type string[][] -local <?t?> -]] - -TEST 'table' [[ ----@type {}[] -local t - -local <?v?> = t[1] -]] - -TEST 'string' [[ ----@type string[][] -local v = {} - -for _, a in ipairs(v) do - for i, <?b?> in ipairs(a) do - end -end -]] - ---TEST 'number' [[ ------@param x number ---local f --- ---f = function (<?x?>) 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", <?function?> (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 (<?i?>) -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 (<?i?>) -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 (<?i?>) -end) -]] - -TEST '👍' [[ ----@class 👍 -local <?x?> -]] - -TEST 'integer' [[ ----@type boolean -local x - -<?x?> = 1 -]] - -TEST 'integer' [[ ----@class Class -local x - -<?x?> = 1 -]] - -TEST 'unknown' [[ ----@return number -local function f(x) - local <?y?> = x() -end -]] - -TEST 'unknown' [[ -local mt - ----@return number -function mt:f() end - -local <?v?> = mt() -]] - -TEST 'unknown' [[ -local <?mt?> - ----@class X -function mt:f(x) end -]] - -TEST 'any' [[ -local mt - ----@class X -function mt:f(<?x?>) end -]] - -TEST 'unknown' [[ -local <?mt?> - ----@type number -function mt:f(x) end -]] - -TEST 'any' [[ -local mt - ----@type number -function mt:f(<?x?>) end -]] - -TEST 'Test' [[ ----@class Test -_G.<?Test?> = {} -]] - -TEST 'integer' [[ -local mt = {} - ----@param callback fun(i: integer) -function mt:loop(callback) end - -mt:loop(function (<?i?>) - -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 { - <?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 { - d = { - <?y?> = , - } -} -]] - -TEST 'integer' [[ -for <?i?> = a, b, c do end -]] - -TEST 'number' [[ ----@param x number -function F(<?x?>) 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 <?v?> = t.x() -]] - -TEST 'function' [[ ----@overload fun() -function <?f?>() end -]] - -TEST 'integer' [[ ----@type table<string, integer> -local t - -t.<?a?> -]] - -TEST '"a"|"b"|"c"' [[ ----@type table<string, "a"|"b"|"c"> -local t - -t.<?a?> -]] - -TEST 'integer' [[ ----@class A ----@field x integer - ----@type A -local t -t.<?x?> -]] - -TEST 'boolean' [[ -local <?var?> = true -var = 1 -var = 1.0 -]] - -TEST 'unknown' [[ ----@return ... -local function f() end - -local <?x?> = f() -]] - -TEST 'unknown' [[ ----@return ... -local function f() end - -local _, <?x?> = f() -]] - -TEST 'unknown' [[ -local t = { - x = 1, - y = 2, -} - -local <?x?> = t[#t] -]] - -TEST 'string' [[ -local t = { - x = 1, - [1] = 'x', -} - -local <?x?> = t[#t] -]] - -TEST 'string' [[ -local t = { 'x' } - -local <?x?> = t[#t] -]] - -TEST '(string|integer)[]' [[ ----@type (string|integer)[] -local <?x?> -]] - -TEST 'boolean' [[ ----@type table<string, boolean> -local t - ----@alias uri string - ----@type string -local uri - -local <?v?> = t[uri] -]] - -TEST 'A' [[ ----@class A -G = {} - -<?G?>:A() -]] - -TEST 'A' [[ ----@type A -local <?x?> = nil -]] - -TEST 'A' [[ ----@class A ----@field b B -local mt - -function mt:f() - self.b:x() - 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 'string' [[ -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 'string' [[ -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' [[ -local x -x = true -do - x = 1 -end -print(<?x?>) -]] - -TEST 'boolean' [[ -local x -x = true -function XX() - do - x = 1 - end -end -print(<?x?>) -]] - -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 'nil' [[ ----@type integer? -local x - -if not 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 not x then - return -end - -print(<?x?>) -]] - -TEST 'integer' [[ ----@type integer? -local x - -if xxx and x then - print(<?x?>) -end -]] - -TEST 'unknown' [[ ----@type integer? -local x - -if not x and x then - print(<?x?>) -end -]] - -TEST 'integer' [[ ----@type integer? -local x - -if x and not mark[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 integer? -local x - -while x do - print(<?x?>) -end -]] - -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 'nil' [[ ----@type string? -local x - -if 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 '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?>) -]] - -TEST 'nil' [[ ----@type integer? -local t - -if t then -else - print(<?t?>) -end - -print(t) -]] - -TEST 'table' [[ -local function f() - if x then - return y - end - return {} -end - -local <?z?> = f() -]] - -TEST 'integer|table' [[ -local function returnI() - return 1 -end - -local function f() - if x then - return returnI() - end - return {} -end - -local <?z?> = f() -]] - -TEST 'number' [[ -for _ in _ do - ---@type number - local <?x?> -end -]] - -TEST 'unknown' [[ -for _ in _ do - ---@param x number - local <?x?> -end -]] - -TEST 'unknown' [[ ----@type number -for <?x?> in _ do -end -]] - -TEST 'number' [[ ----@param x number -for <?x?> in _ do -end -]] - -TEST 'table' [[ ----@alias tp table - ----@type tp -local <?x?> -]] - -TEST '{ name: boolean }' [[ ----@alias tp {name: boolean} - ----@type tp -local <?x?> -]] - -TEST 'boolean|{ name: boolean }' [[ ----@alias tp boolean | {name: boolean} - ----@type tp -local <?x?> -]] - -TEST '`1`|`true`' [[ ----@type `1` | `true` -local <?x?> -]] - -TEST 'function' [[ -local x - -function x() end - -print(<?x?>) -]] - -TEST 'unknown' [[ -local x - -if x.field == 'haha' then - print(<?x?>) -end -]] - -TEST 'string' [[ ----@type string? -local t - -if not t or xxx then - return -end - -print(<?t?>) -]] - -TEST 'table' [[ ----@type table|nil -local t - -return function () - if not t then - return - end - - print(<?t?>) -end -]] - -TEST 'table' [[ ----@type table|nil -local t - -f(function () - if not t then - return - end - - print(<?t?>) -end) -]] - -TEST 'table' [[ ----@type table? -local t - -t = t or {} - -print(<?t?>) -]] - -TEST 'unknown|nil' [[ -local x - -if x == nil then -end - -print(<?x?>) -]] - -TEST 'table<xxx, true>' [[ ----@alias xxx table<xxx, true> - ----@type xxx -local <?t?> -]] - -TEST 'xxx[][]' [[ ----@alias xxx xxx[] - ----@type xxx -local <?t?> -]] - -TEST 'fun(x: fun(x: xxx))' [[ ----@alias xxx fun(x: xxx) - ----@type xxx -local <?t?> -]] - -TEST 'table' [[ ----@type table|nil -local t - -while t do - print(<?t?>) -end -]] - -TEST 'table|nil' [[ ----@type table|nil -local t - -while <?t?> do - print(t) -end -]] - -TEST 'table' [[ ----@type table|nil -local t - -while t ~= nil do - print(<?t?>) -end -]] - -TEST 'table|nil' [[ ----@type table|nil -local t - -while <?t?> ~= nil do - print(t) -end -]] - -TEST 'integer' [[ ----@type integer? -local n - -if not n then - error('n is nil') -end - -print(<?n?>) -]] - -TEST 'integer' [[ ----@type integer? -local n - -if not n then - os.exit() -end - -print(<?n?>) -]] - -TEST 'table' [[ ----@type table? -local n - -print((n and <?n?>.x)) -]] - -TEST 'table' [[ ----@type table? -local n - -n = n and <?n?>.x or 1 -]] - -TEST 'table' [[ ----@type table? -local n - -n = ff[n and <?n?>.x] -]] - -TEST 'integer' [[ -local x - -if type(x) == 'integer' then - print(<?x?>) -end -]] - -TEST 'boolean|integer' [[ -local x - -if type(x) == 'integer' -or type(x) == 'boolean' then - print(<?x?>) -end -]] - -TEST 'fun()' [[ ----@type fun()? -local x - -if type(x) == 'function' then - print(<?x?>) -end -]] - -TEST 'function' [[ -local x - -if type(x) == 'function' then - print(<?x?>) -end -]] - -TEST 'integer' [[ -local x -local tp = type(x) - -if tp == 'integer' then - print(<?x?>) -end -]] - -TEST 'integer' [[ ----@type integer? -local x - -if (x == nil) then -else - print(<?x?>) -end -]] - -TEST 'B' [[ ----@class A ----@class B - ----@type A -local x - ----@type B -x = call(x) - -print(<?x?>) -]] - -TEST 'nil' [[ -local function f() -end - -local <?x?> = f() -]] - -TEST 'integer[]' [[ ----@type integer[] -local x -if not x then - return -end - -print(<?x?>) -]] - -TEST 'unknown' [[ ----@type string[] -local t - -local <?x?> = t.x -]] - -TEST 'integer|unknown' [[ -local function f() - return GG -end - -local t - -t.x = 1 -t.x = f() - -print(t.<?x?>) -]] - -TEST 'integer' [[ -local function f() - if X then - return X - else - return 1 - end -end - -local <?n?> = f() -]] - -TEST 'unknown' [[ -local function f() - return t[k] -end - -local <?n?> = f() -]] - -TEST 'integer|nil' [[ -local function f() - if x then - return - else - return 1 - end -end - -local <?n?> = f() -]] - -TEST 'integer' [[ ----@class A ----@field x integer -local m - -m.<?x?> = true - -print(m.x) -]] - -TEST 'integer' [[ ----@class A ----@field x integer -local m - -m.x = true - -print(m.<?x?>) -]] - -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.<?x?>) -]] - -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.<?x?>) -]] - -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.<?x?>) -]] - -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.<?x?>) -]] - -TEST 'boolean?' [[ ----@generic T ----@param x T ----@return T -local function echo(x) end - ----@type boolean? -local b - -local <?x?> = echo(b) -]] - -TEST 'boolean' [[ ----@generic T ----@param x T? ----@return T -local function echo(x) end - ----@type boolean? -local b - -local <?x?> = echo(b) -]] - -TEST 'boolean' [[ ----@generic T ----@param x? T ----@return T -local function echo(x) end - ----@type boolean? -local b - -local <?x?> = echo(b) -]] - -TEST 'boolean' [[ ----@overload fun():boolean ----@param x integer ----@return number -function f(x) -end - -local <?x?> = f() -]] - -TEST 'number' [[ ----@overload fun():boolean ----@param x integer ----@return number -function f(x) -end - -local <?x?> = f(1) -]] - -TEST 'boolean' [[ ----@overload fun():boolean ----@param x integer ----@return number -function f(x) -end - -function r0() - return -end - -local <?x?> = f(r0()) -]] - -TEST 'number' [[ ----@overload fun():boolean ----@param x integer ----@return number -function f(x) -end - -function r1() - return 1 -end - -local <?x?> = f(r1()) -]] - -TEST 'boolean' [[ ----@overload fun():boolean ----@param x integer ----@return number -function f(x) -end - ----@type fun() -local r0 - -local <?x?> = f(r0()) -]] - -TEST 'number' [[ ----@overload fun():boolean ----@param x integer ----@return number -function f(x) -end - ----@type fun():integer -local r1 - -local <?x?> = f(r1()) -]] - -TEST 'boolean' [[ ----@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 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 <?n2?> = 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 <?n3?> = f(0, 0) -]] - -TEST 'boolean' [[ ----@type {[integer]: boolean, xx: integer} -local t - -local <?n?> = t[1] -]] - -TEST 'boolean' [[ ----@type integer -local i - ----@type {[integer]: boolean, xx: integer} -local t - -local <?n?> = t[i] -]] - -TEST 'string' [=[ -local x = true -local y = x--[[@as integer]] --is `integer` here -local z = <?x?>--[[@as string]] --is `true` here -]=] - -TEST 'integer' [[ ----@type integer -local x - -if type(x) == 'number' then - print(<?x?>) -end -]] - -TEST 'boolean' [[ ----@class A ----@field [integer] boolean -local mt - -function mt:f() - ---@type integer - local index - local <?x?> = self[index] -end -]] - -TEST 'boolean' [[ ----@class A ----@field [B] boolean - ----@class B - ----@type A -local a - ----@type B -local b - -local <?x?> = a[b] -]] - -TEST 'number' [[ ----@type {x: string ; y: boolean; z: number} -local t - -local <?z?> = t.z -]] - -TEST 'fun():number, boolean' [[ ----@type {f: fun():number, boolean} -local t - -local <?f?> = t.f -]] - -TEST 'fun():number' [[ ----@type {(f: fun():number), x: boolean} -local t - -local <?f?> = t.f -]] - -TEST 'boolean' [[ ----@param ... boolean -local function f(...) - local <?n?> = ... -end -]] - -TEST 'boolean' [[ ----@param ... boolean -local function f(...) - local _, <?n?> = ... -end -]] - -TEST 'boolean' [[ ----@return boolean ... -local function f() end - -local <?n?> = f() -]] - -TEST 'boolean' [[ ----@return boolean ... -local function f() end - -local _, <?n?> = f() -]] - -TEST 'boolean' [[ ----@type fun():name1: boolean, name2:number -local f - -local <?n?> = f() -]] - -TEST 'number' [[ ----@type fun():name1: boolean, name2:number -local f - -local _, <?n?> = f() -]] -TEST 'boolean' [[ ----@type fun():(name1: boolean, name2:number) -local f - -local <?n?> = f() -]] - -TEST 'number' [[ ----@type fun():(name1: boolean, name2:number) -local f - -local _, <?n?> = f() -]] - -TEST 'boolean' [[ ----@type fun():...: boolean -local f - -local _, <?n?> = f() -]] - -TEST 'string' [[ -local s -while true do - s = '' -end -print(<?s?>) -]] - -TEST 'string' [[ -local s -for _ in _ do - s = '' -end -print(<?s?>) -]] - -TEST 'A' [[ ----@class A: string - ----@type A -local <?s?> = '' -]] - -TEST 'number' [[ ----@return number -local function f() end -local x, <?y?> = 1, f() -]] - -TEST 'boolean' [[ ----@return number, boolean -local function f() end -local x, y, <?z?> = 1, f() -]] - -TEST 'number' [[ ----@return number, boolean -local function f() end -local x, y, <?z?> = 1, 2, f() -]] - -TEST 'unknown' [[ -local f - -print(<?f?>) - -function f() end -]] - -TEST 'unknown' [[ -local f - -do - print(<?f?>) -end - -function f() end -]] - -TEST 'function' [[ -local f - -function A() - print(<?f?>) -end - -function f() end -]] - -TEST 'number' [[ ----@type number|nil -local n - -local t = { - x = n and <?n?>, -} -]] - -TEST 'table' [[ ----@type table? -local n - -if not n or not <?n?>.x then -end -]] - -TEST 'table' [[ ----@type table? -local n - -if not n or not <?n?>[1] then -end -]] - -TEST 'number' [[ ----@type number|false -local n - ----@cast n -false - -print(<?n?>) -]] - -TEST 'table' [[ ----@type number|table -local n - -if n ----@cast n table -and <?n?>.type == 'xxx' then -end -]] - -TEST 'integer' [[ ----@type integer? -local n -if true then - n = 0 -end -local <?x?> = n or 0 -]] - -TEST 'number' [=[ -local <?x?> = F()--[[@as number]] -]=] - -TEST 'number' [=[ -local function f() - return F()--[[@as number]] -end - -local <?x?> = f() -]=] - -TEST 'number' [=[ -local <?x?> = X --[[@as number]] -]=] - -TEST 'number' [[ ----@return number?, number? -local function f() end - -for <?x?>, y in f do -end -]] - -TEST 'number' [[ ----@return number?, number? -local function f() end - -for x, <?y?> in f do -end -]] - -TEST 'number|nil' [[ ----@type table|nil -local a - ----@type number|nil -local b - -local <?c?> = a and b -]] - -TEST 'number|table|nil' [[ ----@type table|nil -local a - ----@type number|nil -local b - -local <?c?> = a or b -]] - -TEST 'number|table|nil' [[ ----@type table|nil -local a - ----@type number|nil -local b - -local c = a and b -local <?d?> = a or b -]] - -TEST 'number' [[ -local x - ----@return number -local function f() -end - -x = f() - -print(<?x?>) -]] - -TEST 'number' [[ -local x - ----@return number -local function f() -end - -_, x = pcall(f) - -print(<?x?>) -]] - -TEST 'string' [[ ----@type table<string|number, string> -local t - ----@type number -local n ----@type string -local s - -local <?test?> = t[n] -local test2 = t[s] --test and test2 are unknow -]] - -TEST 'string' [[ ----@type table<string|number, string> -local t - ----@type number -local n ----@type string -local s - -local test = t[n] -local <?test2?> = t[s] --test and test2 are unknow -]] - -TEST 'table<number, boolean>' [[ ----@type table<number, boolean> -local t - -<?t?> = {} -]] - -TEST 'integer' [[ ----@type integer[]|A -local t - -local <?x?> = t[1] -]] - -TEST 'integer' [[ ----@type integer ----@diagnostic disable -local <?t?> -]] - -TEST 'A' [[ ----@class A ----@diagnostic disable -local <?t?> -]] - -TEST '{ [string]: number, [true]: string, [1]: boolean, tag: integer }' [[ ----@type {[string]: number, [true]: string, [1]: boolean, tag: integer} -local <?t?> -]] - -TEST 'unknown' [[ -local mt = {} -mt.<?x?> = nil -]] - -TEST 'unknown' [[ -mt = {} -mt.<?x?> = nil -]] - -TEST 'A' [[ ----@class A ----@operator unm: A - ----@type A -local a -local <?b?> = -a -]] - -TEST 'A' [[ ----@class A ----@operator bnot: A - ----@type A -local a -local <?b?> = ~a -]] - -TEST 'A' [[ ----@class A ----@operator len: A - ----@type A -local a -local <?b?> = #a -]] - -TEST 'A' [[ ----@class A ----@operator add: A - ----@type A -local a -local <?b?> = a + 1 -]] - -TEST 'A' [[ ----@class A ----@operator sub: A - ----@type A -local a -local <?b?> = a - 1 -]] - -TEST 'A' [[ ----@class A ----@operator mul: A - ----@type A -local a -local <?b?> = a * 1 -]] - -TEST 'A' [[ ----@class A ----@operator div: A - ----@type A -local a -local <?b?> = a / 1 -]] - -TEST 'A' [[ ----@class A ----@operator mod: A - ----@type A -local a -local <?b?> = a % 1 -]] - -TEST 'A' [[ ----@class A ----@operator pow: A - ----@type A -local a -local <?b?> = a ^ 1 -]] - -TEST 'A' [[ ----@class A ----@operator idiv: A - ----@type A -local a -local <?b?> = a // 1 -]] - -TEST 'A' [[ ----@class A ----@operator band: A - ----@type A -local a -local <?b?> = a & 1 -]] - -TEST 'A' [[ ----@class A ----@operator bor: A - ----@type A -local a -local <?b?> = a | 1 -]] - -TEST 'A' [[ ----@class A ----@operator bxor: A - ----@type A -local a -local <?b?> = a ~ 1 -]] - -TEST 'A' [[ ----@class A ----@operator shl: A - ----@type A -local a -local <?b?> = a << 1 -]] - -TEST 'A' [[ ----@class A ----@operator shr: A - ----@type A -local a -local <?b?> = a >> 1 -]] - -TEST 'A' [[ ----@class A ----@operator concat: A - ----@type A -local a -local <?b?> = a .. 1 -]] - -TEST 'A' [[ ----@class A ----@operator add(boolean): boolean ----@operator add(integer): A - ----@type A -local a -local <?b?> = a + 1 -]] - -TEST 'boolean' [[ ----@class A ----@operator add(boolean): boolean ----@operator add(integer): A - ----@type A -local a -local <?b?> = a + true -]] - -TEST 'A' [[ ----@class A ----@operator call: A - ----@type A -local a -local <?b?> = a() -]] - -TEST 'A' [[ ----@class A ----@operator call: A - ----@type A -local a - -local t = { - <?x?> = a(), -} -]] - -TEST 'boolean' [[ ----@class A ----@field n number ----@field [string] boolean -local t - -local <?x?> = t.xx -]] - -TEST 'number' [[ ----@class A ----@field n number ----@field [string] boolean -local t - -local <?x?> = t.n -]] - -TEST 'string' [[ ----@class string ----@operator mod: string - -local <?b?> = '' % 1 -]] - -TEST 'string|integer' [[ ----@type boolean -local bool - -local <?x?> = bool and '' or 0 -]] - -TEST 'string|integer' [[ -local bool - -if X then - bool = true -else - bool = false -end - -local <?x?> = bool and '' or 0 -]] - -TEST 'boolean' [[ ----@type boolean|true|false -local <?b?> -]] - -TEST 'integer|false' [[ -local <?b?> = X == 1 and X == 1 and 1 -]] - -TEST 'unknown|nil' [[ -local function f() - if X then - return ({})[1] - end - return nil -end - -local <?n?> = f() -]] - -TEST 'integer' [[ ----@generic T ----@vararg T # ERROR ----@return T -local function test(...) - return ... -end - -local <?n?> = test(1) -]] - -TEST 'boolean' [[ ----@type boolean, number -local <?x?>, y -]] - -TEST 'number' [[ ----@type boolean, number -local x, <?y?> -]] - -TEST 'unknown' [[ ----@type _, number -local <?x?>, y -]] - -TEST 'number[]' [[ -local t ----@cast t number[]? - -local x = t and <?t?>[i] -]] - -TEST 'number?' [[ ----@type number[]? -local t - -local <?x?> = t and t[i] -]] - -TEST 'number' [[ ----@type number -local x - -if not <?x?>.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(<?x?>) -]] - -TEST 'integer' [[ -local A = { - ---@class XXX - B = {} -} - -A.B.C = 1 - -print(A.B.<?C?>) -]] - -TEST '-2|-3|1' [[ ----@type 1|-2|-3 -local <?n?> -]] - -TEST 'table' [[ ----@enum A -local m = {} - -print(<?m?>) -]] - -TEST 'A' [[ ----@class A ----@overload fun():A -local m = {} - ----@return A -function m:init() - return <?self?> -end -]] - -TEST 'string' [[ ----@vararg string -function F(...) - local t = {...} - for k, <?v?> in pairs(t) do - end -end -]] - -TEST 'string' [[ ----@vararg string -function F(...) - local t = {...} - for k, <?v?> in ipairs(t) do - end -end -]] - -TEST 'integerA' [[ ----@type integerA -for <?i?> = 1, 10 do -end -]] - -TEST 'string' [[ ----@class A ----@field x string - ----@class B : A -local t = {} - -t.x = t.x - -print(t.<?x?>) -]] - -TEST 'unknown' [[ -local t = { - x = 1, -} - -local x - -local <?v?> = t[x] -]] - -TEST 'A|B' [[ ----@class A ----@class B: A - ----@type A|B -local <?t?> -]] - -TEST 'function' [[ ----@class myClass -local myClass = { has = { nested = {} } } - -function myClass.has.nested.fn() end - ----@type myClass -local class - -class.has.nested.<?fn?>() -]] - -TEST 'integer[]' [[ ----@generic T ----@param f fun(x: T) ----@return T[] -local function x(f) end - ----@param x integer -local <?arr?> = x(function (x) end) -]] - -TEST 'integer[]' [[ ----@generic T ----@param f fun():T ----@return T[] -local function x(f) end - -local <?arr?> = x(function () - return 1 -end) -]] - -TEST 'integer[]' [[ ----@generic T ----@param f fun():T ----@return T[] -local function x(f) end - ----@return integer -local <?arr?> = 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 <?arr?> = x(cb) -]] - -TEST 'integer[]' [[ ----@generic T ----@param f fun():T ----@return T[] -local function x(f) end - ----@type fun(): integer -local cb - -local <?arr?> = x(cb) -]] - -TEST 'integer' [[ ----@return fun(x: integer) -local function f() - return function (<?x?>) - end -end -]] - -TEST 'string' [[ ----@class A ----@field f fun(x: string) - ----@type A -local t = { - f = function (<?x?>) end -} -]] - -config.set(nil, 'Lua.runtime.special', { - ['xx.assert'] = 'assert' -}) - -TEST 'number' [[ ----@type number? -local t - -xx.assert(t) - -print(<?t?>) -]] - -config.set(nil, 'Lua.runtime.special', nil) - -TEST 'A' [[ ----@class A -local mt - ----@return <?self?> -function mt:init() -end -]] - -TEST 'A' [[ ----@class A -local mt - ----@return self -function mt:init() -end - -local <?o?> = mt:init() -]] - -TEST 'A' [[ ----@class A ----@field x <?self?> -]] - -TEST 'A' [[ ----@class A ----@field x self - ----@type A -local o - -print(o.<?x?>) -]] - -TEST 'A' [[ ----@class A ----@overload fun(): self -local A - -local <?o?> = A() -]] - -TEST 'number' [[ ----@type table<'Test1', fun(x: number)> -local t = { - ["Test1"] = function(<?x?>) end, -} -]] - -TEST 'number' [[ ----@type table<5, fun(x: number)> -local t = { - [5] = function(<?x?>) end, -} -]] - -TEST 'number' [[ ----@type fun(x: number) -local function f(<?x?>) end -]] - -TEST 'boolean' [[ ----@generic T: string | boolean | table ----@param x T ----@return T -local function f(x) - return x -end - -local <?x?> = f(true) -]] - -TEST 'number' [[ ----@class A ----@field [1] number ----@field [2] boolean -local t - -local <?n?> = t[1] -]] - -TEST 'boolean' [[ ----@class A ----@field [1] number ----@field [2] boolean -local t - -local <?n?> = t[2] -]] - -TEST 'N' [[ ----@class N: number -local x - -if x == 0.1 then - print(<?x?>) -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 <?r?> = 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 <?r?> = m * v -]] - -TEST 'A|B' [[ ----@class A ----@class B - ----@type A|B -local t - -if x then - ---@cast t A -else - print(<?t?>) -end -]] - -TEST 'A|B' [[ ----@class A ----@class B - ----@type A|B -local t - -if x then - ---@cast t A -elseif <?t?> then -end -]] - -TEST 'A|B' [[ ----@class A ----@class B - ----@type A|B -local t - -if x then - ---@cast t A - print(t) -elseif <?t?> then -end -]] - -TEST 'A|B' [[ ----@class A ----@class B - ----@type A|B -local t - -if x then - ---@cast t A - print(t) -elseif <?t?> then - ---@cast t A - print(t) -end -]] - -TEST 'function' [[ -local function x() - print(<?x?>) -end -]] - -TEST 'number' [[ ----@type number? -local x - -do - if not x then - return - end -end - -print(<?x?>) -]] - -TEST 'number' [[ ----@type number[] -local xs - ----@type fun(x): number? -local f - -for _, <?x?> in ipairs(xs) do - x = f(x) -end -]] - -TEST 'number' [[ ----@type number? -X = Y - -if X then - print(<?X?>) -end -]] - -TEST 'number' [[ ----@type number|boolean -X = Y - -if type(X) == 'number' then - print(<?X?>) -end -]] - -TEST 'boolean' [[ ----@type number|boolean -X = Y - -if type(X) ~= 'number' then - print(<?X?>) -end -]] - -TEST 'boolean' [[ ----@type number -X = Y - ----@cast X boolean - -print(<?X?>) -]] - -TEST 'number' [[ ----@type number -local t - -if xxx == <?t?> then - print(t) -end -]] - -TEST 'V' [[ ----@class V -X = 1 - -print(<?X?>) -]] - -TEST 'V' [[ ----@class V -X.Y = 1 - -print(X.<?Y?>) -]] - -TEST 'integer' [[ -local x = {} - -x.y = 1 -local y = x.y -x.y = nil - -print(<?y?>) -]] - -TEST 'function' [[ -function X() - <?Y?>() -end - -function Y() -end -]] - -TEST 'A_Class' [[ ----@class A_Class -local A = { x = 5 } - -function A:func() - for i = 1, <?self?>.x do - print(i) - end - - self.y = 3 - self.y = self.y + 3 -end -]] - -TEST 'number' [[ ----@type number? -local n -local <?v?> = n or error('') -]] - -TEST 'Foo' [[ ----@class Foo ----@operator mul(Foo): Foo ----@operator mul(Bar): Foo ----@class Bar - ----@type Foo -local foo - ----@type Foo|Bar -local fooOrBar - -local <?b?> = foo * fooOrBar -]] - -TEST 'number' [[ -local a = 4; -local b = 2; - -local <?c?> = a / b; -]] - -TEST 'string' [[ -local a = '4'; -local b = '2'; - -local <?c?> = a .. b; -]] - -TEST 'number|{ [1]: string }' [[ ----@alias Some ----| { [1]: string } ----| number - -local x ---@type Some - -print(<?x?>) -]] - -TEST 'integer' [[ ----@class metatable : table ----@field __index table - ----@param table table ----@param metatable? metatable ----@return table -function setmetatable(table, metatable) end - -local m = setmetatable({},{ __index = { a = 1 } }) - -m.<?a?> -]] - -TEST 'integer' [[ ----@class metatable : table ----@field __index table - ----@param table table ----@param metatable? metatable ----@return table -function setmetatable(table, metatable) end - -local mt = {a = 1 } -local m = setmetatable({},{ __index = mt }) - -m.<?a?> -]] - -TEST 'integer' [[ -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() -]=] - -TEST 'boolean|number' [[ ----@alias A number ----@alias(partial) A boolean - ----@type A -local <?x?> -]] +require 'type_inference.common' +require 'type_inference.param_match' diff --git a/test/type_inference/param_match.lua b/test/type_inference/param_match.lua new file mode 100644 index 00000000..8ead05ef --- /dev/null +++ b/test/type_inference/param_match.lua @@ -0,0 +1,139 @@ + +TEST 'boolean' [[ +---@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 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 <?n2?> = 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 <?n3?> = f(0, 0) +]] + +TEST 'boolean' [[ +---@overload fun():boolean +---@param x integer +---@return number +function f(x) +end + +local <?x?> = f() +]] + +TEST 'number' [[ +---@overload fun():boolean +---@param x integer +---@return number +function f(x) +end + +local <?x?> = f(1) +]] + +TEST 'boolean' [[ +---@overload fun():boolean +---@param x integer +---@return number +function f(x) +end + +function r0() + return +end + +local <?x?> = f(r0()) +]] + +TEST 'number' [[ +---@overload fun():boolean +---@param x integer +---@return number +function f(x) +end + +function r1() + return 1 +end + +local <?x?> = f(r1()) +]] + +TEST 'boolean' [[ +---@overload fun():boolean +---@param x integer +---@return number +function f(x) +end + +---@type fun() +local r0 + +local <?x?> = f(r0()) +]] + +TEST 'number' [[ +---@overload fun():boolean +---@param x integer +---@return number +function f(x) +end + +---@type fun():integer +local r1 + +local <?x?> = f(r1()) +]] + +TEST '1' [[ +---@overload fun(a: 'x'): 1 +---@overload fun(a: 'y'): 2 +local function f(...) end + +local <?r?> = f('x') +]] + +TEST '2' [[ +---@overload fun(a: 'x'): 1 +---@overload fun(a: 'y'): 2 +local function f(...) end + +local <?r?> = f('y') +]] + +TEST '1' [[ +---@overload fun(a: boolean): 1 +---@overload fun(a: number): 2 +local function f(...) end + +local <?r?> = f(true) +]] + +TEST '2' [[ +---@overload fun(a: boolean): 1 +---@overload fun(a: number): 2 +local function f(...) end + +local <?r?> = f(10) +]] |