Skip to content

Commit 79cb119

Browse files
committed
Adds support for continued analysis through calls
1 parent ef20234 commit 79cb119

File tree

3 files changed

+283
-51
lines changed

3 files changed

+283
-51
lines changed

spec/table_field_limitations_spec.lua

-43
Original file line numberDiff line numberDiff line change
@@ -112,24 +112,6 @@ end
112112
]])
113113
end)
114114

115-
it("stops checking referenced upvalues if function call is known to not have table as an upvalue", function()
116-
assert_warnings({}, [[
117-
local x = {}
118-
x[1] = 1
119-
local function printx() x = 1 end
120-
local function ret2() return 2 end
121-
ret2()
122-
x[1] = 1
123-
124-
local y = {}
125-
y[1] = 1
126-
function y.printx() y = 1 end
127-
function y.ret2() return 2 end
128-
y.ret2()
129-
y[1] = 1
130-
]])
131-
end)
132-
133115
it("halts checking at the end of control flow blocks with jumps", function()
134116
assert_warnings({}, [[
135117
local x = {1}
@@ -156,29 +138,4 @@ end
156138
a[1] = a[1]
157139
]])
158140
end)
159-
160-
it("stops checking if a function is called", function()
161-
assert_warnings({
162-
{line = 8, column = 3, name = 'y', end_column = 3, field = 'x', code = '315', set_is_nil = '' },
163-
{line = 8, column = 9, name = 'y', end_column = 9, field = 'a', code = '325', },
164-
{line = 14, column = 9, name = 't', end_column = 9, field = 'a', code = '325', },
165-
}, [[
166-
local x = {}
167-
x.y = 1
168-
print("Unrelated text")
169-
x.y = 2
170-
x[1] = x.z
171-
172-
local y = {}
173-
y.x = y.a
174-
y.x = 1
175-
function y:func() return 1 end
176-
y:func()
177-
178-
local t = {}
179-
t.x = t.a
180-
local var = 'func'
181-
t.x = y[var]() + 1
182-
]])
183-
end)
184141
end)

spec/table_fields_functions_spec.lua

