Skip to content

Commit 15890fa

Browse files
committed
Fix event sorting
A warning and an inline option boundary event can share location when a function declaration is unreachable code (a push event is generated for each closure start). Previously event comparator assumed that only two warnings or two inline option events can share location, resulting in an error when this assumption is violated. The fix is to compare events of different types by set priority before falling back to code comparison for warning. Also remove a hack that partially worked around this issue by adding `code` field to some inline option events. Ref #74.
1 parent 48fd788 commit 15890fa

File tree

3 files changed

+38
-10
lines changed

3 files changed

+38
-10
lines changed

spec/check_spec.lua

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,17 @@ end
617617
]])
618618
end)
619619

620+
it("detects unreachable functions", function()
621+
assert.same({
622+
{code = "231", name = "f", line = 1, column = 7, end_column = 7},
623+
{code = "511", line = 3, column = 1, end_column = 8}
624+
}, check[[
625+
local f = nil
626+
do return end
627+
function f() end
628+
]])
629+
end)
630+
620631
it("detects unreachable code in nested function", function()
621632
assert.same({
622633
{code = "511", line = 4, column = 7, end_column = 12}

src/luacheck/core_utils.lua

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,33 @@ function core_utils.is_definition(opts, warning)
5858
return opts.allow_defined or (opts.allow_defined_top and warning.top)
5959
end
6060

61-
local function location_comparator(event1, event2)
62-
-- If two events share location, neither can be an invalid comment event.
63-
-- However, they can be equal by identity due to the way table.sort is implemented.
64-
return event1.line < event2.line or
65-
event1.line == event2.line and (event1.column < event2.column or
66-
event1.column == event2.column and event1.code and event1.code < event2.code)
61+
local function event_priority(event)
62+
-- Inline option boundaries have priority over inline option declarations
63+
-- so that `-- luacheck: push ignore foo` is interpreted correctly (push first).
64+
if event.push or event.pop then
65+
return -2
66+
elseif event.options then
67+
return -1
68+
else
69+
return tonumber(event.code)
70+
end
71+
end
72+
73+
local function event_comparator(event1, event2)
74+
if event1.line ~= event2.line then
75+
return event1.line < event2.line
76+
elseif event1.column ~= event2.column then
77+
return event1.column < event2.column
78+
else
79+
return event_priority(event1) < event_priority(event2)
80+
end
6781
end
6882

83+
-- Sorts an array of warnings, inline options (tables with `options` field)
84+
-- or inline option boundaries (tables with `push` or `pop` field) by location
85+
-- information as provided in `line` and `column` fields.
6986
function core_utils.sort_by_location(array)
70-
table.sort(array, location_comparator)
87+
table.sort(array, event_comparator)
7188
end
7289

7390
return core_utils

src/luacheck/inline_options.lua

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ local function add_inline_option(events, per_line_opts, body, location, end_colu
9898
end
9999

100100
if body == "push" or body == "pop" then
101-
table.insert(events, {code = 1, [body] = true, line = location.line, column = location.column, end_column = end_column})
101+
table.insert(events, {[body] = true, line = location.line, column = location.column, end_column = end_column})
102102

103103
if after_push then
104104
body = after_push
@@ -120,7 +120,7 @@ local function add_inline_option(events, per_line_opts, body, location, end_colu
120120

121121
table.insert(per_line_opts[location.line], opts)
122122
else
123-
table.insert(events, {code = 2, options = opts, line = location.line, column = location.column, end_column = end_column})
123+
table.insert(events, {options = opts, line = location.line, column = location.column, end_column = end_column})
124124
end
125125

126126
return true
@@ -216,7 +216,7 @@ local function handle_events(events, per_line_opts)
216216

217217
-- Go through all events.
218218
for _, event in ipairs(events) do
219-
if type(event.code) == "string" then
219+
if event.code then
220220
-- It's a warning, put it into list of not handled warnings.
221221
table.insert(unfiltered_warnings, event)
222222
elseif event.options then

0 commit comments

Comments
 (0)