Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/tests/NonStrictTypeChecker.test.cpp
2723 views
1
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
2
#include "Luau/NonStrictTypeChecker.h"
3
4
#include "Fixture.h"
5
6
#include "Luau/Ast.h"
7
#include "Luau/BuiltinDefinitions.h"
8
#include "Luau/Common.h"
9
#include "Luau/Error.h"
10
#include "Luau/IostreamHelpers.h"
11
#include "Luau/ModuleResolver.h"
12
#include "Luau/VisitType.h"
13
14
#include "ScopedFlags.h"
15
#include "doctest.h"
16
#include <iostream>
17
18
LUAU_DYNAMIC_FASTINT(LuauConstraintGeneratorRecursionLimit)
19
20
LUAU_FASTINT(LuauNonStrictTypeCheckerRecursionLimit)
21
LUAU_FASTINT(LuauCheckRecursionLimit)
22
LUAU_FASTFLAG(LuauAddRecursionCounterToNonStrictTypeChecker)
23
LUAU_FASTFLAG(LuauExplicitTypeInstantiationSupport)
24
LUAU_FASTFLAG(DebugLuauForceOldSolver)
25
26
using namespace Luau;
27
28
#define NONSTRICT_REQUIRE_ERR_AT_POS(pos, result, idx) \
29
do \
30
{ \
31
auto pos_ = (pos); \
32
bool foundErr = false; \
33
int index = 0; \
34
for (const auto& err : result.errors) \
35
{ \
36
if (err.location.begin == pos_) \
37
{ \
38
foundErr = true; \
39
break; \
40
} \
41
index++; \
42
} \
43
REQUIRE_MESSAGE(foundErr, "Expected error at " << pos_); \
44
idx = index; \
45
} while (false)
46
47
#define NONSTRICT_REQUIRE_CHECKED_ERR(pos, name, result) \
48
do \
49
{ \
50
int errIndex; \
51
NONSTRICT_REQUIRE_ERR_AT_POS(pos, result, errIndex); \
52
auto err = get<CheckedFunctionCallError>(result.errors[errIndex]); \
53
REQUIRE(err != nullptr); \
54
CHECK_EQ(err->checkedFunctionName, name); \
55
} while (false)
56
57
#define NONSTRICT_REQUIRE_FUNC_DEFINITION_ERR(pos, argname, result) \
58
do \
59
{ \
60
int errIndex; \
61
NONSTRICT_REQUIRE_ERR_AT_POS(pos, result, errIndex); \
62
auto err = get<NonStrictFunctionDefinitionError>(result.errors[errIndex]); \
63
REQUIRE(err != nullptr); \
64
CHECK_EQ(err->argument, argname); \
65
} while (false)
66
67
68
struct NonStrictTypeCheckerFixture : Fixture
69
{
70
71
NonStrictTypeCheckerFixture() = default;
72
73
CheckResult checkNonStrict(const std::string& code)
74
{
75
ScopedFastFlag flags[] = {
76
{FFlag::DebugLuauForceOldSolver, false},
77
};
78
LoadDefinitionFileResult res = loadDefinition(definitions);
79
LUAU_ASSERT(res.success);
80
return check(Mode::Nonstrict, code);
81
}
82
83
CheckResult checkNonStrictModule(const std::string& moduleName)
84
{
85
ScopedFastFlag flags[] = {
86
{FFlag::DebugLuauForceOldSolver, false},
87
};
88
LoadDefinitionFileResult res = loadDefinition(definitions);
89
LUAU_ASSERT(res.success);
90
return getFrontend().check(moduleName);
91
}
92
93
Frontend& getFrontend() override
94
{
95
if (frontend)
96
return *frontend;
97
98
Frontend& f = Fixture::getFrontend();
99
registerHiddenTypes(f);
100
registerTestTypes();
101
return *frontend;
102
}
103
104
std::string definitions = R"BUILTIN_SRC(
105
@checked declare function abs(n: number): number
106
@checked declare function lower(s: string): string
107
declare function cond() : boolean
108
@checked declare function contrived(n : Not<number>) : number
109
110
-- interesting types of things that we would like to mark as checked
111
@checked declare function onlyNums(...: number) : number
112
@checked declare function mixedArgs(x: string, ...: number) : number
113
@checked declare function optionalArg(x: string?) : number
114
declare foo: {
115
bar: @checked (number) -> number,
116
}
117
118
@checked declare function optionalArgsAtTheEnd1(x: string, y: number?, z: number?) : number
119
@checked declare function optionalArgsAtTheEnd2(x: string, y: number?, z: string) : number
120
121
type DateTypeArg = {
122
year: number,
123
month: number,
124
day: number,
125
hour: number?,
126
min: number?,
127
sec: number?,
128
isdst: boolean?,
129
}
130
131
declare os : {
132
time: @checked (time: DateTypeArg?) -> number
133
}
134
135
@checked declare function require(target : any) : any
136
@checked declare function getAllTheArgsWrong(one: string, two: number, three: boolean) : any
137
)BUILTIN_SRC";
138
};
139
140
TEST_SUITE_BEGIN("NonStrictTypeCheckerTest");
141
142
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "interesting_checked_functions")
143
{
144
CheckResult result = checkNonStrict(R"(
145
onlyNums(1,1,1)
146
onlyNums(1, "a")
147
148
mixedArgs("a", 1, 2)
149
mixedArgs(1, 1, 1)
150
mixedArgs("a", true)
151
152
optionalArg(nil)
153
optionalArg("a")
154
optionalArg(3)
155
)");
156
157
LUAU_REQUIRE_ERROR_COUNT(4, result);
158
NONSTRICT_REQUIRE_CHECKED_ERR(Position(2, 12), "onlyNums", result); // onlyNums(1, "a")
159
160
NONSTRICT_REQUIRE_CHECKED_ERR(Position(5, 10), "mixedArgs", result); // mixedArgs(1, 1, 1)
161
NONSTRICT_REQUIRE_CHECKED_ERR(Position(6, 15), "mixedArgs", result); // mixedArgs("a", true)
162
163
NONSTRICT_REQUIRE_CHECKED_ERR(Position(10, 12), "optionalArg", result); // optionalArg(3)
164
}
165
166
167
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "simple_negation_caching_example")
168
{
169
CheckResult result = checkNonStrict(R"(
170
local x = 3
171
abs(x)
172
abs(x)
173
)");
174
175
LUAU_REQUIRE_NO_ERRORS(result);
176
177
result = checkNonStrict(R"(
178
local x = 3
179
contrived(x)
180
contrived(x)
181
)");
182
183
LUAU_REQUIRE_ERROR_COUNT(2, result);
184
NONSTRICT_REQUIRE_CHECKED_ERR(Position(2, 10), "contrived", result);
185
NONSTRICT_REQUIRE_CHECKED_ERR(Position(3, 10), "contrived", result);
186
}
187
188
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "simple_non_strict_failure")
189
{
190
CheckResult result = checkNonStrict(R"BUILTIN_SRC(
191
abs("hi")
192
)BUILTIN_SRC");
193
LUAU_REQUIRE_ERROR_COUNT(1, result);
194
NONSTRICT_REQUIRE_CHECKED_ERR(Position(1, 4), "abs", result);
195
}
196
197
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "nested_function_calls_constant")
198
{
199
CheckResult result = checkNonStrict(R"(
200
local x
201
abs(lower(x))
202
)");
203
204
LUAU_REQUIRE_ERROR_COUNT(2, result);
205
NONSTRICT_REQUIRE_CHECKED_ERR(Position(2, 4), "abs", result);
206
NONSTRICT_REQUIRE_CHECKED_ERR(Position(2, 10), "lower", result);
207
}
208
209
210
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "if_then_else_does_not_warn_with_never_local")
211
{
212
CheckResult result = checkNonStrict(R"(
213
local x : never
214
if cond() then
215
abs(x)
216
else
217
lower(x)
218
end
219
)");
220
221
LUAU_REQUIRE_NO_ERRORS(result);
222
}
223
224
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "if_then_else_warns_nil_branches")
225
{
226
auto result = checkNonStrict(R"(
227
local x
228
if cond() then
229
abs(x)
230
else
231
lower(x)
232
end
233
)");
234
235
LUAU_REQUIRE_ERROR_COUNT(2, result);
236
NONSTRICT_REQUIRE_CHECKED_ERR(Position(3, 8), "abs", result);
237
NONSTRICT_REQUIRE_CHECKED_ERR(Position(5, 10), "lower", result);
238
}
239
240
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "if_then_else_doesnt_warn_else_branch")
241
{
242
auto result = checkNonStrict(R"(
243
local x : string = "hi"
244
if cond() then
245
abs(x)
246
else
247
lower(x)
248
end
249
)");
250
251
LUAU_REQUIRE_ERROR_COUNT(1, result);
252
NONSTRICT_REQUIRE_CHECKED_ERR(Position(3, 8), "abs", result);
253
}
254
255
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "if_then_no_else")
256
{
257
CheckResult result = checkNonStrict(R"(
258
local x : string
259
if cond() then
260
abs(x)
261
end
262
)");
263
264
LUAU_REQUIRE_ERROR_COUNT(1, result);
265
NONSTRICT_REQUIRE_CHECKED_ERR(Position(3, 8), "abs", result);
266
}
267
268
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "if_then_no_else_err_in_cond")
269
{
270
CheckResult result = checkNonStrict(R"(
271
local x : string = ""
272
if abs(x) then
273
lower(x)
274
end
275
)");
276
LUAU_REQUIRE_ERROR_COUNT(1, result);
277
NONSTRICT_REQUIRE_CHECKED_ERR(Position(2, 7), "abs", result);
278
}
279
280
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "if_then_else_expr_should_warn")
281
{
282
CheckResult result = checkNonStrict(R"(
283
local x = 42
284
local y = if cond() then abs(x) else lower(x)
285
)");
286
287
LUAU_REQUIRE_ERROR_COUNT(1, result);
288
NONSTRICT_REQUIRE_CHECKED_ERR(Position(2, 43), "lower", result);
289
}
290
291
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "if_then_else_expr_should_not_warn_for_never")
292
{
293
CheckResult result = checkNonStrict(R"(
294
local x : never
295
local y = if cond() then abs(x) else lower(x)
296
)");
297
298
LUAU_REQUIRE_NO_ERRORS(result);
299
}
300
301
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "if_then_else_expr_doesnt_warn_else_branch")
302
{
303
CheckResult result = checkNonStrict(R"(
304
local x : string = "hi"
305
local y = if cond() then abs(x) else lower(x)
306
)");
307
LUAU_REQUIRE_ERROR_COUNT(1, result);
308
NONSTRICT_REQUIRE_CHECKED_ERR(Position(2, 29), "abs", result);
309
}
310
311
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "sequencing_if_checked_call")
312
{
313
CheckResult result = checkNonStrict(R"(
314
local x
315
if cond() then
316
x = 5
317
else
318
x = nil
319
end
320
lower(x)
321
)");
322
LUAU_REQUIRE_ERROR_COUNT(1, result);
323
NONSTRICT_REQUIRE_CHECKED_ERR(Position(7, 6), "lower", result);
324
}
325
326
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "function_def_unrelated_checked_calls")
327
{
328
CheckResult result = checkNonStrict(R"(
329
function h(x, y)
330
abs(x)
331
lower(y)
332
end
333
)");
334
335
LUAU_REQUIRE_NO_ERRORS(result);
336
}
337
338
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "function_def_basic_no_errors")
339
{
340
CheckResult result = checkNonStrict(R"(
341
function f(x)
342
abs(x)
343
end
344
)");
345
LUAU_REQUIRE_NO_ERRORS(result);
346
}
347
348
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "function_def_basic_errors")
349
{
350
CheckResult result = checkNonStrict(R"(
351
function f(x : string)
352
abs(x)
353
end
354
)");
355
LUAU_REQUIRE_ERROR_COUNT(1, result);
356
NONSTRICT_REQUIRE_CHECKED_ERR(Position(2, 8), "abs", result);
357
}
358
359
360
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "function_def_failure")
361
{
362
CheckResult result = checkNonStrict(R"(
363
function f(x)
364
abs(lower(x))
365
end
366
)");
367
LUAU_REQUIRE_ERROR_COUNT(1, result);
368
NONSTRICT_REQUIRE_CHECKED_ERR(Position(2, 8), "abs", result);
369
}
370
371
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "function_def_sequencing_errors")
372
{
373
CheckResult result = checkNonStrict(R"(
374
function f(x)
375
abs(x)
376
lower(x)
377
end
378
)");
379
380
LUAU_REQUIRE_ERROR_COUNT(1, result);
381
NONSTRICT_REQUIRE_FUNC_DEFINITION_ERR(Position(1, 11), "x", result);
382
}
383
384
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "function_def_sequencing_errors_2")
385
{
386
CheckResult result = checkNonStrict(R"(
387
local t = {function(x)
388
abs(x)
389
lower(x)
390
end}
391
)");
392
393
LUAU_REQUIRE_ERROR_COUNT(1, result);
394
CHECK(toString(result.errors[0]) == "the argument 'x' is used in a way that will error at runtime");
395
}
396
397
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "local_fn_produces_error")
398
{
399
CheckResult result = checkNonStrict(R"(
400
local x = 5
401
local function y() lower(x) end
402
)");
403
404
LUAU_REQUIRE_ERROR_COUNT(1, result);
405
NONSTRICT_REQUIRE_CHECKED_ERR(Position(2, 25), "lower", result);
406
}
407
408
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "fn_expr_produces_error")
409
{
410
CheckResult result = checkNonStrict(R"(
411
local x = 5
412
local y = function() lower(x) end
413
)");
414
415
LUAU_REQUIRE_ERROR_COUNT(1, result);
416
NONSTRICT_REQUIRE_CHECKED_ERR(Position(2, 27), "lower", result);
417
}
418
419
420
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "function_def_if_warns_never")
421
{
422
CheckResult result = checkNonStrict(R"(
423
function f(x: never)
424
if cond() then
425
abs(x)
426
else
427
lower(x)
428
end
429
end
430
)");
431
432
LUAU_REQUIRE_NO_ERRORS(result);
433
}
434
435
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "function_def_if_no_else")
436
{
437
CheckResult result = checkNonStrict(R"(
438
function f(x)
439
if cond() then
440
abs(x)
441
end
442
end
443
)");
444
LUAU_REQUIRE_NO_ERRORS(result);
445
}
446
447
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "function_def_if_assignment_errors")
448
{
449
CheckResult result = checkNonStrict(R"(
450
function f(x)
451
if cond() then
452
x = 5
453
else
454
x = nil
455
end
456
lower(x)
457
end
458
)");
459
LUAU_REQUIRE_ERROR_COUNT(1, result);
460
NONSTRICT_REQUIRE_CHECKED_ERR(Position(7, 10), "lower", result);
461
}
462
463
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "generic_type_instantiation")
464
{
465
ScopedFastFlag semantics{FFlag::LuauExplicitTypeInstantiationSupport, true};
466
467
CheckResult result = checkNonStrict(R"(
468
function array<T>(): {T}
469
return {}
470
end
471
472
local foo = array<<number>>()
473
local bar = array<<string>>()
474
)");
475
476
LUAU_REQUIRE_NO_ERRORS(result);
477
CHECK_EQ("{number}", toString(requireType("foo")));
478
CHECK_EQ("{string}", toString(requireType("bar")));
479
}
480
481
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "function_def_if_assignment_no_errors")
482
{
483
CheckResult result = checkNonStrict(R"(
484
function f(x : string | number)
485
if cond() then
486
x = 5
487
else
488
x = "hi"
489
end
490
abs(x)
491
end
492
)");
493
LUAU_REQUIRE_NO_ERRORS(result);
494
}
495
496
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "local_only_one_warning")
497
{
498
CheckResult result = checkNonStrict(R"(
499
local x = 5
500
lower(x)
501
)");
502
LUAU_REQUIRE_ERROR_COUNT(1, result);
503
NONSTRICT_REQUIRE_CHECKED_ERR(Position(2, 6), "lower", result);
504
}
505
506
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "phi_node_assignment")
507
{
508
CheckResult result = checkNonStrict(R"(
509
local x = "a" -- x1
510
if cond() then
511
x = 3 -- x2
512
end
513
lower(x) -- phi {x1, x2}
514
)");
515
516
LUAU_REQUIRE_NO_ERRORS(result);
517
} //
518
519
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "phi_node_assignment_err")
520
{
521
CheckResult result = checkNonStrict(R"(
522
local x = nil
523
if cond() then
524
if cond() then
525
x = 5
526
end
527
abs(x)
528
else
529
lower(x)
530
end
531
)");
532
533
LUAU_REQUIRE_ERROR_COUNT(1, result);
534
NONSTRICT_REQUIRE_CHECKED_ERR(Position(8, 10), "lower", result);
535
}
536
537
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "tblprop_is_checked")
538
{
539
CheckResult result = checkNonStrict(R"(
540
foo.bar("hi")
541
)");
542
LUAU_REQUIRE_ERROR_COUNT(1, result);
543
NONSTRICT_REQUIRE_CHECKED_ERR(Position(1, 8), "foo.bar", result);
544
}
545
546
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "exprgroup_is_checked")
547
{
548
CheckResult result = checkNonStrict(R"(
549
local foo = (abs("foo"))
550
)");
551
552
LUAU_REQUIRE_ERROR_COUNT(1, result);
553
554
auto r1 = get<CheckedFunctionCallError>(result.errors[0]);
555
LUAU_ASSERT(r1);
556
CHECK_EQ("abs", r1->checkedFunctionName);
557
CHECK_EQ("number", toString(r1->expected));
558
CHECK_EQ("string", toString(r1->passed));
559
}
560
561
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "binop_is_checked")
562
{
563
CheckResult result = checkNonStrict(R"(
564
local foo = 4 + abs("foo")
565
)");
566
567
LUAU_REQUIRE_ERROR_COUNT(1, result);
568
569
auto r1 = get<CheckedFunctionCallError>(result.errors[0]);
570
LUAU_ASSERT(r1);
571
CHECK_EQ("abs", r1->checkedFunctionName);
572
CHECK_EQ("number", toString(r1->expected));
573
CHECK_EQ("string", toString(r1->passed));
574
}
575
576
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "incorrect_arg_count")
577
{
578
CheckResult result = checkNonStrict(R"(
579
foo.bar(1,2,3)
580
abs(3, "hi");
581
)");
582
LUAU_REQUIRE_ERROR_COUNT(2, result);
583
auto r1 = get<CheckedFunctionIncorrectArgs>(result.errors[0]);
584
auto r2 = get<CheckedFunctionIncorrectArgs>(result.errors[1]);
585
LUAU_ASSERT(r1);
586
LUAU_ASSERT(r2);
587
CHECK_EQ("abs", r1->functionName);
588
CHECK_EQ("foo.bar", r2->functionName);
589
}
590
591
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "optionals_in_checked_function_can_be_omitted")
592
{
593
CheckResult result = checkNonStrict(R"(
594
optionalArgsAtTheEnd1("a")
595
optionalArgsAtTheEnd1("a", 3)
596
optionalArgsAtTheEnd1("a", nil, 3)
597
)");
598
599
LUAU_REQUIRE_NO_ERRORS(result);
600
}
601
602
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "generic_type_packs_in_non_strict")
603
{
604
CheckResult result = checkNonStrict(R"(
605
--!nonstrict
606
local test: <T...>(T...) -> () -- TypeError: Unknown type 'T'
607
)");
608
609
LUAU_REQUIRE_NO_ERRORS(result);
610
}
611
612
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "optionals_in_checked_function_in_middle_cannot_be_omitted")
613
{
614
CheckResult result = checkNonStrict(R"(
615
optionalArgsAtTheEnd2("a", "a") -- error
616
optionalArgsAtTheEnd2("a", nil, "b")
617
optionalArgsAtTheEnd2("a", 3, "b")
618
optionalArgsAtTheEnd2("a", "b", "c") -- error
619
)");
620
LUAU_REQUIRE_ERROR_COUNT(3, result);
621
NONSTRICT_REQUIRE_CHECKED_ERR(Position(1, 27), "optionalArgsAtTheEnd2", result);
622
NONSTRICT_REQUIRE_CHECKED_ERR(Position(4, 27), "optionalArgsAtTheEnd2", result);
623
auto r1 = get<CheckedFunctionIncorrectArgs>(result.errors[2]);
624
LUAU_ASSERT(r1);
625
CHECK_EQ(3, r1->expected);
626
CHECK_EQ(2, r1->actual);
627
}
628
629
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "non_testable_type_throws_ice")
630
{
631
CHECK_THROWS_AS(
632
checkNonStrict(R"(
633
os.time({year = 0, month = 0, day = 0, min = 0, isdst = nil})
634
)"),
635
Luau::InternalCompilerError
636
);
637
}
638
639
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "non_strict_shouldnt_warn_on_require_module")
640
{
641
fileResolver.source["Modules/A"] = R"(
642
--!strict
643
type t = {x : number}
644
local e : t = {x = 3}
645
return e
646
)";
647
fileResolver.sourceTypes["Modules/A"] = SourceCode::Module;
648
649
fileResolver.source["Modules/B"] = R"(
650
--!nonstrict
651
local E = require(script.Parent.A)
652
)";
653
654
CheckResult result = checkNonStrictModule("Modules/B");
655
LUAU_REQUIRE_NO_ERRORS(result);
656
}
657
658
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "nonstrict_shouldnt_warn_on_valid_buffer_use")
659
{
660
loadDefinition(R"(
661
declare buffer: {
662
create: @checked (size: number) -> buffer,
663
readi8: @checked (b: buffer, offset: number) -> number,
664
writef64: @checked (b: buffer, offset: number, value: number) -> (),
665
}
666
)");
667
668
CheckResult result = checkNonStrict(R"(
669
local b = buffer.create(100)
670
buffer.writef64(b, 0, 5)
671
buffer.readi8(b, 0)
672
)");
673
LUAU_REQUIRE_NO_ERRORS(result);
674
}
675
676
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "nonstrict_method_calls")
677
{
678
Luau::unfreeze(getFrontend().globals.globalTypes);
679
Luau::unfreeze(getFrontend().globalsForAutocomplete.globalTypes);
680
681
registerBuiltinGlobals(getFrontend(), getFrontend().globals);
682
registerTestTypes();
683
684
Luau::freeze(getFrontend().globals.globalTypes);
685
Luau::freeze(getFrontend().globalsForAutocomplete.globalTypes);
686
687
CheckResult result = checkNonStrict(R"(
688
local test = "test"
689
test:lower()
690
)");
691
692
LUAU_REQUIRE_NO_ERRORS(result);
693
}
694
695
TEST_CASE_FIXTURE(Fixture, "unknown_globals_in_non_strict_1")
696
{
697
CheckResult result = check(Mode::Nonstrict, R"(
698
foo = 5
699
local wrong1 = foob
700
701
local x = 12
702
local wrong2 = x + foblm
703
)");
704
705
LUAU_REQUIRE_ERROR_COUNT(2, result);
706
}
707
708
TEST_CASE_FIXTURE(BuiltinsFixture, "unknown_types_in_non_strict")
709
{
710
CheckResult result = check(Mode::Nonstrict, R"(
711
--!nonstrict
712
local foo: Foo = 1
713
)");
714
715
LUAU_REQUIRE_ERROR_COUNT(1, result);
716
const UnknownSymbol* err = get<UnknownSymbol>(result.errors[0]);
717
CHECK_EQ(err->name, "Foo");
718
CHECK_EQ(err->context, UnknownSymbol::Context::Type);
719
}
720
721
TEST_CASE_FIXTURE(BuiltinsFixture, "unknown_types_in_non_strict_2")
722
{
723
CheckResult result = check(Mode::Nonstrict, R"(
724
--!nonstrict
725
local foo = 1 :: Foo
726
)");
727
728
LUAU_REQUIRE_ERROR_COUNT(1, result);
729
const UnknownSymbol* err = get<UnknownSymbol>(result.errors[0]);
730
CHECK_EQ(err->name, "Foo");
731
CHECK_EQ(err->context, UnknownSymbol::Context::Type);
732
}
733
734
TEST_CASE_FIXTURE(BuiltinsFixture, "buffer_is_not_unknown")
735
{
736
CheckResult result = check(Mode::Nonstrict, R"(
737
local function wrap(b: buffer, i: number, v: number)
738
buffer.writeu32(b, i * 4, v)
739
end
740
)");
741
742
LUAU_REQUIRE_NO_ERRORS(result);
743
}
744
745
TEST_CASE_FIXTURE(Fixture, "incomplete_function_annotation")
746
{
747
CheckResult result = check(Mode::Nonstrict, R"(
748
local x: () ->
749
)");
750
751
LUAU_REQUIRE_ERRORS(result);
752
}
753
754
TEST_CASE_FIXTURE(Fixture, "unknown_globals_in_function_calls")
755
{
756
CheckResult result = check(Mode::Nonstrict, R"(
757
local function foo() : ()
758
bar()
759
end
760
)");
761
762
LUAU_REQUIRE_ERROR_COUNT(1, result);
763
const UnknownSymbol* err = get<UnknownSymbol>(result.errors[0]);
764
CHECK_EQ(err->name, "bar");
765
CHECK_EQ(err->context, UnknownSymbol::Context::Binding);
766
}
767
768
TEST_CASE_FIXTURE(Fixture, "unknown_globals_in_one_sided_conditionals")
769
{
770
CheckResult result = check(Mode::Nonstrict, R"(
771
local function foo(cond) : ()
772
if cond then
773
bar()
774
end
775
end
776
)");
777
778
LUAU_REQUIRE_ERROR_COUNT(1, result);
779
const UnknownSymbol* err = get<UnknownSymbol>(result.errors[0]);
780
CHECK_EQ(err->name, "bar");
781
CHECK_EQ(err->context, UnknownSymbol::Context::Binding);
782
}
783
784
TEST_CASE_FIXTURE(BuiltinsFixture, "new_non_strict_should_suppress_dynamic_require_errors")
785
{
786
ScopedFastFlag sff{FFlag::DebugLuauForceOldSolver, false};
787
// Avoid warning about dynamic requires in new nonstrict mode
788
CheckResult result = check(Mode::Nonstrict, R"(
789
function passThrough(module)
790
require(module)
791
end
792
)");
793
794
LUAU_REQUIRE_ERROR_COUNT(0, result);
795
// We should still warn about dynamic requires in strict mode
796
result = check(Mode::Strict, R"(
797
function passThrough(module)
798
require(module)
799
end
800
)");
801
802
LUAU_REQUIRE_ERROR_COUNT(1, result);
803
const UnknownRequire* req = get<UnknownRequire>(result.errors[0]);
804
CHECK(req != nullptr);
805
}
806
807
TEST_CASE_FIXTURE(BuiltinsFixture, "new_non_strict_should_suppress_unknown_require_errors")
808
{
809
ScopedFastFlag sff{FFlag::DebugLuauForceOldSolver, false};
810
811
// Avoid warning about dynamic requires in new nonstrict mode
812
CheckResult result = check(Mode::Nonstrict, R"(
813
require(script.NonExistent)
814
require("@self/NonExistent")
815
)");
816
817
LUAU_REQUIRE_ERROR_COUNT(0, result);
818
// We should still warn about dynamic requires in strict mode
819
result = check(Mode::Strict, R"(
820
require(script.NonExistent)
821
require("@self/NonExistent")
822
)");
823
824
LUAU_REQUIRE_ERROR_COUNT(2, result);
825
const UnknownRequire* req1 = get<UnknownRequire>(result.errors[0]);
826
CHECK(req1 != nullptr);
827
const UnknownRequire* req2 = get<UnknownRequire>(result.errors[1]);
828
CHECK(req2 != nullptr);
829
}
830
831
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "new_non_strict_stringifies_checked_function_errors_as_one_indexed")
832
{
833
CheckResult result = checkNonStrict(R"(
834
getAllTheArgsWrong(3, true, "what")
835
)");
836
LUAU_REQUIRE_ERROR_COUNT(3, result);
837
const CheckedFunctionCallError* err1 = get<CheckedFunctionCallError>(result.errors[0]);
838
const CheckedFunctionCallError* err2 = get<CheckedFunctionCallError>(result.errors[1]);
839
const CheckedFunctionCallError* err3 = get<CheckedFunctionCallError>(result.errors[2]);
840
CHECK(err1 != nullptr);
841
CHECK(err2 != nullptr);
842
CHECK(err3 != nullptr);
843
CHECK_EQ(
844
"the function 'getAllTheArgsWrong' expects to get a string as its 1st argument, but is being given a number", toString(result.errors[0])
845
);
846
CHECK_EQ(
847
"the function 'getAllTheArgsWrong' expects to get a number as its 2nd argument, but is being given a boolean", toString(result.errors[1])
848
);
849
CHECK_EQ(
850
"the function 'getAllTheArgsWrong' expects to get a boolean as its 3rd argument, but is being given a string", toString(result.errors[2])
851
);
852
}
853
854
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "new_non_strict_skips_warnings_on_unreduced_typefunctions")
855
{
856
CheckResult result = checkNonStrict(R"(
857
function foo(x)
858
local y = x + 1
859
return abs(y)
860
end
861
)");
862
863
LUAU_REQUIRE_NO_ERRORS(result);
864
}
865
866
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "nonstrict_check_block_recursion_limit")
867
{
868
int limit = 250;
869
870
ScopedFastFlag sff{FFlag::LuauAddRecursionCounterToNonStrictTypeChecker, true};
871
872
ScopedFastInt luauNonStrictTypeCheckerRecursionLimit{FInt::LuauNonStrictTypeCheckerRecursionLimit, limit - 100};
873
ScopedFastInt luauConstraintGeneratorRecursionLimit{DFInt::LuauConstraintGeneratorRecursionLimit, limit + 500};
874
ScopedFastInt luauCheckRecursionLimit{FInt::LuauCheckRecursionLimit, limit + 500};
875
876
CheckResult result = checkNonStrict(rep("do ", limit) + "local a = 1" + rep(" end", limit));
877
878
// Nonstrict recursion limit just exits early and doesn't produce an error
879
LUAU_REQUIRE_NO_ERRORS(result);
880
}
881
882
#if 0 // CLI-181303 requires a ConstraintGenerator::checkPack fix to succeed in debug on Windows
883
TEST_CASE_FIXTURE(NonStrictTypeCheckerFixture, "nonstrict_check_expr_recursion_limit")
884
{
885
int limit = 250;
886
887
ScopedFastFlag sff{FFlag::LuauAddRecursionCounterToNonStrictTypeChecker, true};
888
889
ScopedFastInt luauNonStrictTypeCheckerRecursionLimit{FInt::LuauNonStrictTypeCheckerRecursionLimit, limit - 100};
890
ScopedFastInt luauConstraintGeneratorRecursionLimit{DFInt::LuauConstraintGeneratorRecursionLimit, limit + 500};
891
ScopedFastInt luauCheckRecursionLimit{FInt::LuauCheckRecursionLimit, limit + 500};
892
893
CheckResult result = checkNonStrict(R"(("foo"))" + rep(":lower()", limit));
894
895
// Nonstrict recursion limit just exits early and doesn't produce an error
896
LUAU_REQUIRE_NO_ERRORS(result);
897
}
898
#endif
899
900
TEST_SUITE_END();
901
902