Skip to content

Commit

Permalink
Adds support for continued analysis through calls
Browse files Browse the repository at this point in the history
  • Loading branch information
arichard4 committed May 11, 2022
1 parent ef20234 commit 197e276
Show file tree
Hide file tree
Showing 3 changed files with 283 additions and 51 deletions.
43 changes: 0 additions & 43 deletions spec/table_field_limitations_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -112,24 +112,6 @@ end
]])
end)

it("stops checking referenced upvalues if function call is known to not have table as an upvalue", function()
assert_warnings({}, [[
local x = {}
x[1] = 1
local function printx() x = 1 end
local function ret2() return 2 end
ret2()
x[1] = 1
local y = {}
y[1] = 1
function y.printx() y = 1 end
function y.ret2() return 2 end
y.ret2()
y[1] = 1
]])
end)

it("halts checking at the end of control flow blocks with jumps", function()
assert_warnings({}, [[
local x = {1}
Expand All @@ -156,29 +138,4 @@ end
a[1] = a[1]
]])
end)

it("stops checking if a function is called", function()
assert_warnings({
{line = 8, column = 3, name = 'y', end_column = 3, field = 'x', code = '315', set_is_nil = '' },
{line = 8, column = 9, name = 'y', end_column = 9, field = 'a', code = '325', },
{line = 14, column = 9, name = 't', end_column = 9, field = 'a', code = '325', },
}, [[
local x = {}
x.y = 1
print("Unrelated text")
x.y = 2
x[1] = x.z
local y = {}
y.x = y.a
y.x = 1
function y:func() return 1 end
y:func()
local t = {}
t.x = t.a
local var = 'func'
t.x = y[var]() + 1
]])
end)
end)
199 changes: 199 additions & 0 deletions spec/table_fields_functions_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
local helper = require "spec.helper"

local function assert_warnings(warnings, src)
assert.same(warnings, helper.get_stage_warnings("check_table_fields", src))
end

describe("table field checks", function()
it("method invocation ends tracking of the base table", function()
assert_warnings({
{code = "315", line = 27, column = 3, end_column = 3, name = 'y', field = 'y', set_is_nil = ''},
{code = "325", line = 27, column = 9, end_column = 9, name = 'y', field = 'x'},
}, [[
local y = {}
local x = {}
x.y = 1
function x:func() print(self) end
x.y = x:func()
x.y = x.z
local a = {}
a.y = 1
function a:func() print(self) end
a[a:func()] = 1
a.y = a.z
local b = {}
b.y = 1
function b:func() print(self) return 1 end
b[1] = a[b:func()]
b.y = b.z
local c = {}
c.y = 1
function c:func() print(self) return 1 end
c:func()
c.y = c.z
y.y = y.x
]])
end)

it("functions calls using a table as a whole end tracking of that table", function()
assert_warnings({
{code = "315", line = 31, column = 3, end_column = 3, name = 'y', field = 'y', set_is_nil = ''},
{code = "325", line = 31, column = 9, end_column = 9, name = 'y', field = 'x'},
}, [[
local y = {}
local x = {}
x.y = 1
x.y = print(x)
x.y = x.z
local a = {}
a.y = 1
function a:func() print(self) end
a[print(a)] = 1
a.y = a.z
local b = {}
b.y = 1
function b:func() print(self) return 1 end
b[1] = a[print(b)]
b.y = b.z
local c = {}
c.y = 1
function c:func() print(self) return 1 end
print(c)
c.y = c.z
local d = {}
d.y = 1
print(print(d))
d.y = d.z
y.y = y.x
]])
end)

it("functions calls using a table field access only that field", function()
assert_warnings({
{code = "315", line = 1, column = 14, end_column = 14, name = 'x', field = 2, set_is_nil = ''},
}, [[
local x = {1,1}
print(x[1])
x[1], x[2] = 1, 2
local y = {}
function y.func(var) print(var) return 1 end
y[1] = y.func(x[1])
x[1] = 1
y[2] = y:func(x[1])
x[1] = 1
return x, y
]])
end)

it("functions calls stop checking for tables accessed as upvalues", function()
assert_warnings({
{code = "315", line = 27, column = 3, end_column = 3, name = 'y', field = 'y', set_is_nil = ''},
}, [[
local y = {}
local x = {}
x.y = 1
function x.func() print(x) end
x.y = x.func()
x.y = x.z
local a = {}
a.y = 1
function a.func() print(a) end
a[a.func()] = 1
a.y = a.z
local b = {}
b.y = 1
function glob_func() print(b) return 1 end
b[1] = a[glob_func()]
b.y = b.z
local c = {}
c.y = 1
local function func() print(c) return 1 end
func()
c.y = c.z
y.y = 1
return x, a, b, c
]])
end)

it("handles multiple layers of nested function calls correctly", function()
assert_warnings({
{code = "315", line = 2, column = 3, end_column = 3, name = 'x', field = 'y', set_is_nil = ''},
}, [[
local x = {...}
x.y = x[2]
]])
end)

it("assumes that parameters and upvalues have all keys accessed or written to on a function call", function()
assert_warnings({
}, [[
local function other_func() end
local x = {}
function func(t)
t = {1}
x = {1}
other_func()
t[2] = t[2]
x[2] = x[2]
end
]])
end)

it("stop tracking for tables passed externally", function()
assert_warnings({}, [[
local t
local function func1(var)
t = var
end
local x = {}
func1(x)
x[1] = 1
print(t[1])
local z = {}
local a
function z:func() a = self end
z:func()
z[1] = 1
print(a[1])
local y
local function func2() return y end
function func3()
y = {}
local t = func2()
y[1] = 1
print(t[1])
end
]])
end)

it("continues analysis through a function call", function()
assert_warnings({
{line = 3, column = 12, name = 'x', end_column = 15, field = 'oops', code = '325', },
}, [[
local x = {}
print("Meh")
print(x.oops)
]])
end)
end)
Loading

0 comments on commit 197e276

Please sign in to comment.