diff --git a/.emmyrc.json b/.emmyrc.json new file mode 100644 index 0000000..0e12414 --- /dev/null +++ b/.emmyrc.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://raw.githubusercontent.com/EmmyLuaLs/emmylua-analyzer-rust/refs/heads/main/crates/emmylua_code_analysis/resources/schema.json", + "workspace": { + "ignoreDir": [ + ".rocks", + "build", + "tmp" + ] + }, + "runtime": { + "version": "LuaJIT" + }, + "diagnostics": { + "disable": [ + "need-check-nil", + "redefined-local", + "unresolved-require" + ], + "globals": [ + "box" + ] + } +} diff --git a/.github/workflows/packaging.yml b/.github/workflows/packaging.yml index 1bf02ea..8c6fee2 100644 --- a/.github/workflows/packaging.yml +++ b/.github/workflows/packaging.yml @@ -15,7 +15,7 @@ jobs: if: | github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - name: Check module version if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') }} @@ -32,7 +32,7 @@ jobs: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository needs: version-check - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false @@ -41,6 +41,7 @@ jobs: - { os: 'debian', dist: 'stretch' } - { os: 'debian', dist: 'buster' } - { os: 'debian', dist: 'bullseye' } + - { os: 'debian', dist: 'bookworm' } - { os: 'el', dist: '7' } - { os: 'el', dist: '8' } - { os: 'fedora', dist: '30' } @@ -54,6 +55,7 @@ jobs: - { os: 'ubuntu', dist: 'bionic' } - { os: 'ubuntu', dist: 'focal' } - { os: 'ubuntu', dist: 'jammy' } + - { os: 'ubuntu', dist: 'noble' } env: OS: ${{ matrix.platform.os }} @@ -61,12 +63,12 @@ jobs: steps: - name: Clone the module - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Clone the packpack tool - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: packpack/packpack path: packpack diff --git a/.github/workflows/push_rockspec.yml b/.github/workflows/push_rockspec.yml index ae3e383..381c689 100644 --- a/.github/workflows/push_rockspec.yml +++ b/.github/workflows/push_rockspec.yml @@ -17,7 +17,7 @@ jobs: version-check: # We need this job to run only on push with tag. if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') }} - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - name: Check module version uses: tarantool/actions/check-module-version@master @@ -27,7 +27,7 @@ jobs: require('test.rock_utils').assert_nonbuiltin('checks') push-scm-rockspec: - runs-on: [ ubuntu-20.04 ] + runs-on: [ ubuntu-24.04 ] if: github.ref == 'refs/heads/master' && github.event.workflow_run.conclusion == 'success' steps: - uses: actions/checkout@master @@ -38,7 +38,7 @@ jobs: files: ${{ env.ROCK_NAME }}-scm-1.rockspec push-tagged-rockspec: - runs-on: [ ubuntu-20.04 ] + runs-on: [ ubuntu-24.04 ] if: startsWith(github.ref, 'refs/tags') needs: version-check steps: diff --git a/.github/workflows/reusable_testing.yml b/.github/workflows/reusable_testing.yml index efe987f..518408a 100644 --- a/.github/workflows/reusable_testing.yml +++ b/.github/workflows/reusable_testing.yml @@ -5,13 +5,13 @@ on: inputs: artifact_name: description: The name of the tarantool build artifact - default: ubuntu-focal + default: ubuntu-jammy required: false type: string jobs: run_tests: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - name: Clone the checks module uses: actions/checkout@v4 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3e46f23..5c489c2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,12 +8,12 @@ jobs: strategy: fail-fast: false matrix: - tarantool: ['1.10', '2.5', '2.6', '2.7', '2.8', '2.10'] + tarantool: ['1.10','2.11', '3.3'] - runs-on: [ubuntu-20.04] + runs-on: [ubuntu-22.04] steps: - - uses: actions/checkout@v3 - - uses: tarantool/setup-tarantool@v2 + - uses: actions/checkout@v4 + - uses: tarantool/setup-tarantool@v3 with: tarantool-version: ${{ matrix.tarantool }} @@ -36,9 +36,9 @@ jobs: strategy: fail-fast: false - runs-on: [ubuntu-20.04] + runs-on: [ubuntu-22.04] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup LuaJIT run: sudo apt install -y luajit diff --git a/.luacheckrc b/.luacheckrc index 57c02f0..7f11a70 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -2,3 +2,12 @@ include_files = {'**/*.lua', '*.luacheckrc', '*.rockspec'} exclude_files = {'.rocks/', 'tmp/', 'build/'} max_line_length = 120 redefined = false +globals = { + "box", +} +ignore = { + -- Accessing an undefined field of a global variable . + "143/table", + -- Unused variable with `_` prefix. + "212/_.*", +} diff --git a/CHANGELOG.md b/CHANGELOG.md index b66ea7b..903bb80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,25 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Added + +### Changed + +### Fixed + +### Removed + +## [3.4.0] - 2025-10-21 + +This release introduces bumped CMake version to avoid compatibility +problem with CMake 4.x. + +### Changed + +- Bump CMake version to avoid compatibility problem with CMake 4.x (#66). + ## [3.3.0] - 2023-04-19 ### Added @@ -16,6 +35,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Override built-in checks, if installed. ## [3.2.0] - 2023-01-27 + ### Added - "error" type supported. diff --git a/CMakeLists.txt b/CMakeLists.txt index 2025061..ef330dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.6 FATAL_ERROR) +cmake_minimum_required(VERSION 3.10...3.31 FATAL_ERROR) project(checks NONE) diff --git a/checks.lua b/checks.lua index 35060a8..c61997b 100644 --- a/checks.lua +++ b/checks.lua @@ -1,3 +1,12 @@ +---The `checks` module provides the ability to check the types of arguments +---passed to a Lua function. It is designed to reveal mistakes in code, not to +---validate user input. +--- +---The returned module table is callable: `checks(type_1, ...)` is equivalent +---to `checks.checks(type_1, ...)`. It exposes the `checks` function and the +---`_VERSION` string field. +--- +---@alias checks.qualifier string|table local ffi = require('ffi') ffi.cdef[[ @@ -18,14 +27,14 @@ local _qualifiers_cache = { -- }, } ---- Check that string (or substring) starts with given string --- Optionally restricting the matching with the given offsets --- @function startswith --- @string inp original string --- @string head the substring to check against --- @int[opt] _start start index of matching boundary --- @int[opt] _end end index of matching boundary --- @returns boolean +---Check that the given string (or substring) starts with the given string, +---optionally restricting the matching with the given offsets. +--- +---@param inp string The original string. +---@param head string The substring to check against. +---@param _start? integer Start index of the matching boundary (default: `1`). +---@param _end? integer End index of the matching boundary (default: `#inp`). +---@return boolean `true` if `inp` starts with `head`, `false` otherwise. local function startswith(inp, head, _start, _end) if type(inp) ~= 'string' then error(err_string_arg:format(1, 'string.startswith', 'string', @@ -69,6 +78,13 @@ local function startswith(inp, head, _start, _end) end +---Check that a value conforms to the given string type qualifier. +--- +---@param value any The value to check. +---@param expected_type string The string type qualifier, e.g. `'string'`, +--- `'?number|string'`, or `'?'` for any type. +---@return boolean? `true` if the value conforms to the type qualifier. +---@return string? error_message The formatted error message if the check fails. local function check_string_type(value, expected_type) -- 1. Check any value. if expected_type == '?' then @@ -76,6 +92,7 @@ local function check_string_type(value, expected_type) end -- 2. Parse type qualifier + ---@type { [integer]: string, optional: boolean }? local qualifier = _qualifiers_cache[expected_type] if qualifier == nil then qualifier = { optional = false } @@ -123,6 +140,11 @@ local function check_string_type(value, expected_type) ) end +---Format a table key for an error message: `.key` for strings and `[n]` +---for numbers. +--- +---@param key any The table key. +---@return string The formatted key name. local function keyname_fmt(key) if type(key) == 'string' then return string.format('.%s', key) @@ -133,6 +155,12 @@ local function keyname_fmt(key) end end +---Check that a table conforms to the given table type qualifier. +--- +---@param tbl table? The table to check. +---@param expected_fields table The table type qualifier. +---@return boolean? `true` if the table conforms to the type qualifier. +---@return string? error_message The formatted error message if the check fails. local function check_table_type(tbl, expected_fields) if tbl == nil then tbl = nil @@ -144,11 +172,13 @@ local function check_table_type(tbl, expected_fields) if type(expected_type) == 'string' then local ok, efmt = check_string_type(value, expected_type) if not ok then + ---@cast efmt string return nil, string.format(efmt, '%s'..keyname_fmt(expected_key), '%s') end elseif type(expected_type) == 'table' then local ok, efmt = check_string_type(value, '?table') if not ok then + ---@cast efmt string return nil, string.format(efmt, '%s'..keyname_fmt(expected_key), '%s') end @@ -159,6 +189,7 @@ local function check_table_type(tbl, expected_fields) local ok, efmt = check_table_type(value, expected_type) if not ok then + ---@cast efmt string return nil, string.format(efmt, '%s'..keyname_fmt(expected_key), '%s') end else @@ -187,12 +218,27 @@ local function check_table_type(tbl, expected_fields) return true end +---Checks that the arguments of the calling function conform to the specified +---types. Must be called at the top of the function being checked; each type +---qualifier corresponds to the argument at the same position. +--- +---String qualifiers check Lua types, Tarantool-specific types (`uint64`, +---`int64`, `decimal`, `uuid`, etc.), metatable `__type` values, and custom +---checker names; they can be combined into union types (`'number|string'`) and +---made optional (`'?string'`). Table qualifiers validate the values of a table +---argument. +--- +---@param ... checks.qualifier|number Type qualifiers, one per argument to +--- check. The first argument may also be a stack level (used internally). local function checks(...) local skip = 0 + ---@type integer local level = 1 - if type(...) == 'number' then - level = ... + local first = ... + if type(first) == 'number' then + ---@cast first integer + level = first skip = 1 end level = level + 1 -- escape the checks level @@ -216,6 +262,7 @@ local function checks(...) local ok, efmt = check_string_type(value, expected_type) if not ok then local info = debug.getinfo(level, 'nl') + ---@cast efmt string local err = string.format(efmt, '#'..tostring(i), info.name) error(err, level) end @@ -224,18 +271,23 @@ local function checks(...) local ok, efmt = check_string_type(value, '?table') if not ok then local info = debug.getinfo(level, 'nl') + ---@cast efmt string local err = string.format(efmt, '#'..tostring(i), info.name) error(err, level) end if rawget(_G, '_checks_v2_compatible') and value == nil then value = {} + -- In emmylua-check 0.22.0 the debug.setlocal index + -- parameter is mistyped as `string`; it is an integer. + ---@diagnostic disable-next-line: param-type-mismatch debug.setlocal(level, i, value) end local ok, efmt = check_table_type(value, expected_type) if not ok then local info = debug.getinfo(level, 'nl') + ---@cast efmt string local err = string.format(efmt, argname, info.name) error(err, level) end @@ -249,15 +301,39 @@ local function checks(...) end end +---The `checks` function is also available as a global, so it can be used +---without loading the module (Tarantool 2.11.0 and later). rawset(_G, 'checks', checks) +---The `checkers` global table provides access to checkers for different types. +---It can be extended with custom checkers that perform arbitrary validations. +--- +---@class checks.checkers +---@field datetime fun(arg: any): boolean Check that the value is a datetime object. +---@field decimal fun(arg: any): boolean Check that the value has the decimal type. +---@field error fun(arg: any): boolean Check that the value is an error object. +---@field int64 fun(arg: any): boolean Check that the value is an int64 value. +---@field interval fun(arg: any): boolean Check that the value is an interval object. +---@field tuple fun(arg: any): boolean Check that the value is a tuple. +---@field uint64 fun(arg: any): boolean Check that the value is a uint64 value. +---@field uuid fun(arg: any): boolean Check that the value is a uuid object. +---@field uuid_bin fun(arg: any): boolean Check that the value is a uuid as a 16-byte binary string. +---@field uuid_str fun(arg: any): boolean Check that the value is a uuid as a 36-byte hexadecimal string. local checkers = rawget(_G, 'checkers') or {} rawset(_G, 'checkers', checkers) +---When set to `true`, substitutes `nil` table arguments with empty tables for +---backward compatibility with v2.1. +---@type boolean local _checks_v2_compatible = rawget(_G, '_checks_v2_compatible') or false rawset(_G, '_checks_v2_compatible', _checks_v2_compatible) -local ffi = require('ffi') +---Check whether the specified value is a `uint64` value: an integer Lua number +---in the range from 0 to 2^53-1 (inclusive), a cdata `ctype`, or a +---cdata `ctype` in the range from 0 to `LLONG_MAX`. +--- +---@param arg any The value to check. +---@return boolean `true` if the value is a `uint64` value, `false` otherwise. function checkers.uint64(arg) if type(arg) == 'number' then -- Double floating point format has 52 fraction bits @@ -277,6 +353,13 @@ function checkers.uint64(arg) return false end +---Check whether the specified value is an `int64` value: an integer Lua +---number in the range from -2^53+1 to 2^53-1 (inclusive), a cdata +---`ctype`, or a cdata `ctype` in the range from 0 to +---`LLONG_MAX`. +--- +---@param arg any The value to check. +---@return boolean `true` if the value is an `int64` value, `false` otherwise. function checkers.int64(arg) if type(arg) == 'number' then return (arg > -2^53) and (arg < 2^53) and (math.floor(arg) == arg) @@ -293,6 +376,7 @@ function checkers.int64(arg) return false end +---Check whether the specified value is a tuple. local has_box = rawget(_G, 'box') ~= nil if has_box and box.tuple ~= nil then checkers.tuple = box.tuple.is @@ -303,11 +387,19 @@ if has_decimal then -- There is a decimal.is_decimal check since 2.4, but we -- reimplement it here to support older versions which have decimal. local cdata_t = ffi.typeof(decimal.new(0)) + ---Check whether the specified value has the decimal type. + --- + ---@param arg any The value to check. + ---@return boolean `true` if the value has the decimal type, `false` otherwise. checkers.decimal = function(arg) return ffi.istype(cdata_t, arg) end end +---Register a checker for a cdata type checked via FFI. +--- +---@param checks_type string The name of the checker to register in `checkers`. +---@param c_type string The C type name, e.g. `'struct tt_uuid'`. local function add_ffi_type_checker(checks_type, c_type) local has_cdata_t, cdata_t = pcall(ffi.typeof, c_type) if has_cdata_t then @@ -322,6 +414,11 @@ end -- https://github.com/tarantool/tarantool/blob/7682d34162be34648172d91008e9185301bce8f6/src/lua/uuid.lua#L29 add_ffi_type_checker('uuid', 'struct tt_uuid') +---Check whether the specified value is a uuid represented by a 36-byte +---hexadecimal string. +--- +---@param arg any The value to check. +---@return boolean `true` if the value is a uuid string, `false` otherwise. function checkers.uuid_str(arg) if type(arg) == 'string' and #arg == 36 then local match = arg:match( @@ -339,6 +436,11 @@ function checkers.uuid_str(arg) end end +---Check whether the specified value is a uuid represented by a 16-byte binary +---string. +--- +---@param arg any The value to check. +---@return boolean `true` if the value is a uuid binary string, `false` otherwise. function checkers.uuid_bin(arg) if type(arg) == 'string' and #arg == 16 then return true @@ -349,6 +451,7 @@ end add_ffi_type_checker('error', 'struct error') +---Check whether the specified value is a datetime object. local has_datetime, datetime = pcall(require, 'datetime') if has_datetime then checkers.datetime = datetime.is_datetime @@ -356,7 +459,7 @@ end add_ffi_type_checker('interval', 'struct interval') -return setmetatable( +local M = setmetatable( { checks = checks, _VERSION = require('checks.version'), @@ -368,3 +471,5 @@ return setmetatable( end } ) + +return M diff --git a/checks/version.lua b/checks/version.lua index 37ef5c9..a37cd51 100644 --- a/checks/version.lua +++ b/checks/version.lua @@ -1,4 +1,4 @@ -- Сontains the module version. -- Requires manual update in case of release commit. -return '3.3.0' +return '3.4.0' diff --git a/debian/prebuild.sh b/debian/prebuild.sh index ecc64d2..3f2a4be 100755 --- a/debian/prebuild.sh +++ b/debian/prebuild.sh @@ -2,4 +2,13 @@ set -e -o pipefail -curl -LsSf https://www.tarantool.io/release/1.10/installer.sh | sudo bash +if [[ $DIST == "noble" ]] || [[ $DIST == "bookworm" ]]; then + curl -LsSf https://www.tarantool.io/release/3/installer.sh | sudo bash +elif [[ $DIST == "impish" ]] || [[ $DIST == "jammy" ]]; then + curl -LsSf https://www.tarantool.io/release/2/installer.sh | sudo bash +else + curl -LsSf https://www.tarantool.io/release/1.10/installer.sh | sudo bash +fi + +sudo apt install -y tt +tt version diff --git a/test/perftest.lua b/test/perftest.lua index 13502e7..1a83176 100755 --- a/test/perftest.lua +++ b/test/perftest.lua @@ -134,7 +134,7 @@ for _, case in pairs(cases) do local name = ('test_%s_%s'):format(json.encode(case.check), case.argtype):gsub('%.', '_') g[name] = function(_) - local fn = function(arg) -- luacheck: no unused args + local fn = function(_arg) checks(case.check) end diff --git a/test/test.lua b/test/test.lua index 1f55aa3..652d5b6 100755 --- a/test/test.lua +++ b/test/test.lua @@ -19,41 +19,41 @@ end local testdata = {} local _l_number_optstring = 2 + debug.getinfo(1).currentline -function testdata.fn_number_optstring(arg1, arg2) -- luacheck: no unused args +function testdata.fn_number_optstring(_arg1, _arg2) checks('number', '?string') end local _l_number_or_string = 2 + debug.getinfo(1).currentline -function testdata.fn_number_or_string(arg1) -- luacheck: no unused args +function testdata.fn_number_or_string(_arg1) checks('number|string') end local _l_positive_number = 2 + debug.getinfo(1).currentline -function testdata.fn_positive_number(arg1) -- luacheck: no unused args +function testdata.fn_positive_number(_arg1) checks('positive_number') end -function testdata.fn_anytype(arg1) -- luacheck: no unused args +function testdata.fn_anytype(_arg1) checks('?') end local _l_nil_or_number_or_string = 2 + debug.getinfo(1).currentline -function testdata.fn_nil_or_number_or_string(arg1) -- luacheck: no unused args +function testdata.fn_nil_or_number_or_string(_arg1) checks('nil|number|string') end local _l_optnumber_or_optstring = 2 + debug.getinfo(1).currentline -function testdata.fn_optnumber_or_optstring(arg1) -- luacheck: no unused args +function testdata.fn_optnumber_or_optstring(_arg1) checks('?number|?string') end local _l_varargs = 2 + debug.getinfo(1).currentline -function testdata.fn_varargs(arg1, ...) -- luacheck: no unused args +function testdata.fn_varargs(_arg1, ...) -- luacheck: ignore 212 checks('string') end local _l_options = 2 + debug.getinfo(1).currentline -function testdata.fn_options(options) -- luacheck: no unused args +function testdata.fn_options(_options) checks({ mystring = '?string', mynumber = '?number', @@ -61,17 +61,17 @@ function testdata.fn_options(options) -- luacheck: no unused args end local _l_array = 2 + debug.getinfo(1).currentline -function testdata.fn_array(array) -- luacheck: no unused args +function testdata.fn_array(_array) checks({'number', 'number'}) end local _l_table = 2 + debug.getinfo(1).currentline -function testdata.fn_table(table) -- luacheck: no unused args +function testdata.fn_table(_table) checks({mykey = 'number'}) end local _l_inception = 2 + debug.getinfo(1).currentline -function testdata.fn_inception(options) -- luacheck: no unused args +function testdata.fn_inception(_options) checks({ we = { need = { @@ -90,27 +90,27 @@ local function deepchecks() end local _l_deepcheck = 2 + debug.getinfo(1).currentline -function testdata.fn_deepcheck(arg1) -- luacheck: no unused args +function testdata.fn_deepcheck(_arg1) deepchecks() end local _l_excess_checks = 2 + debug.getinfo(1).currentline -function testdata.fn_excess_checks(arg1) -- luacheck: no unused args +function testdata.fn_excess_checks(_arg1) checks('?number', '?string') end local _l_missing_checks = 2 + debug.getinfo(1).currentline -function testdata.fn_missing_checks(arg1, arg2) -- luacheck: no unused args +function testdata.fn_missing_checks(_arg1, _arg2) checks('?number') end local _l_bad_check_type_1 = 2 + debug.getinfo(1).currentline -function testdata.bad_check_type_1(arg1, arg2) -- luacheck: no unused args +function testdata.bad_check_type_1(_arg1, _arg2) checks("?string", 5) end local _l_bad_check_type_2 = 2 + debug.getinfo(1).currentline -function testdata.bad_check_type_2(arg1, arg2) -- luacheck: no unused args +function testdata.bad_check_type_2(_arg1, _arg2) checks({param = 5}) end @@ -294,12 +294,12 @@ local err_cases = { { code = 'fn_options({mynumber = "bad"})', line = _l_options, - error = 'bad argument options.mynumber to fn_options (?number expected, got string)', + error = 'bad argument _options.mynumber to fn_options (?number expected, got string)', }, { code = 'fn_options({badfield = "bad"})', line = _l_options, - error = 'unexpected argument options.badfield to fn_options', + error = 'unexpected argument _options.badfield to fn_options', }, -- fn_array @@ -311,32 +311,32 @@ local err_cases = { { code = 'fn_array()', line = _l_array, - error = 'bad argument array[1] to fn_array (number expected, got nil)', + error = 'bad argument _array[1] to fn_array (number expected, got nil)', }, { code = 'fn_array(nil)', line = _l_array, - error = 'bad argument array[1] to fn_array (number expected, got nil)', + error = 'bad argument _array[1] to fn_array (number expected, got nil)', }, { code = 'fn_array(box.NULL)', line = _l_array, - error = 'bad argument array[1] to fn_array (number expected, got nil)', + error = 'bad argument _array[1] to fn_array (number expected, got nil)', }, { code = 'fn_array({})', line = _l_array, - error = 'bad argument array[1] to fn_array (number expected, got nil)', + error = 'bad argument _array[1] to fn_array (number expected, got nil)', }, { code = 'fn_array({"str1"})', line = _l_array, - error = 'bad argument array[1] to fn_array (number expected, got string)', + error = 'bad argument _array[1] to fn_array (number expected, got string)', }, { code = 'fn_array({1})', line = _l_array, - error = 'bad argument array[2] to fn_array (number expected, got nil)', + error = 'bad argument _array[2] to fn_array (number expected, got nil)', }, { code = 'fn_array({1, 2})', @@ -344,7 +344,7 @@ local err_cases = { { code = 'fn_array({1, 2, 3})', line = _l_array, - error = 'unexpected argument array[3] to fn_array', + error = 'unexpected argument _array[3] to fn_array', }, -- fn_table @@ -356,17 +356,17 @@ local err_cases = { { code = 'fn_table()', line = _l_table, - error = 'bad argument table.mykey to fn_table (number expected, got nil)', + error = 'bad argument _table.mykey to fn_table (number expected, got nil)', }, { code = 'fn_table({})', line = _l_table, - error = 'bad argument table.mykey to fn_table (number expected, got nil)', + error = 'bad argument _table.mykey to fn_table (number expected, got nil)', }, { code = 'fn_table({mykey = "str"})', line = _l_table, - error = 'bad argument table.mykey to fn_table (number expected, got string)', + error = 'bad argument _table.mykey to fn_table (number expected, got string)', }, { code = 'fn_table({mykey = 0})', @@ -374,7 +374,7 @@ local err_cases = { { code = 'fn_table({mykey = 0, excess = 1})', line = _l_table, - error = 'unexpected argument table.excess to fn_table', + error = 'unexpected argument _table.excess to fn_table', }, -- fn_inception @@ -387,7 +387,7 @@ local err_cases = { { code = 'fn_inception({we = false})', line = _l_inception, - error = 'bad argument options.we to fn_inception (?table expected, got boolean)', + error = 'bad argument _options.we to fn_inception (?table expected, got boolean)', }, { code = 'fn_inception({we = {}})', @@ -407,7 +407,7 @@ local err_cases = { { code = 'fn_inception({we = {need = {to = {go = {deeper = {}}}}}})', line = _l_inception, - error = 'bad argument options.we.need.to.go.deeper to fn_inception (?number expected, got table)', + error = 'bad argument _options.we.need.to.go.deeper to fn_inception (?number expected, got table)', }, -- fn_deepcheck @@ -429,7 +429,7 @@ local err_cases = { { code = 'fn_missing_checks()', line = _l_missing_checks, - error = 'checks: argument "arg2" is not checked', + error = 'checks: argument "_arg2" is not checked', }, { code = 'bad_check_type_1()', @@ -456,6 +456,7 @@ for _, case in pairs(err_cases) do g[name] = function(_) local fn = loadstring(case.code) + ---@cast fn function local ok, err = pcall(fn) if case.error == nil then @@ -582,34 +583,34 @@ for _, case in pairs(options_v2_cases) do end ------------------------------------------------------------------------------ -function testdata.fn_int64(arg) -- luacheck: no unused args +function testdata.fn_int64(_arg) checks('int64') end -function testdata.fn_uint64(arg) -- luacheck: no unused args +function testdata.fn_uint64(_arg) checks('uint64') end local uuid = require('uuid') testdata.myid = uuid() -function testdata.fn_uuid(arg) -- luacheck: no unused args +function testdata.fn_uuid(_arg) checks('uuid') end -function testdata.fn_uuid_str(arg) -- luacheck: no unused args +function testdata.fn_uuid_str(_arg) checks('uuid_str') end -function testdata.fn_uuid_bin(arg) -- luacheck: no unused args +function testdata.fn_uuid_bin(_arg) checks('uuid_bin') end -function testdata.fn_tuple(arg) -- luacheck: no unused args +function testdata.fn_tuple(_arg) checks('tuple') end -function testdata.fn_decimal(arg) -- luacheck: no unused args +function testdata.fn_decimal(_arg) checks('decimal') end @@ -618,13 +619,13 @@ if has_decimal then testdata.decimal = decimal end -function testdata.fn_error(arg) -- luacheck: no unused args +function testdata.fn_error(_arg) checks('error') end local has_error = (box.error ~= nil) and (box.error.new ~= nil) -function testdata.fn_datetime(arg) -- luacheck: no unused args +function testdata.fn_datetime(_arg) checks('datetime') end @@ -633,7 +634,7 @@ if has_datetime then testdata.datetime = datetime end -function testdata.fn_interval(arg) -- luacheck: no unused args +function testdata.fn_interval(_arg) checks('interval') end @@ -1095,6 +1096,7 @@ for _, case in pairs(ret_cases) do t.skip_if(case.skip, "type unsupported") local fn = loadstring(case.code) + ---@cast fn function local ok, err = pcall(fn) t.assert_equals(case.ok, ok, err) end diff --git a/test/types.lua b/test/types.lua new file mode 100644 index 0000000..07e6b2b --- /dev/null +++ b/test/types.lua @@ -0,0 +1,6 @@ +---@meta +--- +--- Tarantool extensions to the standard Lua `table` library. +--- +---@class tablelib +---@field deepcopy fun(t: table): table Deep-copies a table.