From b80b7afa2d3995024e0fa8c68cc4e3275441b2de Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Sat, 16 Nov 2024 17:48:23 -0300 Subject: [PATCH] fix: make get_types report all inherited interface fields Fixes #852. --- spec/api/get_types_spec.lua | 132 ++++++++++++++++++++++++++++++++++++ tl.lua | 6 ++ tl.tl | 6 ++ 3 files changed, 144 insertions(+) diff --git a/spec/api/get_types_spec.lua b/spec/api/get_types_spec.lua index 7b91ef91f..2c5b2c6bf 100644 --- a/spec/api/get_types_spec.lua +++ b/spec/api/get_types_spec.lua @@ -62,6 +62,138 @@ describe("tl.get_types", function() assert.same(fields, {"init", "x", "y"}) end) + it("reports inherited interface fields in record field list, case 1 (#852)", function() + local env = tl.init_env() + env.report_types = true + local result = assert(tl.check_string([[ + local interface IFoo + bar: function(self) + end + + local record Foo is IFoo + -- Uncommenting this causes 'bar' to be hidden from fields of Foo + qux:function(Foo) + + -- Using this style doesn't have this problem + -- qux:function(self) + end + + function Foo:bar() + end + + function Foo:qux() + end + + local record Runner + foo: Foo + end + + function Runner:run() + -- self.foo. + end + ]], env)) + + local tr, trenv = tl.get_types(result) + local y = 5 + local x = 10 + local type_at_y_x = tr.by_pos[""][y][x] + assert(tr.types[type_at_y_x].str == "Foo") + local fields = {} + for k, _ in pairs(tr.types[type_at_y_x].fields) do + table.insert(fields, k) + end + table.sort(fields) + assert.same(fields, {"bar", "qux"}) + end) + + it("reports inherited interface fields in record field list, case 2 (#852)", function() + local env = tl.init_env() + env.report_types = true + local result = assert(tl.check_string([[ + local interface IFoo + bar: function(self) + end + + local record Foo is IFoo + -- Uncommenting this causes 'bar' to be hidden from fields of Foo + -- qux:function(Foo) + + -- Using this style doesn't have this problem + qux:function(self) + end + + function Foo:bar() + end + + function Foo:qux() + end + + local record Runner + foo: Foo + end + + function Runner:run() + -- self.foo. + end + ]], env)) + + local tr, trenv = tl.get_types(result) + local y = 5 + local x = 10 + local type_at_y_x = tr.by_pos[""][y][x] + assert(tr.types[type_at_y_x].str == "Foo") + local fields = {} + for k, _ in pairs(tr.types[type_at_y_x].fields) do + table.insert(fields, k) + end + table.sort(fields) + assert.same(fields, {"bar", "qux"}) + end) + + it("reports inherited interface fields in record field list, case 3 (#852)", function() + local env = tl.init_env() + env.report_types = true + local result = assert(tl.check_string([[ + local interface IFoo + bar: function(self) + end + + local record Foo is IFoo + -- Uncommenting this causes 'bar' to be hidden from fields of Foo + -- qux:function(Foo) + + -- Using this style doesn't have this problem + -- qux:function(self) + end + + function Foo:bar() + end + + function Foo:qux() + end + + local record Runner + foo: Foo + end + + function Runner:run() + -- self.foo. + end + ]], env)) + + local tr, trenv = tl.get_types(result) + local y = 5 + local x = 10 + local type_at_y_x = tr.by_pos[""][y][x] + assert(tr.types[type_at_y_x].str == "Foo") + local fields = {} + for k, _ in pairs(tr.types[type_at_y_x].fields) do + table.insert(fields, k) + end + table.sort(fields) + assert.same(fields, {"bar", "qux"}) + end) + it("reports reference of a nominal type", function() local env = tl.init_env() env.report_types = true diff --git a/tl.lua b/tl.lua index 0a42c65de..f7a3da1ca 100644 --- a/tl.lua +++ b/tl.lua @@ -13043,6 +13043,12 @@ self:expand_type(node, values, elements) }) if typ.interface_list then self:expand_interfaces(typ) + + if self.collector then + for fname, ftype in fields_of(typ) do + self.env.reporter:add_field(typ, fname, ftype) + end + end end if fmacros then diff --git a/tl.tl b/tl.tl index d3671b30f..a243e0289 100644 --- a/tl.tl +++ b/tl.tl @@ -13043,6 +13043,12 @@ do if typ.interface_list then self:expand_interfaces(typ) + + if self.collector then + for fname, ftype in fields_of(typ) do + self.env.reporter:add_field(typ, fname, ftype) + end + end end if fmacros then