Skip to content

Commit

Permalink
Produce less flow graph edges for literal loop conditions
Browse files Browse the repository at this point in the history
Improves local resolution and unreachable code detection around infinite
loops like 'while true do ... end'.
  • Loading branch information
mpeterv committed Jul 21, 2015
1 parent ae94d75 commit 96f4c87
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 18 deletions.
53 changes: 42 additions & 11 deletions spec/check_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ for i in pairs{} do end
{code = "113", name = "print", line = 9, column = 1, end_column = 5}
}, check[[
local a
if true then
if ... then
a = 2
else
a = 3
Expand Down Expand Up @@ -352,21 +352,21 @@ goto fail
{code = "511", line = 2, column = 1, end_column = 2}
}, check[[
do return end
if true then return 6 end
if ... then return 6 end
return 3
]])

assert.same({
{code = "511", line = 7, column = 1, end_column = 2},
{code = "511", line = 13, column = 1, end_column = 6}
}, check[[
if false then
if ... then
return 4
else
return 6
end
if true then
if ... then
return 7
else
return 8
Expand All @@ -376,14 +376,45 @@ return 3
]])
end)

it("detects unreachable code with literal conditions", function()
assert.same({
{code = "511", line = 4, column = 1, end_column = 6}
}, check[[
while true do
(...)()
end
return
]])

assert.same({}, check[[
repeat
if ... then
break
end
until false
return
]])

assert.same({
{code = "511", line = 6, column = 1, end_column = 6}
}, check[[
repeat
if nil then
break
end
until false
return
]])
end)

it("detects accessing uninitialized variables", function()
assert.same({
{code = "113", name = "get", line = 6, column = 8, end_column = 10},
{code = "321", name = "a", line = 6, column = 12, end_column = 12}
}, check[[
local a
if true then
if ... then
a = 5
else
a = get(a)
Expand Down Expand Up @@ -423,22 +454,22 @@ a, b = 1, 2, 3; (...)(a, b)
it("detects empty blocks", function()
assert.same({
{code = "541", line = 1, column = 1, end_column = 2},
{code = "542", line = 3, column = 9, end_column = 12},
{code = "542", line = 5, column = 14, end_column = 17},
{code = "542", line = 3, column = 8, end_column = 11},
{code = "542", line = 5, column = 12, end_column = 15},
{code = "542", line = 7, column = 1, end_column = 4}
}, check[[
do end
if true then
if ... then
elseif false then
elseif ... then
else
end
while true do end
repeat until true
while ... do end
repeat until ...
]])
end)

Expand Down
79 changes: 75 additions & 4 deletions spec/linearize_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -131,13 +131,13 @@ do print(foo) end]]))
assert.equal([[
1: Local ... (2..8)
2: Noop
3: Eval True
3: Eval Id
4: Cjump -> 9
5: Local s (6..6)
6: Eval Call
7: Noop
8: Jump -> 3]], get_line_as_string([[
while true do
while cond do
local s = io.read()
print(s)
end]]))
Expand All @@ -147,12 +147,12 @@ end]]))
2: Noop
3: Local s (4..5)
4: Eval Call
5: Eval False
5: Eval Id
6: Cjump -> 3]], get_line_as_string([[
repeat
local s = io.read()
print(s)
until false]]))
until cond]]))

assert.equal([[
1: Local ... (2..9)
Expand Down Expand Up @@ -182,6 +182,50 @@ for k, v in pairs(t) do
end]]))
end)

it("linearizes loops with literal condition correctly", function()
assert.equal([[
1: Local ... (2..6)
2: Noop
3: Eval Number
4: Eval Call
5: Noop
6: Jump -> 3]], get_line_as_string([[
while 1 do
foo()
end]]))

assert.equal([[
1: Local ... (2..7)
2: Noop
3: Eval False
4: Jump -> 8
5: Eval Call
6: Noop
7: Jump -> 3]], get_line_as_string([[
while false do
foo()
end]]))

assert.equal([[
1: Local ... (2..4)
2: Noop
3: Eval Call
4: Eval True]], get_line_as_string([[
repeat
foo()
until true]]))

assert.equal([[
1: Local ... (2..5)
2: Noop
3: Eval Call
4: Eval Nil
5: Jump -> 3]], get_line_as_string([[
repeat
foo()
until nil]]))
end)

it("linearizes nested loops and breaks correctly", function()
assert.equal([[
1: Local ... (2..24)
Expand Down Expand Up @@ -255,6 +299,33 @@ if cond() then
end]]))
end)

it("linearizes if with literal condition correctly", function()
assert.equal([[
1: Local ... (2..14)
2: Noop
3: Eval True
4: Noop
5: Eval Call
6: Cjump -> 9
7: Eval Call
8: Jump -> 14
9: Eval False
10: Jump -> 13
11: Eval Call
12: Jump -> 14
13: Eval Call
14: Jump -> 15]], get_line_as_string([[
if true then
if cond() then
stmts()
elseif false then
stmts()
else
stmts()
end
end]]))
end)

it("linearizes gotos correctly", function()
assert.equal([[
1: Local ... (2..13)
Expand Down
20 changes: 17 additions & 3 deletions src/luacheck/linearize.lua
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,20 @@ function LinState:emit_goto(name, is_conditional, location)
table.insert(self.scopes.top.gotos, new_goto(name, jump, location))
end

local tag_to_boolean = {
Nil = false, False = false,
True = true, Number = true, String = true, Table = true, Function = true
}

-- Emits goto that jumps to ::name:: if bool(cond_node) == false.
function LinState:emit_cond_goto(name, cond_node)
local cond_bool = tag_to_boolean[cond_node.tag]

if cond_bool ~= true then
self:emit_goto(name, cond_bool ~= false)
end
end

function LinState:emit_noop(node, loop_end)
self:emit(new_noop_item(node, loop_end))
end
Expand Down Expand Up @@ -278,7 +292,7 @@ function LinState:emit_stmt_While(node)
self:enter_scope()
self:register_label("do")
self:emit_expr(node[1])
self:emit_goto("break", true)
self:emit_cond_goto("break", node[1])
self:emit_block(node[2])
self:emit_noop(node, true)
self:emit_goto("do")
Expand All @@ -294,7 +308,7 @@ function LinState:emit_stmt_Repeat(node)
self:emit_stmts(node[1])
self:emit_expr(node[2])
self:leave_scope()
self:emit_goto("do", true)
self:emit_cond_goto("do", node[2])
self:register_label("break")
self:leave_scope()
end
Expand Down Expand Up @@ -346,7 +360,7 @@ function LinState:emit_stmt_If(node)
for i = 1, #node - 1, 2 do
self:enter_scope()
self:emit_expr(node[i])
self:emit_goto("else", true)
self:emit_cond_goto("else", node[i])
self:check_empty_block(node[i + 1])
self:emit_block(node[i + 1])
self:emit_goto("end")
Expand Down

0 comments on commit 96f4c87

Please sign in to comment.