Skip to content

Commit 8898599

Browse files
committed
some bug fixes
1 parent a46162e commit 8898599

17 files changed

Lines changed: 527 additions & 90 deletions

nattlua/analyzer/control_flow.lua

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,17 @@ return function(META--[[#: any]])
266266
for _, a in ipairs(tracked_from_scope) do
267267
for _, b in ipairs(current_tracked) do
268268
if
269-
(a.kind == "upvalue" and b.kind == "upvalue" and a.upvalue == b.upvalue) or
270-
(a.kind == "table" and b.kind == "table" and a.obj == b.obj)
269+
(
270+
a.kind == "upvalue" and
271+
b.kind == "upvalue" and
272+
a.upvalue == b.upvalue
273+
)
274+
or
275+
(
276+
a.kind == "table" and
277+
b.kind == "table" and
278+
a.obj == b.obj
279+
)
271280
then
272281
table_insert(tracked_objects, a)
273282
end
@@ -282,11 +291,7 @@ return function(META--[[#: any]])
282291
end
283292

284293
self:PushScope(function_scope)
285-
self:ApplyMutationsAfterStatement(
286-
frame.scope,
287-
true,
288-
frame.scope:GetTrackedNarrowings()
289-
)
294+
self:ApplyMutationsAfterStatement(frame.scope, true, frame.scope:GetTrackedNarrowings())
290295
self:PopScope()
291296
end
292297
end

nattlua/analyzer/mutation_tracking.lua

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,9 @@ return function(META--[[#: any]])
225225
if self:IsInvertedExpressionContext() then
226226
if self:IsFalsyExpressionContext() then
227227
local val = stack[#stack].falsy
228+
228229
if set_upvalue_fn then set_upvalue_fn(val) end
230+
229231
return val
230232
elseif self:IsTruthyExpressionContext() then
231233
local union = stack[#stack].truthy
@@ -239,12 +241,15 @@ return function(META--[[#: any]])
239241
end
240242

241243
if set_upvalue_fn then set_upvalue_fn(union) end
244+
242245
return union
243246
end
244247
else
245248
if self:IsTruthyExpressionContext() then
246249
local val = stack[#stack].truthy
250+
247251
if set_upvalue_fn then set_upvalue_fn(val) end
252+
248253
return val
249254
elseif self:IsFalsyExpressionContext() then
250255
local union = stack[#stack].falsy
@@ -258,6 +263,7 @@ return function(META--[[#: any]])
258263
end
259264

260265
if set_upvalue_fn then set_upvalue_fn(union) end
266+
261267
return union
262268
end
263269
end
@@ -428,7 +434,10 @@ return function(META--[[#: any]])
428434

429435
-- stack is needed to simply track upvalues used, even if they were not mutated for warnings
430436
if upvalue then
431-
table.insert(objects, {kind = "upvalue", upvalue = upvalue, stack = stack and shallow_copy(stack)})
437+
table.insert(
438+
objects,
439+
{kind = "upvalue", upvalue = upvalue, stack = stack and shallow_copy(stack)}
440+
)
432441
end
433442
elseif data.kind == "table" then
434443
if data.stack then

nattlua/analyzer/operators/binary.lua

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -337,9 +337,10 @@ local function BinaryWithUnion(self, node, l, r, op)
337337

338338
if upvalue then upvalue:SetTruthyFalsyUnion(truthy_union, falsy_union) end
339339

340-
-- Store truthy/falsy on the left value for table field narrowing
341-
-- through stored checks (e.g., local check = t.x ~= nil; if check then)
342-
if l:GetParentTable() then l:SetStoredTruthyFalsy(truthy_union, falsy_union) end
340+
-- Store truthy/falsy on the left value for table field narrowing
341+
-- through stored checks (e.g., local check = t.x ~= nil; if check then)
342+
if l:GetParentTable() then l:SetStoredTruthyFalsy(truthy_union, falsy_union) end
343+
343344
-- special case for type(x) ==/~=
344345
if self.type_checked and (op == "==" or op == "!=" or op == "~=") then
345346
local type_checked = self.type_checked
@@ -468,19 +469,12 @@ local function is_condition_expression(node)
468469

469470
local pt = parent.Type
470471

471-
if
472-
pt == "statement_if" or
473-
pt == "statement_while" or
474-
pt == "statement_repeat"
475-
then
472+
if pt == "statement_if" or pt == "statement_while" or pt == "statement_repeat" then
476473
return true
477474
end
478475

479476
-- keep walking up through nested binary/prefix operators
480-
if
481-
pt == "expression_binary_operator" or
482-
pt == "expression_prefix_operator"
483-
then
477+
if pt == "expression_binary_operator" or pt == "expression_prefix_operator" then
484478
n = parent
485479
else
486480
break

nattlua/analyzer/operators/function_call_body.lua

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,32 @@ return function(self, obj, input)
370370
function_node.Type == "statement_type_function"
371371
then
372372
if function_node.self_call then
373-
self:CreateLocalValue("self", input:GetWithNumber(1) or Nil())
373+
local arg = input:GetWithNumber(1) or Nil()
374+
self:CreateLocalValue("self", arg)
375+
376+
-- if the meta table has @SelfArgument, also create a typesystem self
377+
-- so that --[[#type]] blocks see the contract type instead of the concrete value
378+
if not is_type_function and arg.Type == "table" then
379+
local self_arg_contract
380+
381+
if arg:GetSelfArgument() then
382+
self_arg_contract = arg:GetSelfArgument()
383+
elseif arg:GetMetaTable() and arg:GetMetaTable().Type == "table" then
384+
self_arg_contract = arg:GetMetaTable():GetSelfArgument()
385+
end
386+
387+
if self_arg_contract then
388+
-- make @SelfArgument accessible from the contract itself
389+
-- so that self.@SelfArgument works in typesystem blocks
390+
if not self_arg_contract:GetSelfArgument() then
391+
self_arg_contract:SetSelfArgument(self_arg_contract)
392+
end
393+
394+
self:PushAnalyzerEnvironment("typesystem")
395+
self:CreateLocalValue("self", self_arg_contract)
396+
self:PopAnalyzerEnvironment()
397+
end
398+
end
374399
end
375400
end
376401

@@ -385,12 +410,9 @@ return function(self, obj, input)
385410
for i, identifier in ipairs(function_node.identifiers_typesystem) do
386411
local generic_expression = call_expression.expressions_typesystem and
387412
call_expression.expressions_typesystem[i] or
388-
nil
389-
390-
if generic_expression then
391-
local T = self:AnalyzeExpression(generic_expression)
392-
self:CreateLocalValue(identifier.value:GetValueString(), T)
393-
end
413+
identifier
414+
local T = self:AnalyzeExpression(generic_expression)
415+
self:CreateLocalValue(identifier.value:GetValueString(), T)
394416
end
395417
end
396418

nattlua/analyzer/operators/prefix.lua

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,20 @@ local function Prefix(analyzer, node, r)
8383

8484
if res then return res end
8585

86-
return r:GetArrayLength()
86+
local len = r:GetArrayLength()
87+
88+
if len.Type == "number" or len.Type == "range" then
89+
-- Tag with the contract table if it exists, since Get() is called on the contract
90+
local contract = r:GetContract()
91+
92+
if contract and contract ~= r and contract.Type == "table" then
93+
len.LengthSourceTable = contract
94+
else
95+
len.LengthSourceTable = r
96+
end
97+
end
98+
99+
return len
87100
elseif r.Type == "string" then
88101
local str = r:GetData()
89102

@@ -209,7 +222,6 @@ return {
209222
end
210223

211224
analyzer:TrackUpvalueUnion(r, truthy_union, falsy_union)
212-
213225
return new_union
214226
end
215227

nattlua/analyzer/statements/numeric_for.lua

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ return {
112112
init = LNumber(literal_init)
113113
end
114114

115+
-- Carry the LengthSourceTable tag from max to the range
116+
if init.Type == "range" and max.LengthSourceTable then
117+
init.LengthSourceTable = max.LengthSourceTable
118+
end
119+
115120
if init.Type == "number" then init:SetDontWiden(true) end
116121
else
117122
if init:IsNumeric() and max:IsNumeric() then

nattlua/definitions/lua/globals.nlua

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -502,9 +502,7 @@ analyzer function setmetatable(tbl: Table, meta: Table | nil)
502502
end
503503
end
504504

505-
if meta.Type == "table" then
506-
tbl:SetMetaTable(meta)
507-
end
505+
if meta.Type == "table" then tbl:SetMetaTable(meta) end
508506

509507
return tbl
510508
end

nattlua/types/number.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ end
127127
function META:Copy()--[[#: TNumber]]
128128
local copy = self.New(self.Data)
129129
copy:CopyInternalsFrom(self)
130+
copy.LengthSourceTable = self.LengthSourceTable
130131
return copy
131132
end
132133

nattlua/types/range.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ end
9797
function META:Copy()
9898
local copy = LNumberRange(self:GetMin(), self:GetMax())
9999
copy:CopyInternalsFrom(self)
100+
copy.LengthSourceTable = self.LengthSourceTable
100101
return copy
101102
end
102103

nattlua/types/table.lua

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -909,7 +909,11 @@ function META:Get(key--[[#: TBaseType]])--[[#: (TBaseType | false), (any | nil)]
909909
local len = math_abs(min - max)
910910

911911
if len == math_huge or len == -math_huge then
912-
union:AddType(Nil())
912+
-- Check if this range key came from #self (tagged by the analyzer)
913+
local source = key.LengthSourceTable
914+
local is_bounded = source and source == self
915+
916+
if not is_bounded then union:AddType(Nil()) end
913917

914918
for _, keyval in ipairs(self.Data) do
915919
if keyval.key.Type == "number" then union:AddType(keyval.val) end

0 commit comments

Comments
 (0)