mirror of
https://github.com/luau-lang/luau.git
synced 2025-05-04 10:33:46 +01:00
Add feature flag and test cases for loop CFA
This commit is contained in:
parent
0b2755f964
commit
1364b16ef9
2 changed files with 523 additions and 0 deletions
|
@ -40,6 +40,7 @@ LUAU_FASTFLAGVARIABLE(LuauFixCyclicModuleExports, false)
|
|||
LUAU_FASTFLAG(LuauOccursIsntAlwaysFailure)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTypecheckTypeguards, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTinyControlFlowAnalysis, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauLoopControlFlowAnalysis, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauAlwaysCommitInferencesOfFunctionCalls, false)
|
||||
LUAU_FASTFLAG(LuauParseDeclareClassIndexer)
|
||||
LUAU_FASTFLAGVARIABLE(LuauIndexTableIntersectionStringExpr, false)
|
||||
|
|
|
@ -26,6 +26,46 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "if_not_x_return")
|
|||
CHECK_EQ("string", toString(requireTypeAtPosition({6, 24})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "if_not_x_break")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauLoopControlFlowAnalysis", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(x: {{value: string?}})
|
||||
for _, record in x do
|
||||
if not record.value then
|
||||
break
|
||||
end
|
||||
|
||||
local foo = record.value
|
||||
end
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
CHECK_EQ("string", toString(requireTypeAtPosition({7, 34})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "if_not_x_continue")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauLoopControlFlowAnalysis", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(x: {{value: string?}})
|
||||
for _, record in x do
|
||||
if not record.value then
|
||||
continue
|
||||
end
|
||||
|
||||
local foo = record.value
|
||||
end
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
CHECK_EQ("string", toString(requireTypeAtPosition({7, 38})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "if_not_x_return_elif_not_y_return")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauTinyControlFlowAnalysis", true};
|
||||
|
@ -48,6 +88,56 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "if_not_x_return_elif_not_y_return")
|
|||
CHECK_EQ("string", toString(requireTypeAtPosition({9, 24})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "if_not_x_break_elif_not_y_break")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauLoopControlFlowAnalysis", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(x: {{value: string?}}, y: {{value: string?}})
|
||||
for i, recordX in x do
|
||||
local recordY = y[i]
|
||||
if not recordX.value then
|
||||
break
|
||||
elseif not recordY.value then
|
||||
break
|
||||
end
|
||||
|
||||
local foo = recordX.value
|
||||
local bar = recordY.value
|
||||
end
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
CHECK_EQ("string", toString(requireTypeAtPosition({10, 38})));
|
||||
CHECK_EQ("string", toString(requireTypeAtPosition({11, 38})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "if_not_x_continue_elif_not_y_continue")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauLoopControlFlowAnalysis", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(x: {{value: string?}}, y: {{value: string?}})
|
||||
for i, recordX in x do
|
||||
local recordY = y[i]
|
||||
if not recordX.value then
|
||||
continue
|
||||
elseif not recordY.value then
|
||||
continue
|
||||
end
|
||||
|
||||
local foo = recordX.value
|
||||
local bar = recordY.value
|
||||
end
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
CHECK_EQ("string", toString(requireTypeAtPosition({10, 38})));
|
||||
CHECK_EQ("string", toString(requireTypeAtPosition({11, 38})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "if_not_x_return_elif_rand_return_elif_not_y_return")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauTinyControlFlowAnalysis", true};
|
||||
|
@ -72,6 +162,60 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "if_not_x_return_elif_rand_return_elif_not_y_
|
|||
CHECK_EQ("string", toString(requireTypeAtPosition({11, 24})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "if_not_x_break_elif_rand_break_elif_not_y_break")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauLoopControlFlowAnalysis", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(x: {{value: string?}}, y: {{value: string?}})
|
||||
for i, recordX in x do
|
||||
local recordY = y[i]
|
||||
if not recordX.value then
|
||||
break
|
||||
elseif math.random() > 0.5 then
|
||||
break
|
||||
elseif not recordY.value then
|
||||
break
|
||||
end
|
||||
|
||||
local foo = recordX.value
|
||||
local bar = recordY.value
|
||||
end
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
CHECK_EQ("string", toString(requireTypeAtPosition({12, 38})));
|
||||
CHECK_EQ("string", toString(requireTypeAtPosition({13, 38})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "if_not_x_continue_elif_rand_continue_elif_not_y_continue")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauLoopControlFlowAnalysis", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(x: {{value: string?}}, y: {{value: string?}})
|
||||
for i, recordX in x do
|
||||
local recordY = y[i]
|
||||
if not recordX.value then
|
||||
continue
|
||||
elseif math.random() > 0.5 then
|
||||
continue
|
||||
elseif not recordY.value then
|
||||
continue
|
||||
end
|
||||
|
||||
local foo = recordX.value
|
||||
local bar = recordY.value
|
||||
end
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
CHECK_EQ("string", toString(requireTypeAtPosition({12, 38})));
|
||||
CHECK_EQ("string", toString(requireTypeAtPosition({13, 38})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "if_not_x_return_elif_not_rand_return_elif_not_y_fallthrough")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauTinyControlFlowAnalysis", true};
|
||||
|
@ -96,6 +240,60 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "if_not_x_return_elif_not_rand_return_elif_no
|
|||
CHECK_EQ("string?", toString(requireTypeAtPosition({11, 24})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "if_not_x_break_elif_rand_break_elif_not_y_fallthrough")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauLoopControlFlowAnalysis", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(x: {{value: string?}}, y: {{value: string?}})
|
||||
for i, recordX in x do
|
||||
local recordY = y[i]
|
||||
if not recordX.value then
|
||||
break
|
||||
elseif math.random() > 0.5 then
|
||||
break
|
||||
elseif not recordY.value then
|
||||
|
||||
end
|
||||
|
||||
local foo = recordX.value
|
||||
local bar = recordY.value
|
||||
end
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
CHECK_EQ("string", toString(requireTypeAtPosition({12, 38})));
|
||||
CHECK_EQ("string?", toString(requireTypeAtPosition({13, 38})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "if_not_x_continue_elif_rand_continue_elif_not_y_fallthrough")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauLoopControlFlowAnalysis", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(x: {{value: string?}}, y: {{value: string?}})
|
||||
for i, recordX in x do
|
||||
local recordY = y[i]
|
||||
if not recordX.value then
|
||||
continue
|
||||
elseif math.random() > 0.5 then
|
||||
continue
|
||||
elseif not recordY.value then
|
||||
|
||||
end
|
||||
|
||||
local foo = recordX.value
|
||||
local bar = recordY.value
|
||||
end
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
CHECK_EQ("string", toString(requireTypeAtPosition({12, 38})));
|
||||
CHECK_EQ("string?", toString(requireTypeAtPosition({13, 38})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "if_not_x_return_elif_not_y_fallthrough_elif_not_z_return")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauTinyControlFlowAnalysis", true};
|
||||
|
@ -122,6 +320,66 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "if_not_x_return_elif_not_y_fallthrough_elif_
|
|||
CHECK_EQ("string?", toString(requireTypeAtPosition({12, 24})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "if_not_x_break_elif_not_y_fallthrough_elif_not_z_break")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauLoopControlFlowAnalysis", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(x: {{value: string?}}, y: {{value: string?}}, z: {{value: string?}})
|
||||
for i, recordX in x do
|
||||
local recordY = y[i]
|
||||
local recordZ = y[i]
|
||||
if not recordX.value then
|
||||
break
|
||||
elseif not recordY.value then
|
||||
|
||||
elseif not recordZ.value then
|
||||
break
|
||||
end
|
||||
|
||||
local foo = recordX.value
|
||||
local bar = recordY.value
|
||||
local baz = recordZ.value
|
||||
end
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
CHECK_EQ("string", toString(requireTypeAtPosition({13, 38})));
|
||||
CHECK_EQ("string?", toString(requireTypeAtPosition({14, 38})));
|
||||
CHECK_EQ("string?", toString(requireTypeAtPosition({15, 38})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "if_not_x_continue_elif_not_y_fallthrough_elif_not_z_continue")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauLoopControlFlowAnalysis", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(x: {{value: string?}}, y: {{value: string?}}, z: {{value: string?}})
|
||||
for i, recordX in x do
|
||||
local recordY = y[i]
|
||||
local recordZ = y[i]
|
||||
if not recordX.value then
|
||||
continue
|
||||
elseif not recordY.value then
|
||||
|
||||
elseif not recordZ.value then
|
||||
continue
|
||||
end
|
||||
|
||||
local foo = recordX.value
|
||||
local bar = recordY.value
|
||||
local baz = recordZ.value
|
||||
end
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
CHECK_EQ("string", toString(requireTypeAtPosition({13, 38})));
|
||||
CHECK_EQ("string?", toString(requireTypeAtPosition({14, 38})));
|
||||
CHECK_EQ("string?", toString(requireTypeAtPosition({15, 38})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "do_if_not_x_return")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauTinyControlFlowAnalysis", true};
|
||||
|
@ -142,6 +400,50 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "do_if_not_x_return")
|
|||
CHECK_EQ("string", toString(requireTypeAtPosition({8, 24})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "for_record_do_if_not_x_break")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauLoopControlFlowAnalysis", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(x: {{value: string?}})
|
||||
for _, record in x do
|
||||
do
|
||||
if not record.value then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
local foo = record.value
|
||||
end
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
CHECK_EQ("string", toString(requireTypeAtPosition({9, 38})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "for_record_do_if_not_x_continue")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauLoopControlFlowAnalysis", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(x: {{value: string?}})
|
||||
for _, record in x do
|
||||
do
|
||||
if not record.value then
|
||||
continue
|
||||
end
|
||||
end
|
||||
|
||||
local foo = record.value
|
||||
end
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
CHECK_EQ("string", toString(requireTypeAtPosition({9, 38})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "early_return_in_a_loop_which_isnt_guaranteed_to_run_first")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauTinyControlFlowAnalysis", true};
|
||||
|
@ -271,6 +573,60 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "if_not_x_return_if_not_y_return")
|
|||
CHECK_EQ("string", toString(requireTypeAtPosition({11, 24})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "if_not_x_break_if_not_y_break")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauLoopControlFlowAnalysis", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(x: {{value: string?}}, y: {{value: string?}})
|
||||
for i, recordX in x do
|
||||
local recordY = y[i]
|
||||
if not recordX.value then
|
||||
break
|
||||
end
|
||||
|
||||
if not recordY.value then
|
||||
break
|
||||
end
|
||||
|
||||
local foo = recordX.value
|
||||
local bar = recordY.value
|
||||
end
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
CHECK_EQ("string", toString(requireTypeAtPosition({12, 38})));
|
||||
CHECK_EQ("string", toString(requireTypeAtPosition({13, 38})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "if_not_x_continue_if_not_y_continue")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauLoopControlFlowAnalysis", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(x: {{value: string?}}, y: {{value: string?}})
|
||||
for i, recordX in x do
|
||||
local recordY = y[i]
|
||||
if not recordX.value then
|
||||
continue
|
||||
end
|
||||
|
||||
if not recordY.value then
|
||||
continue
|
||||
end
|
||||
|
||||
local foo = recordX.value
|
||||
local bar = recordY.value
|
||||
end
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
CHECK_EQ("string", toString(requireTypeAtPosition({12, 38})));
|
||||
CHECK_EQ("string", toString(requireTypeAtPosition({13, 38})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_does_not_leak_out")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauTinyControlFlowAnalysis", true};
|
||||
|
@ -294,6 +650,56 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_does_not_leak_out")
|
|||
CHECK_EQ("nil", toString(requireTypeAtPosition({8, 29})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_does_not_leak_out_breaking")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauLoopControlFlowAnalysis", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(x: {{value: string?}})
|
||||
for _, record in x do
|
||||
if typeof(record.value) == "string" then
|
||||
break
|
||||
else
|
||||
type Foo = number
|
||||
end
|
||||
|
||||
local foo: Foo = record.value
|
||||
end
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
CHECK_EQ("Unknown type 'Foo'", toString(result.errors[0]));
|
||||
|
||||
CHECK_EQ("nil", toString(requireTypeAtPosition({9, 43})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_does_not_leak_out_continuing")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauLoopControlFlowAnalysis", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(x: {{value: string?}})
|
||||
for _, record in x do
|
||||
if typeof(record.value) == "string" then
|
||||
continue
|
||||
else
|
||||
type Foo = number
|
||||
end
|
||||
|
||||
local foo: Foo = record.value
|
||||
end
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
CHECK_EQ("Unknown type 'Foo'", toString(result.errors[0]));
|
||||
|
||||
CHECK_EQ("nil", toString(requireTypeAtPosition({9, 43})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "prototyping_and_visiting_alias_has_the_same_scope")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauTinyControlFlowAnalysis", true};
|
||||
|
@ -320,6 +726,56 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "prototyping_and_visiting_alias_has_the_same_
|
|||
CHECK_EQ("nil", toString(requireTypeAtPosition({8, 29})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "prototyping_and_visiting_alias_has_the_same_scope_breaking")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauLoopControlFlowAnalysis", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(x: {{value: string?}})
|
||||
for _, record in x do
|
||||
type Foo = number
|
||||
|
||||
if typeof(record.value) == "string" then
|
||||
break
|
||||
end
|
||||
|
||||
local foo: Foo = record.value
|
||||
end
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
CHECK_EQ("Type 'nil' could not be converted into 'number'", toString(result.errors[0]));
|
||||
|
||||
CHECK_EQ("nil", toString(requireTypeAtPosition({9, 43})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "prototyping_and_visiting_alias_has_the_same_scope_continuing")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauLoopControlFlowAnalysis", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local function f(x: {{value: string?}})
|
||||
for _, record in x do
|
||||
type Foo = number
|
||||
|
||||
if typeof(record.value) == "string" then
|
||||
continue
|
||||
end
|
||||
|
||||
local foo: Foo = record.value
|
||||
end
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||
|
||||
CHECK_EQ("Type 'nil' could not be converted into 'number'", toString(result.errors[0]));
|
||||
|
||||
CHECK_EQ("nil", toString(requireTypeAtPosition({9, 43})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "tagged_unions")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauTinyControlFlowAnalysis", true};
|
||||
|
@ -355,6 +811,72 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "tagged_unions")
|
|||
CHECK_EQ("Err<E>", toString(requireTypeAtPosition({16, 19})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "tagged_unions_breaking")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauLoopControlFlowAnalysis", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Ok<T> = { tag: "ok", value: T }
|
||||
type Err<E> = { tag: "err", error: E }
|
||||
type Result<T, E> = Ok<T> | Err<E>
|
||||
|
||||
local function process<T, E>(results: {Result<T, E>})
|
||||
for _, result in results do
|
||||
if result.tag == "ok" then
|
||||
local tag = result.tag
|
||||
local val = result.value
|
||||
|
||||
break
|
||||
end
|
||||
|
||||
local tag = result.tag
|
||||
local err = result.error
|
||||
end
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
CHECK_EQ("\"ok\"", toString(requireTypeAtPosition({8, 39})));
|
||||
CHECK_EQ("T", toString(requireTypeAtPosition({9, 39})));
|
||||
|
||||
CHECK_EQ("\"err\"", toString(requireTypeAtPosition({14, 35})));
|
||||
CHECK_EQ("E", toString(requireTypeAtPosition({15, 35})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "tagged_unions_continuing")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauLoopControlFlowAnalysis", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
type Ok<T> = { tag: "ok", value: T }
|
||||
type Err<E> = { tag: "err", error: E }
|
||||
type Result<T, E> = Ok<T> | Err<E>
|
||||
|
||||
local function process<T, E>(results: {Result<T, E>})
|
||||
for _, result in results do
|
||||
if result.tag == "ok" then
|
||||
local tag = result.tag
|
||||
local val = result.value
|
||||
|
||||
continue
|
||||
end
|
||||
|
||||
local tag = result.tag
|
||||
local err = result.error
|
||||
end
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
CHECK_EQ("\"ok\"", toString(requireTypeAtPosition({8, 39})));
|
||||
CHECK_EQ("T", toString(requireTypeAtPosition({9, 39})));
|
||||
|
||||
CHECK_EQ("\"err\"", toString(requireTypeAtPosition({14, 35})));
|
||||
CHECK_EQ("E", toString(requireTypeAtPosition({15, 35})));
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(BuiltinsFixture, "do_assert_x")
|
||||
{
|
||||
ScopedFastFlag sff{"LuauTinyControlFlowAnalysis", true};
|
||||
|
|
Loading…
Add table
Reference in a new issue