Skip to content

Commit e07e197

Browse files
committed
when calling or indexing a union with nil, error and act as if the nil value doesn't exist
1 parent e99be62 commit e07e197

File tree

5 files changed

+100
-43
lines changed

5 files changed

+100
-43
lines changed
Lines changed: 58 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,45 @@
11
local table = _G.table
22
local NormalizeTuples = require("nattlua.types.tuple").NormalizeTuples
3+
local Union = require("nattlua.types.union").Union
34
local Tuple = require("nattlua.types.tuple").Tuple
45
local Nil = require("nattlua.types.symbol").Nil
56
local AnalyzeImport = require("nattlua.analyzer.expressions.import").AnalyzeImport
7+
8+
local function postfix_call(self, self_arg, node, callable)
9+
local types = {self_arg}
10+
self:AnalyzeExpressions(node.expressions, types)
11+
12+
local arguments
13+
14+
if self:IsTypesystem() then
15+
if
16+
#types == 1 and
17+
types[1].Type == "tuple" and
18+
callable:GetInputSignature():GetTupleLength() == math.huge
19+
then
20+
arguments = types[1]
21+
else
22+
arguments = Tuple(types)
23+
end
24+
else
25+
arguments = NormalizeTuples(types)
26+
end
27+
28+
self:PushCurrentExpression(node)
29+
local ret, err = self:Call(callable, arguments, node)
30+
self:PopCurrentExpression()
31+
32+
if not ret then
33+
self:Error(err)
34+
35+
if callable.Type == "function" and callable:IsExplicitOutputSignature() then
36+
return callable:GetOutputSignature():Copy()
37+
end
38+
end
39+
40+
return ret, err
41+
end
42+
643
return {
744
AnalyzePostfixCall = function(self, node)
845
if
@@ -13,6 +50,7 @@ return {
1350
return AnalyzeImport(self, node, node.left.value.value == "require" and node.path)
1451
end
1552

53+
1654
local is_type_call = node.type_call or
1755
node.left and
1856
(
@@ -32,54 +70,44 @@ return {
3270

3371
if self:IsRuntime() then self_arg = self_arg:GetFirstValue() end
3472
end
73+
local returned_tuple
3574

36-
local types = {self_arg}
37-
self:AnalyzeExpressions(node.expressions, types)
38-
local arguments
75+
if self_arg and self_arg.Type == "union" then
76+
for _, self_arg in ipairs(self_arg:GetData()) do
77+
local tup = postfix_call(self, self_arg, node, callable)
3978

40-
if self:IsTypesystem() then
41-
if
42-
#types == 1 and
43-
types[1].Type == "tuple" and
44-
callable:GetInputSignature():GetTupleLength() == math.huge
45-
then
46-
arguments = types[1]
47-
else
48-
arguments = Tuple(types)
79+
if tup then
80+
local s = tup:GetFirstValue()
81+
if s and not s:IsEmpty() then
82+
if returned_tuple then
83+
returned_tuple:AddType(s)
84+
end
85+
returned_tuple = returned_tuple or Union({s})
86+
end
87+
end
4988
end
50-
else
51-
arguments = NormalizeTuples(types)
5289
end
5390

54-
self:PushCurrentExpression(node)
55-
local ret, err = self:Call(callable, arguments, node)
56-
self:PopCurrentExpression()
57-
local returned_tuple
58-
59-
if not ret then
60-
self:Error(err)
61-
62-
if callable.Type == "function" and callable:IsExplicitOutputSignature() then
63-
returned_tuple = callable:GetOutputSignature():Copy()
64-
else
65-
returned_tuple = Tuple({Nil()})
91+
if not returned_tuple then
92+
local val, err = postfix_call(self, self_arg, node, callable)
93+
if not val then
94+
return val, err
6695
end
67-
else
68-
returned_tuple = ret
96+
returned_tuple = val
6997
end
98+
self:PopAnalyzerEnvironment()
7099

71100
-- TUPLE UNPACK MESS
72101
if node.tokens["("] and node.tokens[")"] and returned_tuple.Type == "tuple" then
73102
returned_tuple = returned_tuple:GetWithNumber(1)
74103
end
75104

76105
if self:IsTypesystem() then
77-
if returned_tuple.Type == "tuple" and returned_tuple:HasOneValue() then
106+
if returned_tuple and returned_tuple.Type == "tuple" and returned_tuple:HasOneValue() then
78107
returned_tuple = returned_tuple:GetWithNumber(1)
79108
end
80109
end
81110

82-
self:PopAnalyzerEnvironment()
83111
return returned_tuple
84112
end,
85113
}

nattlua/analyzer/operators/call.lua

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,17 +81,19 @@ local function union_call(self, analyzer, input, call_node)
8181
if recursively_called then return recursively_called end
8282
end
8383

84-
local ok, err = analyzer:Call(obj, input:Copy(), call_node, true)
85-
local val = analyzer:Assert(ok, err)
86-
87-
-- TODO
88-
if val.Type == "tuple" and val:HasOneValue() then
89-
val = val:Unpack(1)
90-
elseif val.Type == "union" and val:GetMinimumLength() == 1 then
91-
val = val:GetAtTupleIndex(1)
92-
end
84+
local val, err = analyzer:Call(obj, input:Copy(), call_node, true)
85+
86+
if not val then
87+
analyzer:Error(err)
88+
else
89+
if val.Type == "tuple" and val:HasOneValue() then
90+
val = val:Unpack(1)
91+
elseif val.Type == "union" and val:GetMinimumLength() == 1 then
92+
val = val:GetAtTupleIndex(1)
93+
end
9394

94-
new:AddType(val)
95+
new:AddType(val)
96+
end
9597

9698
if obj.Type == "function" then analyzer:PopCallFrame() end
9799
end

nattlua/analyzer/operators/index.lua

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,8 @@ local function index_union(analyzer, obj, key)
135135
else
136136
local val, err = analyzer:IndexOperator(obj, key)
137137

138-
if not val then return val, err end
139138

140-
union:AddType(val)
139+
if not val then analyzer:Error(err) else union:AddType(val) end
141140
end
142141
end
143142

nattlua/definitions/lua/globals.nlua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ type _ = any
44
type @Name = "_G"
55
type rawlen = function=(v: Table | string)>(number)
66
type rawequal = function=(v1: any, v2: any)>(boolean)
7-
type collectgarbage = function=(opt: string, arg: number)>(...) | function=(opt: string)>(...) | function=()>(...)
7+
type collectgarbage = function=(opt: string, arg: number)>(...: any) | function=(opt: string)>(...: any) | function=()>(...: any)
88

99
analyzer function type_print(...: ...any)
1010
print(...)

test/tests/nattlua/analyzer/analyzer.lua

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,3 +706,31 @@ attest.equal(-x, 42)
706706
attest.equal(~x, 43)
707707
attest.equal(#x, 44)
708708
]]
709+
analyze[[
710+
local x = _ as nil | function=()>(1)
711+
attest.expect_diagnostic<|"error", "uncallable object nil"|>
712+
local res = x()
713+
attest.equal(res, 1)
714+
]]
715+
analyze[[
716+
717+
local x = _ as {foo = nil | function=()>(1)}
718+
attest.expect_diagnostic<|"error", "uncallable object nil"|>
719+
local res = x.foo()
720+
attest.equal(res, 1)
721+
722+
]]
723+
analyze[[
724+
725+
local x = _ as {foo = nil | function=(self: self)>(1)}
726+
attest.expect_diagnostic<|"error", "uncallable object nil"|>
727+
local res = x:foo()
728+
attest.equal(res, 1)
729+
]]
730+
analyze[[
731+
local x = io.popen("")
732+
attest.expect_diagnostic("error", "nil is not a subset of")
733+
attest.expect_diagnostic("error", "on type symbol")
734+
local y = x:read("*all")
735+
attest.equal(y, _ as nil | string)
736+
]]

0 commit comments

Comments
 (0)