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