summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/cli/test.lua1
-rw-r--r--test/cli/visualize/test.lua23
-rw-r--r--test/cli/visualize/testdata/all-types-expected.txt587
-rw-r--r--test/cli/visualize/testdata/all-types.txt30
-rw-r--r--test/cli/visualize/testdata/shorten-names-expected.txt327
-rw-r--r--test/cli/visualize/testdata/shorten-names.txt18
-rw-r--r--test/code_action/init.lua144
-rw-r--r--test/completion/common.lua299
-rw-r--r--test/crossfile/completion.lua14
-rw-r--r--test/crossfile/diagnostic.lua24
-rw-r--r--test/crossfile/hover.lua57
-rw-r--r--test/crossfile/references.lua4
-rw-r--r--test/diagnostics/ambiguity-1.lua29
-rw-r--r--test/diagnostics/assign-type-mismatch.lua481
-rw-r--r--test/diagnostics/await-in-sync.lua132
-rw-r--r--test/diagnostics/cast-local-type.lua334
-rw-r--r--test/diagnostics/cast-type-mismatch.lua13
-rw-r--r--test/diagnostics/circle-doc-class.lua14
-rw-r--r--test/diagnostics/close-non-object.lua18
-rw-r--r--test/diagnostics/code-after-break.lua7
-rw-r--r--test/diagnostics/common.lua2270
-rw-r--r--test/diagnostics/count-down-loop.lua29
-rw-r--r--test/diagnostics/deprecated.lua21
-rw-r--r--test/diagnostics/discard-returns.lua17
-rw-r--r--test/diagnostics/doc-field-no-class.lua16
-rw-r--r--test/diagnostics/duplicate-doc-alias.lua10
-rw-r--r--test/diagnostics/duplicate-doc-field.lua38
-rw-r--r--test/diagnostics/duplicate-doc-param.lua12
-rw-r--r--test/diagnostics/duplicate-index.lua24
-rw-r--r--test/diagnostics/duplicate-set-field.lua74
-rw-r--r--test/diagnostics/empty-block.lua32
-rw-r--r--test/diagnostics/global-element.lua34
-rw-r--r--test/diagnostics/global-in-nil-env.lua44
-rw-r--r--test/diagnostics/incomplete-signature-doc.lua167
-rw-r--r--test/diagnostics/init.lua115
-rw-r--r--test/diagnostics/inject-field.lua84
-rw-r--r--test/diagnostics/invisible.lua145
-rw-r--r--test/diagnostics/lowercase-global.lua39
-rw-r--r--test/diagnostics/missing-fields.lua233
-rw-r--r--test/diagnostics/missing-global-doc.lua36
-rw-r--r--test/diagnostics/missing-local-export-doc.lua175
-rw-r--r--test/diagnostics/missing-parameter.lua90
-rw-r--r--test/diagnostics/missing-return-value.lua34
-rw-r--r--test/diagnostics/missing-return.lua158
-rw-r--r--test/diagnostics/need-check-nil.lua68
-rw-r--r--test/diagnostics/newfield-call.lua15
-rw-r--r--test/diagnostics/newline-call.lua34
-rw-r--r--test/diagnostics/not-yieldable.lua48
-rw-r--r--test/diagnostics/param-type-mismatch.lua248
-rw-r--r--test/diagnostics/redefined-local.lua22
-rw-r--r--test/diagnostics/redundant-parameter.lua214
-rw-r--r--test/diagnostics/redundant-return-value.lua32
-rw-r--r--test/diagnostics/redundant-return.lua34
-rw-r--r--test/diagnostics/redundant-value.lua7
-rw-r--r--test/diagnostics/return-type-mismatch.lua167
-rw-r--r--test/diagnostics/trailing-space.lua32
-rw-r--r--test/diagnostics/type-check.lua1262
-rw-r--r--test/diagnostics/unbalanced-assignments.lua34
-rw-r--r--test/diagnostics/undefined-doc-class.lua3
-rw-r--r--test/diagnostics/undefined-doc-name.lua19
-rw-r--r--test/diagnostics/undefined-doc-param.lua21
-rw-r--r--test/diagnostics/undefined-env-child.lua36
-rw-r--r--test/diagnostics/undefined-field.lua148
-rw-r--r--test/diagnostics/undefined-global.lua36
-rw-r--r--test/diagnostics/unknown-cast-variable.lua8
-rw-r--r--test/diagnostics/unknown-diag-code.lua3
-rw-r--r--test/diagnostics/unknown-operator.lua4
-rw-r--r--test/diagnostics/unreachable-code.lua71
-rw-r--r--test/diagnostics/unused-function.lua48
-rw-r--r--test/diagnostics/unused-label.lua3
-rw-r--r--test/diagnostics/unused-local.lua99
-rw-r--r--test/diagnostics/unused-vararg.lua6
-rw-r--r--test/full/example.lua2
-rw-r--r--test/full/init.lua2
-rw-r--r--test/full/projects.lua2
-rw-r--r--test/full/self.lua4
-rw-r--r--test/hover/init.lua52
-rw-r--r--test/plugins/ast/helper.lua64
-rw-r--r--test/plugins/ast/test.lua145
-rw-r--r--test/plugins/ffi/builder.lua233
-rw-r--r--test/plugins/ffi/cdef.lua61
-rw-r--r--test/plugins/ffi/parser.lua176
-rw-r--r--test/plugins/ffi/test.lua43
-rw-r--r--test/plugins/node/test.lua56
-rw-r--r--test/plugins/test.lua3
-rw-r--r--test/signature/init.lua42
-rw-r--r--test/type_inference/init.lua58
87 files changed, 6400 insertions, 3718 deletions
diff --git a/test/cli/test.lua b/test/cli/test.lua
new file mode 100644
index 00000000..7b988f68
--- /dev/null
+++ b/test/cli/test.lua
@@ -0,0 +1 @@
+require 'cli.visualize.test'
diff --git a/test/cli/visualize/test.lua b/test/cli/visualize/test.lua
new file mode 100644
index 00000000..c9722c32
--- /dev/null
+++ b/test/cli/visualize/test.lua
@@ -0,0 +1,23 @@
+local visualize = require 'cli.visualize'
+
+local testDataDir = 'test/cli/visualize/testdata/'
+
+local function TestVisualize(fileName)
+ local inputFile = testDataDir .. fileName .. '.txt'
+ local outputFile = testDataDir .. fileName .. '-expected.txt'
+ local output = ''
+ local writer = {}
+ function writer:write(text)
+ output = output .. text
+ end
+ visualize.visualizeAst(io.open(inputFile):read('a'), writer)
+ local expectedOutput = io.open(outputFile):read('a')
+ if expectedOutput ~= output then
+ -- uncomment this to update reference output
+ --io.open(outputFile, "w+"):write(output):close()
+ error('output mismatch for test file ' .. inputFile)
+ end
+end
+
+TestVisualize('all-types')
+TestVisualize('shorten-names')
diff --git a/test/cli/visualize/testdata/all-types-expected.txt b/test/cli/visualize/testdata/all-types-expected.txt
new file mode 100644
index 00000000..5391658c
--- /dev/null
+++ b/test/cli/visualize/testdata/all-types-expected.txt
@@ -0,0 +1,587 @@
+digraph AST {
+ node [shape = rect]
+ "main:0:300000" [
+ label="main\l\l"
+ tooltip="start: 0\nfinish: 300000\nlocals: <table>\nstate: <table>\nreturns: <table>\n1: <node setglobal>\n2: <node setfield>\n3: <node setindex>\n4: <node setmethod>\n5: <node local>\n6: <node while>\n7: <node call>\n8: <node loop>\n9: <node setglobal>\n10: <node if>\n"
+ ]
+ "setglobal:0:3" [
+ label="setglobal\lfoo\l"
+ tooltip="start: 0\nfinish: 3\nrange: 35\nvalue: <node table>\nnode: <node local>\n1: foo\n"
+ ]
+ "main:0:300000" -> "setglobal:0:3"
+ "table:6:35" [
+ label="table\l\l"
+ tooltip="start: 6\nfinish: 35\n1: <node tablefield>\n2: <node tablefield>\n3: <node tableindex>\n"
+ ]
+ "setglobal:0:3" -> "table:6:35"
+ "tablefield:7:8" [
+ label="tablefield\lx\l"
+ tooltip="start: 7\nfinish: 8\nnode: <node table>\nrange: 12\nfield: <node field>\nvalue: <node integer>\n"
+ ]
+ "table:6:35" -> "tablefield:7:8"
+ "field:7:8" [
+ label="field\lx\l"
+ tooltip="start: 7\nfinish: 8\n1: x\n"
+ ]
+ "tablefield:7:8" -> "field:7:8"
+ "integer:11:12" [
+ label="integer\l5\l"
+ tooltip="start: 11\nfinish: 12\n1: 5\n"
+ ]
+ "tablefield:7:8" -> "integer:11:12"
+ "tablefield:14:17" [
+ label="tablefield\lbar\l"
+ tooltip="start: 14\nfinish: 17\nnode: <node table>\nrange: 21\nfield: <node field>\nvalue: <node integer>\n"
+ ]
+ "table:6:35" -> "tablefield:14:17"
+ "field:14:17" [
+ label="field\lbar\l"
+ tooltip="start: 14\nfinish: 17\n1: bar\n"
+ ]
+ "tablefield:14:17" -> "field:14:17"
+ "integer:20:21" [
+ label="integer\l6\l"
+ tooltip="start: 20\nfinish: 21\n1: 6\n"
+ ]
+ "tablefield:14:17" -> "integer:20:21"
+ "tableindex:23:30" [
+ label="tableindex\lbaz\l"
+ tooltip="start: 23\nfinish: 30\nrange: 34\nnode: <node table>\nvalue: <node integer>\nindex: <node string>\n"
+ ]
+ "table:6:35" -> "tableindex:23:30"
+ "string:24:29" [
+ label="string\lbaz\l"
+ tooltip="start: 24\nfinish: 29\n1: baz\n2: \"\n"
+ ]
+ "tableindex:23:30" -> "string:24:29"
+ "integer:33:34" [
+ label="integer\l7\l"
+ tooltip="start: 33\nfinish: 34\n1: 7\n"
+ ]
+ "tableindex:23:30" -> "integer:33:34"
+ "setfield:10000:10005" [
+ label="setfield\ly\l"
+ tooltip="start: 10000\nfinish: 10005\nrange: 10017\nfield: <node field>\nnode: <node getglobal>\ndot: <node .>\nvalue: <node binary>\n"
+ ]
+ "main:0:300000" -> "setfield:10000:10005"
+ "getglobal:10000:10003" [
+ label="getglobal\lfoo\l"
+ tooltip="start: 10000\nfinish: 10003\nnext: <node setfield>\nnode: <node local>\n1: foo\n"
+ ]
+ "setfield:10000:10005" -> "getglobal:10000:10003"
+ "field:10004:10005" [
+ label="field\ly\l"
+ tooltip="start: 10004\nfinish: 10005\n1: y\n"
+ ]
+ "setfield:10000:10005" -> "field:10004:10005"
+ "binary:10008:10017" [
+ label="binary\l+\l"
+ tooltip="start: 10008\nfinish: 10017\nop: <node +>\n1: <node getfield>\n2: <node integer>\n"
+ ]
+ "setfield:10000:10005" -> "binary:10008:10017"
+ "getfield:10008:10013" [
+ label="getfield\lx\l"
+ tooltip="start: 10008\nfinish: 10013\nfield: <node field>\ndot: <node .>\nnode: <node getglobal>\n"
+ ]
+ "binary:10008:10017" -> "getfield:10008:10013"
+ "getglobal:10008:10011" [
+ label="getglobal\lfoo\l"
+ tooltip="start: 10008\nfinish: 10011\nnext: <node getfield>\nnode: <node local>\n1: foo\n"
+ ]
+ "getfield:10008:10013" -> "getglobal:10008:10011"
+ "field:10012:10013" [
+ label="field\lx\l"
+ tooltip="start: 10012\nfinish: 10013\n1: x\n"
+ ]
+ "getfield:10008:10013" -> "field:10012:10013"
+ "integer:10016:10017" [
+ label="integer\l1\l"
+ tooltip="start: 10016\nfinish: 10017\n1: 1\n"
+ ]
+ "binary:10008:10017" -> "integer:10016:10017"
+ "setindex:20000:20006" [
+ label="setindex\l1\l"
+ tooltip="start: 20000\nfinish: 20006\nrange: 20015\nnode: <node getglobal>\nvalue: <node getindex>\nindex: <node integer>\n"
+ ]
+ "main:0:300000" -> "setindex:20000:20006"
+ "getglobal:20000:20003" [
+ label="getglobal\lfoo\l"
+ tooltip="start: 20000\nfinish: 20003\nnext: <node setindex>\nnode: <node local>\n1: foo\n"
+ ]
+ "setindex:20000:20006" -> "getglobal:20000:20003"
+ "integer:20004:20005" [
+ label="integer\l1\l"
+ tooltip="start: 20004\nfinish: 20005\n1: 1\n"
+ ]
+ "setindex:20000:20006" -> "integer:20004:20005"
+ "getindex:20009:20015" [
+ label="getindex\l0\l"
+ tooltip="start: 20009\nfinish: 20015\nnode: <node getglobal>\nindex: <node integer>\n"
+ ]
+ "setindex:20000:20006" -> "getindex:20009:20015"
+ "getglobal:20009:20012" [
+ label="getglobal\lfoo\l"
+ tooltip="start: 20009\nfinish: 20012\nnext: <node getindex>\nnode: <node local>\n1: foo\n"
+ ]
+ "getindex:20009:20015" -> "getglobal:20009:20012"
+ "integer:20013:20014" [
+ label="integer\l0\l"
+ tooltip="start: 20013\nfinish: 20014\n1: 0\n"
+ ]
+ "getindex:20009:20015" -> "integer:20013:20014"
+ "setmethod:30009:30023" [
+ label="setmethod\lsomeMethod\l"
+ tooltip="start: 30009\nfinish: 30023\nrange: 30042\nmethod: <node method>\nvalue: <node function>\ncolon: <node :>\nvstart: 30000\nnode: <node getglobal>\n"
+ ]
+ "main:0:300000" -> "setmethod:30009:30023"
+ "getglobal:30009:30012" [
+ label="getglobal\lfoo\l"
+ tooltip="start: 30009\nfinish: 30012\nnode: <node local>\nnext: <node setmethod>\n1: foo\n"
+ ]
+ "setmethod:30009:30023" -> "getglobal:30009:30012"
+ "method:30013:30023" [
+ label="method\lsomeMethod\l"
+ tooltip="start: 30013\nfinish: 30023\n1: someMethod\n"
+ ]
+ "setmethod:30009:30023" -> "method:30013:30023"
+ "function:30000:30042" [
+ label="function\l\l"
+ tooltip="start: 30000\nfinish: 30042\nhasReturn: true\nargs: <node funcargs>\nlocals: <table>\nkeyword: <table>\nbstart: 30025\nreturns: <table>\n1: <node return>\n"
+ ]
+ "setmethod:30009:30023" -> "function:30000:30042"
+ "funcargs:30023:30025" [
+ label="funcargs\l\l"
+ tooltip="start: 30023\nfinish: 30025\n1: <node self>\n"
+ ]
+ "function:30000:30042" -> "funcargs:30023:30025"
+ "self:30008:30008" [
+ label="self\lself\l"
+ tooltip="start: 30008\nfinish: 30008\neffect: 30008\n1: self\n"
+ ]
+ "funcargs:30023:30025" -> "self:30008:30008"
+ "return:30026:30038" [
+ label="return\l\l"
+ tooltip="start: 30026\nfinish: 30038\n1: <node boolean>\n"
+ ]
+ "function:30000:30042" -> "return:30026:30038"
+ "boolean:30033:30038" [
+ label="boolean\l\l"
+ tooltip="start: 30033\nfinish: 30038\n1: false\n"
+ ]
+ "return:30026:30038" -> "boolean:30033:30038"
+ "local:40006:40007" [
+ label="local\ls\l"
+ tooltip="start: 40006\nfinish: 40007\nrange: 40011\nlocPos: 40000\nvalue: <node integer>\nref: <table>\neffect: 40011\n1: s\n"
+ ]
+ "main:0:300000" -> "local:40006:40007"
+ "integer:40010:40011" [
+ label="integer\l0\l"
+ tooltip="start: 40010\nfinish: 40011\n1: 0\n"
+ ]
+ "local:40006:40007" -> "integer:40010:40011"
+ "while:50000:70003" [
+ label="while\l\l"
+ tooltip="start: 50000\nfinish: 70003\nfilter: <node binary>\nkeyword: <table>\nbstart: 50013\n1: <node setlocal>\n"
+ ]
+ "main:0:300000" -> "while:50000:70003"
+ "binary:50006:50012" [
+ label="binary\l<\l"
+ tooltip="start: 50006\nfinish: 50012\nop: <node <>\n1: <node getlocal>\n2: <node integer>\n"
+ ]
+ "while:50000:70003" -> "binary:50006:50012"
+ "getlocal:50006:50007" [
+ label="getlocal\ls\l"
+ tooltip="start: 50006\nfinish: 50007\nnode: <node local>\n1: s\n"
+ ]
+ "binary:50006:50012" -> "getlocal:50006:50007"
+ "integer:50010:50012" [
+ label="integer\l10\l"
+ tooltip="start: 50010\nfinish: 50012\n1: 10\n"
+ ]
+ "binary:50006:50012" -> "integer:50010:50012"
+ "setlocal:60001:60002" [
+ label="setlocal\ls\l"
+ tooltip="start: 60001\nfinish: 60002\nrange: 60038\nvalue: <node binary>\nnode: <node local>\n1: s\n"
+ ]
+ "while:50000:70003" -> "setlocal:60001:60002"
+ "binary:60005:60038" [
+ label="binary\l+\l"
+ tooltip="start: 60005\nfinish: 60038\nop: <node +>\n1: <node getlocal>\n2: <node paren>\n"
+ ]
+ "setlocal:60001:60002" -> "binary:60005:60038"
+ "getlocal:60005:60006" [
+ label="getlocal\ls\l"
+ tooltip="start: 60005\nfinish: 60006\nnode: <node local>\n1: s\n"
+ ]
+ "binary:60005:60038" -> "getlocal:60005:60006"
+ "paren:60009:60038" [
+ label="paren\l\l"
+ tooltip="start: 60009\nfinish: 60038\nexp: <node binary>\n"
+ ]
+ "binary:60005:60038" -> "paren:60009:60038"
+ "binary:60010:60037" [
+ label="binary\lor\l"
+ tooltip="start: 60010\nfinish: 60037\nop: <node or>\n1: <node call>\n2: <node integer>\n"
+ ]
+ "paren:60009:60038" -> "binary:60010:60037"
+ "call:60010:60031" [
+ label="call\l\l"
+ tooltip="start: 60010\nfinish: 60031\nargs: <node callargs>\nnode: <node getmethod>\n"
+ ]
+ "binary:60010:60037" -> "call:60010:60031"
+ "getmethod:60010:60024" [
+ label="getmethod\lsomeMethod\l"
+ tooltip="start: 60010\nfinish: 60024\nmethod: <node method>\nnode: <node getglobal>\ncolon: <node :>\n"
+ ]
+ "call:60010:60031" -> "getmethod:60010:60024"
+ "getglobal:60010:60013" [
+ label="getglobal\lfoo\l"
+ tooltip="start: 60010\nfinish: 60013\nnext: <node getmethod>\nnode: <node local>\n1: foo\n"
+ ]
+ "getmethod:60010:60024" -> "getglobal:60010:60013"
+ "method:60014:60024" [
+ label="method\lsomeMethod\l"
+ tooltip="start: 60014\nfinish: 60024\n1: someMethod\n"
+ ]
+ "getmethod:60010:60024" -> "method:60014:60024"
+ "callargs:60024:60031" [
+ label="callargs\l\l"
+ tooltip="start: 60024\nfinish: 60031\n1: <node self>\n2: <node getglobal>\n3: <node integer>\n"
+ ]
+ "call:60010:60031" -> "callargs:60024:60031"
+ "self:60013:60014" [
+ label="self\lself\l"
+ tooltip="start: 60013\nfinish: 60014\n1: self\n"
+ ]
+ "callargs:60024:60031" -> "self:60013:60014"
+ "getglobal:60025:60026" [
+ label="getglobal\li\l"
+ tooltip="start: 60025\nfinish: 60026\nnode: <node local>\n1: i\n"
+ ]
+ "callargs:60024:60031" -> "getglobal:60025:60026"
+ "integer:60028:60030" [
+ label="integer\l10\l"
+ tooltip="start: 60028\nfinish: 60030\n1: 10\n"
+ ]
+ "callargs:60024:60031" -> "integer:60028:60030"
+ "integer:60035:60037" [
+ label="integer\l10\l"
+ tooltip="start: 60035\nfinish: 60037\n1: 10\n"
+ ]
+ "binary:60010:60037" -> "integer:60035:60037"
+ "call:80000:80008" [
+ label="call\l\l"
+ tooltip="start: 80000\nfinish: 80008\nargs: <node callargs>\nnode: <node getglobal>\n"
+ ]
+ "main:0:300000" -> "call:80000:80008"
+ "getglobal:80000:80005" [
+ label="getglobal\lprint\l"
+ tooltip="start: 80000\nfinish: 80005\nnode: <node local>\n1: print\n"
+ ]
+ "call:80000:80008" -> "getglobal:80000:80005"
+ "callargs:80005:80008" [
+ label="callargs\l\l"
+ tooltip="start: 80005\nfinish: 80008\n1: <node getlocal>\n"
+ ]
+ "call:80000:80008" -> "callargs:80005:80008"
+ "getlocal:80006:80007" [
+ label="getlocal\ls\l"
+ tooltip="start: 80006\nfinish: 80007\nnode: <node local>\n1: s\n"
+ ]
+ "callargs:80005:80008" -> "getlocal:80006:80007"
+ "loop:90000:140003" [
+ label="loop\l\l"
+ tooltip="start: 90000\nfinish: 140003\ninit: <node integer>\nstep: <node integer>\nmax: <node integer>\nlocals: <table>\nkeyword: <table>\nbstart: 90018\nloc: <node local>\n1: <node in>\n"
+ ]
+ "main:0:300000" -> "loop:90000:140003"
+ "local:90004:90005" [
+ label="local\lj\l"
+ tooltip="start: 90004\nfinish: 90005\neffect: 90017\n1: j\n"
+ ]
+ "loop:90000:140003" -> "local:90004:90005"
+ "integer:90008:90010" [
+ label="integer\l10\l"
+ tooltip="start: 90008\nfinish: 90010\n1: 10\n"
+ ]
+ "loop:90000:140003" -> "integer:90008:90010"
+ "integer:90012:90013" [
+ label="integer\l1\l"
+ tooltip="start: 90012\nfinish: 90013\n1: 1\n"
+ ]
+ "loop:90000:140003" -> "integer:90012:90013"
+ "integer:90015:90017" [
+ label="integer\l-1\l"
+ tooltip="start: 90015\nfinish: 90017\n1: -1\n"
+ ]
+ "loop:90000:140003" -> "integer:90015:90017"
+ "in:100001:130004" [
+ label="in\l\l"
+ tooltip="start: 100001\nfinish: 130004\nexps: <node list>\nlabels: <table>\nlocals: <table>\nkeyword: <table>\nbstart: 100020\nkeys: <node list>\n1: <node goto>\n2: <node label>\n"
+ ]
+ "loop:90000:140003" -> "in:100001:130004"
+ "list:100005:100006" [
+ label="list\l\l"
+ tooltip="start: 100005\nfinish: 100006\nrange: 100009\n1: <node local>\n"
+ ]
+ "in:100001:130004" -> "list:100005:100006"
+ "local:100005:100006" [
+ label="local\li\l"
+ tooltip="start: 100005\nfinish: 100006\neffect: 100019\n1: i\n"
+ ]
+ "list:100005:100006" -> "local:100005:100006"
+ "list:100010:100019" [
+ label="list\l\l"
+ tooltip="start: 100010\nfinish: 100019\n1: <node call>\n"
+ ]
+ "in:100001:130004" -> "list:100010:100019"
+ "call:100010:100019" [
+ label="call\l\l"
+ tooltip="start: 100010\nfinish: 100019\nargs: <node callargs>\nnode: <node getglobal>\n"
+ ]
+ "list:100010:100019" -> "call:100010:100019"
+ "getglobal:100010:100016" [
+ label="getglobal\lipairs\l"
+ tooltip="start: 100010\nfinish: 100016\nspecial: ipairs\nnode: <node local>\n1: ipairs\n"
+ ]
+ "call:100010:100019" -> "getglobal:100010:100016"
+ "callargs:100016:100019" [
+ label="callargs\l\l"
+ tooltip="start: 100016\nfinish: 100019\n1: <node getlocal>\n"
+ ]
+ "call:100010:100019" -> "callargs:100016:100019"
+ "getlocal:100017:100018" [
+ label="getlocal\ls\l"
+ tooltip="start: 100017\nfinish: 100018\nnode: <node local>\n1: s\n"
+ ]
+ "callargs:100016:100019" -> "getlocal:100017:100018"
+ "goto:110007:110015" [
+ label="goto\lfoolabel\l"
+ tooltip="start: 110007\nfinish: 110015\nkeyStart: 110002\nnode: <node label>\n1: foolabel\n"
+ ]
+ "in:100001:130004" -> "goto:110007:110015"
+ "label:120004:120012" [
+ label="label\lfoolabel\l"
+ tooltip="start: 120004\nfinish: 120012\nref: <table>\n1: foolabel\n"
+ ]
+ "in:100001:130004" -> "label:120004:120012"
+ "setglobal:160009:160012" [
+ label="setglobal\lfoo\l"
+ tooltip="start: 160009\nfinish: 160012\nrange: 210003\nvstart: 160000\nvalue: <node function>\nnode: <node local>\n1: foo\n"
+ ]
+ "main:0:300000" -> "setglobal:160009:160012"
+ "function:160000:210003" [
+ label="function\l\l"
+ tooltip="start: 160000\nfinish: 210003\nkeyword: <table>\nhasReturn: true\nargs: <node funcargs>\nbstart: 160014\nreturns: <table>\n1: <node return>\n"
+ ]
+ "setglobal:160009:160012" -> "function:160000:210003"
+ "funcargs:160012:160014" [
+ label="funcargs\l\l"
+ tooltip="start: 160012\nfinish: 160014\n"
+ ]
+ "function:160000:210003" -> "funcargs:160012:160014"
+ "return:170001:200004" [
+ label="return\l\l"
+ tooltip="start: 170001\nfinish: 200004\n1: <node function>\n"
+ ]
+ "function:160000:210003" -> "return:170001:200004"
+ "function:170008:200004" [
+ label="function\l\l"
+ tooltip="start: 170008\nfinish: 200004\nargs: <node funcargs>\nlocals: <table>\nbstart: 170024\nkeyword: <table>\nvararg: <node ...>\n1: <node repeat>\n"
+ ]
+ "return:170001:200004" -> "function:170008:200004"
+ "funcargs:170016:170024" [
+ label="funcargs\l\l"
+ tooltip="start: 170016\nfinish: 170024\n1: <node local>\n2: <node ...>\n"
+ ]
+ "function:170008:200004" -> "funcargs:170016:170024"
+ "local:170017:170018" [
+ label="local\lx\l"
+ tooltip="start: 170017\nfinish: 170018\neffect: 170018\n1: x\n"
+ ]
+ "funcargs:170016:170024" -> "local:170017:170018"
+ "...:170020:170023" [
+ label="...\l\l"
+ tooltip="start: 170020\nfinish: 170023\nref: <table>\n1: ...\n"
+ ]
+ "funcargs:170016:170024" -> "...:170020:170023"
+ "repeat:180002:190028" [
+ label="repeat\l\l"
+ tooltip="start: 180002\nfinish: 190028\nfilter: <node binary>\nkeyword: <table>\nbstart: 180008\n"
+ ]
+ "function:170008:200004" -> "repeat:180002:190028"
+ "binary:190008:190028" [
+ label="binary\l>\l"
+ tooltip="start: 190008\nfinish: 190028\nop: <node >>\n1: <node call>\n2: <node integer>\n"
+ ]
+ "repeat:180002:190028" -> "binary:190008:190028"
+ "call:190008:190024" [
+ label="call\l\l"
+ tooltip="start: 190008\nfinish: 190024\nargs: <node callargs>\nnode: <node getglobal>\n"
+ ]
+ "binary:190008:190028" -> "call:190008:190024"
+ "getglobal:190008:190014" [
+ label="getglobal\lselect\l"
+ tooltip="start: 190008\nfinish: 190014\nnode: <node local>\n1: select\n"
+ ]
+ "call:190008:190024" -> "getglobal:190008:190014"
+ "callargs:190014:190024" [
+ label="callargs\l\l"
+ tooltip="start: 190014\nfinish: 190024\n1: <node string>\n2: <node varargs>\n"
+ ]
+ "call:190008:190024" -> "callargs:190014:190024"
+ "string:190015:190018" [
+ label="string\l#\l"
+ tooltip="start: 190015\nfinish: 190018\n1: #\n2: '\n"
+ ]
+ "callargs:190014:190024" -> "string:190015:190018"
+ "varargs:190020:190023" [
+ label="varargs\l\l"
+ tooltip="start: 190020\nfinish: 190023\nnode: <node ...>\n"
+ ]
+ "callargs:190014:190024" -> "varargs:190020:190023"
+ "integer:190027:190028" [
+ label="integer\l0\l"
+ tooltip="start: 190027\nfinish: 190028\n1: 0\n"
+ ]
+ "binary:190008:190028" -> "integer:190027:190028"
+ "if:230000:290003" [
+ label="if\l\l"
+ tooltip="start: 230000\nfinish: 290003\n1: <node ifblock>\n2: <node elseifblock>\n3: <node elseblock>\n"
+ ]
+ "main:0:300000" -> "if:230000:290003"
+ "ifblock:230000:250000" [
+ label="ifblock\l\l"
+ tooltip="start: 230000\nfinish: 250000\nbstart: 230009\nfilter: <node getglobal>\nhasReturn: true\nkeyword: <table>\n1: <node return>\n"
+ ]
+ "if:230000:290003" -> "ifblock:230000:250000"
+ "getglobal:230003:230004" [
+ label="getglobal\lx\l"
+ tooltip="start: 230003\nfinish: 230004\nnode: <node local>\n1: x\n"
+ ]
+ "ifblock:230000:250000" -> "getglobal:230003:230004"
+ "return:240001:240048" [
+ label="return\l\l"
+ tooltip="start: 240001\nfinish: 240048\n1: <node binary>\n"
+ ]
+ "ifblock:230000:250000" -> "return:240001:240048"
+ "binary:240008:240048" [
+ label="binary\lor\l"
+ tooltip="start: 240008\nfinish: 240048\nop: <node or>\n1: <node binary>\n2: <node binary>\n"
+ ]
+ "return:240001:240048" -> "binary:240008:240048"
+ "binary:240008:240023" [
+ label="binary\lor\l"
+ tooltip="start: 240008\nfinish: 240023\nop: <node or>\n1: <node binary>\n2: <node binary>\n"
+ ]
+ "binary:240008:240048" -> "binary:240008:240023"
+ "binary:240008:240013" [
+ label="binary\l<\l"
+ tooltip="start: 240008\nfinish: 240013\nop: <node <>\n1: <node getglobal>\n2: <node integer>\n"
+ ]
+ "binary:240008:240023" -> "binary:240008:240013"
+ "getglobal:240008:240009" [
+ label="getglobal\lx\l"
+ tooltip="start: 240008\nfinish: 240009\nnode: <node local>\n1: x\n"
+ ]
+ "binary:240008:240013" -> "getglobal:240008:240009"
+ "integer:240012:240013" [
+ label="integer\l0\l"
+ tooltip="start: 240012\nfinish: 240013\n1: 0\n"
+ ]
+ "binary:240008:240013" -> "integer:240012:240013"
+ "binary:240017:240023" [
+ label="binary\l>=\l"
+ tooltip="start: 240017\nfinish: 240023\nop: <node >=>\n1: <node getglobal>\n2: <node integer>\n"
+ ]
+ "binary:240008:240023" -> "binary:240017:240023"
+ "getglobal:240017:240018" [
+ label="getglobal\lx\l"
+ tooltip="start: 240017\nfinish: 240018\nnode: <node local>\n1: x\n"
+ ]
+ "binary:240017:240023" -> "getglobal:240017:240018"
+ "integer:240022:240023" [
+ label="integer\l0\l"
+ tooltip="start: 240022\nfinish: 240023\n1: 0\n"
+ ]
+ "binary:240017:240023" -> "integer:240022:240023"
+ "binary:240027:240048" [
+ label="binary\land\l"
+ tooltip="start: 240027\nfinish: 240048\nop: <node and>\n1: <node binary>\n2: <node binary>\n"
+ ]
+ "binary:240008:240048" -> "binary:240027:240048"
+ "binary:240027:240037" [
+ label="binary\l<=\l"
+ tooltip="start: 240027\nfinish: 240037\nop: <node <=>\n1: <node unary>\n2: <node integer>\n"
+ ]
+ "binary:240027:240048" -> "binary:240027:240037"
+ "unary:240027:240032" [
+ label="unary\lnot\l"
+ tooltip="start: 240027\nfinish: 240032\nop: <node not>\n1: <node getglobal>\n"
+ ]
+ "binary:240027:240037" -> "unary:240027:240032"
+ "getglobal:240031:240032" [
+ label="getglobal\lx\l"
+ tooltip="start: 240031\nfinish: 240032\nnode: <node local>\n1: x\n"
+ ]
+ "unary:240027:240032" -> "getglobal:240031:240032"
+ "integer:240036:240037" [
+ label="integer\l0\l"
+ tooltip="start: 240036\nfinish: 240037\n1: 0\n"
+ ]
+ "binary:240027:240037" -> "integer:240036:240037"
+ "binary:240042:240048" [
+ label="binary\l>\l"
+ tooltip="start: 240042\nfinish: 240048\nop: <node >>\n1: <node unary>\n2: <node integer>\n"
+ ]
+ "binary:240027:240048" -> "binary:240042:240048"
+ "unary:240042:240044" [
+ label="unary\l-\l"
+ tooltip="start: 240042\nfinish: 240044\nop: <node ->\n1: <node getglobal>\n"
+ ]
+ "binary:240042:240048" -> "unary:240042:240044"
+ "getglobal:240043:240044" [
+ label="getglobal\lx\l"
+ tooltip="start: 240043\nfinish: 240044\nnode: <node local>\n1: x\n"
+ ]
+ "unary:240042:240044" -> "getglobal:240043:240044"
+ "integer:240047:240048" [
+ label="integer\l7\l"
+ tooltip="start: 240047\nfinish: 240048\n1: 7\n"
+ ]
+ "binary:240042:240048" -> "integer:240047:240048"
+ "elseifblock:250000:270000" [
+ label="elseifblock\l\l"
+ tooltip="start: 250000\nfinish: 270000\nbstart: 250013\nfilter: <node getglobal>\nhasReturn: true\nkeyword: <table>\n1: <node return>\n"
+ ]
+ "if:230000:290003" -> "elseifblock:250000:270000"
+ "getglobal:250007:250008" [
+ label="getglobal\ly\l"
+ tooltip="start: 250007\nfinish: 250008\nnode: <node local>\n1: y\n"
+ ]
+ "elseifblock:250000:270000" -> "getglobal:250007:250008"
+ "return:260001:260011" [
+ label="return\l\l"
+ tooltip="start: 260001\nfinish: 260011\n1: <node number>\n"
+ ]
+ "elseifblock:250000:270000" -> "return:260001:260011"
+ "number:260008:260011" [
+ label="number\l5.7\l"
+ tooltip="start: 260008\nfinish: 260011\n1: 5.7\n"
+ ]
+ "return:260001:260011" -> "number:260008:260011"
+ "elseblock:270000:290000" [
+ label="elseblock\l\l"
+ tooltip="start: 270000\nfinish: 290000\nkeyword: <table>\nbstart: 270004\nhasReturn: true\n1: <node return>\n"
+ ]
+ "if:230000:290003" -> "elseblock:270000:290000"
+ "return:280001:280091" [
+ label="return\l\l"
+ tooltip="start: 280001\nfinish: 280091\n1: <node string>\n"
+ ]
+ "elseblock:270000:290000" -> "return:280001:280091"
+ "string:280008:280091" [
+ label="string\la very long strin...\l"
+ tooltip="start: 280008\nfinish: 280091\n1: a very long strin...\n2: \"\n"
+ ]
+ "return:280001:280091" -> "string:280008:280091"
+}
diff --git a/test/cli/visualize/testdata/all-types.txt b/test/cli/visualize/testdata/all-types.txt
new file mode 100644
index 00000000..167bd54b
--- /dev/null
+++ b/test/cli/visualize/testdata/all-types.txt
@@ -0,0 +1,30 @@
+foo = {x = 5, bar = 6, ["baz"] = 7}
+foo.y = foo.x + 1
+foo[1] = foo[0]
+function foo:someMethod() return false end
+local s = 0
+while s < 10 do
+ s = s + (foo:someMethod(i, 10) or 10)
+end
+print(s)
+for j = 10, 1, -1 do
+ for i in ipairs(s) do
+ goto foolabel
+ ::foolabel::
+ end
+end
+
+function foo()
+ return function(x, ...)
+ repeat
+ until select('#', ...) > 0
+ end
+end
+
+if x then
+ return x < 0 or x >= 0 or not x <= 0 and -x > 7
+elseif y then
+ return 5.7
+else
+ return "a very long string that should get shortened to not destroy the layout completely"
+end
diff --git a/test/cli/visualize/testdata/shorten-names-expected.txt b/test/cli/visualize/testdata/shorten-names-expected.txt
new file mode 100644
index 00000000..c03a697a
--- /dev/null
+++ b/test/cli/visualize/testdata/shorten-names-expected.txt
@@ -0,0 +1,327 @@
+digraph AST {
+ node [shape = rect]
+ "main:0:170009" [
+ label="main\l\l"
+ tooltip="start: 0\nfinish: 170009\nlocals: <table>\nstate: <table>\n1: <node setglobal>\n2: <node setglobal>\n3: <node setglobal>\n4: <node setglobal>\n5: <node setglobal>\n6: <node setglobal>\n7: <node setglobal>\n8: <node setglobal>\n9: <node setglobal>\n10: <node setglobal>\n11: <node setglobal>\n12: <node setglobal>\n13: <node setglobal>\n14: <node setglobal>\n15: <node setglobal>\n15..17: (...)\n"
+ ]
+ "setglobal:0:1" [
+ label="setglobal\ls\l"
+ tooltip="start: 0\nfinish: 1\nrange: 101\nvalue: <node string>\nnode: <node local>\n1: s\n"
+ ]
+ "main:0:170009" -> "setglobal:0:1"
+ "string:4:101" [
+ label="string\lvery long string ...\l"
+ tooltip="start: 4\nfinish: 101\n1: very long string ...\n2: \"\n"
+ ]
+ "setglobal:0:1" -> "string:4:101"
+ "setglobal:10000:10001" [
+ label="setglobal\ls\l"
+ tooltip="start: 10000\nfinish: 10001\nrange: 10031\nvalue: <node string>\nnode: <node local>\n1: s\n"
+ ]
+ "main:0:170009" -> "setglobal:10000:10001"
+ "string:10004:10031" [
+ label="string\lstring\\nwith\\n\...\l"
+ tooltip="start: 10004\nfinish: 10031\nescs: <table>\n1: string\\nwith\\n\...\n2: \"\n"
+ ]
+ "setglobal:10000:10001" -> "string:10004:10031"
+ "setglobal:20000:20001" [
+ label="setglobal\lx\l"
+ tooltip="start: 20000\nfinish: 20001\nrange: 20009\nvalue: <node binary>\nnode: <node local>\n1: x\n"
+ ]
+ "main:0:170009" -> "setglobal:20000:20001"
+ "binary:20004:20009" [
+ label="binary\l+\l"
+ tooltip="start: 20004\nfinish: 20009\nop: <node +>\n1: <node getglobal>\n2: <node integer>\n"
+ ]
+ "setglobal:20000:20001" -> "binary:20004:20009"
+ "getglobal:20004:20005" [
+ label="getglobal\lx\l"
+ tooltip="start: 20004\nfinish: 20005\nnode: <node local>\n1: x\n"
+ ]
+ "binary:20004:20009" -> "getglobal:20004:20005"
+ "integer:20008:20009" [
+ label="integer\l1\l"
+ tooltip="start: 20008\nfinish: 20009\n1: 1\n"
+ ]
+ "binary:20004:20009" -> "integer:20008:20009"
+ "setglobal:30000:30001" [
+ label="setglobal\lx\l"
+ tooltip="start: 30000\nfinish: 30001\nrange: 30009\nvalue: <node binary>\nnode: <node local>\n1: x\n"
+ ]
+ "main:0:170009" -> "setglobal:30000:30001"
+ "binary:30004:30009" [
+ label="binary\l+\l"
+ tooltip="start: 30004\nfinish: 30009\nop: <node +>\n1: <node getglobal>\n2: <node integer>\n"
+ ]
+ "setglobal:30000:30001" -> "binary:30004:30009"
+ "getglobal:30004:30005" [
+ label="getglobal\lx\l"
+ tooltip="start: 30004\nfinish: 30005\nnode: <node local>\n1: x\n"
+ ]
+ "binary:30004:30009" -> "getglobal:30004:30005"
+ "integer:30008:30009" [
+ label="integer\l1\l"
+ tooltip="start: 30008\nfinish: 30009\n1: 1\n"
+ ]
+ "binary:30004:30009" -> "integer:30008:30009"
+ "setglobal:40000:40001" [
+ label="setglobal\lx\l"
+ tooltip="start: 40000\nfinish: 40001\nrange: 40009\nvalue: <node binary>\nnode: <node local>\n1: x\n"
+ ]
+ "main:0:170009" -> "setglobal:40000:40001"
+ "binary:40004:40009" [
+ label="binary\l+\l"
+ tooltip="start: 40004\nfinish: 40009\nop: <node +>\n1: <node getglobal>\n2: <node integer>\n"
+ ]
+ "setglobal:40000:40001" -> "binary:40004:40009"
+ "getglobal:40004:40005" [
+ label="getglobal\lx\l"
+ tooltip="start: 40004\nfinish: 40005\nnode: <node local>\n1: x\n"
+ ]
+ "binary:40004:40009" -> "getglobal:40004:40005"
+ "integer:40008:40009" [
+ label="integer\l1\l"
+ tooltip="start: 40008\nfinish: 40009\n1: 1\n"
+ ]
+ "binary:40004:40009" -> "integer:40008:40009"
+ "setglobal:50000:50001" [
+ label="setglobal\lx\l"
+ tooltip="start: 50000\nfinish: 50001\nrange: 50009\nvalue: <node binary>\nnode: <node local>\n1: x\n"
+ ]
+ "main:0:170009" -> "setglobal:50000:50001"
+ "binary:50004:50009" [
+ label="binary\l+\l"
+ tooltip="start: 50004\nfinish: 50009\nop: <node +>\n1: <node getglobal>\n2: <node integer>\n"
+ ]
+ "setglobal:50000:50001" -> "binary:50004:50009"
+ "getglobal:50004:50005" [
+ label="getglobal\lx\l"
+ tooltip="start: 50004\nfinish: 50005\nnode: <node local>\n1: x\n"
+ ]
+ "binary:50004:50009" -> "getglobal:50004:50005"
+ "integer:50008:50009" [
+ label="integer\l1\l"
+ tooltip="start: 50008\nfinish: 50009\n1: 1\n"
+ ]
+ "binary:50004:50009" -> "integer:50008:50009"
+ "setglobal:60000:60001" [
+ label="setglobal\lx\l"
+ tooltip="start: 60000\nfinish: 60001\nrange: 60009\nvalue: <node binary>\nnode: <node local>\n1: x\n"
+ ]
+ "main:0:170009" -> "setglobal:60000:60001"
+ "binary:60004:60009" [
+ label="binary\l+\l"
+ tooltip="start: 60004\nfinish: 60009\nop: <node +>\n1: <node getglobal>\n2: <node integer>\n"
+ ]
+ "setglobal:60000:60001" -> "binary:60004:60009"
+ "getglobal:60004:60005" [
+ label="getglobal\lx\l"
+ tooltip="start: 60004\nfinish: 60005\nnode: <node local>\n1: x\n"
+ ]
+ "binary:60004:60009" -> "getglobal:60004:60005"
+ "integer:60008:60009" [
+ label="integer\l1\l"
+ tooltip="start: 60008\nfinish: 60009\n1: 1\n"
+ ]
+ "binary:60004:60009" -> "integer:60008:60009"
+ "setglobal:70000:70001" [
+ label="setglobal\lx\l"
+ tooltip="start: 70000\nfinish: 70001\nrange: 70009\nvalue: <node binary>\nnode: <node local>\n1: x\n"
+ ]
+ "main:0:170009" -> "setglobal:70000:70001"
+ "binary:70004:70009" [
+ label="binary\l+\l"
+ tooltip="start: 70004\nfinish: 70009\nop: <node +>\n1: <node getglobal>\n2: <node integer>\n"
+ ]
+ "setglobal:70000:70001" -> "binary:70004:70009"
+ "getglobal:70004:70005" [
+ label="getglobal\lx\l"
+ tooltip="start: 70004\nfinish: 70005\nnode: <node local>\n1: x\n"
+ ]
+ "binary:70004:70009" -> "getglobal:70004:70005"
+ "integer:70008:70009" [
+ label="integer\l1\l"
+ tooltip="start: 70008\nfinish: 70009\n1: 1\n"
+ ]
+ "binary:70004:70009" -> "integer:70008:70009"
+ "setglobal:80000:80001" [
+ label="setglobal\lx\l"
+ tooltip="start: 80000\nfinish: 80001\nrange: 80009\nvalue: <node binary>\nnode: <node local>\n1: x\n"
+ ]
+ "main:0:170009" -> "setglobal:80000:80001"
+ "binary:80004:80009" [
+ label="binary\l+\l"
+ tooltip="start: 80004\nfinish: 80009\nop: <node +>\n1: <node getglobal>\n2: <node integer>\n"
+ ]
+ "setglobal:80000:80001" -> "binary:80004:80009"
+ "getglobal:80004:80005" [
+ label="getglobal\lx\l"
+ tooltip="start: 80004\nfinish: 80005\nnode: <node local>\n1: x\n"
+ ]
+ "binary:80004:80009" -> "getglobal:80004:80005"
+ "integer:80008:80009" [
+ label="integer\l1\l"
+ tooltip="start: 80008\nfinish: 80009\n1: 1\n"
+ ]
+ "binary:80004:80009" -> "integer:80008:80009"
+ "setglobal:90000:90001" [
+ label="setglobal\lx\l"
+ tooltip="start: 90000\nfinish: 90001\nrange: 90009\nvalue: <node binary>\nnode: <node local>\n1: x\n"
+ ]
+ "main:0:170009" -> "setglobal:90000:90001"
+ "binary:90004:90009" [
+ label="binary\l+\l"
+ tooltip="start: 90004\nfinish: 90009\nop: <node +>\n1: <node getglobal>\n2: <node integer>\n"
+ ]
+ "setglobal:90000:90001" -> "binary:90004:90009"
+ "getglobal:90004:90005" [
+ label="getglobal\lx\l"
+ tooltip="start: 90004\nfinish: 90005\nnode: <node local>\n1: x\n"
+ ]
+ "binary:90004:90009" -> "getglobal:90004:90005"
+ "integer:90008:90009" [
+ label="integer\l1\l"
+ tooltip="start: 90008\nfinish: 90009\n1: 1\n"
+ ]
+ "binary:90004:90009" -> "integer:90008:90009"
+ "setglobal:100000:100001" [
+ label="setglobal\lx\l"
+ tooltip="start: 100000\nfinish: 100001\nrange: 100009\nvalue: <node binary>\nnode: <node local>\n1: x\n"
+ ]
+ "main:0:170009" -> "setglobal:100000:100001"
+ "binary:100004:100009" [
+ label="binary\l+\l"
+ tooltip="start: 100004\nfinish: 100009\nop: <node +>\n1: <node getglobal>\n2: <node integer>\n"
+ ]
+ "setglobal:100000:100001" -> "binary:100004:100009"
+ "getglobal:100004:100005" [
+ label="getglobal\lx\l"
+ tooltip="start: 100004\nfinish: 100005\nnode: <node local>\n1: x\n"
+ ]
+ "binary:100004:100009" -> "getglobal:100004:100005"
+ "integer:100008:100009" [
+ label="integer\l1\l"
+ tooltip="start: 100008\nfinish: 100009\n1: 1\n"
+ ]
+ "binary:100004:100009" -> "integer:100008:100009"
+ "setglobal:110000:110001" [
+ label="setglobal\lx\l"
+ tooltip="start: 110000\nfinish: 110001\nrange: 110009\nvalue: <node binary>\nnode: <node local>\n1: x\n"
+ ]
+ "main:0:170009" -> "setglobal:110000:110001"
+ "binary:110004:110009" [
+ label="binary\l+\l"
+ tooltip="start: 110004\nfinish: 110009\nop: <node +>\n1: <node getglobal>\n2: <node integer>\n"
+ ]
+ "setglobal:110000:110001" -> "binary:110004:110009"
+ "getglobal:110004:110005" [
+ label="getglobal\lx\l"
+ tooltip="start: 110004\nfinish: 110005\nnode: <node local>\n1: x\n"
+ ]
+ "binary:110004:110009" -> "getglobal:110004:110005"
+ "integer:110008:110009" [
+ label="integer\l1\l"
+ tooltip="start: 110008\nfinish: 110009\n1: 1\n"
+ ]
+ "binary:110004:110009" -> "integer:110008:110009"
+ "setglobal:120000:120001" [
+ label="setglobal\lx\l"
+ tooltip="start: 120000\nfinish: 120001\nrange: 120009\nvalue: <node binary>\nnode: <node local>\n1: x\n"
+ ]
+ "main:0:170009" -> "setglobal:120000:120001"
+ "binary:120004:120009" [
+ label="binary\l+\l"
+ tooltip="start: 120004\nfinish: 120009\nop: <node +>\n1: <node getglobal>\n2: <node integer>\n"
+ ]
+ "setglobal:120000:120001" -> "binary:120004:120009"
+ "getglobal:120004:120005" [
+ label="getglobal\lx\l"
+ tooltip="start: 120004\nfinish: 120005\nnode: <node local>\n1: x\n"
+ ]
+ "binary:120004:120009" -> "getglobal:120004:120005"
+ "integer:120008:120009" [
+ label="integer\l1\l"
+ tooltip="start: 120008\nfinish: 120009\n1: 1\n"
+ ]
+ "binary:120004:120009" -> "integer:120008:120009"
+ "setglobal:130000:130001" [
+ label="setglobal\lx\l"
+ tooltip="start: 130000\nfinish: 130001\nrange: 130009\nvalue: <node binary>\nnode: <node local>\n1: x\n"
+ ]
+ "main:0:170009" -> "setglobal:130000:130001"
+ "binary:130004:130009" [
+ label="binary\l+\l"
+ tooltip="start: 130004\nfinish: 130009\nop: <node +>\n1: <node getglobal>\n2: <node integer>\n"
+ ]
+ "setglobal:130000:130001" -> "binary:130004:130009"
+ "getglobal:130004:130005" [
+ label="getglobal\lx\l"
+ tooltip="start: 130004\nfinish: 130005\nnode: <node local>\n1: x\n"
+ ]
+ "binary:130004:130009" -> "getglobal:130004:130005"
+ "integer:130008:130009" [
+ label="integer\l1\l"
+ tooltip="start: 130008\nfinish: 130009\n1: 1\n"
+ ]
+ "binary:130004:130009" -> "integer:130008:130009"
+ "setglobal:140000:140001" [
+ label="setglobal\lx\l"
+ tooltip="start: 140000\nfinish: 140001\nrange: 140009\nvalue: <node binary>\nnode: <node local>\n1: x\n"
+ ]
+ "main:0:170009" -> "setglobal:140000:140001"
+ "binary:140004:140009" [
+ label="binary\l+\l"
+ tooltip="start: 140004\nfinish: 140009\nop: <node +>\n1: <node getglobal>\n2: <node integer>\n"
+ ]
+ "setglobal:140000:140001" -> "binary:140004:140009"
+ "getglobal:140004:140005" [
+ label="getglobal\lx\l"
+ tooltip="start: 140004\nfinish: 140005\nnode: <node local>\n1: x\n"
+ ]
+ "binary:140004:140009" -> "getglobal:140004:140005"
+ "integer:140008:140009" [
+ label="integer\l1\l"
+ tooltip="start: 140008\nfinish: 140009\n1: 1\n"
+ ]
+ "binary:140004:140009" -> "integer:140008:140009"
+ "setglobal:160000:160001" [
+ label="setglobal\lx\l"
+ tooltip="start: 160000\nfinish: 160001\nrange: 160009\nvalue: <node binary>\nnode: <node local>\n1: x\n"
+ ]
+ "main:0:170009" -> "setglobal:160000:160001"
+ "binary:160004:160009" [
+ label="binary\l+\l"
+ tooltip="start: 160004\nfinish: 160009\nop: <node +>\n1: <node getglobal>\n2: <node integer>\n"
+ ]
+ "setglobal:160000:160001" -> "binary:160004:160009"
+ "getglobal:160004:160005" [
+ label="getglobal\lx\l"
+ tooltip="start: 160004\nfinish: 160005\nnode: <node local>\n1: x\n"
+ ]
+ "binary:160004:160009" -> "getglobal:160004:160005"
+ "integer:160008:160009" [
+ label="integer\l1\l"
+ tooltip="start: 160008\nfinish: 160009\n1: 1\n"
+ ]
+ "binary:160004:160009" -> "integer:160008:160009"
+ "setglobal:170000:170001" [
+ label="setglobal\lx\l"
+ tooltip="start: 170000\nfinish: 170001\nrange: 170009\nvalue: <node binary>\nnode: <node local>\n1: x\n"
+ ]
+ "main:0:170009" -> "setglobal:170000:170001"
+ "binary:170004:170009" [
+ label="binary\l+\l"
+ tooltip="start: 170004\nfinish: 170009\nop: <node +>\n1: <node getglobal>\n2: <node integer>\n"
+ ]
+ "setglobal:170000:170001" -> "binary:170004:170009"
+ "getglobal:170004:170005" [
+ label="getglobal\lx\l"
+ tooltip="start: 170004\nfinish: 170005\nnode: <node local>\n1: x\n"
+ ]
+ "binary:170004:170009" -> "getglobal:170004:170005"
+ "integer:170008:170009" [
+ label="integer\l1\l"
+ tooltip="start: 170008\nfinish: 170009\n1: 1\n"
+ ]
+ "binary:170004:170009" -> "integer:170008:170009"
+}
diff --git a/test/cli/visualize/testdata/shorten-names.txt b/test/cli/visualize/testdata/shorten-names.txt
new file mode 100644
index 00000000..90aa7f0e
--- /dev/null
+++ b/test/cli/visualize/testdata/shorten-names.txt
@@ -0,0 +1,18 @@
+s = "very long string that is very long and would be annoying if it was fully included in the output"
+s = "string\nwith\n\new\nlines"
+x = x + 1
+x = x + 1
+x = x + 1
+x = x + 1
+x = x + 1
+x = x + 1
+x = x + 1
+x = x + 1
+x = x + 1
+x = x + 1
+x = x + 1
+x = x + 1
+x = x + 1
+-- more than 15 lines should be shortened for tooltip in main
+x = x + 1
+x = x + 1 \ No newline at end of file
diff --git a/test/code_action/init.lua b/test/code_action/init.lua
index 2329ce4c..264cfacf 100644
--- a/test/code_action/init.lua
+++ b/test/code_action/init.lua
@@ -2,38 +2,40 @@ local core = require 'core.code-action'
local files = require 'files'
local lang = require 'language'
local catch = require 'catch'
+local furi = require 'file-uri'
rawset(_G, 'TEST', true)
local EXISTS = {}
-local function eq(a, b)
- if a == EXISTS and b ~= nil then
+local function eq(expected, result)
+ if expected == EXISTS and result ~= nil then
return true
end
- if b == EXISTS and a ~= nil then
+ if result == EXISTS and expected ~= nil then
return true
end
- local tp1, tp2 = type(a), type(b)
+ local tp1, tp2 = type(expected), type(result)
if tp1 ~= tp2 then
- return false
+ return false, string.format(": expected type %s, got %s", tp1, tp2)
end
if tp1 == 'table' then
local mark = {}
- for k in pairs(a) do
- if not eq(a[k], b[k]) then
- return false
+ for k in pairs(expected) do
+ local ok, err = eq(expected[k], result[k])
+ if not ok then
+ return false, string.format(".%s%s", k, err)
end
mark[k] = true
end
- for k in pairs(b) do
+ for k in pairs(result) do
if not mark[k] then
- return false
+ return false, string.format(".%s: missing key in result", k)
end
end
return true
end
- return a == b
+ return expected == result, string.format(": expected %s, got %s", expected, result)
end
function TEST(script)
@@ -47,6 +49,32 @@ function TEST(script)
end
end
+local function TEST_CROSSFILE(testfiles)
+ local mainscript = table.remove(testfiles, 1)
+ return function(expected)
+ for _, data in ipairs(testfiles) do
+ local uri = furi.encode(TESTROOT .. data.path)
+ files.setText(uri, data.content)
+ files.compileState(uri)
+ end
+
+ local newScript, catched = catch(mainscript, '?')
+ files.setText(TESTURI, newScript)
+ files.compileState(TESTURI)
+
+ local _ <close> = function ()
+ for _, info in ipairs(testfiles) do
+ files.remove(furi.encode(TESTROOT .. info.path))
+ end
+ files.remove(TESTURI)
+ end
+
+ local results = core(TESTURI, catched['?'][1][1], catched['?'][1][2])
+ assert(results)
+ assert(eq(expected, results))
+ end
+end
+
TEST [[
print(<?a?>, b, c)
]]
@@ -154,3 +182,97 @@ local t = {
-- edit = EXISTS,
-- },
--}
+
+TEST_CROSSFILE {
+[[
+ <?unrequiredModule?>.myFunction()
+]],
+ {
+ path = 'unrequiredModule.lua',
+ content = [[
+ local m = {}
+ m.myFunction = print
+ return m
+ ]]
+ }
+} {
+ {
+ title = lang.script('ACTION_AUTOREQUIRE', 'unrequiredModule', 'unrequiredModule'),
+ kind = 'refactor.rewrite',
+ command = {
+ title = 'autoRequire',
+ command = 'lua.autoRequire',
+ arguments = {
+ {
+ uri = TESTURI,
+ target = furi.encode(TESTROOT .. 'unrequiredModule.lua'),
+ name = 'unrequiredModule',
+ requireName = 'unrequiredModule'
+ },
+ },
+ }
+ }
+}
+
+TEST_CROSSFILE {
+[[
+ <?myModule?>.myFunction()
+]],
+ {
+ path = 'myModule/init.lua',
+ content = [[
+ local m = {}
+ m.myFunction = print
+ return m
+ ]]
+ }
+} {
+ {
+ title = lang.script('ACTION_AUTOREQUIRE', 'myModule.init', 'myModule'),
+ kind = 'refactor.rewrite',
+ command = {
+ title = 'autoRequire',
+ command = 'lua.autoRequire',
+ arguments = {
+ {
+ uri = TESTURI,
+ target = furi.encode(TESTROOT .. 'myModule/init.lua'),
+ name = 'myModule',
+ requireName = 'myModule.init'
+ },
+ },
+ }
+ },
+ {
+ title = lang.script('ACTION_AUTOREQUIRE', 'init', 'myModule'),
+ kind = 'refactor.rewrite',
+ command = {
+ title = 'autoRequire',
+ command = 'lua.autoRequire',
+ arguments = {
+ {
+ uri = TESTURI,
+ target = furi.encode(TESTROOT .. 'myModule/init.lua'),
+ name = 'myModule',
+ requireName = 'init'
+ },
+ },
+ }
+ },
+ {
+ title = lang.script('ACTION_AUTOREQUIRE', 'myModule', 'myModule'),
+ kind = 'refactor.rewrite',
+ command = {
+ title = 'autoRequire',
+ command = 'lua.autoRequire',
+ arguments = {
+ {
+ uri = TESTURI,
+ target = furi.encode(TESTROOT .. 'myModule/init.lua'),
+ name = 'myModule',
+ requireName = 'myModule'
+ },
+ },
+ }
+ },
+}
diff --git a/test/completion/common.lua b/test/completion/common.lua
index 8d23822a..90037c27 100644
--- a/test/completion/common.lua
+++ b/test/completion/common.lua
@@ -3816,6 +3816,32 @@ f(<??>)
}
TEST [[
+local x = 1
+local y = 2
+
+---@enum(key) Enum
+local t = {
+ x = x,
+ y = y,
+}
+
+---@param p Enum
+local function f(p) end
+
+f(<??>)
+]]
+{
+ {
+ label = '"x"',
+ kind = define.CompletionItemKind.EnumMember,
+ },
+ {
+ label = '"y"',
+ kind = define.CompletionItemKind.EnumMember,
+ },
+}
+
+TEST [[
--
<??>
]]
@@ -3838,6 +3864,12 @@ local x = function (x, y) end
(EXISTS)
TEST [[
+--- <??>
+local x = function (x, y) end
+]]
+(EXISTS)
+
+TEST [[
local x = {
<??>
})
@@ -4114,3 +4146,270 @@ f({
kind = define.CompletionItemKind.Text,
},
}
+
+TEST [[
+while true do
+ continue<??>
+end
+]]
+{
+ {
+ label = 'continue',
+ kind = define.CompletionItemKind.Keyword,
+ },
+ {
+ label = 'goto continue ..',
+ kind = define.CompletionItemKind.Snippet,
+ additionalTextEdits = {
+ {
+ start = 10004,
+ finish = 10004,
+ newText = 'goto ',
+ },
+ {
+ start = 20000,
+ finish = 20000,
+ newText = ' ::continue::\n',
+ },
+ }
+ },
+}
+
+TEST [[
+while true do
+ goto continue<??>
+end
+]]
+{
+ {
+ label = 'continue',
+ kind = define.CompletionItemKind.Keyword,
+ },
+ {
+ label = 'goto continue ..',
+ kind = define.CompletionItemKind.Snippet,
+ additionalTextEdits = {
+ {
+ start = 20000,
+ finish = 20000,
+ newText = ' ::continue::\n',
+ }
+ }
+ },
+}
+
+TEST [[
+while true do
+ goto continue<??>
+ ::continue::
+end
+]]
+{
+ {
+ label = 'continue',
+ kind = define.CompletionItemKind.Keyword,
+ },
+ {
+ label = 'goto continue ..',
+ kind = define.CompletionItemKind.Snippet,
+ additionalTextEdits = {
+ }
+ },
+}
+
+Cared['description'] = true
+TEST [[
+---@class Foo
+---@field ['with quotes'] integer
+---@field without_quotes integer
+
+---@type Foo
+local bar = {}
+
+bar.<??>
+]]
+{
+ {
+ label = "'with quotes'",
+ kind = define.CompletionItemKind.Field,
+ textEdit = {
+ start = 70004,
+ finish = 70004,
+ newText = "['with quotes']"
+ },
+ additionalTextEdits = {
+ {
+ start = 70003,
+ finish = 70004,
+ newText = '',
+ }
+ },
+ description = [[
+```lua
+(field) Foo['with quotes']: integer
+```]]
+ },
+ {
+ label = 'without_quotes',
+ kind = define.CompletionItemKind.Field,
+ description = [[
+```lua
+(field) Foo.without_quotes: integer
+```]]
+ },
+}
+Cared['description'] = false
+
+TEST [[
+---@class A
+local M = {}
+
+function M:method1()
+end
+
+function M.static1(tt)
+end
+
+function M:method2()
+end
+
+function M.static2(tt)
+end
+
+---@type A
+local a
+
+a.<??>
+]]
+{
+ {
+ label ='static1(tt)',
+ kind = define.CompletionItemKind.Function,
+ },
+ {
+ label ='static2(tt)',
+ kind = define.CompletionItemKind.Function,
+ },
+ {
+ label ='method1(self)',
+ kind = define.CompletionItemKind.Method,
+ },
+ {
+ label ='method2(self)',
+ kind = define.CompletionItemKind.Method,
+ },
+}
+
+TEST [[
+---@class A
+local M = {}
+
+function M:method1()
+end
+
+function M.static1(tt)
+end
+
+function M:method2()
+end
+
+function M.static2(tt)
+end
+
+---@type A
+local a
+
+a:<??>
+]]
+{
+ {
+ label ='method1()',
+ kind = define.CompletionItemKind.Method,
+ },
+ {
+ label ='method2()',
+ kind = define.CompletionItemKind.Method,
+ },
+ {
+ label ='static1()',
+ kind = define.CompletionItemKind.Function,
+ },
+ {
+ label ='static2()',
+ kind = define.CompletionItemKind.Function,
+ },
+}
+
+TEST [[
+---@class A
+---@field x number
+---@field y? number
+---@field z number
+
+---@type A
+local t = {
+ <??>
+}
+]]
+{
+ {
+ label = 'x',
+ kind = define.CompletionItemKind.Property,
+ },
+ {
+ label = 'z',
+ kind = define.CompletionItemKind.Property,
+ },
+ {
+ label = 'y?',
+ kind = define.CompletionItemKind.Property,
+ },
+}
+
+TEST [[
+---@class A
+---@field x number
+---@field y? number
+---@field z number
+
+---@param t A
+local function f(t) end
+
+f {
+ <??>
+}
+]]
+{
+ {
+ label = 'x',
+ kind = define.CompletionItemKind.Property,
+ },
+ {
+ label = 'z',
+ kind = define.CompletionItemKind.Property,
+ },
+ {
+ label = 'y?',
+ kind = define.CompletionItemKind.Property,
+ },
+}
+
+TEST [[
+---@class A
+---@overload fun(x: {id: string})
+
+---@generic T
+---@param t `T`
+---@return T
+local function new(t) end
+
+new 'A' {
+ <??>
+}
+]]
+{
+ {
+ label = 'id',
+ kind = define.CompletionItemKind.Property,
+ }
+}
diff --git a/test/crossfile/completion.lua b/test/crossfile/completion.lua
index 113b0327..227350cb 100644
--- a/test/crossfile/completion.lua
+++ b/test/crossfile/completion.lua
@@ -61,7 +61,7 @@ function TEST(data)
local mainUri
local pos
for _, info in ipairs(data) do
- local uri = furi.encode(info.path)
+ local uri = furi.encode(TESTROOT .. info.path)
local script = info.content or ''
if info.main then
local newScript, catched = catch(script, '?')
@@ -75,7 +75,7 @@ function TEST(data)
local _ <close> = function ()
for _, info in ipairs(data) do
- files.remove(furi.encode(info.path))
+ files.remove(furi.encode(TESTROOT .. info.path))
end
end
@@ -108,6 +108,12 @@ function TEST(data)
: gsub('\r\n', '\n')
end
end
+ for _, eitem in ipairs(expect) do
+ if eitem['description'] then
+ eitem['description'] = eitem['description']
+ : gsub('%$(.-)%$', _G)
+ end
+ end
assert(result)
assert(eq(expect, result))
end
@@ -936,7 +942,7 @@ TEST {
kind = CompletionItemKind.Variable,
detail = 'function',
description = [[
-从 [myfunc.lua](file:///myfunc.lua) 中导入
+从 [myfunc.lua]($TESTROOTURI$myfunc.lua) 中导入
```lua
function (a: any, b: any)
@@ -967,7 +973,7 @@ TEST {
kind = CompletionItemKind.Variable,
detail = 'function',
description = [[
-从 [dir\myfunc.lua](file:///dir/myfunc.lua) 中导入
+从 [dir\myfunc.lua]($TESTROOTURI$dir/myfunc.lua) 中导入
```lua
function (a: any, b: any)
diff --git a/test/crossfile/diagnostic.lua b/test/crossfile/diagnostic.lua
index e270d3da..1acf3fe2 100644
--- a/test/crossfile/diagnostic.lua
+++ b/test/crossfile/diagnostic.lua
@@ -58,20 +58,26 @@ function TEST(datas)
local results = {}
+ local origins = {}
for _, data in ipairs(datas) do
local uri = furi.encode(data.path)
core(uri, false, function (result)
- results[#results+1] = {
- result.start,
- result.finish,
- uri,
- }
+ if result.code == datas.code then
+ results[#results+1] = {
+ result.start,
+ result.finish,
+ uri,
+ }
+ end
+ origins[#origins+1] = result
end)
end
+ assert(datas.code, 'Need code')
assert(founded(targetList, results))
end
TEST {
+ code = 'different-requires',
{
path = 'f/a.lua',
content = '',
@@ -87,6 +93,7 @@ TEST {
}
TEST {
+ code = 'different-requires',
{
path = 'f/a.lua',
content = '',
@@ -106,6 +113,7 @@ TEST {
}
TEST {
+ code = 'different-requires',
{
path = 'a.lua',
content = '',
@@ -125,6 +133,7 @@ TEST {
}
TEST {
+ code = 'different-requires',
{
path = 'a/init.lua',
content = '',
@@ -144,6 +153,7 @@ TEST {
}
TEST {
+ code = 'invisible',
{ path = 'a.lua', content = [[
---@class A
---@field package x string
@@ -156,6 +166,7 @@ TEST {
}
TEST {
+ code = 'invisible',
{ path = 'a.lua', content = [[
---@class A
---@field package x string
@@ -169,6 +180,7 @@ TEST {
}
TEST {
+ code = 'duplicate-doc-field',
{ path = 'a.lua', content = [[
---@class A
---@field <!x!> number
@@ -180,6 +192,7 @@ TEST {
}
TEST {
+ code = 'duplicate-set-field',
{ path = 'a.lua', content = [[
---@class A
local mt
@@ -197,6 +210,7 @@ TEST {
}
TEST {
+ code = 'duplicate-set-field',
{ path = 'a.lua', content = [[
---@class A
local mt
diff --git a/test/crossfile/hover.lua b/test/crossfile/hover.lua
index d013af98..a18c714b 100644
--- a/test/crossfile/hover.lua
+++ b/test/crossfile/hover.lua
@@ -1637,6 +1637,30 @@ TEST {
{
path = 'a.lua',
content = [[
+ ---@enum(key) <?A?>
+ local t = {
+ x = 1 << 0,
+ y = 1 << 1,
+ z = 1 << 2,
+ }
+ ]]
+ },
+ hover = [[
+```lua
+(enum) A
+```
+
+---
+
+```lua
+"x" | "y" | "z"
+```]]
+}
+
+TEST {
+ {
+ path = 'a.lua',
+ content = [[
---@alias someType
---| "#" # description
@@ -1695,6 +1719,26 @@ function f(x: number, y: number)
```]]
}
+TEST { { path = 'a.lua', content = [[
+---@overload fun(self: self, x: number)
+---@overload fun(self: self, x: number, y: number)
+function M:f(...)
+end
+
+M:<?f?>
+]] },
+hover = [[
+```lua
+(method) M:f(x: number)
+```
+
+---
+
+```lua
+(method) M:f(x: number, y: number)
+```]]
+}
+
TEST { {path = 'a.lua', content = [[
---@class A
@@ -1795,3 +1839,16 @@ local x: integer = 1
comment1]]
}
+
+TEST { {path = 'a.lua', content = [[
+local t = {}
+
+print(<?t?>['a b'])
+]]},
+hover = [[
+```lua
+local t: {
+ ['a b']: unknown,
+}
+```]]
+}
diff --git a/test/crossfile/references.lua b/test/crossfile/references.lua
index faa9dba9..36c08170 100644
--- a/test/crossfile/references.lua
+++ b/test/crossfile/references.lua
@@ -58,7 +58,7 @@ function TEST(datas)
local sourceList
local sourceUri
for i, data in ipairs(datas) do
- local uri = furi.encode(data.path)
+ local uri = furi.encode(TESTROOT .. data.path)
local newScript, catched = catch(data.content, '!?~')
if catched['!'] or catched['~'] then
for _, position in ipairs(catched['!'] + catched['~']) do
@@ -79,7 +79,7 @@ function TEST(datas)
local _ <close> = function ()
for _, info in ipairs(datas) do
- files.remove(furi.encode(info.path))
+ files.remove(furi.encode(TESTROOT .. info.path))
end
end
diff --git a/test/diagnostics/ambiguity-1.lua b/test/diagnostics/ambiguity-1.lua
new file mode 100644
index 00000000..6b8e41da
--- /dev/null
+++ b/test/diagnostics/ambiguity-1.lua
@@ -0,0 +1,29 @@
+TEST [[
+local x
+x = <!x or 0 + 1!>
+]]
+
+TEST [[
+local x, y
+x = <!x + y or 0!>
+]]
+
+TEST [[
+local x, y, z
+x = x and y or '' .. z
+]]
+
+TEST [[
+local x
+x = x or -1
+]]
+
+TEST [[
+local x
+x = x or (0 + 1)
+]]
+
+TEST [[
+local x, y
+x = (x + y) or 0
+]]
diff --git a/test/diagnostics/assign-type-mismatch.lua b/test/diagnostics/assign-type-mismatch.lua
new file mode 100644
index 00000000..dc55a7da
--- /dev/null
+++ b/test/diagnostics/assign-type-mismatch.lua
@@ -0,0 +1,481 @@
+local config = require 'config'
+
+TEST [[
+local m = {}
+
+---@type integer[]
+m.ints = {}
+]]
+
+TEST [[
+---@class A
+---@field x A
+
+---@type A
+local t
+
+t.x = {}
+]]
+
+TEST [[
+---@class A
+---@field x integer
+
+---@type A
+local t
+
+<!t.x!> = true
+]]
+
+TEST [[
+---@class A
+---@field x integer
+
+---@type A
+local t
+
+---@type boolean
+local y
+
+<!t.x!> = y
+]]
+
+TEST [[
+---@class A
+local m
+
+m.x = 1
+
+---@type A
+local t
+
+<!t.x!> = true
+]]
+
+TEST [[
+---@class A
+local m
+
+---@type integer
+m.x = 1
+
+<!m.x!> = true
+]]
+
+TEST [[
+---@class A
+local mt
+
+---@type integer
+mt.x = 1
+
+function mt:init()
+ <!self.x!> = true
+end
+]]
+
+TEST [[
+---@class A
+---@field x integer
+
+---@type A
+local t = {
+ <!x!> = true
+}
+]]
+
+TEST [[
+---@type boolean[]
+local t = {}
+
+t[5] = nil
+]]
+
+TEST [[
+---@type table<string, true>
+local t = {}
+
+t['x'] = nil
+]]
+
+TEST [[
+---@type [boolean]
+local t = { <![1]!> = nil }
+
+t = nil
+]]
+
+TEST [[
+local t = { true }
+
+t[1] = nil
+]]
+
+TEST [[
+---@class A
+local t = {
+ x = 1
+}
+
+<!t.x!> = true
+]]
+
+TEST [[
+---@type number
+local t
+
+t = 1
+]]
+
+TEST [[
+---@type number
+local t
+
+---@type integer
+local y
+
+t = y
+]]
+
+TEST [[
+---@class A
+local m
+
+---@type number
+m.x = 1
+
+<!m.x!> = {}
+]]
+
+TEST [[
+local n
+
+if G then
+ n = {}
+else
+ n = nil
+end
+
+local t = {
+ x = n,
+}
+]]
+
+TEST [[
+---@type boolean[]
+local t = {}
+
+---@type boolean?
+local x
+
+t[#t+1] = x
+]]
+
+TEST [[
+---@type number
+local n
+---@type integer
+local i
+
+<?i?> = n
+]]
+
+config.set(nil, 'Lua.type.castNumberToInteger', true)
+TEST [[
+---@type number
+local n
+---@type integer
+local i
+
+i = n
+]]
+
+config.set(nil, 'Lua.type.castNumberToInteger', false)
+TEST [[
+---@type number|boolean
+local nb
+
+---@type number
+local n
+
+<?n?> = nb
+]]
+
+config.set(nil, 'Lua.type.weakUnionCheck', true)
+TEST [[
+---@type number|boolean
+local nb
+
+---@type number
+local n
+
+n = nb
+]]
+
+config.set(nil, 'Lua.type.weakUnionCheck', false)
+TEST [[
+---@class Option: string
+
+---@param x Option
+local function f(x) end
+
+---@type Option
+local x = 'aaa'
+
+f(x)
+]]
+config.set(nil, 'Lua.type.weakUnionCheck', true)
+
+TEST [[
+---@type number
+local <!x!> = 'aaa'
+]]
+TEST [[
+---@class X
+
+---@class A
+local mt = G
+
+---@type X
+mt._x = nil
+]]
+config.set(nil, 'Lua.type.weakUnionCheck', false)
+
+config.set(nil, 'Lua.type.weakNilCheck', true)
+TEST [[
+---@type number?
+local nb
+
+---@type number
+local n
+
+n = nb
+]]
+
+TEST [[
+---@type number|nil
+local nb
+
+---@type number
+local n
+
+n = nb
+]]
+config.set(nil, 'Lua.type.weakNilCheck', false)
+
+TEST [[
+---@class A
+local a = {}
+
+---@class B
+local <!b!> = a
+]]
+
+TEST [[
+---@class A
+local a = {}
+
+---@class B: A
+local b = a
+]]
+
+TEST [[
+---@class A
+local a = {}
+a.__index = a
+
+---@class B: A
+local b = setmetatable({}, a)
+]]
+
+TEST [[
+---@class A
+local a = {}
+
+---@class B: A
+local b = setmetatable({}, {__index = a})
+]]
+
+TEST [[
+---@class A
+local a = {}
+
+---@class B
+local <!b!> = setmetatable({}, {__index = a})
+]]
+
+TEST [[
+---@class A
+---@field x number?
+local a
+
+---@class B
+---@field x number
+local b
+
+b.x = a.x
+]]
+
+TEST [[
+
+---@class A
+---@field x number?
+local a
+
+---@type number
+local t
+
+t = a.x
+]]
+
+TEST [[
+local mt = {}
+mt.x = 1
+mt.x = nil
+]]
+
+config.set(nil, 'Lua.type.weakUnionCheck', true)
+TEST [[
+---@type number
+local x = G
+]]
+
+TEST [[
+---@generic T
+---@param x T
+---@return T
+local function f(x)
+ return x
+end
+]]
+config.set(nil, 'Lua.type.weakUnionCheck', false)
+
+
+TEST [[
+---@alias test boolean
+
+---@type test
+local <!test!> = 4
+]]
+
+TEST [[
+---@class MyClass
+local MyClass = {}
+
+function MyClass:new()
+ ---@class MyClass
+ local myObject = setmetatable({
+ initialField = true
+ }, self)
+
+ print(myObject.initialField)
+end
+]]
+
+TEST [[
+---@class T
+local t = {
+ x = nil
+}
+
+t.x = 1
+]]
+
+TEST [[
+---@type {[1]: string, [10]: number, xx: boolean}
+local t = {
+ <!true!>,
+ <![10]!> = 's',
+ <!xx!> = 1,
+}
+]]
+
+TEST [[
+---@type boolean[]
+local t = { <!1!>, <!2!>, <!3!> }
+]]
+
+TEST [[
+---@type boolean[]
+local t = { true, false, nil }
+]]
+
+TEST [[
+---@type boolean|nil
+local x
+
+---@type boolean[]
+local t = { true, false, x }
+]]
+
+TEST [[
+---@enum Enum
+local t = {
+ x = 1,
+ y = 2,
+}
+
+---@type Enum
+local y
+
+---@type integer
+local x = y
+]]
+
+TEST [[
+---@type string|string[]|string[][]
+local t = {{'a'}}
+]]
+
+TEST [[
+local A = "Hello"
+local B = "World"
+
+---@alias myLiteralAliases `A` | `B`
+
+---@type myLiteralAliases
+local x = A
+]]
+
+TEST [[
+local enum = { a = 1, b = 2 }
+
+---@type { [integer] : boolean }
+local t = {
+ <![enum.a]!> = 1,
+ <![enum.b]!> = 2,
+ <![3]!> = 3,
+}
+]]
+
+TEST [[
+---@class SomeClass
+---@field [1] string
+-- ...
+
+---@param some_param SomeClass|SomeClass[]
+local function some_fn(some_param) return end
+
+some_fn { { "test" } } -- <- diagnostic: "Cannot assign `table` to `string`."
+]]
+
+TEST [[
+---@type string[]
+local arr = {
+ <!3!>,
+}
+]]
+
+TEST [[
+---@type (string|boolean)[]
+local arr2 = {
+ <!3!>, -- no warnings
+}
+]]
+
+TEST [[
+local t = {}
+t.a = 1
+t.a = 2
+return t
+]]
diff --git a/test/diagnostics/await-in-sync.lua b/test/diagnostics/await-in-sync.lua
new file mode 100644
index 00000000..323c1113
--- /dev/null
+++ b/test/diagnostics/await-in-sync.lua
@@ -0,0 +1,132 @@
+TEST [[
+function F()
+ <!coroutine.yield!>()
+end
+]]
+
+TEST [[
+---@async
+function F()
+ coroutine.yield()
+end
+]]
+
+TEST [[
+---@type async fun()
+local f
+
+function F()
+ <!f!>()
+end
+]]
+
+TEST [[
+---@type async fun()
+local f
+
+---@async
+function F()
+ f()
+end
+]]
+
+TEST [[
+local function f(cb)
+ cb()
+end
+
+return function()
+ <!f>(function () ---@async
+ return nil
+ end)
+end
+]]
+
+TEST [[
+local function f(cb)
+ pcall(cb)
+end
+
+return function()
+ <!f!>(function () ---@async
+ return nil
+ end)
+end
+]]
+
+TEST [[
+---@param c any
+local function f(c)
+ return c
+end
+
+return function ()
+ f(function () ---@async
+ return nil
+ end)
+end
+]]
+
+TEST [[
+---@param ... any
+local function f(...)
+ return ...
+end
+
+return function ()
+ f(function () ---@async
+ return nil
+ end)
+end
+]]
+
+TEST [[
+---@vararg any
+local function f(...)
+ return ...
+end
+
+return function ()
+ f(function () ---@async
+ return nil
+ end)
+end
+]]
+
+TEST [[
+local function f(...)
+ return ...
+end
+
+return function ()
+ f(function () ---@async
+ return nil
+ end)
+end
+]]
+
+TEST [[
+local function f(...)
+ return ...
+end
+
+return function ()
+ f(function () ---@async
+ return nil
+ end)
+end
+]]
+
+TEST [[
+local function f(cb)
+ cb()
+end
+
+local function af()
+ <!f!>(function () ---@async
+ return nil
+ end)
+end
+
+return af
+]]
diff --git a/test/diagnostics/cast-local-type.lua b/test/diagnostics/cast-local-type.lua
new file mode 100644
index 00000000..f79bf48d
--- /dev/null
+++ b/test/diagnostics/cast-local-type.lua
@@ -0,0 +1,334 @@
+TEST [[
+local x = 0
+
+<!x!> = true
+]]
+
+TEST [[
+---@type integer
+local x
+
+<!x!> = true
+]]
+
+TEST [[
+---@type unknown
+local x
+
+x = nil
+]]
+
+TEST [[
+---@type unknown
+local x
+
+x = 1
+]]
+
+TEST [[
+---@type unknown|nil
+local x
+
+x = 1
+]]
+
+TEST [[
+local x = {}
+
+x = nil
+]]
+
+TEST [[
+---@type string
+local x
+
+<?x?> = nil
+]]
+
+TEST [[
+---@type string?
+local x
+
+x = nil
+]]
+
+TEST [[
+---@type table
+local x
+
+<!x!> = nil
+]]
+
+TEST [[
+local x
+
+x = nil
+]]
+
+TEST [[
+---@type integer
+local x
+
+---@type number
+<!x!> = f()
+]]
+
+TEST [[
+---@type number
+local x
+
+---@type integer
+x = f()
+]]
+
+TEST [[
+---@type number|boolean
+local x
+
+---@type string
+<!x!> = f()
+]]
+
+TEST [[
+---@type number|boolean
+local x
+
+---@type boolean
+x = f()
+]]
+
+TEST [[
+---@type number|boolean
+local x
+
+---@type boolean|string
+<!x!> = f()
+]]
+
+TEST [[
+---@type boolean
+local x
+
+if not x then
+ return
+end
+
+x = f()
+]]
+
+TEST [[
+---@type boolean
+local x
+
+---@type integer
+local y
+
+<!x!> = y
+]]
+
+TEST [[
+local y = true
+
+local x
+x = 1
+x = y
+]]
+
+TEST [[
+local t = {}
+
+local x = 0
+x = x + #t
+]]
+
+TEST [[
+local x = 0
+
+x = 1.0
+]]
+
+TEST [[
+---@class A
+
+local t = {}
+
+---@type A
+local a
+
+t = a
+]]
+
+TEST [[
+---@type integer
+local x
+
+x = 1.0
+]]
+
+TEST [[
+---@type integer
+local x
+
+<!x!> = 1.5
+]]
+
+TEST [[
+---@type integer
+local x
+
+x = 1 + G
+]]
+
+TEST [[
+---@type integer
+local x
+
+x = 1 + G
+]]
+
+TEST [[
+---@alias A integer
+
+---@type A
+local a
+
+---@type integer
+local b
+
+b = a
+]]
+
+TEST [[
+---@type string[]
+local t
+
+<!t!> = 'xxx'
+]]
+
+TEST [[
+---@type 1|2
+local x
+
+x = 1
+x = 2
+<!x!> = 3
+]]
+
+TEST [[
+---@type 'x'|'y'
+local x
+
+x = 'x'
+x = 'y'
+<!x!> = 'z'
+]]
+
+TEST [[
+local t = {
+ x = 1,
+}
+
+local x
+t[x] = true
+]]
+
+TEST [[
+---@type table<string, string>
+local x
+
+---@type table<number, string>
+local y
+
+<!x!> = y
+]]
+
+TEST [[
+---@type table<string, string>
+local x
+
+---@type table<string, number>
+local y
+
+<!x!> = y
+]]
+
+TEST [[
+---@type table<string, string>
+local x
+
+---@type table<string, string>
+local y
+
+x = y
+]]
+
+TEST [[
+---@type { x: number, y: number }
+local t1
+
+---@type { x: number }
+local t2
+
+<!t1!> = t2
+]]
+
+TEST [[
+---@type { x: number, [integer]: number }
+local t1
+
+---@type { x: number }
+local t2
+
+<!t1!> = t2
+]]
+
+TEST [[
+local x
+
+if X then
+ x = 'A'
+elseif X then
+ x = 'B'
+else
+ x = 'C'
+end
+
+local y = x
+
+<!y!> = nil
+]]
+(function (diags)
+ local diag = diags[1]
+ assert(diag.message == [[
+已显式定义变量的类型为 `string` ,不能再将其类型转换为 `nil`。
+- `nil` 无法匹配 `string`
+- 类型 `nil` 无法匹配 `string`]])
+end)
+
+TEST [[
+---@type 'A'|'B'|'C'|'D'|'E'|'F'|'G'|'H'|'I'|'J'|'K'|'L'|'M'|'N'|'O'|'P'|'Q'|'R'|'S'|'T'|'U'|'V'|'W'|'X'|'Y'|'Z'
+local x
+
+<!x!> = nil
+]]
+(function (diags)
+ local diag = diags[1]
+ assert(diag.message == [[
+已显式定义变量的类型为 `'A'|'B'|'C'|'D'|'E'...(+21)` ,不能再将其类型转换为 `nil`。
+- `nil` 无法匹配 `'A'|'B'|'C'|'D'|'E'...(+21)`
+- `nil` 无法匹配 `'A'|'B'|'C'|'D'|'E'...(+21)` 中的任何子类
+- 类型 `nil` 无法匹配 `'Z'`
+- 类型 `nil` 无法匹配 `'Y'`
+- 类型 `nil` 无法匹配 `'X'`
+- 类型 `nil` 无法匹配 `'W'`
+- 类型 `nil` 无法匹配 `'V'`
+- 类型 `nil` 无法匹配 `'U'`
+- 类型 `nil` 无法匹配 `'T'`
+- 类型 `nil` 无法匹配 `'S'`
+- 类型 `nil` 无法匹配 `'R'`
+- 类型 `nil` 无法匹配 `'Q'`
+...(+13)
+- 类型 `nil` 无法匹配 `'C'`
+- 类型 `nil` 无法匹配 `'B'`
+- 类型 `nil` 无法匹配 `'A'`]])
+end)
diff --git a/test/diagnostics/cast-type-mismatch.lua b/test/diagnostics/cast-type-mismatch.lua
new file mode 100644
index 00000000..9fba58f6
--- /dev/null
+++ b/test/diagnostics/cast-type-mismatch.lua
@@ -0,0 +1,13 @@
+TEST [[
+---@type string|boolean
+local t
+
+---@cast t string
+]]
+
+TEST [[
+---@type string|boolean
+local t
+
+---@cast t <!number!>
+]]
diff --git a/test/diagnostics/circle-doc-class.lua b/test/diagnostics/circle-doc-class.lua
new file mode 100644
index 00000000..089b0c9b
--- /dev/null
+++ b/test/diagnostics/circle-doc-class.lua
@@ -0,0 +1,14 @@
+TEST [[
+---@class <!A : B!>
+---@class <!B : C!>
+---@class <!C : D!>
+---@class <!D : A!>
+]]
+
+TEST [[
+---@class A : B
+---@class B : C
+---@class C : D
+---@class D
+]]
+
diff --git a/test/diagnostics/close-non-object.lua b/test/diagnostics/close-non-object.lua
new file mode 100644
index 00000000..11b882b7
--- /dev/null
+++ b/test/diagnostics/close-non-object.lua
@@ -0,0 +1,18 @@
+TEST [[
+local _ <close> = <!1!>
+]]
+
+TEST [[
+local _ <close> = <!''!>
+]]
+
+TEST [[
+local c <close> = <!(function () return 1 end)()!>
+]]
+
+TEST [[
+---@type unknown
+local t
+
+local _ <close> = t
+]]
diff --git a/test/diagnostics/code-after-break.lua b/test/diagnostics/code-after-break.lua
new file mode 100644
index 00000000..a150948b
--- /dev/null
+++ b/test/diagnostics/code-after-break.lua
@@ -0,0 +1,7 @@
+TEST [[
+while true do
+ break
+ <!print()
+ print()!>
+end
+]]
diff --git a/test/diagnostics/common.lua b/test/diagnostics/common.lua
deleted file mode 100644
index a1dbe819..00000000
--- a/test/diagnostics/common.lua
+++ /dev/null
@@ -1,2270 +0,0 @@
-local config = require 'config'
-local util = require 'utility'
-
-local disables = config.get(nil, 'Lua.diagnostics.disable')
-
-TEST [[
-local <!x!>
-]]
-
-TEST [[
-local y
-local x <close> = y
-]]
-
-TEST [[
-local function x()
-end
-x()
-]]
-
-TEST [[
-return function (x)
- x.a = 1
-end
-]]
-
-TEST [[
-local <!t!> = {}
-<!t!>.a = 1
-]]
-
-TEST [[
-local <!function <!x!>()
-end!>
-]]
-
-
-TEST [[
-local <!x!> = <!function () end!>
-]]
-
-TEST [[
-local <!x!>
-<!x!> = <!function () end!>
-]]
-
-TEST [[
-local <!function x()
-end!>
-local <!function <!y!>()
- x()
-end!>
-]]
-
-TEST [[
-local print, _G
-print(<!x!>)
-print(<!log!>)
-print(<!X!>)
-print(<!Log!>)
-print(<!y!>)
-print(Z)
-print(_G)
-Z = 1
-]]
-
-TEST [[
-::<!LABEL!>::
-]]
-
-TEST [[
-<! !>
-]]
-
-TEST [[
-
-<! !>
-]]
-
-TEST [[
-X = 1<! !>
-]]
-
-TEST [[
-X = [=[
- ]=]
-]]
-
-TEST [[
--- xxxx
-]]
-
-TEST [[
--- [=[
- ]=]
-]]
-
-TEST [[
-local x
-print(x)
-local <!x!>
-print(x)
-]]
-
-TEST [[
-local x
-print(x)
-local <!x!>
-print(x)
-local <!x!>
-print(x)
-]]
-
-TEST [[
-local _
-print(_)
-local _
-print(_)
-local _ENV
-<!print!>(_ENV) -- 由于重定义了_ENV,因此print变为了未定义全局变量
-]]
-
-TEST [[
-local x
-return x, function (<!x!>)
- return x
-end
-]]
-
-TEST [[
-print(1)
-_ENV = nil
-]]
-
-TEST [[
----@diagnostic disable: undefined-global
-_ENV = nil
-<!print!>(<!A!>) -- `print` and `A` should warning
-]]
-
-TEST [[
----@diagnostic disable: undefined-global
-local _ENV = nil
-<!print!>(<!A!>) -- `print` and `A` should warning
-]]
-
-TEST [[
-_ENV = {}
-print(A) -- no warning
-]]
-
-TEST [[
-local _ENV = {}
-print(A) -- no warning
-]]
-
-TEST [[
----@type iolib
-_ENV = {}
-<!print!>(stderr) -- `print` is warning but `stderr` is not
-]]
-
-TEST [[
----@type iolib
-local _ENV = {}
-<!print!>(stderr) -- `print` is warning but `stderr` is not
-]]
-
-TEST [[
-local _ENV = { print = print }
-print(1)
-]]
-
-util.arrayInsert(disables, 'undefined-env-child')
-TEST [[
-_ENV = nil
-<!GLOBAL!> = 1 --> _ENV.GLOBAL = 1
-]]
-
-TEST [[
-_ENV = nil
-local _ = <!print!> --> local _ = _ENV.print
-]]
-
-TEST [[
-_ENV = {}
-GLOBAL = 1 --> _ENV.GLOBAL = 1
-]]
-
-TEST [[
-_ENV = {}
-local _ = print --> local _ = _ENV.print
-]]
-
-TEST [[
-GLOBAL = 1
-_ENV = nil
-]]
-
-util.arrayRemove(disables, 'undefined-env-child')
-TEST [[
-<!print()
-('string')!>:sub(1, 1)
-]]
-
-TEST [[
-print()
-('string')
-]]
-
-TEST [[
-print
-{}
-{}
-]]
-
-TEST [[
-local x
-return x
- : f(1)
- : f(1)
-]]
-
-TEST [[
-return {
- <!print
- 'string'!>
-}
-]]
-
-TEST [[
-return {
- <!print
- {
- x = 1,
- }!>
-}
-]]
-
-TEST [[
-print()
-'string'
-]]
-
-TEST [[
-print
-{
- x = 1,
-}
-]]
-
-TEST [[
-local function x(a, b)
- return a, b
-end
-x(1, 2, <!3!>)
-]]
-
-TEST [[
-local function x(a, b, ...)
- return a, b, ...
-end
-x(1, 2, 3, 4, 5)
-]]
-
-TEST [[
----@type fun(a, b, ...)
-local x
-x(1, 2, 3, 4, 5)
-]]
-
-TEST [[
-local m = {}
-function m:x(a, b)
- return a, b
-end
-m:x(1, 2, <!3!>)
-]]
-
-TEST [[
-local m = {}
-function m:x(a, b)
- return a, b
-end
-m.x(m, 2, 3, <!4!>)
-]]
-
-TEST [[
-local m = {}
-function m.x(a, b)
- return a, b
-end
-m:x(1, <!2!>, <!3!>, <!4!>)
-]]
-
-TEST [[
-local function x(a, b)
- return a, b
-end
-x(1)
-]]
-
-TEST [[
----@param a integer
----@param b integer
-local function x(a, b)
- return a, b
-end
-<!x(1)!>
-]]
-
-TEST [[
----@param a integer
----@param b integer
-local function x(a, b)
- return a, b
-end
-<!x()!>
-]]
-
-TEST [[
----@param a integer
----@param b integer
----@param ... integer
-local function x(a, b, ...)
- return a, b, ...
-end
-x(1, 2)
-]]
-
-TEST [[
----@diagnostic disable: unused-local
-
----@param a integer
----@param b integer
-local function f(a, b)
-end
-
-f(...)
-]]
-
-TEST [[
----@diagnostic disable: unused-local
-
----@param a integer
----@param b integer
-local function f(a, b)
-end
-
-local function return2Numbers()
- return 1, 2
-end
-
-f(return2Numbers())
-]]
-
-TEST [[
----@param a integer
----@param b? integer
-local function x(a, b)
- return a, b
-end
-x(1)
-]]
-
-TEST [[
----@param b integer?
-local function x(a, b)
- return a, b
-end
-x(1)
-]]
-
-TEST [[
----@param b integer|nil
-local function x(a, b)
- return a, b
-end
-x(1)
-]]
-
-TEST [[
-local m = {}
-function m.x()
-end
-m:x()
-]]
-
-TEST [[
-InstanceName = 1
-Instance = _G[InstanceName]
-]]
-
-TEST [[
-local _ = (''):sub(1, 2)
-]]
-
-TEST [=[
-return [[
-
-]]
-]=]
-
-util.arrayInsert(disables, 'close-non-object')
-TEST [[
-local _ <close> = function () end
-]]
-util.arrayRemove(disables, 'close-non-object')
-
-TEST [[
-local _ <close> = <!1!>
-]]
-
-TEST [[
-local _ <close> = <!''!>
-]]
-
-TEST [[
-local c <close> = <!(function () return 1 end)()!>
-]]
-
-util.arrayInsert(disables, 'unused-local')
-TEST [[
-local f = <!function () end!>
-]]
-
-TEST [[
-local f;f = <!function () end!>
-]]
-
-TEST [[
-local <!function f() end!>
-]]
-
-TEST [[
-local <!function f()
- f()
-end!>
-]]
-
-
-TEST [[
-local <!function test()
-end!>
-
-local <!function foo ()
-end!>
-]]
-
-util.arrayRemove(disables, 'unused-local')
-TEST [[
-local mt, x
-function mt:m()
- function x:m()
- end
-end
-return mt, x
-]]
-
-TEST [[
-local mt = {}
-function mt:f()
-end
-return mt
-]]
-
-TEST [[
-local <!mt!> = {}
-function <!mt!>:f()
-end
-]]
-
-TEST [[
-local <!x!> = {}
-<!x!>.a = 1
-]]
-
-TEST [[
-local <!x!> = {}
-<!x!>['a'] = 1
-]]
-
-TEST [[
-local function f(<!self!>)
- return 'something'
-end
-f()
-]]
-
-TEST [[
-local function f(<!...!>)
- return 'something'
-end
-f()
-]]
-
-TEST [[
-local function f(var)
- print(var)
-end
-local var
-f(var)
-]]
-
-TEST [[
-local function f(a, b)
- return a, b
-end
-f(1, 2, <!3!>, <!4!>)
-]]
-
-TEST [[
-local mt = {}
-function mt:f(a, b)
- return a, b
-end
-mt.f(mt, 2, 3, <!4!>)
-]]
-
-
-TEST [[
-local mt = {}
-function mt.f(a, b)
- return a, b
-end
-mt:f(1, <!2!>, <!3!>, <!4!>)
-]]
-
-TEST [[
-local mt = {}
-function mt:f(a, b)
- return a, b
-end
-mt:f(1, 2, <!3!>, <!4!>)
-]]
-
-TEST [[
-local function f(a, b, ...)
- return a, b, ...
-end
-f(1, 2, 3, 4)
-]]
-
-TEST [[
-local _ = next({}, 1, <!2!>)
-print(1, 2, 3, 4, 5)
-]]
-
-TEST [[
-local function f(callback)
- callback(1, 2, 3)
-end
-f(function () end)
-]]
-
---TEST [[
---local realTostring = tostring
---tostring = function () end
---tostring(<!1!>)
---tostring = realTostring
---tostring(1)
---]]
-
-TEST [[
-<!aa!> = 1
-tostring = 1
-ROOT = 1
-_G.bb = 1
-]]
-
-TEST [[
-local f = load('')
-if f then
- f(1, 2, 3)
-end
-]]
-
-TEST [[
-local _ = <!unpack!>
-]]
-
-TEST [[
-X = table[<!x!>]
-]]
-
-TEST [[
-return {
- <!x!> = 1,
- y = 2,
- <!x!> = 3,
-}
-]]
-
-TEST [[
-return {
- x = 1,
- y = 2,
-}, {
- x = 1,
- y = 2,
-}
-]]
-
-TEST [[
-local m = {}
-function m.open()
-end
-
-m:open()
-]]
-
-TEST [[
-local m = {}
-function m:open()
-end
-
-m.open(m)
-]]
-
-TEST [[
-<!if true then
-end!>
-]]
-
-TEST [[
-<!if true then
-else
-end!>
-]]
-
-TEST [[
-if true then
-else
- return
-end
-]]
-
-TEST [[
-while true do
-end
-]]
-
-TEST [[
-<!for _ = 1, 10 do
-end!>
-]]
-
-TEST [[
-<!for _ in pairs({}) do
-end!>
-]]
-
-TEST [[
-local _ = 1, <!2!>
-]]
-
-TEST [[
-_ = 1, <!2!>
-]]
-
-TEST [[
-function X()
- do
- local k
- print(k)
- end
- local k = 1
- print(k)
-end
-]]
-
-TEST [[
-function X()
- local loc
- print(loc)
-end
-]]
-
-TEST [[
-local <!t!> = {}
-<!t!>[1] = 1
-]]
-
-TEST [[
-T1 = 1
-_ENV.T2 = 1
-_G.T3 = 1
-_ENV._G.T4 = 1
-_G._G._G.T5 = 1
-rawset(_G, 'T6', 1)
-rawset(_ENV, 'T7', 1)
-print(T1)
-print(T2)
-print(T3)
-print(T4)
-print(T5)
-print(T6)
-print(T7)
-]]
-
-TEST [[
-local x
-x = <!x or 0 + 1!>
-]]
-
-TEST [[
-local x, y
-x = <!x + y or 0!>
-]]
-
-TEST [[
-local x, y, z
-x = x and y or '' .. z
-]]
-
-TEST [[
-local x
-x = x or -1
-]]
-
-TEST [[
-local x
-x = x or (0 + 1)
-]]
-
-TEST [[
-local x, y
-x = (x + y) or 0
-]]
-
-TEST [[
-local t = {}
-t.a = 1
-t.a = 2
-return t
-]]
-
-TEST [[
-table.insert({}, 1, 2, <!3!>)
-]]
-
-TEST [[
-while true do
- break
- <!print()
- print()!>
-end
-]]
-
-TEST [[
-local x, <!y!>, <!z!> = 1
-print(x, y, z)
-]]
-
-TEST [[
-local x, y, <!z!> = 1, 2
-print(x, y, z)
-]]
-
-TEST [[
-local x, y, z = print()
-print(x, y, z)
-]]
-
-TEST [[
-local x, y, z
-print(x, y, z)
-]]
-
-TEST [[
-local x, y, z
-x, <!y!>, <!z!> = 1
-print(x, y, z)
-]]
-
-TEST [[
-X, <!Y!>, <!Z!> = 1
-]]
-
-TEST [[
-T = {}
-T.x, <!T.y!>, <!T.z!> = 1
-]]
-
-TEST [[
-T = {}
-T['x'], <!T['y']!>, <!T['z']!> = 1
-]]
-
---TEST [[
------@class <!Class!>
------@class <!Class!>
---]]
-
-TEST [[
----@alias <!A!> integer
----@alias <!A!> integer
-]]
-
-TEST [[
----@class A : <!B!>
-]]
-
-TEST [[
----@class <!A : B!>
----@class <!B : C!>
----@class <!C : D!>
----@class <!D : A!>
-]]
-
-TEST [[
----@class A : B
----@class B : C
----@class C : D
----@class D
-]]
-
-TEST [[
----@type <!A!>
-]]
-
-TEST [[
----@class A
----@type A|<!B!>|<!C!>
-]]
-
-TEST [[
----@class AAA
----@alias B AAA
-
----@type B
-]]
-
-TEST [[
----@alias B <!AAA!>
-]]
-
-TEST [[
----@class A
----@class B
----@alias <!A!> B
-]]
-
-TEST [[
----@param <!x!> <!Class!>
-]]
-
-TEST [[
----@class Class
----@param <!y!> Class
-local function f(x)
- return x
-end
-f()
-]]
-
-TEST [[
----@class Class
----@param <!y!> Class
-function F(x)
- return x
-end
-F()
-]]
-
-TEST [[
----@class Class
----@param <!x!> Class
----@param y Class
----@param <!x!> Class
-local function f(x, y)
- return x, y
-end
-
-local _
-f(_, _)
-]]
-
-TEST [[
----@field <!x Class!>
----@class Class
-]]
-
-TEST [[
----@class Class
-
----@field <!x Class!>
-]]
-
-TEST [[
----@class Class
----
----@field x Class
-]]
-
-TEST [[
----@class Class
----@field <!x!> Class
----@field <!x!> Class
-]]
-
-TEST [[
----@class Class : any
-]]
-
-TEST [[
----@type fun(a: integer)
-local f
-f(1)
-]]
-
-TEST [[
----@class c
-c = {}
-]]
-
-TEST [[
----@generic T: any
----@param v T
----@param message any
----@return T
----@return any message
-function assert(v, message)
- return v, message
-end
-]]
-
-TEST [[
----@type string
----|
-]]
-
-TEST [[
----@type
----| 'xx'
-]]
-
-TEST [[
----@class class
-local t
-]]
----[==[
--- checkUndefinedField 通用
-TEST [[
----@class Foo
----@field field1 integer
-local mt = {}
-function mt:Constructor()
- self.field2 = 1
-end
-function mt:method1() return 1 end
-function mt.method2() return 2 end
-
----@class Bar: Foo
----@field field4 integer
-local mt2 = {}
-
----@type Foo
-local v
-print(v.field1 + 1)
-print(v.field2 + 1)
-print(v.<!field3!> + 1)
-print(v:method1())
-print(v.method2())
-print(v:<!method3!>())
-
----@type Bar
-local v2
-print(v2.field1 + 1)
-print(v2.field2 + 1)
-print(v2.<!field3!> + 1)
-print(v2.field4 + 1)
-print(v2:method1())
-print(v2.method2())
-print(v2:<!method3!>())
-
-local v3 = {}
-print(v3.abc)
-
----@class Bar2
-local mt3
-function mt3:method() return 1 end
-print(mt3:method())
-]]
-
--- checkUndefinedField 通过type找到class
-TEST [[
----@class Foo
-local Foo
-function Foo:method1() end
-
----@type Foo
-local v
-v:method1()
-v:<!method2!>() -- doc.class.name
-]]
-
--- checkUndefinedField 通过type找到class,涉及到 class 继承版
-TEST [[
----@class Foo
-local Foo
-function Foo:method1() end
----@class Bar: Foo
-local Bar
-function Bar:method3() end
-
----@type Bar
-local v
-v:method1()
-v:<!method2!>() -- doc.class.name
-v:method3()
-]]
-
--- checkUndefinedField 类名和类变量同名,类变量被直接使用
-TEST [[
----@class Foo
-local Foo
-function Foo:method1() end
-Foo:<!method2!>() -- doc.class
-Foo:<!method2!>() -- doc.class
-]]
-
--- checkUndefinedField 没有@class的不检测
-TEST [[
-local Foo
-function Foo:method1()
- return Foo:method2() -- table
-end
-]]
-
--- checkUndefinedField 类名和类变量不同名,类变量被直接使用、使用self
-TEST [[
----@class Foo
-local mt
-function mt:method1()
- mt.<!method2!>() -- doc.class
- self:method1()
- return self.<!method2!>() -- doc.class.name
-end
-]]
-
--- checkUndefinedField 当会推导成多个class类型时
-TEST [[
----@class Foo
-local mt
-function mt:method1() end
-
----@class Bar
-local mt2
-function mt2:method2() end
-
----@type Foo
-local v
----@type Bar
-local v2
-<!v2!> = v
-v2:method1()
-v2:<!method2!>()
-]]
-
-TEST [[
----@type table
-T1 = {}
-print(T1.f1)
----@type tablelib
-T2 = {}
-print(T2.<!f2!>)
-]]
---]==]
-TEST [[
----@overload fun(...)
-local function f() end
-
-f(1)
-]]
-
-TEST [[
-for i = <!10, 1!> do
- print(i)
-end
-]]
-
-TEST [[
-for i = <!10, 1, 5!> do
- print(i)
-end
-]]
-
-TEST [[
-for i = <!100, 10, 1!> do
- print(i)
-end
-]]
-
-TEST [[
-for i = <!1, -10!> do
- print(i)
-end
-]]
-
-TEST [[
-for i = 1, 1 do
- print(i)
-end
-]]
-
-TEST [[
-local m = {}
-
-function <!m:fff!>()
-end
-
-function <!m:fff!>()
-end
-
-return m
-]]
-
-TEST [[
-local m = {}
-
-function <!m:fff!>()
-end
-
-do
- function <!m:fff!>()
- end
-end
-
-return m
-]]
-
-TEST [[
-local m = {}
-
-m.x = true
-m.x = false
-
-return m
-]]
-
-TEST [[
-local m = {}
-
-m.x = io.open('')
-m.x = nil
-
-return m
-]]
-
-TEST [[
----@meta
-
----@class A
----@field a boolean
-
----@return A
-local function f() end
-
-local r = f()
-r.x = 1
-
-return r.x
-]]
-
-TEST [[
----@diagnostic disable-next-line
-x = 1
-]]
-
-TEST [[
----@diagnostic disable-next-line: lowercase-global
-x = 1
-]]
-
-TEST [[
----@diagnostic disable-next-line: unused-local
-<!x!> = 1
-]]
-
-TEST [[
----@diagnostic disable
-x = 1
-]]
-
-TEST [[
----@diagnostic disable
----@diagnostic enable
-<!x!> = 1
-]]
-
-TEST [[
----@diagnostic disable
----@diagnostic disable
----@diagnostic enable
-x = 1
-]]
-
-TEST [[
----@diagnostic disable-next-line: <!xxx!>
-]]
-
-TEST [[
-local mt = {}
-
-function mt:a(x)
- return self, x
-end
-
-function mt:b(y)
- self:a(1):b(2)
- return y
-end
-
-return mt
-]]
-
-TEST [[
-local function each()
- return function ()
- end
-end
-
-for x in each() do
- print(x)
-end
-]]
-
-TEST [[
----@type string
-local s
-
-print(s:upper())
-]]
-
-TEST [[
-local t = ().
-return t
-]]
-
-TEST [[
-return {
- [1] = 1,
- ['1'] = 1,
-}
-]]
-
-TEST [[
-return {
- [print()] = 1,
- [print()] = 1,
-}
-]]
-
-TEST [[
----@type { x: number, y: number}
----| "'resume'"
-]]
-
-TEST [[
-return {
- 1, <!2!>, 3,
- [<!2!>] = 4,
-}
-]]
-
-TEST [[
---- @class Emit
---- @field on fun(eventName: string, cb: function)
---- @field on fun(eventName: '"died"', cb: fun(i: integer))
---- @field on fun(eventName: '"won"', cb: fun(s: string))
-local emit = {}
-]]
-
-TEST [[
---- @class Emit
---- @field on fun(eventName: string, cb: function)
---- @field <!on!> fun(eventName: '"died"', cb: fun(i: integer))
---- @field on fun(eventName: '"won"', cb: fun(s: string))
---- @field <!on!> fun(eventName: '"died"', cb: fun(i: integer))
-local emit = {}
-]]
-
--- redundant-return
-TEST [[
-local function f()
- <!return!>
-end
-f()
-]]
-
-TEST [[
-local function f()
- return nil
-end
-f()
-]]
-
-TEST [[
-local function f()
- local function x()
- <!return!>
- end
- x()
- return true
-end
-f()
-]]
-
-TEST [[
-local function f()
- local function x()
- return true
- end
- return x()
-end
-f()
-]]
-
-TEST [[
----@type file*
-local f
-local _ = f:read '*a'
-local _ = f:read('*a')
-]]
-
-TEST [[
-function F()
- <!coroutine.yield!>()
-end
-]]
-
-TEST [[
----@async
-function F()
- coroutine.yield()
-end
-]]
-
-TEST [[
----@type async fun()
-local f
-
-function F()
- <!f!>()
-end
-]]
-
-TEST [[
----@type async fun()
-local f
-
----@async
-function F()
- f()
-end
-]]
-
-TEST [[
-local function f(cb)
- cb()
-end
-
-return function()
- <!f>(function () ---@async
- return nil
- end)
-end
-]]
-
-TEST [[
-local function f(cb)
- pcall(cb)
-end
-
-return function()
- <!f!>(function () ---@async
- return nil
- end)
-end
-]]
-
-TEST [[
----@param c any
-local function f(c)
- return c
-end
-
-return function ()
- f(function () ---@async
- return nil
- end)
-end
-]]
-
-TEST [[
----@param ... any
-local function f(...)
- return ...
-end
-
-return function ()
- f(function () ---@async
- return nil
- end)
-end
-]]
-
-TEST [[
----@vararg any
-local function f(...)
- return ...
-end
-
-return function ()
- f(function () ---@async
- return nil
- end)
-end
-]]
-
-TEST [[
-local function f(...)
- return ...
-end
-
-return function ()
- f(function () ---@async
- return nil
- end)
-end
-]]
-
-TEST [[
-local function f(...)
- return ...
-end
-
-return function ()
- f(function () ---@async
- return nil
- end)
-end
-]]
-
-TEST [[
----@nodiscard
-local function f()
- return 1
-end
-
-<!f()!>
-]]
-
-TEST [[
----@nodiscard
-local function f()
- return 1
-end
-
-X = f()
-]]
-
-config.get(nil, 'Lua.diagnostics.neededFileStatus')['not-yieldable'] = 'Any'
-TEST [[
----@param cb fun()
-local function f(cb)
- return cb
-end
-
----@async
-local function af()
- return nil
-end
-
-f(<!af!>)
-]]
-
-TEST [[
----@param cb async fun()
-local function f(cb)
- return cb
-end
-
----@async
-local function af()
- return nil
-end
-
-f(af)
-]]
-
-TEST [[
-local function f(cb)
- cb()
-end
-
-local function af()
- <!f!>(function () ---@async
- return nil
- end)
-end
-
-return af
-]]
-
-TEST [[
-local function f(cb)
- cb()
-end
-
----@async
-local function af()
- f(function () ---@async
- return nil
- end)
-end
-
-return af
-]]
-
-TEST [[
-local _ = type(function () ---@async
- return nil
-end)
-]]
-
-TEST [[
----@param ... number
-local function f(...)
- return ...
-end
-
-return f
-]]
-
-TEST [[
----@type fun(...: string)
-]]
-
-TEST [[
----@type fun(xxx, yyy, ...): boolean
-]]
-
-TEST [[
-local <!x!>
-
-return {
- x = 1,
-}
-]]
-
-TEST [[
----@class A #1
-]]
-
-TEST [[
----@class A 1
-]]
-
-TEST [[
-return ('1'):upper()
-]]
-
-TEST [[
-local value
-value = '1'
-value = value:upper()
-]]
-
-TEST [[
-T = {}
----@deprecated # comment
-T.x = 1
-
-print(<!T.x!>)
-]]
-
-TEST [[
-T = {}
-
----@deprecated
-function T:ff()
-end
-
-<!T:ff!>()
-]]
-
-TEST [[
----@type string?
-local x
-
-S = <!x!>:upper()
-]]
-
-TEST [[
----@type string?
-local x
-
-if x then
- S = x:upper()
-end
-]]
-
-TEST [[
----@type string?
-local x
-
-if not x then
- x = ''
-end
-
-S = x:upper()
-]]
-
-TEST [[
----@type fun()?
-local x
-
-S = <!x!>()
-]]
-
-TEST [[
----@type integer?
-local x
-
-T = {}
-T[<!x!>] = 1
-]]
-
-TEST [[
-local x, y
-local z = x and y
-
-print(z.y)
-]]
-
-TEST [[
-local x, y
-function x()
- y()
-end
-
-function y()
- x()
-end
-
-x()
-]]
-
-TEST [[
----@meta
-
----@param x fun()
-local function f1(x)
-end
-
----@return fun()
-local function f2()
-end
-
-f1(f2())
-]]
-
-TEST [[
----@meta
-
----@type fun():integer
-local f
-
----@param x integer
-local function foo(x) end
-
-foo(f())
-]]
-
-TEST [[
----@type string|table
-local n
-
-print(n.x)
-]]
-
-TEST [[
----@diagnostic disable: unused-local, unused-function, undefined-global
-
-function F() end
-
----@param x boolean
-function F(x) end
-
-F(k())
-]]
-
-TEST [[
-local function f()
- return 1, 2, 3
-end
-
-local function k()
-end
-
-k(<!f()!>)
-]]
-
-TEST [[
----@diagnostic disable: unused-local
-local function f()
- return 1, 2, 3
-end
-
----@param x integer
-local function k(x)
-end
-
-k(f())
-]]
-
-TEST [[
----@cast <!x!> integer
-]]
-
-TEST [[
----@diagnostic disable: unused-local
-local x, y
----@cast y number
-]]
-
-TEST [[
----@class A
-
----@class B
----@field [integer] A
----@field [A] true
-]]
-
-TEST [[
----@class A
-
----@class B
----@field [<!A!>] A
----@field [<!A!>] true
-]]
-
-TEST [[
----@diagnostic disable: unused-local
-
----@type 'x'
-local t
-
-local n = t:upper()
-]]
-
-TEST [[
----@diagnostic disable: unused-local
-
----@alias A 'x'
-
----@type A
-local t
-
-local n = t:upper()
-]]
-
-TEST [[
-local t = {}
-
-function t:init() end
-
-<!t.init()!>
-]]
-
-TEST [[
----@meta
-
-return function f(x, y, z) end
-]]
-
-util.arrayInsert(disables, 'redundant-return')
-TEST [[
----@return number
-function F()
- <!return!>
-end
-]]
-
-TEST [[
----@return number, number
-function F()
- <!return!> 1
-end
-]]
-
-TEST [[
----@return number, number?
-function F()
- return 1
-end
-]]
-
-TEST [[
----@return ...
-function F()
- return
-end
-]]
-
-TEST [[
----@return number, number?
-function F()
- return 1, 1, <!1!>
-end
-]]
-
-TEST [[
----@return number, number?
-function F()
- return 1, 1, <!1!>, <!2!>, <!3!>
-end
-]]
-
-TEST [[
----@meta
-
----@return number, number
-local function r2() end
-
----@return number, number?
-function F()
- return 1, <!r2()!>
-end
-]]
-
-TEST [[
----@return number
-function F()
- X = 1<!!>
-end
-]]
-
-TEST [[
-local A
----@return number
-function F()
- if A then
- return 1
- end<!!>
-end
-]]
-
-TEST [[
-local A, B
----@return number
-function F()
- if A then
- return 1
- elseif B then
- return 2
- end<!!>
-end
-]]
-
-TEST [[
-local A, B
----@return number
-function F()
- if A then
- return 1
- elseif B then
- return 2
- else
- return 3
- end
-end
-]]
-
-TEST [[
-local A, B
----@return number
-function F()
- if A then
- elseif B then
- return 2
- else
- return 3
- end<!!>
-end
-]]
-
-TEST [[
----@return any
-function F()
- X = 1
-end
-]]
-
-TEST [[
----@return any, number
-function F()
- X = 1<!!>
-end
-]]
-
-TEST [[
----@return number, any
-function F()
- X = 1<!!>
-end
-]]
-
-TEST [[
----@return any, any
-function F()
- X = 1
-end
-]]
-
-TEST [[
-local A
----@return number
-function F()
- for _ = 1, 10 do
- if A then
- return 1
- end
- end
- error('should not be here')
-end
-]]
-
-TEST [[
-local A
----@return number
-function F()
- while true do
- if A then
- return 1
- end
- end
-end
-]]
-
-TEST [[
-local A
----@return number
-function F()
- while A do
- if A then
- return 1
- end
- end<!!>
-end
-]]
-
-TEST [[
-local A
----@return number
-function F()
- while A do
- if A then
- return 1
- else
- return 2
- end
- end
-end
-]]
-
-TEST [[
----@return number?
-function F()
-
-end
-]]
-
-util.arrayRemove(disables, 'redundant-return')
-
-TEST [[
----@class A
----@operator <!xxx!>: A
-]]
-
-config.add(nil, 'Lua.diagnostics.unusedLocalExclude', 'll_*')
-
-TEST [[
-local <!xx!>
-local ll_1
-local ll_2
-local <!ll!>
-]]
-
-config.remove(nil, 'Lua.diagnostics.unusedLocalExclude', 'll_*')
-
-TEST [[
----@diagnostic disable: undefined-global
-
-if X then
- return false
-elseif X then
- return false
-else
- return false
-end
-<!return true!>
-]]
-
-TEST [[
----@diagnostic disable: undefined-global
-
-function X()
- if X then
- return false
- elseif X then
- return false
- else
- return false
- end
- <!return true!>
-end
-]]
-
-TEST [[
-while true do
-end
-
-<!print(1)!>
-]]
-
-TEST [[
-while true do
-end
-
-<!print(1)!>
-]]
-
-TEST [[
-while X do
- X = 1
-end
-
-print(1)
-]]
-
-TEST [[
----@diagnostic disable: undefined-global
-
-while true do
- if not X then
- break
- end
-end
-
-print(1)
-
-do return end
-]]
-
-TEST [[
----@type unknown
-local t
-
-local _ <close> = t
-]]
-
-TEST [[
----@meta
----@diagnostic disable: duplicate-set-field
----@class A
-local m = {}
-
-function m.ff() end
-
-function m.ff(x) end
-
-m.ff(1)
-]]
-
-TEST [[
-local done = false
-
-local function set_done()
- done = true
-end
-
-while not done do
- set_done()
-end
-
-print(1)
-]]
-
-TEST [[
----@class A
----@field private x number
----@field protected y number
----@field public z number
-local t
-print(t.x)
-]]
-
-TEST [[
----@class A
----@field private x number
----@field protected y number
----@field public z number
-
----@type A
-local t
-
-print(t.<!x!>)
-]]
-
-TEST [[
----@class A
----@field private x number
----@field protected y number
----@field public z number
-
----@class B: A
-local t
-
-print(t.y)
-]]
-
-TEST [[
----@class A
----@field private x number
----@field protected y number
----@field public z number
-
----@class B: A
-
----@type B
-local t
-
-print(t.<!y!>)
-]]
-
-TEST [[
----@class A
----@field private x number
----@field protected y number
----@field public z number
-
----@class B: A
-
----@type B
-local t
-
-print(t.z)
-]]
-
-TEST [[
----@class A
----@field _id number
-
----@type A
-local t
-
-print(t._id)
-]]
-
-config.set(nil, 'Lua.doc.privateName', { '_*' })
-TEST [[
----@class A
----@field _id number
-
----@type A
-local t
-
-print(t.<!_id!>)
-
----@class B: A
-local t2
-
-print(t2.<!_id!>)
-]]
-config.set(nil, 'Lua.doc.privateName', nil)
-
-config.set(nil, 'Lua.doc.protectedName', { '_*' })
-TEST [[
----@class A
----@field _id number
-
----@type A
-local t
-
-print(t.<!_id!>)
-
----@class B: A
-local t2
-
-print(t2._id)
-]]
-config.set(nil, 'Lua.doc.protectedName', nil)
-
-TEST [[
----@class A
----@field private x number
-local mt = {}
-
-function mt:init()
- print(self.x)
-end
-]]
-
-TEST [[
----@diagnostic disable: unused-local
----@class A
----@field private x number
-local mt = {}
-
-function mt:init()
- ---@type A
- local obj = {}
-
- obj.x = 1
-end
-]]
-
-TEST [[
----@diagnostic disable: unused-local
----@class A
----@field private x number
-local mt = {}
-
-mt.init = function ()
- ---@type A
- local obj = {}
-
- obj.x = 1
-end
-]]
-
-TEST [[
----@class A
-X = {}
-
-function <!X.f!>() end
-
-function <!X.f!>() end
-]]
-
-TEST [[
----@meta
-
----@class A
-X = {}
-
-function X.f() end
-
-function X.f() end
-]]
-
-TEST [[
----@class A
-X = {}
-
-if true then
- function X.f() end
-else
- function X.f() end
-end
-]]
-
-TESTWITH 'global-in-nil-env' [[
-local function foo(_ENV)
- Joe = "human"
-end
-]]
diff --git a/test/diagnostics/count-down-loop.lua b/test/diagnostics/count-down-loop.lua
new file mode 100644
index 00000000..f4e385f5
--- /dev/null
+++ b/test/diagnostics/count-down-loop.lua
@@ -0,0 +1,29 @@
+TEST [[
+for i = <!10, 1!> do
+ print(i)
+end
+]]
+
+TEST [[
+for i = <!10, 1, 5!> do
+ print(i)
+end
+]]
+
+TEST [[
+for i = <!100, 10, 1!> do
+ print(i)
+end
+]]
+
+TEST [[
+for i = <!1, -10!> do
+ print(i)
+end
+]]
+
+TEST [[
+for i = 1, 1 do
+ print(i)
+end
+]]
diff --git a/test/diagnostics/deprecated.lua b/test/diagnostics/deprecated.lua
new file mode 100644
index 00000000..c5486752
--- /dev/null
+++ b/test/diagnostics/deprecated.lua
@@ -0,0 +1,21 @@
+TEST [[
+local _ = <!unpack!>
+]]
+
+TEST [[
+T = {}
+---@deprecated # comment
+T.x = 1
+
+print(<!T.x!>)
+]]
+
+TEST [[
+T = {}
+
+---@deprecated
+function T:ff()
+end
+
+<!T:ff!>()
+]]
diff --git a/test/diagnostics/discard-returns.lua b/test/diagnostics/discard-returns.lua
new file mode 100644
index 00000000..2e348390
--- /dev/null
+++ b/test/diagnostics/discard-returns.lua
@@ -0,0 +1,17 @@
+TEST [[
+---@nodiscard
+local function f()
+ return 1
+end
+
+<!f()!>
+]]
+
+TEST [[
+---@nodiscard
+local function f()
+ return 1
+end
+
+X = f()
+]]
diff --git a/test/diagnostics/doc-field-no-class.lua b/test/diagnostics/doc-field-no-class.lua
new file mode 100644
index 00000000..87db518c
--- /dev/null
+++ b/test/diagnostics/doc-field-no-class.lua
@@ -0,0 +1,16 @@
+TEST [[
+---@field <!x Class!>
+---@class Class
+]]
+
+TEST [[
+---@class Class
+
+---@field <!x Class!>
+]]
+
+TEST [[
+---@class Class
+---
+---@field x Class
+]]
diff --git a/test/diagnostics/duplicate-doc-alias.lua b/test/diagnostics/duplicate-doc-alias.lua
new file mode 100644
index 00000000..0373fee9
--- /dev/null
+++ b/test/diagnostics/duplicate-doc-alias.lua
@@ -0,0 +1,10 @@
+TEST [[
+---@alias <!A!> integer
+---@alias <!A!> integer
+]]
+
+TEST [[
+---@class A
+---@class B
+---@alias <!A!> B
+]]
diff --git a/test/diagnostics/duplicate-doc-field.lua b/test/diagnostics/duplicate-doc-field.lua
new file mode 100644
index 00000000..8f385335
--- /dev/null
+++ b/test/diagnostics/duplicate-doc-field.lua
@@ -0,0 +1,38 @@
+TEST [[
+---@class Class
+---@field <!x!> Class
+---@field <!x!> Class
+]]
+
+TEST [[
+--- @class Emit
+--- @field on fun(eventName: string, cb: function)
+--- @field on fun(eventName: '"died"', cb: fun(i: integer))
+--- @field on fun(eventName: '"won"', cb: fun(s: string))
+local emit = {}
+]]
+
+TEST [[
+--- @class Emit
+--- @field on fun(eventName: string, cb: function)
+--- @field <!on!> fun(eventName: '"died"', cb: fun(i: integer))
+--- @field on fun(eventName: '"won"', cb: fun(s: string))
+--- @field <!on!> fun(eventName: '"died"', cb: fun(i: integer))
+local emit = {}
+]]
+
+TEST [[
+---@class A
+
+---@class B
+---@field [integer] A
+---@field [A] true
+]]
+
+TEST [[
+---@class A
+
+---@class B
+---@field [<!A!>] A
+---@field [<!A!>] true
+]]
diff --git a/test/diagnostics/duplicate-doc-param.lua b/test/diagnostics/duplicate-doc-param.lua
new file mode 100644
index 00000000..42eb73d3
--- /dev/null
+++ b/test/diagnostics/duplicate-doc-param.lua
@@ -0,0 +1,12 @@
+TEST [[
+---@class Class
+---@param <!x!> Class
+---@param y Class
+---@param <!x!> Class
+local function f(x, y)
+ return x, y
+end
+
+local _
+f(_, _)
+]]
diff --git a/test/diagnostics/duplicate-index.lua b/test/diagnostics/duplicate-index.lua
new file mode 100644
index 00000000..3289c736
--- /dev/null
+++ b/test/diagnostics/duplicate-index.lua
@@ -0,0 +1,24 @@
+TEST [[
+return {
+ <!x!> = 1,
+ y = 2,
+ <!x!> = 3,
+}
+]]
+
+TEST [[
+return {
+ x = 1,
+ y = 2,
+}, {
+ x = 1,
+ y = 2,
+}
+]]
+
+TEST [[
+return {
+ 1, <!2!>, 3,
+ [<!2!>] = 4,
+}
+]]
diff --git a/test/diagnostics/duplicate-set-field.lua b/test/diagnostics/duplicate-set-field.lua
new file mode 100644
index 00000000..469bc3ea
--- /dev/null
+++ b/test/diagnostics/duplicate-set-field.lua
@@ -0,0 +1,74 @@
+TEST [[
+local m = {}
+
+function <!m:fff!>()
+end
+
+function <!m:fff!>()
+end
+
+return m
+]]
+
+TEST [[
+local m = {}
+
+function <!m:fff!>()
+end
+
+do
+ function <!m:fff!>()
+ end
+end
+
+return m
+]]
+
+TEST [[
+local m = {}
+
+m.x = true
+m.x = false
+
+return m
+]]
+
+TEST [[
+local m = {}
+
+m.x = io.open('')
+m.x = nil
+
+return m
+]]
+
+TEST [[
+---@class A
+X = {}
+
+function <!X.f!>() end
+
+function <!X.f!>() end
+]]
+
+TEST [[
+---@meta
+
+---@class A
+X = {}
+
+function X.f() end
+
+function X.f() end
+]]
+
+TEST [[
+---@class A
+X = {}
+
+if true then
+ function X.f() end
+else
+ function X.f() end
+end
+]]
diff --git a/test/diagnostics/empty-block.lua b/test/diagnostics/empty-block.lua
new file mode 100644
index 00000000..750397a4
--- /dev/null
+++ b/test/diagnostics/empty-block.lua
@@ -0,0 +1,32 @@
+TEST [[
+<!if true then
+end!>
+]]
+
+TEST [[
+<!if true then
+else
+end!>
+]]
+
+TEST [[
+if true then
+else
+ return
+end
+]]
+
+TEST [[
+while true do
+end
+]]
+
+TEST [[
+<!for _ = 1, 10 do
+end!>
+]]
+
+TEST [[
+<!for _ in pairs({}) do
+end!>
+]]
diff --git a/test/diagnostics/global-element.lua b/test/diagnostics/global-element.lua
index 0e4cdd61..0c31bade 100644
--- a/test/diagnostics/global-element.lua
+++ b/test/diagnostics/global-element.lua
@@ -1,31 +1,5 @@
local config = require 'config'
-local util = require 'utility'
--- disable all default groups to make isolated tests
-config.set(nil, 'Lua.diagnostics.groupFileStatus',
-{
- ['ambiguity'] = 'None',
- ['await'] = 'None',
- ['codestyle'] = 'None',
- ['conventions'] = 'None',
- ['duplicate'] = 'None',
- ['global'] = 'None',
- ['luadoc'] = 'None',
- ['redefined'] = 'None',
- ['strict'] = 'None',
- ['strong'] = 'None',
- ['type-check'] = 'None',
- ['unbalanced'] = 'None',
- ['unused'] = 'None'
-})
-
--- enable single diagnostic that is to be tested
-config.set(nil, 'Lua.diagnostics.neededFileStatus',
-{
- ['global-element'] = 'Any!' -- override groupFileStatus
-})
-
--- check that local elements are not warned about
TEST [[
local x = 123
x = 321
@@ -87,11 +61,3 @@ function GLOBAL_CLOSURE()
<!elem2!> = 2
end
]]
-
--- reset configurations
-config.set(nil, 'Lua.diagnostics.groupFileStatus',
-{})
-config.set(nil, 'Lua.diagnostics.neededFileStatus',
-{})
-config.set(nil, 'Lua.diagnostics.globals',
-{})
diff --git a/test/diagnostics/global-in-nil-env.lua b/test/diagnostics/global-in-nil-env.lua
new file mode 100644
index 00000000..a0b8cd3e
--- /dev/null
+++ b/test/diagnostics/global-in-nil-env.lua
@@ -0,0 +1,44 @@
+TEST [[
+local _
+print(_)
+local _
+print(_)
+local _ENV
+<!print!>(_ENV) -- 由于重定义了_ENV,因此print变为了未定义全局变量
+]]
+
+TEST [[
+_ENV = nil
+<!print!>(<!A!>) -- `print` and `A` should warning
+]]
+
+TEST [[
+local _ENV = nil
+<!print!>(<!A!>) -- `print` and `A` should warning
+]]
+
+TEST [[
+_ENV = {}
+print(A) -- no warning
+]]
+
+TEST [[
+local _ENV = {}
+print(A) -- no warning
+]]
+
+TEST [[
+_ENV = nil
+<!GLOBAL!> = 1 --> _ENV.GLOBAL = 1
+]]
+
+TEST [[
+_ENV = nil
+local _ = <!print!> --> local _ = _ENV.print
+]]
+
+TEST [[
+local function foo(_ENV)
+ Joe = "human"
+end
+]]
diff --git a/test/diagnostics/incomplete-signature-doc.lua b/test/diagnostics/incomplete-signature-doc.lua
index c3099cd2..7cd144c0 100644
--- a/test/diagnostics/incomplete-signature-doc.lua
+++ b/test/diagnostics/incomplete-signature-doc.lua
@@ -1,31 +1,21 @@
-local config = require 'config'
-local util = require 'utility'
-
--- disable all default groups to make isolated tests
-config.set(nil, 'Lua.diagnostics.groupFileStatus',
-{
- ['ambiguity'] = 'None',
- ['await'] = 'None',
- ['codestyle'] = 'None',
- ['conventions'] = 'None',
- ['duplicate'] = 'None',
- ['global'] = 'None',
- ['luadoc'] = 'None',
- ['redefined'] = 'None',
- ['strict'] = 'None',
- ['strong'] = 'None',
- ['type-check'] = 'None',
- ['unbalanced'] = 'None',
- ['unused'] = 'None'
-})
-
--- enable single diagnostic that is to be tested
-config.set(nil, 'Lua.diagnostics.neededFileStatus',
-{
- ['incomplete-signature-doc'] = 'Any!' -- override groupFileStatus
-})
-
--- check global functions
+-- -------------------------------------
+-- about the structure of these test cases
+--
+-- the following test cases are grouped by the number of parameters and return values of the functions
+-- so first global functions with:
+-- no parameter and return value (FG), one parameter (FGP), two parameters (FGPP),
+-- one return value (FGR), two return values (FGRR) and parameter and return value (FGPR)
+-- after that, these groups are also done for local functions (FL, FLP, ...)
+--
+-- in these groups, different versions of documentation are tested:
+-- no comment, simple comment, @async annotation (which is no signature doc),
+-- incomplete signature doc (only part of the necessary @param or @return annotations, if possible) - the only cases that should generating warnings
+-- and complete signature docs (all necessary @param and @return annotations)
+-- -------------------------------------
+
+-- global functions no parameter, no return value
+-- no incomplete signature docs possible
+
TEST [[
function FG0()
end
@@ -33,15 +23,26 @@ end
---comment
function FG1()
end
+
+---@async
+function FG1_()
+end
]]
+-- global functions with single parameter, no return value
+-- no incomplete signature docs possible
TEST [[
function FGP0(p)
print(p)
end
---comment
-function FGP1(<!p!>)
+function FGP1(p)
+ print(p)
+end
+
+---@async
+function FGP1_(p)
print(p)
end
@@ -52,13 +53,20 @@ function FGP2(p)
end
]]
+-- global functions with two parameters, no return value
+-- incomplete signature docs when exactly one of the parameters is documented
TEST [[
function FGPP0(p0, p1)
print(p0, p1)
end
---comment
-function FGPP1(<!p0!>, <!p1!>)
+function FGPP1(p0, p1)
+ print(p0, p1)
+end
+
+---@async
+function FGPP1_(p0, p1)
print(p0, p1)
end
@@ -69,6 +77,12 @@ function FGPP2(p0, <!p1!>)
end
---comment
+---@param p1 any
+function FGPP2_(<!p0!>, p1)
+ print(p0, p1)
+end
+
+---comment
---@param p0 any
---@param p1 any
function FGPP3(p0, p1)
@@ -76,6 +90,8 @@ function FGPP3(p0, p1)
end
]]
+-- global functions with no parameter, single return value
+-- no incomplete signature docs possible
TEST [[
function FGR0()
return 0
@@ -83,7 +99,12 @@ end
---comment
function FGR1()
- return <!0!>
+ return 0
+end
+
+---@async
+function FGR1_()
+ return 0
end
---comment
@@ -93,6 +114,8 @@ function FGR2()
end
]]
+-- global functions with no parameter, two return values
+-- incomplete signature docs when exactly one of the return values is documented
TEST [[
function FGRR0()
return 0, 1
@@ -100,7 +123,12 @@ end
---comment
function FGRR1()
- return <!0!>, <!1!>
+ return 0, 1
+end
+
+---@async
+function FGRR1_()
+ return 0, 1
end
---comment
@@ -117,6 +145,8 @@ function FGRR3()
end
]]
+-- global functions with one parameter, one return value
+-- incomplete signature docs when exactly one of parameter or return value is documented
TEST [[
function FGPR0(p)
print(p)
@@ -124,9 +154,15 @@ function FGPR0(p)
end
---comment
-function FGPR1(<!p!>)
+function FGPR1(p)
print(p)
- return <!0!>
+ return 0
+end
+
+---@async
+function FGPR1_(p)
+ print(p)
+ return 0
end
---comment
@@ -152,8 +188,8 @@ function FGPR4(p)
end
]]
--- check local functions
-
+-- local functions with no parameter, no return value
+-- no incomplete signature docs possible
TEST [[
local function FL0()
end
@@ -165,8 +201,13 @@ local function FL1()
end
FL1()
+
+---@async
+local function FL1_()
]]
+-- local functions with single parameter, no return value
+-- no incomplete signature docs possible
TEST [[
local function FLP0(p)
print(p)
@@ -175,12 +216,17 @@ end
FLP0(0)
---comment
-local function FLP1(<!p!>)
+local function FLP1(p)
print(p)
end
FLP1(0)
+---@async
+local function FLP1_(p)
+ print(p)
+end
+
---comment
---@param p any
local function FLP2(p)
@@ -190,6 +236,8 @@ end
FLP2(0)
]]
+-- local functions with two parameters, no return value
+-- incomplete signature docs when exactly one of the parameters is documented
TEST [[
local function FLPP0(p0, p1)
print(p0, p1)
@@ -198,12 +246,17 @@ end
FLPP0(0, 1)
---comment
-local function FLPP1(<!p0!>, <!p1!>)
+local function FLPP1(p0, p1)
print(p0, p1)
end
FLPP1(0, 1)
+---@async
+local function FLPP1_(p0, p1)
+ print(p0, p1)
+end
+
---comment
---@param p0 any
local function FLPP2(p0, <!p1!>)
@@ -222,6 +275,8 @@ end
FLPP3(0, 1)
]]
+-- local functions with no parameter, single return value
+-- no incomplete signature docs possible
TEST [[
local function FLR0()
return 0
@@ -231,7 +286,12 @@ local vr0 = FLR0()
---comment
local function FLR1()
- return <!0!>
+ return 0
+end
+
+---@async
+local function FLR1_()
+ return 0
end
local vr1 = FLR1()
@@ -245,6 +305,8 @@ end
local vr2 = FLR2()
]]
+-- local functions with no parameter, two return values
+-- incomplete signature docs when exactly one of the return values is documented
TEST [[
local function FLRR0()
return 0, 1
@@ -254,11 +316,16 @@ local vrr0, _ = FLRR0()
---comment
local function FLRR1()
- return <!0!>, <!1!>
+ return 0, 1
end
local vrr1, _ = FLRR1()
+---@async
+local function FLRR1_()
+ return 0, 1
+end
+
---comment
---@return integer
local function FLRR2()
@@ -277,6 +344,8 @@ end
local vrr3, _ = FLRR3()
]]
+-- local functions with one parameter, one return value
+-- incomplete signature docs when exactly one of parameter or return value is documented
TEST [[
local function FLPR0(p)
print(p)
@@ -286,9 +355,15 @@ end
local vpr0 = FLPR0(0)
---comment
-local function FLPR1(<!p!>)
+local function FLPR1(p)
print(p)
- return <!0!>
+ return 0
+end
+
+---@async
+local function FLPR1_(p)
+ print(p)
+ return 0
end
local vpr1 = FLPR1(0)
@@ -321,11 +396,3 @@ end
local vpr4 = FLPR4(0)
]]
-
--- reset configurations
-config.set(nil, 'Lua.diagnostics.groupFileStatus',
-{})
-config.set(nil, 'Lua.diagnostics.neededFileStatus',
-{})
-config.set(nil, 'Lua.diagnostics.globals',
-{}) \ No newline at end of file
diff --git a/test/diagnostics/init.lua b/test/diagnostics/init.lua
index fbea6680..99a5dc24 100644
--- a/test/diagnostics/init.lua
+++ b/test/diagnostics/init.lua
@@ -4,9 +4,15 @@ local config = require 'config'
local util = require 'utility'
local catch = require 'catch'
-config.get(nil, 'Lua.diagnostics.neededFileStatus')['deprecated'] = 'Any'
-config.get(nil, 'Lua.diagnostics.neededFileStatus')['type-check'] = 'Any'
-config.get(nil, 'Lua.diagnostics.neededFileStatus')['await-in-sync'] = 'Any'
+local status = config.get(nil, 'Lua.diagnostics.neededFileStatus')
+
+for key in pairs(status) do
+ status[key] = 'Any!'
+end
+
+config.set('nil', 'Lua.type.castNumberToInteger', false)
+config.set('nil', 'Lua.type.weakUnionCheck', false)
+config.set('nil', 'Lua.type.weakNilCheck', false)
rawset(_G, 'TEST', true)
@@ -32,9 +38,14 @@ function TEST(script)
files.setText(TESTURI, newScript)
files.open(TESTURI)
local origins = {}
+ local filteds = {}
local results = {}
core(TESTURI, false, function (result)
- results[#results+1] = { result.start, result.finish }
+ if DIAG_CARE == result.code
+ or DIAG_CARE == '*' then
+ results[#results+1] = { result.start, result.finish }
+ filteds[#filteds+1] = result
+ end
origins[#origins+1] = result
end)
@@ -49,43 +60,69 @@ function TEST(script)
files.remove(TESTURI)
return function (callback)
- callback(origins)
+ callback(filteds)
end
end
-function TESTWITH(code)
- return function (script)
- local newScript, catched = catch(script, '!')
- files.setText(TESTURI, newScript)
- files.open(TESTURI)
- local origins = {}
- local results = {}
- core(TESTURI, false, function (result)
- if code ~= result.code then
- return
- end
- results[#results+1] = { result.start, result.finish }
- origins[#origins+1] = result
- end)
-
- if results[1] then
- if not founded(catched['!'] or {}, results) then
- error(('%s\n%s'):format(util.dump(catched['!']), util.dump(results)))
- end
- else
- assert(#catched['!'] == 0)
- end
-
- files.remove(TESTURI)
-
- return function (callback)
- callback(origins)
- end
- end
+local function check(name)
+ DIAG_CARE = name
+ require('diagnostics.' .. name)
end
-require 'diagnostics.common'
-require 'diagnostics.type-check'
-require 'diagnostics.incomplete-signature-doc'
-require 'diagnostics.missing-global-doc'
-require 'diagnostics.global-element'
+check 'ambiguity-1'
+check 'assign-type-mismatch'
+check 'await-in-sync'
+check 'cast-local-type'
+check 'cast-type-mismatch'
+check 'circle-doc-class'
+check 'close-non-object'
+check 'code-after-break'
+check 'count-down-loop'
+check 'deprecated'
+check 'discard-returns'
+check 'doc-field-no-class'
+check 'duplicate-doc-alias'
+check 'duplicate-doc-field'
+check 'duplicate-doc-param'
+check 'duplicate-index'
+check 'duplicate-set-field'
+check 'empty-block'
+check 'global-element'
+check 'global-in-nil-env'
+check 'incomplete-signature-doc'
+check 'inject-field'
+check 'invisible'
+check 'lowercase-global'
+check 'missing-fields'
+check 'missing-global-doc'
+check 'missing-local-export-doc'
+check 'missing-parameter'
+check 'missing-return-value'
+check 'missing-return'
+check 'need-check-nil'
+check 'newfield-call'
+check 'newline-call'
+check 'not-yieldable'
+check 'param-type-mismatch'
+check 'redefined-local'
+check 'redundant-parameter'
+check 'redundant-return-value'
+check 'redundant-return'
+check 'redundant-value'
+check 'return-type-mismatch'
+check 'trailing-space'
+check 'unbalanced-assignments'
+check 'undefined-doc-class'
+check 'undefined-doc-name'
+check 'undefined-doc-param'
+check 'undefined-env-child'
+check 'undefined-field'
+check 'undefined-global'
+check 'unknown-cast-variable'
+check 'unknown-diag-code'
+check 'unknown-operator'
+check 'unreachable-code'
+check 'unused-function'
+check 'unused-label'
+check 'unused-local'
+check 'unused-vararg'
diff --git a/test/diagnostics/inject-field.lua b/test/diagnostics/inject-field.lua
new file mode 100644
index 00000000..9bb0f8fc
--- /dev/null
+++ b/test/diagnostics/inject-field.lua
@@ -0,0 +1,84 @@
+TEST [[
+---@class Class
+local m = {}
+
+m.xx = 1 -- OK
+
+---@type Class
+local m
+
+m.xx = 1 -- OK
+m.<!yy!> = 1 -- Warning
+]]
+
+TEST [[
+---@class Class
+local m = {}
+
+m.xx = 1 -- OK
+
+---@class Class
+local m
+
+m.xx = 1 -- OK
+m.yy = 1 -- OK
+]]
+
+TEST [[
+---@type { xx: number }
+local m
+
+m.xx = 1 -- OK
+m.<!yy!> = 1 -- Warning
+]]
+
+TEST [[
+---@type { xx: number, [any]: any }
+local m
+
+m.xx = 1 -- OK
+m.yy = 1 -- OK
+]]
+
+TEST [[
+---@class Class
+---@field x number
+
+---@type Class
+local t
+
+t.x = 1 -- OK
+t.<!y!> = 2 -- Warning
+]]
+
+TEST [[
+---@class Class
+---@field x number
+---@field [any] any
+
+---@type Class
+local t
+
+t.x = 1 -- OK
+t.y = 2 -- OK
+]]
+
+
+TEST [[
+---@class (exact) Class
+---@field x number
+local m = {
+ x = 1, -- OK
+ <!y!> = 2, -- Warning
+}
+
+m.x = 1 -- OK
+m.<!y!> = 2 -- Warning
+
+function m:init() -- OK
+ self.x = 1 -- OK
+ self.<!y!> = 2 -- Warning
+ function self:<!xx!>() -- Warning
+ end
+end
+]]
diff --git a/test/diagnostics/invisible.lua b/test/diagnostics/invisible.lua
new file mode 100644
index 00000000..2fc6791e
--- /dev/null
+++ b/test/diagnostics/invisible.lua
@@ -0,0 +1,145 @@
+local config = require 'config'
+
+TEST [[
+---@class A
+---@field private x number
+---@field protected y number
+---@field public z number
+local t
+print(t.x)
+]]
+
+TEST [[
+---@class A
+---@field private x number
+---@field protected y number
+---@field public z number
+
+---@type A
+local t
+
+print(t.<!x!>)
+]]
+
+TEST [[
+---@class A
+---@field private x number
+---@field protected y number
+---@field public z number
+
+---@class B: A
+local t
+
+print(t.y)
+]]
+
+TEST [[
+---@class A
+---@field private x number
+---@field protected y number
+---@field public z number
+
+---@class B: A
+
+---@type B
+local t
+
+print(t.<!y!>)
+]]
+
+TEST [[
+---@class A
+---@field private x number
+---@field protected y number
+---@field public z number
+
+---@class B: A
+
+---@type B
+local t
+
+print(t.z)
+]]
+TEST [[
+---@class A
+---@field _id number
+
+---@type A
+local t
+
+print(t._id)
+]]
+
+config.set(nil, 'Lua.doc.privateName', { '_*' })
+TEST [[
+---@class A
+---@field _id number
+
+---@type A
+local t
+
+print(t.<!_id!>)
+
+---@class B: A
+local t2
+
+print(t2.<!_id!>)
+]]
+config.set(nil, 'Lua.doc.privateName', nil)
+
+config.set(nil, 'Lua.doc.protectedName', { '_*' })
+TEST [[
+---@class A
+---@field _id number
+
+---@type A
+local t
+
+print(t.<!_id!>)
+
+---@class B: A
+local t2
+
+print(t2._id)
+]]
+config.set(nil, 'Lua.doc.protectedName', nil)
+
+TEST [[
+---@class A
+---@field private x number
+local mt = {}
+
+function mt:init()
+ print(self.x)
+end
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+---@diagnostic disable: missing-fields
+---@class A
+---@field private x number
+local mt = {}
+
+function mt:init()
+ ---@type A
+ local obj = {}
+
+ obj.x = 1
+end
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+---@diagnostic disable: missing-fields
+---@class A
+---@field private x number
+local mt = {}
+
+mt.init = function ()
+ ---@type A
+ local obj = {}
+
+ obj.x = 1
+end
+]]
diff --git a/test/diagnostics/lowercase-global.lua b/test/diagnostics/lowercase-global.lua
new file mode 100644
index 00000000..bf73b8c8
--- /dev/null
+++ b/test/diagnostics/lowercase-global.lua
@@ -0,0 +1,39 @@
+TEST [[
+<!aa!> = 1
+tostring = 1
+ROOT = 1
+_G.bb = 1
+]]
+
+TEST [[
+---@diagnostic disable-next-line
+x = 1
+]]
+
+TEST [[
+---@diagnostic disable-next-line: lowercase-global
+x = 1
+]]
+
+TEST [[
+---@diagnostic disable-next-line: unused-local
+<!x!> = 1
+]]
+
+TEST [[
+---@diagnostic disable
+x = 1
+]]
+
+TEST [[
+---@diagnostic disable
+---@diagnostic enable
+<!x!> = 1
+]]
+
+TEST [[
+---@diagnostic disable
+---@diagnostic disable
+---@diagnostic enable
+x = 1
+]]
diff --git a/test/diagnostics/missing-fields.lua b/test/diagnostics/missing-fields.lua
new file mode 100644
index 00000000..ab87f81d
--- /dev/null
+++ b/test/diagnostics/missing-fields.lua
@@ -0,0 +1,233 @@
+TEST [[
+---@diagnostic disable: unused-local
+---@class A
+---@field x number
+---@field y? number
+---@field z number
+
+---@type A
+local t = <!{}!>
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+---@class A
+---@field x number
+---@field y? number
+---@field z number
+
+---@type A
+local t = <!{
+ x = 1,
+}!>
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+---@class A
+---@field x number
+---@field y? number
+---@field z number
+
+---@type A
+local t = <!{
+ x = 1,
+ y = 2,
+}!>
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+---@class A
+---@field x number
+---@field y? number
+---@field z number
+
+---@type A
+local t = {
+ x = 1,
+ y = 2,
+ z = 3,
+}
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+---@class A
+---@field x number
+---@field y? number
+---@field z number
+
+---@type A
+local t = {
+ x = 1,
+ z = 3,
+}
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+---@class A
+---@field x number
+---@field y? number
+---@field z number
+
+---@param a A
+local function f(a) end
+
+f <!{}!>
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+---@class A
+---@field x number
+---@field y? number
+---@field z number
+
+---@param a A
+local function f(a) end
+
+f <!{
+ x = 1,
+}!>
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+---@class A
+---@field x number
+---@field y? number
+---@field z number
+
+---@param a A
+local function f(a) end
+
+f <!{
+ x = 1,
+ y = 2,
+}!>
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+---@class A
+---@field x number
+---@field y? number
+---@field z number
+
+---@param a A
+local function f(a) end
+
+f {
+ x = 1,
+ y = 2,
+ z = 3,
+}
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+---@class A
+---@field x number
+---@field y? number
+---@field z number
+
+---@param a A
+local function f(a) end
+
+f {
+ x = 1,
+ z = 3,
+}
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+---@class A
+---@field x number
+local t = {}
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+
+---@class A
+---@field x number
+
+---@class A
+local t = {}
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+
+---@class Foo
+---@field a number
+---@field b number
+---@field c number
+
+---@type Foo|Foo[]
+local a = {
+ {
+ a = 1,
+ b = 2,
+ c = 3,
+ }
+}
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+
+---@class Foo
+---@field a number
+---@field b number
+---@field c number
+
+---@class Bar
+---@field ba number
+---@field bb number
+---@field bc number
+
+---@type Foo|Bar
+local b = {
+ a = 1,
+ b = 2,
+ c = 3,
+}
+]]
+
+TEST [[
+---@class A
+---@field x integer
+
+---@type A
+return <!{}!>
+]]
+
+TEST [[
+---@class A
+---@field x number
+
+---@class B
+---@field y number
+
+---@type A|B
+local t = <!{
+ z = 1,
+}!>
+]]
+
+TEST [[
+---@class A
+---@field x number
+
+---@class B
+---@field y number
+
+---@type A|B
+local t = {
+ y = 1,
+}
+]]
diff --git a/test/diagnostics/missing-global-doc.lua b/test/diagnostics/missing-global-doc.lua
index 2ebc237a..de5250fd 100644
--- a/test/diagnostics/missing-global-doc.lua
+++ b/test/diagnostics/missing-global-doc.lua
@@ -1,31 +1,3 @@
-local config = require 'config'
-local util = require 'utility'
-
--- disable all default groups to make isolated tests
-config.set(nil, 'Lua.diagnostics.groupFileStatus',
-{
- ['ambiguity'] = 'None',
- ['await'] = 'None',
- ['codestyle'] = 'None',
- ['conventions'] = 'None',
- ['duplicate'] = 'None',
- ['global'] = 'None',
- ['luadoc'] = 'None',
- ['redefined'] = 'None',
- ['strict'] = 'None',
- ['strong'] = 'None',
- ['type-check'] = 'None',
- ['unbalanced'] = 'None',
- ['unused'] = 'None'
-})
-
--- enable single diagnostic that is to be tested
-config.set(nil, 'Lua.diagnostics.neededFileStatus',
-{
- ['missing-global-doc'] = 'Any!' -- override groupFileStatus
-})
-
-
-- check global functions
TEST [[
<!function FG0()
@@ -323,11 +295,3 @@ end
local vpr4 = FLPR4(0)
]]
-
--- reset configurations
-config.set(nil, 'Lua.diagnostics.groupFileStatus',
-{})
-config.set(nil, 'Lua.diagnostics.neededFileStatus',
-{})
-config.set(nil, 'Lua.diagnostics.globals',
-{}) \ No newline at end of file
diff --git a/test/diagnostics/missing-local-export-doc.lua b/test/diagnostics/missing-local-export-doc.lua
new file mode 100644
index 00000000..3d942216
--- /dev/null
+++ b/test/diagnostics/missing-local-export-doc.lua
@@ -0,0 +1,175 @@
+-- check global functions
+TEST [[
+local mod = {}
+
+local <!function fl0()
+end!>
+
+---comment
+local function fl1()
+end
+
+local function fl2()
+end
+
+function FG0()
+end
+
+mod.fl0 = fl0
+mod.fl1 = fl1
+return mod
+]]
+
+TEST [[
+local mod = {}
+
+local function flp0(<!p!>)
+ print(p)
+end
+
+---comment
+local function flp1(<!p!>)
+ print(p)
+end
+
+---comment
+---@param p any
+local function flp2(p)
+ print(p)
+end
+
+mod.flp0 = flp0
+mod.flp1 = flp1
+return mod
+]]
+
+TEST [[
+local mod = {}
+
+local function flpp0(<!p0!>, <!p1!>)
+ print(p0, p1)
+end
+
+---comment
+local function flpp1(<!p0!>, <!p1!>)
+ print(p0, p1)
+end
+
+---comment
+---@param p0 any
+local function flpp2(p0, <!p1!>)
+ print(p0, p1)
+end
+
+---comment
+---@param p0 any
+---@param p1 any
+local function flpp3(p0, p1)
+ print(p0, p1)
+end
+
+mod.flpp0 = flpp0
+mod.flpp1 = flpp1
+mod.flpp2 = flpp2
+mod.flpp3 = flpp3
+return mod
+]]
+
+TEST [[
+local mod = {}
+
+local function flr0()
+ return <!0!>
+end
+
+---comment
+local function flr1()
+ return <!0!>
+end
+
+---comment
+---@return integer
+local function flr2()
+ return 0
+end
+
+mod.flr0 = flr0
+mod.flr1 = flr1
+mod.flr2 = flr2
+return mod
+]]
+
+TEST [[
+local mod = {}
+
+local function flrr0()
+ return <!0!>, <!1!>
+end
+
+---comment
+local function flrr1()
+ return <!0!>, <!1!>
+end
+
+---comment
+---@return integer
+local function flrr2()
+ return 0, <!1!>
+end
+
+---comment
+---@return integer
+---@return integer
+local function flrr3()
+ return 0, 1
+end
+
+mod.flrr0 = flrr0
+mod.flrr1 = flrr1
+mod.flrr2 = flrr2
+return mod
+]]
+
+TEST [[
+local mod = {}
+
+local function flpr0(<!p!>)
+ print(p)
+ return <!0!>
+end
+
+---comment
+local function flpr1(<!p!>)
+ print(p)
+ return <!0!>
+end
+
+---comment
+---@param p any
+local function flpr2(p)
+ print(p)
+ return <!0!>
+end
+
+---comment
+---@return integer
+local function flpr3(<!p!>)
+ print(p)
+ return 0
+end
+
+---comment
+---@param p any
+---@return integer
+local function flpr4(p)
+ print(p)
+ return 0
+end
+
+mod.flpr0 = flpr0
+mod.flpr1 = flpr1
+mod.flpr2 = flpr2
+mod.flpr3 = flpr3
+mod.flpr4 = flpr4
+return mod
+]]
diff --git a/test/diagnostics/missing-parameter.lua b/test/diagnostics/missing-parameter.lua
new file mode 100644
index 00000000..154d630b
--- /dev/null
+++ b/test/diagnostics/missing-parameter.lua
@@ -0,0 +1,90 @@
+
+TEST [[
+local function x(a, b)
+ return a, b
+end
+x(1)
+]]
+
+TEST [[
+---@param a integer
+---@param b integer
+local function x(a, b)
+ return a, b
+end
+<!x(1)!>
+]]
+
+TEST [[
+---@param a integer
+---@param b integer
+local function x(a, b)
+ return a, b
+end
+<!x()!>
+]]
+
+TEST [[
+---@param a integer
+---@param b integer
+---@param ... integer
+local function x(a, b, ...)
+ return a, b, ...
+end
+x(1, 2)
+]]
+
+TEST [[
+---@param a integer
+---@param b integer
+local function f(a, b)
+end
+
+f(...)
+]]
+
+TEST [[
+---@param a integer
+---@param b integer
+local function f(a, b)
+end
+
+local function return2Numbers()
+ return 1, 2
+end
+
+f(return2Numbers())
+]]
+
+TEST [[
+---@param a integer
+---@param b? integer
+local function x(a, b)
+ return a, b
+end
+x(1)
+]]
+
+TEST [[
+---@param b integer?
+local function x(a, b)
+ return a, b
+end
+x(1)
+]]
+
+TEST [[
+---@param b integer|nil
+local function x(a, b)
+ return a, b
+end
+x(1)
+]]
+
+TEST [[
+local t = {}
+
+function t:init() end
+
+<!t.init()!>
+]]
diff --git a/test/diagnostics/missing-return-value.lua b/test/diagnostics/missing-return-value.lua
new file mode 100644
index 00000000..3bad7974
--- /dev/null
+++ b/test/diagnostics/missing-return-value.lua
@@ -0,0 +1,34 @@
+TEST [[
+---@type fun():number
+local function f()
+ <!return!>
+end
+]]
+
+TEST [[
+---@return number
+function F()
+ <!return!>
+end
+]]
+
+TEST [[
+---@return number, number
+function F()
+ <!return!> 1
+end
+]]
+
+TEST [[
+---@return number, number?
+function F()
+ return 1
+end
+]]
+
+TEST [[
+---@return ...
+function F()
+ return
+end
+]]
diff --git a/test/diagnostics/missing-return.lua b/test/diagnostics/missing-return.lua
new file mode 100644
index 00000000..b8c1e7d3
--- /dev/null
+++ b/test/diagnostics/missing-return.lua
@@ -0,0 +1,158 @@
+TEST [[
+---@type fun():number
+local function f()
+<!!>end
+]]
+
+TEST [[
+---@type fun():number?
+local function f()
+end
+]]
+
+TEST [[
+---@type fun():...
+local function f()
+end
+]]
+
+TEST [[
+---@return number
+function F()
+ X = 1<!!>
+end
+]]
+TEST [[
+local A
+---@return number
+function F()
+ if A then
+ return 1
+ end<!!>
+end
+]]
+
+TEST [[
+local A, B
+---@return number
+function F()
+ if A then
+ return 1
+ elseif B then
+ return 2
+ end<!!>
+end
+]]
+
+TEST [[
+local A, B
+---@return number
+function F()
+ if A then
+ return 1
+ elseif B then
+ return 2
+ else
+ return 3
+ end
+end
+]]
+
+TEST [[
+local A, B
+---@return number
+function F()
+ if A then
+ elseif B then
+ return 2
+ else
+ return 3
+ end<!!>
+end
+]]
+
+TEST [[
+---@return any
+function F()
+ X = 1
+end
+]]
+
+TEST [[
+---@return any, number
+function F()
+ X = 1<!!>
+end
+]]
+
+TEST [[
+---@return number, any
+function F()
+ X = 1<!!>
+end
+]]
+
+TEST [[
+---@return any, any
+function F()
+ X = 1
+end
+]]
+
+TEST [[
+local A
+---@return number
+function F()
+ for _ = 1, 10 do
+ if A then
+ return 1
+ end
+ end
+ error('should not be here')
+end
+]]
+
+TEST [[
+local A
+---@return number
+function F()
+ while true do
+ if A then
+ return 1
+ end
+ end
+end
+]]
+
+TEST [[
+local A
+---@return number
+function F()
+ while A do
+ if A then
+ return 1
+ end
+ end<!!>
+end
+]]
+
+TEST [[
+local A
+---@return number
+function F()
+ while A do
+ if A then
+ return 1
+ else
+ return 2
+ end
+ end
+end
+]]
+
+TEST [[
+---@return number?
+function F()
+
+end
+]]
diff --git a/test/diagnostics/need-check-nil.lua b/test/diagnostics/need-check-nil.lua
new file mode 100644
index 00000000..c4e3bba6
--- /dev/null
+++ b/test/diagnostics/need-check-nil.lua
@@ -0,0 +1,68 @@
+TEST [[
+---@type string?
+local x
+
+local s = <!x!>:upper()
+]]
+
+TEST [[
+---@type string?
+local x
+
+S = <!x!>:upper()
+]]
+
+TEST [[
+---@type string?
+local x
+
+if x then
+ S = x:upper()
+end
+]]
+
+TEST [[
+---@type string?
+local x
+
+if not x then
+ x = ''
+end
+
+S = x:upper()
+]]
+
+TEST [[
+---@type fun()?
+local x
+
+S = <!x!>()
+]]
+
+TEST [[
+---@type integer?
+local x
+
+T = {}
+T[<!x!>] = 1
+]]
+
+TEST [[
+local x, y
+local z = x and y
+
+print(z.y)
+]]
+
+TEST [[
+local x, y
+function x()
+ y()
+end
+
+function y()
+ x()
+end
+
+x()
+]]
diff --git a/test/diagnostics/newfield-call.lua b/test/diagnostics/newfield-call.lua
new file mode 100644
index 00000000..63de9db9
--- /dev/null
+++ b/test/diagnostics/newfield-call.lua
@@ -0,0 +1,15 @@
+TEST [[
+return {
+ <!print
+ 'string'!>
+}
+]]
+
+TEST [[
+return {
+ <!print
+ {
+ x = 1,
+ }!>
+}
+]]
diff --git a/test/diagnostics/newline-call.lua b/test/diagnostics/newline-call.lua
new file mode 100644
index 00000000..ca160aa3
--- /dev/null
+++ b/test/diagnostics/newline-call.lua
@@ -0,0 +1,34 @@
+TEST [[
+<!print()
+('string')!>:sub(1, 1)
+]]
+
+TEST [[
+print()
+('string')
+]]
+
+TEST [[
+print
+{}
+{}
+]]
+
+TEST [[
+local x
+return x
+ : f(1)
+ : f(1)
+]]
+
+TEST [[
+print()
+'string'
+]]
+
+TEST [[
+print
+{
+ x = 1,
+}
+]]
diff --git a/test/diagnostics/not-yieldable.lua b/test/diagnostics/not-yieldable.lua
new file mode 100644
index 00000000..81e972ee
--- /dev/null
+++ b/test/diagnostics/not-yieldable.lua
@@ -0,0 +1,48 @@
+TEST [[
+---@param cb fun()
+local function f(cb)
+ return cb
+end
+
+---@async
+local function af()
+ return nil
+end
+
+f(<!af!>)
+]]
+
+TEST [[
+---@param cb async fun()
+local function f(cb)
+ return cb
+end
+
+---@async
+local function af()
+ return nil
+end
+
+f(af)
+]]
+
+TEST [[
+local function f(cb)
+ cb()
+end
+
+---@async
+local function af()
+ f(function () ---@async
+ return nil
+ end)
+end
+
+return af
+]]
+
+TEST [[
+local _ = type(function () ---@async
+ return nil
+end)
+]]
diff --git a/test/diagnostics/param-type-mismatch.lua b/test/diagnostics/param-type-mismatch.lua
new file mode 100644
index 00000000..e31e9933
--- /dev/null
+++ b/test/diagnostics/param-type-mismatch.lua
@@ -0,0 +1,248 @@
+TEST [[
+---@param x number
+local function f(x) end
+
+f(<!true!>)
+]]
+
+TEST [[
+---@class A
+
+---@param n A
+local function f(n)
+end
+
+---@class B
+local a = {}
+
+---@type A?
+a.x = XX
+
+f(a.x)
+]]
+TEST [[
+---@alias A string|boolean
+
+---@param x string|boolean
+local function f(x) end
+
+---@type A
+local x
+
+f(x)
+]]
+
+TEST [[
+---@alias A string|boolean
+
+---@param x A
+local function f(x) end
+
+---@type string|boolean
+local x
+
+f(x)
+]]
+
+TEST [[
+---@param b boolean
+local function f(b)
+end
+
+---@type boolean
+local t
+
+if t then
+ f(t)
+end
+]]
+
+TEST [[
+---@enum A
+local t = {
+ x = 1,
+ y = 2,
+}
+
+---@param x A
+local function f(x)
+end
+
+f(<!t!>)
+f(t.x)
+f(1)
+f(<!3!>)
+]]
+
+TEST [[
+---@enum A
+local t = {
+ x = { h = 1 },
+ y = { h = 2 },
+}
+
+---@param x A
+local function f(x)
+end
+
+f(t.x)
+f(t.y)
+f(<!{ h = 1 }!>)
+]]
+
+TEST [[
+---@enum(key) A
+local t = {
+ x = 1,
+ ['y'] = 2,
+}
+
+---@param x A
+local function f(x)
+end
+
+f('x')
+f('y')
+f(<!'z'!>)
+]]
+
+TEST [[
+---@generic T: string | boolean | table
+---@param x T
+---@return T
+local function f(x)
+ return x
+end
+
+f(<!1!>)
+]]
+
+TEST [[
+---@param opts {a:number, b:number}
+local function foo(opts)
+
+end
+
+---@param opts {a:number, b:number}
+local function bar(opts)
+ foo(opts)
+end
+]]
+
+TEST [[
+---@param opts {a:number, b:number}
+local function foo(opts)
+
+end
+
+---@param opts {c:number, d:number}
+local function bar(opts)
+ foo(<!opts!>) -- this should raise linting error
+end
+]]
+
+TEST [[
+---@param opts {[number]: boolean}
+local function foo(opts)
+
+end
+
+---@param opts {[1]: boolean}
+local function bar(opts)
+ foo(opts)
+end
+]]
+
+TEST [[
+---@generic T
+---@param v1 T
+---@param v2 T|table
+local function func(v1, v2)
+end
+
+func('hello', 'world')
+]]
+
+TEST [[
+---@generic T1, T2, T3, T4, T5
+---@param f fun(): T1?, T2?, T3?, T4?, T5?
+---@return T1?, T2?, T3?, T4?, T5?
+local function foo(f)
+ return f()
+end
+
+local a, b = foo(function()
+ return 1
+end)
+]]
+
+TEST [[
+---@generic T1, T2, T3, T4, T5
+---@param f fun(): T1|nil, T2|nil, T3|nil, T4|nil, T5|nil
+---@return T1?, T2?, T3?, T4?, T5?
+local function foo(f)
+ return f()
+end
+
+local a, b = foo(function()
+ return 1
+end)
+]]
+
+TEST [[
+---@param v integer
+---@return boolean
+local function is_string(v)
+ return type(v) == 'string'
+end
+
+print(is_string(3))
+]]
+
+TEST [[
+---@param p integer|string
+local function get_val(p)
+ local is_number = type(p) == 'number'
+ return is_number and p or p
+end
+
+get_val('hi')
+]]
+
+TEST [[
+---@class Class
+local Class = {}
+
+---@param source string
+function Class.staticCreator(source)
+
+end
+
+Class.staticCreator(<!true!>)
+Class<!:!>staticCreator() -- Expecting a waring
+]]
+
+TEST [[
+---@class A
+
+---@class B : A
+
+---@class C : B
+
+---@class D : B
+
+---@param x A
+local function func(x) end
+
+---@type C|D
+local var
+func(var)
+]]
+
+TEST [[
+---@class MyClass
+---@overload fun(x : string) : MyClass
+local MyClass = {}
+
+local w = MyClass(<!1!>)
+]]
diff --git a/test/diagnostics/redefined-local.lua b/test/diagnostics/redefined-local.lua
new file mode 100644
index 00000000..c594ed2c
--- /dev/null
+++ b/test/diagnostics/redefined-local.lua
@@ -0,0 +1,22 @@
+TEST [[
+local x
+print(x)
+local <!x!>
+print(x)
+]]
+
+TEST [[
+local x
+print(x)
+local <!x!>
+print(x)
+local <!x!>
+print(x)
+]]
+
+TEST [[
+local x
+return x, function (<!x!>)
+ return x
+end
+]]
diff --git a/test/diagnostics/redundant-parameter.lua b/test/diagnostics/redundant-parameter.lua
new file mode 100644
index 00000000..fabe3340
--- /dev/null
+++ b/test/diagnostics/redundant-parameter.lua
@@ -0,0 +1,214 @@
+TEST [[
+local function x(a, b)
+ return a, b
+end
+x(1, 2, <!3!>)
+]]
+
+TEST [[
+local function x(a, b, ...)
+ return a, b, ...
+end
+x(1, 2, 3, 4, 5)
+]]
+
+TEST [[
+---@type fun(a, b, ...)
+local x
+x(1, 2, 3, 4, 5)
+]]
+
+TEST [[
+local m = {}
+function m:x(a, b)
+ return a, b
+end
+m:x(1, 2, <!3!>)
+]]
+
+TEST [[
+local m = {}
+function m:x(a, b)
+ return a, b
+end
+m.x(m, 2, 3, <!4!>)
+]]
+
+TEST [[
+local m = {}
+function m.x(a, b)
+ return a, b
+end
+m:x(1, <!2!>, <!3!>, <!4!>)
+]]
+
+TEST [[
+local m = {}
+function m.x()
+end
+m:x()
+]]
+
+TEST [[
+local function f(a, b)
+ return a, b
+end
+f(1, 2, <!3!>, <!4!>)
+]]
+
+TEST [[
+local mt = {}
+function mt:f(a, b)
+ return a, b
+end
+mt.f(mt, 2, 3, <!4!>)
+]]
+
+TEST [[
+local mt = {}
+function mt.f(a, b)
+ return a, b
+end
+mt:f(1, <!2!>, <!3!>, <!4!>)
+]]
+
+TEST [[
+local mt = {}
+function mt:f(a, b)
+ return a, b
+end
+mt:f(1, 2, <!3!>, <!4!>)
+]]
+
+TEST [[
+local function f(a, b, ...)
+ return a, b, ...
+end
+f(1, 2, 3, 4)
+]]
+
+TEST [[
+local _ = next({}, 1, <!2!>)
+print(1, 2, 3, 4, 5)
+]]
+
+TEST [[
+local function f(callback)
+ callback(1, 2, 3)
+end
+f(function () end)
+]]
+
+--TEST [[
+--local realTostring = tostring
+--tostring = function () end
+--tostring(<!1!>)
+--tostring = realTostring
+--tostring(1)
+--]]
+
+TEST [[
+local f = load('')
+if f then
+ f(1, 2, 3)
+end
+]]
+
+TEST [[
+local m = {}
+function m.open()
+end
+
+m:open()
+]]
+
+TEST [[
+local m = {}
+function m:open()
+end
+
+m.open(m)
+]]
+
+TEST [[
+table.insert({}, 1, 2, <!3!>)
+]]
+
+TEST [[
+---@overload fun(...)
+local function f() end
+
+f(1)
+]]
+
+TEST [[
+function F() end
+
+---@param x boolean
+function F(x) end
+
+F(k())
+]]
+
+TEST [[
+local function f()
+ return 1, 2, 3
+end
+
+local function k()
+end
+
+k(<!f()!>)
+]]
+
+TEST [[
+---@diagnostic disable: unused-local
+local function f()
+ return 1, 2, 3
+end
+
+---@param x integer
+local function k(x)
+end
+
+k(f())
+]]
+
+TEST [[
+---@meta
+
+---@param x fun()
+local function f1(x)
+end
+
+---@return fun()
+local function f2()
+end
+
+f1(f2())
+]]
+
+TEST [[
+---@meta
+
+---@type fun():integer
+local f
+
+---@param x integer
+local function foo(x) end
+
+foo(f())
+]]
+
+TEST [[
+---@meta
+---@diagnostic disable: duplicate-set-field
+---@class A
+local m = {}
+
+function m.ff() end
+
+function m.ff(x) end
+
+m.ff(1)
+]]
diff --git a/test/diagnostics/redundant-return-value.lua b/test/diagnostics/redundant-return-value.lua
new file mode 100644
index 00000000..f3e2c584
--- /dev/null
+++ b/test/diagnostics/redundant-return-value.lua
@@ -0,0 +1,32 @@
+TEST [[
+---@type fun():number
+local function f()
+ return 1, <!true!>
+end
+]]
+
+TEST [[
+---@return number, number?
+function F()
+ return 1, 1, <!1!>
+end
+]]
+
+TEST [[
+---@return number, number?
+function F()
+ return 1, 1, <!1!>, <!2!>, <!3!>
+end
+]]
+
+TEST [[
+---@meta
+
+---@return number, number
+local function r2() end
+
+---@return number, number?
+function F()
+ return 1, <!r2()!>
+end
+]]
diff --git a/test/diagnostics/redundant-return.lua b/test/diagnostics/redundant-return.lua
new file mode 100644
index 00000000..11214aab
--- /dev/null
+++ b/test/diagnostics/redundant-return.lua
@@ -0,0 +1,34 @@
+TEST [[
+local function f()
+ <!return!>
+end
+f()
+]]
+
+TEST [[
+local function f()
+ return nil
+end
+f()
+]]
+
+TEST [[
+local function f()
+ local function x()
+ <!return!>
+ end
+ x()
+ return true
+end
+f()
+]]
+
+TEST [[
+local function f()
+ local function x()
+ return true
+ end
+ return x()
+end
+f()
+]]
diff --git a/test/diagnostics/redundant-value.lua b/test/diagnostics/redundant-value.lua
new file mode 100644
index 00000000..a63544df
--- /dev/null
+++ b/test/diagnostics/redundant-value.lua
@@ -0,0 +1,7 @@
+TEST [[
+local _ = 1, <!2!>
+]]
+
+TEST [[
+_ = 1, <!2!>
+]]
diff --git a/test/diagnostics/return-type-mismatch.lua b/test/diagnostics/return-type-mismatch.lua
new file mode 100644
index 00000000..aaf7807f
--- /dev/null
+++ b/test/diagnostics/return-type-mismatch.lua
@@ -0,0 +1,167 @@
+TEST [[
+---@return number
+function F()
+ return <!true!>
+end
+]]
+
+TEST [[
+---@return number?
+function F()
+ return 1
+end
+]]
+
+TEST [[
+---@return number?
+function F()
+ return nil
+end
+]]
+
+TEST [[
+---@return number, number
+local function f()
+ return 1, 1
+end
+
+---@return number, boolean
+function F()
+ return <!f()!>
+end
+]]
+
+TEST [[
+---@return boolean, number
+local function f()
+ return true, 1
+end
+
+---@return number, boolean
+function F()
+ return <!f()!>
+end
+]]
+
+TEST [[
+---@return boolean, number?
+local function f()
+ return true, 1
+end
+
+---@return number, boolean
+function F()
+ return 1, f()
+end
+]]
+
+TEST [[
+---@return number, number?
+local function f()
+ return 1, 1
+end
+
+---@return number, boolean, number
+function F()
+ return 1, <!f()!>
+end
+]]
+
+TEST [[
+---@class A
+---@field x number?
+
+---@return number
+function F()
+ ---@type A
+ local t
+ return t.x
+end
+]]
+
+TEST [[
+---@class A
+---@field x number?
+local t = {}
+
+---@return number
+function F()
+ return t.x
+end
+]]
+
+TEST [[
+---@param ... number
+local function f(...)
+end
+
+f(nil)
+]]
+
+TEST [[
+---@return number
+function F()
+ local n = 0
+ if true then
+ n = 1
+ end
+ return n
+end
+]]
+
+TEST [[
+---@param x boolean
+---@return number
+---@overload fun(): boolean
+local function f(x)
+ if x then
+ return 1
+ else
+ return false
+ end
+end
+]]
+
+TEST [[
+---@param x boolean
+---@return number
+---@overload fun()
+local function f(x)
+ if x then
+ return 1
+ else
+ return
+ end
+end
+]]
+
+TEST [[
+---@param x boolean
+---@return number
+---@overload fun()
+local function f(x)
+ if x then
+ return 1
+ end
+end
+]]
+
+TEST [[
+---@param x boolean
+---@return number
+---@overload fun(): boolean, boolean
+local function f(x)
+ if x then
+ return 1
+ else
+ return false, false
+ end
+end
+]]
+
+TEST [[
+---@type fun():number
+local function f()
+ return <!true!>
+end
+]]
diff --git a/test/diagnostics/trailing-space.lua b/test/diagnostics/trailing-space.lua
new file mode 100644
index 00000000..ff794714
--- /dev/null
+++ b/test/diagnostics/trailing-space.lua
@@ -0,0 +1,32 @@
+TEST [[
+<! !>
+]]
+
+TEST [[
+
+<! !>
+]]
+
+TEST [[
+X = 1<! !>
+]]
+
+TEST [[
+X = [=[
+ ]=]
+]]
+
+TEST [[
+-- xxxx
+]]
+
+TEST [[
+-- [=[
+ ]=]
+]]
+
+TEST [=[
+return [[
+
+]]
+]=]
diff --git a/test/diagnostics/type-check.lua b/test/diagnostics/type-check.lua
deleted file mode 100644
index 18e7190d..00000000
--- a/test/diagnostics/type-check.lua
+++ /dev/null
@@ -1,1262 +0,0 @@
-local config = require 'config'
-
-config.add(nil, 'Lua.diagnostics.disable', 'unused-local')
-config.add(nil, 'Lua.diagnostics.disable', 'unused-function')
-config.add(nil, 'Lua.diagnostics.disable', 'undefined-global')
-config.add(nil, 'Lua.diagnostics.disable', 'redundant-return')
-config.set(nil, 'Lua.type.castNumberToInteger', false)
-
-TEST [[
-local x = 0
-
-<!x!> = true
-]]
-
-TEST [[
----@type integer
-local x
-
-<!x!> = true
-]]
-
-TEST [[
----@type unknown
-local x
-
-x = nil
-]]
-
-TEST [[
----@type unknown
-local x
-
-x = 1
-]]
-
-TEST [[
----@type unknown|nil
-local x
-
-x = 1
-]]
-
-TEST [[
-local x = {}
-
-x = nil
-]]
-
-TEST [[
----@type string
-local x
-
-<?x?> = nil
-]]
-
-TEST [[
----@type string?
-local x
-
-x = nil
-]]
-
-TEST [[
----@type table
-local x
-
-<!x!> = nil
-]]
-
-TEST [[
-local x
-
-x = nil
-]]
-
-TEST [[
----@type integer
-local x
-
----@type number
-<!x!> = f()
-]]
-
-TEST [[
----@type number
-local x
-
----@type integer
-x = f()
-]]
-
-TEST [[
----@type number|boolean
-local x
-
----@type string
-<!x!> = f()
-]]
-
-TEST [[
----@type number|boolean
-local x
-
----@type boolean
-x = f()
-]]
-
-TEST [[
----@type number|boolean
-local x
-
----@type boolean|string
-<!x!> = f()
-]]
-
-TEST [[
----@type boolean
-local x
-
-if not x then
- return
-end
-
-x = f()
-]]
-
-TEST [[
----@type boolean
-local x
-
----@type integer
-local y
-
-<!x!> = y
-]]
-
-TEST [[
-local y = true
-
-local x
-x = 1
-x = y
-]]
-
-TEST [[
-local t = {}
-
-local x = 0
-x = x + #t
-]]
-
-TEST [[
-local x = 0
-
-x = 1.0
-]]
-
-TEST [[
----@class A
-
-local t = {}
-
----@type A
-local a
-
-t = a
-]]
-
-TEST [[
-local m = {}
-
----@type integer[]
-m.ints = {}
-]]
-
-TEST [[
----@class A
----@field x A
-
----@type A
-local t
-
-t.x = {}
-]]
-
-TEST [[
----@class A
----@field x integer
-
----@type A
-local t
-
-<!t.x!> = true
-]]
-
-TEST [[
----@class A
----@field x integer
-
----@type A
-local t
-
----@type boolean
-local y
-
-<!t.x!> = y
-]]
-
-TEST [[
----@class A
-local m
-
-m.x = 1
-
----@type A
-local t
-
-<!t.x!> = true
-]]
-
-TEST [[
----@class A
-local m
-
----@type integer
-m.x = 1
-
-<!m.x!> = true
-]]
-
-TEST [[
----@class A
-local mt
-
----@type integer
-mt.x = 1
-
-function mt:init()
- <!self.x!> = true
-end
-]]
-
-TEST [[
----@class A
----@field x integer
-
----@type A
-local t = {
- <!x!> = true
-}
-]]
-
-TEST [[
----@type boolean[]
-local t = {}
-
-t[5] = nil
-]]
-
-TEST [[
----@type table<string, true>
-local t = {}
-
-t['x'] = nil
-]]
-
-TEST [[
-local t = { true }
-
-t[1] = nil
-]]
-
-TEST [[
----@class A
-local t = {
- x = 1
-}
-
-<!t.x!> = true
-]]
-
-TEST [[
----@type number
-local t
-
-t = 1
-]]
-
-TEST [[
----@type number
-local t
-
----@type integer
-local y
-
-t = y
-]]
-
-TEST [[
----@class A
-local m
-
----@type number
-m.x = 1
-
-<!m.x!> = {}
-]]
-
-TEST [[
----@param x number
-local function f(x) end
-
-f(<!true!>)
-]]
-
-TEST [[
----@type integer
-local x
-
-x = 1.0
-]]
-
-TEST [[
----@type integer
-local x
-
-<!x!> = 1.5
-]]
-
-TEST [[
----@diagnostic disable:undefined-global
----@type integer
-local x
-
-x = 1 + G
-]]
-
-TEST [[
----@diagnostic disable:undefined-global
----@type integer
-local x
-
-x = 1 + G
-]]
-
-TEST [[
----@alias A integer
-
----@type A
-local a
-
----@type integer
-local b
-
-b = a
-]]
-
-TEST [[
----@type string|boolean
-local t
-
----@cast t string
-]]
-
-TEST [[
----@type string|boolean
-local t
-
----@cast t <!number!>
-]]
-
-TEST [[
-local n
-
-if G then
- n = {}
-else
- n = nil
-end
-
-local t = {
- x = n,
-}
-]]
-
-TEST [[
----@class A
-
----@param n A
-local function f(n)
-end
-
----@class B
-local a = {}
-
----@type A?
-a.x = XX
-
-f(a.x)
-]]
-
-TEST [[
----@type string?
-local x
-
-local s = <!x!>:upper()
-]]
-
-TEST [[
----@alias A string|boolean
-
----@param x string|boolean
-local function f(x) end
-
----@type A
-local x
-
-f(x)
-]]
-
-TEST [[
----@alias A string|boolean
-
----@param x A
-local function f(x) end
-
----@type string|boolean
-local x
-
-f(x)
-]]
-
-TEST [[
----@type boolean[]
-local t = {}
-
----@type boolean?
-local x
-
-t[#t+1] = x
-]]
-
-TEST [[
----@type number
-local n
----@type integer
-local i
-
-<?i?> = n
-]]
-
-config.set(nil, 'Lua.type.castNumberToInteger', true)
-TEST [[
----@type number
-local n
----@type integer
-local i
-
-i = n
-]]
-config.set(nil, 'Lua.type.castNumberToInteger', false)
-
-TEST [[
----@type number|boolean
-local nb
-
----@type number
-local n
-
-<?n?> = nb
-]]
-
-config.set(nil, 'Lua.type.weakUnionCheck', true)
-TEST [[
----@type number|boolean
-local nb
-
----@type number
-local n
-
-n = nb
-]]
-config.set(nil, 'Lua.type.weakUnionCheck', false)
-
-TEST [[
----@class Option: string
-
----@param x Option
-local function f(x) end
-
----@type Option
-local x = 'aaa'
-
-f(x)
-]]
-
-TEST [[
----@type number
-local <!x!> = 'aaa'
-]]
-
-TEST [[
----@return number
-function F()
- return <!true!>
-end
-]]
-
-TEST [[
----@return number?
-function F()
- return 1
-end
-]]
-
-TEST [[
----@return number?
-function F()
- return nil
-end
-]]
-
-TEST [[
----@return number, number
-local function f()
- return 1, 1
-end
-
----@return number, boolean
-function F()
- return <!f()!>
-end
-]]
-
-TEST [[
----@return boolean, number
-local function f()
- return true, 1
-end
-
----@return number, boolean
-function F()
- return <!f()!>
-end
-]]
-
-TEST [[
----@return boolean, number?
-local function f()
- return true, 1
-end
-
----@return number, boolean
-function F()
- return 1, f()
-end
-]]
-
-TEST [[
----@return number, number?
-local function f()
- return 1, 1
-end
-
----@return number, boolean, number
-function F()
- return 1, <!f()!>
-end
-]]
-
-TEST [[
----@class A
----@field x number?
-
----@return number
-function F()
- ---@type A
- local t
- return t.x
-end
-]]
-
-TEST [[
----@class A
----@field x number?
-local t = {}
-
----@return number
-function F()
- return t.x
-end
-]]
-
-TEST [[
----@param ... number
-local function f(...)
-end
-
-f(nil)
-]]
-
-TEST [[
----@return number
-function F()
- local n = 0
- if true then
- n = 1
- end
- return n
-end
-]]
-
-TEST [[
----@class X
-
----@class A
-local mt = G
-
----@type X
-mt._x = nil
-]]
-
-config.set(nil, 'Lua.type.weakNilCheck', true)
-TEST [[
----@type number?
-local nb
-
----@type number
-local n
-
-n = nb
-]]
-
-TEST [[
----@type number|nil
-local nb
-
----@type number
-local n
-
-n = nb
-]]
-config.set(nil, 'Lua.type.weakNilCheck', false)
-
-TEST [[
----@class A
-local a = {}
-
----@class B
-local <!b!> = a
-]]
-
-TEST [[
----@class A
-local a = {}
-
----@class B: A
-local b = a
-]]
-
-TEST [[
----@class A
-local a = {}
-a.__index = a
-
----@class B: A
-local b = setmetatable({}, a)
-]]
-
-TEST [[
----@class A
-local a = {}
-
----@class B: A
-local b = setmetatable({}, {__index = a})
-]]
-
-TEST [[
----@class A
-local a = {}
-
----@class B
-local <!b!> = setmetatable({}, {__index = a})
-]]
-
-TEST [[
----@class A
----@field x number?
-local a
-
----@class B
----@field x number
-local b
-
-b.x = a.x
-]]
-
-TEST [[
-
----@class A
----@field x number?
-local a
-
----@type number
-local t
-
-t = a.x
-]]
-
-TEST [[
-local mt = {}
-mt.x = 1
-mt.x = nil
-]]
-
-TEST [[
----@type string[]
-local t
-
-<!t!> = 'xxx'
-]]
-
-TEST [[
----@param b boolean
-local function f(b)
-end
-
----@type boolean
-local t
-
-if t then
- f(t)
-end
-]]
-
-config.set(nil, 'Lua.type.weakUnionCheck', true)
-TEST [[
----@type number
-local x = G
-]]
-
-TEST [[
----@generic T
----@param x T
----@return T
-local function f(x)
- return x
-end
-]]
-config.set(nil, 'Lua.type.weakUnionCheck', false)
-
-TEST [[
----@type 1|2
-local x
-
-x = 1
-x = 2
-<!x!> = 3
-]]
-
-TEST [[
----@type 'x'|'y'
-local x
-
-x = 'x'
-x = 'y'
-<!x!> = 'z'
-]]
-
-TEST [[
----@enum A
-local t = {
- x = 1,
- y = 2,
-}
-
----@param x A
-local function f(x)
-end
-
-f(<!t!>)
-f(t.x)
-f(1)
-f(<!3!>)
-]]
-
-TEST [[
----@enum A
-local t = {
- x = { h = 1 },
- y = { h = 2 },
-}
-
----@param x A
-local function f(x)
-end
-
-f(t.x)
-f(t.y)
-f(<!{ h = 1 }!>)
-]]
-
-TEST [[
-local t = {
- x = 1,
-}
-
-local x
-t[x] = true
-]]
-
-TEST [[
----@param x boolean
----@return number
----@overload fun(): boolean
-local function f(x)
- if x then
- return 1
- else
- return false
- end
-end
-]]
-
-TEST [[
----@param x boolean
----@return number
----@overload fun()
-local function f(x)
- if x then
- return 1
- else
- return
- end
-end
-]]
-
-TEST [[
----@param x boolean
----@return number
----@overload fun()
-local function f(x)
- if x then
- return 1
- end
-end
-]]
-
-TEST [[
----@param x boolean
----@return number
----@overload fun(): boolean, boolean
-local function f(x)
- if x then
- return 1
- else
- return false, false
- end
-end
-]]
-
-TEST [[
----@alias test boolean
-
----@type test
-local <!test!> = 4
-]]
-
-TEST [[
----@class MyClass
-local MyClass = {}
-
-function MyClass:new()
- ---@class MyClass
- local myObject = setmetatable({
- initialField = true
- }, self)
-
- print(myObject.initialField)
-end
-]]
-
-TEST [[
----@class T
-local t = {
- x = nil
-}
-
-t.x = 1
-]]
-
-TEST [[
----@generic T: string | boolean | table
----@param x T
----@return T
-local function f(x)
- return x
-end
-
-f(<!1!>)
-]]
-
-TEST [[
----@type table<string, string>
-local x
-
----@type table<number, string>
-local y
-
-<!x!> = y
-]]
-
-TEST [[
----@type table<string, string>
-local x
-
----@type table<string, number>
-local y
-
-<!x!> = y
-]]
-
-TEST [[
----@type table<string, string>
-local x
-
----@type table<string, string>
-local y
-
-x = y
-]]
-
-TEST [[
----@param opts {a:number, b:number}
-local function foo(opts)
-
-end
-
----@param opts {a:number, b:number}
-local function bar(opts)
- foo(opts)
-end
-]]
-
-TEST [[
----@param opts {a:number, b:number}
-local function foo(opts)
-
-end
-
----@param opts {c:number, d:number}
-local function bar(opts)
- foo(<!opts!>) -- this should raise linting error
-end
-]]
-
-TEST [[
----@param opts {[number]: boolean}
-local function foo(opts)
-
-end
-
----@param opts {[1]: boolean}
-local function bar(opts)
- foo(opts)
-end
-]]
-
-TEST [[
----@type {[1]: string, [10]: number, xx: boolean}
-local t = {
- <!true!>,
- <![10]!> = 's',
- <!xx!> = 1,
-}
-]]
-
-TEST [[
----@type { x: number, y: number }
-local t1
-
----@type { x: number }
-local t2
-
-<!t1!> = t2
-]]
-
-TEST [[
----@type { x: number, [integer]: number }
-local t1
-
----@type { x: number }
-local t2
-
-<!t1!> = t2
-]]
-
-TEST [[
----@type boolean[]
-local t = { <!1!>, <!2!>, <!3!> }
-]]
-
-TEST [[
----@type boolean[]
-local t = { true, false, nil }
-]]
-
-TEST [[
----@type boolean|nil
-local x
-
----@type boolean[]
-local t = { true, false, x }
-]]
-
-TEST [[
----@type fun():number
-local function f()
-<!!>end
-]]
-
-TEST [[
----@type fun():number
-local function f()
- <!return!>
-end
-]]
-
-TEST [[
----@type fun():number?
-local function f()
-end
-]]
-
-TEST [[
----@type fun():...
-local function f()
-end
-]]
-
-TEST [[
----@type fun():number
-local function f()
- return 1, <!true!>
-end
-]]
-
-TEST [[
----@type fun():number
-local function f()
- return <!true!>
-end
-]]
-
-TEST [[
----@enum Enum
-local t = {
- x = 1,
- y = 2,
-}
-
----@type Enum
-local y
-
----@type integer
-local x = y
-]]
-
-TEST [[
----@generic T
----@param v1 T
----@param v2 T|table
-local function func(v1, v2)
-end
-
-func('hello', 'world')
-]]
-
-TEST [[
----@generic T1, T2, T3, T4, T5
----@param f fun(): T1?, T2?, T3?, T4?, T5?
----@return T1?, T2?, T3?, T4?, T5?
-local function foo(f)
- return f()
-end
-
-local a, b = foo(function()
- return 1
-end)
-]]
-
-TEST [[
----@generic T1, T2, T3, T4, T5
----@param f fun(): T1|nil, T2|nil, T3|nil, T4|nil, T5|nil
----@return T1?, T2?, T3?, T4?, T5?
-local function foo(f)
- return f()
-end
-
-local a, b = foo(function()
- return 1
-end)
-]]
-
-TEST [[
----@type string|string[]|string[][]
-local t = {{'a'}}
-]]
-
-TEST [[
-local A = "Hello"
-local B = "World"
-
----@alias myLiteralAliases `A` | `B`
-
----@type myLiteralAliases
-local x = A
-]]
-
-TEST [[
-local enum = { a = 1, b = 2 }
-
----@type { [integer] : boolean }
-local t = {
- <![enum.a]!> = 1,
- <![enum.b]!> = 2,
- <![3]!> = 3,
-}
-]]
-
-TEST [[
-local x
-
-if X then
- x = 'A'
-elseif X then
- x = 'B'
-else
- x = 'C'
-end
-
-local y = x
-
-<!y!> = nil
-]]
-(function (diags)
- local diag = diags[1]
- assert(diag.message == [[
-已显式定义变量的类型为 `string` ,不能再将其类型转换为 `nil`。
-- `nil` 无法匹配 `string`
-- 类型 `nil` 无法匹配 `string`]])
-end)
-
-
-TEST [[
----@type 'A'|'B'|'C'|'D'|'E'|'F'|'G'|'H'|'I'|'J'|'K'|'L'|'M'|'N'|'O'|'P'|'Q'|'R'|'S'|'T'|'U'|'V'|'W'|'X'|'Y'|'Z'
-local x
-
-<!x!> = nil
-]]
-(function (diags)
- local diag = diags[1]
- assert(diag.message == [[
-已显式定义变量的类型为 `'A'|'B'|'C'|'D'|'E'...(+21)` ,不能再将其类型转换为 `nil`。
-- `nil` 无法匹配 `'A'|'B'|'C'|'D'|'E'...(+21)`
-- `nil` 无法匹配 `'A'|'B'|'C'|'D'|'E'...(+21)` 中的任何子类
-- 类型 `nil` 无法匹配 `'Z'`
-- 类型 `nil` 无法匹配 `'Y'`
-- 类型 `nil` 无法匹配 `'X'`
-- 类型 `nil` 无法匹配 `'W'`
-- 类型 `nil` 无法匹配 `'V'`
-- 类型 `nil` 无法匹配 `'U'`
-- 类型 `nil` 无法匹配 `'T'`
-- 类型 `nil` 无法匹配 `'S'`
-- 类型 `nil` 无法匹配 `'R'`
-- 类型 `nil` 无法匹配 `'Q'`
-...(+13)
-- 类型 `nil` 无法匹配 `'C'`
-- 类型 `nil` 无法匹配 `'B'`
-- 类型 `nil` 无法匹配 `'A'`]])
-end)
-
-TEST [[
----@param v integer
----@return boolean
-local function is_string(v)
- return type(v) == 'string'
-end
-
-print(is_string(3))
-]]
-
-TEST [[
----@class SomeClass
----@field [1] string
--- ...
-
----@param some_param SomeClass|SomeClass[]
-local function some_fn(some_param) return end
-
-some_fn { { "test" } } -- <- diagnostic: "Cannot assign `table` to `string`."
-]]
-
-TEST [[
----@param p integer|string
-local function get_val(p)
- local is_number = type(p) == 'number'
- return is_number and p or p
-end
-
-get_val('hi')
-]]
-
-TESTWITH 'param-type-mismatch' [[
----@class Class
-local Class = {}
-
----@param source string
-function Class.staticCreator(source)
-
-end
-
-Class.staticCreator(<!true!>)
-Class<!:!>staticCreator() -- Expecting a waring
-]]
-
-TESTWITH 'assign-type-mismatch' [[
----@type string[]
-local arr = {
- <!3!>,
-}
-]]
-
-TESTWITH 'assign-type-mismatch' [[
----@type (string|boolean)[]
-local arr2 = {
- <!3!>, -- no warnings
-}
-]]
-
-TEST [[
----@class A
-
----@class B : A
-
----@class C : B
-
----@class D : B
-
----@param x A
-local function func(x) end
-
----@type C|D
-local var
-func(var)
-]]
-
-config.remove(nil, 'Lua.diagnostics.disable', 'unused-local')
-config.remove(nil, 'Lua.diagnostics.disable', 'unused-function')
-config.remove(nil, 'Lua.diagnostics.disable', 'undefined-global')
-config.remove(nil, 'Lua.diagnostics.disable', 'redundant-return')
-config.set(nil, 'Lua.type.castNumberToInteger', true)
diff --git a/test/diagnostics/unbalanced-assignments.lua b/test/diagnostics/unbalanced-assignments.lua
new file mode 100644
index 00000000..a0d47f56
--- /dev/null
+++ b/test/diagnostics/unbalanced-assignments.lua
@@ -0,0 +1,34 @@
+TEST [[
+local x, <!y!>, <!z!> = 1
+]]
+
+TEST [[
+local x, y, <!z!> = 1, 2
+]]
+
+TEST [[
+local x, y, z = print()
+]]
+
+TEST [[
+local x, y, z
+]]
+
+TEST [[
+local x, y, z
+x, <!y!>, <!z!> = 1
+]]
+
+TEST [[
+X, <!Y!>, <!Z!> = 1
+]]
+
+TEST [[
+T = {}
+T.x, <!T.y!>, <!T.z!> = 1
+]]
+
+TEST [[
+T = {}
+T['x'], <!T['y']!>, <!T['z']!> = 1
+]]
diff --git a/test/diagnostics/undefined-doc-class.lua b/test/diagnostics/undefined-doc-class.lua
new file mode 100644
index 00000000..d10b89d8
--- /dev/null
+++ b/test/diagnostics/undefined-doc-class.lua
@@ -0,0 +1,3 @@
+TEST [[
+---@class A : <!B!>
+]]
diff --git a/test/diagnostics/undefined-doc-name.lua b/test/diagnostics/undefined-doc-name.lua
new file mode 100644
index 00000000..9a55108a
--- /dev/null
+++ b/test/diagnostics/undefined-doc-name.lua
@@ -0,0 +1,19 @@
+TEST [[
+---@type <!A!>
+]]
+
+TEST [[
+---@class A
+---@type A|<!B!>|<!C!>
+]]
+
+TEST [[
+---@class AAA
+---@alias B AAA
+
+---@type B
+]]
+
+TEST [[
+---@alias B <!AAA!>
+]]
diff --git a/test/diagnostics/undefined-doc-param.lua b/test/diagnostics/undefined-doc-param.lua
new file mode 100644
index 00000000..7bfbafa5
--- /dev/null
+++ b/test/diagnostics/undefined-doc-param.lua
@@ -0,0 +1,21 @@
+TEST [[
+---@param <!x!> Class
+]]
+
+TEST [[
+---@class Class
+---@param <!y!> Class
+local function f(x)
+ return x
+end
+f()
+]]
+
+TEST [[
+---@class Class
+---@param <!y!> Class
+function F(x)
+ return x
+end
+F()
+]]
diff --git a/test/diagnostics/undefined-env-child.lua b/test/diagnostics/undefined-env-child.lua
new file mode 100644
index 00000000..73904a74
--- /dev/null
+++ b/test/diagnostics/undefined-env-child.lua
@@ -0,0 +1,36 @@
+TEST [[
+---@type iolib
+_ENV = io
+<!print!>(stderr) -- `print` is warning but `stderr` is not
+]]
+
+TEST [[
+---@type iolib
+local _ENV = io
+<!print!>(stderr) -- `print` is warning but `stderr` is not
+]]
+
+TEST [[
+local _ENV = { print = print }
+print(1)
+]]
+
+TEST [[
+_ENV = {}
+GLOBAL = 1 --> _ENV.GLOBAL = 1
+]]
+
+TEST [[
+_ENV = {}
+local _ = print --> local _ = _ENV.print
+]]
+
+TEST [[
+GLOBAL = 1
+_ENV = nil
+]]
+
+TEST [[
+print(1)
+_ENV = nil
+]]
diff --git a/test/diagnostics/undefined-field.lua b/test/diagnostics/undefined-field.lua
new file mode 100644
index 00000000..aff329fb
--- /dev/null
+++ b/test/diagnostics/undefined-field.lua
@@ -0,0 +1,148 @@
+TEST [[
+---@class Foo
+---@field field1 integer
+local mt = {}
+function mt:Constructor()
+ self.field2 = 1
+end
+function mt:method1() return 1 end
+function mt.method2() return 2 end
+
+---@class Bar: Foo
+---@field field4 integer
+local mt2 = {}
+
+---@type Foo
+local v
+print(v.field1 + 1)
+print(v.field2 + 1)
+print(v.<!field3!> + 1)
+print(v:method1())
+print(v.method2())
+print(v:<!method3!>())
+
+---@type Bar
+local v2
+print(v2.field1 + 1)
+print(v2.field2 + 1)
+print(v2.<!field3!> + 1)
+print(v2.field4 + 1)
+print(v2:method1())
+print(v2.method2())
+print(v2:<!method3!>())
+
+local v3 = {}
+print(v3.abc)
+
+---@class Bar2
+local mt3
+function mt3:method() return 1 end
+print(mt3:method())
+]]
+
+-- checkUndefinedField 通过type找到class
+TEST [[
+---@class Foo
+local Foo
+function Foo:method1() end
+
+---@type Foo
+local v
+v:method1()
+v:<!method2!>() -- doc.class.name
+]]
+
+-- checkUndefinedField 通过type找到class,涉及到 class 继承版
+TEST [[
+---@class Foo
+local Foo
+function Foo:method1() end
+---@class Bar: Foo
+local Bar
+function Bar:method3() end
+
+---@type Bar
+local v
+v:method1()
+v:<!method2!>() -- doc.class.name
+v:method3()
+]]
+
+-- checkUndefinedField 类名和类变量同名,类变量被直接使用
+TEST [[
+---@class Foo
+local Foo
+function Foo:method1() end
+Foo:<!method2!>() -- doc.class
+Foo:<!method2!>() -- doc.class
+]]
+
+-- checkUndefinedField 没有@class的不检测
+TEST [[
+local Foo
+function Foo:method1()
+ return Foo:method2() -- table
+end
+]]
+
+-- checkUndefinedField 类名和类变量不同名,类变量被直接使用、使用self
+TEST [[
+---@class Foo
+local mt
+function mt:method1()
+ mt.<!method2!>() -- doc.class
+ self:method1()
+ return self.<!method2!>() -- doc.class.name
+end
+]]
+
+-- checkUndefinedField 当会推导成多个class类型时
+TEST [[
+---@class Foo
+local mt
+function mt:method1() end
+
+---@class Bar
+local mt2
+function mt2:method2() end
+
+---@type Foo
+local v
+---@type Bar
+local v2
+v2 = v
+v2:method1()
+v2:<!method2!>()
+]]
+
+TEST [[
+---@type table
+T1 = {}
+print(T1.f1)
+---@type tablelib
+T2 = {}
+print(T2.<!f2!>)
+]]
+
+TEST [[
+---@type string|table
+local n
+
+print(n.x)
+]]
+
+TEST [[
+---@type 'x'
+local t
+
+local n = t:upper()
+]]
+
+TEST [[
+---@alias A 'x'
+
+---@type A
+local t
+
+local n = t:upper()
+]]
diff --git a/test/diagnostics/undefined-global.lua b/test/diagnostics/undefined-global.lua
new file mode 100644
index 00000000..f5a6396c
--- /dev/null
+++ b/test/diagnostics/undefined-global.lua
@@ -0,0 +1,36 @@
+TEST [[
+local print, _G
+print(<!x!>)
+print(<!log!>)
+print(<!X!>)
+print(<!Log!>)
+print(<!y!>)
+print(Z)
+print(_G)
+Z = 1
+]]
+
+TEST [[
+X = table[<!x!>]
+]]
+TEST [[
+T1 = 1
+_ENV.T2 = 1
+_G.T3 = 1
+_ENV._G.T4 = 1
+_G._G._G.T5 = 1
+rawset(_G, 'T6', 1)
+rawset(_ENV, 'T7', 1)
+print(T1)
+print(T2)
+print(T3)
+print(T4)
+print(T5)
+print(T6)
+print(T7)
+]]
+
+TEST [[
+---@class c
+c = {}
+]]
diff --git a/test/diagnostics/unknown-cast-variable.lua b/test/diagnostics/unknown-cast-variable.lua
new file mode 100644
index 00000000..a347083f
--- /dev/null
+++ b/test/diagnostics/unknown-cast-variable.lua
@@ -0,0 +1,8 @@
+TEST [[
+---@cast <!x!> integer
+]]
+
+TEST [[
+local x, y
+---@cast y number
+]]
diff --git a/test/diagnostics/unknown-diag-code.lua b/test/diagnostics/unknown-diag-code.lua
new file mode 100644
index 00000000..33b41886
--- /dev/null
+++ b/test/diagnostics/unknown-diag-code.lua
@@ -0,0 +1,3 @@
+TEST [[
+---@diagnostic disable-next-line: <!xxx!>
+]]
diff --git a/test/diagnostics/unknown-operator.lua b/test/diagnostics/unknown-operator.lua
new file mode 100644
index 00000000..bb193f6a
--- /dev/null
+++ b/test/diagnostics/unknown-operator.lua
@@ -0,0 +1,4 @@
+TEST [[
+---@class A
+---@operator <!xxx!>: A
+]]
diff --git a/test/diagnostics/unreachable-code.lua b/test/diagnostics/unreachable-code.lua
new file mode 100644
index 00000000..4444252f
--- /dev/null
+++ b/test/diagnostics/unreachable-code.lua
@@ -0,0 +1,71 @@
+TEST [[
+if X then
+ return false
+elseif X then
+ return false
+else
+ return false
+end
+<!return true!>
+]]
+
+TEST [[
+function X()
+ if X then
+ return false
+ elseif X then
+ return false
+ else
+ return false
+ end
+ <!return true!>
+end
+]]
+
+TEST [[
+while true do
+end
+
+<!print(1)!>
+]]
+
+TEST [[
+while true do
+end
+
+<!print(1)!>
+]]
+
+TEST [[
+while X do
+ X = 1
+end
+
+print(1)
+]]
+
+TEST [[
+while true do
+ if not X then
+ break
+ end
+end
+
+print(1)
+
+do return end
+]]
+
+TEST [[
+local done = false
+
+local function set_done()
+ done = true
+end
+
+while not done do
+ set_done()
+end
+
+print(1)
+]]
diff --git a/test/diagnostics/unused-function.lua b/test/diagnostics/unused-function.lua
new file mode 100644
index 00000000..c2cea23a
--- /dev/null
+++ b/test/diagnostics/unused-function.lua
@@ -0,0 +1,48 @@
+TEST [[
+local <!function x()
+end!>
+]]
+
+TEST [[
+local x = <!function () end!>
+]]
+
+TEST [[
+local x
+x = <!function () end!>
+]]
+
+TEST [[
+local <!function x()
+end!>
+local <!function y()
+ x()
+end!>
+]]
+
+TEST [[
+local f = <!function () end!>
+]]
+
+TEST [[
+local f;f = <!function () end!>
+]]
+
+TEST [[
+local <!function f() end!>
+]]
+
+TEST [[
+local <!function f()
+ f()
+end!>
+]]
+
+
+TEST [[
+local <!function test()
+end!>
+
+local <!function foo ()
+end!>
+]]
diff --git a/test/diagnostics/unused-label.lua b/test/diagnostics/unused-label.lua
new file mode 100644
index 00000000..3a89a147
--- /dev/null
+++ b/test/diagnostics/unused-label.lua
@@ -0,0 +1,3 @@
+TEST [[
+::<!LABEL!>::
+]]
diff --git a/test/diagnostics/unused-local.lua b/test/diagnostics/unused-local.lua
new file mode 100644
index 00000000..425f3e1e
--- /dev/null
+++ b/test/diagnostics/unused-local.lua
@@ -0,0 +1,99 @@
+local config = require 'config'
+
+TEST [[
+local <!x!>
+]]
+
+TEST [[
+local y
+local x <close> = y
+]]
+
+TEST [[
+local function x()
+end
+x()
+]]
+
+TEST [[
+return function (x)
+ x.a = 1
+end
+]]
+
+TEST [[
+local <!t!> = {}
+<!t!>.a = 1
+]]
+
+TEST [[
+InstanceName = 1
+Instance = _G[InstanceName]
+]]
+
+TEST [[
+local _ = (''):sub(1, 2)
+]]
+
+TEST [[
+local mt, x
+function mt:m()
+ function x:m()
+ end
+end
+return mt, x
+]]
+
+TEST [[
+local mt = {}
+function mt:f()
+end
+return mt
+]]
+
+TEST [[
+local <!mt!> = {}
+function <!mt!>:f()
+end
+]]
+
+TEST [[
+local <!x!> = {}
+<!x!>.a = 1
+]]
+
+TEST [[
+local <!x!> = {}
+<!x!>['a'] = 1
+]]
+
+TEST [[
+local function f(<!self!>)
+ return 'something'
+end
+f()
+]]
+
+TEST [[
+local function f(var)
+ print(var)
+end
+local var
+f(var)
+]]
+
+TEST [[
+local <!t!> = {}
+<!t!>[1] = 1
+]]
+
+config.add(nil, 'Lua.diagnostics.unusedLocalExclude', 'll_*')
+
+TEST [[
+local <!xx!>
+local ll_1
+local ll_2
+local <!ll!>
+]]
+
+config.remove(nil, 'Lua.diagnostics.unusedLocalExclude', 'll_*')
diff --git a/test/diagnostics/unused-vararg.lua b/test/diagnostics/unused-vararg.lua
new file mode 100644
index 00000000..2bed4aab
--- /dev/null
+++ b/test/diagnostics/unused-vararg.lua
@@ -0,0 +1,6 @@
+TEST [[
+local function f(<!...!>)
+ return 'something'
+end
+f()
+]]
diff --git a/test/full/example.lua b/test/full/example.lua
index de32d60b..920dcd0d 100644
--- a/test/full/example.lua
+++ b/test/full/example.lua
@@ -4,7 +4,7 @@ local files = require 'files'
local diag = require 'core.diagnostics'
local config = require 'config'
local fs = require 'bee.filesystem'
-local luadoc = require "parser.luadoc"
+local luadoc = require "parser".luadoc
-- 临时
---@diagnostic disable: await-in-sync
diff --git a/test/full/init.lua b/test/full/init.lua
index ac78f134..a6496a99 100644
--- a/test/full/init.lua
+++ b/test/full/init.lua
@@ -37,7 +37,7 @@ end) do
end
end
-util.revertTable(times)
+util.revertArray(times)
for _, time in ipairs(times) do
print(time)
end
diff --git a/test/full/projects.lua b/test/full/projects.lua
index dacc101c..20ef6724 100644
--- a/test/full/projects.lua
+++ b/test/full/projects.lua
@@ -41,7 +41,7 @@ local function doProjects(pathname)
print('开始诊断...')
- ws.ready = true
+ furi.encode(path:string())
diag.diagnosticsScope(furi.encode(path:string()))
local clock = os.clock()
diff --git a/test/full/self.lua b/test/full/self.lua
index 09d2d154..d118e034 100644
--- a/test/full/self.lua
+++ b/test/full/self.lua
@@ -32,7 +32,7 @@ end
print('基准诊断目录:', path)
-ws.ready = true
+ws.awaitReady(furi.encode(path:string()))
diag.diagnosticsScope(furi.encode(path:string()))
local clock = os.clock()
@@ -86,7 +86,7 @@ end) do
end
end
-util.revertTable(printTexts)
+util.revertArray(printTexts)
for _, text in ipairs(printTexts) do
print(text)
diff --git a/test/hover/init.lua b/test/hover/init.lua
index e77d8c98..0cfce3a4 100644
--- a/test/hover/init.lua
+++ b/test/hover/init.lua
@@ -1228,6 +1228,17 @@ local <?x?>
local x: table<ClassA, ClassB>
]]
+TEST [[
+---@type [ClassA, ClassB]
+local <?x?>
+]]
+[[
+local x: [ClassA, ClassB] {
+ [1]: ClassA,
+ [2]: ClassB,
+}
+]]
+
--TEST [[
-----@class ClassA
-----@class ClassB
@@ -2383,6 +2394,25 @@ local obj: B {
TEST [[
---@class A
+local M = {}
+
+---@private
+M.x = 0
+
+---@private
+function M:init()
+ self.x = 1
+end
+
+---@type A
+local <?a?>
+]]
+[[
+local a: A
+]]
+
+TEST [[
+---@class A
---@field x fun(): string
---@type table<string, A>
@@ -2447,3 +2477,25 @@ local t: {
[3]: table,
}
]]
+
+TEST [[
+---@class A
+---@overload fun(x: number): boolean
+local <?x?>
+]]
+[[
+local x: A
+]]
+
+TEST [[
+---@type A
+local <?f?>
+
+---@enum A
+local t = {
+ x = f,
+}
+]]
+[[
+local f: A
+]]
diff --git a/test/plugins/ast/helper.lua b/test/plugins/ast/helper.lua
new file mode 100644
index 00000000..f892ea1b
--- /dev/null
+++ b/test/plugins/ast/helper.lua
@@ -0,0 +1,64 @@
+local helper = require 'plugins.astHelper'
+local parser = require 'parser'
+
+function Run(script, plugin)
+ local state = parser.compile(script, "Lua", "Lua 5.4")
+ plugin(state)
+ parser.luadoc(state)
+ return state
+end
+
+local function TestInsertDoc(script)
+ local state = Run(script, function (state)
+ local comment = assert(helper.buildComment("class", "AA", state.ast[1].start))
+ helper.InsertDoc(state.ast, comment)
+ end)
+ assert(state.ast[1].bindDocs)
+end
+
+TestInsertDoc("A={}")
+
+local function TestaddClassDoc(script)
+ local state = Run(script, function (state)
+ assert(helper.addClassDoc(state.ast, state.ast[1], "AA"))
+ end)
+ assert(state.ast[1].bindDocs)
+end
+
+TestaddClassDoc [[a={}]]
+
+TestaddClassDoc [[local a={}]]
+
+local function TestaddClassDocAtParam(script, index)
+ index = index or 1
+ local arg
+ local state = Run(script, function (state)
+ local func = state.ast[1].value
+ local ok
+ ok, arg = helper.addClassDocAtParam(state.ast, "AA", func, index)
+ assert(ok)
+ end)
+ assert(arg.bindDocs)
+end
+
+TestaddClassDocAtParam [[
+ function a(b) end
+]]
+
+local function TestaddParamTypeDoc(script, index)
+ index = index or 1
+ local func
+ Run(script, function (state)
+ func = state.ast[1].value
+ assert(helper.addParamTypeDoc(state.ast, "string", func.args[index]))
+ end)
+ assert(func.args[index].bindDocs)
+end
+
+TestaddParamTypeDoc [[
+ local function t(a)end
+]]
+
+TestaddParamTypeDoc([[
+ local function t(a,b,c,d)end
+]], 4)
diff --git a/test/plugins/ast/test.lua b/test/plugins/ast/test.lua
new file mode 100644
index 00000000..a01b9e2e
--- /dev/null
+++ b/test/plugins/ast/test.lua
@@ -0,0 +1,145 @@
+local parser = require 'parser'
+local guide = require 'parser.guide'
+local helper = require 'plugins.astHelper'
+
+---@diagnostic disable: await-in-sync
+---@param ... function checkers
+local function TestPlugin(script, plugin, ...)
+ local state = parser.compile(script, "Lua", "Lua 5.4")
+ state.ast = plugin(TESTURI, state.ast) or state.ast
+ parser.luadoc(state)
+ for i = 1, select('#', ...) do
+ select(i, ...)(state)
+ end
+end
+
+local function isDocClass(ast)
+ return ast.bindDocs[1].type == 'doc.class'
+end
+
+local function TestAIsClass(state, next)
+ assert(isDocClass(state.ast[1]))
+end
+
+--- when call Class
+local function plugin_AddClass(uri, ast)
+ guide.eachSourceType(ast, "call", function (source)
+ local node = source.node
+ if not guide.isGet(node) then
+ return
+ end
+ if not guide.isGlobal(node) then
+ return
+ end
+ if guide.getKeyName(node) ~= 'Class' then
+ return
+ end
+ local wants = {
+ ['local'] = true,
+ ['setglobal'] = true
+ }
+ local classnameNode = guide.getParentTypes(source, wants)
+ if not classnameNode then
+ return
+ end
+ local classname = guide.getKeyName(classnameNode)
+ if classname then
+ helper.addClassDoc(ast, classnameNode, classname)
+ end
+ end)
+end
+
+local function plugin_AddClassAtParam(uri, ast)
+ guide.eachSourceType(ast, "function", function (src)
+ helper.addClassDocAtParam(ast, "A", src, 1)
+ end)
+end
+
+local function TestSelfIsClass(state, next)
+ guide.eachSourceType(state.ast, "local", function (source)
+ if source[1] == 'self' then
+ assert(source.bindDocs)
+ assert(source.parent.type == 'function')
+ assert(#source.parent.args == 0)
+ end
+ end)
+end
+
+local function TestAHasField(state, next)
+ local foundFields = false
+ local cb = function (source)
+ if not source.bindDocs or not source.bindDocs[1].class or source.bindDocs[1].class[1] ~= "A" then
+ return
+ end
+ assert(#source.bindDocs == 1)
+ assert(#source.bindDocs[1].fields >= 2)
+ assert(source.bindDocs[1].fields[1].field[1] == "x")
+ assert(source.bindDocs[1].fields[2].field[1] == "y")
+ foundFields = true
+ end
+ guide.eachSourceType(state.ast, "local", cb)
+ guide.eachSourceType(state.ast, "setglobal", cb)
+ assert(foundFields)
+end
+
+local function plugin_AddMultipleDocs(uri, ast)
+ guide.eachSourceType(ast, "call", function(source)
+ local node = source.node
+ if guide.getKeyName(node) ~= 'Class' then
+ return
+ end
+ local wants = {
+ ['local'] = true,
+ ['setglobal'] = true
+ }
+ local classnameNode = guide.getParentTypes(source, wants)
+ if not classnameNode then
+ return
+ end
+ local classname = guide.getKeyName(classnameNode)
+ if classname then
+ local group = {}
+ helper.addClassDoc(ast, classnameNode, classname, group)
+ helper.addDoc(ast, classnameNode, "field", "x number", group)
+ helper.addDoc(ast, classnameNode, "field", "y string", group)
+ end
+ end)
+end
+
+local function TestPlugin1(script)
+ TestPlugin(script, plugin_AddClass, TestAIsClass)
+end
+
+local function TestPlugin2(script)
+ TestPlugin(script, plugin_AddClassAtParam, TestSelfIsClass)
+end
+
+local function TestPlugin3(script)
+ TestPlugin(script, plugin_AddMultipleDocs, TestAIsClass, TestAHasField)
+end
+
+TestPlugin1 [[
+ local A = Class(function() end)
+]]
+
+TestPlugin1 [[
+ A = Class(function() end)
+]]
+
+TestPlugin2 [[
+ local function ctor(self) end
+]]
+
+TestPlugin2 [[
+ function ctor(self) end
+]]
+
+TestPlugin3 [[
+ local A = Class()
+]]
+
+TestPlugin3 [[
+ A = Class()
+]]
+
+require 'plugins.ast.helper'
diff --git a/test/plugins/ffi/builder.lua b/test/plugins/ffi/builder.lua
new file mode 100644
index 00000000..ebbfab55
--- /dev/null
+++ b/test/plugins/ffi/builder.lua
@@ -0,0 +1,233 @@
+local ffi = require 'plugins.ffi'
+local util = require 'utility'
+rawset(_G, 'TEST', true)
+
+local function removeEmpty(lines)
+ local removeLines = {}
+ for i, v in ipairs(lines) do
+ if v ~= '\n' then
+ removeLines[#removeLines+1] = v:gsub('^%s+', '')
+ end
+ end
+ return removeLines
+end
+
+local function formatLines(lines)
+ if not lines or #lines == 0 then
+ return {}
+ end
+ table.remove(lines, 1)
+ return removeEmpty(lines)
+end
+
+---@param str string
+local function splitLines(str)
+ local lines = {}
+ local i = 1
+ for line in str:gmatch("[^\r\n]+") do
+ lines[i] = line
+ i = i + 1
+ end
+ return lines
+end
+
+function TEST(wanted)
+ wanted = removeEmpty(splitLines(wanted))
+ return function (script)
+ local lines = formatLines(ffi.compileCodes({ script }))
+ assert(util.equal(wanted, lines), util.dump(lines))
+ end
+end
+
+TEST [[
+ ---@alias ffi.namespace*.EVP_MD ffi.namespace*.struct@env_md_st
+
+ ---@return ffi.namespace*.EVP_MD
+ function m.EVP_md5() end
+]] [[
+ typedef struct env_md_st EVP_MD;
+ const EVP_MD *EVP_md5(void);
+]]
+
+TEST [[
+ ---@class ffi.namespace*.struct@a
+ ---@field _in integer
+]] [[
+ struct a {
+ int in;
+ };
+]]
+
+TEST [[
+---@param _in integer
+function m.test(_in) end
+]] [[
+ void test(int in);
+]]
+
+TEST [[
+ ---@alias ffi.namespace*.ENGINE ffi.namespace*.struct@engine_st
+ ---@alias ffi.namespace*.ENGINE1 ffi.namespace*.enum@engine_st1
+]] [[
+ typedef struct engine_st ENGINE;
+ typedef enum engine_st1 ENGINE1;
+]]
+
+TEST [[
+ ---@param a integer[][]
+ function m.test(a) end
+]] [[
+ void test(int a[][]);
+]]
+
+TEST [[
+ ---@class ffi.namespace*.struct@A
+ ---@field b integer[]
+ ---@field c integer[]
+]] [[
+ struct A {
+ int b[5];
+ int c[];
+ };
+]]
+
+TEST [[
+ m.B = 5
+ m.A = 0
+ m.D = 7
+ m.C = 6
+]] [[
+ enum {
+ A,
+ B=5,
+ C,
+ D,
+ };
+]]
+
+TEST [[
+ m.B = 2
+ m.A = 1
+ m.C = 5
+ ---@alias ffi.namespace*.enum@a 1 | 2 | 'B' | 'A' | 5 | 'C'
+]] [[
+ enum a {
+ A = 1,
+ B = 2,
+ C = A|B+2,
+ };
+]]
+
+TEST [[
+ ---@param a boolean
+ ---@param b boolean
+ ---@param c integer
+ ---@param d integer
+ function m.test(a, b, c, d) end
+]] [[
+ void test(bool a, _Bool b, size_t c, ssize_t d);
+]]
+
+TEST [[
+ ---@param a integer
+ ---@param b integer
+ ---@param c integer
+ ---@param d integer
+ function m.test(a, b, c, d) end
+]] [[
+ void test(int8_t a, int16_t b, int32_t c, int64_t d);
+]]
+
+TEST [[
+ ---@param a integer
+ ---@param b integer
+ ---@param c integer
+ ---@param d integer
+ function m.test(a, b, c, d) end
+]] [[
+ void test(uint8_t a, uint16_t b, uint32_t c, uint64_t d);
+]]
+
+TEST [[
+ ---@param a integer
+ ---@param b integer
+ ---@param c integer
+ ---@param d integer
+ function m.test(a, b, c, d) end
+]] [[
+ void test(unsigned char a, unsigned short b, unsigned long c, unsigned int d);
+]]
+
+TEST [[
+ ---@param a integer
+ ---@param b integer
+ ---@param c integer
+ ---@param d integer
+ function m.test(a, b, c, d) end
+]] [[
+ void test(unsigned char a, unsigned short b, unsigned long c, unsigned int d);
+]]
+
+TEST [[
+ ---@param a integer
+ ---@param b integer
+ ---@param c integer
+ ---@param d integer
+ function m.test(a, b, c, d) end
+]] [[
+ void test(signed char a, signed short b, signed long c, signed int d);
+]]
+
+TEST [[
+ ---@param a integer
+ ---@param b integer
+ ---@param c integer
+ ---@param d integer
+ function m.test(a, b, c, d) end
+]] [[
+ void test(char a, short b, long c, int d);
+]]
+
+TEST [[
+ ---@param a number
+ ---@param b number
+ ---@param c integer
+ ---@param d integer
+ function m.test(a, b, c, d) end
+]] [[
+ void test(float a, double b, int8_t c, uint8_t d);
+]]
+
+TEST [[
+ ---@alias ffi.namespace*.H ffi.namespace*.void
+
+ function m.test() end
+]] [[
+ typedef void H;
+
+ H test();
+]]
+
+TEST [[
+ ---@class ffi.namespace*.a
+
+ ---@param a ffi.namespace*.a
+ function m.test(a) end
+]] [[
+ typedef struct {} a;
+
+ void test(a* a);
+]]
+
+TEST [[
+ ---@class ffi.namespace*.struct@a
+ ---@field a integer
+ ---@field b ffi.namespace*.char*
+
+ ---@param a ffi.namespace*.struct@a
+ function m.test(a) end
+]] [[
+ struct a {int a;char* b;};
+
+ void test(struct a* a);
+]]
diff --git a/test/plugins/ffi/cdef.lua b/test/plugins/ffi/cdef.lua
new file mode 100644
index 00000000..cf3992d3
--- /dev/null
+++ b/test/plugins/ffi/cdef.lua
@@ -0,0 +1,61 @@
+
+local files = require 'files'
+local code = require 'plugins.ffi.searchCode'
+local cdefRerence = require 'plugins.ffi.cdefRerence'
+
+rawset(_G, 'TEST', true)
+
+function TEST(wanted)
+ ---@async
+ return function (script)
+ files.setText(TESTURI, script)
+ local codeResults = code(cdefRerence(), TESTURI)
+ assert(codeResults)
+ table.sort(codeResults)
+ assert(table.concat(codeResults, '|') == wanted, table.concat(codeResults, '|') .. ' ~= ' .. wanted)
+ files.remove(TESTURI)
+ end
+end
+
+TEST 'aaa|bbb' [[
+local ffi = require 'ffi'
+local cdef = ffi.cdef
+cdef('aaa')
+cdef = function ()
+end
+cdef('bbb')
+]]
+
+TEST 'aaa' [[
+local ffi = require 'ffi'
+
+ffi.cdef('aaa')
+]]
+
+TEST 'aa.aa' [[
+local ffi = require 'ffi'
+local t1 = ffi
+
+t1.cdef"aa.aa"
+]]
+
+TEST 'aaa' [[
+local ffi = require 'ffi'
+local code = 'aaa'
+ffi.cdef(code)
+]]
+
+TEST 'aaa|bbb' [[
+local ffi = require 'ffi'
+local code = 'aaa'
+code = 'bbb'
+local t1 = ffi
+t1.cdef(code)
+]]
+
+TEST 'aa.aa' [[
+local ffi = require 'ffi'
+local cdef = ffi.cdef
+
+cdef"aa.aa"
+]]
diff --git a/test/plugins/ffi/parser.lua b/test/plugins/ffi/parser.lua
new file mode 100644
index 00000000..6d7f2cea
--- /dev/null
+++ b/test/plugins/ffi/parser.lua
@@ -0,0 +1,176 @@
+local utility = require 'utility'
+local cdriver = require 'plugins.ffi.c-parser.cdriver'
+
+rawset(_G, 'TEST', true)
+local ctypes = require 'plugins.ffi.c-parser.ctypes'
+ctypes.TESTMODE = true
+
+--TODO expand all singlenode
+function TEST(wanted, full)
+ return function (script)
+ local rrr = cdriver.process_context(script .. "$EOF$")
+ assert(rrr)
+ if full then
+ for i, v in ipairs(rrr) do
+ assert(utility.equal(v, wanted[i]), utility.dump(v))
+ end
+ else
+ assert(utility.equal(rrr[1], wanted), utility.dump(rrr[1]))
+ end
+ end
+end
+
+TEST {
+ name = "struct@A",
+ type = {
+ fields = {
+ {
+ isarray = true,
+ name = "a",
+ type = { "int", },
+ },
+ {
+ isarray = true,
+ name = "b",
+ type = { "int", },
+ },
+ },
+ name = "A",
+ type = "struct",
+ },
+}
+ [[
+ struct A {
+ int a[5];
+ int b[];
+ };
+]]
+
+TEST {
+ name = 'union@a',
+ type = {
+ name = 'a',
+ type = 'union',
+ fields = {
+ { name = 'b', type = { 'int' } },
+ { name = 'c', type = { 'int8_t' } }
+ }
+ }
+} [[
+ union a{
+ int b;
+ int8_t c;
+ };
+]]
+
+TEST {
+ name = 'union@a',
+ type = {
+ name = 'a',
+ type = 'union',
+ }
+} [[
+ union a{};
+]]
+
+TEST {
+ name = 'enum@anonymous',
+ type = {
+ type = 'enum',
+ values = {
+ { name = 'a', value = { '1' } },
+ { name = 'b', value = { 'a' } },
+ }
+ }
+} [[
+ enum {
+ a = 1,
+ b = a,
+ };
+]]
+
+TEST {
+ name = 'enum@a',
+ type = {
+ name = 'a',
+ type = 'enum',
+ values = {
+ { name = 'b', value = { op = '|', { '1' }, { '2' } } },
+ }
+ }
+} [[
+ enum a{
+ b = 1|2,
+ };
+]]
+
+TEST {
+ name = 'enum@a',
+ type = {
+ name = 'a',
+ type = 'enum',
+ }
+} [[
+ enum a{};
+]]
+
+TEST {
+ name = 'struct@a',
+ type = {
+ name = 'a',
+ type = 'struct',
+ fields = {
+ { name = 'f', type = { 'int' } },
+ { name = 'b', type = { 'int', '*', '*' } }
+ }
+ }
+} [[
+ struct a {int f,**b;};
+]]
+
+TEST {
+ name = 'struct@a',
+ type = {
+ name = 'a',
+ type = 'struct',
+ fields = {
+ { name = 'f', type = { 'int' } },
+ }
+ }
+} [[
+ struct a {int f;};
+]]
+
+TEST({
+ { name = "struct@anonymous", type = { type = 'struct' } },
+ {
+ name = 'a',
+ type = {
+ name = 'a',
+ type = 'typedef',
+ def = {
+ { type = 'struct', }
+ }
+ }
+ }
+}, true) [[
+ typedef struct {} a;
+]]
+
+TEST {
+ name = 'a',
+ type = {
+ name = 'a',
+ type = 'function',
+ params = {
+ { type = { 'int' }, name = 'b' },
+ },
+ ret = {
+ type = { 'int' }
+ },
+ vararg = false
+ },
+
+} [[
+ int a(int b);
+]]
diff --git a/test/plugins/ffi/test.lua b/test/plugins/ffi/test.lua
new file mode 100644
index 00000000..93be2ff5
--- /dev/null
+++ b/test/plugins/ffi/test.lua
@@ -0,0 +1,43 @@
+local lclient = require 'lclient'
+local ws = require 'workspace'
+local furi = require 'file-uri'
+local files = require 'files'
+local diagnostic = require 'provider.diagnostic'
+
+--TODO how to changed the runtime version?
+local template = require 'config.template'
+
+template['Lua.runtime.version'].default = 'LuaJIT'
+
+TESTURI = furi.encode(TESTROOT .. 'unittest.ffi.lua')
+
+---@async
+local function TestBuilder()
+ local builder = require 'core.command.reloadFFIMeta'
+ files.setText(TESTURI, [[
+ local ffi = require 'ffi'
+ ffi.cdef 'void test();'
+ ]])
+ local uri = ws.getFirstScope().uri
+ builder(uri)
+end
+
+---@async
+lclient():start(function (languageClient)
+ languageClient:registerFakers()
+ local rootUri = TESTURI
+ languageClient:initialize {
+ rootUri = rootUri,
+ }
+
+ diagnostic.pause()
+
+ ws.awaitReady(rootUri)
+
+ require 'plugins.ffi.cdef'
+ require 'plugins.ffi.parser'
+ require 'plugins.ffi.builder'
+ TestBuilder()
+
+ diagnostic.resume()
+end)
diff --git a/test/plugins/node/test.lua b/test/plugins/node/test.lua
new file mode 100644
index 00000000..15e4d16c
--- /dev/null
+++ b/test/plugins/node/test.lua
@@ -0,0 +1,56 @@
+local files = require 'files'
+local scope = require 'workspace.scope'
+local nodeHelper = require 'plugins.nodeHelper'
+local vm = require 'vm'
+local guide = require 'parser.guide'
+
+
+local pattern, msg = nodeHelper.createFieldPattern("*.components")
+assert(pattern, msg)
+
+---@param source parser.object
+function OnCompileFunctionParam(next, func, source)
+ if next(func, source) then
+ return true
+ end
+ --从该参数的使用模式来推导该类型
+ if nodeHelper.matchPattern(source, pattern) then
+ local type = vm.declareGlobal('type', 'TestClass', TESTURI)
+ vm.setNode(source, vm.createNode(type, source))
+ return true
+ end
+end
+
+local myplugin = { OnCompileFunctionParam = OnCompileFunctionParam }
+
+---@diagnostic disable: await-in-sync
+local function TestPlugin(script)
+ local prefix = [[
+ ---@class TestClass
+ ---@field b string
+ ]]
+ ---@param checker fun(state:parser.state)
+ return function (plugin, checker)
+ files.open(TESTURI)
+ files.setText(TESTURI, prefix .. script, true)
+ scope.getScope(TESTURI):set('pluginInterfaces', plugin)
+ local state = files.getState(TESTURI)
+ assert(state)
+ checker(state)
+ files.remove(TESTURI)
+ end
+end
+
+TestPlugin [[
+ local function t(a)
+ a.components:test()
+ end
+]](myplugin, function (state)
+ guide.eachSourceType(state.ast, 'local', function (src)
+ if guide.getKeyName(src) == 'a' then
+ local node = vm.compileNode(src)
+ assert(node)
+ assert(not vm.isUnknown(node))
+ end
+ end)
+end)
diff --git a/test/plugins/test.lua b/test/plugins/test.lua
new file mode 100644
index 00000000..b6533de9
--- /dev/null
+++ b/test/plugins/test.lua
@@ -0,0 +1,3 @@
+require 'plugins.ast.test'
+require 'plugins.ffi.test'
+require 'plugins.node.test'
diff --git a/test/signature/init.lua b/test/signature/init.lua
index 147f93e1..f46ce017 100644
--- a/test/signature/init.lua
+++ b/test/signature/init.lua
@@ -330,3 +330,45 @@ t(<??>)
{
'function (<!x: number!>)',
}
+
+TEST [[
+---@class 😅
+
+---@param a 😅
+---@param b integer
+local function f(a, b)
+end
+
+f(1, 2<??>)
+]]
+{
+'function f(a: 😅, <!b: integer!>)',
+}
+
+TEST [[
+---@class A
+---@field event fun(self: self, ev: "onChat", c: string)
+---@field event fun(self: self, ev: "onTimer", t: integer)
+
+---@type A
+local t
+
+t:event("onChat", <??>)
+]]
+{
+'(method) (ev: "onChat", <!c: string!>)',
+}
+
+TEST [[
+---@class A
+---@field event fun(self: self, ev: "onChat", c: string)
+---@field event fun(self: self, ev: "onTimer", t: integer)
+
+---@type A
+local t
+
+t:event("onTimer", <??>)
+]]
+{
+'(method) (ev: "onTimer", <!t: integer!>)',
+}
diff --git a/test/type_inference/init.lua b/test/type_inference/init.lua
index 12a8e2ab..98a07ca3 100644
--- a/test/type_inference/init.lua
+++ b/test/type_inference/init.lua
@@ -44,6 +44,10 @@ function TEST(wanted)
end
end
+TEST 'nil' [[
+local <?t?> = nil
+]]
+
TEST 'string' [[
local <?var?> = '111'
]]
@@ -4278,3 +4282,57 @@ 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()
+]=]
+--