diff --git a/spec/cache_spec.lua b/spec/cache_spec.lua index cf8a9b31..4a1e329f 100644 --- a/spec/cache_spec.lua +++ b/spec/cache_spec.lua @@ -68,7 +68,7 @@ describe("cache", function() end) it("handles error result", function() - assert.same('return {2,4,10,"message"}', cache.serialize({error = "syntax", line = 2, column = 4, offset = 10, msg = "message"})) + assert.same('return {{"011",[3]=2,[4]=4,[24]="message"}}', cache.serialize({{code = "011", line = 2, column = 4, msg = "message"}})) end) end) @@ -159,7 +159,7 @@ return {{"111"},{"122"}} cache.update(tmpname, {"foo", "bar"}, {1, 2}, - {{{code="111"}}, {error = "syntax", line = 2, column = 4, offset = 10, msg = "message"}}) + {{{code="111"}}, {{code = "011", line = 2, column = 4, msg = "message"}}}) end) after_each(function() @@ -173,7 +173,7 @@ return {{"111"},{"122"}} it("loads cached results", function() assert.same({ foo = {{code="111"}}, - bar = {error = "syntax", line = 2, column = 4, offset = 10, msg = "message"} + bar = {{code = "011", line = 2, column = 4, msg = "message"}} }, cache.load(tmpname, {"foo", "bar"}, {1, 2})) end) @@ -183,7 +183,7 @@ return {{"111"},{"122"}} it("does not load outdated results", function() assert.same( - {bar = {error = "syntax", line = 2, column = 4, offset = 10, msg = "message"}}, + {bar = {{code = "011", line = 2, column = 4, msg = "message"}}}, cache.load(tmpname, {"foo", "bar", "baz"}, {2, 2})) end) end) diff --git a/spec/cli_spec.lua b/spec/cli_spec.lua index 134a5bf5..4ae31c08 100644 --- a/spec/cli_spec.lua +++ b/spec/cli_spec.lua @@ -59,7 +59,7 @@ Total: 0 warnings / 0 errors in 1 file it("works for incorrect files", function() assert.equal([[ -Checking spec/samples/bad_code.lua Failure +Checking spec/samples/bad_code.lua 5 warnings spec/samples/bad_code.lua:3:16: unused function helper spec/samples/bad_code.lua:3:23: unused variable length argument @@ -74,13 +74,13 @@ Total: 5 warnings / 0 errors in 1 file it("works for incorrect patterns in options", function() assert.equal([[ -Fatal error: Invalid pattern '^%1foo$' +Critical error: Invalid pattern '^%1foo$' ]], get_output "spec/samples/bad_code.lua --ignore %1foo --no-config") end) it("checks stdin when given -", function() assert.equal([[ -Checking stdin Failure +Checking stdin 5 warnings stdin:3:16: unused function helper stdin:3:23: unused variable length argument @@ -95,7 +95,7 @@ Total: 5 warnings / 0 errors in 1 file it("colors output", function() assert.equal([[ Checking spec/samples/good_code.lua ###OK# -Checking spec/samples/bad_code.lua ###Failure# +Checking spec/samples/bad_code.lua ###5 warnings# spec/samples/bad_code.lua:3:16: unused function ##helper# spec/samples/bad_code.lua:3:23: unused variable length argument @@ -110,7 +110,7 @@ Total: ###5# warnings / ###0# errors in 2 files it("does not color output with --no-color", function() assert.equal([[ Checking spec/samples/good_code.lua OK -Checking spec/samples/bad_code.lua Failure +Checking spec/samples/bad_code.lua 5 warnings spec/samples/bad_code.lua:3:16: unused function 'helper' spec/samples/bad_code.lua:3:23: unused variable length argument @@ -123,7 +123,7 @@ Total: 5 warnings / 0 errors in 2 files end) it("suppresses OK output with -q", function() - assert.equal([[Checking spec/samples/bad_code.lua Failure + assert.equal([[Checking spec/samples/bad_code.lua 5 warnings spec/samples/bad_code.lua:3:16: unused function helper spec/samples/bad_code.lua:3:23: unused variable length argument @@ -131,7 +131,7 @@ Total: 5 warnings / 0 errors in 2 files spec/samples/bad_code.lua:8:10: variable opt was previously defined as an argument on line 7 spec/samples/bad_code.lua:9:11: accessing undefined variable hepler -Checking spec/samples/unused_code.lua Failure +Checking spec/samples/unused_code.lua 9 warnings spec/samples/unused_code.lua:3:18: unused argument baz spec/samples/unused_code.lua:4:8: unused loop variable i @@ -151,8 +151,8 @@ Total: 0 warnings / 0 errors in 1 file end) it("suppresses warnings output with -qq", function() - assert.equal([[Checking spec/samples/bad_code.lua Failure -Checking spec/samples/unused_code.lua Failure + assert.equal([[Checking spec/samples/bad_code.lua 5 warnings +Checking spec/samples/unused_code.lua 9 warnings Total: 14 warnings / 0 errors in 3 files ]], get_output "-qq spec/samples/*d_code.lua --no-config") @@ -165,7 +165,7 @@ Total: 14 warnings / 0 errors in 3 files it("allows to ignore some types of warnings", function() assert.equal([[ -Checking spec/samples/bad_code.lua Failure +Checking spec/samples/bad_code.lua 3 warnings spec/samples/bad_code.lua:7:10: setting non-standard global variable embrace spec/samples/bad_code.lua:8:10: variable opt was previously defined as an argument on line 7 @@ -174,7 +174,7 @@ Checking spec/samples/bad_code.lua Failure Total: 3 warnings / 0 errors in 1 file ]], get_output "-u spec/samples/bad_code.lua --no-config") assert.equal([[ -Checking spec/samples/bad_code.lua Failure +Checking spec/samples/bad_code.lua 3 warnings spec/samples/bad_code.lua:3:16: unused function helper spec/samples/bad_code.lua:3:23: unused variable length argument @@ -183,7 +183,7 @@ Checking spec/samples/bad_code.lua Failure Total: 3 warnings / 0 errors in 1 file ]], get_output "-g spec/samples/bad_code.lua --no-config") assert.equal([[ -Checking spec/samples/bad_code.lua Failure +Checking spec/samples/bad_code.lua 4 warnings spec/samples/bad_code.lua:3:16: unused function helper spec/samples/bad_code.lua:3:23: unused variable length argument @@ -196,7 +196,7 @@ Total: 4 warnings / 0 errors in 1 file it("allows to define additional globals", function() assert.equal([[ -Checking spec/samples/bad_code.lua Failure +Checking spec/samples/bad_code.lua 4 warnings spec/samples/bad_code.lua:3:16: unused function helper spec/samples/bad_code.lua:3:23: unused variable length argument @@ -209,7 +209,7 @@ Total: 4 warnings / 0 errors in 1 file it("allows to set standard globals", function() assert.equal([[ -Checking spec/samples/bad_code.lua Failure +Checking spec/samples/bad_code.lua 6 warnings spec/samples/bad_code.lua:1:1: accessing undefined variable package spec/samples/bad_code.lua:3:16: unused function helper @@ -221,7 +221,7 @@ Checking spec/samples/bad_code.lua Failure Total: 6 warnings / 0 errors in 1 file ]], get_output "--std none spec/samples/bad_code.lua --no-config") assert.equal([[ -Checking spec/samples/bad_code.lua Failure +Checking spec/samples/bad_code.lua 5 warnings spec/samples/bad_code.lua:3:16: unused function helper spec/samples/bad_code.lua:3:23: unused variable length argument @@ -235,7 +235,7 @@ Total: 5 warnings / 0 errors in 1 file it("allows to ignore some variables", function() assert.equal([[ -Checking spec/samples/bad_code.lua Failure +Checking spec/samples/bad_code.lua 3 warnings spec/samples/bad_code.lua:3:16: unused function helper spec/samples/bad_code.lua:3:23: unused variable length argument @@ -247,7 +247,7 @@ Total: 3 warnings / 0 errors in 1 file it("allows to only watch some variables", function() assert.equal([[ -Checking spec/samples/bad_code.lua Failure +Checking spec/samples/bad_code.lua 1 warning spec/samples/bad_code.lua:3:16: unused function helper @@ -257,7 +257,7 @@ Total: 1 warning / 0 errors in 1 file it("recognizes different types of variables", function() assert.equal([[ -Checking spec/samples/unused_code.lua Failure +Checking spec/samples/unused_code.lua 9 warnings spec/samples/unused_code.lua:3:18: unused argument baz spec/samples/unused_code.lua:4:8: unused loop variable i @@ -275,7 +275,7 @@ Total: 9 warnings / 0 errors in 1 file it("allows to ignore unused arguments", function() assert.equal([[ -Checking spec/samples/unused_code.lua Failure +Checking spec/samples/unused_code.lua 4 warnings spec/samples/unused_code.lua:5:13: unused variable q spec/samples/unused_code.lua:13:7: value assigned to variable x is unused @@ -288,7 +288,7 @@ Total: 4 warnings / 0 errors in 1 file it("allows to ignore unused secondary values and variables", function() assert.equal([[ -Checking spec/samples/unused_secondaries.lua Failure +Checking spec/samples/unused_secondaries.lua 4 warnings spec/samples/unused_secondaries.lua:3:7: unused variable a spec/samples/unused_secondaries.lua:6:7: unused variable x @@ -299,7 +299,7 @@ Total: 4 warnings / 0 errors in 1 file ]], get_output "spec/samples/unused_secondaries.lua --no-config") assert.equal([[ -Checking spec/samples/unused_secondaries.lua Failure +Checking spec/samples/unused_secondaries.lua 1 warning spec/samples/unused_secondaries.lua:6:7: unused variable x @@ -309,7 +309,7 @@ Total: 1 warning / 0 errors in 1 file it("allows to ignore warnings related to implicit self", function() assert.equal([[ -Checking spec/samples/redefined.lua Failure +Checking spec/samples/redefined.lua 5 warnings spec/samples/redefined.lua:4:10: shadowing upvalue a on line 1 spec/samples/redefined.lua:4:13: variable self is never set @@ -323,20 +323,20 @@ Total: 5 warnings / 0 errors in 1 file it("handles errors gracefully", function() assert.equal([[ -Checking spec/samples/python_code.lua Syntax error +Checking spec/samples/python_code.lua 1 error spec/samples/python_code.lua:1:6: expected '=' near '__future__' Checking spec/samples/absent_code.lua I/O error -Total: 0 warnings / 2 errors in 2 files +Total: 0 warnings / 1 error in 1 file, couldn't check 1 file ]], get_output "spec/samples/python_code.lua spec/samples/absent_code.lua --no-config") assert.equal(2, get_exitcode "spec/samples/python_code.lua spec/samples/absent_code.lua --no-config") end) it("expands rockspecs", function() assert.equal([[ -Checking spec/samples/bad_code.lua Failure +Checking spec/samples/bad_code.lua 5 warnings spec/samples/bad_code.lua:3:16: unused function helper spec/samples/bad_code.lua:3:23: unused variable length argument @@ -354,13 +354,13 @@ Total: 5 warnings / 0 errors in 2 files assert.equal([[ Checking spec/samples/bad.rockspec Syntax error -Total: 0 warnings / 1 error in 1 file +Total: 0 warnings / 0 errors in 0 files, couldn't check 1 file ]], get_output "spec/samples/bad.rockspec --no-config") end) it("allows ignoring defined globals", function() assert.equal([[ -Checking spec/samples/defined.lua Failure +Checking spec/samples/defined.lua 1 warning spec/samples/defined.lua:4:4: accessing undefined variable baz @@ -371,7 +371,7 @@ Total: 1 warning / 0 errors in 2 files assert.equal([[ Checking spec/samples/defined2.lua OK -Checking spec/samples/defined.lua Failure +Checking spec/samples/defined.lua 1 warning spec/samples/defined.lua:4:4: accessing undefined variable baz @@ -381,7 +381,7 @@ Total: 1 warning / 0 errors in 2 files it("allows restricting scope of defined globals to the file with their definition", function() assert.equal([[ -Checking spec/samples/defined2.lua Failure +Checking spec/samples/defined2.lua 1 warning spec/samples/defined2.lua:1:1: accessing undefined variable foo @@ -393,7 +393,7 @@ Total: 1 warning / 0 errors in 2 files it("allows ignoring globals defined in top level scope", function() assert.equal([[ -Checking spec/samples/defined4.lua Failure +Checking spec/samples/defined4.lua 2 warnings spec/samples/defined4.lua:1:10: unused global variable foo spec/samples/defined4.lua:3:4: setting non-standard global variable bar @@ -404,7 +404,7 @@ Total: 2 warnings / 0 errors in 1 file it("detects unused defined globals", function() assert.equal([[ -Checking spec/samples/defined3.lua Failure +Checking spec/samples/defined3.lua 3 warnings spec/samples/defined3.lua:1:1: unused global variable foo spec/samples/defined3.lua:2:1: unused global variable foo @@ -414,7 +414,7 @@ Total: 3 warnings / 0 errors in 1 file ]], get_output "spec/samples/defined3.lua -d --no-config") assert.equal([[ -Checking spec/samples/defined3.lua Failure +Checking spec/samples/defined3.lua 1 warning spec/samples/defined3.lua:3:1: unused global variable bar @@ -432,7 +432,7 @@ Total: 0 warnings / 0 errors in 1 file ]], get_output "spec/samples/defined3.lua -gd --no-config") assert.equal([[ -Checking spec/samples/defined3.lua Failure +Checking spec/samples/defined3.lua 1 warning spec/samples/defined3.lua:3:1: unused global variable bar @@ -459,7 +459,7 @@ Total: 0 warnings / 0 errors in 2 files it("detects flow issues", function() assert.equal([[ -Checking spec/samples/bad_flow.lua Failure +Checking spec/samples/bad_flow.lua 6 warnings spec/samples/bad_flow.lua:1:28: empty if branch spec/samples/bad_flow.lua:6:4: empty do..end block @@ -474,7 +474,7 @@ Total: 6 warnings / 0 errors in 1 file it("detects redefinitions", function() assert.equal([[ -Checking spec/samples/redefined.lua Failure +Checking spec/samples/redefined.lua 6 warnings spec/samples/redefined.lua:3:11: unused argument self spec/samples/redefined.lua:4:10: shadowing upvalue a on line 1 @@ -489,7 +489,7 @@ Total: 6 warnings / 0 errors in 1 file it("detects issues related to read-only globals", function() assert.equal([[ -Checking spec/samples/read_globals.lua Failure +Checking spec/samples/read_globals.lua 5 warnings spec/samples/read_globals.lua:1:1: setting read-only global variable string spec/samples/read_globals.lua:2:1: mutating read-only global variable table @@ -503,7 +503,7 @@ Total: 5 warnings / 0 errors in 1 file it("allows showing warning codes", function() assert.equal([[ -Checking spec/samples/read_globals.lua Failure +Checking spec/samples/read_globals.lua 5 warnings spec/samples/read_globals.lua:1:1: (W121) setting read-only global variable string spec/samples/read_globals.lua:2:1: (W122) mutating read-only global variable table @@ -517,7 +517,7 @@ Total: 5 warnings / 0 errors in 1 file it("applies inline options", function() assert.equal([[ -Checking spec/samples/inline_options.lua Failure +Checking spec/samples/inline_options.lua 8 warnings / 2 errors spec/samples/inline_options.lua:6:16: unused function f spec/samples/inline_options.lua:12:4: accessing undefined variable qu @@ -525,16 +525,16 @@ Checking spec/samples/inline_options.lua Failure spec/samples/inline_options.lua:24:10: unused variable g spec/samples/inline_options.lua:26:7: unused variable f spec/samples/inline_options.lua:26:10: unused variable g - spec/samples/inline_options.lua:28:1: unpaired inline option - spec/samples/inline_options.lua:30:4: unpaired inline option + spec/samples/inline_options.lua:28:1: unpaired push directive + spec/samples/inline_options.lua:30:4: unpaired pop directive spec/samples/inline_options.lua:36:1: empty do..end block spec/samples/inline_options.lua:37:10: empty if branch -Total: 10 warnings / 0 errors in 1 file +Total: 8 warnings / 2 errors in 1 file ]], get_output "spec/samples/inline_options.lua --std=none --no-config") assert.equal([[ -Checking spec/samples/inline_options.lua Failure +Checking spec/samples/inline_options.lua 7 warnings / 2 errors spec/samples/inline_options.lua:6:16: unused function f spec/samples/inline_options.lua:12:4: accessing undefined variable qu @@ -542,15 +542,15 @@ Checking spec/samples/inline_options.lua Failure spec/samples/inline_options.lua:24:10: unused variable g spec/samples/inline_options.lua:26:7: unused variable f spec/samples/inline_options.lua:26:10: unused variable g - spec/samples/inline_options.lua:28:1: unpaired inline option - spec/samples/inline_options.lua:30:4: unpaired inline option + spec/samples/inline_options.lua:28:1: unpaired push directive + spec/samples/inline_options.lua:30:4: unpaired pop directive spec/samples/inline_options.lua:36:1: empty do..end block -Total: 9 warnings / 0 errors in 1 file +Total: 7 warnings / 2 errors in 1 file ]], get_output "spec/samples/inline_options.lua --std=none --ignore=542 --no-config") assert.equal([[ -Checking spec/samples/global_inline_options.lua Failure +Checking spec/samples/global_inline_options.lua 3 warnings spec/samples/global_inline_options.lua:6:10: unused global variable f spec/samples/global_inline_options.lua:7:4: setting non-standard global variable baz @@ -560,7 +560,7 @@ Total: 3 warnings / 0 errors in 1 file ]], get_output "spec/samples/global_inline_options.lua --std=lua52 --no-config") assert.equal([[ -Checking spec/samples/read_globals_inline_options.lua Failure +Checking spec/samples/read_globals_inline_options.lua 5 warnings spec/samples/read_globals_inline_options.lua:2:10: accessing undefined variable baz spec/samples/read_globals_inline_options.lua:3:1: setting read-only global variable foo @@ -572,7 +572,7 @@ Total: 5 warnings / 0 errors in 1 file ]], get_output "spec/samples/read_globals_inline_options.lua --std=lua52 --no-config") assert.equal([[ -Checking spec/samples/read_globals_inline_options.lua Failure +Checking spec/samples/read_globals_inline_options.lua 1 warning spec/samples/read_globals_inline_options.lua:3:16: mutating read-only global variable baz @@ -582,7 +582,7 @@ Total: 1 warning / 0 errors in 1 file it("inline options can use extended stds", function() assert.equal([[ -Checking spec/samples/custom_std_inline_options.lua Failure +Checking spec/samples/custom_std_inline_options.lua 2 warnings spec/samples/custom_std_inline_options.lua:3:1: accessing undefined variable tostring spec/samples/custom_std_inline_options.lua:6:25: accessing undefined variable it @@ -593,7 +593,7 @@ Total: 2 warnings / 0 errors in 1 file it("inline options can be disabled", function() assert.equal([[ -Checking spec/samples/inline_options.lua Failure +Checking spec/samples/inline_options.lua 26 warnings spec/samples/inline_options.lua:3:1: accessing undefined variable foo spec/samples/inline_options.lua:4:1: accessing undefined variable bar @@ -640,7 +640,7 @@ Total: 26 warnings / 0 errors in 1 file it("caches results", function() assert.equal([[ Checking spec/samples/good_code.lua OK -Checking spec/samples/bad_code.lua Failure +Checking spec/samples/bad_code.lua 5 warnings spec/samples/bad_code.lua:3:16: unused function helper spec/samples/bad_code.lua:3:23: unused variable length argument @@ -648,7 +648,7 @@ Checking spec/samples/bad_code.lua Failure spec/samples/bad_code.lua:8:10: variable opt was previously defined as an argument on line 7 spec/samples/bad_code.lua:9:11: accessing undefined variable hepler -Checking spec/samples/python_code.lua Syntax error +Checking spec/samples/python_code.lua 1 error spec/samples/python_code.lua:1:6: expected '=' near '__future__' @@ -665,7 +665,7 @@ spec/samples/bad_code.lua local A="113";return {{A,"package",1,1},{"211","helper",3,16,%[9%]=true},{"212","...",3,23},{"111","embrace",7,10,%[11%]=true},{"412","opt",8,10,7,18},{A,"hepler",9,11}} spec/samples/python_code.lua (%d+) -return {1,6,6,"expected '=' near '__future__'"} +return {{"011",%[3%]=1,%[4%]=6,%[24%]="expected '=' near '__future__'"}} ]]) assert.string(good_mtime) assert.string(bad_mtime) @@ -673,7 +673,7 @@ return {1,6,6,"expected '=' near '__future__'"} assert.equal([[ Checking spec/samples/good_code.lua OK -Checking spec/samples/bad_code.lua Failure +Checking spec/samples/bad_code.lua 5 warnings spec/samples/bad_code.lua:3:16: unused function helper spec/samples/bad_code.lua:3:23: unused variable length argument @@ -681,7 +681,7 @@ Checking spec/samples/bad_code.lua Failure spec/samples/bad_code.lua:8:10: variable opt was previously defined as an argument on line 7 spec/samples/bad_code.lua:9:11: accessing undefined variable hepler -Checking spec/samples/python_code.lua Syntax error +Checking spec/samples/python_code.lua 1 error spec/samples/python_code.lua:1:6: expected '=' near '__future__' @@ -695,18 +695,18 @@ Total: 5 warnings / 1 error in 3 files return {{"111", "global", 1, 1}, {"321", "uninit", 6, 8}} spec/samples/good_code.lua %s -return {5, 7, 7, "this code is actually bad"} +return {{"011",[3]=5,[4]=7,[24]="this code is actually bad"}} spec/samples/bad_code.lua %s return {}]]):format(python_mtime, good_mtime, tostring(tonumber(bad_mtime) - 1))) fh:close() assert.equal([[ -Checking spec/samples/good_code.lua Syntax error +Checking spec/samples/good_code.lua 1 error spec/samples/good_code.lua:5:7: this code is actually bad -Checking spec/samples/bad_code.lua Failure +Checking spec/samples/bad_code.lua 5 warnings spec/samples/bad_code.lua:3:16: unused function helper spec/samples/bad_code.lua:3:23: unused variable length argument @@ -714,12 +714,12 @@ Checking spec/samples/bad_code.lua Failure spec/samples/bad_code.lua:8:10: variable opt was previously defined as an argument on line 7 spec/samples/bad_code.lua:9:11: accessing undefined variable hepler -Checking spec/samples/python_code.lua Failure +Checking spec/samples/python_code.lua 2 warnings spec/samples/python_code.lua:1:1: setting non-standard global variable global spec/samples/python_code.lua:6:8: accessing uninitialized variable uninit -Checking spec/samples/unused_code.lua Failure +Checking spec/samples/unused_code.lua 9 warnings spec/samples/unused_code.lua:3:18: unused argument baz spec/samples/unused_code.lua:4:8: unused loop variable i @@ -742,7 +742,7 @@ Total: 16 warnings / 1 error in 4 files it("uses multithreading", function() assert.equal([[ Checking spec/samples/good_code.lua OK -Checking spec/samples/bad_code.lua Failure +Checking spec/samples/bad_code.lua 5 warnings spec/samples/bad_code.lua:3:16: unused function helper spec/samples/bad_code.lua:3:23: unused variable length argument @@ -750,7 +750,7 @@ Checking spec/samples/bad_code.lua Failure spec/samples/bad_code.lua:8:10: variable opt was previously defined as an argument on line 7 spec/samples/bad_code.lua:9:11: accessing undefined variable hepler -Checking spec/samples/python_code.lua Syntax error +Checking spec/samples/python_code.lua 1 error spec/samples/python_code.lua:1:6: expected '=' near '__future__' @@ -797,7 +797,7 @@ not ok 3 spec/samples/bad_code.lua:3:23: (W212) unused variable length argument not ok 4 spec/samples/bad_code.lua:7:10: (W111) setting non-standard global variable 'embrace' not ok 5 spec/samples/bad_code.lua:8:10: (W412) variable 'opt' was previously defined as an argument on line 7 not ok 6 spec/samples/bad_code.lua:9:11: (W113) accessing undefined variable 'hepler' -not ok 7 spec/samples/python_code.lua:1:6: expected '=' near '__future__' +not ok 7 spec/samples/python_code.lua:1:6: (E011) expected '=' near '__future__' ]], get_output "spec/samples/good_code.lua spec/samples/bad_code.lua spec/samples/python_code.lua --std=lua52 --formatter TAP --codes --no-config") end) @@ -821,8 +821,8 @@ not ok 7 spec/samples/python_code.lua:1:6: expected '=' near '__future__' - - + + ]], get_output "spec/samples/good_code.lua spec/samples/bad_code.lua spec/samples/python_code.lua --std=lua52 --formatter JUnit --no-config") @@ -840,13 +840,17 @@ spec/samples/bad_code.lua:9:11: accessing undefined variable 'hepler' spec/samples/python_code.lua:1:6: expected '=' near '__future__' ]], get_output "spec/samples/good_code.lua spec/samples/bad_code.lua spec/samples/python_code.lua --std=lua52 --formatter plain --no-config") + assert.equal([[ +spec/samples/404.lua: I/O error +]], get_output "spec/samples/404.lua --formatter plain --no-config") + assert.equal([[ spec/samples/bad_code.lua:3:16: (W211) unused function 'helper' spec/samples/bad_code.lua:3:23: (W212) unused variable length argument spec/samples/bad_code.lua:7:10: (W111) setting non-standard global variable 'embrace' spec/samples/bad_code.lua:8:10: (W412) variable 'opt' was previously defined as an argument on line 7 spec/samples/bad_code.lua:9:11: (W113) accessing undefined variable 'hepler' -spec/samples/python_code.lua:1:6: expected '=' near '__future__' +spec/samples/python_code.lua:1:6: (E011) expected '=' near '__future__' ]], get_output "spec/samples/good_code.lua spec/samples/bad_code.lua spec/samples/python_code.lua --std=lua52 --formatter plain --codes --no-config") end) @@ -856,19 +860,18 @@ spec/samples/python_code.lua:1:6: expected '=' near '__future__' end) it("expands folders", function() - local output = get_output "spec/samples -qqq --no-config" - assert.truthy(output:match("^Total: %d+ warnings / 1 error in 20 files\n$")) + assert.matches("^Total: %d+ warnings / %d+ errors in 20 files\n$", get_output "spec/samples -qqq --no-config") end) describe("config", function() describe("loading", function() it("uses .luacheckrc in current directory if possible", function() assert.equal([[ -Checking nested/ab.lua Failure +Checking nested/ab.lua 1 warning nested/ab.lua:1:10: accessing undefined variable b -Checking nested/nested/abc.lua Failure +Checking nested/nested/abc.lua 2 warnings nested/nested/abc.lua:1:7: accessing undefined variable a nested/nested/abc.lua:1:13: accessing undefined variable c @@ -879,12 +882,12 @@ Total: 3 warnings / 0 errors in 2 files it("does not use .luacheckrc in current directory with --no-config", function() assert.equal([[ -Checking nested/ab.lua Failure +Checking nested/ab.lua 2 warnings nested/ab.lua:1:7: accessing undefined variable a nested/ab.lua:1:10: accessing undefined variable b -Checking nested/nested/abc.lua Failure +Checking nested/nested/abc.lua 3 warnings nested/nested/abc.lua:1:7: accessing undefined variable a nested/nested/abc.lua:1:10: accessing undefined variable b @@ -896,11 +899,11 @@ Total: 5 warnings / 0 errors in 2 files it("uses .luacheckrc in upper directory", function() assert.equal([[ -Checking ab.lua Failure +Checking ab.lua 1 warning ab.lua:1:10: accessing undefined variable b -Checking nested/abc.lua Failure +Checking nested/abc.lua 2 warnings nested/abc.lua:1:7: accessing undefined variable a nested/abc.lua:1:13: accessing undefined variable c @@ -927,7 +930,7 @@ Total: 0 warnings / 0 errors in 1 file it("uses per-file overrides", function() assert.equal([[ -Checking spec/samples/bad_code.lua Failure +Checking spec/samples/bad_code.lua 4 warnings spec/samples/bad_code.lua:3:16: unused function helper spec/samples/bad_code.lua:3:23: unused variable length argument @@ -950,11 +953,11 @@ Total: 0 warnings / 0 errors in 1 file it("uses all overrides prefixing file name", function() assert.equal([[ -Checking spec/samples/unused_secondaries.lua Failure +Checking spec/samples/unused_secondaries.lua 1 warning spec/samples/unused_secondaries.lua:12:1: value assigned to variable o is unused -Checking spec/samples/unused_code.lua Failure +Checking spec/samples/unused_code.lua 7 warnings spec/samples/unused_code.lua:3:18: unused argument baz spec/samples/unused_code.lua:4:8: unused loop variable i @@ -970,14 +973,14 @@ Total: 8 warnings / 0 errors in 2 files it("allows reenabling warnings ignored in config using --enable", function() assert.equal([[ -Checking spec/samples/bad_code.lua Failure +Checking spec/samples/bad_code.lua 4 warnings spec/samples/bad_code.lua:3:16: unused function helper spec/samples/bad_code.lua:3:23: unused variable length argument spec/samples/bad_code.lua:7:10: setting non-standard global variable embrace spec/samples/bad_code.lua:9:11: accessing undefined variable hepler -Checking spec/samples/unused_code.lua Failure +Checking spec/samples/unused_code.lua 1 warning spec/samples/unused_code.lua:5:13: unused variable q @@ -997,62 +1000,62 @@ Codes: true it("uses exclude_files option", function() assert.equal([[ -Checking spec/samples/argparse.lua Failure -Checking spec/samples/compat.lua Failure -Checking spec/samples/custom_std_inline_options.lua Failure -Checking spec/samples/global_inline_options.lua Failure -Checking spec/samples/globals.lua Failure -Checking spec/samples/inline_options.lua Failure -Checking spec/samples/python_code.lua Syntax error -Checking spec/samples/read_globals.lua Failure -Checking spec/samples/read_globals_inline_options.lua Failure -Checking spec/samples/redefined.lua Failure -Checking spec/samples/unused_code.lua Failure -Checking spec/samples/unused_secondaries.lua Failure - -Total: 56 warnings / 1 error in 14 files +Checking spec/samples/argparse.lua 6 warnings +Checking spec/samples/compat.lua 4 warnings +Checking spec/samples/custom_std_inline_options.lua 3 warnings / 1 error +Checking spec/samples/global_inline_options.lua 3 warnings +Checking spec/samples/globals.lua 2 warnings +Checking spec/samples/inline_options.lua 7 warnings / 2 errors +Checking spec/samples/python_code.lua 1 error +Checking spec/samples/read_globals.lua 5 warnings +Checking spec/samples/read_globals_inline_options.lua 3 warnings +Checking spec/samples/redefined.lua 7 warnings +Checking spec/samples/unused_code.lua 9 warnings +Checking spec/samples/unused_secondaries.lua 4 warnings + +Total: 53 warnings / 4 errors in 14 files ]], get_output "spec/samples --config=spec/configs/exclude_files_config.luacheckrc -qq") end) it("loads exclude_files option correctly from upper directory", function() assert.equal([[ -Checking argparse.lua Failure -Checking compat.lua Failure -Checking custom_std_inline_options.lua Failure -Checking global_inline_options.lua Failure -Checking globals.lua Failure -Checking inline_options.lua Failure -Checking python_code.lua Syntax error -Checking read_globals.lua Failure -Checking read_globals_inline_options.lua Failure -Checking redefined.lua Failure -Checking unused_code.lua Failure -Checking unused_secondaries.lua Failure - -Total: 56 warnings / 1 error in 14 files +Checking argparse.lua 6 warnings +Checking compat.lua 4 warnings +Checking custom_std_inline_options.lua 3 warnings / 1 error +Checking global_inline_options.lua 3 warnings +Checking globals.lua 2 warnings +Checking inline_options.lua 7 warnings / 2 errors +Checking python_code.lua 1 error +Checking read_globals.lua 5 warnings +Checking read_globals_inline_options.lua 3 warnings +Checking redefined.lua 7 warnings +Checking unused_code.lua 9 warnings +Checking unused_secondaries.lua 4 warnings + +Total: 53 warnings / 4 errors in 14 files ]], get_output(". --config=spec/configs/exclude_files_config.luacheckrc -qq", "spec/samples/")) end) it("combines excluded files from config and cli", function() assert.equal([[ -Checking argparse.lua Failure -Checking compat.lua Failure -Checking custom_std_inline_options.lua Failure -Checking global_inline_options.lua Failure -Checking globals.lua Failure -Checking inline_options.lua Failure -Checking python_code.lua Syntax error -Checking redefined.lua Failure -Checking unused_code.lua Failure -Checking unused_secondaries.lua Failure - -Total: 48 warnings / 1 error in 12 files +Checking argparse.lua 6 warnings +Checking compat.lua 4 warnings +Checking custom_std_inline_options.lua 3 warnings / 1 error +Checking global_inline_options.lua 3 warnings +Checking globals.lua 2 warnings +Checking inline_options.lua 7 warnings / 2 errors +Checking python_code.lua 1 error +Checking redefined.lua 7 warnings +Checking unused_code.lua 9 warnings +Checking unused_secondaries.lua 4 warnings + +Total: 45 warnings / 4 errors in 12 files ]], get_output(". --config=spec/configs/exclude_files_config.luacheckrc -qq --exclude-files './read*'", "spec/samples/")) end) it("allows defining custom stds", function() assert.equal([[ -Checking spec/samples/globals.lua Failure +Checking spec/samples/globals.lua 2 warnings spec/samples/globals.lua:1:15: accessing undefined variable rawlen spec/samples/globals.lua:1:22: accessing undefined variable tostring @@ -1061,7 +1064,7 @@ Total: 2 warnings / 0 errors in 1 file ]], get_output "spec/samples/globals.lua --config=spec/configs/custom_stds_config.luacheckrc") assert.equal([[ -Checking spec/samples/globals.lua Failure +Checking spec/samples/globals.lua 2 warnings spec/samples/globals.lua:1:1: accessing undefined variable print spec/samples/globals.lua:1:15: accessing undefined variable rawlen @@ -1070,7 +1073,7 @@ Total: 2 warnings / 0 errors in 1 file ]], get_output "spec/samples/globals.lua --config=spec/configs/custom_stds_config.luacheckrc --std=other_std") assert.equal([[ -Checking spec/samples/globals.lua Failure +Checking spec/samples/globals.lua 1 warning spec/samples/globals.lua:1:15: accessing undefined variable rawlen @@ -1078,7 +1081,7 @@ Total: 1 warning / 0 errors in 1 file ]], get_output "spec/samples/globals.lua --config=spec/configs/custom_stds_config.luacheckrc --std=+other_std") assert.equal([[ -Checking spec/samples/globals.lua Failure +Checking spec/samples/globals.lua 1 warning spec/samples/globals.lua:1:7: accessing undefined variable setfenv @@ -1088,7 +1091,7 @@ Total: 1 warning / 0 errors in 1 file it("allows importing options with require", function() assert.equal([[ -Checking spec/samples/globals.lua Failure +Checking spec/samples/globals.lua 1 warning spec/samples/globals.lua:1:7: (W113) accessing undefined variable setfenv @@ -1098,16 +1101,16 @@ Total: 1 warning / 0 errors in 1 file end) describe("error handling", function() - it("raises fatal error on config with syntax errors", function() + it("raises critical error on config with syntax errors", function() assert.equal([[ -Fatal error: Couldn't load configuration from spec/configs/bad_config.luacheckrc: syntax error +Critical error: Couldn't load configuration from spec/configs/bad_config.luacheckrc: syntax error ]], get_output "spec/samples/empty.lua --config=spec/configs/bad_config.luacheckrc") assert.equal(3, get_exitcode "spec/samples/empty.lua --config=spec/configs/bad_config.luacheckrc") end) - it("raises fatal error on non-existent config", function() + it("raises critical error on non-existent config", function() assert.equal([[ -Fatal error: Couldn't find configuration file spec/configs/config_404.luacheckrc +Critical error: Couldn't find configuration file spec/configs/config_404.luacheckrc ]], get_output "spec/samples/empty.lua --config=spec/configs/config_404.luacheckrc") assert.equal(3, get_exitcode "spec/samples/empty.lua --config=spec/configs/config_404.luacheckrc") end) diff --git a/spec/format_spec.lua b/spec/format_spec.lua index 68d81d74..8da7c3ba 100644 --- a/spec/format_spec.lua +++ b/spec/format_spec.lua @@ -6,22 +6,23 @@ end describe("format", function() it("returns formatted report", function() - assert.equal([[Checking stdin Failure + assert.equal([[Checking stdin 1 warning stdin:2:7: unused global variable foo -Checking foo.lua Failure +Checking foo.lua 1 warning foo.lua:2:7: unused global variable foo Checking bar.lua OK -Checking baz.lua Syntax error +Checking baz.lua 1 error baz.lua:4:3: something went wrong Total: 2 warnings / 1 error in 4 files]], remove_color(format({ warnings = 2, errors = 1, + fatals = 0, { { code = "131", @@ -40,29 +41,32 @@ Total: 2 warnings / 1 error in 4 files]], remove_color(format({ }, {}, { - error = "syntax", - line = 4, - column = 3, - offset = 20, - msg = "something went wrong" + { + code = "011", + line = 4, + column = 3, + msg = "something went wrong" + } } }, {"stdin", "foo.lua", "bar.lua", "baz.lua"}, {}))) end) it("does not output OK messages with options.quiet >= 1", function() - assert.equal([[Checking stdin Failure + assert.equal([[Checking stdin 1 warning stdin:2:7: unused global variable foo -Checking foo.lua Failure +Checking foo.lua 1 warning / 1 error foo.lua:2:7: unused global variable foo + foo.lua:3:10: invalid inline option Checking baz.lua Syntax error -Total: 2 warnings / 1 error in 4 files]], remove_color(format({ +Total: 2 warnings / 1 error in 3 files, couldn't check 1 file]], remove_color(format({ warnings = 2, errors = 1, + fatals = 1, { { code = "131", @@ -77,23 +81,29 @@ Total: 2 warnings / 1 error in 4 files]], remove_color(format({ name = "foo", line = 2, column = 7 + }, + { + code = "021", + line = 3, + column = 10 } }, {}, { - error = "syntax" + fatal = "syntax" } }, {"stdin", "foo.lua", "bar.lua", "baz.lua"}, {quiet = 1}))) end) it("does not output warnings with options.quiet >= 2", function() - assert.equal([[Checking stdin Failure -Checking foo.lua Failure + assert.equal([[Checking stdin 1 warning +Checking foo.lua 1 warning Checking baz.lua Syntax error -Total: 2 warnings / 1 error in 4 files]], remove_color(format({ +Total: 2 warnings / 0 errors in 3 files, couldn't check 1 file]], remove_color(format({ warnings = 2, - errors = 1, + errors = 0, + fatals = 1, { { code = "131", @@ -112,15 +122,16 @@ Total: 2 warnings / 1 error in 4 files]], remove_color(format({ }, {}, { - error = "syntax" + fatal = "syntax" } }, {"stdin", "foo.lua", "bar.lua", "baz.lua"}, {quiet = 2}))) end) it("does not output file info with options.quiet == 3", function() - assert.equal("Total: 2 warnings / 1 error in 4 files", remove_color(format({ + assert.equal("Total: 2 warnings / 0 errors in 3 files, couldn't check 1 file", remove_color(format({ warnings = 2, - errors = 1, + errors = 0, + fatals = 1, { { code = "131", @@ -139,26 +150,27 @@ Total: 2 warnings / 1 error in 4 files]], remove_color(format({ }, {}, { - error = "syntax" + fatal = "syntax" } }, {"stdin", "foo.lua", "bar.lua", "baz.lua"}, {quiet = 3}))) end) it("does not color output if options.color == false", function() - assert.equal([[Checking stdin Failure + assert.equal([[Checking stdin 1 warning stdin:2:7: unused global variable 'foo' -Checking foo.lua Failure +Checking foo.lua 1 warning foo.lua:2:7: unused global variable 'foo' Checking bar.lua OK Checking baz.lua Syntax error -Total: 2 warnings / 1 error in 4 files]], format({ +Total: 2 warnings / 0 errors in 3 files, couldn't check 1 file]], format({ warnings = 2, - errors = 1, + errors = 0, + fatals = 1, { { code = "131", @@ -177,7 +189,7 @@ Total: 2 warnings / 1 error in 4 files]], format({ }, {}, { - error = "syntax" + fatal = "syntax" } }, {"stdin", "foo.lua", "bar.lua", "baz.lua"}, {color = false})) end) diff --git a/spec/luacheck_spec.lua b/spec/luacheck_spec.lua index 23792899..c415e384 100644 --- a/spec/luacheck_spec.lua +++ b/spec/luacheck_spec.lua @@ -32,7 +32,8 @@ describe("luacheck", function() it("works on empty list", function() assert.same({ warnings = 0, - errors = 0 + errors = 0, + fatals = 0 }, luacheck({})) end) @@ -76,14 +77,16 @@ describe("luacheck", function() } }, { - error = "syntax", - line = 1, - column = 6, - offset = 6, - msg = "expected '=' near '__future__'" + { + code = "011", + line = 1, + column = 6, + msg = "expected '=' near '__future__'" + } }, warnings = 5, - errors = 1 + errors = 1, + fatals = 0 }, luacheck({ "spec/samples/good_code.lua", "spec/samples/bad_code.lua", @@ -118,14 +121,16 @@ describe("luacheck", function() } }, { - error = "syntax", - line = 1, - column = 6, - offset = 6, - msg = "expected '=' near '__future__'" + { + code = "011", + line = 1, + column = 6, + msg = "expected '=' near '__future__'" + } }, warnings = 3, - errors = 1 + errors = 1, + fatals = 0 }, luacheck({ "spec/samples/good_code.lua", "spec/samples/bad_code.lua", @@ -154,14 +159,16 @@ describe("luacheck", function() } }, { - error = "syntax", - line = 1, - column = 6, - offset = 6, - msg = "expected '=' near '__future__'" + { + code = "011", + line = 1, + column = 6, + msg = "expected '=' near '__future__'" + } }, warnings = 2, - errors = 1 + errors = 1, + fatals = 0 }, luacheck({ "spec/samples/good_code.lua", "spec/samples/bad_code.lua", @@ -198,7 +205,8 @@ describe("check_strings", function() it("works on empty list", function() assert.same({ warnings = 0, - errors = 0 + errors = 0, + fatals = 0 }, luacheck.check_strings({})) end) @@ -213,14 +221,16 @@ describe("check_strings", function() } }, { - error = "syntax", - line = 1, - column = 8, - offset = 8, - msg = "unexpected symbol near 'return'" + { + code = "011", + line = 1, + column = 8, + msg = "unexpected symbol near 'return'" + } }, warnings = 1, - errors = 1 + errors = 1, + fatals = 0 }, luacheck.check_strings({"return foo", "return return"})) end) @@ -228,18 +238,20 @@ describe("check_strings", function() assert.same({ {}, { - error = "syntax", - line = 1, - column = 8, - offset = 8, - msg = "unexpected symbol near 'return'" + { + code = "011", + line = 1, + column = 8, + msg = "unexpected symbol near 'return'" + } }, warnings = 0, - errors = 1 + errors = 1, + fatals = 0 }, luacheck.check_strings({"return foo", "return return"}, {ignore = {"113"}})) end) - it("ignores tables", function() + it("ignores tables with .fatal field", function() assert.same({ { { @@ -250,11 +262,12 @@ describe("check_strings", function() } }, { - error = "I/O" + fatal = "I/O" }, warnings = 1, - errors = 1 - }, luacheck.check_strings({"return foo", {error = "I/O"}})) + errors = 0, + fatals = 1 + }, luacheck.check_strings({"return foo", {fatal = "I/O"}})) end) end) @@ -268,10 +281,10 @@ describe("get_report", function() assert.is_table(luacheck.get_report("return foo")) end) - it("returns nil, error on syntax error", function() - local res, err = luacheck.get_report("return return") - assert.is_nil(res) - assert.same({line = 1, column = 8, offset = 8, msg = "unexpected symbol near 'return'"}, err) + it("returns a table with single error event on syntax error", function() + local report = luacheck.get_report("return return") + assert.is_table(report) + assert.same({code = "011", line = 1, column = 8, msg = "unexpected symbol near 'return'"}, report[1]) end) end) @@ -302,7 +315,8 @@ describe("process_reports", function() }, {}, warnings = 1, - errors = 0 + errors = 0, + fatals = 0 }, luacheck.process_reports({luacheck.get_report("return foo"), luacheck.get_report("return math")})) end) @@ -325,7 +339,8 @@ describe("process_reports", function() } }, warnings = 2, - errors = 0 + errors = 0, + fatals = 0 }, luacheck.process_reports({luacheck.get_report("return foo"), luacheck.get_report("return math")}, { std = "none" })) diff --git a/src/luacheck/cache.lua b/src/luacheck/cache.lua index f9419899..76f4dea5 100644 --- a/src/luacheck/cache.lua +++ b/src/luacheck/cache.lua @@ -12,7 +12,7 @@ local fields = { "code", "name", "line", "column", "prev_line", "prev_column", "secondary", "self", "func", "filtered", "top", "invalid", "unpaired", "read_only", "global", "filtered_111", "filtered_121", "filtered_131", "filtered_112", "filtered_122", "filtered_113", "definition", - "in_module"} + "in_module", "msg"} -- Converts table with fields into table with indexes. local function compress(t) @@ -83,11 +83,6 @@ end -- Serializes check result into a string. function cache.serialize(events) - if events.error then - return ("return {%d,%d,%d,%s}"):format( - events.line, events.column, events.offset, ("%q"):format(events.msg)) - end - local strings = {} local buffer = {"", "return {"} @@ -171,17 +166,6 @@ local function load_cached(cached) return end - -- Is it a syntax error message? - if type(res[1]) == "number" then - return { - error = "syntax", - line = res[1], - column = res[2], - offset = res[3], - msg = res[4] - } - end - local decompressed = {} for i, event in ipairs(res) do diff --git a/src/luacheck/check.lua b/src/luacheck/check.lua index 15f79637..e9572786 100644 --- a/src/luacheck/check.lua +++ b/src/luacheck/check.lua @@ -154,10 +154,7 @@ function ChState:warn_empty_block(location, do_end) }) end ---- Checks source. --- Returns an array of warnings. --- Raises {line = line, column = column, offset = offset, msg = msg} on syntax errors. -local function check(src) +local function check_or_throw(src) local ast, comments, code_lines = parse(src) local chstate = ChState() local line = linearize(chstate, ast) @@ -168,4 +165,24 @@ local function check(src) return chstate.warnings end +--- Checks source. +-- Returns an array of warnings and errors. Codes for errors start with "0". +-- Syntax errors (with code "011") have message stored in .msg field. +local function check(src) + local warnings, err = utils.pcall(check_or_throw, src) + + if warnings then + return warnings + else + local syntax_error = { + code = "011", + line = err.line, + column = err.column, + msg = err.msg + } + + return {syntax_error} + end +end + return check diff --git a/src/luacheck/filter.lua b/src/luacheck/filter.lua index b5fb30d9..6ebda97d 100644 --- a/src/luacheck/filter.lua +++ b/src/luacheck/filter.lua @@ -41,7 +41,7 @@ local function get_defined_and_used_globals(file_report, opts) local defined, globally_defined, used = {}, {}, {} for _, warning in ipairs(file_report) do - if warning.code and warning.code:match("11.") then + if warning.code:match("11.") then if warning.code == "111" then if (opts.inline and warning.definition) or core_utils.is_definition(opts, warning) then if (opts.inline and warning.in_module) or opts.module then @@ -86,7 +86,7 @@ local function filter_implicit_defs_file(file_report, opts, globally_defined, gl local res = {} for _, warning in ipairs(file_report) do - if warning.code and warning.code:match("11.") then + if warning.code:match("11.") then if warning.code == "111" then if (opts.inline and warning.in_module) or opts.module then if not locally_defined[warning.name] then @@ -125,7 +125,7 @@ local function filter_implicit_defs(report, opts) local info = get_implicit_defs_info(report, opts) for i, file_report in ipairs(report) do - if not file_report.error then + if not file_report.fatal then res[i] = filter_implicit_defs_file(file_report, opts[i], info.globally_defined, info.globally_used, info.locally_defined[i]) else res[i] = file_report @@ -256,16 +256,16 @@ end local function filter_file_report(report, opts) local res = {} - for _, warning in ipairs(report) do - if warning.code and ((opts.inline and warning.read_only) or warning.code:match("11[12]") - and not warning.module and opts.read_globals[warning.name]) and not ( - (opts.inline and warning.global) or (opts.globals[warning.name] and not opts.read_globals[warning.name])) then - warning.code = "12" .. warning.code:sub(3, 3) + for _, event in ipairs(report) do + if ((opts.inline and event.read_only) or event.code:match("11[12]") + and not event.module and opts.read_globals[event.name]) and not ( + (opts.inline and event.global) or (opts.globals[event.name] and not opts.read_globals[event.name])) then + event.code = "12" .. event.code:sub(3, 3) end - if (not warning.code and opts.inline) or (warning.code and (not warning.filtered and - not warning["filtered_" .. warning.code] or not opts.inline) and not filter.filters(opts, warning)) then - table.insert(res, warning) + if event.code == "011" or (event.code:match("02.") and opts.inline) or (event.code:sub(1, 1) ~= "0" and (not event.filtered and + not event["filtered_" .. event.code] or not opts.inline) and not filter.filters(opts, event)) then + table.insert(res, event) end end @@ -277,7 +277,7 @@ local function filter_report(report, opts) local res = {} for i, file_report in ipairs(report) do - if not file_report.error then + if not file_report.fatal then res[i] = filter_file_report(file_report, opts[i]) else res[i] = file_report diff --git a/src/luacheck/format.lua b/src/luacheck/format.lua index 4e302a2d..40a4984f 100644 --- a/src/luacheck/format.lua +++ b/src/luacheck/format.lua @@ -3,6 +3,10 @@ local utils = require "luacheck.utils" local color_support = not utils.is_windows or os.getenv("ANSICON") local message_formats = { + ["011"] = function(w) return w.msg end, + ["021"] = "invalid inline option", + ["022"] = "unpaired push directive", + ["023"] = "unpaired pop directive", ["111"] = function(w) if w.module then return "setting non-module global variable %s" else return "setting non-standard global variable %s" end end, @@ -45,12 +49,6 @@ local message_formats = { } local function get_message_format(warning) - if warning.invalid then - return "invalid inline option" - elseif warning.unpaired then - return "unpaired inline option" - end - local message_format = message_formats[warning.code] if type(message_format) == "function" then @@ -101,20 +99,43 @@ local function capitalize(str) return str:gsub("^.", string.upper) end -local function error_type(file_report) - return capitalize(file_report.error) .. " error" +local function fatal_type(file_report) + return capitalize(file_report.fatal) .. " error" +end + +local function count_warnings_errors(events) + local warnings, errors = 0, 0 + + for _, event in ipairs(events) do + if event.code:sub(1, 1) == "0" then + errors = errors + 1 + else + warnings = warnings + 1 + end + end + + return warnings, errors end local function format_file_report_header(report, file_name, _, color) local label = "Checking " .. file_name local status - if report.error then - status = format_color(error_type(report), color, "bright") + if report.fatal then + status = format_color(fatal_type(report), color, "bright") elseif #report == 0 then status = format_color("OK", color, "bright", "green") else - status = format_color("Failure", color, "bright", "red") + local warnings, errors = count_warnings_errors(report) + + if warnings > 0 then + status = format_color(tostring(warnings).." warning"..plural(warnings), color, "bright", "red") + end + + if errors > 0 then + status = status and (status.." / ") or "" + status = status..(format_color(tostring(errors).." error"..plural(errors), color, "bright")) + end end return label .. (" "):rep(math.max(50 - #label, 1)) .. status @@ -124,33 +145,29 @@ local function format_location(file, location) return ("%s:%d:%d"):format(file, location.line, location.column) end -local function format_warning(file_name, warning, codes, color) - local message_format = get_message_format(warning) - local message = message_format:format(warning.name and format_name(warning.name, color), warning.prev_line) +local function event_code(event) + return (event.code:sub(1, 1) == "0" and "E" or "W")..event.code +end - if warning.code and codes then - message = ("(W%s) %s"):format(warning.code, message) - end +local function format_event(file_name, event, codes, color) + local message_format = get_message_format(event) + local message = message_format:format(event.name and format_name(event.name, color), event.prev_line) - return format_location(file_name, warning) .. ": " .. message -end + if codes then + message = ("(%s) %s"):format(event_code(event), message) + end -local function format_error_msg(file_name, error_report) - return format_location(file_name, error_report) .. ": " .. error_report.msg + return format_location(file_name, event) .. ": " .. message end local function format_file_report(report, file_name, codes, color) local buf = {format_file_report_header(report, file_name, codes, color)} - if report.msg or #report > 0 then + if #report > 0 then table.insert(buf, "") - for _, warning in ipairs(report) do - table.insert(buf, " " .. format_warning(file_name, warning, codes, color)) - end - - if report.msg then - table.insert(buf, " " .. format_error_msg(file_name, report)) + for _, event in ipairs(report) do + table.insert(buf, " " .. format_event(file_name, event, codes, color)) end table.insert(buf, "") @@ -166,7 +183,7 @@ function formatters.default(report, file_names, codes, quiet, color) if quiet <= 2 then for i, file_report in ipairs(report) do - if quiet == 0 or file_report.error or #file_report > 0 then + if quiet == 0 or file_report.fatal or #file_report > 0 then table.insert(buf, (quiet == 2 and format_file_report_header or format_file_report) ( file_report, file_names[i], codes, color)) end @@ -177,12 +194,17 @@ function formatters.default(report, file_names, codes, quiet, color) end end - table.insert(buf, ("Total: %s warning%s / %s error%s in %d file%s"):format( + local total = ("Total: %s warning%s / %s error%s in %d file%s"):format( format_number(report.warnings, color), plural(report.warnings), format_number(report.errors, color), plural(report.errors), - #report, plural(#report) - )) + #report - report.fatals, plural(#report - report.fatals)) + + if report.fatals > 0 then + total = total..(", couldn't check %s file%s"):format( + report.fatals, plural(report.fatals)) + end + table.insert(buf, total) return table.concat(buf, "\n") end @@ -190,17 +212,13 @@ function formatters.TAP(report, file_names, codes) local buf = {} for i, file_report in ipairs(report) do - if file_report.error then - if file_report.msg then - table.insert(buf, ("not ok %d %s"):format(#buf + 1, format_error_msg(file_names[i], file_report))) - else - table.insert(buf, ("not ok %d %s: %s error"):format(#buf + 1, file_names[i], error_type(file_report))) - end + if file_report.fatal then + table.insert(buf, ("not ok %d %s: %s"):format(#buf + 1, file_names[i], fatal_type(file_report))) elseif #file_report == 0 then table.insert(buf, ("ok %d %s"):format(#buf + 1, file_names[i])) else for _, warning in ipairs(file_report) do - table.insert(buf, ("not ok %d %s"):format(#buf + 1, format_warning(file_names[i], warning, codes))) + table.insert(buf, ("not ok %d %s"):format(#buf + 1, format_event(file_names[i], warning, codes))) end end end @@ -214,7 +232,7 @@ function formatters.JUnit(report, file_names) local num_testcases = 0 for _, file_report in ipairs(report) do - if file_report.error or #file_report == 0 then + if file_report.fatal or #file_report == 0 then num_testcases = num_testcases + 1 else num_testcases = num_testcases + #file_report @@ -224,25 +242,17 @@ function formatters.JUnit(report, file_names) table.insert(buf, ([[]]):format(num_testcases)) for file_i, file_report in ipairs(report) do - if file_report.error then + if file_report.fatal then table.insert(buf, ([[ ]]):format(file_names[file_i], file_names[file_i])) - - if file_report.msg then - table.insert(buf, ([[ ]]):format( - error_type(file_report), format_error_msg(file_names[file_i], file_report))) - else - table.insert(buf, ([[ ]]):format(error_type(file_report))) - end - + table.insert(buf, ([[ ]]):format(fatal_type(file_report))) table.insert(buf, [[ ]]) elseif #file_report == 0 then table.insert(buf, ([[ ]]):format(file_names[file_i], file_names[file_i])) else - for warning_i, warning in ipairs(file_report) do - local warning_type = warning.code and ("W" .. warning.code) or "Inline option" - table.insert(buf, ([[ ]]):format(file_names[file_i], warning_i, file_names[file_i])) + for event_i, event in ipairs(file_report) do + table.insert(buf, ([[ ]]):format(file_names[file_i], event_i, file_names[file_i])) table.insert(buf, ([[ ]]):format( - warning_type, format_warning(file_names[file_i], warning))) + event_code(event), format_event(file_names[file_i], event))) table.insert(buf, [[ ]]) end end @@ -256,15 +266,11 @@ function formatters.plain(report, file_names, codes) local buf = {} for i, file_report in ipairs(report) do - if file_report.error then - if file_report.msg then - table.insert(buf, format_error_msg(file_names[i], file_report)) - else - table.insert(buf, ("%s: %s error"):format(file_names[i], error_type(file_report))) - end + if file_report.fatal then + table.insert(buf, ("%s: %s"):format(file_names[i], fatal_type(file_report))) else - for _, warning in ipairs(file_report) do - table.insert(buf, format_warning(file_names[i], warning, codes)) + for _, event in ipairs(file_report) do + table.insert(buf, format_event(file_names[i], event, codes)) end end end diff --git a/src/luacheck/init.lua b/src/luacheck/init.lua index bb4f5d1a..3a60d12d 100644 --- a/src/luacheck/init.lua +++ b/src/luacheck/init.lua @@ -39,27 +39,34 @@ local function validate_options(fname, items, opts) end end --- Returns report for a string or nil, {line = line, column = column, offset = offset, msg = msg} in case of syntax error. +-- Returns report for a string. Report is an array of warnings and errors. function luacheck.get_report(src) assert(type(src) == "string", ("bad argument #1 to 'luacheck.get_report' (string expected, got %s)"):format(type(src))) - return utils.pcall(check, src) + return check(src) end --- Applies options to reports. Reports with .error field are unchanged. +-- Applies options to reports. Reports with .fatal field are unchanged. -- Options are applied to reports[i] in order: options, options[i], options[i][1], options[i][2], ... --- Returns new array of reports, adds .warnings and .errors fields. +-- Returns new array of reports, adds .warnings, .errors and .fatals fields to this array. function luacheck.process_reports(reports, opts) assert(type(reports) == "table", ("bad argument #1 to 'luacheck.process_reports' (table expected, got %s)"):format(type(reports))) validate_options("luacheck.process_reports", reports, opts) local report = filter.filter(reports, opts) report.warnings = 0 report.errors = 0 + report.fatals = 0 for _, file_report in ipairs(report) do - if file_report.error then - report.errors = report.errors + 1 + if file_report.fatal then + report.fatals = report.fatals + 1 else - report.warnings = report.warnings + #file_report + for _, event in ipairs(file_report) do + if event.code:sub(1, 1) == "0" then + report.errors = report.errors + 1 + else + report.warnings = report.warnings + 1 + end + end end end @@ -67,7 +74,7 @@ function luacheck.process_reports(reports, opts) end -- Checks strings with options, returns report. --- Error reports are unchanged. +-- Tables with .fatal field are unchanged. function luacheck.check_strings(srcs, opts) assert(type(srcs) == "table", ("bad argument #1 to 'luacheck.check_strings' (table expected, got %s)"):format(type(srcs))) @@ -82,17 +89,10 @@ function luacheck.check_strings(srcs, opts) local reports = {} for i, src in ipairs(srcs) do - if type(src) == "table" then + if type(src) == "table" and src.fatal then reports[i] = src else - local report, err = luacheck.get_report(src) - - if report then - reports[i] = report - else - err.error = "syntax" - reports[i] = err - end + reports[i] = luacheck.get_report(src) end end @@ -113,7 +113,7 @@ function luacheck.check_files(files, opts) local srcs = {} for i, file in ipairs(files) do - srcs[i] = utils.read_file(file) or {error = "I/O"} + srcs[i] = utils.read_file(file) or {fatal = "I/O"} end return luacheck.check_strings(srcs, opts) diff --git a/src/luacheck/inline_options.lua b/src/luacheck/inline_options.lua index af8b3e6e..d89f9df8 100644 --- a/src/luacheck/inline_options.lua +++ b/src/luacheck/inline_options.lua @@ -182,9 +182,9 @@ end -- Mutates shape of warnings in events according to inline options. -- Warnings which are simply filtered are marked with .filtered. --- Returns array of unpaired push/pop comments. +-- Returns arrays of unpaired push events and unpaired pop events. local function handle_events(events, per_line_opts) - local unpaired_comments = {} + local unpaired_pushes, unpaired_pops = {}, {} local unfiltered_warnings = {} local option_stack = utils.Stack() local boundaries = utils.Stack() @@ -213,12 +213,12 @@ local function handle_events(events, per_line_opts) elseif event.pop then if boundaries.size == 0 or (boundaries.top.closure and not event.closure) then -- Unpaired pop boundary, do nothing. - table.insert(unpaired_comments, event) + table.insert(unpaired_pops, event) else if event.closure then -- There could be unpaired push boundaries, pop them. while not boundaries.top.closure do - table.insert(unpaired_comments, boundaries:pop()) + table.insert(unpaired_pushes, boundaries:pop()) end end @@ -242,7 +242,7 @@ local function handle_events(events, per_line_opts) apply_inline_options(option_stack, per_line_opts, unfiltered_warnings) end - return unpaired_comments + return unpaired_pushes, unpaired_pops end -- Filteres warnings using inline options, adds invalid comments. @@ -254,9 +254,10 @@ end -- .in_module is added to 111 warnings that are in module due to inline options. -- .read_only is added to 111 and 112 warnings related to read only globals. -- .global is added to 111 and 112 related to regular globals. --- Invalid comments have same shape as warnings except they don't have .code field. --- Instead, they may have .invalid or .unpaired field for syntactically invalid inline options and unpaired --- push/pop options, correspondingly. +-- Invalid comments have same shape as warnings, with codes: +-- 021 - syntactically invalid comment; +-- 022 - unpaired push comment; +-- 023 - unpaired pop comment. local function handle_inline_options(ast, comments, code_lines, warnings) -- Create array of all events sorted by location. -- This includes inline options, warnings and implicit push/pop operations corresponding to closure starts/ends. @@ -271,14 +272,18 @@ local function handle_inline_options(ast, comments, code_lines, warnings) add_closure_boundaries(ast, events) local per_line_opts, invalid_comments = add_inline_options(events, comments, code_lines) core_utils.sort_by_location(events) - local unpaired_comments = handle_events(events, per_line_opts) + local unpaired_pushes, unpaired_pops = handle_events(events, per_line_opts) for _, comment in ipairs(invalid_comments) do - table.insert(warnings, {invalid = true, line = comment.location.line, column = comment.location.column}) + table.insert(warnings, {code = "021", line = comment.location.line, column = comment.location.column}) end - for _, event in ipairs(unpaired_comments) do - table.insert(warnings, {unpaired = true, line = event.line, column = event.column}) + for _, event in ipairs(unpaired_pushes) do + table.insert(warnings, {code = "022", line = event.line, column = event.column}) + end + + for _, event in ipairs(unpaired_pops) do + table.insert(warnings, {code = "023", line = event.line, column = event.column}) end return warnings diff --git a/src/luacheck/main.lua b/src/luacheck/main.lua index 9bdc54b6..c277eab5 100644 --- a/src/luacheck/main.lua +++ b/src/luacheck/main.lua @@ -11,16 +11,16 @@ local fs = require "luacheck.fs" local Globber = require "luacheck.globber" local utils = require "luacheck.utils" -local function fatal(msg) - io.stderr:write("Fatal error: "..msg.."\n") +local function critical(msg) + io.stderr:write("Critical error: "..msg.."\n") os.exit(3) end local function global_error_handler(err) if type(err) == "table" and err.pattern then - fatal("Invalid pattern '" .. err.pattern .. "'") + critical("Invalid pattern '" .. err.pattern .. "'") else - fatal(debug.traceback( + critical(debug.traceback( ("Luacheck %s bug (please report at github.com/mpeterv/luacheck/issues):\n%s"):format(luacheck._VERSION, err), 2)) end end @@ -300,7 +300,7 @@ Otherwise, the pattern matches warning code.]]) end end - return sparse_mtimes, cache.load(cache_filename, cache_files, cache_mtimes) or fatal( + return sparse_mtimes, cache.load(cache_filename, cache_files, cache_mtimes) or critical( ("Couldn't load cache from %s: data corrupted"):format(cache_filename)) end @@ -323,17 +323,6 @@ Otherwise, the pattern matches warning code.]]) return res end - local function get_report(source) - local report, err = luacheck.get_report(source) - - if report then - return report - else - err.error = "syntax" - return err - end - end - -- Returns sparse array of new reports. local function get_new_reports(files, srcs, jobs) local dense_srcs = {} @@ -347,7 +336,7 @@ Otherwise, the pattern matches warning code.]]) end local map = jobs and multithreading.has_lanes and multithreading.pmap or utils.map - local dense_res = map(get_report, dense_srcs, jobs) + local dense_res = map(luacheck.get_report, dense_srcs, jobs) local res = {} @@ -376,7 +365,7 @@ Otherwise, the pattern matches warning code.]]) end end - return cache.update(cache_filename, cache_files, cache_mtimes, cache_reports) or fatal( + return cache.update(cache_filename, cache_files, cache_mtimes, cache_reports) or critical( ("Couldn't save cache to %s: I/O error"):format(cache_filename)) end @@ -403,7 +392,7 @@ Otherwise, the pattern matches warning code.]]) for i, file in ipairs(files) do if bad_files[i] then - res[i] = {error = bad_files[i]} + res[i] = {fatal = bad_files[i]} else res[i] = cached_reports[file] or new_reports[i] end @@ -453,14 +442,14 @@ Otherwise, the pattern matches warning code.]]) ok, formatter = config.relative_require(conf, formatter) if not ok then - fatal(("Couldn't load custom formatter '%s': %s"):format(args.formatter, formatter)) + critical(("Couldn't load custom formatter '%s': %s"):format(args.formatter, formatter)) end end ok, output = pcall(formatter, report, file_names, args) if not ok then - fatal(("Couldn't run custom formatter '%s': %s"):format(tostring(args.formatter), output)) + critical(("Couldn't run custom formatter '%s': %s"):format(tostring(args.formatter), output)) end return output @@ -478,7 +467,7 @@ Otherwise, the pattern matches warning code.]]) conf, err = config.load_config(args.config) if not conf then - fatal(err) + critical(err) end end @@ -502,9 +491,9 @@ Otherwise, the pattern matches warning code.]]) local exit_code - if report.errors > 0 then + if report.fatals > 0 then exit_code = 2 - elseif report.warnings > 0 then + elseif report.warnings > 0 or report.errors > 0 then exit_code = 1 else exit_code = 0