mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-15 22:35:43 +08:00
378 lines
9.4 KiB
C++
378 lines
9.4 KiB
C++
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||
|
|
||
|
#include "Fixture.h"
|
||
|
|
||
|
#include "doctest.h"
|
||
|
#include "Luau/BuiltinDefinitions.h"
|
||
|
|
||
|
using namespace Luau;
|
||
|
|
||
|
TEST_SUITE_BEGIN("TypeSingletons");
|
||
|
|
||
|
TEST_CASE_FIXTURE(Fixture, "bool_singletons")
|
||
|
{
|
||
|
ScopedFastFlag sffs[] = {
|
||
|
{"LuauSingletonTypes", true},
|
||
|
{"LuauParseSingletonTypes", true},
|
||
|
};
|
||
|
|
||
|
CheckResult result = check(R"(
|
||
|
local a: true = true
|
||
|
local b: false = false
|
||
|
)");
|
||
|
|
||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||
|
}
|
||
|
|
||
|
TEST_CASE_FIXTURE(Fixture, "string_singletons")
|
||
|
{
|
||
|
ScopedFastFlag sffs[] = {
|
||
|
{"LuauSingletonTypes", true},
|
||
|
{"LuauParseSingletonTypes", true},
|
||
|
};
|
||
|
|
||
|
CheckResult result = check(R"(
|
||
|
local a: "foo" = "foo"
|
||
|
local b: "bar" = "bar"
|
||
|
)");
|
||
|
|
||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||
|
}
|
||
|
|
||
|
TEST_CASE_FIXTURE(Fixture, "bool_singletons_mismatch")
|
||
|
{
|
||
|
ScopedFastFlag sffs[] = {
|
||
|
{"LuauSingletonTypes", true},
|
||
|
{"LuauParseSingletonTypes", true},
|
||
|
};
|
||
|
|
||
|
CheckResult result = check(R"(
|
||
|
local a: true = false
|
||
|
)");
|
||
|
|
||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||
|
CHECK_EQ("Type 'false' could not be converted into 'true'", toString(result.errors[0]));
|
||
|
}
|
||
|
|
||
|
TEST_CASE_FIXTURE(Fixture, "string_singletons_mismatch")
|
||
|
{
|
||
|
ScopedFastFlag sffs[] = {
|
||
|
{"LuauSingletonTypes", true},
|
||
|
{"LuauParseSingletonTypes", true},
|
||
|
};
|
||
|
|
||
|
CheckResult result = check(R"(
|
||
|
local a: "foo" = "bar"
|
||
|
)");
|
||
|
|
||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||
|
CHECK_EQ("Type '\"bar\"' could not be converted into '\"foo\"'", toString(result.errors[0]));
|
||
|
}
|
||
|
|
||
|
TEST_CASE_FIXTURE(Fixture, "string_singletons_escape_chars")
|
||
|
{
|
||
|
ScopedFastFlag sffs[] = {
|
||
|
{"LuauSingletonTypes", true},
|
||
|
{"LuauParseSingletonTypes", true},
|
||
|
};
|
||
|
|
||
|
CheckResult result = check(R"(
|
||
|
local a: "\n" = "\000\r"
|
||
|
)");
|
||
|
|
||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||
|
CHECK_EQ(R"(Type '"\000\r"' could not be converted into '"\n"')", toString(result.errors[0]));
|
||
|
}
|
||
|
|
||
|
TEST_CASE_FIXTURE(Fixture, "bool_singleton_subtype")
|
||
|
{
|
||
|
ScopedFastFlag sffs[] = {
|
||
|
{"LuauSingletonTypes", true},
|
||
|
{"LuauParseSingletonTypes", true},
|
||
|
};
|
||
|
|
||
|
CheckResult result = check(R"(
|
||
|
local a: true = true
|
||
|
local b: boolean = a
|
||
|
)");
|
||
|
|
||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||
|
}
|
||
|
|
||
|
TEST_CASE_FIXTURE(Fixture, "string_singleton_subtype")
|
||
|
{
|
||
|
ScopedFastFlag sffs[] = {
|
||
|
{"LuauSingletonTypes", true},
|
||
|
{"LuauParseSingletonTypes", true},
|
||
|
};
|
||
|
|
||
|
CheckResult result = check(R"(
|
||
|
local a: "foo" = "foo"
|
||
|
local b: string = a
|
||
|
)");
|
||
|
|
||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||
|
}
|
||
|
|
||
|
TEST_CASE_FIXTURE(Fixture, "function_call_with_singletons")
|
||
|
{
|
||
|
ScopedFastFlag sffs[] = {
|
||
|
{"LuauSingletonTypes", true},
|
||
|
{"LuauParseSingletonTypes", true},
|
||
|
};
|
||
|
|
||
|
CheckResult result = check(R"(
|
||
|
function f(a: true, b: "foo") end
|
||
|
f(true, "foo")
|
||
|
)");
|
||
|
|
||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||
|
}
|
||
|
|
||
|
TEST_CASE_FIXTURE(Fixture, "function_call_with_singletons_mismatch")
|
||
|
{
|
||
|
ScopedFastFlag sffs[] = {
|
||
|
{"LuauSingletonTypes", true},
|
||
|
{"LuauParseSingletonTypes", true},
|
||
|
};
|
||
|
|
||
|
CheckResult result = check(R"(
|
||
|
function f(a: true, b: "foo") end
|
||
|
f(true, "bar")
|
||
|
)");
|
||
|
|
||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||
|
CHECK_EQ("Type '\"bar\"' could not be converted into '\"foo\"'", toString(result.errors[0]));
|
||
|
}
|
||
|
|
||
|
TEST_CASE_FIXTURE(Fixture, "overloaded_function_call_with_singletons")
|
||
|
{
|
||
|
ScopedFastFlag sffs[] = {
|
||
|
{"LuauSingletonTypes", true},
|
||
|
{"LuauParseSingletonTypes", true},
|
||
|
};
|
||
|
|
||
|
CheckResult result = check(R"(
|
||
|
function f(a, b) end
|
||
|
local g : ((true, string) -> ()) & ((false, number) -> ()) = (f::any)
|
||
|
g(true, "foo")
|
||
|
g(false, 37)
|
||
|
)");
|
||
|
|
||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||
|
}
|
||
|
|
||
|
TEST_CASE_FIXTURE(Fixture, "overloaded_function_call_with_singletons_mismatch")
|
||
|
{
|
||
|
ScopedFastFlag sffs[] = {
|
||
|
{"LuauSingletonTypes", true},
|
||
|
{"LuauParseSingletonTypes", true},
|
||
|
};
|
||
|
|
||
|
CheckResult result = check(R"(
|
||
|
function f(a, b) end
|
||
|
local g : ((true, string) -> ()) & ((false, number) -> ()) = (f::any)
|
||
|
g(true, 37)
|
||
|
)");
|
||
|
|
||
|
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||
|
CHECK_EQ("Type 'number' could not be converted into 'string'", toString(result.errors[0]));
|
||
|
CHECK_EQ("Other overloads are also not viable: (false, number) -> ()", toString(result.errors[1]));
|
||
|
}
|
||
|
|
||
|
TEST_CASE_FIXTURE(Fixture, "enums_using_singletons")
|
||
|
{
|
||
|
ScopedFastFlag sffs[] = {
|
||
|
{"LuauSingletonTypes", true},
|
||
|
{"LuauParseSingletonTypes", true},
|
||
|
};
|
||
|
|
||
|
CheckResult result = check(R"(
|
||
|
type MyEnum = "foo" | "bar" | "baz"
|
||
|
local a : MyEnum = "foo"
|
||
|
local b : MyEnum = "bar"
|
||
|
local c : MyEnum = "baz"
|
||
|
)");
|
||
|
|
||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||
|
}
|
||
|
|
||
|
TEST_CASE_FIXTURE(Fixture, "enums_using_singletons_mismatch")
|
||
|
{
|
||
|
ScopedFastFlag sffs[] = {
|
||
|
{"LuauSingletonTypes", true},
|
||
|
{"LuauParseSingletonTypes", true},
|
||
|
{"LuauExtendedTypeMismatchError", true},
|
||
|
};
|
||
|
|
||
|
CheckResult result = check(R"(
|
||
|
type MyEnum = "foo" | "bar" | "baz"
|
||
|
local a : MyEnum = "bang"
|
||
|
)");
|
||
|
|
||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||
|
CHECK_EQ("Type '\"bang\"' could not be converted into '\"bar\" | \"baz\" | \"foo\"'; none of the union options are compatible",
|
||
|
toString(result.errors[0]));
|
||
|
}
|
||
|
|
||
|
TEST_CASE_FIXTURE(Fixture, "enums_using_singletons_subtyping")
|
||
|
{
|
||
|
ScopedFastFlag sffs[] = {
|
||
|
{"LuauSingletonTypes", true},
|
||
|
{"LuauParseSingletonTypes", true},
|
||
|
};
|
||
|
|
||
|
CheckResult result = check(R"(
|
||
|
type MyEnum1 = "foo" | "bar"
|
||
|
type MyEnum2 = MyEnum1 | "baz"
|
||
|
local a : MyEnum1 = "foo"
|
||
|
local b : MyEnum2 = a
|
||
|
local c : string = b
|
||
|
)");
|
||
|
|
||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||
|
}
|
||
|
|
||
|
TEST_CASE_FIXTURE(Fixture, "tagged_unions_using_singletons")
|
||
|
{
|
||
|
ScopedFastFlag sffs[] = {
|
||
|
{"LuauSingletonTypes", true},
|
||
|
{"LuauParseSingletonTypes", true},
|
||
|
{"LuauExpectedTypesOfProperties", true},
|
||
|
};
|
||
|
|
||
|
CheckResult result = check(R"(
|
||
|
type Dog = { tag: "Dog", howls: boolean }
|
||
|
type Cat = { tag: "Cat", meows: boolean }
|
||
|
type Animal = Dog | Cat
|
||
|
local a : Dog = { tag = "Dog", howls = true }
|
||
|
local b : Animal = { tag = "Cat", meows = true }
|
||
|
local c : Animal = a
|
||
|
c = b
|
||
|
)");
|
||
|
|
||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||
|
}
|
||
|
|
||
|
TEST_CASE_FIXTURE(Fixture, "tagged_unions_using_singletons_mismatch")
|
||
|
{
|
||
|
ScopedFastFlag sffs[] = {
|
||
|
{"LuauSingletonTypes", true},
|
||
|
{"LuauParseSingletonTypes", true},
|
||
|
};
|
||
|
|
||
|
CheckResult result = check(R"(
|
||
|
type Dog = { tag: "Dog", howls: boolean }
|
||
|
type Cat = { tag: "Cat", meows: boolean }
|
||
|
type Animal = Dog | Cat
|
||
|
local a : Animal = { tag = "Cat", howls = true }
|
||
|
)");
|
||
|
|
||
|
LUAU_REQUIRE_ERRORS(result);
|
||
|
}
|
||
|
|
||
|
TEST_CASE_FIXTURE(Fixture, "tagged_unions_immutable_tag")
|
||
|
{
|
||
|
ScopedFastFlag sffs[] = {
|
||
|
{"LuauSingletonTypes", true},
|
||
|
{"LuauParseSingletonTypes", true},
|
||
|
};
|
||
|
|
||
|
CheckResult result = check(R"(
|
||
|
type Dog = { tag: "Dog", howls: boolean }
|
||
|
type Cat = { tag: "Cat", meows: boolean }
|
||
|
type Animal = Dog | Cat
|
||
|
local a : Animal = { tag = "Cat", meows = true }
|
||
|
a.tag = "Dog"
|
||
|
)");
|
||
|
|
||
|
LUAU_REQUIRE_ERRORS(result);
|
||
|
}
|
||
|
|
||
|
TEST_CASE_FIXTURE(Fixture, "table_properties_singleton_strings")
|
||
|
{
|
||
|
ScopedFastFlag sffs[] = {
|
||
|
{"LuauParseSingletonTypes", true},
|
||
|
};
|
||
|
|
||
|
CheckResult result = check(R"(
|
||
|
--!strict
|
||
|
type T = {
|
||
|
["foo"] : number,
|
||
|
["$$bar"] : string,
|
||
|
baz : boolean
|
||
|
}
|
||
|
local t: T = {
|
||
|
["foo"] = 37,
|
||
|
["$$bar"] = "hi",
|
||
|
baz = true
|
||
|
}
|
||
|
local a: number = t.foo
|
||
|
local b: string = t["$$bar"]
|
||
|
local c: boolean = t.baz
|
||
|
t.foo = 5
|
||
|
t["$$bar"] = "lo"
|
||
|
t.baz = false
|
||
|
)");
|
||
|
|
||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||
|
}
|
||
|
TEST_CASE_FIXTURE(Fixture, "table_properties_singleton_strings_mismatch")
|
||
|
{
|
||
|
ScopedFastFlag sffs[] = {
|
||
|
{"LuauParseSingletonTypes", true},
|
||
|
};
|
||
|
|
||
|
CheckResult result = check(R"(
|
||
|
--!strict
|
||
|
type T = {
|
||
|
["$$bar"] : string,
|
||
|
}
|
||
|
local t: T = {
|
||
|
["$$bar"] = "hi",
|
||
|
}
|
||
|
t["$$bar"] = 5
|
||
|
)");
|
||
|
|
||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||
|
CHECK_EQ("Type 'number' could not be converted into 'string'", toString(result.errors[0]));
|
||
|
}
|
||
|
|
||
|
TEST_CASE_FIXTURE(Fixture, "table_properties_alias_or_parens_is_indexer")
|
||
|
{
|
||
|
ScopedFastFlag sffs[] = {
|
||
|
{"LuauParseSingletonTypes", true},
|
||
|
};
|
||
|
|
||
|
CheckResult result = check(R"(
|
||
|
--!strict
|
||
|
type S = "bar"
|
||
|
type T = {
|
||
|
[("foo")] : number,
|
||
|
[S] : string,
|
||
|
}
|
||
|
)");
|
||
|
|
||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||
|
CHECK_EQ("Syntax error: Cannot have more than one table indexer", toString(result.errors[0]));
|
||
|
}
|
||
|
|
||
|
TEST_CASE_FIXTURE(Fixture, "table_properties_type_error_escapes")
|
||
|
{
|
||
|
ScopedFastFlag sffs[] = {
|
||
|
{"LuauParseSingletonTypes", true},
|
||
|
};
|
||
|
|
||
|
CheckResult result = check(R"(
|
||
|
--!strict
|
||
|
local x: { ["<>"] : number }
|
||
|
x = { ["\n"] = 5 }
|
||
|
)");
|
||
|
|
||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||
|
CHECK_EQ(R"(Table type '{| ["\n"]: number |}' not compatible with type '{| ["<>"]: number |}' because the former is missing field '<>')",
|
||
|
toString(result.errors[0]));
|
||
|
}
|
||
|
|
||
|
TEST_SUITE_END();
|