2022-03-18 08:46:04 +08:00
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
# include "Luau/AstQuery.h"
# include "Luau/BuiltinDefinitions.h"
2022-10-07 08:23:29 +08:00
# include "Luau/Error.h"
2022-03-18 08:46:04 +08:00
# include "Luau/Scope.h"
# include "Luau/TypeInfer.h"
2023-01-05 04:53:17 +08:00
# include "Luau/Type.h"
# include "Luau/VisitType.h"
2022-03-18 08:46:04 +08:00
2024-03-23 01:47:10 +08:00
# include "ClassFixture.h"
2022-03-18 08:46:04 +08:00
# include "Fixture.h"
2024-08-17 02:29:33 +08:00
# include "ScopedFlags.h"
2022-03-18 08:46:04 +08:00
# include "doctest.h"
using namespace Luau ;
2024-11-02 03:06:07 +08:00
LUAU_FASTFLAG ( LuauInstantiateInSubtyping )
LUAU_FASTFLAG ( LuauSolverV2 )
LUAU_FASTINT ( LuauTarjanChildLimit )
LUAU_FASTFLAG ( LuauRetrySubtypingWithoutHiddenPack )
2022-04-15 07:57:43 +08:00
2022-03-18 08:46:04 +08:00
TEST_SUITE_BEGIN ( " TypeInferFunctions " ) ;
2024-04-13 01:18:49 +08:00
TEST_CASE_FIXTURE ( Fixture , " general_case_table_literal_blocks " )
{
CheckResult result = check ( R " (
- - ! strict
function f ( x : { [ any ] : number } )
return x
end
local Foo = { bar = " $$$ " }
f ( { [ Foo . bar ] = 0 } )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2024-02-03 05:32:42 +08:00
TEST_CASE_FIXTURE ( Fixture , " overload_resolution " )
{
CheckResult result = check ( R " (
type A = ( number ) - > string
type B = ( string ) - > number
local function foo ( f : A & B )
return f ( 1 ) , f ( " five " )
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
TypeId t = requireType ( " foo " ) ;
const FunctionType * fooType = get < FunctionType > ( requireType ( " foo " ) ) ;
REQUIRE ( fooType ! = nullptr ) ;
CHECK ( toString ( t ) = = " (((number) -> string) & ((string) -> number)) -> (string, number) " ) ;
}
2022-03-18 08:46:04 +08:00
TEST_CASE_FIXTURE ( Fixture , " tc_function " )
{
CheckResult result = check ( " function five() return 5 end " ) ;
LUAU_REQUIRE_NO_ERRORS ( result ) ;
2023-01-05 04:53:17 +08:00
const FunctionType * fiveType = get < FunctionType > ( requireType ( " five " ) ) ;
2022-03-18 08:46:04 +08:00
REQUIRE ( fiveType ! = nullptr ) ;
}
TEST_CASE_FIXTURE ( Fixture , " check_function_bodies " )
{
2024-02-10 01:51:12 +08:00
CheckResult result = check ( R " (
function myFunction ( ) : number
local a = 0
a = true
return a
end
) " );
2022-03-18 08:46:04 +08:00
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
2024-08-31 04:16:51 +08:00
if ( FFlag : : LuauSolverV2 )
2024-02-10 01:51:12 +08:00
{
const TypePackMismatch * tm = get < TypePackMismatch > ( result . errors [ 0 ] ) ;
REQUIRE ( tm ) ;
CHECK ( toString ( tm - > wantedTp ) = = " number " ) ;
CHECK ( toString ( tm - > givenTp ) = = " boolean " ) ;
}
else
{
2024-08-02 22:30:04 +08:00
CHECK_EQ (
result . errors [ 0 ] ,
( TypeError {
Location { Position { 3 , 16 } , Position { 3 , 20 } } ,
TypeMismatch {
builtinTypes - > numberType ,
builtinTypes - > booleanType ,
}
} )
) ;
2024-02-10 01:51:12 +08:00
}
2022-03-18 08:46:04 +08:00
}
2022-07-01 07:52:43 +08:00
TEST_CASE_FIXTURE ( Fixture , " cannot_hoist_interior_defns_into_signature " )
{
// This test verifies that the signature does not have access to types
// declared within the body. Under DCR, if the function's inner scope
// encompasses the entire function expression, it would be possible for this
// to type check (but the solver output is somewhat undefined). This test
// ensures that this isn't the case.
CheckResult result = check ( R " (
local function f ( x : T )
type T = number
end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
2024-08-02 22:30:04 +08:00
CHECK (
result . errors [ 0 ] = =
TypeError {
Location { { 1 , 28 } , { 1 , 29 } } ,
getMainSourceModule ( ) - > name ,
UnknownSymbol {
" T " ,
UnknownSymbol : : Context : : Type ,
}
}
) ;
2022-07-01 07:52:43 +08:00
}
2022-03-18 08:46:04 +08:00
TEST_CASE_FIXTURE ( Fixture , " infer_return_type " )
{
CheckResult result = check ( " function take_five() return 5 end " ) ;
LUAU_REQUIRE_NO_ERRORS ( result ) ;
2023-01-05 04:53:17 +08:00
const FunctionType * takeFiveType = get < FunctionType > ( requireType ( " take_five " ) ) ;
2022-03-18 08:46:04 +08:00
REQUIRE ( takeFiveType ! = nullptr ) ;
2022-06-17 09:05:14 +08:00
std : : vector < TypeId > retVec = flatten ( takeFiveType - > retTypes ) . first ;
2022-03-18 08:46:04 +08:00
REQUIRE ( ! retVec . empty ( ) ) ;
2023-03-11 04:21:07 +08:00
REQUIRE_EQ ( * follow ( retVec [ 0 ] ) , * builtinTypes - > numberType ) ;
2022-03-18 08:46:04 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " infer_from_function_return_type " )
{
CheckResult result = check ( " function take_five() return 5 end local five = take_five() " ) ;
LUAU_REQUIRE_NO_ERRORS ( result ) ;
2023-03-11 04:21:07 +08:00
CHECK_EQ ( * builtinTypes - > numberType , * follow ( requireType ( " five " ) ) ) ;
2022-03-18 08:46:04 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " infer_that_function_does_not_return_a_table " )
{
CheckResult result = check ( R " (
function take_five ( )
return 5
end
take_five ( ) . prop = 888
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
2023-03-11 04:21:07 +08:00
CHECK_EQ ( result . errors [ 0 ] , ( TypeError { Location { Position { 5 , 8 } , Position { 5 , 24 } } , NotATable { builtinTypes - > numberType } } ) ) ;
2022-03-18 08:46:04 +08:00
}
2023-02-18 07:41:51 +08:00
TEST_CASE_FIXTURE ( Fixture , " generalize_table_property " )
{
CheckResult result = check ( R " (
local T = { }
T . foo = function ( x )
return x
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
TypeId t = requireType ( " T " ) ;
const TableType * tt = get < TableType > ( follow ( t ) ) ;
REQUIRE ( tt ) ;
2023-04-29 03:55:13 +08:00
TypeId fooTy = tt - > props . at ( " foo " ) . type ( ) ;
2023-02-18 07:41:51 +08:00
CHECK ( " <a>(a) -> a " = = toString ( fooTy ) ) ;
}
2022-03-18 08:46:04 +08:00
TEST_CASE_FIXTURE ( Fixture , " vararg_functions_should_allow_calls_of_any_types_and_size " )
{
CheckResult result = check ( R " (
function f ( . . . ) end
f ( 1 )
f ( " foo " , 2 )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " vararg_function_is_quantified " )
2022-03-18 08:46:04 +08:00
{
CheckResult result = check ( R " (
local T = { }
function T . f ( . . . )
local result = { }
for i = 1 , select ( " # " , . . . ) do
local dictionary = select ( i , . . . )
for key , value in pairs ( dictionary ) do
result [ key ] = value
end
end
return result
2022-04-15 07:57:43 +08:00
end
2022-03-18 08:46:04 +08:00
return T
) " );
2022-09-09 06:14:25 +08:00
LUAU_REQUIRE_NO_ERRORS ( result ) ;
2023-01-07 05:14:35 +08:00
auto r = first ( getMainModule ( ) - > returnType ) ;
2022-03-18 08:46:04 +08:00
REQUIRE ( r ) ;
2023-01-05 04:53:17 +08:00
TableType * ttv = getMutable < TableType > ( * r ) ;
2022-03-18 08:46:04 +08:00
REQUIRE ( ttv ) ;
2022-09-02 07:14:03 +08:00
REQUIRE ( ttv - > props . count ( " f " ) ) ;
2023-04-29 03:55:13 +08:00
TypeId k = ttv - > props [ " f " ] . type ( ) ;
2022-03-18 08:46:04 +08:00
REQUIRE ( k ) ;
}
TEST_CASE_FIXTURE ( Fixture , " list_only_alternative_overloads_that_match_argument_count " )
{
CheckResult result = check ( R " (
local multiply : ( ( number ) - > number ) & ( ( number ) - > string ) & ( ( number , number ) - > number )
multiply ( " " )
) " );
LUAU_REQUIRE_ERROR_COUNT ( 2 , result ) ;
2024-08-31 04:16:51 +08:00
if ( FFlag : : LuauSolverV2 )
2023-05-06 05:52:49 +08:00
{
GenericError * g = get < GenericError > ( result . errors [ 0 ] ) ;
REQUIRE ( g ) ;
CHECK ( g - > message = = " None of the overloads for function that accept 1 arguments are compatible. " ) ;
}
else
{
TypeMismatch * tm = get < TypeMismatch > ( result . errors [ 0 ] ) ;
REQUIRE ( tm ) ;
CHECK_EQ ( builtinTypes - > numberType , tm - > wantedType ) ;
CHECK_EQ ( builtinTypes - > stringType , tm - > givenType ) ;
}
2022-03-18 08:46:04 +08:00
ExtraInformation * ei = get < ExtraInformation > ( result . errors [ 1 ] ) ;
REQUIRE ( ei ) ;
2023-05-06 05:52:49 +08:00
2024-08-31 04:16:51 +08:00
if ( FFlag : : LuauSolverV2 )
2024-08-17 02:29:33 +08:00
CHECK ( " Available overloads: (number) -> number; (number) -> string; and (number, number) -> number " = = ei - > message ) ;
2023-05-06 05:52:49 +08:00
else
CHECK_EQ ( " Other overloads are also not viable: (number) -> string " , ei - > message ) ;
2022-03-18 08:46:04 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " list_all_overloads_if_no_overload_takes_given_argument_count " )
{
CheckResult result = check ( R " (
local multiply : ( ( number ) - > number ) & ( ( number ) - > string ) & ( ( number , number ) - > number )
multiply ( )
) " );
LUAU_REQUIRE_ERROR_COUNT ( 2 , result ) ;
GenericError * ge = get < GenericError > ( result . errors [ 0 ] ) ;
REQUIRE ( ge ) ;
CHECK_EQ ( " No overload for function accepts 0 arguments. " , ge - > message ) ;
ExtraInformation * ei = get < ExtraInformation > ( result . errors [ 1 ] ) ;
REQUIRE ( ei ) ;
CHECK_EQ ( " Available overloads: (number) -> number; (number) -> string; and (number, number) -> number " , ei - > message ) ;
}
TEST_CASE_FIXTURE ( Fixture , " dont_give_other_overloads_message_if_only_one_argument_matching_overload_exists " )
{
CheckResult result = check ( R " (
local multiply : ( ( number ) - > number ) & ( ( number ) - > string ) & ( ( number , number ) - > number )
multiply ( 1 , " " )
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
TypeMismatch * tm = get < TypeMismatch > ( result . errors [ 0 ] ) ;
REQUIRE ( tm ) ;
2023-03-11 04:21:07 +08:00
CHECK_EQ ( builtinTypes - > numberType , tm - > wantedType ) ;
CHECK_EQ ( builtinTypes - > stringType , tm - > givenType ) ;
2022-03-18 08:46:04 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " infer_return_type_from_selected_overload " )
{
CheckResult result = check ( R " (
type T = { method : ( ( T , number ) - > number ) & ( ( number ) - > string ) }
local T : T
local a = T . method ( T , 4 )
local b = T . method ( 5 )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
CHECK_EQ ( " number " , toString ( requireType ( " a " ) ) ) ;
CHECK_EQ ( " string " , toString ( requireType ( " b " ) ) ) ;
}
TEST_CASE_FIXTURE ( Fixture , " too_many_arguments " )
{
2024-08-17 02:29:33 +08:00
// This is not part of the new non-strict specification currently.
2024-11-02 03:06:07 +08:00
DOES_NOT_PASS_NEW_SOLVER_GUARD ( ) ;
2024-08-17 02:29:33 +08:00
2022-03-18 08:46:04 +08:00
CheckResult result = check ( R " (
- - ! nonstrict
function g ( a : number ) end
g ( )
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
auto err = result . errors [ 0 ] ;
auto acm = get < CountMismatch > ( err ) ;
REQUIRE ( acm ) ;
CHECK_EQ ( 1 , acm - > expected ) ;
CHECK_EQ ( 0 , acm - > actual ) ;
}
2022-10-28 18:37:29 +08:00
TEST_CASE_FIXTURE ( Fixture , " too_many_arguments_error_location " )
{
CheckResult result = check ( R " (
- - ! strict
function myfunction ( a : number , b : number ) end
myfunction ( 1 )
function getmyfunction ( )
return myfunction
end
getmyfunction ( ) ( )
) " );
LUAU_REQUIRE_ERROR_COUNT ( 2 , result ) ;
{
TypeError err = result . errors [ 0 ] ;
// Ensure the location matches the location of the function identifier
CHECK_EQ ( err . location , Location ( Position ( 4 , 8 ) , Position ( 4 , 18 ) ) ) ;
auto acm = get < CountMismatch > ( err ) ;
REQUIRE ( acm ) ;
CHECK_EQ ( 2 , acm - > expected ) ;
CHECK_EQ ( 1 , acm - > actual ) ;
}
{
TypeError err = result . errors [ 1 ] ;
// Ensure the location matches the location of the expression returning the function
CHECK_EQ ( err . location , Location ( Position ( 9 , 8 ) , Position ( 9 , 23 ) ) ) ;
auto acm = get < CountMismatch > ( err ) ;
REQUIRE ( acm ) ;
CHECK_EQ ( 2 , acm - > expected ) ;
CHECK_EQ ( 0 , acm - > actual ) ;
}
}
2022-03-18 08:46:04 +08:00
TEST_CASE_FIXTURE ( Fixture , " recursive_function " )
{
CheckResult result = check ( R " (
function count ( n : number )
if n = = 0 then
return 0
else
return count ( n - 1 )
end
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " lambda_form_of_local_function_cannot_be_recursive " )
{
CheckResult result = check ( R " (
local f = function ( ) return f ( ) end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " recursive_local_function " )
{
CheckResult result = check ( R " (
local function count ( n : number )
if n = = 0 then
return 0
else
return count ( n - 1 )
end
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
// FIXME: This and the above case get handled very differently. It's pretty dumb.
// We really should unify the two code paths, probably by deleting AstStatFunction.
TEST_CASE_FIXTURE ( Fixture , " another_recursive_local_function " )
{
CheckResult result = check ( R " (
local count
function count ( n : number )
if n = = 0 then
return 0
else
return count ( n - 1 )
end
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2023-05-20 03:37:30 +08:00
// We had a bug where we'd look up the type of a recursive call using the DFG,
// not the bindings tables. As a result, we would erroneously use the
// generalized type of foo() in this recursive fragment. This creates a
// constraint cycle that doesn't always work itself out.
//
// The fix is for the DFG node within the scope of foo() to retain the
// ungeneralized type of foo.
TEST_CASE_FIXTURE ( BuiltinsFixture , " recursive_calls_must_refer_to_the_ungeneralized_type " )
{
CheckResult result = check ( R " (
function foo ( )
string . format ( ' % s : % s ' , " 51 " , foo ( ) )
end
) " );
}
2022-03-18 08:46:04 +08:00
TEST_CASE_FIXTURE ( Fixture , " cyclic_function_type_in_rets " )
{
CheckResult result = check ( R " (
function f ( )
return f
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
CHECK_EQ ( " t1 where t1 = () -> t1 " , toString ( requireType ( " f " ) ) ) ;
}
TEST_CASE_FIXTURE ( Fixture , " another_higher_order_function " )
{
CheckResult result = check ( R " (
local Get_des
function Get_des ( func )
Get_des ( func )
end
local function f ( d )
d : IsA ( " BasePart " )
d . Parent : FindFirstChild ( " Humanoid " )
d : IsA ( " Decal " )
end
Get_des ( f )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " another_other_higher_order_function " )
{
2024-08-31 04:16:51 +08:00
if ( FFlag : : LuauSolverV2 )
2024-08-17 02:29:33 +08:00
{
CheckResult result = check ( R " (
local function f ( d )
d : foo ( )
d : foo ( )
end
) " );
2022-03-18 08:46:04 +08:00
2024-08-17 02:29:33 +08:00
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
else
{
CheckResult result = check ( R " (
local d
d : foo ( )
d : foo ( )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2022-03-18 08:46:04 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " local_function " )
{
CheckResult result = check ( R " (
function f ( )
return 8
end
function g ( )
local function f ( )
return ' hello '
end
return f
end
local h = g ( )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
TypeId h = follow ( requireType ( " h " ) ) ;
2023-01-05 04:53:17 +08:00
const FunctionType * ftv = get < FunctionType > ( h ) ;
2022-03-18 08:46:04 +08:00
REQUIRE ( ftv ! = nullptr ) ;
2022-06-17 09:05:14 +08:00
std : : optional < TypeId > rt = first ( ftv - > retTypes ) ;
2022-03-18 08:46:04 +08:00
REQUIRE ( bool ( rt ) ) ;
TypeId retType = follow ( * rt ) ;
2023-01-05 04:53:17 +08:00
CHECK_EQ ( PrimitiveType : : String , getPrimitiveType ( retType ) ) ;
2022-03-18 08:46:04 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " func_expr_doesnt_leak_free " )
{
CheckResult result = check ( R " (
local p = function ( x ) return x end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
2023-01-05 04:53:17 +08:00
const Luau : : FunctionType * fn = get < FunctionType > ( requireType ( " p " ) ) ;
2022-03-18 08:46:04 +08:00
REQUIRE ( fn ) ;
2022-06-17 09:05:14 +08:00
auto ret = first ( fn - > retTypes ) ;
2022-03-18 08:46:04 +08:00
REQUIRE ( ret ) ;
2023-01-05 04:53:17 +08:00
REQUIRE ( get < GenericType > ( follow ( * ret ) ) ) ;
2022-03-18 08:46:04 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " first_argument_can_be_optional " )
{
CheckResult result = check ( R " (
local T = { }
function T . new ( a : number ? , b : number ? , c : number ? ) return 5 end
local m = T . new ( )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
dumpErrors ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " it_is_ok_not_to_supply_enough_retvals " )
{
CheckResult result = check ( R " (
function get_two ( ) return 5 , 6 end
local a = get_two ( )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
dumpErrors ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " duplicate_functions2 " )
{
CheckResult result = check ( R " (
function foo ( ) end
function bar ( )
local function foo ( ) end
end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 0 , result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " duplicate_functions_allowed_in_nonstrict " )
{
CheckResult result = check ( R " (
- - ! nonstrict
function foo ( ) end
function foo ( ) end
function bar ( )
local function foo ( ) end
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " duplicate_functions_with_different_signatures_not_allowed_in_nonstrict " )
{
2024-08-17 02:29:33 +08:00
// This is not part of the spec for the new non-strict mode currently.
2024-11-02 03:06:07 +08:00
DOES_NOT_PASS_NEW_SOLVER_GUARD ( ) ;
2024-08-17 02:29:33 +08:00
2022-03-18 08:46:04 +08:00
CheckResult result = check ( R " (
- - ! nonstrict
function foo ( ) : number
return 1
end
foo ( )
function foo ( n : number ) : number
return 2
end
foo ( )
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
TypeMismatch * tm = get < TypeMismatch > ( result . errors [ 0 ] ) ;
REQUIRE ( tm ) ;
CHECK_EQ ( " () -> number " , toString ( tm - > wantedType ) ) ;
CHECK_EQ ( " (number) -> number " , toString ( tm - > givenType ) ) ;
}
TEST_CASE_FIXTURE ( Fixture , " complicated_return_types_require_an_explicit_annotation " )
{
CheckResult result = check ( R " (
local i = 0
function most_of_the_natural_numbers ( ) : number ?
if i < 10 then
2024-08-17 02:29:33 +08:00
i + = 1
2022-03-18 08:46:04 +08:00
return i
else
return nil
end
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
2022-12-03 02:09:59 +08:00
TypeId ty = requireType ( " most_of_the_natural_numbers " ) ;
2023-01-05 04:53:17 +08:00
const FunctionType * functionType = get < FunctionType > ( ty ) ;
2022-12-03 02:09:59 +08:00
REQUIRE_MESSAGE ( functionType , " Expected function but got " < < toString ( ty ) ) ;
2022-03-18 08:46:04 +08:00
2022-06-17 09:05:14 +08:00
std : : optional < TypeId > retType = first ( functionType - > retTypes ) ;
2022-03-18 08:46:04 +08:00
REQUIRE ( retType ) ;
2023-01-05 04:53:17 +08:00
CHECK ( get < UnionType > ( * retType ) ) ;
2022-03-18 08:46:04 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " infer_higher_order_function " )
{
CheckResult result = check ( R " (
function apply ( f , x )
return f ( x )
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
2023-01-05 04:53:17 +08:00
const FunctionType * ftv = get < FunctionType > ( requireType ( " apply " ) ) ;
2022-03-18 08:46:04 +08:00
REQUIRE ( ftv ! = nullptr ) ;
std : : vector < TypeId > argVec = flatten ( ftv - > argTypes ) . first ;
REQUIRE_EQ ( 2 , argVec . size ( ) ) ;
2023-01-05 04:53:17 +08:00
const FunctionType * fType = get < FunctionType > ( follow ( argVec [ 0 ] ) ) ;
2022-11-11 06:53:13 +08:00
REQUIRE_MESSAGE ( fType ! = nullptr , " Expected a function but got " < < toString ( argVec [ 0 ] ) ) ;
2022-03-18 08:46:04 +08:00
std : : vector < TypeId > fArgs = flatten ( fType - > argTypes ) . first ;
2022-04-15 07:57:43 +08:00
TypeId xType = follow ( argVec [ 1 ] ) ;
2022-03-18 08:46:04 +08:00
CHECK_EQ ( 1 , fArgs . size ( ) ) ;
2022-04-15 07:57:43 +08:00
CHECK_EQ ( xType , follow ( fArgs [ 0 ] ) ) ;
2022-03-18 08:46:04 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " higher_order_function_2 " )
{
CheckResult result = check ( R " (
function bottomupmerge ( comp , a , b , left , mid , right )
local i , j = left , mid
for k = left , right do
if i < mid and ( j > right or not comp ( a [ j ] , a [ i ] ) ) then
b [ k ] = a [ i ]
i = i + 1
else
b [ k ] = a [ j ]
j = j + 1
end
end
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
2023-01-05 04:53:17 +08:00
const FunctionType * ftv = get < FunctionType > ( requireType ( " bottomupmerge " ) ) ;
2022-03-18 08:46:04 +08:00
REQUIRE ( ftv ! = nullptr ) ;
std : : vector < TypeId > argVec = flatten ( ftv - > argTypes ) . first ;
REQUIRE_EQ ( 6 , argVec . size ( ) ) ;
2023-01-05 04:53:17 +08:00
const FunctionType * fType = get < FunctionType > ( follow ( argVec [ 0 ] ) ) ;
2022-03-18 08:46:04 +08:00
REQUIRE ( fType ! = nullptr ) ;
}
TEST_CASE_FIXTURE ( Fixture , " higher_order_function_3 " )
{
CheckResult result = check ( R " (
function swap ( p )
local t = p [ 0 ]
p [ 0 ] = p [ 1 ]
p [ 1 ] = t
return nil
end
function swapTwice ( p )
swap ( p )
swap ( p )
return p
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
2023-01-05 04:53:17 +08:00
const FunctionType * ftv = get < FunctionType > ( requireType ( " swapTwice " ) ) ;
2022-03-18 08:46:04 +08:00
REQUIRE ( ftv ! = nullptr ) ;
std : : vector < TypeId > argVec = flatten ( ftv - > argTypes ) . first ;
REQUIRE_EQ ( 1 , argVec . size ( ) ) ;
2023-01-05 04:53:17 +08:00
const TableType * argType = get < TableType > ( follow ( argVec [ 0 ] ) ) ;
2024-03-31 07:14:44 +08:00
REQUIRE_MESSAGE ( argType ! = nullptr , argVec [ 0 ] ) ;
2022-03-18 08:46:04 +08:00
CHECK ( bool ( argType - > indexer ) ) ;
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " higher_order_function_4 " )
2022-03-18 08:46:04 +08:00
{
CheckResult result = check ( R " (
function bottomupmerge ( comp , a , b , left , mid , right )
local i , j = left , mid
for k = left , right do
if i < mid and ( j > right or not comp ( a [ j ] , a [ i ] ) ) then
b [ k ] = a [ i ]
i = i + 1
else
b [ k ] = a [ j ]
j = j + 1
end
end
end
2024-08-17 02:29:33 +08:00
function mergesort < T > ( arr : { T } , comp : ( T , T ) - > boolean )
2022-03-18 08:46:04 +08:00
local work = { }
for i = 1 , # arr do
work [ i ] = arr [ i ]
end
local width = 1
while width < # arr do
for i = 1 , # arr , 2 * width do
bottomupmerge ( comp , arr , work , i , math . min ( i + width , # arr ) , math . min ( i + 2 * width - 1 , # arr ) )
end
local temp = work
work = arr
arr = temp
width = width * 2
end
return arr
end
) " );
2024-08-24 00:35:30 +08:00
LUAU_REQUIRE_NO_ERRORS ( result ) ;
2022-03-18 08:46:04 +08:00
/*
* mergesort takes two arguments : an array of some type T and a function that takes two Ts .
* We must assert that these two types are in fact the same type .
* In other words , comp ( arr [ x ] , arr [ y ] ) is well - typed .
*/
2023-01-05 04:53:17 +08:00
const FunctionType * ftv = get < FunctionType > ( requireType ( " mergesort " ) ) ;
2022-03-18 08:46:04 +08:00
REQUIRE ( ftv ! = nullptr ) ;
std : : vector < TypeId > argVec = flatten ( ftv - > argTypes ) . first ;
REQUIRE_EQ ( 2 , argVec . size ( ) ) ;
2023-01-05 04:53:17 +08:00
const TableType * arg0 = get < TableType > ( follow ( argVec [ 0 ] ) ) ;
2022-03-18 08:46:04 +08:00
REQUIRE ( arg0 ! = nullptr ) ;
REQUIRE ( bool ( arg0 - > indexer ) ) ;
2023-01-05 04:53:17 +08:00
const FunctionType * arg1 = get < FunctionType > ( follow ( argVec [ 1 ] ) ) ;
2022-03-18 08:46:04 +08:00
REQUIRE ( arg1 ! = nullptr ) ;
REQUIRE_EQ ( 2 , size ( arg1 - > argTypes ) ) ;
std : : vector < TypeId > arg1Args = flatten ( arg1 - > argTypes ) . first ;
CHECK_EQ ( * arg0 - > indexer - > indexResultType , * arg1Args [ 0 ] ) ;
CHECK_EQ ( * arg0 - > indexer - > indexResultType , * arg1Args [ 1 ] ) ;
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " mutual_recursion " )
2022-03-18 08:46:04 +08:00
{
CheckResult result = check ( R " (
- - ! strict
function newPlayerCharacter ( )
startGui ( ) - - Unknown symbol ' startGui '
end
local characterAddedConnection : any
function startGui ( )
characterAddedConnection = game : GetService ( " Players " ) . LocalPlayer . CharacterAdded : connect ( newPlayerCharacter )
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " toposort_doesnt_break_mutual_recursion " )
2022-03-18 08:46:04 +08:00
{
CheckResult result = check ( R " (
- - ! strict
local x = nil
function f ( ) g ( ) end
- - make sure print ( x ) doesn ' t get toposorted here , breaking the mutual block
function g ( ) x = f end
print ( x )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
dumpErrors ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " check_function_before_lambda_that_uses_it " )
{
CheckResult result = check ( R " (
- - ! nonstrict
function f ( )
return 114
end
return function ( )
2022-07-22 05:16:54 +08:00
return f ( ) : andThen ( )
2022-03-18 08:46:04 +08:00
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " it_is_ok_to_oversaturate_a_higher_order_function_argument " )
2022-03-18 08:46:04 +08:00
{
CheckResult result = check ( R " (
function onerror ( ) end
function foo ( ) end
xpcall ( foo , onerror )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " another_indirect_function_case_where_it_is_ok_to_provide_too_many_arguments " )
{
CheckResult result = check ( R " (
local mycb : ( number , number ) - > ( )
function f ( ) end
mycb = f
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " report_exiting_without_return_nonstrict " )
{
2024-08-17 02:29:33 +08:00
// new non-strict mode spec does not include this error yet.
2024-11-02 03:06:07 +08:00
DOES_NOT_PASS_NEW_SOLVER_GUARD ( ) ;
2024-08-17 02:29:33 +08:00
2022-03-18 08:46:04 +08:00
CheckResult result = check ( R " (
- - ! nonstrict
local function f1 ( v ) : number ?
if v then
return 1
end
end
local function f2 ( v )
if v then
return 1
end
end
local function f3 ( v ) : ( )
if v then
return
end
end
local function f4 ( v )
if v then
return
end
end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
FunctionExitsWithoutReturning * err = get < FunctionExitsWithoutReturning > ( result . errors [ 0 ] ) ;
CHECK ( err ) ;
}
TEST_CASE_FIXTURE ( Fixture , " report_exiting_without_return_strict " )
{
CheckResult result = check ( R " (
- - ! strict
local function f1 ( v ) : number ?
if v then
return 1
end
end
local function f2 ( v )
if v then
return 1
end
end
local function f3 ( v ) : ( )
if v then
return
end
end
local function f4 ( v )
if v then
return
end
end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 2 , result ) ;
FunctionExitsWithoutReturning * annotatedErr = get < FunctionExitsWithoutReturning > ( result . errors [ 0 ] ) ;
CHECK ( annotatedErr ) ;
FunctionExitsWithoutReturning * inferredErr = get < FunctionExitsWithoutReturning > ( result . errors [ 1 ] ) ;
CHECK ( inferredErr ) ;
}
TEST_CASE_FIXTURE ( Fixture , " calling_function_with_incorrect_argument_type_yields_errors_spanning_argument " )
{
CheckResult result = check ( R " (
function foo ( a : number , b : string ) end
foo ( " Test " , 123 )
) " );
LUAU_REQUIRE_ERROR_COUNT ( 2 , result ) ;
2024-08-02 22:30:04 +08:00
CHECK_EQ (
result . errors [ 0 ] ,
( TypeError {
Location { Position { 3 , 12 } , Position { 3 , 18 } } ,
TypeMismatch {
builtinTypes - > numberType ,
builtinTypes - > stringType ,
}
} )
) ;
CHECK_EQ (
result . errors [ 1 ] ,
( TypeError {
Location { Position { 3 , 20 } , Position { 3 , 23 } } ,
TypeMismatch {
builtinTypes - > stringType ,
builtinTypes - > numberType ,
}
} )
) ;
2022-03-18 08:46:04 +08:00
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " calling_function_with_anytypepack_doesnt_leak_free_types " )
2022-03-18 08:46:04 +08:00
{
CheckResult result = check ( R " (
- - ! nonstrict
2022-07-22 05:16:54 +08:00
function Test ( a )
2022-03-18 08:46:04 +08:00
return 1 , " "
end
2022-07-22 05:16:54 +08:00
2022-03-18 08:46:04 +08:00
local tab = { }
table . insert ( tab , Test ( 1 ) ) ;
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
ToStringOptions opts ;
opts . exhaustive = true ;
opts . maxTableLength = 0 ;
2024-08-31 04:16:51 +08:00
if ( FFlag : : LuauSolverV2 )
2024-08-17 02:29:33 +08:00
CHECK_EQ ( " {string} " , toString ( requireType ( " tab " ) , opts ) ) ;
else
CHECK_EQ ( " {any} " , toString ( requireType ( " tab " ) , opts ) ) ;
2022-03-18 08:46:04 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " too_many_return_values " )
{
2024-08-17 02:29:33 +08:00
// FIXME: CLI-116157 variadic and generic type packs seem to be interacting incorrectly.
2024-11-02 03:06:07 +08:00
DOES_NOT_PASS_NEW_SOLVER_GUARD ( ) ;
2024-08-17 02:29:33 +08:00
2022-03-18 08:46:04 +08:00
CheckResult result = check ( R " (
- - ! strict
function f ( )
return 55
end
local a , b = f ( )
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CountMismatch * acm = get < CountMismatch > ( result . errors [ 0 ] ) ;
REQUIRE ( acm ) ;
2022-09-30 06:23:10 +08:00
CHECK_EQ ( acm - > context , CountMismatch : : FunctionResult ) ;
CHECK_EQ ( acm - > expected , 1 ) ;
CHECK_EQ ( acm - > actual , 2 ) ;
}
TEST_CASE_FIXTURE ( Fixture , " too_many_return_values_in_parentheses " )
{
2024-08-17 02:29:33 +08:00
// FIXME: CLI-116157 variadic and generic type packs seem to be interacting incorrectly.
2024-11-02 03:06:07 +08:00
DOES_NOT_PASS_NEW_SOLVER_GUARD ( ) ;
2024-08-17 02:29:33 +08:00
2022-09-30 06:23:10 +08:00
CheckResult result = check ( R " (
- - ! strict
function f ( )
return 55
end
local a , b = ( f ( ) )
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CountMismatch * acm = get < CountMismatch > ( result . errors [ 0 ] ) ;
REQUIRE ( acm ) ;
CHECK_EQ ( acm - > context , CountMismatch : : FunctionResult ) ;
CHECK_EQ ( acm - > expected , 1 ) ;
CHECK_EQ ( acm - > actual , 2 ) ;
}
TEST_CASE_FIXTURE ( Fixture , " too_many_return_values_no_function " )
{
2024-08-17 02:29:33 +08:00
// FIXME: CLI-116157 variadic and generic type packs seem to be interacting incorrectly.
2024-11-02 03:06:07 +08:00
DOES_NOT_PASS_NEW_SOLVER_GUARD ( ) ;
2024-08-17 02:29:33 +08:00
2022-09-30 06:23:10 +08:00
CheckResult result = check ( R " (
- - ! strict
local a , b = 55
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CountMismatch * acm = get < CountMismatch > ( result . errors [ 0 ] ) ;
REQUIRE ( acm ) ;
CHECK_EQ ( acm - > context , CountMismatch : : ExprListResult ) ;
2022-03-18 08:46:04 +08:00
CHECK_EQ ( acm - > expected , 1 ) ;
CHECK_EQ ( acm - > actual , 2 ) ;
}
TEST_CASE_FIXTURE ( Fixture , " ignored_return_values " )
{
CheckResult result = check ( R " (
- - ! strict
function f ( )
return 55 , " "
end
local a = f ( )
) " );
LUAU_REQUIRE_ERROR_COUNT ( 0 , result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " function_does_not_return_enough_values " )
{
CheckResult result = check ( R " (
- - ! strict
function f ( ) : ( number , string )
return 55
end
) " );
2024-08-31 04:16:51 +08:00
if ( FFlag : : LuauSolverV2 )
2024-08-17 02:29:33 +08:00
{
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
2022-03-18 08:46:04 +08:00
2024-08-17 02:29:33 +08:00
auto tpm = get < TypePackMismatch > ( result . errors [ 0 ] ) ;
REQUIRE ( tpm ) ;
CHECK ( " number, string " = = toString ( tpm - > wantedTp ) ) ;
CHECK ( " number " = = toString ( tpm - > givenTp ) ) ;
}
else
{
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CountMismatch * acm = get < CountMismatch > ( result . errors [ 0 ] ) ;
REQUIRE ( acm ) ;
CHECK_EQ ( acm - > context , CountMismatch : : Return ) ;
CHECK_EQ ( acm - > expected , 2 ) ;
CHECK_EQ ( acm - > actual , 1 ) ;
}
2022-03-18 08:46:04 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " function_cast_error_uses_correct_language " )
{
CheckResult result = check ( R " (
function foo ( a , b ) : number
return 0
end
local a : ( string ) - > number = foo
local b : ( number , number ) - > ( number , number ) = foo
local c : ( string , number ) - > number = foo - - no error
) " );
LUAU_REQUIRE_ERROR_COUNT ( 2 , result ) ;
auto tm1 = get < TypeMismatch > ( result . errors [ 0 ] ) ;
REQUIRE ( tm1 ) ;
CHECK_EQ ( " (string) -> number " , toString ( tm1 - > wantedType ) ) ;
2024-08-31 04:16:51 +08:00
if ( FFlag : : LuauSolverV2 )
2024-08-17 02:29:33 +08:00
CHECK_EQ ( " (unknown, unknown) -> number " , toString ( tm1 - > givenType ) ) ;
else
CHECK_EQ ( " (string, *error-type*) -> number " , toString ( tm1 - > givenType ) ) ;
2022-03-18 08:46:04 +08:00
auto tm2 = get < TypeMismatch > ( result . errors [ 1 ] ) ;
REQUIRE ( tm2 ) ;
CHECK_EQ ( " (number, number) -> (number, number) " , toString ( tm2 - > wantedType ) ) ;
2024-08-31 04:16:51 +08:00
if ( FFlag : : LuauSolverV2 )
2024-08-17 02:29:33 +08:00
CHECK_EQ ( " (unknown, unknown) -> number " , toString ( tm1 - > givenType ) ) ;
else
CHECK_EQ ( " (string, *error-type*) -> number " , toString ( tm2 - > givenType ) ) ;
2022-03-18 08:46:04 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " no_lossy_function_type " )
{
CheckResult result = check ( R " (
- - ! strict
local tbl = { }
function tbl : abc ( a : number , b : number )
return a
end
tbl : abc ( 1 , 2 ) - - Line 6
- - | Column 14
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
TypeId type = requireTypeAtPosition ( Position ( 6 , 14 ) ) ;
2024-08-31 04:16:51 +08:00
if ( FFlag : : LuauSolverV2 )
2024-08-17 02:29:33 +08:00
CHECK_EQ ( " (unknown, number, number) -> number " , toString ( type ) ) ;
else
CHECK_EQ ( " (tbl, number, number) -> number " , toString ( type ) ) ;
2023-05-20 03:37:30 +08:00
auto ftv = get < FunctionType > ( follow ( type ) ) ;
2022-03-18 08:46:04 +08:00
REQUIRE ( ftv ) ;
CHECK ( ftv - > hasSelf ) ;
}
TEST_CASE_FIXTURE ( Fixture , " record_matching_overload " )
{
CheckResult result = check ( R " (
type Overload = ( ( string ) - > string ) & ( ( number ) - > number )
local abc : Overload
abc ( 1 )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
// AstExprCall is the node that has the overload stored on it.
// findTypeAtPosition will look at the AstExprLocal, but this is not what
// we want to look at.
std : : vector < AstNode * > ancestry = findAstAncestryOfPosition ( * getMainSourceModule ( ) , Position ( 3 , 10 ) ) ;
REQUIRE_GE ( ancestry . size ( ) , 2 ) ;
AstExpr * parentExpr = ancestry [ ancestry . size ( ) - 2 ] - > asExpr ( ) ;
REQUIRE ( bool ( parentExpr ) ) ;
REQUIRE ( parentExpr - > is < AstExprCall > ( ) ) ;
ModulePtr module = getMainModule ( ) ;
auto it = module - > astOverloadResolvedTypes . find ( parentExpr ) ;
REQUIRE ( it ) ;
CHECK_EQ ( toString ( * it ) , " (number) -> number " ) ;
}
TEST_CASE_FIXTURE ( Fixture , " return_type_by_overload " )
{
CheckResult result = check ( R " (
type Overload = ( ( string ) - > string ) & ( ( number , number ) - > number )
local abc : Overload
local x = abc ( true )
local y = abc ( true , true )
local z = abc ( true , true , true )
) " );
LUAU_REQUIRE_ERRORS ( result ) ;
CHECK_EQ ( " string " , toString ( requireType ( " x " ) ) ) ;
2024-08-17 02:29:33 +08:00
// the new solver does not currently "favor" arity-matching overloads when the call itself is ill-typed.
2024-08-31 04:16:51 +08:00
if ( FFlag : : LuauSolverV2 )
2024-08-17 02:29:33 +08:00
CHECK_EQ ( " string " , toString ( requireType ( " y " ) ) ) ;
else
CHECK_EQ ( " number " , toString ( requireType ( " y " ) ) ) ;
2022-03-18 08:46:04 +08:00
// Should this be string|number?
CHECK_EQ ( " string " , toString ( requireType ( " z " ) ) ) ;
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " infer_anonymous_function_arguments " )
2022-03-18 08:46:04 +08:00
{
2024-08-17 02:29:33 +08:00
// FIXME: CLI-116133 bidirectional type inference needs to push expected types in for higher-order function calls
2024-11-02 03:06:07 +08:00
DOES_NOT_PASS_NEW_SOLVER_GUARD ( ) ;
2024-08-17 02:29:33 +08:00
2022-03-18 08:46:04 +08:00
// Simple direct arg to arg propagation
CheckResult result = check ( R " (
type Table = { x : number , y : number }
local function f ( a : ( Table ) - > number ) return a ( { x = 1 , y = 2 } ) end
f ( function ( a ) return a . x + a . y end )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
// An optional function is accepted, but since we already provide a function, nil can be ignored
result = check ( R " (
type Table = { x : number , y : number }
local function f ( a : ( ( Table ) - > number ) ? ) if a then return a ( { x = 1 , y = 2 } ) else return 0 end end
f ( function ( a ) return a . x + a . y end )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
// Make sure self calls match correct index
result = check ( R " (
type Table = { x : number , y : number }
local x = { }
x . b = { x = 1 , y = 2 }
function x : f ( a : ( Table ) - > number ) return a ( self . b ) end
x : f ( function ( a ) return a . x + a . y end )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
// Mix inferred and explicit argument types
result = check ( R " (
function f ( a : ( a : number , b : number , c : boolean ) - > number ) return a ( 1 , 2 , true ) end
f ( function ( a : number , b , c ) return c and a + b or b - a end )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
// Anonymous function has a variadic pack
result = check ( R " (
type Table = { x : number , y : number }
local function f ( a : ( Table ) - > number ) return a ( { x = 1 , y = 2 } ) end
f ( function ( . . . ) return select ( 1 , . . . ) . z end )
) " );
LUAU_REQUIRE_ERRORS ( result ) ;
CHECK_EQ ( " Key 'z' not found in table 'Table' " , toString ( result . errors [ 0 ] ) ) ;
// Can't accept more arguments than provided
result = check ( R " (
function f ( a : ( a : number , b : number ) - > number ) return a ( 1 , 2 ) end
f ( function ( a , b , c , . . . ) return a + b end )
) " );
LUAU_REQUIRE_ERRORS ( result ) ;
2023-08-11 22:42:37 +08:00
std : : string expected ;
2022-10-07 08:23:29 +08:00
if ( FFlag : : LuauInstantiateInSubtyping )
{
2023-08-11 22:42:37 +08:00
expected = R " (Type
' < a > ( number , number , a ) - > number '
could not be converted into
' ( number , number ) - > number '
2022-03-18 08:46:04 +08:00
caused by :
2023-08-11 22:42:37 +08:00
Argument count mismatch . Function expects 3 arguments , but only 2 are specified ) " ;
2022-10-07 08:23:29 +08:00
}
else
{
2023-08-11 22:42:37 +08:00
expected = R " (Type
2024-08-02 22:30:04 +08:00
' ( number , number , * error - type * ) - > number '
2023-08-11 22:42:37 +08:00
could not be converted into
' ( number , number ) - > number '
2022-10-07 08:23:29 +08:00
caused by :
2023-08-11 22:42:37 +08:00
Argument count mismatch . Function expects 3 arguments , but only 2 are specified ) " ;
2022-10-07 08:23:29 +08:00
}
2022-03-18 08:46:04 +08:00
2023-08-11 22:42:37 +08:00
CHECK_EQ ( expected , toString ( result . errors [ 0 ] ) ) ;
2022-03-18 08:46:04 +08:00
// Infer from variadic packs into elements
result = check ( R " (
function f ( a : ( . . . number ) - > number ) return a ( 1 , 2 ) end
f ( function ( a , b ) return a + b end )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
// Infer from variadic packs into variadic packs
result = check ( R " (
type Table = { x : number , y : number }
function f ( a : ( . . . Table ) - > number ) return a ( { x = 1 , y = 2 } , { x = 3 , y = 4 } ) end
f ( function ( a , . . . ) local b = . . . return b . z end )
) " );
LUAU_REQUIRE_ERRORS ( result ) ;
CHECK_EQ ( " Key 'z' not found in table 'Table' " , toString ( result . errors [ 0 ] ) ) ;
// Return type inference
result = check ( R " (
type Table = { x : number , y : number }
function f ( a : ( number ) - > Table ) return a ( 4 ) end
f ( function ( x ) return x * 2 end )
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CHECK_EQ ( " Type 'number' could not be converted into 'Table' " , toString ( result . errors [ 0 ] ) ) ;
2022-10-15 03:48:41 +08:00
// Return type doesn't inference 'nil'
result = check ( R " (
function f ( a : ( number ) - > nil ) return a ( 4 ) end
f ( function ( x ) print ( x ) end )
) " );
2022-03-18 08:46:04 +08:00
2022-10-15 03:48:41 +08:00
LUAU_REQUIRE_NO_ERRORS ( result ) ;
2022-03-18 08:46:04 +08:00
}
2023-08-19 02:15:41 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " infer_generic_function_function_argument " )
{
2024-08-17 02:29:33 +08:00
// FIXME: CLI-116133 bidirectional type inference needs to push expected types in for higher-order function calls
2024-11-02 03:06:07 +08:00
DOES_NOT_PASS_NEW_SOLVER_GUARD ( ) ;
2024-08-17 02:29:33 +08:00
2023-08-19 02:15:41 +08:00
CheckResult result = check ( R " (
local function sum < a > ( x : a , y : a , f : ( a , a ) - > a ) return f ( x , y ) end
return sum ( 2 , 3 , function ( a , b ) return a + b end )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
result = check ( R " (
local function map < a , b > ( arr : { a } , f : ( a ) - > b ) local r = { } for i , v in ipairs ( arr ) do table . insert ( r , f ( v ) ) end return r end
local a = { 1 , 2 , 3 }
local r = map ( a , function ( a ) return a + a > 100 end )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
REQUIRE_EQ ( " {boolean} " , toString ( requireType ( " r " ) ) ) ;
check ( R " (
local function foldl < a , b > ( arr : { a } , init : b , f : ( b , a ) - > b ) local r = init for i , v in ipairs ( arr ) do r = f ( r , v ) end return r end
local a = { 1 , 2 , 3 }
local r = foldl ( a , { s = 0 , c = 0 } , function ( a , b ) return { s = a . s + b , c = a . c + 1 } end )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
REQUIRE_EQ ( " { c: number, s: number } " , toString ( requireType ( " r " ) ) ) ;
}
TEST_CASE_FIXTURE ( Fixture , " infer_generic_function_function_argument_overloaded " )
{
2024-08-17 02:29:33 +08:00
// FIXME: CLI-116133 bidirectional type inference needs to push expected types in for higher-order function calls
2024-11-02 03:06:07 +08:00
DOES_NOT_PASS_NEW_SOLVER_GUARD ( ) ;
2024-08-17 02:29:33 +08:00
2023-08-19 02:15:41 +08:00
CheckResult result = check ( R " (
local function g1 < T > ( a : T , f : ( T ) - > T ) return f ( a ) end
local function g2 < T > ( a : T , b : T , f : ( T , T ) - > T ) return f ( a , b ) end
local g12 : typeof ( g1 ) & typeof ( g2 )
g12 ( 1 , function ( x ) return x + x end )
g12 ( 1 , 2 , function ( x , y ) return x + y end )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
result = check ( R " (
local function g1 < T > ( a : T , f : ( T ) - > T ) return f ( a ) end
local function g2 < T > ( a : T , b : T , f : ( T , T ) - > T ) return f ( a , b ) end
local g12 : typeof ( g1 ) & typeof ( g2 )
g12 ( { x = 1 } , function ( x ) return { x = - x . x } end )
g12 ( { x = 1 } , { x = 2 } , function ( x , y ) return { x = x . x + y . x } end )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( BuiltinsFixture , " infer_generic_lib_function_function_argument " )
{
CheckResult result = check ( R " (
local a = { { x = 4 } , { x = 7 } , { x = 1 } }
table . sort ( a , function ( x , y ) return x . x < y . x end )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2023-04-15 02:06:22 +08:00
TEST_CASE_FIXTURE ( Fixture , " variadic_any_is_compatible_with_a_generic_TypePack " )
{
CheckResult result = check ( R " (
- - ! strict
local function f ( . . . ) return . . . end
local g = function ( . . . ) return f ( . . . ) end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2024-01-13 06:25:27 +08:00
// https://github.com/luau-lang/luau/issues/767
2023-04-15 02:06:22 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " variadic_any_is_compatible_with_a_generic_TypePack_2 " )
{
CheckResult result = check ( R " (
local function somethingThatsAny ( . . . : any )
print ( . . . )
end
local function x < T . . . > ( . . . : T . . . )
somethingThatsAny ( . . . ) - - Failed to unify variadic type packs
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2022-03-18 08:46:04 +08:00
TEST_CASE_FIXTURE ( Fixture , " infer_anonymous_function_arguments_outside_call " )
{
2024-08-17 02:29:33 +08:00
// FIXME: CLI-116133 bidirectional type inference needs to push expected types in for higher-order function calls
2024-11-02 03:06:07 +08:00
DOES_NOT_PASS_NEW_SOLVER_GUARD ( ) ;
2024-08-17 02:29:33 +08:00
2022-03-18 08:46:04 +08:00
CheckResult result = check ( R " (
type Table = { x : number , y : number }
local f : ( Table ) - > number = function ( t ) return t . x + t . y end
type TableWithFunc = { x : number , y : number , f : ( number , number ) - > number }
local a : TableWithFunc = { x = 3 , y = 4 , f = function ( a , b ) return a + b end }
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " infer_return_value_type " )
{
CheckResult result = check ( R " (
local function f ( ) : { string | number }
return { 1 , " b " , 3 }
end
local function g ( ) : ( number , { string | number } )
return 4 , { 1 , " b " , 3 }
end
local function h ( ) : . . . { string | number }
return { 4 } , { 1 , " b " , 3 } , { " s " }
end
local function i ( ) : . . . { string | number }
return { 1 , " b " , 3 } , h ( )
end
) " );
2024-08-17 02:29:33 +08:00
// `h` regresses in the new solver, the return type is not being pushed into the body.
2024-08-31 04:16:51 +08:00
if ( FFlag : : LuauSolverV2 )
2024-08-17 02:29:33 +08:00
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
else
LUAU_REQUIRE_NO_ERRORS ( result ) ;
2022-03-18 08:46:04 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " error_detailed_function_mismatch_arg_count " )
{
2024-08-17 02:29:33 +08:00
// FIXME: CLI-116111 test disabled until type path stringification is improved
2024-11-02 03:06:07 +08:00
DOES_NOT_PASS_NEW_SOLVER_GUARD ( ) ;
2024-08-17 02:29:33 +08:00
2022-03-18 08:46:04 +08:00
CheckResult result = check ( R " (
type A = ( number , number ) - > string
type B = ( number ) - > string
local a : A
local b : B = a
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
2023-08-11 22:42:37 +08:00
const std : : string expected = R " (Type
' ( number , number ) - > string '
could not be converted into
' ( number ) - > string '
2022-03-18 08:46:04 +08:00
caused by :
2023-08-11 22:42:37 +08:00
Argument count mismatch . Function expects 2 arguments , but only 1 is specified ) " ;
CHECK_EQ ( expected , toString ( result . errors [ 0 ] ) ) ;
2022-03-18 08:46:04 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " error_detailed_function_mismatch_arg " )
{
2024-08-17 02:29:33 +08:00
// FIXME: CLI-116111 test disabled until type path stringification is improved
2024-11-02 03:06:07 +08:00
DOES_NOT_PASS_NEW_SOLVER_GUARD ( ) ;
2024-08-17 02:29:33 +08:00
2022-03-18 08:46:04 +08:00
CheckResult result = check ( R " (
type A = ( number , number ) - > string
type B = ( number , string ) - > string
local a : A
local b : B = a
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
2023-08-11 22:42:37 +08:00
const std : : string expected = R " (Type
' ( number , number ) - > string '
could not be converted into
' ( number , string ) - > string '
2022-03-18 08:46:04 +08:00
caused by :
2023-10-14 04:20:12 +08:00
Argument # 2 type is not compatible .
2023-08-11 22:42:37 +08:00
Type ' string ' could not be converted into ' number ' ) " ;
CHECK_EQ ( expected , toString ( result . errors [ 0 ] ) ) ;
2022-03-18 08:46:04 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " error_detailed_function_mismatch_ret_count " )
{
2024-08-17 02:29:33 +08:00
// FIXME: CLI-116111 test disabled until type path stringification is improved
2024-11-02 03:06:07 +08:00
DOES_NOT_PASS_NEW_SOLVER_GUARD ( ) ;
2024-08-17 02:29:33 +08:00
2022-03-18 08:46:04 +08:00
CheckResult result = check ( R " (
type A = ( number , number ) - > ( number )
type B = ( number , number ) - > ( number , boolean )
local a : A
local b : B = a
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
2023-08-11 22:42:37 +08:00
const std : : string expected = R " (Type
' ( number , number ) - > number '
could not be converted into
' ( number , number ) - > ( number , boolean ) '
2022-03-18 08:46:04 +08:00
caused by :
2023-08-11 22:42:37 +08:00
Function only returns 1 value , but 2 are required here ) " ;
CHECK_EQ ( expected , toString ( result . errors [ 0 ] ) ) ;
2022-03-18 08:46:04 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " error_detailed_function_mismatch_ret " )
{
2024-08-17 02:29:33 +08:00
// FIXME: CLI-116111 test disabled until type path stringification is improved
2024-11-02 03:06:07 +08:00
DOES_NOT_PASS_NEW_SOLVER_GUARD ( ) ;
2024-08-17 02:29:33 +08:00
2022-03-18 08:46:04 +08:00
CheckResult result = check ( R " (
type A = ( number , number ) - > string
type B = ( number , number ) - > number
local a : A
local b : B = a
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
2023-08-11 22:42:37 +08:00
const std : : string expected = R " (Type
' ( number , number ) - > string '
could not be converted into
' ( number , number ) - > number '
2022-03-18 08:46:04 +08:00
caused by :
2023-10-14 04:20:12 +08:00
Return type is not compatible .
2023-08-11 22:42:37 +08:00
Type ' string ' could not be converted into ' number ' ) " ;
CHECK_EQ ( expected , toString ( result . errors [ 0 ] ) ) ;
2022-03-18 08:46:04 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " error_detailed_function_mismatch_ret_mult " )
{
2024-08-17 02:29:33 +08:00
// FIXME: CLI-116111 test disabled until type path stringification is improved
2024-11-02 03:06:07 +08:00
DOES_NOT_PASS_NEW_SOLVER_GUARD ( ) ;
2024-08-17 02:29:33 +08:00
2022-03-18 08:46:04 +08:00
CheckResult result = check ( R " (
type A = ( number , number ) - > ( number , string )
type B = ( number , number ) - > ( number , boolean )
local a : A
local b : B = a
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
2023-08-11 22:42:37 +08:00
const std : : string expected = R " (Type
' ( number , number ) - > ( number , string ) '
could not be converted into
' ( number , number ) - > ( number , boolean ) '
2022-03-18 08:46:04 +08:00
caused by :
2023-10-14 04:20:12 +08:00
Return # 2 type is not compatible .
2023-08-11 22:42:37 +08:00
Type ' string ' could not be converted into ' boolean ' ) " ;
CHECK_EQ ( expected , toString ( result . errors [ 0 ] ) ) ;
2022-03-18 08:46:04 +08:00
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " function_decl_quantify_right_type " )
2022-03-18 08:46:04 +08:00
{
fileResolver . source [ " game/isAMagicMock " ] = R " (
- - ! nonstrict
return function ( value )
return false
end
) " ;
CheckResult result = check ( R " (
- - ! nonstrict
local MagicMock = { }
MagicMock . is = require ( game . isAMagicMock )
function MagicMock . is ( value )
return false
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " function_decl_non_self_sealed_overwrite " )
2022-03-18 08:46:04 +08:00
{
CheckResult result = check ( R " (
2023-02-25 05:49:38 +08:00
function string . len ( ) : number
return 1
end
local s = string
2022-03-18 08:46:04 +08:00
) " );
2022-04-01 05:01:51 +08:00
LUAU_REQUIRE_NO_ERRORS ( result ) ;
// if 'string' library property was replaced with an internal module type, it will be freed and the next check will crash
frontend . clear ( ) ;
2023-02-25 05:49:38 +08:00
CheckResult result2 = check ( R " (
print ( string . len ( ' hello ' ) )
2022-04-01 05:01:51 +08:00
) " );
2023-02-25 05:49:38 +08:00
LUAU_REQUIRE_NO_ERRORS ( result2 ) ;
2022-04-01 05:01:51 +08:00
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " function_decl_non_self_sealed_overwrite_2 " )
2022-04-01 05:01:51 +08:00
{
CheckResult result = check ( R " (
local t : { f : ( ( x : number ) - > number ) ? } = { }
function t . f ( x )
print ( x + 5 )
2022-04-08 05:29:01 +08:00
return x . . " asd " - - 1 st error : we know that return type is a number , not a string
2022-04-01 05:01:51 +08:00
end
t . f = function ( x )
print ( x + 5 )
2022-04-08 05:29:01 +08:00
return x . . " asd " - - 2 nd error : we know that return type is a number , not a string
2022-04-01 05:01:51 +08:00
end
) " );
2024-08-31 04:16:51 +08:00
if ( FFlag : : LuauSolverV2 )
2024-08-17 02:29:33 +08:00
{
LUAU_REQUIRE_ERROR_COUNT ( 2 , result ) ;
2024-08-24 00:35:30 +08:00
CHECK_EQ (
toString ( result . errors [ 0 ] ) ,
R " (Type function instance add<a, number> depends on generic function parameters but does not appear in the function signature; this construct cannot be type-checked at this time) "
) ;
CHECK_EQ (
toString ( result . errors [ 1 ] ) ,
R " (Type function instance add<a, number> depends on generic function parameters but does not appear in the function signature; this construct cannot be type-checked at this time) "
) ;
2024-08-17 02:29:33 +08:00
}
else
{
LUAU_REQUIRE_ERROR_COUNT ( 2 , result ) ;
CHECK_EQ ( toString ( result . errors [ 0 ] ) , R " (Type 'string' could not be converted into 'number') " ) ;
CHECK_EQ ( toString ( result . errors [ 1 ] ) , R " (Type 'string' could not be converted into 'number') " ) ;
}
2022-03-18 08:46:04 +08:00
}
2022-04-15 07:57:43 +08:00
TEST_CASE_FIXTURE ( Fixture , " inferred_higher_order_functions_are_quantified_at_the_right_time2 " )
{
CheckResult result = check ( R " (
- - ! strict
local function resolveDispatcher ( )
return ( nil : : any ) : : { useContext : ( number ? ) - > any }
end
local useContext
useContext = function ( unstable_observedBits : number ? )
resolveDispatcher ( ) . useContext ( unstable_observedBits )
end
) " );
// LUAU_REQUIRE_NO_ERRORS is particularly unhelpful when this test is broken.
// You get a TypeMismatch error where both types stringify the same.
CHECK ( result . errors . empty ( ) ) ;
if ( ! result . errors . empty ( ) )
{
for ( const auto & e : result . errors )
2024-06-01 03:18:18 +08:00
MESSAGE ( e . moduleName < < " " < < toString ( e . location ) < < " : " < < toString ( e ) ) ;
2022-04-15 07:57:43 +08:00
}
}
TEST_CASE_FIXTURE ( Fixture , " inferred_higher_order_functions_are_quantified_at_the_right_time3 " )
{
2024-08-24 00:35:30 +08:00
// This test regresses in the new solver, but is sort of nonsensical insofar as `foo` is known to be `nil`, so it's "right" to not be able to call
// it.
2024-11-02 03:06:07 +08:00
DOES_NOT_PASS_NEW_SOLVER_GUARD ( ) ;
2024-08-17 02:29:33 +08:00
2022-04-15 07:57:43 +08:00
CheckResult result = check ( R " (
local foo
foo ( ) : bar ( function ( )
return foo ( )
end )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " function_decl_non_self_unsealed_overwrite " )
2022-04-08 05:29:01 +08:00
{
CheckResult result = check ( R " (
local t = { f = nil : : ( ( x : number ) - > number ) ? }
function t . f ( x : string ) : string - - 1 st error : new function value type is incompatible
return x . . " asd "
end
t . f = function ( x )
print ( x + 5 )
return x . . " asd " - - 2 nd error : we know that return type is a number , not a string
end
) " );
2024-08-31 04:16:51 +08:00
if ( FFlag : : LuauSolverV2 )
2024-08-17 02:29:33 +08:00
{
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
2024-08-24 00:35:30 +08:00
CHECK_EQ (
toString ( result . errors [ 0 ] ) ,
R " (Type function instance add<a, number> depends on generic function parameters but does not appear in the function signature; this construct cannot be type-checked at this time) "
) ;
2024-08-17 02:29:33 +08:00
}
else
{
LUAU_REQUIRE_ERROR_COUNT ( 2 , result ) ;
CHECK_EQ ( toString ( result . errors [ 0 ] ) , R " (Type
2023-08-11 22:42:37 +08:00
' ( string ) - > string '
could not be converted into
' ( ( number ) - > number ) ? '
2022-04-08 05:29:01 +08:00
caused by :
2023-10-14 04:20:12 +08:00
None of the union options are compatible . For example :
2023-08-11 22:42:37 +08:00
Type
' ( string ) - > string '
could not be converted into
' ( number ) - > number '
2022-04-08 05:29:01 +08:00
caused by :
2023-10-14 04:20:12 +08:00
Argument # 1 type is not compatible .
2023-08-11 22:42:37 +08:00
Type ' number ' could not be converted into ' string ' ) " );
2024-08-17 02:29:33 +08:00
CHECK_EQ ( toString ( result . errors [ 1 ] ) , R " (Type 'string' could not be converted into 'number') " ) ;
}
2022-04-08 05:29:01 +08:00
}
2022-03-18 08:46:04 +08:00
TEST_CASE_FIXTURE ( Fixture , " strict_mode_ok_with_missing_arguments " )
{
CheckResult result = check ( R " (
local function f ( x : any ) end
f ( )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " function_statement_sealed_table_assignment_through_indexer " )
{
2024-08-17 02:29:33 +08:00
// FIXME: CLI-116122 bug where `t:b` does not check against the type from the indexer annotation on `t`.
2024-11-02 03:06:07 +08:00
DOES_NOT_PASS_NEW_SOLVER_GUARD ( ) ;
2024-08-17 02:29:33 +08:00
2022-03-18 08:46:04 +08:00
CheckResult result = check ( R " (
local t : { [ string ] : ( ) - > number } = { }
function t . a ( ) return 1 end - - OK
function t : b ( ) return 2 end - - not OK
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
2024-08-02 22:30:04 +08:00
CHECK_EQ (
R " (Type
2023-08-11 22:42:37 +08:00
' ( * error - type * ) - > number '
could not be converted into
' ( ) - > number '
2022-03-18 08:46:04 +08:00
caused by :
Argument count mismatch . Function expects 1 argument , but none are specified ) " ,
2024-08-02 22:30:04 +08:00
toString ( result . errors [ 0 ] )
) ;
2022-03-18 08:46:04 +08:00
}
2022-03-25 06:04:14 +08:00
TEST_CASE_FIXTURE ( Fixture , " too_few_arguments_variadic " )
{
CheckResult result = check ( R " (
function test ( a : number , b : string , . . . )
end
test ( 1 )
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
auto err = result . errors [ 0 ] ;
auto acm = get < CountMismatch > ( err ) ;
REQUIRE ( acm ) ;
CHECK_EQ ( 2 , acm - > expected ) ;
CHECK_EQ ( 1 , acm - > actual ) ;
CHECK_EQ ( CountMismatch : : Context : : Arg , acm - > context ) ;
CHECK ( acm - > isVariadic ) ;
}
TEST_CASE_FIXTURE ( Fixture , " too_few_arguments_variadic_generic " )
{
2024-08-17 02:29:33 +08:00
// FIXME: CLI-116157 variadic and generic type packs seem to be interacting incorrectly.
2024-11-02 03:06:07 +08:00
DOES_NOT_PASS_NEW_SOLVER_GUARD ( ) ;
2024-08-17 02:29:33 +08:00
2022-03-25 06:04:14 +08:00
CheckResult result = check ( R " (
function test ( a : number , b : string , . . . )
return 1
end
function wrapper < A . . . > ( f : ( A . . . ) - > number , . . . : A . . . )
end
wrapper ( test )
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
auto err = result . errors [ 0 ] ;
auto acm = get < CountMismatch > ( err ) ;
REQUIRE ( acm ) ;
CHECK_EQ ( 3 , acm - > expected ) ;
CHECK_EQ ( 1 , acm - > actual ) ;
CHECK_EQ ( CountMismatch : : Context : : Arg , acm - > context ) ;
CHECK ( acm - > isVariadic ) ;
}
2022-05-14 03:36:37 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " too_few_arguments_variadic_generic2 " )
2022-03-25 06:04:14 +08:00
{
2024-08-17 02:29:33 +08:00
// FIXME: CLI-116157 variadic and generic type packs seem to be interacting incorrectly.
2024-11-02 03:06:07 +08:00
DOES_NOT_PASS_NEW_SOLVER_GUARD ( ) ;
2024-08-17 02:29:33 +08:00
2022-03-25 06:04:14 +08:00
CheckResult result = check ( R " (
function test ( a : number , b : string , . . . )
return 1
end
function wrapper < A . . . > ( f : ( A . . . ) - > number , . . . : A . . . )
end
pcall ( wrapper , test )
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
auto err = result . errors [ 0 ] ;
auto acm = get < CountMismatch > ( err ) ;
REQUIRE ( acm ) ;
CHECK_EQ ( 4 , acm - > expected ) ;
CHECK_EQ ( 2 , acm - > actual ) ;
CHECK_EQ ( CountMismatch : : Context : : Arg , acm - > context ) ;
CHECK ( acm - > isVariadic ) ;
}
2022-04-15 07:57:43 +08:00
TEST_CASE_FIXTURE ( Fixture , " occurs_check_failure_in_function_return_type " )
{
CheckResult result = check ( R " (
function f ( )
return 5 , f ( )
end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CHECK ( nullptr ! = get < OccursCheckFailed > ( result . errors [ 0 ] ) ) ;
}
2022-07-08 09:22:39 +08:00
TEST_CASE_FIXTURE ( Fixture , " free_is_not_bound_to_unknown " )
{
2023-09-23 03:12:15 +08:00
// This test only makes sense for the old solver
2024-08-31 04:16:51 +08:00
if ( FFlag : : LuauSolverV2 )
2023-09-23 03:12:15 +08:00
return ;
2022-07-08 09:22:39 +08:00
CheckResult result = check ( R " (
local function foo ( f : ( unknown ) - > ( ) , x )
f ( x )
end
) " );
CHECK_EQ ( " <a>((unknown) -> (), a) -> () " , toString ( requireType ( " foo " ) ) ) ;
}
TEST_CASE_FIXTURE ( Fixture , " dont_infer_parameter_types_for_functions_from_their_call_site " )
{
CheckResult result = check ( R " (
local t = { }
function t . f ( x )
return x
end
t . __index = t
function g ( s )
local q = s . p and s . p . q or nil
return q and t . f ( q ) or nil
end
local f = t . f
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
CHECK_EQ ( " <a>(a) -> a " , toString ( requireType ( " f " ) ) ) ;
2024-08-31 04:16:51 +08:00
if ( FFlag : : LuauSolverV2 )
2024-08-17 02:29:33 +08:00
CHECK_EQ ( " ({ read p: { read q: unknown } }) -> ~(false?)? " , toString ( requireType ( " g " ) ) ) ;
2023-03-11 04:21:07 +08:00
else
CHECK_EQ ( " ({+ p: {+ q: nil +} +}) -> nil " , toString ( requireType ( " g " ) ) ) ;
2022-07-08 09:22:39 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " dont_mutate_the_underlying_head_of_typepack_when_calling_with_self " )
{
CheckResult result = check ( R " (
local t = { }
function t : m ( x ) end
function f ( ) : never return 5 : : never end
t : m ( f ( ) )
t : m ( f ( ) )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2022-09-16 06:38:17 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " improved_function_arg_mismatch_errors " )
{
CheckResult result = check ( R " (
local function foo1 ( a : number ) end
foo1 ( )
local function foo2 ( a : number , b : string ? ) end
foo2 ( )
local function foo3 ( a : number , b : string ? , c : any ) end - - any is optional
foo3 ( )
string . find ( )
local t = { }
2022-10-28 18:37:29 +08:00
function t . foo ( x : number , y : string ? , . . . : any ) return 1 end
2022-09-16 06:38:17 +08:00
function t : bar ( x : number , y : string ? ) end
t . foo ( )
t : bar ( )
2022-10-28 18:37:29 +08:00
local u = { a = t , b = function ( ) return t end }
2022-09-16 06:38:17 +08:00
u . a . foo ( )
2022-10-28 18:37:29 +08:00
local x = ( u . a ) . foo ( )
u . b ( ) . foo ( )
2022-09-16 06:38:17 +08:00
) " );
2022-10-28 18:37:29 +08:00
LUAU_REQUIRE_ERROR_COUNT ( 9 , result ) ;
2024-08-31 04:16:51 +08:00
if ( FFlag : : LuauSolverV2 )
2024-08-17 02:29:33 +08:00
{
// These improvements to the error messages are currently regressed in the new type solver.
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Argument count mismatch. Function expects 1 argument, but none are specified " ) ;
CHECK_EQ ( toString ( result . errors [ 1 ] ) , " Argument count mismatch. Function expects 1 to 2 arguments, but none are specified " ) ;
CHECK_EQ ( toString ( result . errors [ 2 ] ) , " Argument count mismatch. Function expects 1 to 3 arguments, but none are specified " ) ;
CHECK_EQ ( toString ( result . errors [ 3 ] ) , " Argument count mismatch. Function expects 2 to 4 arguments, but none are specified " ) ;
CHECK_EQ ( toString ( result . errors [ 4 ] ) , " Argument count mismatch. Function expects at least 1 argument, but none are specified " ) ;
CHECK_EQ ( toString ( result . errors [ 5 ] ) , " Argument count mismatch. Function expects 2 to 3 arguments, but only 1 is specified " ) ;
CHECK_EQ ( toString ( result . errors [ 6 ] ) , " Argument count mismatch. Function expects at least 1 argument, but none are specified " ) ;
CHECK_EQ ( toString ( result . errors [ 7 ] ) , " Argument count mismatch. Function expects at least 1 argument, but none are specified " ) ;
CHECK_EQ ( toString ( result . errors [ 8 ] ) , " Argument count mismatch. Function expects at least 1 argument, but none are specified " ) ;
}
else
{
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Argument count mismatch. Function 'foo1' expects 1 argument, but none are specified " ) ;
CHECK_EQ ( toString ( result . errors [ 1 ] ) , " Argument count mismatch. Function 'foo2' expects 1 to 2 arguments, but none are specified " ) ;
CHECK_EQ ( toString ( result . errors [ 2 ] ) , " Argument count mismatch. Function 'foo3' expects 1 to 3 arguments, but none are specified " ) ;
CHECK_EQ ( toString ( result . errors [ 3 ] ) , " Argument count mismatch. Function 'string.find' expects 2 to 4 arguments, but none are specified " ) ;
CHECK_EQ ( toString ( result . errors [ 4 ] ) , " Argument count mismatch. Function 't.foo' expects at least 1 argument, but none are specified " ) ;
CHECK_EQ ( toString ( result . errors [ 5 ] ) , " Argument count mismatch. Function 't.bar' expects 2 to 3 arguments, but only 1 is specified " ) ;
CHECK_EQ ( toString ( result . errors [ 6 ] ) , " Argument count mismatch. Function 'u.a.foo' expects at least 1 argument, but none are specified " ) ;
CHECK_EQ ( toString ( result . errors [ 7 ] ) , " Argument count mismatch. Function 'u.a.foo' expects at least 1 argument, but none are specified " ) ;
CHECK_EQ ( toString ( result . errors [ 8 ] ) , " Argument count mismatch. Function expects at least 1 argument, but none are specified " ) ;
}
2022-09-16 06:38:17 +08:00
}
// This might be surprising, but since 'any' became optional, unannotated functions in non-strict 'expect' 0 arguments
TEST_CASE_FIXTURE ( BuiltinsFixture , " improved_function_arg_mismatch_error_nonstrict " )
{
2024-08-17 02:29:33 +08:00
// This behavior is not part of the current specification of the new type solver.
2024-11-02 03:06:07 +08:00
DOES_NOT_PASS_NEW_SOLVER_GUARD ( ) ;
2024-08-17 02:29:33 +08:00
2022-09-16 06:38:17 +08:00
CheckResult result = check ( R " (
2024-08-17 02:29:33 +08:00
- - ! nonstrict
local function foo ( a , b ) end
foo ( string . find ( " hello " , " e " ) )
2022-09-16 06:38:17 +08:00
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Argument count mismatch. Function 'foo' expects 0 to 2 arguments, but 3 are specified " ) ;
}
2022-10-22 01:54:01 +08:00
TEST_CASE_FIXTURE ( Fixture , " luau_subtyping_is_np_hard " )
{
2024-08-17 02:29:33 +08:00
// The case that _should_ succeed here (`z = x`) does not currently in the new solver.
2024-11-02 03:06:07 +08:00
DOES_NOT_PASS_NEW_SOLVER_GUARD ( ) ;
2024-08-17 02:29:33 +08:00
2022-10-22 01:54:01 +08:00
CheckResult result = check ( R " (
- - ! strict
- - An example of coding up graph coloring in the Luau type system .
- - This codes a three - node , two color problem .
- - A three - node triangle is uncolorable ,
- - but a three - node line is colorable .
type Red = " red "
type Blue = " blue "
type Color = Red | Blue
type Coloring = ( Color ) - > ( Color ) - > ( Color ) - > boolean
type Uncolorable = ( Color ) - > ( Color ) - > ( Color ) - > false
type Line = Coloring
& ( ( Red ) - > ( Red ) - > ( Color ) - > false )
& ( ( Blue ) - > ( Blue ) - > ( Color ) - > false )
& ( ( Color ) - > ( Red ) - > ( Red ) - > false )
& ( ( Color ) - > ( Blue ) - > ( Blue ) - > false )
type Triangle = Line
& ( ( Red ) - > ( Color ) - > ( Red ) - > false )
& ( ( Blue ) - > ( Color ) - > ( Blue ) - > false )
local x : Triangle
local y : Line
local z : Uncolorable
z = x - - OK , so the triangle is uncolorable
z = y - - Not OK , so the line is colorable
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
2023-08-11 22:42:37 +08:00
const std : : string expected = R " (Type
' ( ( " blue " | " red " ) - > ( " blue " | " red " ) - > ( " blue " | " red " ) - > boolean ) & ( ( " blue " | " red " ) - > ( " blue " ) - > ( " blue " ) - > false ) & ( ( " blue " | " red " ) - > ( " red " ) - > ( " red " ) - > false ) & ( ( " blue " ) - > ( " blue " ) - > ( " blue " | " red " ) - > false ) & ( ( " red " ) - > ( " red " ) - > ( " blue " | " red " ) - > false ) '
could not be converted into
' ( " blue " | " red " ) - > ( " blue " | " red " ) - > ( " blue " | " red " ) - > false ' ; none of the intersection parts are compatible ) " ;
CHECK_EQ ( expected , toString ( result . errors [ 0 ] ) ) ;
2022-11-05 01:33:22 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " function_is_supertype_of_concrete_functions " )
{
2023-01-07 05:14:35 +08:00
registerHiddenTypes ( & frontend ) ;
2022-11-05 01:33:22 +08:00
CheckResult result = check ( R " (
function foo ( f : fun ) end
function a ( ) end
function id ( x ) return x end
foo ( a )
foo ( id )
foo ( foo )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " concrete_functions_are_not_supertypes_of_function " )
{
2023-01-07 05:14:35 +08:00
registerHiddenTypes ( & frontend ) ;
2022-11-05 01:33:22 +08:00
CheckResult result = check ( R " (
local a : fun = function ( ) end
function one ( arg : ( ) - > ( ) ) end
function two ( arg : < T > ( T ) - > T ) end
one ( a )
two ( a )
) " );
LUAU_REQUIRE_ERROR_COUNT ( 2 , result ) ;
CHECK ( 6 = = result . errors [ 0 ] . location . begin . line ) ;
2024-08-17 02:29:33 +08:00
auto tm1 = get < TypeMismatch > ( result . errors [ 0 ] ) ;
REQUIRE ( tm1 ) ;
CHECK ( " () -> () " = = toString ( tm1 - > wantedType ) ) ;
CHECK ( " function " = = toString ( tm1 - > givenType ) ) ;
2022-11-05 01:33:22 +08:00
CHECK ( 7 = = result . errors [ 1 ] . location . begin . line ) ;
2024-08-17 02:29:33 +08:00
auto tm2 = get < TypeMismatch > ( result . errors [ 1 ] ) ;
REQUIRE ( tm2 ) ;
CHECK ( " <T>(T) -> T " = = toString ( tm2 - > wantedType ) ) ;
CHECK ( " function " = = toString ( tm2 - > givenType ) ) ;
2022-11-05 01:33:22 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " other_things_are_not_related_to_function " )
{
2023-01-07 05:14:35 +08:00
registerHiddenTypes ( & frontend ) ;
2022-11-05 01:33:22 +08:00
CheckResult result = check ( R " (
local a : fun = function ( ) end
local b : { } = a
local c : boolean = a
local d : fun = true
local e : fun = { }
) " );
LUAU_REQUIRE_ERROR_COUNT ( 4 , result ) ;
CHECK ( 2 = = result . errors [ 0 ] . location . begin . line ) ;
CHECK ( 3 = = result . errors [ 1 ] . location . begin . line ) ;
CHECK ( 4 = = result . errors [ 2 ] . location . begin . line ) ;
CHECK ( 5 = = result . errors [ 3 ] . location . begin . line ) ;
2022-10-22 01:54:01 +08:00
}
2022-12-03 02:09:59 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " fuzz_must_follow_in_overload_resolution " )
{
CheckResult result = check ( R " (
for _ in function < t0 > ( ) : ( t0 ) & ( ( ( ) - > ( ) ) & ( ( ) - > ( ) ) )
end do
_ ( _ ( _ , _ , _ ) , _ )
end
) " );
LUAU_REQUIRE_ERRORS ( result ) ;
}
2023-03-11 04:21:07 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " dont_assert_when_the_tarjan_limit_is_exceeded_during_generalization " )
{
2024-08-31 04:16:51 +08:00
ScopedFastFlag sff { FFlag : : LuauSolverV2 , true } ;
2024-08-17 02:29:33 +08:00
ScopedFastInt sfi { FInt : : LuauTarjanChildLimit , 1 } ;
2023-03-11 04:21:07 +08:00
CheckResult result = check ( R " (
function f ( t )
t . x . y . z = 441
end
) " );
2023-05-26 05:36:34 +08:00
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
2023-03-11 04:21:07 +08:00
2024-08-17 02:29:33 +08:00
CHECK_MESSAGE ( get < UnificationTooComplex > ( result . errors [ 0 ] ) , " Expected UnificationTooComplex but got: " < < toString ( result . errors [ 0 ] ) ) ;
2023-03-11 04:21:07 +08:00
}
2023-03-25 02:03:04 +08:00
/* We had a bug under DCR where instantiated type packs had a nullptr scope.
*
* This caused an issue with promotion .
*/
TEST_CASE_FIXTURE ( Fixture , " instantiated_type_packs_must_have_a_non_null_scope " )
{
CheckResult result = check ( R " (
2024-08-17 02:29:33 +08:00
function pcall < A . . . , R . . . > ( . . . : ( A . . . ) - > R . . . ) : ( boolean , R . . . )
return nil : : any
2023-03-25 02:03:04 +08:00
end
type Dispatch < A > = ( A ) - > ( )
function mountReducer ( )
dispatchAction ( )
return nil : : any
end
function dispatchAction ( )
end
function useReducer ( ) : Dispatch < any >
local result , setResult = pcall ( mountReducer )
return setResult
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2023-05-13 01:50:47 +08:00
TEST_CASE_FIXTURE ( Fixture , " inner_frees_become_generic_in_dcr " )
{
2024-08-31 04:16:51 +08:00
if ( ! FFlag : : LuauSolverV2 )
2023-05-13 01:50:47 +08:00
return ;
CheckResult result = check ( R " (
function f ( x )
local z = x
return x
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
std : : optional < TypeId > ty = findTypeAtPosition ( Position { 3 , 19 } ) ;
REQUIRE ( ty ) ;
2023-05-20 03:37:30 +08:00
CHECK ( get < GenericType > ( follow ( * ty ) ) ) ;
2023-05-13 01:50:47 +08:00
}
TEST_CASE_FIXTURE ( Fixture , " function_exprs_are_generalized_at_signature_scope_not_enclosing " )
{
CheckResult result = check ( R " (
local foo
local bar
- - foo being a function expression is deliberate : the bug we ' re testing
- - only existed for function expressions , not for function statements .
foo = function ( a )
return bar
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
2024-08-31 04:16:51 +08:00
if ( FFlag : : LuauSolverV2 )
2024-08-17 02:29:33 +08:00
CHECK ( toString ( requireType ( " foo " ) ) = = " ((unknown) -> nil)? " ) ;
else
{
// note that b is not in the generic list; it is free, the unconstrained type of `bar`.
CHECK ( toString ( requireType ( " foo " ) ) = = " <a>(a) -> b " ) ;
}
2023-05-13 01:50:47 +08:00
}
2023-06-03 03:52:15 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " param_1_and_2_both_takes_the_same_generic_but_their_arguments_are_incompatible " )
{
CheckResult result = check ( R " (
local function foo < a > ( x : a , y : a ? )
return x
end
local vec2 = { x = 5 , y = 7 }
local ret : number = foo ( vec2 , { x = 5 } )
) " );
2024-08-31 04:16:51 +08:00
if ( FFlag : : LuauSolverV2 )
2024-08-17 02:29:33 +08:00
{
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
2023-06-03 03:52:15 +08:00
2024-08-17 02:29:33 +08:00
auto tm = get < TypeMismatch > ( result . errors [ 0 ] ) ;
REQUIRE ( tm ) ;
CHECK ( " number " = = toString ( tm - > wantedType ) ) ;
CHECK ( " { x: number } " = = toString ( tm - > givenType ) ) ;
}
else
{
// In the old solver, this produces a very strange result:
//
// Here, we instantiate `<a>(x: a, y: a?) -> a` with a fresh type `'a` for `a`.
// In argument #1, we unify `vec2` with `'a`.
// This is ok, so we record an equality constraint `'a` with `vec2`.
// In argument #2, we unify `{ x: number }` with `'a?`.
// This fails because `'a` has equality constraint with `vec2`,
// so `{ x: number } <: vec2?`, which is false.
//
// If the unifications were to be committed, then it'd result in the following type error:
//
// Type '{ x: number }' could not be converted into 'vec2?'
// caused by:
// [...] Table type '{ x: number }' not compatible with type 'vec2' because the former is missing field 'y'
//
// However, whenever we check the argument list, if there's an error, we don't commit the unifications, so it actually looks like this:
//
// Type '{ x: number }' could not be converted into 'a?'
// caused by:
// [...] Table type '{ x: number }' not compatible with type 'vec2' because the former is missing field 'y'
//
// Then finally, that generic is left floating free, and since the function returns that generic,
// that free type is then later bound to `number`, which succeeds and mutates the type graph.
// This again changes the type error where `a` becomes bound to `number`.
//
// Type '{ x: number }' could not be converted into 'number?'
// caused by:
// [...] Table type '{ x: number }' not compatible with type 'vec2' because the former is missing field 'y'
//
// Uh oh, that type error is extremely confusing for people who doesn't know how that went down.
// Really, what should happen is we roll each argument incompatibility into a union type, but that needs local type inference.
2023-06-03 03:52:15 +08:00
2024-08-17 02:29:33 +08:00
LUAU_REQUIRE_ERROR_COUNT ( 2 , result ) ;
const std : : string expected = R " (Type '{ x: number }' could not be converted into 'vec2?'
2023-06-03 03:52:15 +08:00
caused by :
2023-10-14 04:20:12 +08:00
None of the union options are compatible . For example :
2023-08-11 22:42:37 +08:00
Table type ' { x : number } ' not compatible with type ' vec2 ' because the former is missing field ' y ' ) " ;
2024-08-17 02:29:33 +08:00
CHECK_EQ ( expected , toString ( result . errors [ 0 ] ) ) ;
CHECK_EQ ( " Type 'vec2' could not be converted into 'number' " , toString ( result . errors [ 1 ] ) ) ;
}
2023-06-03 03:52:15 +08:00
}
TEST_CASE_FIXTURE ( BuiltinsFixture , " param_1_and_2_both_takes_the_same_generic_but_their_arguments_are_incompatible_2 " )
{
CheckResult result = check ( R " (
local function f < a > ( x : a , y : a ) : a
return if math . random ( ) > 0.5 then x else y
end
local z : boolean = f ( 5 , " five " )
) " );
2024-08-31 04:16:51 +08:00
if ( FFlag : : LuauSolverV2 )
2024-08-17 02:29:33 +08:00
{
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
auto tm = get < TypeMismatch > ( result . errors [ 0 ] ) ;
REQUIRE ( tm ) ;
CHECK ( " boolean " = = toString ( tm - > wantedType ) ) ;
CHECK ( " number | string " = = toString ( tm - > givenType ) ) ;
}
else
{
LUAU_REQUIRE_ERROR_COUNT ( 2 , result ) ;
2023-06-03 03:52:15 +08:00
2024-08-17 02:29:33 +08:00
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Type 'string' could not be converted into 'number' " ) ;
CHECK_EQ ( toString ( result . errors [ 1 ] ) , " Type 'number' could not be converted into 'boolean' " ) ;
}
2023-06-03 03:52:15 +08:00
}
2023-06-10 01:08:00 +08:00
TEST_CASE_FIXTURE ( Fixture , " attempt_to_call_an_intersection_of_tables " )
{
CheckResult result = check ( R " (
local function f ( t : { x : number } & { y : string } )
t ( )
end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
2024-10-05 02:29:55 +08:00
if ( FFlag : : LuauSolverV2 )
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Cannot call a value of type { x: number } & { y: string } " ) ;
2023-06-10 01:08:00 +08:00
else
2024-10-05 02:29:55 +08:00
CHECK_EQ ( toString ( result . errors [ 0 ] ) , " Cannot call a value of type {| x: number |} " ) ;
2023-06-10 01:08:00 +08:00
}
2023-06-24 14:19:39 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " attempt_to_call_an_intersection_of_tables_with_call_metamethod " )
{
CheckResult result = check ( R " (
type Callable = typeof ( setmetatable ( { } , {
__call = function ( self , . . . ) return . . . end
} ) )
local function f ( t : Callable & { x : number } )
t ( )
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2023-07-28 23:13:53 +08:00
TEST_CASE_FIXTURE ( Fixture , " generic_packs_are_not_variadic " )
{
2024-08-31 04:16:51 +08:00
ScopedFastFlag sff { FFlag : : LuauSolverV2 , true } ;
2023-07-28 23:13:53 +08:00
CheckResult result = check ( R " (
local function apply < a , b . . . , c . . . > ( f : ( a , b . . . ) - > c . . . , x : a )
return f ( x )
end
local function add ( x : number , y : number )
return x + y
end
apply ( add , 5 )
) " );
2024-08-17 02:29:33 +08:00
// FIXME: this errored at some point, but doesn't anymore.
// the desired behavior here is erroring.
LUAU_REQUIRE_NO_ERRORS ( result ) ;
2023-07-28 23:13:53 +08:00
}
2023-07-08 04:10:48 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " num_is_solved_before_num_or_str " )
{
CheckResult result = check ( R " (
function num ( )
return 5
end
local function num_or_str ( )
if math . random ( ) > 0.5 then
return num ( )
else
return " some string "
end
end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
2024-08-31 04:16:51 +08:00
if ( FFlag : : LuauSolverV2 )
2023-11-11 05:10:07 +08:00
CHECK ( toString ( result . errors . at ( 0 ) ) = = " Type pack 'string' could not be converted into 'number'; at [0], string is not a subtype of number " ) ;
else
CHECK_EQ ( " Type 'string' could not be converted into 'number' " , toString ( result . errors [ 0 ] ) ) ;
2023-07-08 04:10:48 +08:00
CHECK_EQ ( " () -> number " , toString ( requireType ( " num_or_str " ) ) ) ;
}
TEST_CASE_FIXTURE ( BuiltinsFixture , " num_is_solved_after_num_or_str " )
{
CheckResult result = check ( R " (
local function num_or_str ( )
if math . random ( ) > 0.5 then
return num ( )
else
return " some string "
end
end
function num ( )
return 5
end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
2024-08-31 04:16:51 +08:00
if ( FFlag : : LuauSolverV2 )
2023-11-11 05:10:07 +08:00
CHECK ( toString ( result . errors . at ( 0 ) ) = = " Type pack 'string' could not be converted into 'number'; at [0], string is not a subtype of number " ) ;
else
CHECK_EQ ( " Type 'string' could not be converted into 'number' " , toString ( result . errors [ 0 ] ) ) ;
2023-07-08 04:10:48 +08:00
CHECK_EQ ( " () -> number " , toString ( requireType ( " num_or_str " ) ) ) ;
}
2023-08-05 03:18:54 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " apply_of_lambda_with_inferred_and_explicit_types " )
{
CheckResult result = check ( R " (
local function apply ( f , x ) return f ( x ) end
local x = apply ( function ( x : string ) : number return 5 end , " hello! " )
local function apply_explicit < A , B . . . > ( f : ( A ) - > B . . . , x : A ) : B . . . return f ( x ) end
local x = apply_explicit ( function ( x : string ) : number return 5 end , " hello! " )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2023-12-16 05:29:06 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " regex_benchmark_string_format_minimization " )
{
CheckResult result = check ( R " (
( nil : : any ) ( function ( n )
if tonumber ( n ) then
n = tonumber ( n )
elseif n ~ = nil then
string . format ( " invalid argument #4 to 'sub': number expected, got %s " , typeof ( n ) )
end
end ) ;
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2024-07-13 01:03:36 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " subgeneric_type_function_super_monomorphic " )
2024-02-03 05:32:42 +08:00
{
CheckResult result = check ( R " (
local a : ( number , number ) - > number = function ( a , b ) return a - b end
a = function ( a , b ) return a + b end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2024-02-10 01:51:12 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " simple_unannotated_mutual_recursion " )
{
2024-08-24 00:35:30 +08:00
// CLI-117118 - TypeInferFunctions.simple_unannotated_mutual_recursion relies on unstable assertions to pass.
2024-08-31 04:16:51 +08:00
if ( FFlag : : LuauSolverV2 )
2024-08-24 00:35:30 +08:00
return ;
2024-02-10 01:51:12 +08:00
CheckResult result = check ( R " (
function even ( n )
if n = = 0 then
return true
else
return odd ( n - 1 )
end
end
function odd ( n )
if n = = 0 then
return false
elseif n = = 1 then
return true
else
return even ( n - 1 )
end
end
) " );
2024-08-31 04:16:51 +08:00
if ( FFlag : : LuauSolverV2 )
2024-02-10 01:51:12 +08:00
{
2024-08-17 02:29:33 +08:00
LUAU_REQUIRE_ERROR_COUNT ( 5 , result ) ;
2024-08-24 00:35:30 +08:00
// CLI-117117 Constraint solving is incomplete inTypeInferFunctions.simple_unannotated_mutual_recursion
2024-08-17 02:29:33 +08:00
CHECK ( get < ConstraintSolvingIncompleteError > ( result . errors [ 0 ] ) ) ;
2024-08-24 00:35:30 +08:00
// This check is unstable between different machines and different runs of DCR because it depends on string equality between
// blocked type numbers, which is not guaranteed.
bool r = toString ( result . errors [ 1 ] ) = = " Type pack '*blocked-tp-1*' could not be converted into 'boolean'; type *blocked-tp-1*.tail() "
" (*blocked-tp-1*) is not a subtype of boolean (boolean) " ;
CHECK ( r ) ;
2024-08-02 22:30:04 +08:00
CHECK (
toString ( result . errors [ 2 ] ) = =
" Operator '-' could not be applied to operands of types unknown and number; there is no corresponding overload for __sub "
) ;
CHECK (
toString ( result . errors [ 3 ] ) = =
" Operator '-' could not be applied to operands of types unknown and number; there is no corresponding overload for __sub "
) ;
2024-08-17 02:29:33 +08:00
CHECK (
toString ( result . errors [ 4 ] ) = =
" Operator '-' could not be applied to operands of types unknown and number; there is no corresponding overload for __sub "
) ;
2024-02-10 01:51:12 +08:00
}
else
{
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
CHECK ( toString ( result . errors [ 0 ] ) = = " Unknown type used in - operation; consider adding a type annotation to 'n' " ) ;
}
}
TEST_CASE_FIXTURE ( BuiltinsFixture , " simple_lightly_annotated_mutual_recursion " )
{
CheckResult result = check ( R " (
function even ( n : number )
if n = = 0 then
return true
else
return odd ( n - 1 )
end
end
function odd ( n : number )
if n = = 0 then
return false
elseif n = = 1 then
return true
else
return even ( n - 1 )
end
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
CHECK_EQ ( " (number) -> boolean " , toString ( requireType ( " even " ) ) ) ;
CHECK_EQ ( " (number) -> boolean " , toString ( requireType ( " odd " ) ) ) ;
}
2024-03-09 08:47:53 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " tf_suggest_return_type " )
{
2024-08-31 04:16:51 +08:00
if ( ! FFlag : : LuauSolverV2 )
2024-03-09 08:47:53 +08:00
return ;
CheckResult result = check ( R " (
function fib ( n )
return n < 2 and 1 or fib ( n - 1 ) + fib ( n - 2 )
end
) " );
LUAU_REQUIRE_ERRORS ( result ) ;
auto err = get < ExplicitFunctionAnnotationRecommended > ( result . errors . back ( ) ) ;
LUAU_ASSERT ( err ) ;
2024-05-11 02:21:45 +08:00
CHECK ( " number " = = toString ( err - > recommendedReturn ) ) ;
REQUIRE ( 1 = = err - > recommendedArgs . size ( ) ) ;
CHECK ( " number " = = toString ( err - > recommendedArgs [ 0 ] . second ) ) ;
2024-03-09 08:47:53 +08:00
}
TEST_CASE_FIXTURE ( BuiltinsFixture , " tf_suggest_arg_type " )
{
2024-08-31 04:16:51 +08:00
if ( ! FFlag : : LuauSolverV2 )
2024-03-09 08:47:53 +08:00
return ;
CheckResult result = check ( R " (
function fib ( n , u )
return ( n or u ) and ( n < u and n + fib ( n , u ) )
end
) " );
LUAU_REQUIRE_ERRORS ( result ) ;
auto err = get < ExplicitFunctionAnnotationRecommended > ( result . errors . back ( ) ) ;
LUAU_ASSERT ( err ) ;
CHECK ( " number " = = toString ( err - > recommendedReturn ) ) ;
2024-04-26 06:26:09 +08:00
REQUIRE ( err - > recommendedArgs . size ( ) = = 2 ) ;
2024-03-09 08:47:53 +08:00
CHECK ( " number " = = toString ( err - > recommendedArgs [ 0 ] . second ) ) ;
CHECK ( " number " = = toString ( err - > recommendedArgs [ 1 ] . second ) ) ;
}
2024-06-29 08:34:49 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " tf_suggest_arg_type_2 " )
{
2024-08-31 04:16:51 +08:00
if ( ! FFlag : : LuauSolverV2 )
2024-06-29 08:34:49 +08:00
return ;
// Make sure the error types are cloned to module interface
frontend . options . retainFullTypeGraphs = false ;
CheckResult result = check ( R " (
2024-08-17 02:29:33 +08:00
local function escape_fslash ( pre )
return ( # pre % 2 = = 0 and ' \\ ' or ' ' ) . . pre . . ' . '
end
) " );
2024-06-29 08:34:49 +08:00
LUAU_REQUIRE_ERRORS ( result ) ;
2024-08-17 02:29:33 +08:00
auto err = get < NotATable > ( result . errors . back ( ) ) ;
REQUIRE ( err ) ;
CHECK ( " a " = = toString ( err - > ty ) ) ;
2024-06-29 08:34:49 +08:00
}
2024-03-09 08:47:53 +08:00
TEST_CASE_FIXTURE ( Fixture , " local_function_fwd_decl_doesnt_crash " )
{
CheckResult result = check ( R " (
local foo
local function bar ( )
foo ( )
end
function foo ( )
end
bar ( )
) " );
// This test verifies that an ICE doesn't occur, so the bulk of the test is
// just from running check above.
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2024-03-23 01:47:10 +08:00
TEST_CASE_FIXTURE ( Fixture , " bidirectional_checking_of_callback_property " )
{
CheckResult result = check ( R " (
function print ( x : number ) end
type Point = { x : number , y : number }
local T : { callback : ( ( Point ) - > ( ) ) ? } = { }
T . callback = function ( p ) - - No error here
print ( p . z ) - - error here . Point has no property z
end
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
2024-08-31 04:16:51 +08:00
if ( FFlag : : LuauSolverV2 )
2024-08-17 02:29:33 +08:00
{
auto tm = get < TypeMismatch > ( result . errors [ 0 ] ) ;
REQUIRE ( tm ) ;
2024-03-23 01:47:10 +08:00
2024-08-17 02:29:33 +08:00
CHECK ( " ((Point) -> ())? " = = toString ( tm - > wantedType ) ) ;
CHECK ( " ({ read z: number }) -> () " = = toString ( tm - > givenType ) ) ;
Location location = result . errors [ 0 ] . location ;
CHECK ( location . begin . line = = 6 ) ;
CHECK ( location . end . line = = 8 ) ;
}
else
{
CHECK_MESSAGE ( get < UnknownProperty > ( result . errors [ 0 ] ) , " Expected UnknownProperty but got " < < result . errors [ 0 ] ) ;
Location location = result . errors [ 0 ] . location ;
CHECK ( location . begin . line = = 7 ) ;
CHECK ( location . end . line = = 7 ) ;
}
2024-03-23 01:47:10 +08:00
}
TEST_CASE_FIXTURE ( ClassFixture , " bidirectional_inference_of_class_methods " )
{
CheckResult result = check ( R " (
local c = ChildClass . New ( )
- - Instead of reporting that the lambda is the wrong type , report that we are using its argument improperly .
c . Touched : Connect ( function ( other )
print ( other . ThisDoesNotExist )
end )
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
UnknownProperty * err = get < UnknownProperty > ( result . errors [ 0 ] ) ;
REQUIRE ( err ) ;
CHECK ( " ThisDoesNotExist " = = err - > key ) ;
CHECK ( " BaseClass " = = toString ( err - > table ) ) ;
}
TEST_CASE_FIXTURE ( Fixture , " pass_table_literal_to_function_expecting_optional_prop " )
{
CheckResult result = check ( R " (
type T = { prop : number ? }
function f ( t : T ) end
f ( { prop = 5 } )
f ( { } )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2024-03-31 07:14:44 +08:00
TEST_CASE_FIXTURE ( Fixture , " dont_infer_overloaded_functions " )
{
CheckResult result = check ( R " (
function getR6Attachments ( model )
model : FindFirstChild ( " Right Leg " )
model : FindFirstChild ( " Left Leg " )
model : FindFirstChild ( " Torso " )
model : FindFirstChild ( " Torso " )
model : FindFirstChild ( " Head " )
model : FindFirstChild ( " Left Arm " )
model : FindFirstChild ( " Right Arm " )
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
2024-08-31 04:16:51 +08:00
if ( FFlag : : LuauSolverV2 )
2024-04-06 04:45:09 +08:00
CHECK ( " (t1) -> () where t1 = { read FindFirstChild: (t1, string) -> (...unknown) } " = = toString ( requireType ( " getR6Attachments " ) ) ) ;
2024-03-31 07:14:44 +08:00
else
CHECK ( " <a...>(t1) -> () where t1 = {+ FindFirstChild: (t1, string) -> (a...) +} " = = toString ( requireType ( " getR6Attachments " ) ) ) ;
}
2024-04-06 04:45:09 +08:00
TEST_CASE_FIXTURE ( Fixture , " param_y_is_bounded_by_x_of_type_string " )
{
CheckResult result = check ( R " (
local function f ( x : string , y )
x = y
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
CHECK ( " (string, string) -> () " = = toString ( requireType ( " f " ) ) ) ;
}
TEST_CASE_FIXTURE ( BuiltinsFixture , " function_that_could_return_anything_is_compatible_with_function_that_is_expected_to_return_nothing " )
{
CheckResult result = check ( R " (
- - We infer foo : ( g : ( number ) - > ( . . . unknown ) ) - > ( )
function foo ( g )
g ( 0 )
end
- - a requires a function that returns no values
function a ( f : ( ( number ) - > ( ) ) - > ( ) )
end
- - " Returns an unknown number of values " is close enough to " returns no values. "
a ( foo )
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2024-04-13 01:18:49 +08:00
TEST_CASE_FIXTURE ( Fixture , " self_application_does_not_segfault " )
{
( void ) check ( R " (
function f ( a )
f ( f )
return f ( ) , a
end
) " );
// We only care that type checking completes without tripping a crash or an assertion.
}
TEST_CASE_FIXTURE ( Fixture , " function_definition_in_a_do_block " )
{
CheckResult result = check ( R " (
local f
do
function f ( )
end
end
f ( )
) " );
// We are predominantly interested in this test not crashing.
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( BuiltinsFixture , " function_definition_in_a_do_block_with_global " )
{
CheckResult result = check ( R " (
function f ( ) print ( " a " ) end
do
function f ( )
print ( " b " )
end
end
f ( )
) " );
// We are predominantly interested in this test not crashing.
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2024-04-20 05:48:02 +08:00
TEST_CASE_FIXTURE ( Fixture , " fuzzer_alias_global_function_doesnt_hit_nil_assert " )
{
CheckResult result = check ( R " (
function _ ( )
end
local function l0 ( )
function _ ( )
end
end
_ = _
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
TEST_CASE_FIXTURE ( Fixture , " fuzzer_bug_missing_follow_causes_assertion " )
{
CheckResult result = check ( R " (
local _ = ( { _ = function ( )
return _
end , } ) , true , _ [ _ ( ) ]
for l0 = _ [ _ [ _ [ ` { function ( l0 )
end } ` ] ] ] , _ [ _ . n6 [ _ [ _ . n6 ] ] ] , _ [ _ [ _ . n6 [ _ [ _ . n6 ] ] ] ] do
_ + = if _ then " "
end
return _
) " );
}
TEST_CASE_FIXTURE ( Fixture , " cannot_call_union_of_functions " )
{
CheckResult result = check ( R " (
local f : ( ( ) - > ( ) ) | ( ( ) - > ( ) - > ( ) ) = nil : : any
f ( )
) " );
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
2024-10-05 02:29:55 +08:00
std : : string expected = R " (Cannot call a value of the union type:
2024-04-20 05:48:02 +08:00
| ( ) - > ( )
| ( ) - > ( ) - > ( )
We are unable to determine the appropriate result type for such a call . ) " ;
2024-10-05 02:29:55 +08:00
CHECK ( expected = = toString ( result . errors [ 0 ] ) ) ;
2024-04-20 05:48:02 +08:00
}
2024-04-26 06:26:09 +08:00
TEST_CASE_FIXTURE ( Fixture , " fuzzer_missing_follow_in_ast_stat_fun " )
{
( void ) check ( R " (
local _ = function < t0 . . . > ( )
end ~ = _
while ( _ ) do
_ , _ , _ , _ , _ , _ , _ , _ , _ , _ . _ , _ = nil
function _ ( . . . ) : < t0 . . . > ( ) - > ( )
end
function _ < t0 . . . > ( . . . ) : any
_ . . = . . .
end
_ , _ , _ , _ , _ , _ , _ , _ , _ , _ , _ = nil
end
) " );
}
TEST_CASE_FIXTURE ( Fixture , " unifier_should_not_bind_free_types " )
{
CheckResult result = check ( R " (
function foo ( player )
local success , result = player : thing ( )
if ( success ) then
return " Successfully posted message. " ;
elseif ( not result ) then
return false ;
else
return result ;
end
end
) " );
2024-08-31 04:16:51 +08:00
if ( FFlag : : LuauSolverV2 )
2024-04-26 06:26:09 +08:00
{
// The new solver should ideally be able to do better here, but this is no worse than the old solver.
LUAU_REQUIRE_ERROR_COUNT ( 2 , result ) ;
auto tm1 = get < TypePackMismatch > ( result . errors [ 0 ] ) ;
REQUIRE ( tm1 ) ;
CHECK ( toString ( tm1 - > wantedTp ) = = " string " ) ;
CHECK ( toString ( tm1 - > givenTp ) = = " boolean " ) ;
auto tm2 = get < TypePackMismatch > ( result . errors [ 1 ] ) ;
REQUIRE ( tm2 ) ;
CHECK ( toString ( tm2 - > wantedTp ) = = " string " ) ;
2024-08-17 02:29:33 +08:00
CHECK ( toString ( tm2 - > givenTp ) = = " buffer | class | function | number | string | table | thread | true " ) ;
2024-04-26 06:26:09 +08:00
}
else
{
LUAU_REQUIRE_ERROR_COUNT ( 1 , result ) ;
const TypeMismatch * tm = get < TypeMismatch > ( result . errors [ 0 ] ) ;
REQUIRE ( tm ) ;
CHECK ( toString ( tm - > wantedType ) = = " string " ) ;
CHECK ( toString ( tm - > givenType ) = = " boolean " ) ;
}
}
TEST_CASE_FIXTURE ( Fixture , " captured_local_is_assigned_a_function " )
{
CheckResult result = check ( R " (
local f
local function g ( )
f ( )
end
function f ( )
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2024-05-11 02:21:45 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " error_suppression_propagates_through_function_calls " )
{
CheckResult result = check ( R " (
function first ( x : any )
return pairs ( x ) ( x )
end
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
CHECK ( " (any) -> (any?, any) " = = toString ( requireType ( " first " ) ) ) ;
}
2024-05-17 07:02:03 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " fuzzer_normalizer_out_of_resources " )
{
// This luau code should finish typechecking, not segfault upon dereferencing
// the normalized type
CheckResult result = check ( R " (
Module ' l0 ' :
local _ = true , . . . , _
if . . . then
while _ : _ ( _ . _G ) do
do end
_ = _ and _
_ = 0 and { # _ , }
local _ = " CCCCCCCCCCCCCCCCCCCCCCCCCCC "
local l0 = require ( module0 )
end
local function l0 ( )
end
elseif _ then
l0 = _
end
do end
while _ do
_ = if _ then _ elseif _ then _ , if _ then _ else _
_ = _ ( )
do end
do end
if _ then
end
end
_ = _ , { }
) " );
}
2024-07-13 01:03:36 +08:00
TEST_CASE_FIXTURE ( BuiltinsFixture , " overload_resolution_crash_when_argExprs_is_smaller_than_type_args " )
{
CheckResult result = check ( R " (
- - ! strict
local parseError
type Set < T > = { [ T ] : any }
local function captureDependencies (
saveToSet : Set < PubTypes . Dependency > ,
callback : ( . . . any ) - > any ,
. . .
)
local data = table . pack ( xpcall ( callback , parseError , . . . ) )
end
) " );
}
2024-07-27 01:47:49 +08:00
TEST_CASE_FIXTURE ( Fixture , " unpack_depends_on_rhs_pack_to_be_fully_resolved " )
{
CheckResult result = check ( R " (
- - ! strict
local function id ( x )
return x
end
local u , v = id ( 3 ) , id ( id ( 44 ) )
) " );
CHECK_EQ ( builtinTypes - > numberType , requireType ( " v " ) ) ;
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2024-05-17 07:02:03 +08:00
2024-11-02 03:06:07 +08:00
TEST_CASE_FIXTURE ( Fixture , " hidden_variadics_should_not_break_subtyping " )
{
// Only applies to new solver.
ScopedFastFlag sff { FFlag : : LuauRetrySubtypingWithoutHiddenPack , true } ;
CheckResult result = check ( R " (
- - ! strict
type FooType = {
SetValue : ( Value : number ) - > ( )
}
local Foo : FooType = {
SetValue = function ( Value : number )
end
}
) " );
LUAU_REQUIRE_NO_ERRORS ( result ) ;
}
2022-03-18 08:46:04 +08:00
TEST_SUITE_END ( ) ;