From 1001811eca92496e1b4d533c72587db5cf65a148 Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Sun, 16 Jul 2023 17:21:43 -0300 Subject: [PATCH] fix: when parsing vararg arguments, don't pollute base types Fixes #679. --- spec/declaration/local_function_spec.lua | 9 ++++++- tl.lua | 32 +++++++++++----------- tl.tl | 34 +++++++++++++----------- 3 files changed, 43 insertions(+), 32 deletions(-) diff --git a/spec/declaration/local_function_spec.lua b/spec/declaration/local_function_spec.lua index a6e168131..49bda904f 100644 --- a/spec/declaration/local_function_spec.lua +++ b/spec/declaration/local_function_spec.lua @@ -102,6 +102,7 @@ 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 @@ -109,8 +110,14 @@ describe("local function", function() 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([[ diff --git a/tl.lua b/tl.lua index f0038cc92..e4d28da07 100644 --- a/tl.lua +++ b/tl.lua @@ -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 @@ -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 diff --git a/tl.tl b/tl.tl index fabd0aff8..6c39e27e3 100644 --- a/tl.tl +++ b/tl.tl @@ -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 @@ -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