diff options
Diffstat (limited to 'test')
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() +]=] +-- |