Skip to content

Commit

Permalink
fix: when parsing vararg arguments, don't pollute base types
Browse files Browse the repository at this point in the history
Fixes #679.
  • Loading branch information
hishamhm committed Jul 16, 2023
1 parent 54ef78e commit 1001811
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 32 deletions.
9 changes: 8 additions & 1 deletion spec/declaration/local_function_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -102,15 +102,22 @@ describe("local function", function()

it("vararg can only be the last argument", util.check_syntax_error([[
local f: function(...: string, number): boolean
local g: function(string, string..., number): boolean
f = function(...: string, a: number): boolean
return #select(1, ...) == a
end
local ok = f(3, "abc")
]], {
{ y = 1, msg = "'...' can only be last argument" },
{ y = 3, msg = "'...' can only be last argument" },
{ y = 2, msg = "'...' can only be last argument" },
{ y = 4, msg = "'...' can only be last argument" },
}))

it("does not get multiple uses of base types confused (regression test for #679)", util.check([[
local f: function(x: number, s: string, string...): boolean
local g: function(x: number, s: string, ...: string): boolean
]]))
end)

it("declaration", util.check([[
Expand Down
32 changes: 17 additions & 15 deletions tl.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2324,6 +2324,12 @@ parse_argument_list = function(ps, i)
return i, node
end







local function parse_argument_type(ps, i)
local is_va = false
local argument_name = nil
Expand All @@ -2345,33 +2351,29 @@ local function parse_argument_type(ps, i)
i = i + 1
is_va = true
end

typ.is_va = is_va
end

if argument_name == "self" then
typ.is_self = true
end

return i, typ, 0
return i, { i = i, type = typ, is_va = is_va }, 0
end

parse_argument_type_list = function(ps, i)
local tvs = {}
i = parse_bracket_list(ps, i, tvs, "(", ")", "sep", parse_argument_type)
local list = new_type(ps, i, "tuple")
i = parse_bracket_list(ps, i, list, "(", ")", "sep", parse_argument_type)

if list[#list] then
for l = 1, #list - 1 do
if list[l].is_va then
list[l].is_va = nil
fail(ps, i, "'...' can only be last argument")
end
end
if list[#list].is_va then
list[#list].is_va = nil
list.is_va = true
local n = #tvs
for l, tv in ipairs(tvs) do
list[l] = tv.type
if tv.is_va and l < n then
fail(ps, tv.i, "'...' can only be last argument")
end
end
if tvs[n] and tvs[n].is_va then
list.is_va = true
end
return i, list
end

Expand Down
34 changes: 18 additions & 16 deletions tl.tl
Original file line number Diff line number Diff line change
Expand Up @@ -2324,7 +2324,13 @@ parse_argument_list = function(ps: ParseState, i: integer): integer, Node
return i, node
end

local function parse_argument_type(ps: ParseState, i: integer): integer, Type, integer
local record TypeAndVararg
i: integer
type: Type
is_va: boolean
end

local function parse_argument_type(ps: ParseState, i: integer): integer, TypeAndVararg, integer
local is_va = false
local argument_name: string = nil
if ps.tokens[i].kind == "identifier" and ps.tokens[i + 1].tk == ":" then
Expand All @@ -2345,33 +2351,29 @@ local function parse_argument_type(ps: ParseState, i: integer): integer, Type, i
i = i + 1
is_va = true
end
-- HACK: we're storing the attribute in the wrong node during the iteration...
typ.is_va = is_va
end

if argument_name == "self" then
typ.is_self = true
end

return i, typ, 0
return i, { i = i, type = typ, is_va = is_va }, 0
end

parse_argument_type_list = function(ps: ParseState, i: integer): integer, Type
local tvs: {TypeAndVararg} = {}
i = parse_bracket_list(ps, i, tvs, "(", ")", "sep", parse_argument_type)
local list = new_type(ps, i, "tuple")
i = parse_bracket_list(ps, i, list, "(", ")", "sep", parse_argument_type)
-- HACK: ...and then cleaning it up and setting in the right node
if list[#list] then
for l = 1, #list - 1 do
if list[l].is_va then
list[l].is_va = nil
fail(ps, i, "'...' can only be last argument")
end
end
if list[#list].is_va then
list[#list].is_va = nil
list.is_va = true
local n = #tvs
for l, tv in ipairs(tvs) do
list[l] = tv.type
if tv.is_va and l < n then
fail(ps, tv.i, "'...' can only be last argument")
end
end
if tvs[n] and tvs[n].is_va then
list.is_va = true
end
return i, list
end

Expand Down

0 comments on commit 1001811

Please sign in to comment.