Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/tests/Parser.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/Parser.h"
3
4
#include "AstQueryDsl.h"
5
#include "Fixture.h"
6
#include "Luau/Ast.h"
7
#include "Luau/Common.h"
8
#include "ScopedFlags.h"
9
10
#include "doctest.h"
11
12
#include <limits.h>
13
14
using namespace Luau;
15
16
LUAU_FASTINT(LuauRecursionLimit)
17
LUAU_FASTINT(LuauTypeLengthLimit)
18
LUAU_FASTINT(LuauParseErrorLimit)
19
LUAU_DYNAMIC_FASTFLAG(DebugLuauReportReturnTypeVariadicWithTypeSuffix)
20
LUAU_FASTFLAG(LuauConst2)
21
LUAU_FASTFLAG(DebugLuauNoInline)
22
LUAU_FASTFLAG(LuauExternReadWriteAttributes)
23
LUAU_FASTFLAG(LuauIntegerType)
24
25
// Clip with DebugLuauReportReturnTypeVariadicWithTypeSuffix
26
extern bool luau_telemetry_parsed_return_type_variadic_with_type_suffix;
27
28
namespace
29
{
30
31
struct Counter
32
{
33
static int instanceCount;
34
35
int id;
36
37
Counter()
38
{
39
++instanceCount;
40
id = instanceCount;
41
}
42
};
43
44
int Counter::instanceCount = 0;
45
46
std::string_view stringAtLocation(std::string_view source, const Location& location)
47
{
48
std::vector<std::string_view> lines = Luau::split(source, '\n');
49
LUAU_ASSERT(lines.size() > location.begin.line && lines.size() > location.end.line);
50
51
int byteStart = -1;
52
int byteEnd = -1;
53
int bytesSum = 0;
54
55
for (size_t lineNo = 0; lineNo < lines.size(); ++lineNo)
56
{
57
std::string_view line = lines.at(lineNo);
58
59
if (lineNo == location.begin.line)
60
{
61
byteStart = bytesSum + location.begin.column;
62
}
63
64
if (lineNo == location.end.line)
65
{
66
byteEnd = bytesSum + location.end.column;
67
break;
68
}
69
70
bytesSum += static_cast<int>(line.size()) + 1;
71
}
72
73
LUAU_ASSERT(byteStart != -1);
74
LUAU_ASSERT(byteEnd != -1);
75
76
return source.substr(byteStart, byteEnd - byteStart);
77
}
78
79
} // namespace
80
81
TEST_SUITE_BEGIN("AllocatorTests");
82
83
TEST_CASE("allocator_can_be_moved")
84
{
85
Counter* c = nullptr;
86
auto inner = [&]()
87
{
88
Luau::Allocator allocator;
89
c = allocator.alloc<Counter>();
90
Luau::Allocator moved{std::move(allocator)};
91
return moved;
92
};
93
94
Counter::instanceCount = 0;
95
Luau::Allocator a{inner()};
96
97
CHECK_EQ(1, c->id);
98
}
99
100
TEST_CASE("moved_out_Allocator_can_still_be_used")
101
{
102
Luau::Allocator outer;
103
Luau::Allocator inner{std::move(outer)};
104
105
// NOLINTNEXTLINE(bugprone-use-after-move) -- verifying moved-from state
106
int* i = outer.alloc<int>();
107
REQUIRE(i != nullptr);
108
*i = 55;
109
REQUIRE_EQ(*i, 55);
110
}
111
112
TEST_CASE("aligns_things")
113
{
114
Luau::Allocator alloc;
115
116
char* one = alloc.alloc<char>();
117
double* two = alloc.alloc<double>();
118
(void)one;
119
CHECK_EQ(0, reinterpret_cast<intptr_t>(two) & (alignof(double) - 1));
120
}
121
122
TEST_CASE("initial_double_is_aligned")
123
{
124
Luau::Allocator alloc;
125
126
double* one = alloc.alloc<double>();
127
CHECK_EQ(0, reinterpret_cast<intptr_t>(one) & (alignof(double) - 1));
128
}
129
130
TEST_SUITE_END();
131
132
TEST_SUITE_BEGIN("ParserTests");
133
134
TEST_CASE_FIXTURE(Fixture, "basic_parse")
135
{
136
AstStat* stat = parse("print(\"Hello World!\")");
137
REQUIRE(stat != nullptr);
138
}
139
140
TEST_CASE_FIXTURE(Fixture, "can_haz_annotations")
141
{
142
AstStatBlock* block = parse("local foo: string = \"Hello Types!\"");
143
REQUIRE(block != nullptr);
144
}
145
146
TEST_CASE_FIXTURE(Fixture, "local_with_annotation")
147
{
148
std::string code = R"(
149
local foo: string = "Hello Types!"
150
)";
151
152
AstStatBlock* block = parse(code);
153
154
REQUIRE(block != nullptr);
155
156
REQUIRE(block->body.size > 0);
157
158
AstStatLocal* local = block->body.data[0]->as<AstStatLocal>();
159
REQUIRE(local != nullptr);
160
161
REQUIRE_EQ(1, local->vars.size);
162
163
AstLocal* l = local->vars.data[0];
164
REQUIRE(l->annotation != nullptr);
165
166
REQUIRE_EQ(1, local->values.size);
167
168
REQUIRE_EQ(stringAtLocation(code, l->location), "foo");
169
}
170
171
TEST_CASE_FIXTURE(Fixture, "type_names_can_contain_dots")
172
{
173
AstStatBlock* block = parse(R"(
174
local foo: SomeModule.CoolType
175
)");
176
177
REQUIRE(block != nullptr);
178
}
179
180
TEST_CASE_FIXTURE(Fixture, "functions_can_have_return_annotations")
181
{
182
AstStatBlock* block = parse(R"(
183
function foo(): number return 55 end
184
)");
185
186
REQUIRE(block != nullptr);
187
REQUIRE(block->body.size > 0);
188
189
AstStatFunction* statFunction = block->body.data[0]->as<AstStatFunction>();
190
REQUIRE(statFunction != nullptr);
191
192
REQUIRE(statFunction->func->returnAnnotation);
193
auto typePack = statFunction->func->returnAnnotation->as<AstTypePackExplicit>();
194
REQUIRE(typePack);
195
CHECK_EQ(typePack->typeList.types.size, 1);
196
CHECK(typePack->typeList.tailType == nullptr);
197
}
198
199
TEST_CASE_FIXTURE(Fixture, "functions_can_have_a_function_type_annotation")
200
{
201
AstStatBlock* block = parse(R"(
202
function f(): (number) -> nil return nil end
203
)");
204
205
REQUIRE(block != nullptr);
206
REQUIRE(block->body.size > 0);
207
208
AstStatFunction* statFunc = block->body.data[0]->as<AstStatFunction>();
209
REQUIRE(statFunc != nullptr);
210
211
REQUIRE(statFunc->func->returnAnnotation);
212
auto typePack = statFunc->func->returnAnnotation->as<AstTypePackExplicit>();
213
REQUIRE(typePack);
214
CHECK(typePack->typeList.tailType == nullptr);
215
AstArray<AstType*>& retTypes = typePack->typeList.types;
216
REQUIRE(retTypes.size == 1);
217
218
AstTypeFunction* funTy = retTypes.data[0]->as<AstTypeFunction>();
219
REQUIRE(funTy != nullptr);
220
}
221
222
TEST_CASE_FIXTURE(Fixture, "function_return_type_should_disambiguate_from_function_type_and_multiple_returns")
223
{
224
AstStatBlock* block = parse(R"(
225
function f(): (number, string) return 1, "foo" end
226
)");
227
228
REQUIRE(block != nullptr);
229
REQUIRE(block->body.size > 0);
230
231
AstStatFunction* statFunc = block->body.data[0]->as<AstStatFunction>();
232
REQUIRE(statFunc != nullptr);
233
234
REQUIRE(statFunc->func->returnAnnotation);
235
auto typePack = statFunc->func->returnAnnotation->as<AstTypePackExplicit>();
236
REQUIRE(typePack);
237
CHECK(typePack->typeList.tailType == nullptr);
238
AstArray<AstType*>& retTypes = typePack->typeList.types;
239
REQUIRE(retTypes.size == 2);
240
241
AstTypeReference* ty0 = retTypes.data[0]->as<AstTypeReference>();
242
REQUIRE(ty0 != nullptr);
243
REQUIRE(ty0->name == "number");
244
245
AstTypeReference* ty1 = retTypes.data[1]->as<AstTypeReference>();
246
REQUIRE(ty1 != nullptr);
247
REQUIRE(ty1->name == "string");
248
}
249
250
TEST_CASE_FIXTURE(Fixture, "function_return_type_should_parse_as_function_type_annotation_with_no_args")
251
{
252
AstStatBlock* block = parse(R"(
253
function f(): () -> nil return nil end
254
)");
255
256
REQUIRE(block != nullptr);
257
REQUIRE(block->body.size > 0);
258
259
AstStatFunction* statFunc = block->body.data[0]->as<AstStatFunction>();
260
REQUIRE(statFunc != nullptr);
261
262
REQUIRE(statFunc->func->returnAnnotation);
263
auto typePack = statFunc->func->returnAnnotation->as<AstTypePackExplicit>();
264
REQUIRE(typePack);
265
CHECK(typePack->typeList.tailType == nullptr);
266
AstArray<AstType*>& retTypes = typePack->typeList.types;
267
REQUIRE(retTypes.size == 1);
268
269
AstTypeFunction* funTy = retTypes.data[0]->as<AstTypeFunction>();
270
REQUIRE(funTy != nullptr);
271
REQUIRE(funTy->argTypes.types.size == 0);
272
CHECK(funTy->argTypes.tailType == nullptr);
273
274
auto funReturnPack = funTy->returnTypes->as<AstTypePackExplicit>();
275
REQUIRE(funReturnPack);
276
CHECK(funReturnPack->typeList.tailType == nullptr);
277
278
AstTypeReference* ty = funReturnPack->typeList.types.data[0]->as<AstTypeReference>();
279
REQUIRE(ty != nullptr);
280
REQUIRE(ty->name == "nil");
281
}
282
283
TEST_CASE_FIXTURE(Fixture, "annotations_can_be_tables")
284
{
285
AstStatBlock* stat = parse(R"(
286
local zero: number
287
local one: {x: number, y: string}
288
)");
289
290
REQUIRE(stat != nullptr);
291
}
292
293
TEST_CASE_FIXTURE(Fixture, "tables_should_have_an_indexer_and_keys")
294
{
295
AstStatBlock* stat = parse(R"(
296
local t: {
297
[string]: number,
298
f: () -> nil
299
}
300
)");
301
302
REQUIRE(stat != nullptr);
303
}
304
305
TEST_CASE_FIXTURE(Fixture, "tables_can_have_trailing_separator")
306
{
307
AstStatBlock* stat = parse(R"(
308
local zero: number
309
local one: {x: number, y: string, }
310
)");
311
312
REQUIRE(stat != nullptr);
313
}
314
315
TEST_CASE_FIXTURE(Fixture, "tables_can_use_semicolons")
316
{
317
AstStatBlock* stat = parse(R"(
318
local zero: number
319
local one: {x: number; y: string; }
320
)");
321
322
REQUIRE(stat != nullptr);
323
}
324
325
TEST_CASE_FIXTURE(Fixture, "other_places_where_type_annotations_are_allowed")
326
{
327
AstStatBlock* stat = parse(R"(
328
for i: number = 0, 50 do end
329
for i: number, s: string in expr() do end
330
)");
331
332
REQUIRE(stat != nullptr);
333
}
334
335
TEST_CASE_FIXTURE(Fixture, "nil_is_a_valid_type_name")
336
{
337
AstStatBlock* stat = parse(R"(
338
local n: nil
339
)");
340
341
REQUIRE(stat != nullptr);
342
}
343
344
TEST_CASE_FIXTURE(Fixture, "function_type_annotation")
345
{
346
AstStatBlock* stat = parse(R"(
347
local f: (number, string) -> nil
348
)");
349
350
REQUIRE(stat != nullptr);
351
}
352
353
TEST_CASE_FIXTURE(Fixture, "functions_can_return_multiple_values")
354
{
355
AstStatBlock* stat = parse(R"(
356
local f: (number) -> (number, number)
357
)");
358
359
REQUIRE(stat != nullptr);
360
}
361
362
TEST_CASE_FIXTURE(Fixture, "functions_can_have_0_arguments")
363
{
364
AstStatBlock* stat = parse(R"(
365
local f: () -> number
366
)");
367
368
REQUIRE(stat != nullptr);
369
}
370
371
TEST_CASE_FIXTURE(Fixture, "functions_can_return_0_values")
372
{
373
AstStatBlock* block = parse(R"(
374
local f: (number) -> ()
375
)");
376
377
REQUIRE(block != nullptr);
378
}
379
380
TEST_CASE_FIXTURE(Fixture, "intersection_of_two_function_types_if_no_returns")
381
{
382
AstStatBlock* block = parse(R"(
383
local f: (string) -> () & (number) -> ()
384
)");
385
386
REQUIRE(block != nullptr);
387
388
AstStatLocal* local = block->body.data[0]->as<AstStatLocal>();
389
AstTypeIntersection* annotation = local->vars.data[0]->annotation->as<AstTypeIntersection>();
390
REQUIRE(annotation != nullptr);
391
CHECK(annotation->types.data[0]->as<AstTypeFunction>());
392
CHECK(annotation->types.data[1]->as<AstTypeFunction>());
393
}
394
395
TEST_CASE_FIXTURE(Fixture, "intersection_of_two_function_types_if_two_or_more_returns")
396
{
397
AstStatBlock* block = parse(R"(
398
local f: (string) -> (string, number) & (number) -> (number, string)
399
)");
400
401
REQUIRE(block != nullptr);
402
403
AstStatLocal* local = block->body.data[0]->as<AstStatLocal>();
404
AstTypeIntersection* annotation = local->vars.data[0]->annotation->as<AstTypeIntersection>();
405
REQUIRE(annotation != nullptr);
406
CHECK(annotation->types.data[0]->as<AstTypeFunction>());
407
CHECK(annotation->types.data[1]->as<AstTypeFunction>());
408
}
409
410
TEST_CASE_FIXTURE(Fixture, "return_type_is_an_intersection_type_if_led_with_one_parenthesized_type")
411
{
412
AstStatBlock* block = parse(R"(
413
local f: (string) -> (string) & (number) -> (number)
414
)");
415
416
REQUIRE(block != nullptr);
417
418
AstStatLocal* local = block->body.data[0]->as<AstStatLocal>();
419
AstTypeFunction* annotation = local->vars.data[0]->annotation->as<AstTypeFunction>();
420
REQUIRE(annotation != nullptr);
421
422
auto returnTypePack = annotation->returnTypes->as<AstTypePackExplicit>();
423
REQUIRE(returnTypePack);
424
AstTypeIntersection* returnAnnotation = returnTypePack->typeList.types.data[0]->as<AstTypeIntersection>();
425
REQUIRE(returnAnnotation != nullptr);
426
CHECK(returnAnnotation->types.data[0]->as<AstTypeGroup>());
427
CHECK(returnAnnotation->types.data[1]->as<AstTypeFunction>());
428
}
429
430
TEST_CASE_FIXTURE(Fixture, "type_alias_to_a_typeof")
431
{
432
AstStatBlock* block = parse(R"(
433
type A = typeof(1)
434
)");
435
436
REQUIRE(block != nullptr);
437
REQUIRE(block->body.size > 0);
438
439
auto typeAliasStat = block->body.data[0]->as<AstStatTypeAlias>();
440
REQUIRE(typeAliasStat != nullptr);
441
CHECK_EQ(typeAliasStat->location, (Location{{1, 8}, {1, 26}}));
442
}
443
444
TEST_CASE_FIXTURE(Fixture, "type_alias_should_point_to_string")
445
{
446
AstStatBlock* block = parse(R"(
447
type A = string
448
)");
449
450
REQUIRE(block != nullptr);
451
REQUIRE(block->body.size > 0);
452
REQUIRE(block->body.data[0]->is<AstStatTypeAlias>());
453
}
454
455
TEST_CASE_FIXTURE(Fixture, "type_alias_should_not_interfere_with_type_function_call_or_assignment")
456
{
457
AstStatBlock* block = parse(R"(
458
type("a")
459
type = nil
460
)");
461
462
REQUIRE(block != nullptr);
463
REQUIRE(block->body.size > 0);
464
465
AstStatExpr* stat = block->body.data[0]->as<AstStatExpr>();
466
REQUIRE(stat != nullptr);
467
REQUIRE(stat->expr->as<AstExprCall>());
468
469
REQUIRE(block->body.data[1]->is<AstStatAssign>());
470
}
471
472
TEST_CASE_FIXTURE(Fixture, "type_alias_should_work_when_name_is_also_local")
473
{
474
AstStatBlock* block = parse(R"(
475
local A = nil
476
type A = string
477
)");
478
479
REQUIRE(block != nullptr);
480
REQUIRE(block->body.size == 2);
481
REQUIRE(block->body.data[0]->is<AstStatLocal>());
482
REQUIRE(block->body.data[1]->is<AstStatTypeAlias>());
483
}
484
485
TEST_CASE_FIXTURE(Fixture, "type_alias_span_is_correct")
486
{
487
AstStatBlock* block = parse(R"(
488
type Packed1<T...> = (T...) -> (T...)
489
type Packed2<T...> = (Packed1<T...>, T...) -> (Packed1<T...>, T...)
490
)");
491
492
REQUIRE(block != nullptr);
493
REQUIRE(2 == block->body.size);
494
AstStatTypeAlias* t1 = block->body.data[0]->as<AstStatTypeAlias>();
495
REQUIRE(t1);
496
REQUIRE(Location{Position{1, 8}, Position{1, 45}} == t1->location);
497
498
AstStatTypeAlias* t2 = block->body.data[1]->as<AstStatTypeAlias>();
499
REQUIRE(t2);
500
REQUIRE(Location{Position{2, 8}, Position{2, 75}} == t2->location);
501
}
502
503
TEST_CASE_FIXTURE(Fixture, "parse_error_messages")
504
{
505
matchParseError(
506
R"(
507
local a: (number, number) -> (string
508
)",
509
"Expected ')' (to close '(' at line 2), got <eof>"
510
);
511
512
matchParseError(
513
R"(
514
local a: (number, number) -> (
515
string
516
)",
517
"Expected ')' (to close '(' at line 2), got <eof>"
518
);
519
520
matchParseError(
521
R"(
522
local a: (number, number)
523
)",
524
"Expected '->' when parsing function type, got <eof>"
525
);
526
527
matchParseError(
528
R"(
529
local a: (number, number
530
)",
531
"Expected ')' (to close '(' at line 2), got <eof>"
532
);
533
534
matchParseError(
535
R"(
536
local a: {foo: string,
537
)",
538
"Expected identifier when parsing table field, got <eof>"
539
);
540
541
matchParseError(
542
R"(
543
local a: {foo: string
544
)",
545
"Expected '}' (to close '{' at line 2), got <eof>"
546
);
547
548
matchParseError(
549
R"(
550
local a: { [string]: number, [number]: string }
551
)",
552
"Cannot have more than one table indexer"
553
);
554
555
matchParseError(
556
R"(
557
type T = <a>foo
558
)",
559
"Expected '(' when parsing function parameters, got 'foo'"
560
);
561
}
562
563
TEST_CASE_FIXTURE(Fixture, "mixed_intersection_and_union_not_allowed")
564
{
565
matchParseError("type A = number & string | boolean", "Mixing union and intersection types is not allowed; consider wrapping in parentheses.");
566
}
567
568
TEST_CASE_FIXTURE(Fixture, "mixed_intersection_and_union_allowed_when_parenthesized")
569
{
570
try
571
{
572
parse("type A = (number & string) | boolean");
573
}
574
catch (const ParseErrors& e)
575
{
576
FAIL(e.what());
577
}
578
}
579
580
TEST_CASE_FIXTURE(Fixture, "cannot_write_multiple_values_in_type_groups")
581
{
582
matchParseError("type F = ((string, number))", "Expected '->' when parsing function type, got ')'");
583
matchParseError("type F = () -> ((string, number))", "Expected '->' when parsing function type, got ')'");
584
}
585
586
TEST_CASE_FIXTURE(Fixture, "type_alias_error_messages")
587
{
588
matchParseError("type 5 = number", "Expected identifier when parsing type name, got '5'");
589
matchParseError("type A", "Expected '=' when parsing type alias, got <eof>");
590
matchParseError("type A<", "Expected identifier, got <eof>");
591
matchParseError("type A<B", "Expected '>' (to close '<' at column 7), got <eof>");
592
}
593
594
TEST_CASE_FIXTURE(Fixture, "type_assertion_expression")
595
{
596
(void)parse(R"(
597
local a = something() :: any
598
)");
599
}
600
601
// The bug that motivated this test was an infinite loop.
602
// TODO: Set a timer and crash if the timeout is exceeded.
603
TEST_CASE_FIXTURE(Fixture, "last_line_does_not_have_to_be_blank")
604
{
605
(void)parse("-- print('hello')");
606
}
607
608
TEST_CASE_FIXTURE(Fixture, "type_assertion_expression_binds_tightly")
609
{
610
AstStatBlock* stat = parse(R"(
611
local a = one :: any + two :: any
612
)");
613
614
REQUIRE(stat != nullptr);
615
616
AstStatBlock* block = stat->as<AstStatBlock>();
617
REQUIRE(block != nullptr);
618
REQUIRE_EQ(1, block->body.size);
619
620
AstStatLocal* local = block->body.data[0]->as<AstStatLocal>();
621
REQUIRE(local != nullptr);
622
REQUIRE_EQ(1, local->values.size);
623
624
AstExprBinary* bin = local->values.data[0]->as<AstExprBinary>();
625
REQUIRE(bin != nullptr);
626
627
CHECK(nullptr != bin->left->as<AstExprTypeAssertion>());
628
CHECK(nullptr != bin->right->as<AstExprTypeAssertion>());
629
}
630
631
TEST_CASE_FIXTURE(Fixture, "mode_is_unset_if_no_hot_comment")
632
{
633
ParseResult result = parseEx("print('Hello World!')");
634
CHECK(result.hotcomments.empty());
635
}
636
637
TEST_CASE_FIXTURE(Fixture, "sense_hot_comment_on_first_line")
638
{
639
ParseOptions options;
640
options.captureComments = true;
641
642
ParseResult result = parseEx(" --!strict ", options);
643
std::optional<Mode> mode = parseMode(result.hotcomments);
644
REQUIRE(bool(mode));
645
CHECK_EQ(int(*mode), int(Mode::Strict));
646
}
647
648
TEST_CASE_FIXTURE(Fixture, "non_header_hot_comments")
649
{
650
ParseOptions options;
651
options.captureComments = true;
652
653
ParseResult result = parseEx("do end --!strict", options);
654
std::optional<Mode> mode = parseMode(result.hotcomments);
655
REQUIRE(!mode);
656
}
657
658
TEST_CASE_FIXTURE(Fixture, "stop_if_line_ends_with_hyphen")
659
{
660
CHECK_THROWS_AS(parse(" -"), std::exception);
661
}
662
663
TEST_CASE_FIXTURE(Fixture, "nonstrict_mode")
664
{
665
ParseOptions options;
666
options.captureComments = true;
667
668
ParseResult result = parseEx("--!nonstrict", options);
669
CHECK(result.errors.empty());
670
std::optional<Mode> mode = parseMode(result.hotcomments);
671
REQUIRE(bool(mode));
672
CHECK_EQ(int(*mode), int(Mode::Nonstrict));
673
}
674
675
TEST_CASE_FIXTURE(Fixture, "nocheck_mode")
676
{
677
ParseOptions options;
678
options.captureComments = true;
679
680
ParseResult result = parseEx("--!nocheck", options);
681
CHECK(result.errors.empty());
682
std::optional<Mode> mode = parseMode(result.hotcomments);
683
REQUIRE(bool(mode));
684
CHECK_EQ(int(*mode), int(Mode::NoCheck));
685
}
686
687
TEST_CASE_FIXTURE(Fixture, "vertical_space")
688
{
689
ParseResult result = parseEx("a()\vb()");
690
CHECK(result.errors.empty());
691
}
692
693
TEST_CASE_FIXTURE(Fixture, "parse_error_type_name")
694
{
695
matchParseError(
696
R"(
697
local a: Foo.=
698
)",
699
"Expected identifier when parsing field name, got '='"
700
);
701
}
702
703
TEST_CASE_FIXTURE(Fixture, "parse_numbers_decimal")
704
{
705
AstStat* stat = parse("return 1, .5, 1.5, 1e-5, 1.5e-5, 12_345.1_25");
706
REQUIRE(stat != nullptr);
707
708
AstStatReturn* str = stat->as<AstStatBlock>()->body.data[0]->as<AstStatReturn>();
709
CHECK(str->list.size == 6);
710
CHECK_EQ(str->list.data[0]->as<AstExprConstantNumber>()->value, 1.0);
711
CHECK_EQ(str->list.data[1]->as<AstExprConstantNumber>()->value, 0.5);
712
CHECK_EQ(str->list.data[2]->as<AstExprConstantNumber>()->value, 1.5);
713
CHECK_EQ(str->list.data[3]->as<AstExprConstantNumber>()->value, 1.0e-5);
714
CHECK_EQ(str->list.data[4]->as<AstExprConstantNumber>()->value, 1.5e-5);
715
CHECK_EQ(str->list.data[5]->as<AstExprConstantNumber>()->value, 12345.125);
716
717
if (FFlag::LuauIntegerType)
718
{
719
stat = parse("return 1i, 1_000_000i");
720
REQUIRE(stat != nullptr);
721
722
str = stat->as<AstStatBlock>()->body.data[0]->as<AstStatReturn>();
723
CHECK(str->list.size == 2);
724
CHECK(str->list.data[0]->is<AstExprConstantInteger>());
725
CHECK_EQ(str->list.data[0]->as<AstExprConstantInteger>()->value, 1);
726
CHECK(str->list.data[1]->is<AstExprConstantInteger>());
727
CHECK_EQ(str->list.data[1]->as<AstExprConstantInteger>()->value, 1000000);
728
}
729
}
730
731
TEST_CASE_FIXTURE(Fixture, "parse_numbers_hexadecimal")
732
{
733
AstStat* stat = parse("return 0xab, 0xAB05, 0xff_ff, 0xffffffffffffffff");
734
REQUIRE(stat != nullptr);
735
736
AstStatReturn* str = stat->as<AstStatBlock>()->body.data[0]->as<AstStatReturn>();
737
CHECK(str->list.size == 4);
738
CHECK_EQ(str->list.data[0]->as<AstExprConstantNumber>()->value, 0xab);
739
CHECK_EQ(str->list.data[1]->as<AstExprConstantNumber>()->value, 0xAB05);
740
CHECK_EQ(str->list.data[2]->as<AstExprConstantNumber>()->value, 0xFFFF);
741
CHECK_EQ(str->list.data[3]->as<AstExprConstantNumber>()->value, double(ULLONG_MAX));
742
743
if (FFlag::LuauIntegerType)
744
{
745
stat = parse("return 0xabi, 0XAB05i, 0xff_ffi, 0x7fffffffffffffffi, 0x8000000000000000i, 0xffffffffffffffffi");
746
REQUIRE(stat != nullptr);
747
748
str = stat->as<AstStatBlock>()->body.data[0]->as<AstStatReturn>();
749
CHECK(str->list.size == 6);
750
CHECK_EQ(str->list.data[0]->as<AstExprConstantInteger>()->value, 0xab);
751
CHECK_EQ(str->list.data[1]->as<AstExprConstantInteger>()->value, 0xAB05);
752
CHECK_EQ(str->list.data[2]->as<AstExprConstantInteger>()->value, 0xFFFF);
753
CHECK_EQ(str->list.data[3]->as<AstExprConstantInteger>()->value, LLONG_MAX);
754
CHECK_EQ(str->list.data[4]->as<AstExprConstantInteger>()->value, LLONG_MIN);
755
CHECK_EQ(str->list.data[5]->as<AstExprConstantInteger>()->value, -1);
756
}
757
}
758
759
TEST_CASE_FIXTURE(Fixture, "parse_numbers_binary")
760
{
761
AstStat* stat = parse("return 0b1, 0b0, 0b101010, 0b1111111111111111111111111111111111111111111111111111111111111111");
762
REQUIRE(stat != nullptr);
763
764
AstStatReturn* str = stat->as<AstStatBlock>()->body.data[0]->as<AstStatReturn>();
765
CHECK(str->list.size == 4);
766
CHECK_EQ(str->list.data[0]->as<AstExprConstantNumber>()->value, 1);
767
CHECK_EQ(str->list.data[1]->as<AstExprConstantNumber>()->value, 0);
768
CHECK_EQ(str->list.data[2]->as<AstExprConstantNumber>()->value, 42);
769
CHECK_EQ(str->list.data[3]->as<AstExprConstantNumber>()->value, double(ULLONG_MAX));
770
771
if (FFlag::LuauIntegerType)
772
{
773
AstStat* stat = parse(
774
"return 0b1i, 0b0i, 0b101010i, 0b111111111111111111111111111111111111111111111111111111111111111i, "
775
"0b1000000000000000000000000000000000000000000000000000000000000000i, 0b1111111111111111111111111111111111111111111111111111111111111111i"
776
);
777
REQUIRE(stat != nullptr);
778
779
str = stat->as<AstStatBlock>()->body.data[0]->as<AstStatReturn>();
780
CHECK(str->list.size == 6);
781
CHECK_EQ(str->list.data[0]->as<AstExprConstantInteger>()->value, 1);
782
CHECK_EQ(str->list.data[1]->as<AstExprConstantInteger>()->value, 0);
783
CHECK_EQ(str->list.data[2]->as<AstExprConstantInteger>()->value, 42);
784
CHECK_EQ(str->list.data[3]->as<AstExprConstantInteger>()->value, LLONG_MAX);
785
CHECK_EQ(str->list.data[4]->as<AstExprConstantInteger>()->value, LLONG_MIN);
786
CHECK_EQ(str->list.data[5]->as<AstExprConstantInteger>()->value, -1);
787
}
788
}
789
790
TEST_CASE_FIXTURE(Fixture, "parse_numbers_error")
791
{
792
matchParseError("return 0b123", "Malformed number");
793
matchParseError("return 123x", "Malformed number");
794
matchParseError("return 0xg", "Malformed number");
795
matchParseError("return 0x0x123", "Malformed number");
796
matchParseError("return 0xffffffffffffffffffffllllllg", "Malformed number");
797
matchParseError("return 0x0xffffffffffffffffffffffffffff", "Malformed number");
798
if (FFlag::LuauIntegerType)
799
{
800
matchParseError("return 0x0xABCi", "Malformed integer");
801
matchParseError("return 0xABCMi", "Malformed integer");
802
matchParseError("return 0b250i", "Malformed integer");
803
matchParseError("return 0bbbbi", "Malformed integer");
804
matchParseError("return 123ii", "Malformed integer");
805
matchParseError("return 0xABii", "Malformed integer");
806
807
matchParseError("return 99999999999999999999i", "Integer overflow");
808
matchParseError("return 0xFFFFFFFFFFFFFFFFFFi", "Integer overflow");
809
matchParseError("return 0b10000000000000000000000000000000000000000000000000000000000000000i", "Integer overflow");
810
matchParseError("return 123ii", "Malformed integer");
811
matchParseError("return 0xABii", "Malformed integer");
812
}
813
}
814
815
TEST_CASE_FIXTURE(Fixture, "break_return_not_last_error")
816
{
817
matchParseError("return 0 print(5)", "Expected <eof>, got 'print'");
818
matchParseError("while true do break print(5) end", "Expected 'end' (to close 'do' at column 12), got 'print'");
819
}
820
821
TEST_CASE_FIXTURE(Fixture, "error_on_unicode")
822
{
823
matchParseError(
824
R"(
825
local ☃ = 10
826
)",
827
"Expected identifier when parsing variable name, got Unicode character U+2603"
828
);
829
}
830
831
TEST_CASE_FIXTURE(Fixture, "allow_unicode_in_string")
832
{
833
ParseResult result = parseEx("local snowman = \"☃\"");
834
CHECK(result.errors.empty());
835
}
836
837
TEST_CASE_FIXTURE(Fixture, "error_on_confusable")
838
{
839
matchParseError(
840
R"(
841
local pi = 3․13
842
)",
843
"Expected identifier when parsing expression, got Unicode character U+2024 (did you mean '.'?)"
844
);
845
}
846
847
TEST_CASE_FIXTURE(Fixture, "error_on_non_utf8_sequence")
848
{
849
const char* expected = "Expected identifier when parsing expression, got invalid UTF-8 sequence";
850
851
matchParseError("local pi = \xFF!", expected);
852
matchParseError("local pi = \xE2!", expected);
853
}
854
855
TEST_CASE_FIXTURE(Fixture, "lex_broken_unicode")
856
{
857
const std::string testInput = std::string("\xFF\xFE☃․");
858
859
Luau::Allocator alloc;
860
AstNameTable table(alloc);
861
Lexer lexer(testInput.c_str(), testInput.size(), table);
862
Lexeme lexeme = lexer.current();
863
864
lexeme = lexer.next();
865
CHECK_EQ(lexeme.type, Lexeme::BrokenUnicode);
866
CHECK_EQ(lexeme.codepoint, 0);
867
CHECK_EQ(lexeme.location, Luau::Location(Luau::Position(0, 0), Luau::Position(0, 1)));
868
869
lexeme = lexer.next();
870
CHECK_EQ(lexeme.type, Lexeme::BrokenUnicode);
871
CHECK_EQ(lexeme.codepoint, 0);
872
CHECK_EQ(lexeme.location, Luau::Location(Luau::Position(0, 1), Luau::Position(0, 2)));
873
874
lexeme = lexer.next();
875
CHECK_EQ(lexeme.type, Lexeme::BrokenUnicode);
876
CHECK_EQ(lexeme.codepoint, 0x2603);
877
CHECK_EQ(lexeme.location, Luau::Location(Luau::Position(0, 2), Luau::Position(0, 5)));
878
879
lexeme = lexer.next();
880
CHECK_EQ(lexeme.type, Lexeme::BrokenUnicode);
881
CHECK_EQ(lexeme.codepoint, 0x2024);
882
CHECK_EQ(lexeme.location, Luau::Location(Luau::Position(0, 5), Luau::Position(0, 8)));
883
884
lexeme = lexer.next();
885
CHECK_EQ(lexeme.type, Lexeme::Eof);
886
}
887
888
TEST_CASE_FIXTURE(Fixture, "parse_continue")
889
{
890
AstStatBlock* stat = parse(R"(
891
while true do
892
continue()
893
continue = 5
894
continue, continue = continue
895
continue
896
end
897
)");
898
899
REQUIRE(stat != nullptr);
900
901
AstStatBlock* block = stat->as<AstStatBlock>();
902
REQUIRE(block != nullptr);
903
REQUIRE_EQ(1, block->body.size);
904
905
AstStatWhile* wb = block->body.data[0]->as<AstStatWhile>();
906
REQUIRE(wb != nullptr);
907
908
AstStatBlock* wblock = wb->body->as<AstStatBlock>();
909
REQUIRE(wblock != nullptr);
910
REQUIRE_EQ(4, wblock->body.size);
911
912
REQUIRE(wblock->body.data[0]->is<AstStatExpr>());
913
REQUIRE(wblock->body.data[1]->is<AstStatAssign>());
914
REQUIRE(wblock->body.data[2]->is<AstStatAssign>());
915
REQUIRE(wblock->body.data[3]->is<AstStatContinue>());
916
}
917
918
TEST_CASE_FIXTURE(Fixture, "continue_not_last_error")
919
{
920
matchParseError("while true do continue print(5) end", "Expected 'end' (to close 'do' at column 12), got 'print'");
921
}
922
923
TEST_CASE_FIXTURE(Fixture, "parse_export_type")
924
{
925
AstStatBlock* stat = parse(R"(
926
export()
927
export = 5
928
export, export = export
929
export type A = number
930
type A = number
931
)");
932
933
REQUIRE(stat != nullptr);
934
935
AstStatBlock* block = stat->as<AstStatBlock>();
936
REQUIRE(block != nullptr);
937
REQUIRE_EQ(5, block->body.size);
938
939
REQUIRE(block->body.data[0]->is<AstStatExpr>());
940
REQUIRE(block->body.data[1]->is<AstStatAssign>());
941
REQUIRE(block->body.data[2]->is<AstStatAssign>());
942
REQUIRE(block->body.data[3]->is<AstStatTypeAlias>());
943
REQUIRE(block->body.data[4]->is<AstStatTypeAlias>());
944
}
945
946
TEST_CASE_FIXTURE(Fixture, "export_is_an_identifier_only_when_followed_by_type")
947
{
948
try
949
{
950
parse(R"(
951
export function a() end
952
)");
953
FAIL("Expected ParseErrors to be thrown");
954
}
955
catch (const ParseErrors& e)
956
{
957
CHECK_EQ("Incomplete statement: expected assignment or a function call", e.getErrors().front().getMessage());
958
}
959
}
960
961
TEST_CASE_FIXTURE(Fixture, "incomplete_statement_error")
962
{
963
matchParseError("fiddlesticks", "Incomplete statement: expected assignment or a function call");
964
}
965
966
TEST_CASE_FIXTURE(Fixture, "parse_compound_assignment")
967
{
968
AstStatBlock* block = parse(R"(
969
a += 5
970
)");
971
972
REQUIRE(block != nullptr);
973
REQUIRE(block->body.size == 1);
974
REQUIRE(block->body.data[0]->is<AstStatCompoundAssign>());
975
REQUIRE(block->body.data[0]->as<AstStatCompoundAssign>()->op == AstExprBinary::Add);
976
}
977
978
TEST_CASE_FIXTURE(Fixture, "parse_compound_assignment_error_call")
979
{
980
try
981
{
982
parse(R"(
983
a() += 5
984
)");
985
FAIL("Expected ParseErrors to be thrown");
986
}
987
catch (const ParseErrors& e)
988
{
989
CHECK_EQ("Expected identifier when parsing expression, got '+='", e.getErrors().front().getMessage());
990
}
991
}
992
993
TEST_CASE_FIXTURE(Fixture, "parse_compound_assignment_error_not_lvalue")
994
{
995
try
996
{
997
parse(R"(
998
(a) += 5
999
)");
1000
FAIL("Expected ParseErrors to be thrown");
1001
}
1002
catch (const ParseErrors& e)
1003
{
1004
CHECK_EQ("Assigned expression must be a variable or a field", e.getErrors().front().getMessage());
1005
}
1006
}
1007
1008
TEST_CASE_FIXTURE(Fixture, "parse_compound_assignment_error_multiple")
1009
{
1010
try
1011
{
1012
parse(R"(
1013
a, b += 5
1014
)");
1015
FAIL("Expected ParseErrors to be thrown");
1016
}
1017
catch (const ParseErrors& e)
1018
{
1019
CHECK_EQ("Expected '=' when parsing assignment, got '+='", e.getErrors().front().getMessage());
1020
}
1021
}
1022
1023
TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_double_brace_begin")
1024
{
1025
try
1026
{
1027
parse(R"(
1028
_ = `{{oops}}`
1029
)");
1030
FAIL("Expected ParseErrors to be thrown");
1031
}
1032
catch (const ParseErrors& e)
1033
{
1034
CHECK_EQ("Double braces are not permitted within interpolated strings; did you mean '\\{'?", e.getErrors().front().getMessage());
1035
}
1036
}
1037
1038
TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_double_brace_mid")
1039
{
1040
try
1041
{
1042
parse(R"(
1043
_ = `{nice} {{oops}}`
1044
)");
1045
FAIL("Expected ParseErrors to be thrown");
1046
}
1047
catch (const ParseErrors& e)
1048
{
1049
CHECK_EQ("Double braces are not permitted within interpolated strings; did you mean '\\{'?", e.getErrors().front().getMessage());
1050
}
1051
}
1052
1053
TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_without_end_brace")
1054
{
1055
auto columnOfEndBraceError = [this](const char* code)
1056
{
1057
try
1058
{
1059
parse(code);
1060
FAIL("Expected ParseErrors to be thrown");
1061
return UINT_MAX;
1062
}
1063
catch (const ParseErrors& e)
1064
{
1065
CHECK_EQ(e.getErrors().size(), 1);
1066
1067
auto error = e.getErrors().front();
1068
CHECK_EQ("Malformed interpolated string; did you forget to add a '}'?", error.getMessage());
1069
return error.getLocation().begin.column;
1070
}
1071
};
1072
1073
// This makes sure that the error is coming from the closing brace itself
1074
CHECK_EQ(columnOfEndBraceError("_ = `{a`"), 7);
1075
CHECK_EQ(columnOfEndBraceError("_ = `{abcdefg`"), 13);
1076
CHECK_EQ(columnOfEndBraceError("_ = `{a`"), columnOfEndBraceError("_ = `{abcdefg`"));
1077
}
1078
1079
TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_without_end_brace_in_table")
1080
{
1081
try
1082
{
1083
parse(R"(
1084
_ = { `{a` }
1085
)");
1086
FAIL("Expected ParseErrors to be thrown");
1087
}
1088
catch (const ParseErrors& e)
1089
{
1090
CHECK_EQ(e.getErrors().size(), 2);
1091
1092
CHECK_EQ("Malformed interpolated string; did you forget to add a '}'?", e.getErrors().front().getMessage());
1093
CHECK_EQ("Expected '}' (to close '{' at line 2), got <eof>", e.getErrors().back().getMessage());
1094
}
1095
}
1096
1097
TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_mid_without_end_brace_in_table")
1098
{
1099
try
1100
{
1101
parse(R"(
1102
_ = { `x {"y"} {z` }
1103
)");
1104
FAIL("Expected ParseErrors to be thrown");
1105
}
1106
catch (const ParseErrors& e)
1107
{
1108
CHECK_EQ(e.getErrors().size(), 2);
1109
1110
CHECK_EQ("Malformed interpolated string; did you forget to add a '}'?", e.getErrors().front().getMessage());
1111
CHECK_EQ("Expected '}' (to close '{' at line 2), got <eof>", e.getErrors().back().getMessage());
1112
}
1113
}
1114
1115
TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_as_type_fail")
1116
{
1117
try
1118
{
1119
parse(R"(
1120
local a: `what` = `???`
1121
local b: `what {"the"}` = `???`
1122
local c: `what {"the"} heck` = `???`
1123
)");
1124
FAIL("Expected ParseErrors to be thrown");
1125
}
1126
catch (const ParseErrors& parseErrors)
1127
{
1128
CHECK_EQ(parseErrors.getErrors().size(), 3);
1129
1130
for (ParseError error : parseErrors.getErrors())
1131
CHECK_EQ(error.getMessage(), "Interpolated string literals cannot be used as types");
1132
}
1133
}
1134
1135
TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_call_without_parens")
1136
{
1137
try
1138
{
1139
parse(R"(
1140
_ = print `{42}`
1141
)");
1142
FAIL("Expected ParseErrors to be thrown");
1143
}
1144
catch (const ParseErrors& e)
1145
{
1146
CHECK_EQ("Expected identifier when parsing expression, got `{", e.getErrors().front().getMessage());
1147
}
1148
}
1149
1150
TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_without_expression")
1151
{
1152
try
1153
{
1154
parse(R"(
1155
print(`{}`)
1156
)");
1157
FAIL("Expected ParseErrors to be thrown");
1158
}
1159
catch (const ParseErrors& e)
1160
{
1161
CHECK_EQ("Malformed interpolated string, expected expression inside '{}'", e.getErrors().front().getMessage());
1162
}
1163
1164
try
1165
{
1166
parse(R"(
1167
print(`{}{1}`)
1168
)");
1169
FAIL("Expected ParseErrors to be thrown");
1170
}
1171
catch (const ParseErrors& e)
1172
{
1173
CHECK_EQ("Malformed interpolated string, expected expression inside '{}'", e.getErrors().front().getMessage());
1174
}
1175
}
1176
1177
TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_malformed_escape")
1178
{
1179
try
1180
{
1181
parse(R"(
1182
local a = `???\xQQ {1}`
1183
)");
1184
FAIL("Expected ParseErrors to be thrown");
1185
}
1186
catch (const ParseErrors& e)
1187
{
1188
CHECK_EQ("Interpolated string literal contains malformed escape sequence", e.getErrors().front().getMessage());
1189
}
1190
}
1191
1192
TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_weird_token")
1193
{
1194
try
1195
{
1196
parse(R"(
1197
local a = `??? {42 !!}`
1198
)");
1199
FAIL("Expected ParseErrors to be thrown");
1200
}
1201
catch (const ParseErrors& e)
1202
{
1203
CHECK_EQ("Malformed interpolated string, got '!'", e.getErrors().front().getMessage());
1204
}
1205
}
1206
1207
TEST_CASE_FIXTURE(Fixture, "parse_nesting_based_end_detection")
1208
{
1209
try
1210
{
1211
parse(R"(-- i am line 1
1212
function BottomUpTree(item, depth)
1213
if depth > 0 then
1214
local i = item + item
1215
depth = depth - 1
1216
local left, right = BottomUpTree(i-1, depth), BottomUpTree(i, depth)
1217
return { item, left, right }
1218
else
1219
return { item }
1220
end
1221
1222
function ItemCheck(tree)
1223
if tree[2] then
1224
return tree[1] + ItemCheck(tree[2]) - ItemCheck(tree[3])
1225
else
1226
return tree[1]
1227
end
1228
end
1229
)");
1230
FAIL("Expected ParseErrors to be thrown");
1231
}
1232
catch (const ParseErrors& e)
1233
{
1234
CHECK_EQ(
1235
"Expected 'end' (to close 'function' at line 2), got <eof>; did you forget to close 'else' at line 8?", e.getErrors().front().getMessage()
1236
);
1237
}
1238
}
1239
1240
TEST_CASE_FIXTURE(Fixture, "parse_nesting_based_end_detection_single_line")
1241
{
1242
try
1243
{
1244
parse(R"(-- i am line 1
1245
function ItemCheck(tree)
1246
if tree[2] then return tree[1] + ItemCheck(tree[2]) - ItemCheck(tree[3]) else return tree[1]
1247
end
1248
1249
function BottomUpTree(item, depth)
1250
if depth > 0 then
1251
local i = item + item
1252
depth = depth - 1
1253
local left, right = BottomUpTree(i-1, depth), BottomUpTree(i, depth)
1254
return { item, left, right }
1255
else
1256
return { item }
1257
end
1258
end
1259
)");
1260
FAIL("Expected ParseErrors to be thrown");
1261
}
1262
catch (const ParseErrors& e)
1263
{
1264
CHECK_EQ(
1265
"Expected 'end' (to close 'function' at line 2), got <eof>; did you forget to close 'else' at line 3?", e.getErrors().front().getMessage()
1266
);
1267
}
1268
}
1269
1270
TEST_CASE_FIXTURE(Fixture, "parse_nesting_based_end_detection_local_repeat")
1271
{
1272
try
1273
{
1274
parse(R"(-- i am line 1
1275
repeat
1276
print(1)
1277
repeat
1278
print(2)
1279
print(3)
1280
until false
1281
)");
1282
FAIL("Expected ParseErrors to be thrown");
1283
}
1284
catch (const ParseErrors& e)
1285
{
1286
CHECK_EQ(
1287
"Expected 'until' (to close 'repeat' at line 2), got <eof>; did you forget to close 'repeat' at line 4?",
1288
e.getErrors().front().getMessage()
1289
);
1290
}
1291
}
1292
1293
TEST_CASE_FIXTURE(Fixture, "parse_nesting_based_end_detection_local_function")
1294
{
1295
try
1296
{
1297
parse(R"(-- i am line 1
1298
local function BottomUpTree(item, depth)
1299
if depth > 0 then
1300
local i = item + item
1301
depth = depth - 1
1302
local left, right = BottomUpTree(i-1, depth), BottomUpTree(i, depth)
1303
return { item, left, right }
1304
else
1305
return { item }
1306
end
1307
1308
local function ItemCheck(tree)
1309
if tree[2] then
1310
return tree[1] + ItemCheck(tree[2]) - ItemCheck(tree[3])
1311
else
1312
return tree[1]
1313
end
1314
end
1315
)");
1316
FAIL("Expected ParseErrors to be thrown");
1317
}
1318
catch (const ParseErrors& e)
1319
{
1320
CHECK_EQ(
1321
"Expected 'end' (to close 'function' at line 2), got <eof>; did you forget to close 'else' at line 8?", e.getErrors().front().getMessage()
1322
);
1323
}
1324
}
1325
1326
TEST_CASE_FIXTURE(Fixture, "parse_nesting_based_end_detection_failsafe_earlier")
1327
{
1328
try
1329
{
1330
parse(R"(-- i am line 1
1331
local function ItemCheck(tree)
1332
if tree[2] then
1333
return tree[1] + ItemCheck(tree[2]) - ItemCheck(tree[3])
1334
else
1335
return tree[1]
1336
end
1337
end
1338
1339
local function BottomUpTree(item, depth)
1340
if depth > 0 then
1341
local i = item + item
1342
depth = depth - 1
1343
local left, right = BottomUpTree(i-1, depth), BottomUpTree(i, depth)
1344
return { item, left, right }
1345
else
1346
return { item }
1347
end
1348
)");
1349
FAIL("Expected ParseErrors to be thrown");
1350
}
1351
catch (const ParseErrors& e)
1352
{
1353
CHECK_EQ("Expected 'end' (to close 'function' at line 10), got <eof>", e.getErrors().front().getMessage());
1354
}
1355
}
1356
1357
TEST_CASE_FIXTURE(Fixture, "parse_nesting_based_end_detection_nested")
1358
{
1359
try
1360
{
1361
parse(R"(-- i am line 1
1362
function stringifyTable(t)
1363
local entries = {}
1364
for k, v in pairs(t) do
1365
-- if we find a nested table, convert that recursively
1366
if type(v) == "table" then
1367
v = stringifyTable(v)
1368
else
1369
v = tostring(v)
1370
k = tostring(k)
1371
1372
-- add another entry to our stringified table
1373
entries[#entries + 1] = ("s = s"):format(k, v)
1374
end
1375
1376
-- the memory location of the table
1377
local id = tostring(t):sub(8)
1378
1379
return ("{s}@s"):format(table.concat(entries, ", "), id)
1380
end
1381
)");
1382
FAIL("Expected ParseErrors to be thrown");
1383
}
1384
catch (const ParseErrors& e)
1385
{
1386
CHECK_EQ(
1387
"Expected 'end' (to close 'function' at line 2), got <eof>; did you forget to close 'else' at line 8?", e.getErrors().front().getMessage()
1388
);
1389
}
1390
}
1391
1392
TEST_CASE_FIXTURE(Fixture, "parse_error_table_literal")
1393
{
1394
try
1395
{
1396
parse(R"(
1397
function stringifyTable(t)
1398
local foo = (name = t)
1399
return foo
1400
end
1401
)");
1402
FAIL("Expected ParseErrors to be thrown");
1403
}
1404
catch (const ParseErrors& e)
1405
{
1406
CHECK_EQ(
1407
"Expected ')' (to close '(' at column 17), got '='; did you mean to use '{' when defining a table?", e.getErrors().front().getMessage()
1408
);
1409
}
1410
}
1411
1412
TEST_CASE_FIXTURE(Fixture, "parse_error_function_call")
1413
{
1414
try
1415
{
1416
parse(R"(
1417
function stringifyTable(t)
1418
local foo = t:Parse 2
1419
return foo
1420
end
1421
)");
1422
FAIL("Expected ParseErrors to be thrown");
1423
}
1424
catch (const ParseErrors& e)
1425
{
1426
CHECK_EQ(e.getErrors().front().getLocation().begin.line, 2);
1427
CHECK_EQ("Expected '(', '{' or <string> when parsing function call, got '2'", e.getErrors().front().getMessage());
1428
}
1429
}
1430
1431
TEST_CASE_FIXTURE(Fixture, "parse_error_function_call_newline")
1432
{
1433
try
1434
{
1435
parse(R"(
1436
function stringifyTable(t)
1437
local foo = t:Parse
1438
return foo
1439
end
1440
)");
1441
FAIL("Expected ParseErrors to be thrown");
1442
}
1443
catch (const ParseErrors& e)
1444
{
1445
CHECK_EQ(e.getErrors().front().getLocation().begin.line, 2);
1446
CHECK_EQ("Expected function call arguments after '('", e.getErrors().front().getMessage());
1447
}
1448
}
1449
1450
TEST_CASE_FIXTURE(Fixture, "parse_error_with_too_many_nested_type_group")
1451
{
1452
ScopedFastInt sfis{FInt::LuauRecursionLimit, 10};
1453
1454
matchParseError(
1455
"function f(): ((((((((((Fail)))))))))) end", "Exceeded allowed recursion depth; simplify your type annotation to make the code compile"
1456
);
1457
1458
matchParseError(
1459
"function f(): () -> () -> () -> () -> () -> () -> () -> () -> () -> () -> () end",
1460
"Exceeded allowed recursion depth; simplify your type annotation to make the code compile"
1461
);
1462
1463
matchParseError(
1464
"local t: {a: {b: {c: {d: {e: {f: {g: {h: {i: {j: {}}}}}}}}}}}",
1465
"Exceeded allowed recursion depth; simplify your type annotation to make the code compile"
1466
);
1467
1468
matchParseError("local f: ((((((((((Fail))))))))))", "Exceeded allowed recursion depth; simplify your type annotation to make the code compile");
1469
1470
matchParseError(
1471
"local t: a & (b & (c & (d & (e & (f & (g & (h & (i & (j & nil)))))))))",
1472
"Exceeded allowed recursion depth; simplify your type annotation to make the code compile"
1473
);
1474
}
1475
1476
TEST_CASE_FIXTURE(Fixture, "can_parse_complex_unions_successfully")
1477
{
1478
ScopedFastInt sfis[] = {{FInt::LuauRecursionLimit, 10}, {FInt::LuauTypeLengthLimit, 10}};
1479
1480
parse(R"(
1481
local f:
1482
() -> ()
1483
|
1484
() -> ()
1485
|
1486
{a: number}
1487
|
1488
{b: number}
1489
|
1490
((number))
1491
|
1492
((number))
1493
|
1494
(a & (b & nil))
1495
|
1496
(a & (b & nil))
1497
)");
1498
1499
parse(R"(
1500
local f: a? | b? | c? | d? | e? | f? | g? | h?
1501
)");
1502
1503
matchParseError(
1504
"local t: a & b & c & d & e & f & g & h & i & j & nil", "Exceeded allowed type length; simplify your type annotation to make the code compile"
1505
);
1506
}
1507
1508
TEST_CASE_FIXTURE(Fixture, "parse_error_with_too_many_nested_if_statements")
1509
{
1510
ScopedFastInt sfis{FInt::LuauRecursionLimit, 10};
1511
1512
matchParseErrorPrefix(
1513
"function f() if true then if true then if true then if true then if true then if true then if true then if true then if true "
1514
"then if true then if true then end end end end end end end end end end end end",
1515
"Exceeded allowed recursion depth;"
1516
);
1517
}
1518
1519
TEST_CASE_FIXTURE(Fixture, "parse_error_with_too_many_changed_elseif_statements")
1520
{
1521
ScopedFastInt sfis{FInt::LuauRecursionLimit, 10};
1522
1523
matchParseErrorPrefix(
1524
"function f() if false then elseif false then elseif false then elseif false then elseif false then elseif false then elseif "
1525
"false then elseif false then elseif false then elseif false then elseif false then end end",
1526
"Exceeded allowed recursion depth;"
1527
);
1528
}
1529
1530
TEST_CASE_FIXTURE(Fixture, "parse_error_with_too_many_nested_ifelse_expressions1")
1531
{
1532
ScopedFastInt sfis{FInt::LuauRecursionLimit, 10};
1533
1534
matchParseError(
1535
"function f() return if true then 1 elseif true then 2 elseif true then 3 elseif true then 4 elseif true then 5 elseif true then "
1536
"6 elseif true then 7 elseif true then 8 elseif true then 9 elseif true then 10 else 11 end",
1537
"Exceeded allowed recursion depth; simplify your expression to make the code compile"
1538
);
1539
}
1540
1541
TEST_CASE_FIXTURE(Fixture, "parse_error_with_too_many_nested_ifelse_expressions2")
1542
{
1543
ScopedFastInt sfis{FInt::LuauRecursionLimit, 10};
1544
1545
matchParseError(
1546
"function f() return if if if if if if if if if if true then false else true then false else true then false else true then false else true "
1547
"then false else true then false else true then false else true then false else true then false else true then 1 else 2 end",
1548
"Exceeded allowed recursion depth; simplify your expression to make the code compile"
1549
);
1550
}
1551
1552
TEST_CASE_FIXTURE(Fixture, "unparenthesized_function_return_type_list")
1553
{
1554
matchParseError(
1555
"function foo(): string, number end", "Expected a statement, got ','; did you forget to wrap the list of return types in parentheses?"
1556
);
1557
1558
matchParseError(
1559
"function foo(): (number) -> string, string", "Expected a statement, got ','; did you forget to wrap the list of return types in parentheses?"
1560
);
1561
1562
// Will throw if the parse fails
1563
parse(R"(
1564
type Vector3MT = {
1565
__add: (Vector3MT, Vector3MT) -> Vector3MT,
1566
__mul: (Vector3MT, Vector3MT|number) -> Vector3MT
1567
}
1568
)");
1569
}
1570
1571
TEST_CASE_FIXTURE(Fixture, "short_array_types")
1572
{
1573
AstStatBlock* stat = parse(R"(
1574
local n: {string}
1575
)");
1576
1577
REQUIRE(stat != nullptr);
1578
AstStatLocal* local = stat->body.data[0]->as<AstStatLocal>();
1579
AstTypeTable* annotation = local->vars.data[0]->annotation->as<AstTypeTable>();
1580
REQUIRE(annotation != nullptr);
1581
CHECK(annotation->props.size == 0);
1582
REQUIRE(annotation->indexer);
1583
REQUIRE(annotation->indexer->indexType->is<AstTypeReference>());
1584
CHECK(annotation->indexer->indexType->as<AstTypeReference>()->name == "number");
1585
REQUIRE(annotation->indexer->resultType->is<AstTypeReference>());
1586
CHECK(annotation->indexer->resultType->as<AstTypeReference>()->name == "string");
1587
}
1588
1589
TEST_CASE_FIXTURE(Fixture, "short_array_types_must_be_alone")
1590
{
1591
matchParseError("local n: {string, number}", "Expected '}' (to close '{' at column 10), got ','");
1592
matchParseError("local n: {[number]: string, number}", "Expected ':' when parsing table field, got '}'");
1593
matchParseError("local n: {x: string, number}", "Expected ':' when parsing table field, got '}'");
1594
matchParseError("local n: {x: string, nil}", "Expected identifier when parsing table field, got 'nil'");
1595
}
1596
1597
TEST_CASE_FIXTURE(Fixture, "short_array_types_do_not_break_field_names")
1598
{
1599
AstStatBlock* stat = parse(R"(
1600
local n: {string: number}
1601
)");
1602
1603
REQUIRE(stat != nullptr);
1604
AstStatLocal* local = stat->body.data[0]->as<AstStatLocal>();
1605
AstTypeTable* annotation = local->vars.data[0]->annotation->as<AstTypeTable>();
1606
REQUIRE(annotation != nullptr);
1607
REQUIRE(annotation->props.size == 1);
1608
CHECK(!annotation->indexer);
1609
REQUIRE(annotation->props.data[0].name == "string");
1610
REQUIRE(annotation->props.data[0].type->is<AstTypeReference>());
1611
REQUIRE(annotation->props.data[0].type->as<AstTypeReference>()->name == "number");
1612
}
1613
1614
TEST_CASE_FIXTURE(Fixture, "short_array_types_are_not_field_names_when_complex")
1615
{
1616
matchParseError("local n: {string | number: number}", "Expected '}' (to close '{' at column 10), got ':'");
1617
}
1618
1619
TEST_CASE_FIXTURE(Fixture, "nil_can_not_be_a_field_name")
1620
{
1621
matchParseError("local n: {nil: number}", "Expected '}' (to close '{' at column 10), got ':'");
1622
}
1623
1624
TEST_CASE_FIXTURE(Fixture, "string_literal_call")
1625
{
1626
AstStatBlock* stat = parse("do foo 'bar' end");
1627
REQUIRE(stat != nullptr);
1628
AstStatBlock* dob = stat->body.data[0]->as<AstStatBlock>();
1629
AstStatExpr* stc = dob->body.data[0]->as<AstStatExpr>();
1630
REQUIRE(stc != nullptr);
1631
AstExprCall* ec = stc->expr->as<AstExprCall>();
1632
CHECK(ec->args.size == 1);
1633
AstExprConstantString* arg = ec->args.data[0]->as<AstExprConstantString>();
1634
REQUIRE(arg != nullptr);
1635
CHECK(std::string(arg->value.data, arg->value.size) == "bar");
1636
}
1637
1638
TEST_CASE_FIXTURE(Fixture, "multiline_strings_newlines")
1639
{
1640
AstStatBlock* stat = parse("return [=[\nfoo\r\nbar\n\nbaz\n]=]");
1641
REQUIRE(stat != nullptr);
1642
1643
AstStatReturn* ret = stat->body.data[0]->as<AstStatReturn>();
1644
REQUIRE(ret != nullptr);
1645
1646
AstExprConstantString* str = ret->list.data[0]->as<AstExprConstantString>();
1647
REQUIRE(str != nullptr);
1648
CHECK(std::string(str->value.data, str->value.size) == "foo\nbar\n\nbaz\n");
1649
}
1650
1651
TEST_CASE_FIXTURE(Fixture, "string_literals_escape")
1652
{
1653
AstStatBlock* stat = parse(R"(
1654
return
1655
"foo\n\r",
1656
"foo\0324",
1657
"foo\x204",
1658
"foo\u{20}",
1659
"foo\u{0451}"
1660
)");
1661
1662
REQUIRE(stat != nullptr);
1663
1664
AstStatReturn* ret = stat->body.data[0]->as<AstStatReturn>();
1665
REQUIRE(ret != nullptr);
1666
CHECK(ret->list.size == 5);
1667
1668
AstExprConstantString* str;
1669
1670
str = ret->list.data[0]->as<AstExprConstantString>();
1671
REQUIRE(str != nullptr);
1672
CHECK_EQ(std::string(str->value.data, str->value.size), "foo\n\r");
1673
1674
str = ret->list.data[1]->as<AstExprConstantString>();
1675
REQUIRE(str != nullptr);
1676
CHECK_EQ(std::string(str->value.data, str->value.size), "foo 4");
1677
1678
str = ret->list.data[2]->as<AstExprConstantString>();
1679
REQUIRE(str != nullptr);
1680
CHECK_EQ(std::string(str->value.data, str->value.size), "foo 4");
1681
1682
str = ret->list.data[3]->as<AstExprConstantString>();
1683
REQUIRE(str != nullptr);
1684
CHECK_EQ(std::string(str->value.data, str->value.size), "foo ");
1685
1686
str = ret->list.data[4]->as<AstExprConstantString>();
1687
REQUIRE(str != nullptr);
1688
CHECK_EQ(std::string(str->value.data, str->value.size), "foo\xd1\x91");
1689
}
1690
1691
TEST_CASE_FIXTURE(Fixture, "string_literals_escape_newline")
1692
{
1693
AstStatBlock* stat = parse("return \"foo\\z\n bar\", \"foo\\\n bar\", \"foo\\\r\nbar\"");
1694
1695
REQUIRE(stat != nullptr);
1696
1697
AstStatReturn* ret = stat->body.data[0]->as<AstStatReturn>();
1698
REQUIRE(ret != nullptr);
1699
CHECK(ret->list.size == 3);
1700
1701
AstExprConstantString* str;
1702
1703
str = ret->list.data[0]->as<AstExprConstantString>();
1704
REQUIRE(str != nullptr);
1705
CHECK_EQ(std::string(str->value.data, str->value.size), "foobar");
1706
1707
str = ret->list.data[1]->as<AstExprConstantString>();
1708
REQUIRE(str != nullptr);
1709
CHECK_EQ(std::string(str->value.data, str->value.size), "foo\n bar");
1710
1711
str = ret->list.data[2]->as<AstExprConstantString>();
1712
REQUIRE(str != nullptr);
1713
CHECK_EQ(std::string(str->value.data, str->value.size), "foo\nbar");
1714
}
1715
1716
TEST_CASE_FIXTURE(Fixture, "string_literals_escapes")
1717
{
1718
AstStatBlock* stat = parse(R"(
1719
return
1720
"\xAB",
1721
"\u{2024}",
1722
"\121",
1723
"\1x",
1724
"\t",
1725
"\n"
1726
)");
1727
1728
REQUIRE(stat != nullptr);
1729
1730
AstStatReturn* ret = stat->body.data[0]->as<AstStatReturn>();
1731
REQUIRE(ret != nullptr);
1732
CHECK(ret->list.size == 6);
1733
1734
AstExprConstantString* str;
1735
1736
str = ret->list.data[0]->as<AstExprConstantString>();
1737
REQUIRE(str != nullptr);
1738
CHECK_EQ(std::string(str->value.data, str->value.size), "\xAB");
1739
1740
str = ret->list.data[1]->as<AstExprConstantString>();
1741
REQUIRE(str != nullptr);
1742
CHECK_EQ(std::string(str->value.data, str->value.size), "\xE2\x80\xA4");
1743
1744
str = ret->list.data[2]->as<AstExprConstantString>();
1745
REQUIRE(str != nullptr);
1746
CHECK_EQ(std::string(str->value.data, str->value.size), "\x79");
1747
1748
str = ret->list.data[3]->as<AstExprConstantString>();
1749
REQUIRE(str != nullptr);
1750
CHECK_EQ(std::string(str->value.data, str->value.size), "\x01x");
1751
1752
str = ret->list.data[4]->as<AstExprConstantString>();
1753
REQUIRE(str != nullptr);
1754
CHECK_EQ(std::string(str->value.data, str->value.size), "\t");
1755
1756
str = ret->list.data[5]->as<AstExprConstantString>();
1757
REQUIRE(str != nullptr);
1758
CHECK_EQ(std::string(str->value.data, str->value.size), "\n");
1759
}
1760
1761
TEST_CASE_FIXTURE(Fixture, "parse_error_broken_comment")
1762
{
1763
const char* expected = "Expected identifier when parsing expression, got unfinished comment";
1764
1765
matchParseError("--[[unfinished work", expected);
1766
matchParseError("--!strict\n--[[unfinished work", expected);
1767
matchParseError("local x = 1 --[[unfinished work", expected);
1768
}
1769
1770
TEST_CASE_FIXTURE(Fixture, "string_literals_escapes_broken")
1771
{
1772
const char* expected = "String literal contains malformed escape sequence";
1773
1774
matchParseError("return \"\\u{\"", expected);
1775
matchParseError("return \"\\u{FO}\"", expected);
1776
matchParseError("return \"\\u{123456789}\"", expected);
1777
matchParseError("return \"\\359\"", expected);
1778
matchParseError("return \"\\xFO\"", expected);
1779
matchParseError("return \"\\xF\"", expected);
1780
matchParseError("return \"\\x\"", expected);
1781
}
1782
1783
TEST_CASE_FIXTURE(Fixture, "string_literals_broken")
1784
{
1785
matchParseError("return \"", "Malformed string; did you forget to finish it?");
1786
matchParseError("return \"\\", "Malformed string; did you forget to finish it?");
1787
matchParseError("return \"\r\r", "Malformed string; did you forget to finish it?");
1788
}
1789
1790
TEST_CASE_FIXTURE(Fixture, "number_literals")
1791
{
1792
AstStatBlock* stat = parse(R"(
1793
return
1794
1,
1795
1.5,
1796
.5,
1797
12_34_56,
1798
0x1234,
1799
0b010101
1800
)");
1801
1802
REQUIRE(stat != nullptr);
1803
1804
AstStatReturn* ret = stat->body.data[0]->as<AstStatReturn>();
1805
REQUIRE(ret != nullptr);
1806
CHECK(ret->list.size == 6);
1807
1808
AstExprConstantNumber* num;
1809
1810
num = ret->list.data[0]->as<AstExprConstantNumber>();
1811
REQUIRE(num != nullptr);
1812
CHECK_EQ(num->value, 1.0);
1813
1814
num = ret->list.data[1]->as<AstExprConstantNumber>();
1815
REQUIRE(num != nullptr);
1816
CHECK_EQ(num->value, 1.5);
1817
1818
num = ret->list.data[2]->as<AstExprConstantNumber>();
1819
REQUIRE(num != nullptr);
1820
CHECK_EQ(num->value, 0.5);
1821
1822
num = ret->list.data[3]->as<AstExprConstantNumber>();
1823
REQUIRE(num != nullptr);
1824
CHECK_EQ(num->value, 123456);
1825
1826
num = ret->list.data[4]->as<AstExprConstantNumber>();
1827
REQUIRE(num != nullptr);
1828
CHECK_EQ(num->value, 0x1234);
1829
1830
num = ret->list.data[5]->as<AstExprConstantNumber>();
1831
REQUIRE(num != nullptr);
1832
CHECK_EQ(num->value, 0x15);
1833
}
1834
1835
TEST_CASE_FIXTURE(Fixture, "end_extent_of_functions_unions_and_intersections")
1836
{
1837
AstStatBlock* block = parse(R"(
1838
type F = (string) -> string
1839
type G = string | number | boolean
1840
type H = string & number & boolean
1841
print('hello')
1842
)");
1843
1844
REQUIRE_EQ(4, block->body.size);
1845
CHECK_EQ((Position{1, 35}), block->body.data[0]->location.end);
1846
CHECK_EQ((Position{2, 42}), block->body.data[1]->location.end);
1847
CHECK_EQ((Position{3, 42}), block->body.data[2]->location.end);
1848
}
1849
1850
TEST_CASE_FIXTURE(Fixture, "end_extent_doesnt_consume_comments")
1851
{
1852
AstStatBlock* block = parse(R"(
1853
type F = number
1854
--comment
1855
print('hello')
1856
)");
1857
1858
REQUIRE_EQ(2, block->body.size);
1859
CHECK_EQ((Position{1, 23}), block->body.data[0]->location.end);
1860
}
1861
1862
TEST_CASE_FIXTURE(Fixture, "end_extent_doesnt_consume_comments_even_with_capture")
1863
{
1864
// Same should hold when comments are captured
1865
ParseOptions opts;
1866
opts.captureComments = true;
1867
1868
AstStatBlock* block = parse(
1869
R"(
1870
type F = number
1871
--comment
1872
print('hello')
1873
)",
1874
opts
1875
);
1876
1877
REQUIRE_EQ(2, block->body.size);
1878
CHECK_EQ((Position{1, 23}), block->body.data[0]->location.end);
1879
}
1880
1881
TEST_CASE_FIXTURE(Fixture, "parse_error_loop_control")
1882
{
1883
matchParseError("break", "break statement must be inside a loop");
1884
matchParseError("repeat local function a() break end until false", "break statement must be inside a loop");
1885
matchParseError("continue", "continue statement must be inside a loop");
1886
matchParseError("repeat local function a() continue end until false", "continue statement must be inside a loop");
1887
}
1888
1889
TEST_CASE_FIXTURE(Fixture, "parse_error_confusing_function_call")
1890
{
1891
auto result1 = matchParseError(
1892
R"(
1893
function add(x, y) return x + y end
1894
add
1895
(4, 7)
1896
)",
1897
"Ambiguous syntax: this looks like an argument list for a function call, but could also be a start of new statement; use ';' to separate "
1898
"statements"
1899
);
1900
1901
CHECK(result1.errors.size() == 1);
1902
1903
auto result2 = matchParseError(
1904
R"(
1905
function add(x, y) return x + y end
1906
local f = add
1907
(f :: any)['x'] = 2
1908
)",
1909
"Ambiguous syntax: this looks like an argument list for a function call, but could also be a start of new statement; use ';' to separate "
1910
"statements"
1911
);
1912
1913
CHECK(result2.errors.size() == 1);
1914
1915
auto result3 = matchParseError(
1916
R"(
1917
local x = {}
1918
function x:add(a, b) return a + b end
1919
x:add
1920
(1, 2)
1921
)",
1922
"Ambiguous syntax: this looks like an argument list for a function call, but could also be a start of new statement; use ';' to separate "
1923
"statements"
1924
);
1925
1926
CHECK(result3.errors.size() == 1);
1927
1928
auto result4 = matchParseError(
1929
R"(
1930
local t = {}
1931
function f() return t end
1932
t.x, (f)
1933
().y = 5, 6
1934
)",
1935
"Ambiguous syntax: this looks like an argument list for a function call, but could also be a start of new statement; use ';' to separate "
1936
"statements"
1937
);
1938
1939
CHECK(result4.errors.size() == 1);
1940
}
1941
1942
TEST_CASE_FIXTURE(Fixture, "parse_error_varargs")
1943
{
1944
matchParseError("function add(x, y) return ... end", "Cannot use '...' outside of a vararg function");
1945
}
1946
1947
TEST_CASE_FIXTURE(Fixture, "parse_error_assignment_lvalue")
1948
{
1949
matchParseError(
1950
R"(
1951
local a, b
1952
(2), b = b, a
1953
)",
1954
"Assigned expression must be a variable or a field"
1955
);
1956
1957
matchParseError(
1958
R"(
1959
local a, b
1960
a, (3) = b, a
1961
)",
1962
"Assigned expression must be a variable or a field"
1963
);
1964
}
1965
1966
TEST_CASE_FIXTURE(Fixture, "parse_error_type_annotation")
1967
{
1968
matchParseError("local a : 2 = 2", "Expected type, got '2'");
1969
}
1970
1971
TEST_CASE_FIXTURE(Fixture, "parse_error_missing_type_annotation")
1972
{
1973
{
1974
ParseResult result = tryParse("local x:");
1975
CHECK(result.errors.size() == 1);
1976
Position begin = result.errors[0].getLocation().begin;
1977
Position end = result.errors[0].getLocation().end;
1978
CHECK(begin.line == end.line);
1979
int width = end.column - begin.column;
1980
CHECK(width == 0);
1981
CHECK(result.errors[0].getMessage() == "Expected type, got <eof>");
1982
}
1983
1984
{
1985
ParseResult result = tryParse(R"(
1986
local x:=42
1987
)");
1988
CHECK(result.errors.size() == 1);
1989
Position begin = result.errors[0].getLocation().begin;
1990
Position end = result.errors[0].getLocation().end;
1991
CHECK(begin.line == end.line);
1992
int width = end.column - begin.column;
1993
CHECK(width == 1); // Length of `=`
1994
CHECK(result.errors[0].getMessage() == "Expected type, got '='");
1995
}
1996
1997
{
1998
ParseResult result = tryParse(R"(
1999
function func():end
2000
)");
2001
CHECK(result.errors.size() == 1);
2002
Position begin = result.errors[0].getLocation().begin;
2003
Position end = result.errors[0].getLocation().end;
2004
CHECK(begin.line == end.line);
2005
int width = end.column - begin.column;
2006
CHECK(width == 3); // Length of `end`
2007
CHECK(result.errors[0].getMessage() == "Expected type, got 'end'");
2008
}
2009
}
2010
2011
TEST_CASE_FIXTURE(Fixture, "parse_declarations")
2012
{
2013
AstStatBlock* stat = parseEx(R"(
2014
declare foo: number
2015
declare function bar(x: number): string
2016
declare function var(...: any)
2017
)")
2018
.root;
2019
2020
REQUIRE(stat);
2021
REQUIRE_EQ(stat->body.size, 3);
2022
2023
AstStatDeclareGlobal* global = stat->body.data[0]->as<AstStatDeclareGlobal>();
2024
REQUIRE(global);
2025
CHECK(global->name == "foo");
2026
CHECK(global->nameLocation == Location({1, 16}, {1, 19}));
2027
CHECK(global->type);
2028
2029
AstStatDeclareFunction* func = stat->body.data[1]->as<AstStatDeclareFunction>();
2030
REQUIRE(func);
2031
CHECK(func->name == "bar");
2032
CHECK(func->nameLocation == Location({2, 25}, {2, 28}));
2033
REQUIRE_EQ(func->params.types.size, 1);
2034
2035
auto retTypePack = func->retTypes->as<AstTypePackExplicit>();
2036
REQUIRE(retTypePack);
2037
REQUIRE_EQ(retTypePack->typeList.types.size, 1);
2038
2039
AstStatDeclareFunction* varFunc = stat->body.data[2]->as<AstStatDeclareFunction>();
2040
REQUIRE(varFunc);
2041
CHECK(varFunc->name == "var");
2042
CHECK(varFunc->nameLocation == Location({3, 25}, {3, 28}));
2043
CHECK(varFunc->params.tailType);
2044
CHECK(varFunc->vararg);
2045
CHECK(varFunc->varargLocation == Location({3, 29}, {3, 32}));
2046
2047
matchParseError("declare function foo(x)", "All declaration parameters must be annotated");
2048
matchParseError("declare foo", "Expected ':' when parsing global variable declaration, got <eof>");
2049
}
2050
2051
TEST_CASE_FIXTURE(Fixture, "parse_class_declarations")
2052
{
2053
AstStatBlock* stat = parseEx(R"(
2054
declare class Foo
2055
prop: number
2056
function method(self, foo: number): string
2057
end
2058
2059
declare class Bar extends Foo
2060
prop2: string
2061
end
2062
)")
2063
.root;
2064
2065
REQUIRE_EQ(stat->body.size, 2);
2066
2067
AstStatDeclareExternType* declaredExternType = stat->body.data[0]->as<AstStatDeclareExternType>();
2068
REQUIRE(declaredExternType);
2069
CHECK(declaredExternType->name == "Foo");
2070
CHECK(!declaredExternType->superName);
2071
2072
REQUIRE_EQ(declaredExternType->props.size, 2);
2073
2074
AstDeclaredExternTypeProperty& prop = declaredExternType->props.data[0];
2075
CHECK(prop.name == "prop");
2076
CHECK(prop.nameLocation == Location({2, 12}, {2, 16}));
2077
CHECK(prop.ty->is<AstTypeReference>());
2078
CHECK(prop.location == Location({2, 12}, {2, 24}));
2079
2080
AstDeclaredExternTypeProperty& method = declaredExternType->props.data[1];
2081
CHECK(method.name == "method");
2082
CHECK(method.nameLocation == Location({3, 21}, {3, 27}));
2083
CHECK(method.ty->is<AstTypeFunction>());
2084
CHECK(method.location == Location({3, 12}, {3, 54}));
2085
CHECK(method.isMethod);
2086
2087
AstStatDeclareExternType* subclass = stat->body.data[1]->as<AstStatDeclareExternType>();
2088
REQUIRE(subclass);
2089
REQUIRE(subclass->superName);
2090
CHECK(subclass->name == "Bar");
2091
CHECK(*subclass->superName == "Foo");
2092
2093
REQUIRE_EQ(subclass->props.size, 1);
2094
AstDeclaredExternTypeProperty& prop2 = subclass->props.data[0];
2095
CHECK(prop2.name == "prop2");
2096
CHECK(prop2.nameLocation == Location({7, 12}, {7, 17}));
2097
CHECK(prop2.ty->is<AstTypeReference>());
2098
CHECK(prop2.location == Location({7, 12}, {7, 25}));
2099
}
2100
2101
TEST_CASE_FIXTURE(Fixture, "parse_extern_type_declarations")
2102
{
2103
AstStatBlock* stat = parseEx(R"(
2104
declare extern type Foo with
2105
prop: number
2106
function method(self, foo: number): string
2107
end
2108
2109
declare extern type Bar extends Foo with
2110
prop2: string
2111
end
2112
)")
2113
.root;
2114
2115
REQUIRE_EQ(stat->body.size, 2);
2116
2117
AstStatDeclareExternType* declaredExternType = stat->body.data[0]->as<AstStatDeclareExternType>();
2118
REQUIRE(declaredExternType);
2119
CHECK(declaredExternType->name == "Foo");
2120
CHECK(!declaredExternType->superName);
2121
2122
REQUIRE_EQ(declaredExternType->props.size, 2);
2123
2124
AstDeclaredExternTypeProperty& prop = declaredExternType->props.data[0];
2125
CHECK(prop.name == "prop");
2126
CHECK(prop.nameLocation == Location({2, 12}, {2, 16}));
2127
CHECK(prop.ty->is<AstTypeReference>());
2128
CHECK(prop.location == Location({2, 12}, {2, 24}));
2129
2130
AstDeclaredExternTypeProperty& method = declaredExternType->props.data[1];
2131
CHECK(method.name == "method");
2132
CHECK(method.nameLocation == Location({3, 21}, {3, 27}));
2133
CHECK(method.ty->is<AstTypeFunction>());
2134
CHECK(method.location == Location({3, 12}, {3, 54}));
2135
CHECK(method.isMethod);
2136
2137
AstStatDeclareExternType* subclass = stat->body.data[1]->as<AstStatDeclareExternType>();
2138
REQUIRE(subclass);
2139
REQUIRE(subclass->superName);
2140
CHECK(subclass->name == "Bar");
2141
CHECK(*subclass->superName == "Foo");
2142
2143
REQUIRE_EQ(subclass->props.size, 1);
2144
AstDeclaredExternTypeProperty& prop2 = subclass->props.data[0];
2145
CHECK(prop2.name == "prop2");
2146
CHECK(prop2.nameLocation == Location({7, 12}, {7, 17}));
2147
CHECK(prop2.ty->is<AstTypeReference>());
2148
CHECK(prop2.location == Location({7, 12}, {7, 25}));
2149
}
2150
2151
TEST_CASE_FIXTURE(Fixture, "parse_extern_type_declarations_missing_with")
2152
{
2153
ParseResult result = tryParse(R"(
2154
declare extern type Foo
2155
prop: number
2156
function method(self, foo: number): string
2157
end
2158
2159
declare extern type Bar extends Foo
2160
prop2: string
2161
end
2162
)");
2163
2164
REQUIRE_EQ(result.errors.size(), 2);
2165
CHECK("Expected `with` keyword before listing properties of the external type, but got prop instead" == result.errors[0].getMessage());
2166
CHECK("Expected `with` keyword before listing properties of the external type, but got prop2 instead" == result.errors[1].getMessage());
2167
2168
AstStatBlock* stat = result.root;
2169
2170
REQUIRE_EQ(stat->body.size, 2);
2171
2172
AstStatDeclareExternType* declaredExternType = stat->body.data[0]->as<AstStatDeclareExternType>();
2173
REQUIRE(declaredExternType);
2174
CHECK(declaredExternType->name == "Foo");
2175
CHECK(!declaredExternType->superName);
2176
2177
REQUIRE_EQ(declaredExternType->props.size, 2);
2178
2179
AstDeclaredExternTypeProperty& prop = declaredExternType->props.data[0];
2180
CHECK(prop.name == "prop");
2181
CHECK(prop.nameLocation == Location({2, 12}, {2, 16}));
2182
CHECK(prop.ty->is<AstTypeReference>());
2183
CHECK(prop.location == Location({2, 12}, {2, 24}));
2184
2185
AstDeclaredExternTypeProperty& method = declaredExternType->props.data[1];
2186
CHECK(method.name == "method");
2187
CHECK(method.nameLocation == Location({3, 21}, {3, 27}));
2188
CHECK(method.ty->is<AstTypeFunction>());
2189
CHECK(method.location == Location({3, 12}, {3, 54}));
2190
CHECK(method.isMethod);
2191
2192
AstStatDeclareExternType* subclass = stat->body.data[1]->as<AstStatDeclareExternType>();
2193
REQUIRE(subclass);
2194
REQUIRE(subclass->superName);
2195
CHECK(subclass->name == "Bar");
2196
CHECK(*subclass->superName == "Foo");
2197
2198
REQUIRE_EQ(subclass->props.size, 1);
2199
AstDeclaredExternTypeProperty& prop2 = subclass->props.data[0];
2200
CHECK(prop2.name == "prop2");
2201
CHECK(prop2.nameLocation == Location({7, 12}, {7, 17}));
2202
CHECK(prop2.ty->is<AstTypeReference>());
2203
CHECK(prop2.location == Location({7, 12}, {7, 25}));
2204
}
2205
2206
TEST_CASE_FIXTURE(Fixture, "parse_extern_type_declarations")
2207
{
2208
AstStatBlock* stat = parseEx(R"(
2209
declare extern type Foo with
2210
prop: number
2211
function method(self, foo: number): string
2212
end
2213
2214
declare extern type Bar extends Foo with
2215
prop2: string
2216
end
2217
)")
2218
.root;
2219
2220
REQUIRE_EQ(stat->body.size, 2);
2221
2222
AstStatDeclareExternType* declaredExternType = stat->body.data[0]->as<AstStatDeclareExternType>();
2223
REQUIRE(declaredExternType);
2224
CHECK(declaredExternType->name == "Foo");
2225
CHECK(!declaredExternType->superName);
2226
2227
REQUIRE_EQ(declaredExternType->props.size, 2);
2228
2229
AstDeclaredExternTypeProperty& prop = declaredExternType->props.data[0];
2230
CHECK(prop.name == "prop");
2231
CHECK(prop.nameLocation == Location({2, 12}, {2, 16}));
2232
CHECK(prop.ty->is<AstTypeReference>());
2233
CHECK(prop.location == Location({2, 12}, {2, 24}));
2234
2235
AstDeclaredExternTypeProperty& method = declaredExternType->props.data[1];
2236
CHECK(method.name == "method");
2237
CHECK(method.nameLocation == Location({3, 21}, {3, 27}));
2238
CHECK(method.ty->is<AstTypeFunction>());
2239
CHECK(method.location == Location({3, 12}, {3, 54}));
2240
CHECK(method.isMethod);
2241
2242
AstStatDeclareExternType* subclass = stat->body.data[1]->as<AstStatDeclareExternType>();
2243
REQUIRE(subclass);
2244
REQUIRE(subclass->superName);
2245
CHECK(subclass->name == "Bar");
2246
CHECK(*subclass->superName == "Foo");
2247
2248
REQUIRE_EQ(subclass->props.size, 1);
2249
AstDeclaredExternTypeProperty& prop2 = subclass->props.data[0];
2250
CHECK(prop2.name == "prop2");
2251
CHECK(prop2.nameLocation == Location({7, 12}, {7, 17}));
2252
CHECK(prop2.ty->is<AstTypeReference>());
2253
CHECK(prop2.location == Location({7, 12}, {7, 25}));
2254
}
2255
2256
TEST_CASE_FIXTURE(Fixture, "parse_extern_type_declarations_missing_with")
2257
{
2258
ParseResult result = tryParse(R"(
2259
declare extern type Foo
2260
prop: number
2261
function method(self, foo: number): string
2262
end
2263
2264
declare extern type Bar extends Foo
2265
prop2: string
2266
end
2267
)");
2268
2269
REQUIRE_EQ(result.errors.size(), 2);
2270
CHECK("Expected `with` keyword before listing properties of the external type, but got prop instead" == result.errors[0].getMessage());
2271
CHECK("Expected `with` keyword before listing properties of the external type, but got prop2 instead" == result.errors[1].getMessage());
2272
2273
AstStatBlock* stat = result.root;
2274
2275
REQUIRE_EQ(stat->body.size, 2);
2276
2277
AstStatDeclareExternType* declaredExternType = stat->body.data[0]->as<AstStatDeclareExternType>();
2278
REQUIRE(declaredExternType);
2279
CHECK(declaredExternType->name == "Foo");
2280
CHECK(!declaredExternType->superName);
2281
2282
REQUIRE_EQ(declaredExternType->props.size, 2);
2283
2284
AstDeclaredExternTypeProperty& prop = declaredExternType->props.data[0];
2285
CHECK(prop.name == "prop");
2286
CHECK(prop.nameLocation == Location({2, 12}, {2, 16}));
2287
CHECK(prop.ty->is<AstTypeReference>());
2288
CHECK(prop.location == Location({2, 12}, {2, 24}));
2289
2290
AstDeclaredExternTypeProperty& method = declaredExternType->props.data[1];
2291
CHECK(method.name == "method");
2292
CHECK(method.nameLocation == Location({3, 21}, {3, 27}));
2293
CHECK(method.ty->is<AstTypeFunction>());
2294
CHECK(method.location == Location({3, 12}, {3, 54}));
2295
CHECK(method.isMethod);
2296
2297
AstStatDeclareExternType* subclass = stat->body.data[1]->as<AstStatDeclareExternType>();
2298
REQUIRE(subclass);
2299
REQUIRE(subclass->superName);
2300
CHECK(subclass->name == "Bar");
2301
CHECK(*subclass->superName == "Foo");
2302
2303
REQUIRE_EQ(subclass->props.size, 1);
2304
AstDeclaredExternTypeProperty& prop2 = subclass->props.data[0];
2305
CHECK(prop2.name == "prop2");
2306
CHECK(prop2.nameLocation == Location({7, 12}, {7, 17}));
2307
CHECK(prop2.ty->is<AstTypeReference>());
2308
CHECK(prop2.location == Location({7, 12}, {7, 25}));
2309
}
2310
2311
TEST_CASE_FIXTURE(Fixture, "class_method_properties")
2312
{
2313
const ParseResult p1 = matchParseError(
2314
R"(
2315
declare class Foo
2316
-- method's first parameter must be 'self'
2317
function method(foo: number)
2318
function method2(self)
2319
end
2320
)",
2321
"'self' must be present as the unannotated first parameter"
2322
);
2323
2324
REQUIRE_EQ(1, p1.root->body.size);
2325
2326
AstStatDeclareExternType* klass = p1.root->body.data[0]->as<AstStatDeclareExternType>();
2327
REQUIRE(klass != nullptr);
2328
2329
CHECK_EQ(2, klass->props.size);
2330
2331
const ParseResult p2 = matchParseError(
2332
R"(
2333
declare class Foo
2334
function method(self, foo)
2335
function method2()
2336
end
2337
)",
2338
"All declaration parameters aside from 'self' must be annotated"
2339
);
2340
2341
REQUIRE_EQ(1, p2.root->body.size);
2342
2343
AstStatDeclareExternType* klass2 = p2.root->body.data[0]->as<AstStatDeclareExternType>();
2344
REQUIRE(klass2 != nullptr);
2345
2346
CHECK_EQ(2, klass2->props.size);
2347
}
2348
2349
TEST_CASE_FIXTURE(Fixture, "class_indexer")
2350
{
2351
AstStatBlock* stat = parseEx(R"(
2352
declare class Foo
2353
prop: boolean
2354
[string]: number
2355
end
2356
)")
2357
.root;
2358
2359
REQUIRE_EQ(stat->body.size, 1);
2360
2361
AstStatDeclareExternType* declaredExternType = stat->body.data[0]->as<AstStatDeclareExternType>();
2362
REQUIRE(declaredExternType);
2363
REQUIRE(declaredExternType->indexer);
2364
REQUIRE(declaredExternType->indexer->indexType->is<AstTypeReference>());
2365
CHECK(declaredExternType->indexer->indexType->as<AstTypeReference>()->name == "string");
2366
REQUIRE(declaredExternType->indexer->resultType->is<AstTypeReference>());
2367
CHECK(declaredExternType->indexer->resultType->as<AstTypeReference>()->name == "number");
2368
2369
const ParseResult p1 = matchParseError(
2370
R"(
2371
declare class Foo
2372
[string]: number
2373
-- can only have one indexer
2374
[number]: number
2375
end
2376
)",
2377
"Cannot have more than one indexer on an extern type"
2378
);
2379
2380
REQUIRE_EQ(1, p1.root->body.size);
2381
2382
AstStatDeclareExternType* klass = p1.root->body.data[0]->as<AstStatDeclareExternType>();
2383
REQUIRE(klass != nullptr);
2384
CHECK(klass->indexer);
2385
}
2386
2387
TEST_CASE_FIXTURE(Fixture, "parse_variadics")
2388
{
2389
AstStatBlock* stat = parseEx(R"(
2390
function foo(bar, ...: number): ...string
2391
end
2392
2393
type Foo = (string, number, ...number) -> ...boolean
2394
type Bar = () -> (number, ...boolean)
2395
)")
2396
.root;
2397
2398
REQUIRE(stat);
2399
REQUIRE_EQ(stat->body.size, 3);
2400
2401
AstStatFunction* fn = stat->body.data[0]->as<AstStatFunction>();
2402
REQUIRE(fn);
2403
CHECK(fn->func->vararg);
2404
CHECK(fn->func->varargAnnotation);
2405
2406
AstStatTypeAlias* foo = stat->body.data[1]->as<AstStatTypeAlias>();
2407
REQUIRE(foo);
2408
AstTypeFunction* fnFoo = foo->type->as<AstTypeFunction>();
2409
REQUIRE(fnFoo);
2410
CHECK_EQ(fnFoo->argTypes.types.size, 2);
2411
CHECK(fnFoo->argTypes.tailType);
2412
CHECK(fnFoo->returnTypes->is<AstTypePackVariadic>());
2413
2414
AstStatTypeAlias* bar = stat->body.data[2]->as<AstStatTypeAlias>();
2415
REQUIRE(bar);
2416
AstTypeFunction* fnBar = bar->type->as<AstTypeFunction>();
2417
REQUIRE(fnBar);
2418
CHECK_EQ(fnBar->argTypes.types.size, 0);
2419
CHECK(!fnBar->argTypes.tailType);
2420
auto returnTypePack = fnBar->returnTypes->as<AstTypePackExplicit>();
2421
REQUIRE(returnTypePack);
2422
CHECK_EQ(returnTypePack->typeList.types.size, 1);
2423
CHECK(returnTypePack->typeList.tailType);
2424
}
2425
2426
TEST_CASE_FIXTURE(Fixture, "variadics_must_be_last")
2427
{
2428
matchParseError("function foo(): (...number, string) end", "Expected ')' (to close '(' at column 17), got ','");
2429
matchParseError("type Foo = (...number, string) -> (...string, number)", "Expected ')' (to close '(' at column 12), got ','");
2430
}
2431
2432
TEST_CASE_FIXTURE(Fixture, "variadic_definition_parsing")
2433
{
2434
AstStatBlock* stat = parseEx(R"(
2435
declare function foo(...: string): ...string
2436
declare class Foo
2437
function a(self, ...: string): ...string
2438
end
2439
)")
2440
.root;
2441
2442
REQUIRE(stat != nullptr);
2443
2444
matchParseError("declare function foo(...)", "All declaration parameters must be annotated");
2445
matchParseError("declare class Foo function a(self, ...) end", "All declaration parameters aside from 'self' must be annotated");
2446
}
2447
2448
TEST_CASE_FIXTURE(Fixture, "missing_declaration_prop")
2449
{
2450
matchParseError(
2451
R"(
2452
declare class Foo
2453
a: number,
2454
end
2455
)",
2456
"Expected identifier when parsing property name, got ','"
2457
);
2458
}
2459
2460
TEST_CASE_FIXTURE(Fixture, "generic_pack_parsing")
2461
{
2462
ParseResult result = parseEx(R"(
2463
function f<a...>(...: a...)
2464
end
2465
2466
type A = (a...) -> b...
2467
)");
2468
2469
AstStatBlock* stat = result.root;
2470
REQUIRE(stat != nullptr);
2471
2472
AstStatFunction* fn = stat->body.data[0]->as<AstStatFunction>();
2473
REQUIRE(fn != nullptr);
2474
REQUIRE(fn->func->varargAnnotation != nullptr);
2475
2476
AstTypePackGeneric* annot = fn->func->varargAnnotation->as<AstTypePackGeneric>();
2477
REQUIRE(annot != nullptr);
2478
CHECK(annot->genericName == "a");
2479
2480
AstStatTypeAlias* alias = stat->body.data[1]->as<AstStatTypeAlias>();
2481
REQUIRE(alias != nullptr);
2482
AstTypeFunction* fnTy = alias->type->as<AstTypeFunction>();
2483
REQUIRE(fnTy != nullptr);
2484
2485
AstTypePackGeneric* argAnnot = fnTy->argTypes.tailType->as<AstTypePackGeneric>();
2486
REQUIRE(argAnnot != nullptr);
2487
CHECK(argAnnot->genericName == "a");
2488
2489
AstTypePackGeneric* retAnnot = fnTy->returnTypes->as<AstTypePackGeneric>();
2490
REQUIRE(retAnnot != nullptr);
2491
CHECK(retAnnot->genericName == "b");
2492
}
2493
2494
TEST_CASE_FIXTURE(Fixture, "generic_function_declaration_parsing")
2495
{
2496
ParseResult result = parseEx(R"(
2497
declare function f<a, b, c...>()
2498
)");
2499
2500
AstStatBlock* stat = result.root;
2501
REQUIRE(stat != nullptr);
2502
2503
AstStatDeclareFunction* decl = stat->body.data[0]->as<AstStatDeclareFunction>();
2504
REQUIRE(decl != nullptr);
2505
REQUIRE_EQ(decl->generics.size, 2);
2506
REQUIRE_EQ(decl->genericPacks.size, 1);
2507
}
2508
2509
TEST_CASE_FIXTURE(Fixture, "function_type_named_arguments")
2510
{
2511
{
2512
ParseResult result = parseEx("type MyFunc = (a: number, b: string, c: number) -> string");
2513
2514
AstStatBlock* stat = result.root;
2515
REQUIRE(stat != nullptr);
2516
2517
AstStatTypeAlias* decl = stat->body.data[0]->as<AstStatTypeAlias>();
2518
REQUIRE(decl != nullptr);
2519
AstTypeFunction* func = decl->type->as<AstTypeFunction>();
2520
REQUIRE(func != nullptr);
2521
REQUIRE_EQ(func->argTypes.types.size, 3);
2522
REQUIRE_EQ(func->argNames.size, 3);
2523
REQUIRE(func->argNames.data[2]);
2524
CHECK_EQ(func->argNames.data[2]->first, "c");
2525
}
2526
2527
{
2528
ParseResult result = parseEx("type MyFunc = (a: number, string, c: number) -> string");
2529
2530
AstStatBlock* stat = result.root;
2531
REQUIRE(stat != nullptr);
2532
2533
AstStatTypeAlias* decl = stat->body.data[0]->as<AstStatTypeAlias>();
2534
REQUIRE(decl != nullptr);
2535
AstTypeFunction* func = decl->type->as<AstTypeFunction>();
2536
REQUIRE(func != nullptr);
2537
REQUIRE_EQ(func->argTypes.types.size, 3);
2538
REQUIRE_EQ(func->argNames.size, 3);
2539
REQUIRE(!func->argNames.data[1]);
2540
REQUIRE(func->argNames.data[2]);
2541
CHECK_EQ(func->argNames.data[2]->first, "c");
2542
}
2543
2544
{
2545
ParseResult result = parseEx("type MyFunc = (a: number, string, number) -> string");
2546
2547
AstStatBlock* stat = result.root;
2548
REQUIRE(stat != nullptr);
2549
2550
AstStatTypeAlias* decl = stat->body.data[0]->as<AstStatTypeAlias>();
2551
REQUIRE(decl != nullptr);
2552
AstTypeFunction* func = decl->type->as<AstTypeFunction>();
2553
REQUIRE(func != nullptr);
2554
REQUIRE_EQ(func->argTypes.types.size, 3);
2555
REQUIRE_EQ(func->argNames.size, 3);
2556
REQUIRE(!func->argNames.data[1]);
2557
REQUIRE(!func->argNames.data[2]);
2558
}
2559
2560
{
2561
ParseResult result = parseEx("type MyFunc = (a: number, b: string, c: number) -> (d: number, e: string, f: number) -> string");
2562
2563
AstStatBlock* stat = result.root;
2564
REQUIRE(stat != nullptr);
2565
2566
AstStatTypeAlias* decl = stat->body.data[0]->as<AstStatTypeAlias>();
2567
REQUIRE(decl != nullptr);
2568
AstTypeFunction* func = decl->type->as<AstTypeFunction>();
2569
REQUIRE(func != nullptr);
2570
REQUIRE_EQ(func->argTypes.types.size, 3);
2571
REQUIRE_EQ(func->argNames.size, 3);
2572
REQUIRE(func->argNames.data[2]);
2573
CHECK_EQ(func->argNames.data[2]->first, "c");
2574
AstTypeFunction* funcRet = func->returnTypes->as<AstTypePackExplicit>()->typeList.types.data[0]->as<AstTypeFunction>();
2575
REQUIRE(funcRet != nullptr);
2576
REQUIRE_EQ(funcRet->argTypes.types.size, 3);
2577
REQUIRE_EQ(funcRet->argNames.size, 3);
2578
REQUIRE(func->argNames.data[2]);
2579
CHECK_EQ(funcRet->argNames.data[2]->first, "f");
2580
}
2581
2582
matchParseError(
2583
"type MyFunc = (a: number, b: string, c: number) -> (d: number, e: string, f: number)", "Expected '->' when parsing function type, got <eof>"
2584
);
2585
2586
matchParseError("type MyFunc = (number) -> (d: number) <a, b, c> -> number", "Expected '->' when parsing function type, got '<'");
2587
}
2588
2589
TEST_CASE_FIXTURE(Fixture, "function_type_matching_parenthesis")
2590
{
2591
matchParseError("local a: <T>(number -> string", "Expected ')' (to close '(' at column 13), got '->'");
2592
}
2593
2594
TEST_CASE_FIXTURE(Fixture, "parse_type_alias_default_type")
2595
{
2596
AstStat* stat = parse(R"(
2597
type A<T = string> = {}
2598
type B<T... = ...number> = {}
2599
type C<T..., U... = T...> = {}
2600
type D<T..., U... = ()> = {}
2601
type E<T... = (), U... = ()> = {}
2602
type F<T... = (string), U... = ()> = (T...) -> U...
2603
type G<T... = ...number, U... = (string, number, boolean)> = (U...) -> T...
2604
)");
2605
2606
REQUIRE(stat != nullptr);
2607
}
2608
2609
TEST_CASE_FIXTURE(Fixture, "parse_type_alias_default_type_errors")
2610
{
2611
matchParseError("type Y<T = number, U> = {}", "Expected default type after type name", Location{{0, 20}, {0, 21}});
2612
matchParseError("type Y<T... = ...number, U...> = {}", "Expected default type pack after type pack name", Location{{0, 29}, {0, 30}});
2613
matchParseError("type Y<T... = (string) -> number> = {}", "Expected type pack after '=', got type", Location{{0, 14}, {0, 32}});
2614
}
2615
2616
TEST_CASE_FIXTURE(Fixture, "parse_type_pack_errors")
2617
{
2618
matchParseError(
2619
"type Y<T...> = {a: T..., b: number}",
2620
"Unexpected '...' after type name; type pack is not allowed in this context",
2621
Location{{0, 20}, {0, 23}}
2622
);
2623
matchParseError("type Y<T...> = {a: (number | string)...", "Unexpected '...' after type annotation", Location{{0, 36}, {0, 39}});
2624
}
2625
2626
TEST_CASE_FIXTURE(Fixture, "parse_if_else_expression")
2627
{
2628
{
2629
AstStat* stat = parse("return if true then 1 else 2");
2630
2631
REQUIRE(stat != nullptr);
2632
AstStatReturn* str = stat->as<AstStatBlock>()->body.data[0]->as<AstStatReturn>();
2633
REQUIRE(str != nullptr);
2634
CHECK(str->list.size == 1);
2635
auto* ifElseExpr = str->list.data[0]->as<AstExprIfElse>();
2636
REQUIRE(ifElseExpr != nullptr);
2637
}
2638
2639
{
2640
AstStat* stat = parse("return if true then 1 elseif true then 2 else 3");
2641
2642
REQUIRE(stat != nullptr);
2643
AstStatReturn* str = stat->as<AstStatBlock>()->body.data[0]->as<AstStatReturn>();
2644
REQUIRE(str != nullptr);
2645
CHECK(str->list.size == 1);
2646
auto* ifElseExpr1 = str->list.data[0]->as<AstExprIfElse>();
2647
REQUIRE(ifElseExpr1 != nullptr);
2648
auto* ifElseExpr2 = ifElseExpr1->falseExpr->as<AstExprIfElse>();
2649
REQUIRE(ifElseExpr2 != nullptr);
2650
}
2651
2652
// Use "else if" as opposed to elseif
2653
{
2654
AstStat* stat = parse("return if true then 1 else if true then 2 else 3");
2655
2656
REQUIRE(stat != nullptr);
2657
AstStatReturn* str = stat->as<AstStatBlock>()->body.data[0]->as<AstStatReturn>();
2658
REQUIRE(str != nullptr);
2659
CHECK(str->list.size == 1);
2660
auto* ifElseExpr1 = str->list.data[0]->as<AstExprIfElse>();
2661
REQUIRE(ifElseExpr1 != nullptr);
2662
auto* ifElseExpr2 = ifElseExpr1->falseExpr->as<AstExprIfElse>();
2663
REQUIRE(ifElseExpr2 != nullptr);
2664
}
2665
2666
// Use an if-else expression as the conditional expression of an if-else expression
2667
{
2668
AstStat* stat = parse("return if if true then false else true then 1 else 2");
2669
2670
REQUIRE(stat != nullptr);
2671
AstStatReturn* str = stat->as<AstStatBlock>()->body.data[0]->as<AstStatReturn>();
2672
REQUIRE(str != nullptr);
2673
CHECK(str->list.size == 1);
2674
auto* ifElseExpr = str->list.data[0]->as<AstExprIfElse>();
2675
REQUIRE(ifElseExpr != nullptr);
2676
auto* nestedIfElseExpr = ifElseExpr->condition->as<AstExprIfElse>();
2677
REQUIRE(nestedIfElseExpr != nullptr);
2678
}
2679
}
2680
2681
TEST_CASE_FIXTURE(Fixture, "parse_type_pack_type_parameters")
2682
{
2683
AstStat* stat = parse(R"(
2684
type Packed<T...> = () -> T...
2685
2686
type A<X...> = Packed<X...>
2687
type B<X...> = Packed<...number>
2688
type C<X...> = Packed<(number, X...)>
2689
)");
2690
REQUIRE(stat != nullptr);
2691
}
2692
2693
TEST_CASE_FIXTURE(Fixture, "invalid_type_forms")
2694
{
2695
matchParseError("type A = (b: number)", "Expected '->' when parsing function type, got <eof>");
2696
matchParseError("type P<T...> = () -> T... type B = P<(x: number, y: string)>", "Expected '->' when parsing function type, got '>'");
2697
matchParseError("type F<T... = (a: string)> = (T...) -> ()", "Expected '->' when parsing function type, got '>'");
2698
}
2699
2700
TEST_CASE_FIXTURE(Fixture, "parse_user_defined_type_functions")
2701
{
2702
AstStat* stat = parse(R"(
2703
type function foo()
2704
return types.number
2705
end
2706
2707
export type function bar()
2708
return types.string
2709
end
2710
)");
2711
2712
REQUIRE(stat != nullptr);
2713
AstStatTypeFunction* f = stat->as<AstStatBlock>()->body.data[0]->as<AstStatTypeFunction>();
2714
REQUIRE(f != nullptr);
2715
REQUIRE(f->name == "foo");
2716
}
2717
2718
TEST_CASE_FIXTURE(Fixture, "parse_nested_type_function")
2719
{
2720
AstStat* stat = parse(R"(
2721
local v1 = 1
2722
type function foo()
2723
local v2 = 2
2724
local function bar()
2725
v2 += 1
2726
type function inner() end
2727
v2 += 2
2728
end
2729
local function bar2()
2730
v2 += 3
2731
end
2732
end
2733
local function bar() v1 += 1 end
2734
)");
2735
2736
REQUIRE(stat != nullptr);
2737
}
2738
2739
TEST_CASE_FIXTURE(Fixture, "invalid_user_defined_type_functions")
2740
{
2741
matchParseError("local foo = 1; type function bar() print(foo) end", "Type function cannot reference outer local 'foo'");
2742
matchParseError("type function foo() local v1 = 1; type function bar() print(v1) end end", "Type function cannot reference outer local 'v1'");
2743
}
2744
2745
TEST_CASE_FIXTURE(Fixture, "leading_union_intersection_with_single_type_preserves_the_union_intersection_ast_node")
2746
{
2747
AstStatBlock* block = parse(R"(
2748
type Foo = | string
2749
type Bar = & number
2750
)");
2751
2752
REQUIRE_EQ(2, block->body.size);
2753
2754
const auto alias1 = block->body.data[0]->as<AstStatTypeAlias>();
2755
REQUIRE(alias1);
2756
2757
const auto unionType = alias1->type->as<AstTypeUnion>();
2758
REQUIRE(unionType);
2759
CHECK_EQ(1, unionType->types.size);
2760
2761
const auto alias2 = block->body.data[1]->as<AstStatTypeAlias>();
2762
REQUIRE(alias2);
2763
2764
const auto intersectionType = alias2->type->as<AstTypeIntersection>();
2765
REQUIRE(intersectionType);
2766
CHECK_EQ(1, intersectionType->types.size);
2767
}
2768
2769
TEST_CASE_FIXTURE(Fixture, "parse_simple_ast_type_group")
2770
{
2771
AstStatBlock* stat = parse(R"(
2772
type Foo = (string)
2773
)");
2774
REQUIRE(stat);
2775
REQUIRE_EQ(1, stat->body.size);
2776
2777
auto alias1 = stat->body.data[0]->as<AstStatTypeAlias>();
2778
REQUIRE(alias1);
2779
2780
auto group1 = alias1->type->as<AstTypeGroup>();
2781
REQUIRE(group1);
2782
CHECK(group1->type->is<AstTypeReference>());
2783
}
2784
2785
TEST_CASE_FIXTURE(Fixture, "parse_nested_ast_type_group")
2786
{
2787
AstStatBlock* stat = parse(R"(
2788
type Foo = ((string))
2789
)");
2790
REQUIRE(stat);
2791
REQUIRE_EQ(1, stat->body.size);
2792
2793
auto alias1 = stat->body.data[0]->as<AstStatTypeAlias>();
2794
REQUIRE(alias1);
2795
2796
auto group1 = alias1->type->as<AstTypeGroup>();
2797
REQUIRE(group1);
2798
2799
auto group2 = group1->type->as<AstTypeGroup>();
2800
REQUIRE(group2);
2801
CHECK(group2->type->is<AstTypeReference>());
2802
}
2803
2804
TEST_CASE_FIXTURE(Fixture, "parse_return_type_ast_type_group")
2805
{
2806
AstStatBlock* stat = parse(R"(
2807
type Foo = () -> (string)
2808
)");
2809
REQUIRE(stat);
2810
REQUIRE_EQ(1, stat->body.size);
2811
2812
auto alias1 = stat->body.data[0]->as<AstStatTypeAlias>();
2813
REQUIRE(alias1);
2814
2815
auto funcType = alias1->type->as<AstTypeFunction>();
2816
REQUIRE(funcType);
2817
2818
auto returnTypePack = funcType->returnTypes->as<AstTypePackExplicit>();
2819
REQUIRE(returnTypePack);
2820
REQUIRE_EQ(1, returnTypePack->typeList.types.size);
2821
REQUIRE(!returnTypePack->typeList.tailType);
2822
CHECK(returnTypePack->typeList.types.data[0]->is<AstTypeGroup>());
2823
}
2824
2825
TEST_CASE_FIXTURE(Fixture, "inner_and_outer_scope_of_functions_have_correct_end_position")
2826
{
2827
2828
AstStatBlock* stat = parse(R"(
2829
local function foo()
2830
local x = 1
2831
end
2832
)");
2833
REQUIRE(stat);
2834
REQUIRE_EQ(1, stat->body.size);
2835
2836
auto func = stat->body.data[0]->as<AstStatLocalFunction>();
2837
REQUIRE(func);
2838
CHECK_EQ(func->func->body->location, Location{{1, 28}, {3, 8}});
2839
CHECK_EQ(func->location, Location{{1, 8}, {3, 11}});
2840
}
2841
2842
TEST_CASE_FIXTURE(Fixture, "do_block_end_location_is_after_end_token")
2843
{
2844
AstStatBlock* stat = parse(R"(
2845
do
2846
local x = 1
2847
end
2848
)");
2849
REQUIRE(stat);
2850
REQUIRE_EQ(1, stat->body.size);
2851
2852
auto block = stat->body.data[0]->as<AstStatBlock>();
2853
REQUIRE(block);
2854
CHECK_EQ(block->location, Location{{1, 8}, {3, 11}});
2855
}
2856
2857
TEST_CASE_FIXTURE(Fixture, "function_start_locations_are_before_attributes")
2858
{
2859
AstStatBlock* stat = parse(R"(
2860
@native
2861
function globalFunction()
2862
end
2863
2864
@native
2865
local function localFunction()
2866
end
2867
2868
local _ = @native function()
2869
end
2870
)");
2871
REQUIRE(stat);
2872
REQUIRE_EQ(3, stat->body.size);
2873
2874
auto globalFunction = stat->body.data[0]->as<AstStatFunction>();
2875
REQUIRE(globalFunction);
2876
CHECK_EQ(globalFunction->location, Location({1, 8}, {3, 11}));
2877
2878
auto localFunction = stat->body.data[1]->as<AstStatLocalFunction>();
2879
REQUIRE(localFunction);
2880
CHECK_EQ(localFunction->location, Location({5, 8}, {7, 11}));
2881
2882
auto localVariable = stat->body.data[2]->as<AstStatLocal>();
2883
REQUIRE(localVariable);
2884
REQUIRE_EQ(localVariable->values.size, 1);
2885
auto anonymousFunction = localVariable->values.data[0]->as<AstExprFunction>();
2886
CHECK_EQ(anonymousFunction->location, Location({9, 18}, {10, 11}));
2887
}
2888
2889
TEST_CASE_FIXTURE(Fixture, "for_loop_with_single_var_has_comma_positions_of_size_zero")
2890
{
2891
ParseOptions parseOptions;
2892
parseOptions.storeCstData = true;
2893
2894
ParseResult result = parseEx(
2895
R"(
2896
for value in tbl do
2897
end
2898
)",
2899
parseOptions
2900
);
2901
REQUIRE(result.root);
2902
REQUIRE_EQ(1, result.root->body.size);
2903
2904
auto forLoop = result.root->body.data[0]->as<AstStatForIn>();
2905
auto baseCstNode = result.cstNodeMap.find(forLoop);
2906
REQUIRE(baseCstNode);
2907
2908
auto cstNode = (*baseCstNode)->as<CstStatForIn>();
2909
CHECK_EQ(cstNode->varsCommaPositions.size, 0);
2910
}
2911
2912
TEST_CASE_FIXTURE(Fixture, "explicit_type_instantiation_expression_call")
2913
{
2914
std::string source = "local x = f<<T, U>>()";
2915
2916
ParseResult result = parseEx(source);
2917
REQUIRE(result.root);
2918
2919
AstStatLocal* local = result.root->body.data[0]->as<AstStatLocal>();
2920
REQUIRE(local != nullptr);
2921
2922
REQUIRE_EQ(1, local->vars.size);
2923
2924
AstExpr* expr = local->values.data[0];
2925
REQUIRE(expr != nullptr);
2926
2927
AstExprCall* call = expr->as<AstExprCall>();
2928
REQUIRE(call != nullptr);
2929
2930
AstExprInstantiate* explicitTypeInstantiation = call->func->as<AstExprInstantiate>();
2931
REQUIRE(explicitTypeInstantiation != nullptr);
2932
2933
REQUIRE_EQ(stringAtLocation(source, explicitTypeInstantiation->location), "f<<T, U>>");
2934
}
2935
2936
TEST_CASE_FIXTURE(Fixture, "explicit_type_instantiation_expression")
2937
{
2938
AstStat* stat = parse("local x = f<<T, U>>");
2939
REQUIRE(stat != nullptr);
2940
}
2941
2942
TEST_CASE_FIXTURE(Fixture, "explicit_type_instantiation_statement")
2943
{
2944
AstStat* stat = parse("f<<T, U>>()");
2945
REQUIRE(stat != nullptr);
2946
}
2947
2948
TEST_CASE_FIXTURE(Fixture, "explicit_type_instantiation_indexing")
2949
{
2950
AstStat* stat = parse(R"(
2951
t.f<<T, U>>()
2952
t:f<<T, U>>()
2953
t["f"]<<T, U>>()
2954
)");
2955
REQUIRE(stat != nullptr);
2956
}
2957
2958
TEST_CASE_FIXTURE(Fixture, "explicit_type_instantiation_empty_list")
2959
{
2960
AstStat* stat = parse(R"(
2961
f<<>>()
2962
)");
2963
REQUIRE(stat != nullptr);
2964
}
2965
2966
TEST_CASE_FIXTURE(Fixture, "two_left_and_right_arrows_but_no_explicit_type_instantiation")
2967
{
2968
AstStat* stat = parse(R"(
2969
type A = C<B<<T>() -> T>>
2970
)");
2971
REQUIRE(stat != nullptr);
2972
}
2973
2974
TEST_CASE_FIXTURE(Fixture, "basic_less_than_check_no_explicit_type_instantiaton")
2975
{
2976
AstStat* stat = parse(R"(
2977
local a = b.c < d
2978
)");
2979
REQUIRE(stat != nullptr);
2980
}
2981
2982
TEST_CASE_FIXTURE(Fixture, "do_end_block_with_cst")
2983
{
2984
ParseOptions parseOptions;
2985
parseOptions.storeCstData = true;
2986
2987
ParseResult result = parseEx(
2988
R"(
2989
do
2990
local hello = "world"
2991
end
2992
)",
2993
parseOptions
2994
);
2995
REQUIRE(result.root);
2996
2997
const auto moduleCstNode = result.cstNodeMap.find(result.root);
2998
// We only create CST nodes for do ... end blocks
2999
REQUIRE(!moduleCstNode);
3000
3001
REQUIRE_EQ(result.root->body.size, 1);
3002
auto doBlock = result.root->body.data[0]->as<AstStatBlock>();
3003
REQUIRE(doBlock);
3004
const auto doBlockCstNode = result.cstNodeMap.find(doBlock);
3005
REQUIRE(doBlockCstNode);
3006
const auto doBlockCst = (*doBlockCstNode)->as<CstStatDo>();
3007
REQUIRE(doBlockCst);
3008
CHECK_EQ(doBlockCst->statsStartPosition, Position{2, 12});
3009
CHECK_EQ(doBlockCst->endPosition, Position{3, 8});
3010
}
3011
3012
TEST_CASE_FIXTURE(Fixture, "parse_const")
3013
{
3014
ScopedFastFlag sff{FFlag::LuauConst2, true};
3015
AstStatBlock* stat = parse(R"(
3016
const f = 42
3017
)");
3018
3019
REQUIRE(stat != nullptr);
3020
REQUIRE_EQ(stat->body.size, 1);
3021
REQUIRE(stat->body.data[0]->is<AstStatLocal>());
3022
AstStatLocal* statLocal = stat->body.data[0]->as<AstStatLocal>();
3023
REQUIRE_EQ(statLocal->vars.size, 1);
3024
REQUIRE_EQ(statLocal->values.size, 1);
3025
AstLocal* local = statLocal->vars.data[0];
3026
REQUIRE_EQ(std::string(local->name.value), "f");
3027
REQUIRE(local->isConst);
3028
REQUIRE(statLocal->values.data[0]->is<AstExprConstantNumber>());
3029
REQUIRE_EQ(statLocal->values.data[0]->as<AstExprConstantNumber>()->value, 42);
3030
}
3031
3032
TEST_CASE_FIXTURE(Fixture, "parse_const_multi_initialize")
3033
{
3034
ScopedFastFlag sff{FFlag::LuauConst2, true};
3035
AstStatBlock* stat = parse(R"(
3036
const a, b = 42, 32
3037
3038
const a, b, c = 42, f()
3039
3040
const a, b, c = 42, ...
3041
)");
3042
3043
REQUIRE(stat != nullptr);
3044
}
3045
3046
TEST_CASE_FIXTURE(Fixture, "parse_const_function")
3047
{
3048
ScopedFastFlag sff{FFlag::LuauConst2, true};
3049
AstStatBlock* stat = parse(R"(
3050
const function f() return 42 end
3051
)");
3052
3053
REQUIRE(stat != nullptr);
3054
}
3055
3056
TEST_CASE_FIXTURE(Fixture, "parse_const_function_with_attr")
3057
{
3058
ScopedFastFlag sff{FFlag::LuauConst2, true};
3059
AstStatBlock* stat = parse(R"(
3060
@deprecated
3061
const function f() return 42 end
3062
)");
3063
3064
REQUIRE(stat != nullptr);
3065
}
3066
3067
TEST_CASE_FIXTURE(Fixture, "parse_local_const")
3068
{
3069
ScopedFastFlag sff{FFlag::LuauConst2, true};
3070
AstStatBlock* stat = parse(R"(
3071
local const
3072
)");
3073
3074
REQUIRE(stat != nullptr);
3075
}
3076
3077
TEST_CASE_FIXTURE(Fixture, "parse_const_call")
3078
{
3079
ScopedFastFlag sff{FFlag::LuauConst2, true};
3080
AstStatBlock* stat = parse(R"(
3081
local const = function(t) return t end
3082
const { a = "a" }
3083
)");
3084
3085
REQUIRE(stat != nullptr);
3086
}
3087
3088
TEST_CASE_FIXTURE(Fixture, "error_const_not_initialized")
3089
{
3090
ScopedFastFlag sff{FFlag::LuauConst2, true};
3091
3092
matchParseError("const c", "Missing initializer in const declaration");
3093
3094
matchParseError("const a, b = nil", "Missing initializer in const declaration");
3095
3096
matchParseError("const a, b, c = f(), 42", "Missing initializer in const declaration");
3097
3098
matchParseError("const a, b, c = ..., 42", "Missing initializer in const declaration");
3099
}
3100
3101
TEST_CASE_FIXTURE(Fixture, "error_const_reassignment")
3102
{
3103
ScopedFastFlag sff{FFlag::LuauConst2, true};
3104
3105
matchParseError("const a = 42; a = 43", "Assigned expression must be a variable or a field");
3106
3107
matchParseError("local b; const a = 42; a, b = 43", "Assigned expression must be a variable or a field");
3108
3109
matchParseError("local b; const a = 42; b, a = 43", "Assigned expression must be a variable or a field");
3110
3111
matchParseError("local b; const a = 42; b, a = ...", "Assigned expression must be a variable or a field");
3112
3113
matchParseError("const a = 42; function a() end", "Assigned expression must be a variable or a field");
3114
}
3115
3116
TEST_CASE_FIXTURE(Fixture, "error_const_function_reassignment")
3117
{
3118
ScopedFastFlag sff{FFlag::LuauConst2, true};
3119
3120
matchParseError("const function a() return 42 end; a = 43", "Assigned expression must be a variable or a field");
3121
}
3122
3123
TEST_CASE_FIXTURE(Fixture, "const_shadow")
3124
{
3125
ScopedFastFlag sff{FFlag::LuauConst2, true};
3126
3127
AstStatBlock* stat = parse(R"(
3128
const a = 42
3129
const a = 43
3130
3131
do
3132
const a = 44
3133
do
3134
local a = 44.1
3135
do
3136
const a = 44.2
3137
end
3138
a = 44.3
3139
end
3140
end
3141
3142
function f()
3143
const a = 45
3144
local a = 46
3145
return function(x) a = x end
3146
end
3147
)");
3148
3149
REQUIRE(stat != nullptr);
3150
}
3151
3152
TEST_SUITE_END();
3153
3154
TEST_SUITE_BEGIN("ParseErrorRecovery");
3155
3156
TEST_CASE_FIXTURE(Fixture, "multiple_parse_errors")
3157
{
3158
try
3159
{
3160
parse(R"(
3161
local a = 3 * (
3162
return a +
3163
)");
3164
FAIL("Expected ParseErrors to be thrown");
3165
}
3166
catch (const Luau::ParseErrors& e)
3167
{
3168
CHECK_EQ(2, e.getErrors().size());
3169
}
3170
}
3171
3172
// check that we are not skipping tokens that weren't processed at all
3173
TEST_CASE_FIXTURE(Fixture, "statement_error_recovery_expected")
3174
{
3175
try
3176
{
3177
parse(R"(
3178
function a(a, b) return a + b end
3179
some
3180
a(2, 5)
3181
)");
3182
FAIL("Expected ParseErrors to be thrown");
3183
}
3184
catch (const Luau::ParseErrors& e)
3185
{
3186
CHECK_EQ(1, e.getErrors().size());
3187
}
3188
}
3189
3190
TEST_CASE_FIXTURE(Fixture, "statement_error_recovery_unexpected")
3191
{
3192
try
3193
{
3194
parse(R"(+)");
3195
FAIL("Expected ParseErrors to be thrown");
3196
}
3197
catch (const Luau::ParseErrors& e)
3198
{
3199
CHECK_EQ(1, e.getErrors().size());
3200
}
3201
}
3202
3203
TEST_CASE_FIXTURE(Fixture, "extra_token_in_consume")
3204
{
3205
try
3206
{
3207
parse(R"(
3208
function test + (a, f) return a + f end
3209
return test(2, 3)
3210
)");
3211
FAIL("Expected ParseErrors to be thrown");
3212
}
3213
catch (const Luau::ParseErrors& e)
3214
{
3215
CHECK_EQ(1, e.getErrors().size());
3216
CHECK_EQ("Expected '(' when parsing function, got '+'", e.getErrors().front().getMessage());
3217
}
3218
}
3219
3220
TEST_CASE_FIXTURE(Fixture, "extra_token_in_consume_match")
3221
{
3222
try
3223
{
3224
parse(R"(
3225
function test(a, f+) return a + f end
3226
return test(2, 3)
3227
)");
3228
FAIL("Expected ParseErrors to be thrown");
3229
}
3230
catch (const Luau::ParseErrors& e)
3231
{
3232
CHECK_EQ(1, e.getErrors().size());
3233
CHECK_EQ("Expected ')' (to close '(' at column 14), got '+'", e.getErrors().front().getMessage());
3234
}
3235
}
3236
3237
TEST_CASE_FIXTURE(Fixture, "extra_token_in_consume_match_end")
3238
{
3239
try
3240
{
3241
parse(R"(
3242
if true then
3243
return 12
3244
then
3245
end
3246
)");
3247
FAIL("Expected ParseErrors to be thrown");
3248
}
3249
catch (const Luau::ParseErrors& e)
3250
{
3251
CHECK_EQ(1, e.getErrors().size());
3252
CHECK_EQ("Expected 'end' (to close 'then' at line 2), got 'then'", e.getErrors().front().getMessage());
3253
}
3254
}
3255
3256
TEST_CASE_FIXTURE(Fixture, "extra_table_indexer_recovery")
3257
{
3258
try
3259
{
3260
parse(R"(
3261
local a : { [string] : number, [number] : string, count: number }
3262
)");
3263
FAIL("Expected ParseErrors to be thrown");
3264
}
3265
catch (const Luau::ParseErrors& e)
3266
{
3267
CHECK_EQ(1, e.getErrors().size());
3268
}
3269
}
3270
3271
TEST_CASE_FIXTURE(Fixture, "recovery_error_limit_1")
3272
{
3273
ScopedFastInt luauParseErrorLimit(FInt::LuauParseErrorLimit, 1);
3274
3275
try
3276
{
3277
parse("local a = ");
3278
FAIL("Expected ParseErrors to be thrown");
3279
}
3280
catch (const Luau::ParseErrors& e)
3281
{
3282
CHECK_EQ(1, e.getErrors().size());
3283
CHECK_EQ(e.getErrors().front().getMessage(), e.what());
3284
}
3285
}
3286
3287
TEST_CASE_FIXTURE(Fixture, "recovery_error_limit_2")
3288
{
3289
ScopedFastInt luauParseErrorLimit(FInt::LuauParseErrorLimit, 2);
3290
3291
try
3292
{
3293
parse("escape escape escape");
3294
FAIL("Expected ParseErrors to be thrown");
3295
}
3296
catch (const Luau::ParseErrors& e)
3297
{
3298
CHECK_EQ(3, e.getErrors().size());
3299
CHECK_EQ("3 parse errors", std::string(e.what()));
3300
CHECK_EQ("Reached error limit (2)", e.getErrors().back().getMessage());
3301
}
3302
}
3303
3304
class CountAstNodes : public AstVisitor
3305
{
3306
public:
3307
bool visit(AstNode* node) override
3308
{
3309
count++;
3310
3311
return true;
3312
}
3313
3314
unsigned count = 0;
3315
};
3316
3317
TEST_CASE_FIXTURE(Fixture, "recovery_of_parenthesized_expressions")
3318
{
3319
auto checkAstEquivalence = [this](const char* codeWithErrors, const char* code)
3320
{
3321
try
3322
{
3323
parse(codeWithErrors);
3324
}
3325
catch (const Luau::ParseErrors&)
3326
{
3327
}
3328
3329
CountAstNodes counterWithErrors;
3330
sourceModule->root->visit(&counterWithErrors);
3331
3332
parse(code);
3333
3334
CountAstNodes counter;
3335
sourceModule->root->visit(&counter);
3336
3337
CHECK_EQ(counterWithErrors.count, counter.count);
3338
};
3339
3340
auto checkRecovery = [this, checkAstEquivalence](const char* codeWithErrors, const char* code, unsigned expectedErrorCount)
3341
{
3342
try
3343
{
3344
parse(codeWithErrors);
3345
FAIL("Expected ParseErrors to be thrown");
3346
}
3347
catch (const Luau::ParseErrors& e)
3348
{
3349
CHECK_EQ(expectedErrorCount, e.getErrors().size());
3350
checkAstEquivalence(codeWithErrors, code);
3351
}
3352
};
3353
3354
DOES_NOT_PASS_NEW_SOLVER_GUARD();
3355
3356
checkRecovery("function foo(a, b. c) return a + b end", "function foo(a, b) return a + b end", 1);
3357
checkRecovery(
3358
"function foo(a, b: { a: number, b: number. c:number }) return a + b end", "function foo(a, b: { a: number, b: number }) return a + b end", 1
3359
);
3360
3361
checkRecovery("function foo(a, b): (number -> number return a + b end", "function foo(a, b): (number) -> number return a + b end", 1);
3362
checkRecovery("function foo(a, b): (number, number -> number return a + b end", "function foo(a, b): (number) -> number return a + b end", 1);
3363
checkRecovery("function foo(a, b): (number; number) -> number return a + b end", "function foo(a, b): (number) -> number return a + b end", 1);
3364
3365
checkRecovery("function foo(a, b): (number, number return a + b end", "function foo(a, b): (number, number) end", 1);
3366
checkRecovery("local function foo(a, b): (number, number return a + b end", "local function foo(a, b): (number, number) end", 1);
3367
3368
// These tests correctly recovered before the changes and we test that new recovery didn't make them worse
3369
// (by skipping more tokens necessary)
3370
checkRecovery("type F = (number, number -> number", "type F = (number, number) -> number", 1);
3371
checkRecovery("function foo(a, b: { a: number, b: number) return a + b end", "function foo(a, b: { a: number, b: number }) return a + b end", 1);
3372
checkRecovery("function foo(a, b: { [number: number}) return a + b end", "function foo(a, b: { [number]: number}) return a + b end", 1);
3373
checkRecovery("local n: (string | number = 2", "local n: (string | number) = 2", 1);
3374
3375
// Check that we correctly stop at the end of a line
3376
checkRecovery(
3377
R"(
3378
function foo(a, b
3379
return a + b
3380
end
3381
)",
3382
"function foo(a, b) return a + b end",
3383
1
3384
);
3385
}
3386
3387
TEST_CASE_FIXTURE(Fixture, "incomplete_method_call")
3388
{
3389
const std::string_view source = R"(
3390
function howdy()
3391
return game:
3392
end
3393
)";
3394
3395
SourceModule sourceModule;
3396
ParseResult result = Parser::parse(source.data(), source.size(), *sourceModule.names, *sourceModule.allocator, {});
3397
3398
REQUIRE_EQ(1, result.root->body.size);
3399
3400
AstStatFunction* howdyFunction = result.root->body.data[0]->as<AstStatFunction>();
3401
REQUIRE(howdyFunction != nullptr);
3402
3403
AstStatBlock* body = howdyFunction->func->body;
3404
REQUIRE_EQ(1, body->body.size);
3405
3406
AstStatReturn* ret = body->body.data[0]->as<AstStatReturn>();
3407
REQUIRE(ret != nullptr);
3408
3409
REQUIRE_GT(howdyFunction->location.end, body->location.end);
3410
}
3411
3412
TEST_CASE_FIXTURE(Fixture, "incomplete_method_call_2")
3413
{
3414
const std::string_view source = R"(
3415
local game = { GetService=function(s) return 'hello' end }
3416
3417
function a()
3418
game:a
3419
end
3420
)";
3421
3422
SourceModule sourceModule;
3423
ParseResult result = Parser::parse(source.data(), source.size(), *sourceModule.names, *sourceModule.allocator, {});
3424
3425
REQUIRE_EQ(2, result.root->body.size);
3426
3427
AstStatFunction* howdyFunction = result.root->body.data[1]->as<AstStatFunction>();
3428
REQUIRE(howdyFunction != nullptr);
3429
3430
AstStatBlock* body = howdyFunction->func->body;
3431
REQUIRE_EQ(1, body->body.size);
3432
3433
AstStatError* ret = body->body.data[0]->as<AstStatError>();
3434
REQUIRE(ret != nullptr);
3435
3436
REQUIRE_GT(howdyFunction->location.end, body->location.end);
3437
}
3438
3439
TEST_CASE_FIXTURE(Fixture, "incomplete_method_call_still_yields_an_AstExprIndexName")
3440
{
3441
ParseResult result = tryParse(R"(
3442
game:
3443
)");
3444
3445
REQUIRE_EQ(1, result.root->body.size);
3446
3447
AstStatError* stat = result.root->body.data[0]->as<AstStatError>();
3448
REQUIRE(stat);
3449
3450
AstExprError* expr = stat->expressions.data[0]->as<AstExprError>();
3451
REQUIRE(expr);
3452
3453
AstExprIndexName* indexName = expr->expressions.data[0]->as<AstExprIndexName>();
3454
REQUIRE(indexName);
3455
}
3456
3457
TEST_CASE_FIXTURE(Fixture, "recover_confusables")
3458
{
3459
// Binary
3460
matchParseError("local a = 4 != 10", "Unexpected '!='; did you mean '~='?");
3461
matchParseError("local a = true && false", "Unexpected '&&'; did you mean 'and'?");
3462
matchParseError("local a = false || true", "Unexpected '||'; did you mean 'or'?");
3463
3464
// Unary
3465
matchParseError("local a = !false", "Unexpected '!'; did you mean 'not'?");
3466
3467
// Check that separate tokens are not considered as a single one
3468
matchParseError("local a = 4 ! = 10", "Expected identifier when parsing expression, got '!'");
3469
matchParseError("local a = true & & false", "Expected identifier when parsing expression, got '&'");
3470
matchParseError("local a = false | | true", "Expected identifier when parsing expression, got '|'");
3471
}
3472
3473
TEST_CASE_FIXTURE(Fixture, "capture_comments")
3474
{
3475
ParseOptions options;
3476
options.captureComments = true;
3477
3478
ParseResult result = parseEx(
3479
R"(
3480
--!strict
3481
3482
local a = 5 -- comment one
3483
local b = 8 -- comment two
3484
--[[
3485
Multi line comment
3486
]]
3487
local c = 'see'
3488
)",
3489
options
3490
);
3491
3492
CHECK(result.errors.empty());
3493
3494
CHECK_EQ(4, result.commentLocations.size());
3495
CHECK_EQ((Location{{1, 8}, {1, 17}}), result.commentLocations[0].location);
3496
CHECK_EQ((Location{{3, 20}, {3, 34}}), result.commentLocations[1].location);
3497
CHECK_EQ((Location{{4, 20}, {4, 34}}), result.commentLocations[2].location);
3498
CHECK_EQ((Location{{5, 8}, {7, 10}}), result.commentLocations[3].location);
3499
}
3500
3501
TEST_CASE_FIXTURE(Fixture, "capture_broken_comment_at_the_start_of_the_file")
3502
{
3503
ParseOptions options;
3504
options.captureComments = true;
3505
3506
ParseResult result = tryParse(
3507
R"(
3508
--[[
3509
)",
3510
options
3511
);
3512
3513
CHECK_EQ(1, result.commentLocations.size());
3514
CHECK_EQ((Location{{1, 8}, {2, 4}}), result.commentLocations[0].location);
3515
}
3516
3517
TEST_CASE_FIXTURE(Fixture, "capture_broken_comment")
3518
{
3519
ParseOptions options;
3520
options.captureComments = true;
3521
3522
ParseResult result = tryParse(
3523
R"(
3524
local a = "test"
3525
3526
--[[broken!
3527
)",
3528
options
3529
);
3530
3531
CHECK_EQ(1, result.commentLocations.size());
3532
CHECK_EQ((Location{{3, 8}, {4, 4}}), result.commentLocations[0].location);
3533
}
3534
3535
TEST_CASE_FIXTURE(Fixture, "empty_function_type_error_recovery")
3536
{
3537
try
3538
{
3539
parse(R"(
3540
type Fn = (
3541
any,
3542
string | number | ()
3543
) -> any
3544
)");
3545
FAIL("Expected ParseErrors to be thrown");
3546
}
3547
catch (const Luau::ParseErrors& e)
3548
{
3549
CHECK_EQ("Expected '->' after '()' when parsing function type; did you mean 'nil'?", e.getErrors().front().getMessage());
3550
}
3551
3552
// If we have arguments or generics, don't use special case
3553
try
3554
{
3555
parse(R"(type Fn = (any, string | number | (number, number)) -> any)");
3556
FAIL("Expected ParseErrors to be thrown");
3557
}
3558
catch (const Luau::ParseErrors& e)
3559
{
3560
CHECK_EQ("Expected '->' when parsing function type, got ')'", e.getErrors().front().getMessage());
3561
}
3562
3563
try
3564
{
3565
parse(R"(type Fn = (any, string | number | <a>()) -> any)");
3566
FAIL("Expected ParseErrors to be thrown");
3567
}
3568
catch (const Luau::ParseErrors& e)
3569
{
3570
CHECK_EQ("Expected '->' when parsing function type, got ')'", e.getErrors().front().getMessage());
3571
}
3572
3573
try
3574
{
3575
parse(R"(type Fn = (any, string | number | <a...>()) -> any)");
3576
FAIL("Expected ParseErrors to be thrown");
3577
}
3578
catch (const Luau::ParseErrors& e)
3579
{
3580
CHECK_EQ("Expected '->' when parsing function type, got ')'", e.getErrors().front().getMessage());
3581
}
3582
}
3583
3584
TEST_CASE_FIXTURE(Fixture, "AstName_comparison")
3585
{
3586
CHECK(!(AstName() < AstName()));
3587
3588
AstName one{"one"};
3589
AstName two{"two"};
3590
3591
CHECK_NE((one < two), (two < one));
3592
}
3593
3594
TEST_CASE_FIXTURE(Fixture, "generic_type_list_recovery")
3595
{
3596
try
3597
{
3598
parse(R"(
3599
local function foo<T..., U>(a: U, ...: T...): (U, ...T) return a, ... end
3600
return foo(1, 2 -- to check for a second error after recovery
3601
)");
3602
FAIL("Expected ParseErrors to be thrown");
3603
}
3604
catch (const Luau::ParseErrors& e)
3605
{
3606
CHECK_EQ(2, e.getErrors().size());
3607
CHECK_EQ("Generic types come before generic type packs", e.getErrors().front().getMessage());
3608
}
3609
}
3610
3611
TEST_CASE_FIXTURE(Fixture, "recover_index_name_keyword")
3612
{
3613
ParseResult result = tryParse(R"(
3614
local b
3615
local a = b.do
3616
)");
3617
CHECK_EQ(1, result.errors.size());
3618
3619
result = tryParse(R"(
3620
local b
3621
local a = b.
3622
do end
3623
)");
3624
CHECK_EQ(1, result.errors.size());
3625
}
3626
3627
TEST_CASE_FIXTURE(Fixture, "recover_self_call_keyword")
3628
{
3629
ParseResult result = tryParse(R"(
3630
local b
3631
local a = b:do
3632
)");
3633
CHECK_EQ(2, result.errors.size());
3634
3635
result = tryParse(R"(
3636
local b
3637
local a = b:
3638
do end
3639
)");
3640
CHECK_EQ(2, result.errors.size());
3641
}
3642
3643
TEST_CASE_FIXTURE(Fixture, "recover_type_index_name_keyword")
3644
{
3645
ParseResult result = tryParse(R"(
3646
local A
3647
local b : A.do
3648
)");
3649
CHECK_EQ(1, result.errors.size());
3650
3651
result = tryParse(R"(
3652
local A
3653
local b : A.do
3654
do end
3655
)");
3656
CHECK_EQ(1, result.errors.size());
3657
}
3658
3659
TEST_CASE_FIXTURE(Fixture, "recover_expected_type_pack")
3660
{
3661
ParseResult result = tryParse(R"(
3662
type Y<T..., U = T...> = (T...) -> U...
3663
)");
3664
CHECK_EQ(1, result.errors.size());
3665
}
3666
3667
TEST_CASE_FIXTURE(Fixture, "recover_unexpected_type_pack")
3668
{
3669
ParseResult result = tryParse(R"(
3670
type X<T...> = { a: T..., b: number }
3671
type Y<T> = { a: T..., b: number }
3672
type Z<T> = { a: string | T..., b: number }
3673
)");
3674
REQUIRE_EQ(3, result.errors.size());
3675
}
3676
3677
TEST_CASE_FIXTURE(Fixture, "recover_function_return_type_annotations")
3678
{
3679
ParseResult result = tryParse(R"(
3680
type Custom<A, B, C> = { x: A, y: B, z: C }
3681
type Packed<A...> = { x: (A...) -> () }
3682
type F = (number): Custom<boolean, number, string>
3683
type G = Packed<(number): (string, number, boolean)>
3684
local function f(x: number) -> Custom<string, boolean, number>
3685
end
3686
)");
3687
REQUIRE_EQ(3, result.errors.size());
3688
CHECK_EQ(result.errors[0].getMessage(), "Return types in function type annotations are written after '->' instead of ':'");
3689
CHECK_EQ(result.errors[1].getMessage(), "Return types in function type annotations are written after '->' instead of ':'");
3690
CHECK_EQ(result.errors[2].getMessage(), "Function return type annotations are written after ':' instead of '->'");
3691
}
3692
3693
TEST_CASE_FIXTURE(Fixture, "error_message_for_using_function_as_type_annotation")
3694
{
3695
ParseResult result = tryParse(R"(
3696
type Foo = function
3697
)");
3698
REQUIRE_EQ(1, result.errors.size());
3699
CHECK_EQ(
3700
"Using 'function' as a type annotation is not supported, consider replacing with a function type annotation e.g. '(...any) -> ...any'",
3701
result.errors[0].getMessage()
3702
);
3703
}
3704
3705
TEST_CASE_FIXTURE(Fixture, "get_a_nice_error_when_there_is_an_extra_comma_at_the_end_of_a_function_argument_list")
3706
{
3707
ParseResult result = tryParse(R"(
3708
foo(a, b, c,)
3709
)");
3710
3711
REQUIRE(1 == result.errors.size());
3712
3713
CHECK(Location({1, 20}, {1, 21}) == result.errors[0].getLocation());
3714
CHECK("Expected expression after ',' but got ')' instead" == result.errors[0].getMessage());
3715
}
3716
3717
TEST_CASE_FIXTURE(Fixture, "get_a_nice_error_when_there_is_an_extra_comma_at_the_end_of_a_function_parameter_list")
3718
{
3719
ParseResult result = tryParse(R"(
3720
export type VisitFn = (
3721
any,
3722
Array<TAnyNode | Array<TAnyNode>>, -- extra comma here
3723
) -> any
3724
)");
3725
3726
REQUIRE(1 == result.errors.size());
3727
3728
CHECK(Location({4, 8}, {4, 9}) == result.errors[0].getLocation());
3729
CHECK("Expected type after ',' but got ')' instead" == result.errors[0].getMessage());
3730
}
3731
3732
TEST_CASE_FIXTURE(Fixture, "get_a_nice_error_when_there_is_an_extra_comma_at_the_end_of_a_generic_parameter_list")
3733
{
3734
ParseResult result = tryParse(R"(
3735
export type VisitFn = <A, B,>(a: A, b: B) -> ()
3736
)");
3737
3738
REQUIRE(1 == result.errors.size());
3739
3740
CHECK(Location({1, 36}, {1, 37}) == result.errors[0].getLocation());
3741
CHECK("Expected type after ',' but got '>' instead" == result.errors[0].getMessage());
3742
3743
REQUIRE(1 == result.root->body.size);
3744
3745
AstStatTypeAlias* t = result.root->body.data[0]->as<AstStatTypeAlias>();
3746
REQUIRE(t != nullptr);
3747
3748
AstTypeFunction* f = t->type->as<AstTypeFunction>();
3749
REQUIRE(f != nullptr);
3750
3751
CHECK(2 == f->generics.size);
3752
}
3753
3754
TEST_CASE_FIXTURE(Fixture, "get_a_nice_error_when_there_is_no_comma_between_table_members")
3755
{
3756
ParseResult result = tryParse(R"(
3757
local t = {
3758
first = 1
3759
second = 2,
3760
third = 3,
3761
fouth = 4,
3762
}
3763
)");
3764
3765
REQUIRE(1 == result.errors.size());
3766
3767
CHECK(Location({3, 12}, {3, 18}) == result.errors[0].getLocation());
3768
CHECK("Expected ',' after table constructor element" == result.errors[0].getMessage());
3769
3770
REQUIRE(1 == result.root->body.size);
3771
3772
AstExprTable* table = Luau::query<AstExprTable>(result.root);
3773
REQUIRE(table);
3774
CHECK(table->items.size == 4);
3775
}
3776
3777
TEST_CASE_FIXTURE(Fixture, "get_a_nice_error_when_there_is_no_comma_after_last_table_member")
3778
{
3779
ParseResult result = tryParse(R"(
3780
local t = {
3781
first = 1
3782
3783
local ok = true
3784
local good = ok == true
3785
)");
3786
3787
REQUIRE(1 == result.errors.size());
3788
3789
CHECK(Location({4, 8}, {4, 13}) == result.errors[0].getLocation());
3790
CHECK("Expected '}' (to close '{' at line 2), got 'local'" == result.errors[0].getMessage());
3791
3792
REQUIRE(3 == result.root->body.size);
3793
3794
AstExprTable* table = Luau::query<AstExprTable>(result.root);
3795
REQUIRE(table);
3796
CHECK(table->items.size == 1);
3797
}
3798
3799
TEST_CASE_FIXTURE(Fixture, "missing_default_type_pack_argument_after_variadic_type_parameter")
3800
{
3801
ParseResult result = tryParse(R"(
3802
type Foo<T... = > = nil
3803
)");
3804
3805
REQUIRE_EQ(2, result.errors.size());
3806
3807
CHECK_EQ(Location{{1, 23}, {1, 25}}, result.errors[0].getLocation());
3808
CHECK_EQ("Expected type, got '>'", result.errors[0].getMessage());
3809
3810
CHECK_EQ(Location{{1, 23}, {1, 24}}, result.errors[1].getLocation());
3811
CHECK_EQ("Expected type pack after '=', got type", result.errors[1].getMessage());
3812
}
3813
3814
TEST_CASE_FIXTURE(Fixture, "table_type_keys_cant_contain_nul")
3815
{
3816
ParseResult result = tryParse(R"(
3817
type Foo = { ["\0"]: number }
3818
)");
3819
3820
REQUIRE_EQ(1, result.errors.size());
3821
3822
CHECK_EQ(Location{{1, 21}, {1, 22}}, result.errors[0].getLocation());
3823
CHECK_EQ("String literal contains malformed escape sequence or \\0", result.errors[0].getMessage());
3824
}
3825
3826
TEST_CASE_FIXTURE(Fixture, "invalid_escape_literals_get_reported_but_parsing_continues")
3827
{
3828
ParseResult result = tryParse(R"(
3829
local foo = "\xQQ"
3830
print(foo)
3831
)");
3832
3833
REQUIRE_EQ(1, result.errors.size());
3834
3835
CHECK_EQ(Location{{1, 20}, {1, 26}}, result.errors[0].getLocation());
3836
CHECK_EQ("String literal contains malformed escape sequence", result.errors[0].getMessage());
3837
3838
REQUIRE(result.root);
3839
CHECK_EQ(result.root->body.size, 2);
3840
}
3841
3842
TEST_CASE_FIXTURE(Fixture, "unfinished_string_literals_get_reported_but_parsing_continues")
3843
{
3844
ParseResult result = tryParse(R"(
3845
local foo = "hi
3846
print(foo)
3847
)");
3848
3849
REQUIRE_EQ(1, result.errors.size());
3850
3851
CHECK_EQ(Location{{1, 20}, {1, 23}}, result.errors[0].getLocation());
3852
CHECK_EQ("Malformed string; did you forget to finish it?", result.errors[0].getMessage());
3853
3854
REQUIRE(result.root);
3855
CHECK_EQ(result.root->body.size, 2);
3856
}
3857
3858
TEST_CASE_FIXTURE(Fixture, "unfinished_string_literal_types_get_reported_but_parsing_continues")
3859
{
3860
ParseResult result = tryParse(R"(
3861
type Foo = "hi
3862
print(foo)
3863
)");
3864
3865
REQUIRE_EQ(1, result.errors.size());
3866
3867
CHECK_EQ(Location{{1, 19}, {1, 22}}, result.errors[0].getLocation());
3868
CHECK_EQ("Malformed string; did you forget to finish it?", result.errors[0].getMessage());
3869
3870
REQUIRE(result.root);
3871
CHECK_EQ(result.root->body.size, 2);
3872
}
3873
3874
TEST_CASE_FIXTURE(Fixture, "do_block_with_no_end")
3875
{
3876
ParseResult result = tryParse(R"(
3877
do
3878
)");
3879
3880
REQUIRE_EQ(1, result.errors.size());
3881
3882
AstStatBlock* stat0 = result.root->body.data[0]->as<AstStatBlock>();
3883
REQUIRE(stat0);
3884
3885
CHECK(!stat0->hasEnd);
3886
}
3887
3888
TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_with_lookahead_involved")
3889
{
3890
ParseResult result = tryParse(R"(
3891
local x = `{ {y} }`
3892
)");
3893
3894
REQUIRE_MESSAGE(result.errors.empty(), result.errors[0].getMessage());
3895
}
3896
3897
TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_with_lookahead_involved2")
3898
{
3899
ParseResult result = tryParse(R"(
3900
local x = `{ { y{} } }`
3901
)");
3902
3903
REQUIRE_MESSAGE(result.errors.empty(), result.errors[0].getMessage());
3904
}
3905
3906
TEST_CASE_FIXTURE(Fixture, "parse_top_level_checked_fn")
3907
{
3908
ParseOptions opts;
3909
opts.allowDeclarationSyntax = true;
3910
3911
std::string src = R"BUILTIN_SRC(
3912
@checked declare function abs(n: number): number
3913
)BUILTIN_SRC";
3914
3915
ParseResult pr = tryParse(src, opts);
3916
LUAU_ASSERT(pr.errors.size() == 0);
3917
3918
LUAU_ASSERT(pr.root->body.size == 1);
3919
AstStat* root = *(pr.root->body.data);
3920
auto func = root->as<AstStatDeclareFunction>();
3921
LUAU_ASSERT(func);
3922
LUAU_ASSERT(func->isCheckedFunction());
3923
}
3924
3925
TEST_CASE_FIXTURE(Fixture, "parse_declared_table_checked_member")
3926
{
3927
ParseOptions opts;
3928
opts.allowDeclarationSyntax = true;
3929
3930
const std::string src = R"BUILTIN_SRC(
3931
declare math : {
3932
abs : @checked (number) -> number
3933
}
3934
)BUILTIN_SRC";
3935
3936
ParseResult pr = tryParse(src, opts);
3937
LUAU_ASSERT(pr.errors.size() == 0);
3938
3939
LUAU_ASSERT(pr.root->body.size == 1);
3940
AstStat* root = *(pr.root->body.data);
3941
auto glob = root->as<AstStatDeclareGlobal>();
3942
LUAU_ASSERT(glob);
3943
auto tbl = glob->type->as<AstTypeTable>();
3944
LUAU_ASSERT(tbl);
3945
LUAU_ASSERT(tbl->props.size == 1);
3946
auto prop = *tbl->props.data;
3947
auto func = prop.type->as<AstTypeFunction>();
3948
LUAU_ASSERT(func);
3949
LUAU_ASSERT(func->isCheckedFunction());
3950
}
3951
3952
TEST_CASE_FIXTURE(Fixture, "parse_checked_outside_decl_fails")
3953
{
3954
ParseOptions opts;
3955
opts.allowDeclarationSyntax = true;
3956
3957
ParseResult pr = tryParse(
3958
R"(
3959
local @checked = 3
3960
)",
3961
opts
3962
);
3963
LUAU_ASSERT(pr.errors.size() > 0);
3964
auto ts = pr.errors[1].getMessage();
3965
}
3966
3967
TEST_CASE_FIXTURE(Fixture, "parse_checked_in_and_out_of_decl_fails")
3968
{
3969
ParseOptions opts;
3970
opts.allowDeclarationSyntax = true;
3971
3972
auto pr = tryParse(
3973
R"(
3974
local @checked = 3
3975
@checked declare function abs(n: number): number
3976
)",
3977
opts
3978
);
3979
LUAU_ASSERT(pr.errors.size() == 2);
3980
LUAU_ASSERT(pr.errors[0].getLocation().begin.line == 1);
3981
LUAU_ASSERT(pr.errors[1].getLocation().begin.line == 1);
3982
}
3983
3984
TEST_CASE_FIXTURE(Fixture, "parse_checked_as_function_name_fails")
3985
{
3986
ParseOptions opts;
3987
opts.allowDeclarationSyntax = true;
3988
3989
auto pr = tryParse(
3990
R"(
3991
@checked function(x: number) : number
3992
end
3993
)",
3994
opts
3995
);
3996
LUAU_ASSERT(pr.errors.size() > 0);
3997
}
3998
3999
TEST_CASE_FIXTURE(Fixture, "cannot_use_@_as_variable_name")
4000
{
4001
ParseOptions opts;
4002
opts.allowDeclarationSyntax = true;
4003
4004
auto pr = tryParse(
4005
R"(
4006
local @blah = 3
4007
)",
4008
opts
4009
);
4010
4011
LUAU_ASSERT(pr.errors.size() > 0);
4012
}
4013
4014
TEST_CASE_FIXTURE(Fixture, "read_write_table_properties")
4015
{
4016
auto pr = tryParse(R"(
4017
type A = {read x: number}
4018
type B = {write x: number}
4019
type C = {read x: number, write x: number}
4020
type D = {read: () -> string}
4021
type E = {write: (string) -> ()}
4022
type F = {read read: () -> string}
4023
type G = {read write: (string) -> ()}
4024
4025
type H = {read ["A"]: number}
4026
type I = {write ["A"]: string}
4027
4028
type J = {read [number]: number}
4029
type K = {write [number]: string}
4030
)");
4031
4032
LUAU_ASSERT(pr.errors.size() == 0);
4033
}
4034
4035
void checkAttribute(const AstAttr* attr, const AstAttr::Type type, const Location& location)
4036
{
4037
CHECK_EQ(attr->type, type);
4038
CHECK_EQ(attr->location, location);
4039
}
4040
4041
void checkFirstErrorForAttributes(const std::vector<ParseError>& errors, const size_t minSize, const Location& location, const std::string& message)
4042
{
4043
LUAU_ASSERT(minSize >= 1);
4044
4045
CHECK_GE(errors.size(), minSize);
4046
CHECK_EQ(errors[0].getLocation(), location);
4047
CHECK_EQ(errors[0].getMessage(), message);
4048
}
4049
4050
TEST_CASE_FIXTURE(Fixture, "parse_attribute_on_function_stat")
4051
{
4052
4053
AstStatBlock* stat = parse(R"(
4054
@checked
4055
function hello(x, y)
4056
return x + y
4057
end)");
4058
4059
LUAU_ASSERT(stat != nullptr);
4060
4061
AstStatFunction* statFun = stat->body.data[0]->as<AstStatFunction>();
4062
LUAU_ASSERT(statFun != nullptr);
4063
4064
AstArray<AstAttr*> attributes = statFun->func->attributes;
4065
4066
CHECK_EQ(attributes.size, 1);
4067
4068
checkAttribute(attributes.data[0], AstAttr::Type::Checked, Location(Position(1, 0), Position(1, 8)));
4069
}
4070
4071
TEST_CASE_FIXTURE(Fixture, "parse_parametrized_attribute_on_function_stat")
4072
{
4073
AstStatBlock* stat = parse(R"(
4074
@[deprecated{ use = "greetng", reason = "Using <hello> is too causal"}]
4075
function hello(x, y)
4076
return x + y
4077
end)");
4078
4079
LUAU_ASSERT(stat != nullptr);
4080
4081
AstStatFunction* statFun = stat->body.data[0]->as<AstStatFunction>();
4082
LUAU_ASSERT(statFun != nullptr);
4083
4084
AstArray<AstAttr*> attributes = statFun->func->attributes;
4085
4086
CHECK_EQ(attributes.size, 1);
4087
4088
checkAttribute(attributes.data[0], AstAttr::Type::Deprecated, Location(Position(1, 2), Position(1, 70)));
4089
}
4090
4091
TEST_CASE_FIXTURE(Fixture, "non_literal_attribute_arguments_is_not_allowed")
4092
{
4093
ParseResult result = tryParse(R"(
4094
@[deprecated{ reason = reasonString }]
4095
function hello(x, y)
4096
return x + y
4097
end)");
4098
4099
checkFirstErrorForAttributes(
4100
result.errors, 1, Location(Position(1, 13), Position(1, 37)), "Only literals can be passed as arguments for attributes"
4101
);
4102
}
4103
4104
TEST_CASE_FIXTURE(Fixture, "unknown_arguments_for_depricated_is_not_allowed")
4105
{
4106
ParseResult result = tryParse(R"(
4107
@[deprecated({}, "Very deprecated")]
4108
function hello(x, y)
4109
return x + y
4110
end)");
4111
4112
checkFirstErrorForAttributes(result.errors, 1, Location(Position(1, 2), Position(1, 12)), "@deprecated can be parametrized only by 1 argument");
4113
4114
result = tryParse(R"(
4115
@[deprecated "Very deprecated"]
4116
function hello(x, y)
4117
return x + y
4118
end)");
4119
4120
checkFirstErrorForAttributes(result.errors, 1, Location(Position(1, 13), Position(1, 30)), "Unknown argument type for @deprecated");
4121
4122
result = tryParse(R"(
4123
@[deprecated{ foo = "bar" }]
4124
function hello(x, y)
4125
return x + y
4126
end)");
4127
4128
checkFirstErrorForAttributes(
4129
result.errors,
4130
1,
4131
Location(Position(1, 14), Position(1, 17)),
4132
"Unknown argument 'foo' for @deprecated. Only string constants for 'use' and 'reason' are allowed"
4133
);
4134
4135
result = tryParse(R"(
4136
@[deprecated{ use = 5 }]
4137
function hello(x, y)
4138
return x + y
4139
end)");
4140
4141
checkFirstErrorForAttributes(result.errors, 1, Location(Position(1, 20), Position(1, 21)), "Only constant string allowed as value for 'use'");
4142
}
4143
4144
TEST_CASE_FIXTURE(Fixture, "do_not_hang_on_incomplete_attribute_list")
4145
{
4146
ParseResult result = tryParse(R"(
4147
@[]
4148
function hello(x, y)
4149
return x + y
4150
end)");
4151
checkFirstErrorForAttributes(result.errors, 1, Location(Position(1, 0), Position(1, 3)), "Attribute list cannot be empty");
4152
4153
result = tryParse(R"(@[)");
4154
4155
checkFirstErrorForAttributes(
4156
result.errors, 1, Location(Position(0, 2), Position(0, 2)), "Expected identifier when parsing attribute name, got <eof>"
4157
);
4158
4159
result = tryParse(R"(@[
4160
function foo() end
4161
)");
4162
4163
checkFirstErrorForAttributes(
4164
result.errors, 1, Location(Position(1, 8), Position(1, 16)), "Expected identifier when parsing attribute name, got 'function'"
4165
);
4166
4167
result = tryParse(R"(@[deprecated
4168
local function foo() end
4169
)");
4170
4171
checkFirstErrorForAttributes(result.errors, 1, Location(Position(1, 8), Position(1, 13)), "Expected ']' (to close '@[' at line 1), got 'local'");
4172
}
4173
4174
TEST_CASE_FIXTURE(Fixture, "parse_attribute_for_function_expression")
4175
{
4176
AstStatBlock* stat1 = parse(R"(
4177
local function invoker(f)
4178
return f(1)
4179
end
4180
4181
invoker(@checked function(x) return (x + 2) end)
4182
)");
4183
4184
LUAU_ASSERT(stat1 != nullptr);
4185
4186
AstExprFunction* func1 = stat1->body.data[1]->as<AstStatExpr>()->expr->as<AstExprCall>()->args.data[0]->as<AstExprFunction>();
4187
LUAU_ASSERT(func1 != nullptr);
4188
4189
AstArray<AstAttr*> attributes1 = func1->attributes;
4190
4191
CHECK_EQ(attributes1.size, 1);
4192
4193
checkAttribute(attributes1.data[0], AstAttr::Type::Checked, Location(Position(5, 8), Position(5, 16)));
4194
4195
AstStatBlock* stat2 = parse(R"(
4196
local f = @checked function(x) return (x + 2) end
4197
)");
4198
4199
LUAU_ASSERT(stat2 != nullptr);
4200
4201
AstExprFunction* func2 = stat2->body.data[0]->as<AstStatLocal>()->values.data[0]->as<AstExprFunction>();
4202
LUAU_ASSERT(func2 != nullptr);
4203
4204
AstArray<AstAttr*> attributes2 = func2->attributes;
4205
4206
CHECK_EQ(attributes2.size, 1);
4207
4208
checkAttribute(attributes2.data[0], AstAttr::Type::Checked, Location(Position(1, 10), Position(1, 18)));
4209
}
4210
4211
TEST_CASE_FIXTURE(Fixture, "parse_attribute_on_local_function_stat")
4212
{
4213
AstStatBlock* stat = parse(R"(
4214
@checked
4215
local function hello(x, y)
4216
return x + y
4217
end)");
4218
4219
LUAU_ASSERT(stat != nullptr);
4220
4221
AstStatLocalFunction* statFun = stat->body.data[0]->as<AstStatLocalFunction>();
4222
LUAU_ASSERT(statFun != nullptr);
4223
4224
AstArray<AstAttr*> attributes = statFun->func->attributes;
4225
4226
CHECK_EQ(attributes.size, 1);
4227
4228
checkAttribute(attributes.data[0], AstAttr::Type::Checked, Location(Position(1, 4), Position(1, 12)));
4229
}
4230
4231
TEST_CASE_FIXTURE(Fixture, "parse_debugnoinline_on_local_function")
4232
{
4233
ScopedFastFlag noInline{FFlag::DebugLuauNoInline, true};
4234
AstStatBlock* stat = parse(R"(
4235
@debugnoinline
4236
local function hello(x, y)
4237
return x + y
4238
end)");
4239
4240
LUAU_ASSERT(stat != nullptr);
4241
4242
AstStatLocalFunction* statFun = stat->body.data[0]->as<AstStatLocalFunction>();
4243
LUAU_ASSERT(statFun != nullptr);
4244
4245
AstArray<AstAttr*> attributes = statFun->func->attributes;
4246
4247
CHECK_EQ(attributes.size, 1);
4248
4249
checkAttribute(attributes.data[0], AstAttr::Type::DebugNoinline, Location(Position(1, 4), Position(1, 18)));
4250
}
4251
4252
TEST_CASE_FIXTURE(Fixture, "debugnoinline_not_allowed_without_flag")
4253
{
4254
ParseResult result = tryParse(R"(
4255
@debugnoinline
4256
local function hello(x, y)
4257
return x + y
4258
end)");
4259
4260
checkFirstErrorForAttributes(result.errors, 1, Location(Position(1, 0), Position(1, 14)), "Invalid attribute '@debugnoinline'");
4261
}
4262
4263
TEST_CASE_FIXTURE(Fixture, "empty_attribute_name_is_not_allowed")
4264
{
4265
ParseResult result = tryParse(R"(
4266
@
4267
function hello(x, y)
4268
return x + y
4269
end)");
4270
4271
checkFirstErrorForAttributes(result.errors, 1, Location(Position(1, 0), Position(1, 1)), "Attribute name is missing");
4272
}
4273
4274
TEST_CASE_FIXTURE(Fixture, "dont_parse_attributes_on_non_function_stat")
4275
{
4276
ParseResult pr1 = tryParse(R"(
4277
@checked
4278
if a<0 then a = 0 end)");
4279
checkFirstErrorForAttributes(
4280
pr1.errors,
4281
1,
4282
Location(Position(2, 0), Position(2, 2)),
4283
FFlag::LuauConst2
4284
? "Expected 'function', 'local function', 'const function', 'declare function' or a function type declaration after attribute, but got "
4285
"'if' instead"
4286
: "Expected 'function', 'local function', 'declare function' or a function type declaration after attribute, but got 'if' instead"
4287
);
4288
4289
ParseResult pr2 = tryParse(R"(
4290
local i = 1
4291
@checked
4292
while a[i] do
4293
print(a[i])
4294
i = i + 1
4295
end)");
4296
checkFirstErrorForAttributes(
4297
pr2.errors,
4298
1,
4299
Location(Position(3, 0), Position(3, 5)),
4300
FFlag::LuauConst2
4301
? "Expected 'function', 'local function', 'const function', 'declare function' or a function type declaration after attribute, but got "
4302
"'while' instead"
4303
: "Expected 'function', 'local function', 'declare function' or a function type declaration after attribute, but got 'while' instead"
4304
);
4305
4306
ParseResult pr3 = tryParse(R"(
4307
@checked
4308
do
4309
local a2 = 2*a
4310
local d = sqrt(b^2 - 4*a*c)
4311
x1 = (-b + d)/a2
4312
x2 = (-b - d)/a2
4313
end)");
4314
checkFirstErrorForAttributes(
4315
pr3.errors,
4316
1,
4317
Location(Position(2, 0), Position(2, 2)),
4318
FFlag::LuauConst2
4319
? "Expected 'function', 'local function', 'const function', 'declare function' or a function type declaration after attribute, but got "
4320
"'do' instead"
4321
: "Expected 'function', 'local function', 'declare function' or a function type declaration after attribute, but got 'do' instead"
4322
);
4323
4324
ParseResult pr4 = tryParse(R"(
4325
@checked
4326
for i=1,10 do print(i) end
4327
)");
4328
checkFirstErrorForAttributes(
4329
pr4.errors,
4330
1,
4331
Location(Position(2, 0), Position(2, 3)),
4332
FFlag::LuauConst2
4333
? "Expected 'function', 'local function', 'const function', 'declare function' or a function type declaration after attribute, but got "
4334
"'for' instead"
4335
: "Expected 'function', 'local function', 'declare function' or a function type declaration after attribute, but got 'for' instead"
4336
);
4337
4338
ParseResult pr5 = tryParse(R"(
4339
@checked
4340
repeat
4341
line = io.read()
4342
until line ~= ""
4343
)");
4344
checkFirstErrorForAttributes(
4345
pr5.errors,
4346
1,
4347
Location(Position(2, 0), Position(2, 6)),
4348
FFlag::LuauConst2
4349
? "Expected 'function', 'local function', 'const function', 'declare function' or a function type declaration after attribute, but got "
4350
"'repeat' instead"
4351
: "Expected 'function', 'local function', 'declare function' or a function type declaration after attribute, but got 'repeat' instead"
4352
);
4353
4354
4355
ParseResult pr6 = tryParse(R"(
4356
@checked
4357
local x = 10
4358
)");
4359
checkFirstErrorForAttributes(
4360
pr6.errors, 1, Location(Position(2, 6), Position(2, 7)), "Expected 'function' after local declaration with attribute, but got 'x' instead"
4361
);
4362
4363
ParseResult pr7 = tryParse(R"(
4364
local i = 1
4365
while a[i] do
4366
if a[i] == v then @checked break end
4367
i = i + 1
4368
end
4369
)");
4370
checkFirstErrorForAttributes(
4371
pr7.errors,
4372
1,
4373
Location(Position(3, 31), Position(3, 36)),
4374
FFlag::LuauConst2
4375
? "Expected 'function', 'local function', 'const function', 'declare function' or a function type declaration after attribute, but got "
4376
"'break' instead"
4377
: "Expected 'function', 'local function', 'declare function' or a function type declaration after attribute, but got 'break' instead"
4378
);
4379
4380
4381
ParseResult pr8 = tryParse(R"(
4382
function foo1 () @checked return 'a' end
4383
)");
4384
checkFirstErrorForAttributes(
4385
pr8.errors,
4386
1,
4387
Location(Position(1, 26), Position(1, 32)),
4388
FFlag::LuauConst2
4389
? "Expected 'function', 'local function', 'const function', 'declare function' or a function type declaration after attribute, but got "
4390
"'return' instead"
4391
: "Expected 'function', 'local function', 'declare function' or a function type declaration after attribute, but got 'return' instead"
4392
);
4393
}
4394
4395
TEST_CASE_FIXTURE(Fixture, "dont_parse_attribute_on_argument_non_function")
4396
{
4397
ParseResult pr = tryParse(R"(
4398
local function invoker(f, y)
4399
return f(y)
4400
end
4401
4402
invoker(function(x) return (x + 2) end, @checked 1)
4403
)");
4404
4405
checkFirstErrorForAttributes(
4406
pr.errors, 1, Location(Position(5, 40), Position(5, 48)), "Expected 'function' declaration after attribute, but got '1' instead"
4407
);
4408
}
4409
4410
TEST_CASE_FIXTURE(Fixture, "parse_attribute_on_function_type_declaration")
4411
{
4412
ParseOptions opts;
4413
opts.allowDeclarationSyntax = true;
4414
4415
std::string src = R"(
4416
@checked declare function abs(n: number): number
4417
)";
4418
4419
ParseResult pr = tryParse(src, opts);
4420
CHECK_EQ(pr.errors.size(), 0);
4421
4422
LUAU_ASSERT(pr.root->body.size == 1);
4423
4424
AstStat* root = *(pr.root->body.data);
4425
4426
auto func = root->as<AstStatDeclareFunction>();
4427
LUAU_ASSERT(func != nullptr);
4428
4429
CHECK(func->isCheckedFunction());
4430
4431
AstArray<AstAttr*> attributes = func->attributes;
4432
4433
checkAttribute(attributes.data[0], AstAttr::Type::Checked, Location(Position(1, 0), Position(1, 8)));
4434
}
4435
4436
TEST_CASE_FIXTURE(Fixture, "parse_attributes_on_function_type_declaration_in_table")
4437
{
4438
ParseOptions opts;
4439
opts.allowDeclarationSyntax = true;
4440
4441
std::string src = R"(
4442
declare bit32: {
4443
band: @checked (...number) -> number
4444
})";
4445
4446
ParseResult pr = tryParse(src, opts);
4447
CHECK_EQ(pr.errors.size(), 0);
4448
4449
LUAU_ASSERT(pr.root->body.size == 1);
4450
4451
AstStat* root = *(pr.root->body.data);
4452
4453
AstStatDeclareGlobal* glob = root->as<AstStatDeclareGlobal>();
4454
LUAU_ASSERT(glob);
4455
4456
auto tbl = glob->type->as<AstTypeTable>();
4457
LUAU_ASSERT(tbl);
4458
4459
LUAU_ASSERT(tbl->props.size == 1);
4460
AstTableProp prop = tbl->props.data[0];
4461
4462
AstTypeFunction* func = prop.type->as<AstTypeFunction>();
4463
LUAU_ASSERT(func);
4464
4465
AstArray<AstAttr*> attributes = func->attributes;
4466
4467
CHECK_EQ(attributes.size, 1);
4468
checkAttribute(attributes.data[0], AstAttr::Type::Checked, Location(Position(2, 10), Position(2, 18)));
4469
}
4470
4471
TEST_CASE_FIXTURE(Fixture, "dont_parse_attributes_on_non_function_type_declarations")
4472
{
4473
ParseOptions opts;
4474
opts.allowDeclarationSyntax = true;
4475
4476
ParseResult pr1 = tryParse(
4477
R"(
4478
@checked declare foo: number
4479
)",
4480
opts
4481
);
4482
4483
checkFirstErrorForAttributes(
4484
pr1.errors, 1, Location(Position(1, 17), Position(1, 20)), "Expected a function type declaration after attribute, but got 'foo' instead"
4485
);
4486
4487
ParseResult pr2 = tryParse(
4488
R"(
4489
@checked declare class Foo
4490
prop: number
4491
function method(self, foo: number): string
4492
end)",
4493
opts
4494
);
4495
4496
checkFirstErrorForAttributes(
4497
pr2.errors, 1, Location(Position(1, 17), Position(1, 22)), "Expected a function type declaration after attribute, but got 'class' instead"
4498
);
4499
4500
ParseResult pr3 = tryParse(
4501
R"(
4502
declare bit32: {
4503
band: @checked number
4504
})",
4505
opts
4506
);
4507
4508
checkFirstErrorForAttributes(
4509
pr3.errors, 1, Location(Position(2, 19), Position(2, 25)), "Expected '(' when parsing function parameters, got 'number'"
4510
);
4511
}
4512
4513
TEST_CASE_FIXTURE(Fixture, "attributes_cannot_be_duplicated")
4514
{
4515
ParseResult result = tryParse(R"(
4516
@checked
4517
@checked
4518
function hello(x, y)
4519
return x + y
4520
end)");
4521
4522
checkFirstErrorForAttributes(result.errors, 1, Location(Position(2, 4), Position(2, 12)), "Cannot duplicate attribute '@checked'");
4523
}
4524
4525
TEST_CASE_FIXTURE(Fixture, "unsupported_attributes_are_not_allowed")
4526
{
4527
ParseResult result = tryParse(R"(
4528
@checked
4529
@cool_attribute
4530
function hello(x, y)
4531
return x + y
4532
end)");
4533
4534
checkFirstErrorForAttributes(result.errors, 1, Location(Position(2, 4), Position(2, 19)), "Invalid attribute '@cool_attribute'");
4535
}
4536
4537
TEST_CASE_FIXTURE(Fixture, "can_parse_leading_bar_unions_successfully")
4538
{
4539
parse(R"(type A = | "Hello" | "World")");
4540
}
4541
4542
TEST_CASE_FIXTURE(Fixture, "can_parse_leading_ampersand_intersections_successfully")
4543
{
4544
parse(R"(type A = & { string } & { number })");
4545
}
4546
4547
TEST_CASE_FIXTURE(Fixture, "mixed_leading_intersection_and_union_not_allowed")
4548
{
4549
matchParseError("type A = & number | string | boolean", "Mixing union and intersection types is not allowed; consider wrapping in parentheses.");
4550
matchParseError("type A = | number & string & boolean", "Mixing union and intersection types is not allowed; consider wrapping in parentheses.");
4551
}
4552
4553
TEST_CASE_FIXTURE(Fixture, "grouped_function_type")
4554
{
4555
const auto root = parse(R"(
4556
type X<T> = T
4557
local x: X<(() -> ())?>
4558
)");
4559
LUAU_ASSERT(root);
4560
CHECK_EQ(root->body.size, 2);
4561
auto assignment = root->body.data[1]->as<AstStatLocal>();
4562
LUAU_ASSERT(assignment);
4563
CHECK_EQ(assignment->vars.size, 1);
4564
CHECK_EQ(assignment->values.size, 0);
4565
auto binding = assignment->vars.data[0];
4566
CHECK_EQ(binding->name, "x");
4567
auto genericTy = binding->annotation->as<AstTypeReference>();
4568
LUAU_ASSERT(genericTy);
4569
CHECK_EQ(genericTy->parameters.size, 1);
4570
auto paramTy = genericTy->parameters.data[0];
4571
LUAU_ASSERT(paramTy.type);
4572
auto unionTy = paramTy.type->as<AstTypeUnion>();
4573
LUAU_ASSERT(unionTy);
4574
CHECK_EQ(unionTy->types.size, 2);
4575
auto groupTy = unionTy->types.data[0]->as<AstTypeGroup>(); // (() -> ())
4576
REQUIRE(groupTy);
4577
CHECK(groupTy->type->is<AstTypeFunction>()); // () -> ()
4578
CHECK(unionTy->types.data[1]->is<AstTypeOptional>()); // ?
4579
}
4580
4581
TEST_CASE_FIXTURE(Fixture, "complex_union_in_generic_ty")
4582
{
4583
const auto root = parse(R"(
4584
type X<T> = T
4585
local x: X<
4586
| number
4587
| boolean
4588
| string
4589
>
4590
)");
4591
LUAU_ASSERT(root);
4592
CHECK_EQ(root->body.size, 2);
4593
auto assignment = root->body.data[1]->as<AstStatLocal>();
4594
LUAU_ASSERT(assignment);
4595
CHECK_EQ(assignment->vars.size, 1);
4596
CHECK_EQ(assignment->values.size, 0);
4597
auto binding = assignment->vars.data[0];
4598
CHECK_EQ(binding->name, "x");
4599
auto genericTy = binding->annotation->as<AstTypeReference>();
4600
LUAU_ASSERT(genericTy);
4601
CHECK_EQ(genericTy->parameters.size, 1);
4602
auto paramTy = genericTy->parameters.data[0];
4603
LUAU_ASSERT(paramTy.type);
4604
auto unionTy = paramTy.type->as<AstTypeUnion>();
4605
LUAU_ASSERT(unionTy);
4606
CHECK_EQ(unionTy->types.size, 3);
4607
// NOTE: These are `const char*` so we can compare them to `AstName`s later.
4608
std::vector<const char*> expectedTypes{"number", "boolean", "string"};
4609
for (size_t i = 0; i < expectedTypes.size(); i++)
4610
{
4611
auto ty = unionTy->types.data[i]->as<AstTypeReference>();
4612
LUAU_ASSERT(ty);
4613
CHECK_EQ(ty->name, expectedTypes[i]);
4614
}
4615
}
4616
4617
TEST_CASE_FIXTURE(Fixture, "recover_from_bad_table_type")
4618
{
4619
ParseOptions opts;
4620
opts.allowDeclarationSyntax = true;
4621
const auto result = tryParse(
4622
R"(
4623
declare class Widget
4624
state: {string: function(string, Widget)}
4625
end
4626
)",
4627
opts
4628
);
4629
CHECK_EQ(result.errors.size(), 2);
4630
}
4631
4632
TEST_CASE_FIXTURE(Fixture, "function_name_has_correct_start_location")
4633
{
4634
AstStatBlock* block = parse(R"(
4635
function simple()
4636
end
4637
4638
function T:complex()
4639
end
4640
)");
4641
4642
REQUIRE_EQ(2, block->body.size);
4643
4644
const auto function1 = block->body.data[0]->as<AstStatFunction>();
4645
LUAU_ASSERT(function1);
4646
CHECK_EQ(Position{1, 17}, function1->name->location.begin);
4647
4648
const auto function2 = block->body.data[1]->as<AstStatFunction>();
4649
LUAU_ASSERT(function2);
4650
CHECK_EQ(Position{4, 17}, function2->name->location.begin);
4651
}
4652
4653
TEST_CASE_FIXTURE(Fixture, "stat_end_includes_semicolon_position")
4654
{
4655
AstStatBlock* block = parse(R"(
4656
local x = 1
4657
local y = 2;
4658
local z = 3 ;
4659
)");
4660
4661
REQUIRE_EQ(3, block->body.size);
4662
4663
const auto stat1 = block->body.data[0];
4664
LUAU_ASSERT(stat1);
4665
CHECK_FALSE(stat1->hasSemicolon);
4666
CHECK_EQ(Position{1, 19}, stat1->location.end);
4667
4668
const auto stat2 = block->body.data[1];
4669
LUAU_ASSERT(stat2);
4670
CHECK(stat2->hasSemicolon);
4671
CHECK_EQ(Position{2, 20}, stat2->location.end);
4672
4673
const auto stat3 = block->body.data[2];
4674
LUAU_ASSERT(stat3);
4675
CHECK(stat3->hasSemicolon);
4676
CHECK_EQ(Position{3, 22}, stat3->location.end);
4677
}
4678
4679
TEST_CASE_FIXTURE(Fixture, "parsing_type_suffix_for_return_type_with_variadic")
4680
{
4681
ScopedFastFlag sff{DFFlag::DebugLuauReportReturnTypeVariadicWithTypeSuffix, true};
4682
4683
ParseResult result = tryParse(R"(
4684
function foo(): (string, ...number) | boolean
4685
end
4686
)");
4687
4688
// TODO(CLI-140667): this should produce a ParseError in future when we fix the invalid syntax
4689
CHECK(result.errors.size() == 0);
4690
CHECK_EQ(luau_telemetry_parsed_return_type_variadic_with_type_suffix, true);
4691
}
4692
4693
TEST_CASE_FIXTURE(Fixture, "parsing_string_union_indexers")
4694
{
4695
parse(R"(type foo = { ["bar" | "baz"]: number })");
4696
}
4697
4698
TEST_CASE_FIXTURE(Fixture, "parsing_incomplete_string_interpolation_missing_curly_at_eof")
4699
{
4700
auto parseResult = tryParse(R"(print(`{e.x} {e.a)");
4701
const auto first = parseResult.root->body.data[0];
4702
auto expr = first->as<AstStatExpr>();
4703
CHECK(expr != nullptr);
4704
auto call = expr->expr->as<AstExprCall>();
4705
CHECK(call != nullptr);
4706
auto interpString = call->args.data[0]->as<AstExprInterpString>();
4707
CHECK(interpString != nullptr);
4708
CHECK(interpString->expressions.size == 2);
4709
CHECK(interpString->location.begin == Position(0, 6));
4710
CHECK(interpString->location.end == Position(0, 17));
4711
CHECK_EQ(parseResult.errors.size(), 2);
4712
4713
auto err = parseResult.errors[0];
4714
CHECK_EQ(err.getMessage(), "Malformed interpolated string; did you forget to add a '}'?");
4715
CHECK_EQ(err.getLocation(), Location({0, 16}, {0, 17}));
4716
}
4717
4718
TEST_CASE_FIXTURE(Fixture, "parsing_incomplete_string_interpolation_missing_backtick_at_eof")
4719
{
4720
auto parseResult = tryParse(R"(print(`{e.x} {e.a})");
4721
const auto first = parseResult.root->body.data[0];
4722
auto expr = first->as<AstStatExpr>();
4723
CHECK(expr != nullptr);
4724
auto call = expr->expr->as<AstExprCall>();
4725
CHECK(call != nullptr);
4726
auto interpString = call->args.data[0]->as<AstExprInterpString>();
4727
CHECK(interpString != nullptr);
4728
CHECK(interpString->expressions.size == 2);
4729
CHECK(interpString->location.begin == Position(0, 6));
4730
CHECK(interpString->location.end == Position(0, 18));
4731
CHECK_EQ(parseResult.errors.size(), 2);
4732
4733
auto err = parseResult.errors[0];
4734
CHECK_EQ(err.getMessage(), "Malformed interpolated string; did you forget to add a '`'?");
4735
CHECK_EQ(err.getLocation(), Location({0, 17}, {0, 18}));
4736
}
4737
4738
TEST_CASE_FIXTURE(Fixture, "parsing_incomplete_string_interpolation_missing_curly_with_backtick_at_eof")
4739
{
4740
auto parseResult = tryParse(R"(print(`{e.x} {e.a`)");
4741
const auto first = parseResult.root->body.data[0];
4742
auto expr = first->as<AstStatExpr>();
4743
CHECK(expr != nullptr);
4744
auto call = expr->expr->as<AstExprCall>();
4745
CHECK(call != nullptr);
4746
auto interpString = call->args.data[0]->as<AstExprInterpString>();
4747
CHECK(interpString != nullptr);
4748
CHECK(interpString->expressions.size == 2);
4749
CHECK(interpString->location.begin == Position(0, 6));
4750
CHECK(interpString->location.end == Position(0, 18));
4751
CHECK_EQ(parseResult.errors.size(), 2);
4752
4753
auto err = parseResult.errors[0];
4754
CHECK_EQ(err.getMessage(), "Malformed interpolated string; did you forget to add a '}'?");
4755
CHECK_EQ(err.getLocation(), Location({0, 17}, {0, 18}));
4756
}
4757
4758
TEST_CASE_FIXTURE(Fixture, "parsing_incomplete_string_interpolation_missing_curly_broken_string")
4759
{
4760
auto parseResult = tryParse(R"(print(`{e.x} {e.a
4761
)");
4762
const auto first = parseResult.root->body.data[0];
4763
auto expr = first->as<AstStatExpr>();
4764
CHECK(expr != nullptr);
4765
auto call = expr->expr->as<AstExprCall>();
4766
CHECK(call != nullptr);
4767
auto interpString = call->args.data[0]->as<AstExprInterpString>();
4768
CHECK(interpString != nullptr);
4769
CHECK(interpString->expressions.size == 2);
4770
CHECK(interpString->location.begin == Position(0, 6));
4771
CHECK(interpString->location.end == Position(0, 17));
4772
CHECK_EQ(parseResult.errors.size(), 2);
4773
4774
auto err = parseResult.errors[0];
4775
CHECK_EQ(err.getMessage(), "Malformed interpolated string; did you forget to add a '}'?");
4776
CHECK_EQ(err.getLocation(), Location({0, 16}, {0, 17}));
4777
}
4778
4779
TEST_CASE_FIXTURE(Fixture, "parsing_incomplete_string_interpolation_missing_backtick_broken_string")
4780
{
4781
auto parseResult = tryParse(R"(print(`{e.x} {e.a}
4782
)");
4783
const auto first = parseResult.root->body.data[0];
4784
auto expr = first->as<AstStatExpr>();
4785
CHECK(expr != nullptr);
4786
auto call = expr->expr->as<AstExprCall>();
4787
CHECK(call != nullptr);
4788
auto interpString = call->args.data[0]->as<AstExprInterpString>();
4789
CHECK(interpString != nullptr);
4790
CHECK(interpString->expressions.size == 2);
4791
CHECK(interpString->location.begin == Position(0, 6));
4792
CHECK(interpString->location.end == Position(0, 18));
4793
CHECK_EQ(parseResult.errors.size(), 2);
4794
4795
auto err = parseResult.errors[0];
4796
CHECK_EQ(err.getMessage(), "Malformed interpolated string; did you forget to add a '`'?");
4797
CHECK_EQ(err.getLocation(), Location({0, 17}, {0, 18}));
4798
}
4799
4800
TEST_CASE_FIXTURE(Fixture, "parsing_incomplete_string_interpolation_missing_curly_with_backtick_broken_string")
4801
{
4802
auto parseResult = tryParse(R"(print(`{e.x} {e.a`
4803
)");
4804
const auto first = parseResult.root->body.data[0];
4805
auto expr = first->as<AstStatExpr>();
4806
CHECK(expr != nullptr);
4807
auto call = expr->expr->as<AstExprCall>();
4808
CHECK(call != nullptr);
4809
auto interpString = call->args.data[0]->as<AstExprInterpString>();
4810
CHECK(interpString != nullptr);
4811
CHECK(interpString->expressions.size == 2);
4812
CHECK(interpString->location.begin == Position(0, 6));
4813
CHECK(interpString->location.end == Position(0, 18));
4814
CHECK_EQ(parseResult.errors.size(), 2);
4815
4816
auto err = parseResult.errors[0];
4817
CHECK_EQ(err.getMessage(), "Malformed interpolated string; did you forget to add a '}'?");
4818
CHECK_EQ(err.getLocation(), Location({0, 17}, {0, 18}));
4819
}
4820
4821
TEST_CASE_FIXTURE(Fixture, "parse_type_name")
4822
{
4823
std::string code = "<A>(A, string, boolean?) -> number";
4824
Allocator allocator;
4825
AstNameTable names{allocator};
4826
4827
ParseNodeResult<AstType> result = Parser::parseType(code.data(), code.size(), names, allocator);
4828
4829
REQUIRE(result.errors.empty());
4830
REQUIRE(result.root);
4831
4832
auto fun = result.root->as<AstTypeFunction>();
4833
REQUIRE(fun);
4834
REQUIRE(1 == fun->generics.size);
4835
REQUIRE(3 == fun->argTypes.types.size);
4836
4837
auto returnPack = fun->returnTypes->as<AstTypePackExplicit>();
4838
REQUIRE(returnPack);
4839
REQUIRE(1 == returnPack->typeList.types.size);
4840
}
4841
4842
TEST_CASE_FIXTURE(Fixture, "explicit_type_instantiation_errors")
4843
{
4844
matchParseError("local a = x:a<<T>>", "Expected '(', '{' or <string> when parsing function call, got <eof>");
4845
}
4846
4847
TEST_CASE_FIXTURE(Fixture, "extern_read_write_attributes")
4848
{
4849
ScopedFastFlag _[] = {
4850
{FFlag::DebugLuauForceOldSolver, false},
4851
{FFlag::LuauExternReadWriteAttributes, true}
4852
};
4853
4854
ParseResult result = tryParse(R"(
4855
declare extern type Foo with
4856
read ReadOnlyMember: string
4857
write WriteOnlyMember: number
4858
ReadWriteMember: vector
4859
wRITE BadAttributeMember: buffer
4860
end
4861
)");
4862
4863
REQUIRE_EQ(result.errors.size(), 1);
4864
CHECK_EQ(result.errors[0].getLocation().begin.line, 5);
4865
CHECK_EQ(result.errors[0].getMessage(), "Expected blank or 'read' or 'write' attribute, got 'wRITE'");
4866
4867
AstStatBlock* stat = result.root;
4868
4869
REQUIRE_EQ(stat->body.size, 1);
4870
4871
AstStatDeclareExternType* declaredExternType = stat->body.data[0]->as<AstStatDeclareExternType>();
4872
CHECK_EQ(declaredExternType->props.size, 4);
4873
4874
CHECK_EQ(declaredExternType->props.data[0].access, AstTableAccess::Read);
4875
CHECK_EQ(declaredExternType->props.data[1].access, AstTableAccess::Write);
4876
CHECK_EQ(declaredExternType->props.data[2].access, AstTableAccess::ReadWrite);
4877
CHECK_EQ(declaredExternType->props.data[3].access, AstTableAccess::ReadWrite);
4878
}
4879
4880
TEST_SUITE_END();
4881
4882