+199
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
local helper = require "spec.helper"
2+
3+
local function assert_warnings(warnings, src)
4+
assert.same(warnings, helper.get_stage_warnings("check_table_fields", src))
5+
end
6+
7+
describe("table field checks", function()
8+
it("method invocation ends tracking of the base table", function()
9+
assert_warnings({
10+
{code = "315", line = 27, column = 3, end_column = 3, name = 'y', field = 'y', set_is_nil = ''},
11+
{code = "325", line = 27, column = 9, end_column = 9, name = 'y', field = 'x'},
12+
}, [[
13+
local y = {}
14+
15+
local x = {}
16+
x.y = 1
17+
function x:func() print(self) end
18+
x.y = x:func()
19+
x.y = x.z
20+
21+
local a = {}
22+
a.y = 1
23+
function a:func() print(self) end
24+
a[a:func()] = 1
25+
a.y = a.z
26+
27+
local b = {}
28+
b.y = 1
29+
function b:func() print(self) return 1 end
30+
b[1] = a[b:func()]
31+
b.y = b.z
32+
33+
local c = {}
34+
c.y = 1
35+
function c:func() print(self) return 1 end
36+
c:func()
37+
c.y = c.z
38+
39+
y.y = y.x
40+
]])
41+
end)
42+
43+
it("functions calls using a table as a whole end tracking of that table", function()
44+
assert_warnings({
45+
{code = "315", line = 31, column = 3, end_column = 3, name = 'y', field = 'y', set_is_nil = ''},
46+
{code = "325", line = 31, column = 9, end_column = 9, name = 'y', field = 'x'},
47+
}, [[
48+
local y = {}
49+
50+
local x = {}
51+
x.y = 1
52+
x.y = print(x)
53+
x.y = x.z
54+
55+
local a = {}
56+
a.y = 1
57+
function a:func() print(self) end
58+
a[print(a)] = 1
59+
a.y = a.z
60+
61+
local b = {}
62+
b.y = 1
63+
function b:func() print(self) return 1 end
64+
b[1] = a[print(b)]
65+
b.y = b.z
66+
67+
local c = {}
68+
c.y = 1
69+
function c:func() print(self) return 1 end
70+
print(c)
71+
c.y = c.z
72+
73+
local d = {}
74+
d.y = 1
75+
print(print(d))
76+
d.y = d.z
77+
78+
y.y = y.x
79+
]])
80+
end)
81+
82+
it("functions calls using a table field access only that field", function()
83+
assert_warnings({
84+
{code = "315", line = 1, column = 14, end_column = 14, name = 'x', field = 2, set_is_nil = ''},
85+
}, [[
86+
local x = {1,1}
87+
print(x[1])
88+
x[1], x[2] = 1, 2
89+
90+
local y = {}
91+
function y.func(var) print(var) return 1 end
92+
y[1] = y.func(x[1])
93+
x[1] = 1
94+
y[2] = y:func(x[1])
95+
x[1] = 1
96+
97+
return x, y
98+
]])
99+
end)
100+
101+
it("functions calls stop checking for tables accessed as upvalues", function()
102+
assert_warnings({
103+
{code = "315", line = 27, column = 3, end_column = 3, name = 'y', field = 'y', set_is_nil = ''},
104+
}, [[
105+
local y = {}
106+
107+
local x = {}
108+
x.y = 1
109+
function x.func() print(x) end
110+
x.y = x.func()
111+
x.y = x.z
112+
113+
local a = {}
114+
a.y = 1
115+
function a.func() print(a) end
116+
a[a.func()] = 1
117+
a.y = a.z
118+
119+
local b = {}
120+
b.y = 1
121+
function glob_func() print(b) return 1 end
122+
b[1] = a[glob_func()]
123+
b.y = b.z
124+
125+
local c = {}
126+
c.y = 1
127+
local function func() print(c) return 1 end
128+
func()
129+
c.y = c.z
130+
131+
y.y = 1
132+
133+
return x, a, b, c
134+
]])
135+
end)
136+
137+
it("handles multiple layers of nested function calls correctly", function()
138+
assert_warnings({
139+
{code = "315", line = 2, column = 3, end_column = 3, name = 'x', field = 'y', set_is_nil = ''},
140+
}, [[
141+
local x = {...}
142+
x.y = x[2]
143+
]])
144+
end)
145+
146+
it("assumes that parameters and upvalues have all keys accessed or written to on a function call", function()
147+
assert_warnings({
148+
}, [[
149+
local function other_func() end
150+
local x = {}
151+
function func(t)
152+
t = {1}
153+
x = {1}
154+
other_func()
155+
t[2] = t[2]
156+
x[2] = x[2]
157+
end
158+
]])
159+
end)
160+
161+
it("stop tracking for tables passed externally", function()
162+
assert_warnings({}, [[
163+
local t
164+
local function func1(var)
165+
t = var
166+
end
167+
local x = {}
168+
func1(x)
169+
x[1] = 1
170+
print(t[1])
171+
172+
local z = {}
173+
local a
174+
function z:func() a = self end
175+
z:func()
176+
z[1] = 1
177+
print(a[1])
178+
179+
local y
180+
local function func2() return y end
181+
function func3()
182+
y = {}
183+
local t = func2()
184+
y[1] = 1
185+
print(t[1])
186+
end
187+
]])
188+
end)
189+
190+
it("continues analysis through a function call", function()
191+
assert_warnings({
192+
{line = 3, column = 12, name = 'x', end_column = 15, field = 'oops', code = '325', },
193+
}, [[
194+
local x = {}
195+
print("Meh")
196+
print(x.oops)
197+
]])
198+
end)
199+
end)

0 commit comments

Comments
 (0)