luau/tests/NonstrictMode.test.cpp
Andy Friesen c5089def6e
Sync to upstream/release/563 (#833)
* Fix a bug where reading a property from an unsealed table caused
inference to improperly infer the existence of that property.
* Fix #827

We have also made a lot of progress on the new solver and the JIT. Both
projects are still in the process of being built out. Neither are ready
for general use yet.

We are mostly working to tighten up how the new solver handles
refinements and updates to unsealed tables to bring it up to the same
level as the old solver.

---------

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
2023-02-10 11:40:38 -08:00

310 lines
7.1 KiB
C++

// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/Scope.h"
#include "Luau/TypeInfer.h"
#include "Luau/Type.h"
#include "Fixture.h"
#include "doctest.h"
#include <algorithm>
using namespace Luau;
TEST_SUITE_BEGIN("NonstrictModeTests");
TEST_CASE_FIXTURE(Fixture, "infer_nullary_function")
{
CheckResult result = check(R"(
--!nonstrict
function foo(x, y) end
)");
TypeId fooType = requireType("foo");
REQUIRE(fooType);
const FunctionType* ftv = get<FunctionType>(fooType);
REQUIRE_MESSAGE(ftv != nullptr, "Expected a function, got " << toString(fooType));
auto args = flatten(ftv->argTypes).first;
REQUIRE_EQ(2, args.size());
REQUIRE_EQ("any", toString(args[0]));
REQUIRE_EQ("any", toString(args[1]));
auto rets = flatten(ftv->retTypes).first;
REQUIRE_EQ(0, rets.size());
}
TEST_CASE_FIXTURE(Fixture, "infer_the_maximum_number_of_values_the_function_could_return")
{
CheckResult result = check(R"(
--!nonstrict
function getMinCardCountForWidth(width)
if width < 513 then
return 3
else
return 8, 'jellybeans'
end
end
)");
TypeId t = requireType("getMinCardCountForWidth");
REQUIRE(t);
REQUIRE_EQ("(any) -> (...any)", toString(t));
}
#if 0
// Maybe we want this?
TEST_CASE_FIXTURE(Fixture, "return_annotation_is_still_checked")
{
CheckResult result = check(R"(
function foo(x): number return 'hello' end
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
REQUIRE_NE(*typeChecker.anyType, *requireType("foo"));
}
#endif
TEST_CASE_FIXTURE(Fixture, "function_parameters_are_any")
{
CheckResult result = check(R"(
--!nonstrict
function f(arg)
arg = 9
arg:concat(4)
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(Fixture, "inconsistent_return_types_are_ok")
{
CheckResult result = check(R"(
--!nonstrict
function f()
if 1 then
return 4
else
return 'hello'
end
return 'one', 'two'
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(Fixture, "locals_are_any_by_default")
{
CheckResult result = check(R"(
--!nonstrict
local m = 55
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK_EQ(*typeChecker.anyType, *requireType("m"));
}
TEST_CASE_FIXTURE(Fixture, "parameters_having_type_any_are_optional")
{
CheckResult result = check(R"(
--!nonstrict
local function f(a, b)
return a
end
f(5)
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(Fixture, "local_tables_are_not_any")
{
CheckResult result = check(R"(
--!nonstrict
local T = {}
function T:method() end
function T.staticmethod() end
T.method()
T:staticmethod()
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK_EQ("This function does not take self. Did you mean to use a dot instead of a colon?", toString(result.errors[0]));
}
TEST_CASE_FIXTURE(Fixture, "offer_a_hint_if_you_use_a_dot_instead_of_a_colon")
{
CheckResult result = check(R"(
--!nonstrict
local T = {}
function T:method(x: number) end
T.method(5)
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK_EQ("This function must be called with self. Did you mean to use a colon instead of a dot?", toString(result.errors[0]));
}
TEST_CASE_FIXTURE(Fixture, "table_props_are_any")
{
CheckResult result = check(R"(
--!nonstrict
local T = {}
T.foo = 55
)");
LUAU_REQUIRE_NO_ERRORS(result);
TableType* ttv = getMutable<TableType>(requireType("T"));
REQUIRE(ttv != nullptr);
REQUIRE(ttv->props.count("foo"));
TypeId fooProp = ttv->props["foo"].type;
REQUIRE(fooProp != nullptr);
CHECK_EQ(*fooProp, *typeChecker.anyType);
}
TEST_CASE_FIXTURE(Fixture, "inline_table_props_are_also_any")
{
CheckResult result = check(R"(
--!nonstrict
local T = {
one = 1,
two = 'two',
three = function() return 3 end
}
)");
LUAU_REQUIRE_NO_ERRORS(result);
TableType* ttv = getMutable<TableType>(requireType("T"));
REQUIRE_MESSAGE(ttv, "Should be a table: " << toString(requireType("T")));
CHECK_EQ(*typeChecker.anyType, *ttv->props["one"].type);
CHECK_EQ(*typeChecker.anyType, *ttv->props["two"].type);
CHECK_MESSAGE(get<FunctionType>(ttv->props["three"].type), "Should be a function: " << *ttv->props["three"].type);
}
TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_iterator_variables_are_any")
{
CheckResult result = check(R"(
--!nonstrict
function requires_a_table(arg: {}) end
function requires_a_number(arg: number) end
local T = {}
for a, b in pairs(T) do
requires_a_table(a)
requires_a_table(b)
requires_a_number(a)
requires_a_number(b)
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(BuiltinsFixture, "table_dot_insert_and_recursive_calls")
{
CheckResult result = check(R"(
--!nonstrict
function populateListFromIds(list, normalizedData)
local newList = {}
for _, value in ipairs(list) do
if type(value) == "table" then
table.insert(newList, populateListFromIds(value, normalizedData))
else
table.insert(newList, normalizedData[value])
end
end
return newList
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(Fixture, "delay_function_does_not_require_its_argument_to_return_anything")
{
CheckResult result = check(R"(
--!nonstrict
function delay(ms: number?, cb: () -> ()): () end
delay(50, function() end)
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(Fixture, "inconsistent_module_return_types_are_ok")
{
CheckResult result = check(R"(
--!nonstrict
local FFlag: any
if FFlag.get('SomeFlag') then
return {foo='bar'}
else
return function(prop)
return 'bar'
end
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
REQUIRE_EQ("any", toString(getMainModule()->returnType));
}
TEST_CASE_FIXTURE(Fixture, "returning_insufficient_return_values")
{
CheckResult result = check(R"(
--!nonstrict
function foo(): (boolean, string?)
if true then
return true, "hello"
else
return false
end
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(Fixture, "returning_too_many_values")
{
CheckResult result = check(R"(
--!nonstrict
function foo(): boolean
if true then
return true, "hello"
else
return false
end
end
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_SUITE_END();