From 07b97fc27db08897210d8b102a1c3b15122a73f0 Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Mon, 21 Oct 2024 14:50:03 -0300 Subject: [PATCH] improve error message when 'local type' is needed --- spec/lang/stdlib/require_spec.lua | 5 ++++- tl.lua | 25 +++++++++++++++---------- tl.tl | 25 +++++++++++++++---------- 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/spec/lang/stdlib/require_spec.lua b/spec/lang/stdlib/require_spec.lua index 83084a1f..0f3ba4ad 100644 --- a/spec/lang/stdlib/require_spec.lua +++ b/spec/lang/stdlib/require_spec.lua @@ -1320,8 +1320,11 @@ describe("require", function() assert.same({}, result.syntax_errors) assert.same({ - { filename = "main.tl", x = 33, y = 1, msg = "module type is abstract; use a 'type' declaration" } + { filename = "main.tl", x = 33, y = 1, msg = "module type is abstract: interface IFoo" } }, result.type_errors) + assert.same({ + { filename = "main.tl", x = 19, y = 1, msg = "hint: consider using 'local type' instead", tag = "hint" } + }, result.warnings) end) it("a module returning an interface instance can be required concretely (#825)", function () diff --git a/tl.lua b/tl.lua index c8728a40..54a5f512 100644 --- a/tl.lua +++ b/tl.lua @@ -6341,16 +6341,19 @@ function Errors:add_prefixing(w, src, prefix, dst) end end -local function ensure_not_abstract(t) +local function ensure_not_abstract(t, node) if t.typename == "function" and t.macroexp then return nil, "macroexps are abstract; consider using a concrete function" elseif t.typename == "typedecl" then local def = t.def - if def.typename == "interface" then + if def.typename == "record" then + return true + elseif node and node_is_require_call(node) then + return nil, "module type is abstract: " .. tostring(t.def) + elseif def.typename == "interface" then return nil, "interfaces are abstract; consider using a concrete record" - elseif not (def.typename == "record") then - return nil, "cannot use a type definition as a concrete value" end + return nil, "cannot use a type definition as a concrete value" end return true end @@ -11369,6 +11372,9 @@ self:expand_type(node, values, elements) }) assert(var) self:add_var(var, var.tk, t, var.attribute, is_localizing_a_variable(node, i) and "localizing") + if var.elide_type then + self.errs:add_warning("hint", node, "hint: consider using 'local type' instead") + end local infertype = infertypes.tuple[i] if ok and infertype then @@ -11407,6 +11413,9 @@ self:expand_type(node, values, elements) }) end self:add_global(var, var.tk, t, is_inferred) + if var.elide_type then + self.errs:add_warning("hint", node, "hint: consider using 'global type' instead") + end self:dismiss_unresolved(var.tk) end @@ -11727,13 +11736,9 @@ self:expand_type(node, values, elements) }) tuple = flatten_tuple(tuple) for i, t in ipairs(tuple.tuple) do - local ok, err = ensure_not_abstract(t) + local ok, err = ensure_not_abstract(t, node[i]) if not ok then - if node_is_require_call(node[i]) then - self.errs:add(node[i], "module type is abstract; use a 'type' declaration") - else - self.errs:add(node[i], err) - end + self.errs:add(node[i], err) end end diff --git a/tl.tl b/tl.tl index cb445db6..a8bf9397 100644 --- a/tl.tl +++ b/tl.tl @@ -6341,16 +6341,19 @@ function Errors:add_prefixing(w: Where, src: {Error}, prefix: string, dst?: {Err end end -local function ensure_not_abstract(t: Type): boolean, string +local function ensure_not_abstract(t: Type, node?: Node): boolean, string if t is FunctionType and t.macroexp then return nil, "macroexps are abstract; consider using a concrete function" elseif t is TypeDeclType then local def = t.def - if def is InterfaceType then + if def is RecordType then + return true + elseif node and node_is_require_call(node) then + return nil, "module type is abstract: " .. tostring(t.def) + elseif def is InterfaceType then return nil, "interfaces are abstract; consider using a concrete record" - elseif not def is RecordType then - return nil, "cannot use a type definition as a concrete value" end + return nil, "cannot use a type definition as a concrete value" end return true end @@ -11369,6 +11372,9 @@ do assert(var) self:add_var(var, var.tk, t, var.attribute, is_localizing_a_variable(node, i) and "localizing") + if var.elide_type then + self.errs:add_warning("hint", node, "hint: consider using 'local type' instead") + end local infertype = infertypes.tuple[i] if ok and infertype then @@ -11407,6 +11413,9 @@ do end self:add_global(var, var.tk, t, is_inferred) + if var.elide_type then + self.errs:add_warning("hint", node, "hint: consider using 'global type' instead") + end self:dismiss_unresolved(var.tk) end @@ -11727,13 +11736,9 @@ do tuple = flatten_tuple(tuple) for i, t in ipairs(tuple.tuple) do - local ok, err = ensure_not_abstract(t) + local ok, err = ensure_not_abstract(t, node[i]) if not ok then - if node_is_require_call(node[i]) then - self.errs:add(node[i], "module type is abstract; use a 'type' declaration") - else - self.errs:add(node[i], err) - end + self.errs:add(node[i], err) end end