Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: interface instances can be required concretely #832

Merged
merged 5 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions spec/lang/code_gen/local_type_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -239,4 +239,43 @@ describe("local type code generation", function()
]])
end)

it("always elides local type require used as a variable, even if incorrect use of interfaces or aliases", util.gen([[
local interface IFoo
end

local type Alias = IFoo

local record Foo is IFoo
end

local function register(_id:any, _value:any)
end

local foo:Foo

register(IFoo, foo)

register(Alias, foo)
]], [[








local function register(_id, _value)
end

local foo

register(IFoo, foo)

register(Alias, foo)
]], nil, {
{ y = 14, msg = "interfaces are abstract" },
{ y = 16, msg = "interfaces are abstract" },
}))

end)
58 changes: 57 additions & 1 deletion spec/lang/stdlib/require_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1216,7 +1216,7 @@ describe("require", function()

assert.same({}, result.syntax_errors)
assert.same({
{ filename = "main.tl", x = 37, y = 1, msg = "type is not a record" },
{ filename = "main.tl", x = 30, y = 1, msg = "'require' did not return a type, got integer" },
{ filename = "main.tl", x = 45, y = 1, msg = "cannot index key 'Foo' in type integer" },
}, result.type_errors)
end)
Expand Down Expand Up @@ -1296,4 +1296,60 @@ describe("require", function()
assert.same({}, result.type_errors)
end)

it("a module returning an interface gives a helpful error message (#829)", function ()
util.mock_io(finally, {
["ifoo.tl"] = [[
local interface IFoo
bar: function(self)
end

return IFoo
]],
["main.tl"] = [[
local IFoo = require("ifoo")

local record Foo is IFoo
end

function Foo:bar()
print("bar")
end
]],
})
local result, err = tl.process("main.tl")

assert.same({}, result.syntax_errors)
assert.same({
{ 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 ()
util.mock_io(finally, {
["foo.tl"] = [[
local interface IFoo
bar: function(self)
end

local foo: IFoo = {}

return foo
]],
["main.tl"] = [[
local foo = require("foo")

foo.bar = function(self)
print("bar")
end
]],
})
local result, err = tl.process("main.tl")

assert.same({}, result.syntax_errors)
assert.same({}, result.type_errors)
end)

end)
17 changes: 13 additions & 4 deletions spec/util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -599,13 +599,22 @@ function util.check_types(code, types)
end
end

local function gen(lax, code, expected, gen_target)
local function gen(lax, code, expected, gen_target, type_errors)
return function()
local ast, syntax_errors = tl.parse(code, "foo.tl")
assert.same({}, syntax_errors, "Code was not expected to have syntax errors")
local gen_compat = gen_target == "5.4" and "off" or nil
local result = tl.check(ast, "foo.tl", { feat_lax = lax and "on" or "off", gen_target = gen_target, gen_compat = gen_compat })
assert.same({}, result.type_errors)

if type_errors then
local batch = batch_assertions()
local result_type_errors = combine_result(result, "type_errors")
batch_compare(batch, "type errors", type_errors, result_type_errors)
batch:assert()
else
assert.same({}, result.type_errors)
end

local output_code = tl.pretty_print_ast(ast, gen_target)

local expected_ast, expected_errors = tl.parse(expected, "foo.tl")
Expand All @@ -616,11 +625,11 @@ local function gen(lax, code, expected, gen_target)
end
end

function util.gen(code, expected, gen_target)
function util.gen(code, expected, gen_target, type_errors)
assert(type(code) == "string")
assert(type(expected) == "string")

return gen(false, code, expected, gen_target)
return gen(false, code, expected, gen_target, type_errors)
end

function util.run_check_type_error(...)
Expand Down
Loading
Loading