Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/Ast/src/Parser.cpp
2725 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 "Luau/Common.h"
5
#include "Luau/TimeTrace.h"
6
7
#include <algorithm>
8
9
#include <errno.h>
10
#include <limits.h>
11
#include <string.h>
12
13
LUAU_FASTINTVARIABLE(LuauRecursionLimit, 1000)
14
LUAU_FASTINTVARIABLE(LuauTypeLengthLimit, 1000)
15
LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100)
16
17
// Warning: If you are introducing new syntax, ensure that it is behind a separate
18
// flag so that we don't break production games by reverting syntax changes.
19
// See docs/SyntaxChanges.md for an explanation.
20
LUAU_FASTFLAGVARIABLE(LuauSolverV2)
21
LUAU_DYNAMIC_FASTFLAGVARIABLE(DebugLuauReportReturnTypeVariadicWithTypeSuffix, false)
22
LUAU_FASTFLAGVARIABLE(LuauIntegerType)
23
LUAU_FASTFLAGVARIABLE(DesugaredArrayTypeReferenceIsEmpty)
24
LUAU_FASTFLAGVARIABLE(LuauConst2)
25
LUAU_FASTFLAGVARIABLE(DebugLuauNoInline)
26
LUAU_FASTFLAGVARIABLE(LuauExternReadWriteAttributes)
27
28
// Clip with DebugLuauReportReturnTypeVariadicWithTypeSuffix
29
bool luau_telemetry_parsed_return_type_variadic_with_type_suffix = false;
30
31
namespace Luau
32
{
33
34
using AttributeArgumentsValidator = std::function<std::vector<std::pair<Location, std::string>>(Location, const AstArray<AstExpr*>&)>;
35
36
struct AttributeEntry
37
{
38
const char* name;
39
AstAttr::Type type;
40
std::optional<AttributeArgumentsValidator> argsValidator;
41
};
42
43
std::vector<std::pair<Location, std::string>> deprecatedArgsValidator(Location attrLoc, const AstArray<AstExpr*>& args)
44
{
45
46
if (args.size == 0)
47
return {};
48
if (args.size > 1)
49
return {{attrLoc, "@deprecated can be parametrized only by 1 argument"}};
50
51
if (!args.data[0]->is<AstExprTable>())
52
return {{args.data[0]->location, "Unknown argument type for @deprecated"}};
53
54
std::vector<std::pair<Location, std::string>> errors;
55
for (const AstExprTable::Item& item : args.data[0]->as<AstExprTable>()->items)
56
{
57
if (item.kind == AstExprTable::Item::Kind::Record)
58
{
59
AstArray<char> keyString = item.key->as<AstExprConstantString>()->value;
60
std::string key(keyString.data, keyString.size);
61
if (key != "use" && key != "reason")
62
{
63
errors.emplace_back(
64
item.key->location,
65
format("Unknown argument '%s' for @deprecated. Only string constants for 'use' and 'reason' are allowed", key.c_str())
66
);
67
}
68
else if (!item.value->is<AstExprConstantString>())
69
{
70
errors.emplace_back(item.value->location, format("Only constant string allowed as value for '%s'", key.c_str()));
71
}
72
}
73
else
74
{
75
errors.emplace_back(item.value->location, "Only constants keys 'use' and 'reason' are allowed for @deprecated attribute");
76
}
77
}
78
return errors;
79
}
80
81
AttributeEntry kAttributeEntries_DEPRECATED[] = {
82
{"@checked", AstAttr::Type::Checked, {}},
83
{"@native", AstAttr::Type::Native, {}},
84
{"@deprecated", AstAttr::Type::Deprecated, {}},
85
{nullptr, AstAttr::Type::Checked, {}}
86
};
87
88
AttributeEntry kAttributeEntries[] = {
89
{"checked", AstAttr::Type::Checked, {}},
90
{"native", AstAttr::Type::Native, {}},
91
{"deprecated", AstAttr::Type::Deprecated, deprecatedArgsValidator},
92
{nullptr, AstAttr::Type::Checked, {}}
93
};
94
95
std::pair<AttributeEntry, Luau::FValue<bool>&> kDebugAttributeEntries[] = {
96
{{"debugnoinline", AstAttr::Type::DebugNoinline, {}}, FFlag::DebugLuauNoInline},
97
};
98
99
ParseError::ParseError(const Location& location, std::string message)
100
: location(location)
101
, message(std::move(message))
102
{
103
}
104
105
const char* ParseError::what() const throw()
106
{
107
return message.c_str();
108
}
109
110
const Location& ParseError::getLocation() const
111
{
112
return location;
113
}
114
115
const std::string& ParseError::getMessage() const
116
{
117
return message;
118
}
119
120
// LUAU_NOINLINE is used to limit the stack cost of this function due to std::string object / exception plumbing
121
LUAU_NOINLINE void ParseError::raise(const Location& location, const char* format, ...)
122
{
123
va_list args;
124
va_start(args, format);
125
std::string message = vformat(format, args);
126
va_end(args);
127
128
throw ParseError(location, message);
129
}
130
131
ParseErrors::ParseErrors(std::vector<ParseError> errors)
132
: errors(std::move(errors))
133
{
134
LUAU_ASSERT(!this->errors.empty());
135
136
if (this->errors.size() == 1)
137
message = this->errors.front().what();
138
else
139
message = format("%d parse errors", int(this->errors.size()));
140
}
141
142
const char* ParseErrors::what() const throw()
143
{
144
return message.c_str();
145
}
146
147
const std::vector<ParseError>& ParseErrors::getErrors() const
148
{
149
return errors;
150
}
151
152
template<typename T>
153
TempVector<T>::TempVector(std::vector<T>& storage)
154
: storage(storage)
155
, offset(storage.size())
156
, size_(0)
157
{
158
}
159
160
template<typename T>
161
TempVector<T>::~TempVector()
162
{
163
LUAU_ASSERT(storage.size() == offset + size_);
164
storage.erase(storage.begin() + offset, storage.end());
165
}
166
167
template<typename T>
168
const T& TempVector<T>::operator[](size_t index) const
169
{
170
LUAU_ASSERT(index < size_);
171
return storage[offset + index];
172
}
173
174
template<typename T>
175
const T& TempVector<T>::front() const
176
{
177
LUAU_ASSERT(size_ > 0);
178
return storage[offset];
179
}
180
181
template<typename T>
182
const T& TempVector<T>::back() const
183
{
184
LUAU_ASSERT(size_ > 0);
185
return storage.back();
186
}
187
188
template<typename T>
189
bool TempVector<T>::empty() const
190
{
191
return size_ == 0;
192
}
193
194
template<typename T>
195
size_t TempVector<T>::size() const
196
{
197
return size_;
198
}
199
200
template<typename T>
201
void TempVector<T>::push_back(const T& item)
202
{
203
LUAU_ASSERT(storage.size() == offset + size_);
204
storage.push_back(item);
205
size_++;
206
}
207
208
static bool shouldParseTypePack(Lexer& lexer)
209
{
210
if (lexer.current().type == Lexeme::Dot3)
211
return true;
212
else if (lexer.current().type == Lexeme::Name && lexer.lookahead().type == Lexeme::Dot3)
213
return true;
214
215
return false;
216
}
217
218
ParseResult Parser::parse(const char* buffer, size_t bufferSize, AstNameTable& names, Allocator& allocator, ParseOptions options)
219
{
220
LUAU_TIMETRACE_SCOPE("Parser::parse", "Parser");
221
222
Parser p(buffer, bufferSize, names, allocator, options);
223
224
try
225
{
226
AstStatBlock* root = p.parseChunk();
227
size_t lines = p.lexer.current().location.end.line + (bufferSize > 0 && buffer[bufferSize - 1] != '\n');
228
229
return ParseResult{root, lines, std::move(p.hotcomments), std::move(p.parseErrors), std::move(p.commentLocations), std::move(p.cstNodeMap)};
230
}
231
catch (ParseError& err)
232
{
233
// when catching a fatal error, append it to the list of non-fatal errors and return
234
p.parseErrors.push_back(err);
235
236
return ParseResult{nullptr, 0, {}, p.parseErrors, {}, std::move(p.cstNodeMap)};
237
}
238
}
239
240
template<typename Node, typename F>
241
ParseNodeResult<Node> Parser::runParse(const char* buffer, size_t bufferSize, AstNameTable& names, Allocator& allocator, ParseOptions options, F f)
242
{
243
LUAU_TIMETRACE_SCOPE("Parser::parse", "Parser");
244
245
Parser p(buffer, bufferSize, names, allocator, options);
246
247
try
248
{
249
Node* expr = f(p);
250
size_t lines = p.lexer.current().location.end.line + (bufferSize > 0 && buffer[bufferSize - 1] != '\n');
251
252
Lexeme eof = p.lexer.next();
253
if (eof.type != Lexeme::Eof)
254
{
255
expr = nullptr;
256
p.parseErrors.emplace_back(eof.location, "Expected end of file");
257
}
258
259
return ParseNodeResult<Node>{
260
expr, lines, std::move(p.hotcomments), std::move(p.parseErrors), std::move(p.commentLocations), std::move(p.cstNodeMap)
261
};
262
}
263
catch (ParseError& err)
264
{
265
// when catching a fatal error, append it to the list of non-fatal errors and return
266
p.parseErrors.push_back(err);
267
268
return ParseNodeResult<Node>{nullptr, 0, {}, p.parseErrors, {}, std::move(p.cstNodeMap)};
269
}
270
}
271
272
ParseNodeResult<AstExpr> Parser::parseExpr(const char* buffer, size_t bufferSize, AstNameTable& names, Allocator& allocator, ParseOptions options)
273
{
274
return Parser::runParse<AstExpr>(
275
buffer,
276
bufferSize,
277
names,
278
allocator,
279
std::move(options),
280
[](auto&& parser)
281
{
282
return parser.parseExpr();
283
}
284
);
285
}
286
287
ParseNodeResult<AstType> Parser::parseType(const char* buffer, size_t bufferSize, AstNameTable& names, Allocator& allocator, ParseOptions options)
288
{
289
return Parser::runParse<AstType>(
290
buffer,
291
bufferSize,
292
names,
293
allocator,
294
std::move(options),
295
[](auto&& parser)
296
{
297
return parser.parseType();
298
}
299
);
300
}
301
302
Parser::Parser(const char* buffer, size_t bufferSize, AstNameTable& names, Allocator& allocator, const ParseOptions& options)
303
: options(options)
304
, lexer(buffer, bufferSize, names, options.parseFragment ? options.parseFragment->resumePosition : Position(0, 0))
305
, allocator(allocator)
306
, recursionCounter(0)
307
, endMismatchSuspect(Lexeme(Location(), Lexeme::Eof))
308
, localMap(AstName())
309
, cstNodeMap(nullptr)
310
{
311
Function top;
312
top.vararg = true;
313
314
functionStack.reserve(8);
315
functionStack.push_back(top);
316
317
nameSelf = names.getOrAdd("self");
318
nameNumber = names.getOrAdd("number");
319
nameError = names.getOrAdd(kParseNameError);
320
nameNil = names.getOrAdd("nil"); // nil is a reserved keyword
321
322
matchRecoveryStopOnToken.assign(Lexeme::Type::Reserved_END, 0);
323
matchRecoveryStopOnToken[Lexeme::Type::Eof] = 1;
324
325
// required for lookahead() to work across a comment boundary and for nextLexeme() to work when captureComments is false
326
lexer.setSkipComments(true);
327
328
// read first lexeme (any hot comments get .header = true)
329
LUAU_ASSERT(hotcommentHeader);
330
nextLexeme();
331
332
// all hot comments parsed after the first non-comment lexeme are special in that they don't affect type checking / linting mode
333
hotcommentHeader = false;
334
335
// preallocate some buffers that are very likely to grow anyway; this works around std::vector's inefficient growth policy for small arrays
336
localStack.reserve(16);
337
scratchStat.reserve(16);
338
scratchExpr.reserve(16);
339
scratchLocal.reserve(16);
340
scratchBinding.reserve(16);
341
342
if (options.parseFragment)
343
{
344
localMap = options.parseFragment->localMap;
345
localStack = options.parseFragment->localStack;
346
}
347
}
348
349
bool Parser::blockFollow(const Lexeme& l)
350
{
351
return l.type == Lexeme::Eof || l.type == Lexeme::ReservedElse || l.type == Lexeme::ReservedElseif || l.type == Lexeme::ReservedEnd ||
352
l.type == Lexeme::ReservedUntil;
353
}
354
355
AstStatBlock* Parser::parseChunk()
356
{
357
AstStatBlock* result = parseBlock();
358
359
if (lexer.current().type != Lexeme::Eof)
360
expectAndConsumeFail(Lexeme::Eof, nullptr);
361
362
return result;
363
}
364
365
// chunk ::= {stat [`;']} [laststat [`;']]
366
// block ::= chunk
367
AstStatBlock* Parser::parseBlock()
368
{
369
unsigned int localsBegin = saveLocals();
370
371
AstStatBlock* result = parseBlockNoScope();
372
373
restoreLocals(localsBegin);
374
375
return result;
376
}
377
378
static bool isStatLast(AstStat* stat)
379
{
380
return stat->is<AstStatBreak>() || stat->is<AstStatContinue>() || stat->is<AstStatReturn>();
381
}
382
383
AstStatBlock* Parser::parseBlockNoScope()
384
{
385
TempVector<AstStat*> body(scratchStat);
386
387
const Position prevPosition = lexer.previousLocation().end;
388
389
while (!blockFollow(lexer.current()))
390
{
391
unsigned int oldRecursionCount = recursionCounter;
392
393
incrementRecursionCounter("block");
394
395
AstStat* stat = parseStat();
396
397
recursionCounter = oldRecursionCount;
398
399
if (lexer.current().type == ';')
400
{
401
nextLexeme();
402
stat->hasSemicolon = true;
403
stat->location.end = lexer.previousLocation().end;
404
}
405
406
body.push_back(stat);
407
408
if (isStatLast(stat))
409
break;
410
}
411
412
const Location location = Location(prevPosition, lexer.current().location.begin);
413
414
return allocator.alloc<AstStatBlock>(location, copy(body));
415
}
416
417
// stat ::=
418
// varlist `=' explist |
419
// functioncall |
420
// do block end |
421
// while exp do block end |
422
// repeat block until exp |
423
// if exp then block {elseif exp then block} [else block] end |
424
// for binding `=' exp `,' exp [`,' exp] do block end |
425
// for namelist in explist do block end |
426
// function funcname funcbody |
427
// attributes function funcname funcbody |
428
// local function Name funcbody |
429
// local attributes function Name funcbody |
430
// local namelist [`=' explist]
431
// laststat ::= return [explist] | break
432
AstStat* Parser::parseStat()
433
{
434
// guess the type from the token type
435
switch (lexer.current().type)
436
{
437
case Lexeme::ReservedIf:
438
return parseIf();
439
case Lexeme::ReservedWhile:
440
return parseWhile();
441
case Lexeme::ReservedDo:
442
return parseDo();
443
case Lexeme::ReservedFor:
444
return parseFor();
445
case Lexeme::ReservedRepeat:
446
return parseRepeat();
447
case Lexeme::ReservedFunction:
448
return parseFunctionStat(AstArray<AstAttr*>({nullptr, 0}));
449
case Lexeme::ReservedLocal:
450
if (FFlag::LuauConst2)
451
{
452
Location start = lexer.current().location;
453
return parseLocal(start, start.begin, {nullptr, 0}, false);
454
}
455
else
456
return parseLocal_DEPRECATED(AstArray<AstAttr*>({nullptr, 0}));
457
case Lexeme::ReservedReturn:
458
return parseReturn();
459
case Lexeme::ReservedBreak:
460
return parseBreak();
461
case Lexeme::Attribute:
462
case Lexeme::AttributeOpen:
463
return parseAttributeStat();
464
default:;
465
}
466
467
Location start = lexer.current().location;
468
469
// we need to disambiguate a few cases, primarily assignment (lvalue = ...) vs statements-that-are calls
470
AstExpr* expr = parsePrimaryExpr(/* asStatement= */ true);
471
472
if (expr->is<AstExprCall>())
473
return allocator.alloc<AstStatExpr>(expr->location, expr);
474
475
// if the next token is , or =, it's an assignment (, means it's an assignment with multiple variables)
476
if (lexer.current().type == ',' || lexer.current().type == '=')
477
return parseAssignment(expr);
478
479
// if the next token is a compound assignment operator, it's a compound assignment (these don't support multiple variables)
480
if (std::optional<AstExprBinary::Op> op = parseCompoundOp(lexer.current()))
481
return parseCompoundAssignment(expr, *op);
482
483
// we know this isn't a call or an assignment; therefore it must be a context-sensitive keyword such as `type` or `continue`
484
AstName ident = getIdentifier(expr);
485
486
if (ident == "type")
487
return parseTypeAlias(expr->location, /* exported= */ false, expr->location.begin);
488
489
if (ident == "export" && lexer.current().type == Lexeme::Name && AstName(lexer.current().name) == "type")
490
{
491
Position typeKeywordPosition = lexer.current().location.begin;
492
nextLexeme();
493
return parseTypeAlias(expr->location, /* exported= */ true, typeKeywordPosition);
494
}
495
496
if (ident == "continue")
497
return parseContinue(expr->location);
498
499
if (FFlag::LuauConst2 && ident == "const")
500
return parseLocal(expr->location, expr->location.begin, AstArray<AstAttr*>({nullptr, 0}), true);
501
502
if (options.allowDeclarationSyntax)
503
{
504
if (ident == "declare")
505
return parseDeclaration(expr->location, AstArray<AstAttr*>({nullptr, 0}));
506
}
507
508
// skip unexpected symbol if lexer couldn't advance at all (statements are parsed in a loop)
509
if (start == lexer.current().location)
510
nextLexeme();
511
512
return reportStatError(expr->location, copy({expr}), {}, "Incomplete statement: expected assignment or a function call");
513
}
514
515
// if exp then block {elseif exp then block} [else block] end
516
AstStat* Parser::parseIf()
517
{
518
Location start = lexer.current().location;
519
520
nextLexeme(); // if / elseif
521
522
AstExpr* cond = parseExpr();
523
524
Lexeme matchThen = lexer.current();
525
std::optional<Location> thenLocation;
526
if (expectAndConsume(Lexeme::ReservedThen, "if statement"))
527
thenLocation = matchThen.location;
528
529
AstStatBlock* thenbody = parseBlock();
530
531
AstStat* elsebody = nullptr;
532
Location end = start;
533
std::optional<Location> elseLocation;
534
535
if (lexer.current().type == Lexeme::ReservedElseif)
536
{
537
thenbody->hasEnd = true;
538
unsigned int oldRecursionCount = recursionCounter;
539
incrementRecursionCounter("elseif");
540
elseLocation = lexer.current().location;
541
elsebody = parseIf();
542
end = elsebody->location;
543
recursionCounter = oldRecursionCount;
544
}
545
else
546
{
547
Lexeme matchThenElse = matchThen;
548
549
if (lexer.current().type == Lexeme::ReservedElse)
550
{
551
thenbody->hasEnd = true;
552
elseLocation = lexer.current().location;
553
matchThenElse = lexer.current();
554
nextLexeme();
555
556
elsebody = parseBlock();
557
elsebody->location.begin = matchThenElse.location.end;
558
}
559
560
end = lexer.current().location;
561
562
bool hasEnd = expectMatchEndAndConsume(Lexeme::ReservedEnd, matchThenElse);
563
564
if (elsebody)
565
{
566
if (AstStatBlock* elseBlock = elsebody->as<AstStatBlock>())
567
elseBlock->hasEnd = hasEnd;
568
}
569
else
570
thenbody->hasEnd = hasEnd;
571
}
572
573
return allocator.alloc<AstStatIf>(Location(start, end), cond, thenbody, elsebody, thenLocation, elseLocation);
574
}
575
576
// while exp do block end
577
AstStat* Parser::parseWhile()
578
{
579
Location start = lexer.current().location;
580
581
nextLexeme(); // while
582
583
AstExpr* cond = parseExpr();
584
585
Lexeme matchDo = lexer.current();
586
bool hasDo = expectAndConsume(Lexeme::ReservedDo, "while loop");
587
588
functionStack.back().loopDepth++;
589
590
AstStatBlock* body = parseBlock();
591
592
functionStack.back().loopDepth--;
593
594
Location end = lexer.current().location;
595
596
bool hasEnd = expectMatchEndAndConsume(Lexeme::ReservedEnd, matchDo);
597
body->hasEnd = hasEnd;
598
599
return allocator.alloc<AstStatWhile>(Location(start, end), cond, body, hasDo, matchDo.location);
600
}
601
602
// repeat block until exp
603
AstStat* Parser::parseRepeat()
604
{
605
Location start = lexer.current().location;
606
607
Lexeme matchRepeat = lexer.current();
608
nextLexeme(); // repeat
609
610
unsigned int localsBegin = saveLocals();
611
612
functionStack.back().loopDepth++;
613
614
AstStatBlock* body = parseBlockNoScope();
615
616
functionStack.back().loopDepth--;
617
618
Position untilPosition = lexer.current().location.begin;
619
bool hasUntil = expectMatchEndAndConsume(Lexeme::ReservedUntil, matchRepeat);
620
body->hasEnd = hasUntil;
621
622
AstExpr* cond = parseExpr();
623
624
restoreLocals(localsBegin);
625
626
AstStatRepeat* node = allocator.alloc<AstStatRepeat>(Location(start, cond->location), cond, body, hasUntil);
627
if (options.storeCstData)
628
cstNodeMap[node] = allocator.alloc<CstStatRepeat>(untilPosition);
629
return node;
630
}
631
632
// do block end
633
AstStat* Parser::parseDo()
634
{
635
Location start = lexer.current().location;
636
637
Lexeme matchDo = lexer.current();
638
nextLexeme(); // do
639
640
std::optional<Location> statsStart = options.storeCstData ? std::optional{lexer.current().location} : std::nullopt;
641
642
AstStatBlock* body = parseBlock();
643
644
body->location.begin = start.begin;
645
646
Location endLocation = lexer.current().location;
647
body->hasEnd = expectMatchEndAndConsume(Lexeme::ReservedEnd, matchDo);
648
if (body->hasEnd)
649
body->location.end = endLocation.end;
650
651
if (options.storeCstData)
652
{
653
LUAU_ASSERT(statsStart);
654
cstNodeMap[body] = allocator.alloc<CstStatDo>(statsStart->begin, endLocation.begin);
655
}
656
657
return body;
658
}
659
660
// break
661
AstStat* Parser::parseBreak()
662
{
663
Location start = lexer.current().location;
664
665
nextLexeme(); // break
666
667
if (functionStack.back().loopDepth == 0)
668
return reportStatError(start, {}, copy<AstStat*>({allocator.alloc<AstStatBreak>(start)}), "break statement must be inside a loop");
669
670
return allocator.alloc<AstStatBreak>(start);
671
}
672
673
// continue
674
AstStat* Parser::parseContinue(const Location& start)
675
{
676
if (functionStack.back().loopDepth == 0)
677
return reportStatError(start, {}, copy<AstStat*>({allocator.alloc<AstStatContinue>(start)}), "continue statement must be inside a loop");
678
679
// note: the token is already parsed for us!
680
681
return allocator.alloc<AstStatContinue>(start);
682
}
683
684
// for binding `=' exp `,' exp [`,' exp] do block end |
685
// for bindinglist in explist do block end |
686
AstStat* Parser::parseFor()
687
{
688
Location start = lexer.current().location;
689
690
nextLexeme(); // for
691
692
Binding varname = parseBinding();
693
694
if (lexer.current().type == '=')
695
{
696
Position equalsPosition = lexer.current().location.begin;
697
nextLexeme();
698
699
AstExpr* from = parseExpr();
700
701
Position endCommaPosition = lexer.current().location.begin;
702
expectAndConsume(',', "index range");
703
704
AstExpr* to = parseExpr();
705
706
std::optional<Position> stepCommaPosition = std::nullopt;
707
AstExpr* step = nullptr;
708
709
if (lexer.current().type == ',')
710
{
711
stepCommaPosition = lexer.current().location.begin;
712
nextLexeme();
713
714
step = parseExpr();
715
}
716
717
Lexeme matchDo = lexer.current();
718
bool hasDo = expectAndConsume(Lexeme::ReservedDo, "for loop");
719
720
unsigned int localsBegin = saveLocals();
721
722
functionStack.back().loopDepth++;
723
724
AstLocal* var = pushLocal(varname);
725
726
AstStatBlock* body = parseBlock();
727
728
functionStack.back().loopDepth--;
729
730
restoreLocals(localsBegin);
731
732
Location end = lexer.current().location;
733
734
bool hasEnd = expectMatchEndAndConsume(Lexeme::ReservedEnd, matchDo);
735
body->hasEnd = hasEnd;
736
737
AstStatFor* node = allocator.alloc<AstStatFor>(Location(start, end), var, from, to, step, body, hasDo, matchDo.location);
738
if (options.storeCstData)
739
cstNodeMap[node] = allocator.alloc<CstStatFor>(varname.colonPosition, equalsPosition, endCommaPosition, stepCommaPosition);
740
741
return node;
742
}
743
else
744
{
745
TempVector<Binding> names(scratchBinding);
746
AstArray<Position> varsCommaPosition;
747
names.push_back(varname);
748
749
if (lexer.current().type == ',')
750
{
751
if (options.storeCstData)
752
{
753
Position initialCommaPosition = lexer.current().location.begin;
754
nextLexeme();
755
parseBindingList(names, false, &varsCommaPosition, &initialCommaPosition);
756
}
757
else
758
{
759
nextLexeme();
760
761
parseBindingList(names);
762
}
763
}
764
765
Location inLocation = lexer.current().location;
766
bool hasIn = expectAndConsume(Lexeme::ReservedIn, "for loop");
767
768
TempVector<AstExpr*> values(scratchExpr);
769
TempVector<Position> valuesCommaPositions(scratchPosition);
770
parseExprList(values, options.storeCstData ? &valuesCommaPositions : nullptr);
771
772
Lexeme matchDo = lexer.current();
773
bool hasDo = expectAndConsume(Lexeme::ReservedDo, "for loop");
774
775
unsigned int localsBegin = saveLocals();
776
777
functionStack.back().loopDepth++;
778
779
TempVector<AstLocal*> vars(scratchLocal);
780
781
for (size_t i = 0; i < names.size(); ++i)
782
vars.push_back(pushLocal(names[i]));
783
784
AstStatBlock* body = parseBlock();
785
786
functionStack.back().loopDepth--;
787
788
restoreLocals(localsBegin);
789
790
Location end = lexer.current().location;
791
792
bool hasEnd = expectMatchEndAndConsume(Lexeme::ReservedEnd, matchDo);
793
body->hasEnd = hasEnd;
794
795
AstStatForIn* node =
796
allocator.alloc<AstStatForIn>(Location(start, end), copy(vars), copy(values), body, hasIn, inLocation, hasDo, matchDo.location);
797
if (options.storeCstData)
798
{
799
cstNodeMap[node] = allocator.alloc<CstStatForIn>(extractAnnotationColonPositions(names), varsCommaPosition, copy(valuesCommaPositions));
800
}
801
return node;
802
}
803
}
804
805
// funcname ::= Name {`.' Name} [`:' Name]
806
AstExpr* Parser::parseFunctionName(bool& hasself, AstName& debugname)
807
{
808
if (lexer.current().type == Lexeme::Name)
809
debugname = AstName(lexer.current().name);
810
811
// parse funcname into a chain of indexing operators
812
AstExpr* expr = parseNameExpr("function name");
813
814
unsigned int oldRecursionCount = recursionCounter;
815
816
while (lexer.current().type == '.')
817
{
818
Position opPosition = lexer.current().location.begin;
819
nextLexeme();
820
821
Name name = parseName("field name");
822
823
// while we could concatenate the name chain, for now let's just write the short name
824
debugname = name.name;
825
826
expr = allocator.alloc<AstExprIndexName>(Location(expr->location, name.location), expr, name.name, name.location, opPosition, '.');
827
828
// note: while the parser isn't recursive here, we're generating recursive structures of unbounded depth
829
incrementRecursionCounter("function name");
830
}
831
832
recursionCounter = oldRecursionCount;
833
834
// finish with :
835
if (lexer.current().type == ':')
836
{
837
Position opPosition = lexer.current().location.begin;
838
nextLexeme();
839
840
Name name = parseName("method name");
841
842
// while we could concatenate the name chain, for now let's just write the short name
843
debugname = name.name;
844
845
expr = allocator.alloc<AstExprIndexName>(Location(expr->location, name.location), expr, name.name, name.location, opPosition, ':');
846
847
hasself = true;
848
}
849
850
return expr;
851
}
852
853
static bool isExprLValue(AstExpr* expr)
854
{
855
return (expr->is<AstExprLocal>() && (!FFlag::LuauConst2 || !expr->as<AstExprLocal>()->local->isConst)) || expr->is<AstExprGlobal>() ||
856
expr->is<AstExprIndexExpr>() || expr->is<AstExprIndexName>();
857
}
858
859
// function funcname funcbody
860
AstStat* Parser::parseFunctionStat(const AstArray<AstAttr*>& attributes)
861
{
862
Location start = lexer.current().location;
863
864
if (attributes.size > 0)
865
start = attributes.data[0]->location;
866
867
Lexeme matchFunction = lexer.current();
868
nextLexeme();
869
870
bool hasself = false;
871
AstName debugname;
872
AstExpr* expr = parseFunctionName(hasself, debugname);
873
874
if (FFlag::LuauConst2 && !isExprLValue(expr))
875
{
876
expr = reportExprError(expr->location, copy({expr}), "Assigned expression must be a variable or a field");
877
}
878
879
matchRecoveryStopOnToken[Lexeme::ReservedEnd]++;
880
881
AstExprFunction* body = parseFunctionBody(hasself, matchFunction, debugname, nullptr, attributes).first;
882
883
matchRecoveryStopOnToken[Lexeme::ReservedEnd]--;
884
885
AstStatFunction* node = allocator.alloc<AstStatFunction>(Location(start, body->location), expr, body);
886
if (options.storeCstData)
887
cstNodeMap[node] = allocator.alloc<CstStatFunction>(matchFunction.location.begin);
888
return node;
889
}
890
891
std::optional<AstAttr::Type> Parser::validateAttribute(
892
Location loc,
893
const char* attributeName,
894
const TempVector<AstAttr*>& attributes,
895
const AstArray<AstExpr*>& args
896
)
897
{
898
// check if the attribute name is valid
899
std::optional<AstAttr::Type> type;
900
std::optional<AttributeArgumentsValidator> argsValidator;
901
902
for (int i = 0; kAttributeEntries[i].name; ++i)
903
{
904
if (strcmp(attributeName, kAttributeEntries[i].name) == 0)
905
{
906
type = kAttributeEntries[i].type;
907
argsValidator = kAttributeEntries[i].argsValidator;
908
break;
909
}
910
}
911
912
for (const auto& [attributeEntry, fflagBool] : kDebugAttributeEntries)
913
{
914
if (fflagBool && strcmp(attributeName, attributeEntry.name) == 0)
915
{
916
type = attributeEntry.type;
917
argsValidator = attributeEntry.argsValidator;
918
break;
919
}
920
}
921
922
if (!type)
923
{
924
if (strlen(attributeName) == 0)
925
report(loc, "Attribute name is missing");
926
else
927
report(loc, "Invalid attribute '@%s'", attributeName);
928
}
929
else
930
{
931
// check that attribute is not duplicated
932
for (const AstAttr* attr : attributes)
933
{
934
if (attr->type == *type)
935
report(loc, "Cannot duplicate attribute '@%s'", attributeName);
936
}
937
if (argsValidator)
938
{
939
auto errorsToReport = (*argsValidator)(loc, args);
940
for (const auto& [errorLoc, msg] : errorsToReport)
941
{
942
report(errorLoc, "%s", msg.c_str());
943
}
944
}
945
}
946
947
return type;
948
}
949
950
// attribute ::= '@' NAME
951
void Parser::parseAttribute(TempVector<AstAttr*>& attributes)
952
{
953
AstArray<AstExpr*> empty;
954
955
LUAU_ASSERT(lexer.current().type == Lexeme::Type::Attribute || lexer.current().type == Lexeme::Type::AttributeOpen);
956
957
if (lexer.current().type == Lexeme::Type::Attribute)
958
{
959
Location loc = lexer.current().location;
960
961
const char* name = lexer.current().name;
962
std::optional<AstAttr::Type> type = validateAttribute(loc, name, attributes, empty);
963
964
nextLexeme();
965
966
attributes.push_back(allocator.alloc<AstAttr>(loc, type.value_or(AstAttr::Type::Unknown), empty, AstName(name)));
967
}
968
else
969
{
970
Lexeme open = lexer.current();
971
nextLexeme();
972
973
if (lexer.current().type != ']')
974
{
975
while (true)
976
{
977
Name name = parseName("attribute name");
978
979
Location nameLoc = name.location;
980
const char* attrName = name.name.value;
981
982
if (lexer.current().type == Lexeme::RawString || lexer.current().type == Lexeme::QuotedString || lexer.current().type == '{' ||
983
lexer.current().type == '(')
984
{
985
986
auto [args, argsLocation, _exprLocation] = parseCallList(nullptr);
987
988
for (const AstExpr* arg : args)
989
{
990
if (!isConstantLiteral(arg) && !isLiteralTable(arg))
991
report(argsLocation, "Only literals can be passed as arguments for attributes");
992
}
993
994
std::optional<AstAttr::Type> type = validateAttribute(nameLoc, attrName, attributes, args);
995
996
attributes.push_back(
997
allocator.alloc<AstAttr>(Location(nameLoc, argsLocation), type.value_or(AstAttr::Type::Unknown), args, AstName(attrName))
998
);
999
}
1000
else
1001
{
1002
std::optional<AstAttr::Type> type = validateAttribute(nameLoc, attrName, attributes, empty);
1003
attributes.push_back(allocator.alloc<AstAttr>(nameLoc, type.value_or(AstAttr::Type::Unknown), empty, AstName(attrName)));
1004
}
1005
1006
if (lexer.current().type == ',')
1007
{
1008
nextLexeme();
1009
}
1010
else
1011
{
1012
break;
1013
}
1014
}
1015
}
1016
else
1017
{
1018
report(Location(open.location, lexer.current().location), "Attribute list cannot be empty");
1019
1020
// autocomplete expects at least one unknown attribute.
1021
attributes.push_back(
1022
allocator.alloc<AstAttr>(Location(open.location, lexer.current().location), AstAttr::Type::Unknown, empty, nameError)
1023
);
1024
}
1025
1026
expectMatchAndConsume(']', open);
1027
}
1028
}
1029
1030
// attributes ::= {attribute}
1031
AstArray<AstAttr*> Parser::parseAttributes()
1032
{
1033
Lexeme::Type type = lexer.current().type;
1034
1035
LUAU_ASSERT(type == Lexeme::Attribute || type == Lexeme::AttributeOpen);
1036
1037
TempVector<AstAttr*> attributes(scratchAttr);
1038
1039
while (lexer.current().type == Lexeme::Attribute || lexer.current().type == Lexeme::AttributeOpen)
1040
parseAttribute(attributes);
1041
1042
return copy(attributes);
1043
}
1044
1045
// attributes local function Name funcbody
1046
// attributes function funcname funcbody
1047
// attributes `declare function' Name`(' [parlist] `)' [`:` Type]
1048
// declare Name '{' Name ':' attributes `(' [parlist] `)' [`:` Type] '}'
1049
AstStat* Parser::parseAttributeStat()
1050
{
1051
AstArray<AstAttr*> attributes = parseAttributes();
1052
1053
Lexeme::Type type = lexer.current().type;
1054
1055
switch (type)
1056
{
1057
case Lexeme::Type::ReservedFunction:
1058
return parseFunctionStat(attributes);
1059
case Lexeme::Type::ReservedLocal:
1060
if (FFlag::LuauConst2)
1061
return parseLocal(
1062
attributes.size > 0 ? attributes.data[0]->location : lexer.current().location, lexer.current().location.begin, attributes, false
1063
);
1064
else
1065
return parseLocal_DEPRECATED(attributes);
1066
case Lexeme::Type::Name:
1067
{
1068
if (FFlag::LuauConst2 && strcmp("const", lexer.current().data) == 0)
1069
{
1070
Location keywordLoc = lexer.current().location;
1071
nextLexeme();
1072
return parseLocal(attributes.size > 0 ? attributes.data[0]->location : keywordLoc, keywordLoc.begin, attributes, true);
1073
}
1074
if (options.allowDeclarationSyntax && !strcmp("declare", lexer.current().data))
1075
{
1076
AstExpr* expr = parsePrimaryExpr(/* asStatement= */ true);
1077
return parseDeclaration(expr->location, attributes);
1078
}
1079
}
1080
[[fallthrough]];
1081
default:
1082
if (FFlag::LuauConst2)
1083
return reportStatError(
1084
lexer.current().location,
1085
{},
1086
{},
1087
"Expected 'function', 'local function', 'const function', 'declare function' or a function type declaration after attribute, but got "
1088
"%s instead",
1089
lexer.current().toString().c_str()
1090
);
1091
else
1092
return reportStatError(
1093
lexer.current().location,
1094
{},
1095
{},
1096
"Expected 'function', 'local function', 'declare function' or a function type declaration after attribute, but got %s instead",
1097
lexer.current().toString().c_str()
1098
);
1099
}
1100
}
1101
1102
bool isEnoughValues(TempVector<AstExpr*>& values, size_t expected)
1103
{
1104
if (values.size() > 0)
1105
{
1106
AstExpr* last = values.back();
1107
if (last->is<AstExprCall>() || last->is<AstExprVarargs>())
1108
return true;
1109
}
1110
return values.size() == expected;
1111
}
1112
1113
// local function Name funcbody |
1114
// local bindinglist [`=' explist]
1115
AstStat* Parser::parseLocal_DEPRECATED(const AstArray<AstAttr*>& attributes)
1116
{
1117
Location start = lexer.current().location;
1118
1119
if (attributes.size > 0)
1120
start = attributes.data[0]->location;
1121
1122
Position localKeywordPosition = lexer.current().location.begin;
1123
nextLexeme(); // local
1124
1125
if (lexer.current().type == Lexeme::ReservedFunction)
1126
{
1127
Lexeme matchFunction = lexer.current();
1128
nextLexeme();
1129
1130
Position functionKeywordPosition = matchFunction.location.begin;
1131
// matchFunction is only used for diagnostics; to make it suitable for detecting missed indentation between
1132
// `local function` and `end`, we patch the token to begin at the column where `local` starts
1133
if (matchFunction.location.begin.line == start.begin.line)
1134
matchFunction.location.begin.column = start.begin.column;
1135
1136
Name name = parseName("variable name");
1137
1138
matchRecoveryStopOnToken[Lexeme::ReservedEnd]++;
1139
1140
auto [body, var] = parseFunctionBody(false, matchFunction, name.name, &name, attributes);
1141
1142
matchRecoveryStopOnToken[Lexeme::ReservedEnd]--;
1143
1144
Location location{start.begin, body->location.end};
1145
1146
AstStatLocalFunction* node = allocator.alloc<AstStatLocalFunction>(location, var, body);
1147
if (options.storeCstData)
1148
cstNodeMap[node] = allocator.alloc<CstStatLocalFunction>(localKeywordPosition, functionKeywordPosition);
1149
return node;
1150
}
1151
else
1152
{
1153
if (attributes.size != 0)
1154
{
1155
return reportStatError(
1156
lexer.current().location,
1157
{},
1158
{},
1159
"Expected 'function' after local declaration with attribute, but got %s instead",
1160
lexer.current().toString().c_str()
1161
);
1162
}
1163
1164
matchRecoveryStopOnToken['=']++;
1165
1166
TempVector<Binding> names(scratchBinding);
1167
AstArray<Position> varsCommaPositions;
1168
if (options.storeCstData)
1169
parseBindingList(names, false, &varsCommaPositions);
1170
else
1171
parseBindingList(names);
1172
1173
matchRecoveryStopOnToken['=']--;
1174
1175
TempVector<AstLocal*> vars(scratchLocal);
1176
1177
TempVector<AstExpr*> values(scratchExpr);
1178
TempVector<Position> valuesCommaPositions(scratchPosition);
1179
1180
std::optional<Location> equalsSignLocation;
1181
1182
if (lexer.current().type == '=')
1183
{
1184
equalsSignLocation = lexer.current().location;
1185
1186
nextLexeme();
1187
1188
parseExprList(values, options.storeCstData ? &valuesCommaPositions : nullptr);
1189
}
1190
1191
for (size_t i = 0; i < names.size(); ++i)
1192
vars.push_back(pushLocal(names[i]));
1193
1194
Location end = values.empty() ? lexer.previousLocation() : values.back()->location;
1195
1196
AstStatLocal* node = allocator.alloc<AstStatLocal>(Location(start, end), copy(vars), copy(values), equalsSignLocation);
1197
if (options.storeCstData)
1198
{
1199
cstNodeMap[node] = allocator.alloc<CstStatLocal>(extractAnnotationColonPositions(names), varsCommaPositions, copy(valuesCommaPositions));
1200
}
1201
1202
return node;
1203
}
1204
}
1205
1206
AstStat* Parser::parseLocal(const Location start, const Position keywordPosition, const AstArray<AstAttr*>& attributes, bool isConst)
1207
{
1208
if (!isConst)
1209
nextLexeme(); // local
1210
1211
if (lexer.current().type == Lexeme::ReservedFunction)
1212
{
1213
Lexeme matchFunction = lexer.current();
1214
nextLexeme();
1215
1216
Position functionKeywordPosition = matchFunction.location.begin;
1217
// matchFunction is only used for diagnostics; to make it suitable for detecting missed indentation between
1218
// `local function` and `end`, we patch the token to begin at the column where `local` starts
1219
if (matchFunction.location.begin.line == start.begin.line)
1220
matchFunction.location.begin.column = start.begin.column;
1221
1222
Name name = parseName("variable name");
1223
1224
matchRecoveryStopOnToken[Lexeme::ReservedEnd]++;
1225
1226
auto [body, var] = parseFunctionBody(false, matchFunction, name.name, &name, attributes, isConst);
1227
1228
matchRecoveryStopOnToken[Lexeme::ReservedEnd]--;
1229
1230
Location location{start.begin, body->location.end};
1231
1232
AstStatLocalFunction* node = allocator.alloc<AstStatLocalFunction>(location, var, body);
1233
if (options.storeCstData)
1234
cstNodeMap[node] = allocator.alloc<CstStatLocalFunction>(keywordPosition, functionKeywordPosition);
1235
return node;
1236
}
1237
else
1238
{
1239
if (attributes.size != 0)
1240
{
1241
return reportStatError(
1242
lexer.current().location,
1243
{},
1244
{},
1245
"Expected 'function' after local declaration with attribute, but got %s instead",
1246
lexer.current().toString().c_str()
1247
);
1248
}
1249
1250
matchRecoveryStopOnToken['=']++;
1251
1252
TempVector<Binding> names(scratchBinding);
1253
AstArray<Position> varsCommaPositions;
1254
if (options.storeCstData)
1255
parseBindingList(names, false, &varsCommaPositions, nullptr, nullptr, isConst);
1256
else
1257
parseBindingList(names, false, nullptr, nullptr, nullptr, isConst);
1258
1259
matchRecoveryStopOnToken['=']--;
1260
1261
TempVector<AstLocal*> vars(scratchLocal);
1262
1263
TempVector<AstExpr*> values(scratchExpr);
1264
TempVector<Position> valuesCommaPositions(scratchPosition);
1265
1266
std::optional<Location> equalsSignLocation;
1267
1268
if (lexer.current().type == '=')
1269
{
1270
equalsSignLocation = lexer.current().location;
1271
1272
nextLexeme();
1273
1274
parseExprList(values, options.storeCstData ? &valuesCommaPositions : nullptr);
1275
}
1276
1277
for (size_t i = 0; i < names.size(); ++i)
1278
vars.push_back(pushLocal(names[i]));
1279
1280
Location end = values.empty() ? lexer.previousLocation() : values.back()->location;
1281
1282
if (isConst && !isEnoughValues(values, vars.size()))
1283
return reportStatError(Location(start, end), {}, {}, "Missing initializer in const declaration");
1284
1285
AstStatLocal* node = allocator.alloc<AstStatLocal>(Location(start, end), copy(vars), copy(values), equalsSignLocation);
1286
if (options.storeCstData)
1287
{
1288
cstNodeMap[node] = allocator.alloc<CstStatLocal>(extractAnnotationColonPositions(names), varsCommaPositions, copy(valuesCommaPositions));
1289
}
1290
1291
return node;
1292
}
1293
}
1294
1295
// return [explist]
1296
AstStat* Parser::parseReturn()
1297
{
1298
Location start = lexer.current().location;
1299
1300
nextLexeme();
1301
1302
TempVector<AstExpr*> list(scratchExpr);
1303
TempVector<Position> commaPositions(scratchPosition);
1304
1305
if (!blockFollow(lexer.current()) && lexer.current().type != ';')
1306
parseExprList(list, options.storeCstData ? &commaPositions : nullptr);
1307
1308
Location end = list.empty() ? start : list.back()->location;
1309
1310
AstStatReturn* node = allocator.alloc<AstStatReturn>(Location(start, end), copy(list));
1311
if (options.storeCstData)
1312
cstNodeMap[node] = allocator.alloc<CstStatReturn>(copy(commaPositions));
1313
return node;
1314
}
1315
1316
// type Name [`<' varlist `>'] `=' Type
1317
AstStat* Parser::parseTypeAlias(const Location& start, bool exported, Position typeKeywordPosition)
1318
{
1319
// parsing a type function
1320
if (lexer.current().type == Lexeme::ReservedFunction)
1321
return parseTypeFunction(start, exported, typeKeywordPosition);
1322
1323
// parsing a type alias
1324
1325
// note: `type` token is already parsed for us, so we just need to parse the rest
1326
1327
std::optional<Name> name = parseNameOpt("type name");
1328
1329
// Use error name if the name is missing
1330
if (!name)
1331
name = Name(nameError, lexer.current().location);
1332
1333
Position genericsOpenPosition{0, 0};
1334
AstArray<Position> genericsCommaPositions;
1335
Position genericsClosePosition{0, 0};
1336
auto [generics, genericPacks] = options.storeCstData
1337
? parseGenericTypeList(
1338
/* withDefaultValues= */ true, &genericsOpenPosition, &genericsCommaPositions, &genericsClosePosition
1339
)
1340
: parseGenericTypeList(/* withDefaultValues= */ true);
1341
1342
Position equalsPosition = lexer.current().location.begin;
1343
expectAndConsume('=', "type alias");
1344
1345
AstType* type = parseType();
1346
1347
AstStatTypeAlias* node =
1348
allocator.alloc<AstStatTypeAlias>(Location(start, type->location), name->name, name->location, generics, genericPacks, type, exported);
1349
if (options.storeCstData)
1350
cstNodeMap[node] = allocator.alloc<CstStatTypeAlias>(
1351
typeKeywordPosition, genericsOpenPosition, genericsCommaPositions, genericsClosePosition, equalsPosition
1352
);
1353
return node;
1354
}
1355
1356
// type function Name `(' arglist `)' `=' funcbody `end'
1357
AstStat* Parser::parseTypeFunction(const Location& start, bool exported, Position typeKeywordPosition)
1358
{
1359
Lexeme matchFn = lexer.current();
1360
nextLexeme();
1361
1362
size_t errorsAtStart = parseErrors.size();
1363
1364
// parse the name of the type function
1365
std::optional<Name> fnName = parseNameOpt("type function name");
1366
if (!fnName)
1367
fnName = Name(nameError, lexer.current().location);
1368
1369
matchRecoveryStopOnToken[Lexeme::ReservedEnd]++;
1370
1371
size_t oldTypeFunctionDepth = typeFunctionDepth;
1372
typeFunctionDepth = functionStack.size();
1373
1374
AstExprFunction* body = parseFunctionBody(/* hasself */ false, matchFn, fnName->name, nullptr, AstArray<AstAttr*>({nullptr, 0})).first;
1375
1376
typeFunctionDepth = oldTypeFunctionDepth;
1377
1378
matchRecoveryStopOnToken[Lexeme::ReservedEnd]--;
1379
1380
bool hasErrors = parseErrors.size() > errorsAtStart;
1381
1382
AstStatTypeFunction* node =
1383
allocator.alloc<AstStatTypeFunction>(Location(start, body->location), fnName->name, fnName->location, body, exported, hasErrors);
1384
if (options.storeCstData)
1385
cstNodeMap[node] = allocator.alloc<CstStatTypeFunction>(typeKeywordPosition, matchFn.location.begin);
1386
return node;
1387
}
1388
1389
AstDeclaredExternTypeProperty Parser::parseDeclaredExternTypeMethod(const AstArray<AstAttr*>& attributes)
1390
{
1391
Location start = lexer.current().location;
1392
1393
nextLexeme();
1394
1395
Name fnName = parseName("function name");
1396
1397
// TODO: generic method declarations CLI-39909
1398
AstArray<AstGenericType*> generics;
1399
AstArray<AstGenericTypePack*> genericPacks;
1400
generics.size = 0;
1401
generics.data = nullptr;
1402
genericPacks.size = 0;
1403
genericPacks.data = nullptr;
1404
1405
MatchLexeme matchParen = lexer.current();
1406
expectAndConsume('(', "function parameter list start");
1407
1408
TempVector<Binding> args(scratchBinding);
1409
1410
bool vararg = false;
1411
Location varargLocation;
1412
AstTypePack* varargAnnotation = nullptr;
1413
if (lexer.current().type != ')')
1414
std::tie(vararg, varargLocation, varargAnnotation) = parseBindingList(args, /* allowDot3 */ true);
1415
1416
expectMatchAndConsume(')', matchParen);
1417
1418
AstTypePack* retTypes = parseOptionalReturnType();
1419
if (!retTypes)
1420
retTypes = allocator.alloc<AstTypePackExplicit>(lexer.current().location, AstTypeList{copy<AstType*>(nullptr, 0), nullptr});
1421
Location end = lexer.previousLocation();
1422
1423
TempVector<AstType*> vars(scratchType);
1424
TempVector<std::optional<AstArgumentName>> varNames(scratchOptArgName);
1425
1426
if (args.size() == 0 || args[0].name.name != "self" || args[0].annotation != nullptr)
1427
{
1428
return AstDeclaredExternTypeProperty{
1429
fnName.name, fnName.location, reportTypeError(Location(start, end), {}, "'self' must be present as the unannotated first parameter"), true
1430
};
1431
}
1432
1433
// Skip the first index.
1434
for (size_t i = 1; i < args.size(); ++i)
1435
{
1436
varNames.push_back(AstArgumentName{args[i].name.name, args[i].name.location});
1437
1438
if (args[i].annotation)
1439
vars.push_back(args[i].annotation);
1440
else
1441
vars.push_back(reportTypeError(Location(start, end), {}, "All declaration parameters aside from 'self' must be annotated"));
1442
}
1443
1444
if (vararg && !varargAnnotation)
1445
report(start, "All declaration parameters aside from 'self' must be annotated");
1446
1447
AstType* fnType = allocator.alloc<AstTypeFunction>(
1448
Location(start, end), attributes, generics, genericPacks, AstTypeList{copy(vars), varargAnnotation}, copy(varNames), retTypes
1449
);
1450
1451
return AstDeclaredExternTypeProperty{fnName.name, fnName.location, fnType, true, Location(start, end)};
1452
}
1453
1454
AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*>& attributes)
1455
{
1456
// `declare` token is already parsed at this point
1457
1458
if ((attributes.size != 0) && (lexer.current().type != Lexeme::ReservedFunction))
1459
return reportStatError(
1460
lexer.current().location,
1461
{},
1462
{},
1463
"Expected a function type declaration after attribute, but got %s instead",
1464
lexer.current().toString().c_str()
1465
);
1466
1467
if (lexer.current().type == Lexeme::ReservedFunction)
1468
{
1469
nextLexeme();
1470
1471
Name globalName = parseName("global function name");
1472
auto [generics, genericPacks] = parseGenericTypeList(/* withDefaultValues= */ false);
1473
1474
MatchLexeme matchParen = lexer.current();
1475
1476
expectAndConsume('(', "global function declaration");
1477
1478
TempVector<Binding> args(scratchBinding);
1479
1480
bool vararg = false;
1481
Location varargLocation;
1482
AstTypePack* varargAnnotation = nullptr;
1483
1484
if (lexer.current().type != ')')
1485
std::tie(vararg, varargLocation, varargAnnotation) = parseBindingList(args, /* allowDot3= */ true);
1486
1487
expectMatchAndConsume(')', matchParen);
1488
1489
AstTypePack* retTypes;
1490
retTypes = parseOptionalReturnType();
1491
if (!retTypes)
1492
retTypes = allocator.alloc<AstTypePackExplicit>(lexer.current().location, AstTypeList{copy<AstType*>(nullptr, 0), nullptr});
1493
Location end = lexer.current().location;
1494
1495
TempVector<AstType*> vars(scratchType);
1496
TempVector<AstArgumentName> varNames(scratchArgName);
1497
1498
for (size_t i = 0; i < args.size(); ++i)
1499
{
1500
if (!args[i].annotation)
1501
return reportStatError(Location(start, end), {}, {}, "All declaration parameters must be annotated");
1502
1503
vars.push_back(args[i].annotation);
1504
varNames.push_back({args[i].name.name, args[i].name.location});
1505
}
1506
1507
if (vararg && !varargAnnotation)
1508
return reportStatError(Location(start, end), {}, {}, "All declaration parameters must be annotated");
1509
1510
return allocator.alloc<AstStatDeclareFunction>(
1511
Location(start, end),
1512
attributes,
1513
globalName.name,
1514
globalName.location,
1515
generics,
1516
genericPacks,
1517
AstTypeList{copy(vars), varargAnnotation},
1518
copy(varNames),
1519
vararg,
1520
varargLocation,
1521
retTypes
1522
);
1523
}
1524
else if (AstName(lexer.current().name) == "class" || AstName(lexer.current().name) == "extern")
1525
{
1526
bool foundExtern = false;
1527
if (AstName(lexer.current().name) == "extern")
1528
{
1529
foundExtern = true;
1530
nextLexeme();
1531
if (AstName(lexer.current().name) != "type")
1532
return reportStatError(
1533
lexer.current().location, {}, {}, "Expected `type` keyword after `extern`, but got %s instead", lexer.current().name
1534
);
1535
}
1536
1537
1538
nextLexeme();
1539
1540
Location classStart = lexer.current().location;
1541
Name className = parseName("type name");
1542
std::optional<AstName> superName = std::nullopt;
1543
1544
if (AstName(lexer.current().name) == "extends")
1545
{
1546
nextLexeme();
1547
superName = parseName("supertype name").name;
1548
}
1549
1550
if (foundExtern)
1551
{
1552
if (AstName(lexer.current().name) != "with")
1553
report(
1554
lexer.current().location,
1555
"Expected `with` keyword before listing properties of the external type, but got %s instead",
1556
lexer.current().name
1557
);
1558
else
1559
nextLexeme();
1560
}
1561
1562
1563
TempVector<AstDeclaredExternTypeProperty> props(scratchDeclaredClassProps);
1564
AstTableIndexer* indexer = nullptr;
1565
1566
while (lexer.current().type != Lexeme::ReservedEnd)
1567
{
1568
AstArray<AstAttr*> attributes{nullptr, 0};
1569
1570
if (lexer.current().type == Lexeme::Attribute || lexer.current().type == Lexeme::AttributeOpen)
1571
{
1572
attributes = Parser::parseAttributes();
1573
1574
if (lexer.current().type != Lexeme::ReservedFunction)
1575
return reportStatError(
1576
lexer.current().location,
1577
{},
1578
{},
1579
"Expected a method type declaration after attribute, but got %s instead",
1580
lexer.current().toString().c_str()
1581
);
1582
}
1583
1584
// There are two possibilities: Either it's a property or a function.
1585
if (lexer.current().type == Lexeme::ReservedFunction)
1586
{
1587
props.push_back(parseDeclaredExternTypeMethod(attributes));
1588
}
1589
else if (lexer.current().type == '[')
1590
{
1591
const Lexeme begin = lexer.current();
1592
nextLexeme(); // [
1593
1594
if ((lexer.current().type == Lexeme::RawString || lexer.current().type == Lexeme::QuotedString) && lexer.lookahead().type == ']')
1595
{
1596
const Location nameBegin = lexer.current().location;
1597
std::optional<AstArray<char>> chars = parseCharArray();
1598
1599
const Location nameEnd = lexer.previousLocation();
1600
1601
expectMatchAndConsume(']', begin);
1602
expectAndConsume(':', "property type annotation");
1603
AstType* type = parseType();
1604
1605
// since AstName contains a char*, it can't contain null
1606
bool containsNull = chars && (memchr(chars->data, 0, chars->size) != nullptr);
1607
1608
if (chars && !containsNull)
1609
{
1610
props.push_back(
1611
AstDeclaredExternTypeProperty{
1612
AstName(chars->data), Location(nameBegin, nameEnd), type, false, Location(begin.location, lexer.previousLocation())
1613
}
1614
);
1615
}
1616
else
1617
{
1618
report(begin.location, "String literal contains malformed escape sequence or \\0");
1619
}
1620
}
1621
else if (indexer)
1622
{
1623
// maybe we don't need to parse the entire badIndexer...
1624
// however, we either have { or [ to lint, not the entire table type or the bad indexer.
1625
AstTableIndexer* badIndexer = parseTableIndexer(AstTableAccess::ReadWrite, std::nullopt, begin).node;
1626
1627
// we lose all additional indexer expressions from the AST after error recovery here
1628
report(badIndexer->location, "Cannot have more than one indexer on an extern type");
1629
}
1630
else
1631
{
1632
indexer = parseTableIndexer(AstTableAccess::ReadWrite, std::nullopt, begin).node;
1633
}
1634
}
1635
else
1636
{
1637
AstTableAccess access = AstTableAccess::ReadWrite;
1638
1639
if (FFlag::LuauExternReadWriteAttributes)
1640
{
1641
if (lexer.current().type == Lexeme::Name && lexer.lookahead().type != ':')
1642
{
1643
if (AstName(lexer.current().name) == "read")
1644
{
1645
access = AstTableAccess::Read;
1646
lexer.next();
1647
}
1648
else if (AstName(lexer.current().name) == "write")
1649
{
1650
access = AstTableAccess::Write;
1651
lexer.next();
1652
}
1653
else
1654
{
1655
report(lexer.current().location, "Expected blank or 'read' or 'write' attribute, got '%s'", lexer.current().name);
1656
lexer.next();
1657
}
1658
}
1659
}
1660
1661
Location propStart = lexer.current().location;
1662
std::optional<Name> propName = parseNameOpt("property name");
1663
1664
if (!propName)
1665
break;
1666
1667
expectAndConsume(':', "property type annotation");
1668
AstType* propType = parseType();
1669
props.push_back(
1670
AstDeclaredExternTypeProperty{propName->name, propName->location, propType, false, Location(propStart, lexer.previousLocation()), access}
1671
);
1672
}
1673
}
1674
1675
Location classEnd = lexer.current().location;
1676
nextLexeme(); // skip past `end`
1677
1678
return allocator.alloc<AstStatDeclareExternType>(Location(classStart, classEnd), className.name, superName, copy(props), indexer);
1679
}
1680
else if (std::optional<Name> globalName = parseNameOpt("global variable name"))
1681
{
1682
expectAndConsume(':', "global variable declaration");
1683
1684
AstType* type = parseType(/* in declaration context */ true);
1685
return allocator.alloc<AstStatDeclareGlobal>(Location(start, type->location), globalName->name, globalName->location, type);
1686
}
1687
else
1688
{
1689
return reportStatError(start, {}, {}, "declare must be followed by an identifier, 'function', or 'extern type'");
1690
}
1691
}
1692
1693
// varlist `=' explist
1694
AstStat* Parser::parseAssignment(AstExpr* initial)
1695
{
1696
if (!isExprLValue(initial))
1697
initial = reportExprError(initial->location, copy({initial}), "Assigned expression must be a variable or a field");
1698
1699
TempVector<AstExpr*> vars(scratchExpr);
1700
TempVector<Position> varsCommaPositions(scratchPosition);
1701
vars.push_back(initial);
1702
1703
while (lexer.current().type == ',')
1704
{
1705
if (options.storeCstData)
1706
varsCommaPositions.push_back(lexer.current().location.begin);
1707
nextLexeme();
1708
1709
AstExpr* expr = parsePrimaryExpr(/* asStatement= */ true);
1710
1711
if (!isExprLValue(expr))
1712
expr = reportExprError(expr->location, copy({expr}), "Assigned expression must be a variable or a field");
1713
1714
vars.push_back(expr);
1715
}
1716
1717
Position equalsPosition = lexer.current().location.begin;
1718
expectAndConsume('=', "assignment");
1719
1720
TempVector<AstExpr*> values(scratchExprAux);
1721
TempVector<Position> valuesCommaPositions(scratchPosition);
1722
parseExprList(values, options.storeCstData ? &valuesCommaPositions : nullptr);
1723
1724
AstStatAssign* node = allocator.alloc<AstStatAssign>(Location(initial->location, values.back()->location), copy(vars), copy(values));
1725
cstNodeMap[node] = allocator.alloc<CstStatAssign>(copy(varsCommaPositions), equalsPosition, copy(valuesCommaPositions));
1726
return node;
1727
}
1728
1729
// var [`+=' | `-=' | `*=' | `/=' | `%=' | `^=' | `..='] exp
1730
AstStat* Parser::parseCompoundAssignment(AstExpr* initial, AstExprBinary::Op op)
1731
{
1732
if (!isExprLValue(initial))
1733
{
1734
initial = reportExprError(initial->location, copy({initial}), "Assigned expression must be a variable or a field");
1735
}
1736
1737
Position opPosition = lexer.current().location.begin;
1738
nextLexeme();
1739
1740
AstExpr* value = parseExpr();
1741
1742
AstStatCompoundAssign* node = allocator.alloc<AstStatCompoundAssign>(Location(initial->location, value->location), op, initial, value);
1743
if (options.storeCstData)
1744
cstNodeMap[node] = allocator.alloc<CstStatCompoundAssign>(opPosition);
1745
return node;
1746
}
1747
1748
std::pair<AstLocal*, AstArray<AstLocal*>> Parser::prepareFunctionArguments(const Location& start, bool hasself, const TempVector<Binding>& args)
1749
{
1750
AstLocal* self = nullptr;
1751
1752
if (hasself)
1753
self = pushLocal(Binding(Name(nameSelf, start), nullptr));
1754
1755
TempVector<AstLocal*> vars(scratchLocal);
1756
1757
for (size_t i = 0; i < args.size(); ++i)
1758
vars.push_back(pushLocal(args[i]));
1759
1760
return {self, copy(vars)};
1761
}
1762
1763
// funcbody ::= `(' [parlist] `)' [`:' ReturnType] block end
1764
// parlist ::= bindinglist [`,' `...'] | `...'
1765
std::pair<AstExprFunction*, AstLocal*> Parser::parseFunctionBody(
1766
bool hasself,
1767
const Lexeme& matchFunction,
1768
const AstName& debugname,
1769
const Name* localName,
1770
const AstArray<AstAttr*>& attributes,
1771
const bool isConst
1772
)
1773
{
1774
Location start = matchFunction.location;
1775
1776
if (attributes.size > 0)
1777
start = attributes.data[0]->location;
1778
1779
auto* cstNode = options.storeCstData ? allocator.alloc<CstExprFunction>() : nullptr;
1780
1781
auto [generics, genericPacks] =
1782
cstNode
1783
? parseGenericTypeList(
1784
/* withDefaultValues= */ false, &cstNode->openGenericsPosition, &cstNode->genericsCommaPositions, &cstNode->closeGenericsPosition
1785
)
1786
: parseGenericTypeList(/* withDefaultValues= */ false);
1787
1788
MatchLexeme matchParen = lexer.current();
1789
expectAndConsume('(', "function");
1790
1791
// NOTE: This was added in conjunction with passing `searchForMissing` to
1792
// `expectMatchAndConsume` inside `parseTableType` so that the behavior of
1793
// parsing code like below (note the missing `}`):
1794
//
1795
// function (t: { a: number ) end
1796
//
1797
// ... will still parse as (roughly):
1798
//
1799
// function (t: { a: number }) end
1800
//
1801
matchRecoveryStopOnToken[')']++;
1802
1803
TempVector<Binding> args(scratchBinding);
1804
1805
bool vararg = false;
1806
Location varargLocation;
1807
AstTypePack* varargAnnotation = nullptr;
1808
1809
if (lexer.current().type != ')')
1810
{
1811
if (cstNode)
1812
std::tie(vararg, varargLocation, varargAnnotation) =
1813
parseBindingList(args, /* allowDot3= */ true, &cstNode->argsCommaPositions, nullptr, &cstNode->varargAnnotationColonPosition);
1814
else
1815
std::tie(vararg, varargLocation, varargAnnotation) = parseBindingList(args, /* allowDot3= */ true);
1816
}
1817
1818
std::optional<Location> argLocation;
1819
1820
if (matchParen.type == Lexeme::Type('(') && lexer.current().type == Lexeme::Type(')'))
1821
argLocation = Location(matchParen.position, lexer.current().location.end);
1822
1823
expectMatchAndConsume(')', matchParen, true);
1824
1825
matchRecoveryStopOnToken[')']--;
1826
1827
AstTypePack* typelist = parseOptionalReturnType(cstNode ? &cstNode->returnSpecifierPosition : nullptr);
1828
1829
AstLocal* funLocal = nullptr;
1830
1831
if (localName)
1832
{
1833
if (FFlag::LuauConst2)
1834
funLocal = pushLocal(Binding(*localName, nullptr, {0, 0}, isConst));
1835
else
1836
funLocal = pushLocal(Binding(*localName, nullptr));
1837
}
1838
1839
unsigned int localsBegin = saveLocals();
1840
1841
Function fun;
1842
fun.vararg = vararg;
1843
1844
functionStack.emplace_back(fun);
1845
1846
auto [self, vars] = prepareFunctionArguments(start, hasself, args);
1847
1848
AstStatBlock* body = parseBlock();
1849
1850
functionStack.pop_back();
1851
1852
restoreLocals(localsBegin);
1853
1854
Location end = lexer.current().location;
1855
1856
bool hasEnd = expectMatchEndAndConsume(Lexeme::ReservedEnd, matchFunction);
1857
body->hasEnd = hasEnd;
1858
1859
AstExprFunction* node = allocator.alloc<AstExprFunction>(
1860
Location(start, end),
1861
attributes,
1862
generics,
1863
genericPacks,
1864
self,
1865
vars,
1866
vararg,
1867
varargLocation,
1868
body,
1869
functionStack.size(),
1870
debugname,
1871
typelist,
1872
varargAnnotation,
1873
argLocation
1874
);
1875
if (options.storeCstData)
1876
{
1877
cstNode->functionKeywordPosition = matchFunction.location.begin;
1878
cstNode->argsAnnotationColonPositions = extractAnnotationColonPositions(args);
1879
cstNodeMap[node] = cstNode;
1880
}
1881
1882
return {node, funLocal};
1883
}
1884
1885
// explist ::= {exp `,'} exp
1886
void Parser::parseExprList(TempVector<AstExpr*>& result, TempVector<Position>* commaPositions)
1887
{
1888
result.push_back(parseExpr());
1889
1890
while (lexer.current().type == ',')
1891
{
1892
if (commaPositions)
1893
commaPositions->push_back(lexer.current().location.begin);
1894
nextLexeme();
1895
1896
if (lexer.current().type == ')')
1897
{
1898
report(lexer.current().location, "Expected expression after ',' but got ')' instead");
1899
break;
1900
}
1901
1902
result.push_back(parseExpr());
1903
}
1904
}
1905
1906
Parser::Binding Parser::parseBinding(bool isConst)
1907
{
1908
std::optional<Name> name = parseNameOpt("variable name");
1909
1910
// Use placeholder if the name is missing
1911
if (!name)
1912
name = Name(nameError, lexer.current().location);
1913
1914
Position colonPosition = lexer.current().location.begin;
1915
AstType* annotation = parseOptionalType();
1916
1917
if (options.storeCstData)
1918
return Binding(*name, annotation, colonPosition, isConst);
1919
else
1920
return Binding(*name, annotation, {0, 0}, isConst);
1921
}
1922
1923
AstArray<Position> Parser::extractAnnotationColonPositions(const TempVector<Binding>& bindings)
1924
{
1925
TempVector<Position> annotationColonPositions(scratchPosition);
1926
for (size_t i = 0; i < bindings.size(); ++i)
1927
annotationColonPositions.push_back(bindings[i].colonPosition);
1928
return copy(annotationColonPositions);
1929
}
1930
1931
// bindinglist ::= (binding | `...') [`,' bindinglist]
1932
LUAU_NOINLINE std::tuple<bool, Location, AstTypePack*> Parser::parseBindingList(
1933
TempVector<Binding>& result,
1934
bool allowDot3,
1935
AstArray<Position>* commaPositions,
1936
Position* initialCommaPosition,
1937
Position* varargAnnotationColonPosition,
1938
bool isConst
1939
)
1940
{
1941
TempVector<Position> localCommaPositions(scratchPosition);
1942
1943
if (commaPositions && initialCommaPosition)
1944
localCommaPositions.push_back(*initialCommaPosition);
1945
1946
while (true)
1947
{
1948
if (lexer.current().type == Lexeme::Dot3 && allowDot3)
1949
{
1950
Location varargLocation = lexer.current().location;
1951
nextLexeme();
1952
1953
AstTypePack* tailAnnotation = nullptr;
1954
if (lexer.current().type == ':')
1955
{
1956
if (varargAnnotationColonPosition)
1957
*varargAnnotationColonPosition = lexer.current().location.begin;
1958
1959
nextLexeme();
1960
tailAnnotation = parseVariadicArgumentTypePack();
1961
}
1962
1963
if (commaPositions)
1964
*commaPositions = copy(localCommaPositions);
1965
1966
return {true, varargLocation, tailAnnotation};
1967
}
1968
1969
result.push_back(parseBinding(isConst));
1970
1971
if (lexer.current().type != ',')
1972
break;
1973
if (commaPositions)
1974
localCommaPositions.push_back(lexer.current().location.begin);
1975
nextLexeme();
1976
}
1977
1978
if (commaPositions)
1979
*commaPositions = copy(localCommaPositions);
1980
1981
return {false, Location(), nullptr};
1982
}
1983
1984
AstType* Parser::parseOptionalType()
1985
{
1986
if (lexer.current().type == ':')
1987
{
1988
nextLexeme();
1989
return parseType();
1990
}
1991
else
1992
return nullptr;
1993
}
1994
1995
// TypeList ::= Type [`,' TypeList] | ...Type
1996
AstTypePack* Parser::parseTypeList(
1997
TempVector<AstType*>& result,
1998
TempVector<std::optional<AstArgumentName>>& resultNames,
1999
TempVector<Position>* commaPositions,
2000
TempVector<std::optional<Position>>* nameColonPositions
2001
)
2002
{
2003
while (true)
2004
{
2005
if (shouldParseTypePack(lexer))
2006
return parseTypePack();
2007
2008
if (lexer.current().type == Lexeme::Name && lexer.lookahead().type == ':')
2009
{
2010
// Fill in previous argument names with empty slots
2011
while (resultNames.size() < result.size())
2012
resultNames.push_back({});
2013
if (nameColonPositions)
2014
{
2015
while (nameColonPositions->size() < result.size())
2016
nameColonPositions->push_back({});
2017
}
2018
2019
resultNames.push_back(AstArgumentName{AstName(lexer.current().name), lexer.current().location});
2020
nextLexeme();
2021
2022
if (nameColonPositions)
2023
nameColonPositions->push_back(lexer.current().location.begin);
2024
expectAndConsume(':');
2025
}
2026
else if (!resultNames.empty())
2027
{
2028
// If we have a type with named arguments, provide elements for all types
2029
resultNames.push_back({});
2030
if (nameColonPositions)
2031
nameColonPositions->push_back({});
2032
}
2033
2034
result.push_back(parseType());
2035
if (lexer.current().type != ',')
2036
break;
2037
2038
if (commaPositions)
2039
commaPositions->push_back(lexer.current().location.begin);
2040
nextLexeme();
2041
2042
if (lexer.current().type == ')')
2043
{
2044
report(lexer.current().location, "Expected type after ',' but got ')' instead");
2045
break;
2046
}
2047
}
2048
2049
return nullptr;
2050
}
2051
2052
AstTypePack* Parser::parseOptionalReturnType(Position* returnSpecifierPosition)
2053
{
2054
if (lexer.current().type == ':' || lexer.current().type == Lexeme::SkinnyArrow)
2055
{
2056
if (lexer.current().type == Lexeme::SkinnyArrow)
2057
report(lexer.current().location, "Function return type annotations are written after ':' instead of '->'");
2058
2059
if (returnSpecifierPosition)
2060
*returnSpecifierPosition = lexer.current().location.begin;
2061
nextLexeme();
2062
2063
unsigned int oldRecursionCount = recursionCounter;
2064
2065
auto result = parseReturnType();
2066
LUAU_ASSERT(result);
2067
2068
// At this point, if we find a , character, it indicates that there are multiple return types
2069
// in this type annotation, but the list wasn't wrapped in parentheses.
2070
if (lexer.current().type == ',')
2071
{
2072
report(lexer.current().location, "Expected a statement, got ','; did you forget to wrap the list of return types in parentheses?");
2073
2074
nextLexeme();
2075
}
2076
2077
recursionCounter = oldRecursionCount;
2078
2079
return result;
2080
}
2081
2082
return nullptr;
2083
}
2084
2085
// ReturnType ::= Type | `(' TypeList `)'
2086
AstTypePack* Parser::parseReturnType()
2087
{
2088
incrementRecursionCounter("type annotation");
2089
2090
Lexeme begin = lexer.current();
2091
2092
if (lexer.current().type != '(')
2093
{
2094
if (shouldParseTypePack(lexer))
2095
{
2096
return parseTypePack();
2097
}
2098
else
2099
{
2100
AstType* type = parseType();
2101
AstTypePackExplicit* node = allocator.alloc<AstTypePackExplicit>(type->location, AstTypeList{copy(&type, 1), nullptr});
2102
if (options.storeCstData)
2103
cstNodeMap[node] = allocator.alloc<CstTypePackExplicit>();
2104
return node;
2105
}
2106
}
2107
2108
nextLexeme();
2109
2110
matchRecoveryStopOnToken[Lexeme::SkinnyArrow]++;
2111
2112
TempVector<AstType*> result(scratchType);
2113
TempVector<std::optional<AstArgumentName>> resultNames(scratchOptArgName);
2114
TempVector<Position> commaPositions(scratchPosition);
2115
TempVector<std::optional<Position>> nameColonPositions(scratchOptPosition);
2116
AstTypePack* varargAnnotation = nullptr;
2117
2118
// possibly () -> ReturnType
2119
if (lexer.current().type != ')')
2120
{
2121
if (options.storeCstData)
2122
varargAnnotation = parseTypeList(result, resultNames, &commaPositions, &nameColonPositions);
2123
else
2124
varargAnnotation = parseTypeList(result, resultNames);
2125
}
2126
2127
const Location location{begin.location, lexer.current().location};
2128
Position closeParenthesesPosition = lexer.current().location.begin;
2129
expectMatchAndConsume(')', begin, true);
2130
2131
matchRecoveryStopOnToken[Lexeme::SkinnyArrow]--;
2132
2133
if (lexer.current().type != Lexeme::SkinnyArrow && resultNames.empty())
2134
{
2135
// If it turns out that it's just '(A)', it's possible that there are unions/intersections to follow, so fold over it.
2136
if (result.size() == 1)
2137
{
2138
// TODO(CLI-140667): stop parsing type suffix when varargAnnotation != nullptr - this should be a parse error
2139
AstType* inner = varargAnnotation == nullptr ? allocator.alloc<AstTypeGroup>(location, result[0]) : result[0];
2140
AstType* returnType = parseTypeSuffix(inner, begin.location);
2141
2142
if (DFFlag::DebugLuauReportReturnTypeVariadicWithTypeSuffix && varargAnnotation != nullptr &&
2143
(returnType->is<AstTypeUnion>() || returnType->is<AstTypeIntersection>()))
2144
luau_telemetry_parsed_return_type_variadic_with_type_suffix = true;
2145
2146
// If parseType parses nothing, then returnType->location.end only points at the last non-type-pack
2147
// type to successfully parse. We need the span of the whole annotation.
2148
Position endPos = result.size() == 1 ? location.end : returnType->location.end;
2149
2150
AstTypePackExplicit* node =
2151
allocator.alloc<AstTypePackExplicit>(Location{location.begin, endPos}, AstTypeList{copy(&returnType, 1), varargAnnotation});
2152
if (options.storeCstData)
2153
cstNodeMap[node] = allocator.alloc<CstTypePackExplicit>();
2154
return node;
2155
}
2156
2157
AstTypePackExplicit* node = allocator.alloc<AstTypePackExplicit>(location, AstTypeList{copy(result), varargAnnotation});
2158
if (options.storeCstData)
2159
cstNodeMap[node] = allocator.alloc<CstTypePackExplicit>(location.begin, closeParenthesesPosition, copy(commaPositions));
2160
return node;
2161
}
2162
2163
Position returnArrowPosition = lexer.current().location.begin;
2164
AstType* tail = parseFunctionTypeTail(begin, {nullptr, 0}, {}, {}, copy(result), copy(resultNames), varargAnnotation);
2165
2166
if (options.storeCstData && tail->is<AstTypeFunction>())
2167
{
2168
cstNodeMap[tail] = allocator.alloc<CstTypeFunction>(
2169
Position{0, 0},
2170
AstArray<Position>{},
2171
Position{0, 0},
2172
location.begin,
2173
copy(nameColonPositions),
2174
copy(commaPositions),
2175
closeParenthesesPosition,
2176
returnArrowPosition
2177
);
2178
}
2179
2180
AstTypePackExplicit* node = allocator.alloc<AstTypePackExplicit>(Location{location, tail->location}, AstTypeList{copy(&tail, 1), nullptr});
2181
if (options.storeCstData)
2182
cstNodeMap[node] = allocator.alloc<CstTypePackExplicit>();
2183
return node;
2184
}
2185
2186
std::pair<CstExprConstantString::QuoteStyle, unsigned int> Parser::extractStringDetails()
2187
{
2188
CstExprConstantString::QuoteStyle style;
2189
unsigned int blockDepth = 0;
2190
2191
switch (lexer.current().type)
2192
{
2193
case Lexeme::QuotedString:
2194
style =
2195
lexer.current().getQuoteStyle() == Lexeme::QuoteStyle::Double ? CstExprConstantString::QuotedDouble : CstExprConstantString::QuotedSingle;
2196
break;
2197
case Lexeme::InterpStringSimple:
2198
style = CstExprConstantString::QuotedInterp;
2199
break;
2200
case Lexeme::RawString:
2201
{
2202
style = CstExprConstantString::QuotedRaw;
2203
blockDepth = lexer.current().getBlockDepth();
2204
break;
2205
}
2206
default:
2207
LUAU_ASSERT(false && "Invalid string type");
2208
}
2209
2210
return {style, blockDepth};
2211
}
2212
2213
// TableIndexer ::= `[' Type `]' `:' Type
2214
Parser::TableIndexerResult Parser::parseTableIndexer(AstTableAccess access, std::optional<Location> accessLocation, Lexeme begin)
2215
{
2216
AstType* index = parseType();
2217
2218
Position indexerClosePosition = lexer.current().location.begin;
2219
expectMatchAndConsume(']', begin);
2220
2221
Position colonPosition = lexer.current().location.begin;
2222
expectAndConsume(':', "table field");
2223
2224
AstType* result = parseType();
2225
2226
return {
2227
allocator.alloc<AstTableIndexer>(AstTableIndexer{index, result, Location(begin.location, result->location), access, accessLocation}),
2228
begin.location.begin,
2229
indexerClosePosition,
2230
colonPosition,
2231
};
2232
}
2233
2234
// TableProp ::= Name `:' Type
2235
// TablePropOrIndexer ::= TableProp | TableIndexer
2236
// PropList ::= TablePropOrIndexer {fieldsep TablePropOrIndexer} [fieldsep]
2237
// TableType ::= `{' PropList `}'
2238
AstType* Parser::parseTableType(bool inDeclarationContext)
2239
{
2240
incrementRecursionCounter("type annotation");
2241
2242
TempVector<AstTableProp> props(scratchTableTypeProps);
2243
TempVector<CstTypeTable::Item> cstItems(scratchCstTableTypeProps);
2244
AstTableIndexer* indexer = nullptr;
2245
2246
Location start = lexer.current().location;
2247
2248
MatchLexeme matchBrace = lexer.current();
2249
expectAndConsume('{', "table type");
2250
2251
bool isArray = false;
2252
2253
while (lexer.current().type != '}')
2254
{
2255
AstTableAccess access = AstTableAccess::ReadWrite;
2256
std::optional<Location> accessLocation;
2257
2258
if (lexer.current().type == Lexeme::Name && lexer.lookahead().type != ':')
2259
{
2260
if (AstName(lexer.current().name) == "read")
2261
{
2262
accessLocation = lexer.current().location;
2263
access = AstTableAccess::Read;
2264
lexer.next();
2265
}
2266
else if (AstName(lexer.current().name) == "write")
2267
{
2268
accessLocation = lexer.current().location;
2269
access = AstTableAccess::Write;
2270
lexer.next();
2271
}
2272
}
2273
2274
if (lexer.current().type == '[')
2275
{
2276
const Lexeme begin = lexer.current();
2277
nextLexeme(); // [
2278
2279
if ((lexer.current().type == Lexeme::RawString || lexer.current().type == Lexeme::QuotedString) && lexer.lookahead().type == ']')
2280
{
2281
CstExprConstantString::QuoteStyle style;
2282
unsigned int blockDepth = 0;
2283
if (options.storeCstData)
2284
std::tie(style, blockDepth) = extractStringDetails();
2285
2286
Position stringPosition = lexer.current().location.begin;
2287
AstArray<char> sourceString;
2288
std::optional<AstArray<char>> chars = parseCharArray(options.storeCstData ? &sourceString : nullptr);
2289
2290
Position indexerClosePosition = lexer.current().location.begin;
2291
expectMatchAndConsume(']', begin);
2292
Position colonPosition = lexer.current().location.begin;
2293
expectAndConsume(':', "table field");
2294
2295
AstType* type = parseType();
2296
2297
// since AstName contains a char*, it can't contain null
2298
bool containsNull = chars && (memchr(chars->data, 0, chars->size) != nullptr);
2299
2300
if (chars && !containsNull)
2301
{
2302
props.push_back(AstTableProp{AstName(chars->data), begin.location, type, access, accessLocation});
2303
if (options.storeCstData)
2304
cstItems.push_back(
2305
CstTypeTable::Item{
2306
CstTypeTable::Item::Kind::StringProperty,
2307
begin.location.begin,
2308
indexerClosePosition,
2309
colonPosition,
2310
tableSeparator(),
2311
lexer.current().location.begin,
2312
allocator.alloc<CstExprConstantString>(sourceString, style, blockDepth),
2313
stringPosition
2314
}
2315
);
2316
}
2317
else
2318
report(begin.location, "String literal contains malformed escape sequence or \\0");
2319
}
2320
else
2321
{
2322
if (indexer)
2323
{
2324
// maybe we don't need to parse the entire badIndexer...
2325
// however, we either have { or [ to lint, not the entire table type or the bad indexer.
2326
AstTableIndexer* badIndexer = parseTableIndexer(access, accessLocation, begin).node;
2327
2328
// we lose all additional indexer expressions from the AST after error recovery here
2329
report(badIndexer->location, "Cannot have more than one table indexer");
2330
}
2331
else
2332
{
2333
auto tableIndexerResult = parseTableIndexer(access, accessLocation, begin);
2334
indexer = tableIndexerResult.node;
2335
if (options.storeCstData)
2336
cstItems.push_back(
2337
CstTypeTable::Item{
2338
CstTypeTable::Item::Kind::Indexer,
2339
tableIndexerResult.indexerOpenPosition,
2340
tableIndexerResult.indexerClosePosition,
2341
tableIndexerResult.colonPosition,
2342
tableSeparator(),
2343
lexer.current().location.begin,
2344
}
2345
);
2346
}
2347
}
2348
}
2349
else if (props.empty() && !indexer && !(lexer.current().type == Lexeme::Name && lexer.lookahead().type == ':'))
2350
{
2351
AstType* type = parseType();
2352
2353
// array-like table type: {T} desugars into {[number]: T}
2354
isArray = true;
2355
if (FFlag::DesugaredArrayTypeReferenceIsEmpty)
2356
{
2357
Location nullTypeLocation = Location(start.begin, 0);
2358
AstType* index = allocator.alloc<AstTypeReference>(nullTypeLocation, std::nullopt, nameNumber, std::nullopt, nullTypeLocation);
2359
indexer = allocator.alloc<AstTableIndexer>(AstTableIndexer{index, type, type->location, access, accessLocation});
2360
}
2361
else
2362
{
2363
AstType* index = allocator.alloc<AstTypeReference>(type->location, std::nullopt, nameNumber, std::nullopt, type->location);
2364
indexer = allocator.alloc<AstTableIndexer>(AstTableIndexer{index, type, type->location, access, accessLocation});
2365
}
2366
2367
break;
2368
}
2369
else
2370
{
2371
std::optional<Name> name = parseNameOpt("table field");
2372
2373
if (!name)
2374
break;
2375
2376
Position colonPosition = lexer.current().location.begin;
2377
expectAndConsume(':', "table field");
2378
2379
AstType* type = parseType(inDeclarationContext);
2380
2381
props.push_back(AstTableProp{name->name, name->location, type, access, accessLocation});
2382
if (options.storeCstData)
2383
cstItems.push_back(
2384
CstTypeTable::Item{
2385
CstTypeTable::Item::Kind::Property,
2386
Position{0, 0},
2387
Position{0, 0},
2388
colonPosition,
2389
tableSeparator(),
2390
lexer.current().location.begin
2391
}
2392
);
2393
}
2394
2395
if (lexer.current().type == ',' || lexer.current().type == ';')
2396
{
2397
nextLexeme();
2398
}
2399
else
2400
{
2401
if (lexer.current().type != '}')
2402
break;
2403
}
2404
}
2405
2406
Location end = lexer.current().location;
2407
2408
if (!expectMatchAndConsume('}', matchBrace, /* searchForMissing = */ true))
2409
end = lexer.previousLocation();
2410
2411
AstTypeTable* node = allocator.alloc<AstTypeTable>(Location(start, end), copy(props), indexer);
2412
if (options.storeCstData)
2413
cstNodeMap[node] = allocator.alloc<CstTypeTable>(copy(cstItems), isArray);
2414
return node;
2415
}
2416
2417
// ReturnType ::= Type | `(' TypeList `)'
2418
// FunctionType ::= [`<' varlist `>'] `(' [TypeList] `)' `->` ReturnType
2419
AstTypeOrPack Parser::parseFunctionType(bool allowPack, const AstArray<AstAttr*>& attributes)
2420
{
2421
incrementRecursionCounter("type annotation");
2422
2423
bool forceFunctionType = lexer.current().type == '<';
2424
2425
Lexeme begin = lexer.current();
2426
2427
Position genericsOpenPosition{0, 0};
2428
AstArray<Position> genericsCommaPositions;
2429
Position genericsClosePosition{0, 0};
2430
auto [generics, genericPacks] = options.storeCstData
2431
? parseGenericTypeList(
2432
/* withDefaultValues= */ false, &genericsOpenPosition, &genericsCommaPositions, &genericsClosePosition
2433
)
2434
: parseGenericTypeList(/* withDefaultValues= */ false);
2435
2436
Lexeme parameterStart = lexer.current();
2437
2438
expectAndConsume('(', "function parameters");
2439
2440
matchRecoveryStopOnToken[Lexeme::SkinnyArrow]++;
2441
2442
TempVector<AstType*> params(scratchType);
2443
TempVector<std::optional<AstArgumentName>> names(scratchOptArgName);
2444
TempVector<std::optional<Position>> nameColonPositions(scratchOptPosition);
2445
TempVector<Position> argCommaPositions(scratchPosition);
2446
AstTypePack* varargAnnotation = nullptr;
2447
2448
if (lexer.current().type != ')')
2449
{
2450
if (options.storeCstData)
2451
varargAnnotation = parseTypeList(params, names, &argCommaPositions, &nameColonPositions);
2452
else
2453
varargAnnotation = parseTypeList(params, names);
2454
}
2455
2456
Location closeArgsLocation = lexer.current().location;
2457
expectMatchAndConsume(')', parameterStart, true);
2458
2459
matchRecoveryStopOnToken[Lexeme::SkinnyArrow]--;
2460
2461
AstArray<AstType*> paramTypes = copy(params);
2462
2463
if (!names.empty())
2464
forceFunctionType = true;
2465
2466
bool returnTypeIntroducer = lexer.current().type == Lexeme::SkinnyArrow || lexer.current().type == ':';
2467
2468
// Not a function at all. Just a parenthesized type. Or maybe a type pack with a single element
2469
if (params.size() == 1 && !varargAnnotation && !forceFunctionType && !returnTypeIntroducer)
2470
{
2471
if (allowPack)
2472
{
2473
AstTypePackExplicit* node = allocator.alloc<AstTypePackExplicit>(begin.location, AstTypeList{paramTypes, nullptr});
2474
if (options.storeCstData)
2475
cstNodeMap[node] =
2476
allocator.alloc<CstTypePackExplicit>(parameterStart.location.begin, closeArgsLocation.begin, copy(argCommaPositions));
2477
return {{}, node};
2478
}
2479
else
2480
{
2481
return {allocator.alloc<AstTypeGroup>(Location(parameterStart.location, closeArgsLocation), params[0]), {}};
2482
}
2483
}
2484
2485
if (!forceFunctionType && !returnTypeIntroducer && allowPack)
2486
{
2487
AstTypePackExplicit* node = allocator.alloc<AstTypePackExplicit>(begin.location, AstTypeList{paramTypes, varargAnnotation});
2488
if (options.storeCstData)
2489
cstNodeMap[node] = allocator.alloc<CstTypePackExplicit>(parameterStart.location.begin, closeArgsLocation.begin, copy(argCommaPositions));
2490
return {{}, node};
2491
}
2492
2493
AstArray<std::optional<AstArgumentName>> paramNames = copy(names);
2494
2495
Position returnArrowPosition = lexer.current().location.begin;
2496
AstType* node = parseFunctionTypeTail(begin, attributes, generics, genericPacks, paramTypes, paramNames, varargAnnotation);
2497
if (options.storeCstData && node->is<AstTypeFunction>())
2498
{
2499
cstNodeMap[node] = allocator.alloc<CstTypeFunction>(
2500
genericsOpenPosition,
2501
genericsCommaPositions,
2502
genericsClosePosition,
2503
parameterStart.location.begin,
2504
copy(nameColonPositions),
2505
copy(argCommaPositions),
2506
closeArgsLocation.begin,
2507
returnArrowPosition
2508
);
2509
}
2510
return {node, {}};
2511
}
2512
2513
AstType* Parser::parseFunctionTypeTail(
2514
const Lexeme& begin,
2515
const AstArray<AstAttr*>& attributes,
2516
AstArray<AstGenericType*> generics,
2517
AstArray<AstGenericTypePack*> genericPacks,
2518
AstArray<AstType*> params,
2519
AstArray<std::optional<AstArgumentName>> paramNames,
2520
AstTypePack* varargAnnotation
2521
)
2522
{
2523
incrementRecursionCounter("type annotation");
2524
2525
if (lexer.current().type == ':')
2526
{
2527
report(lexer.current().location, "Return types in function type annotations are written after '->' instead of ':'");
2528
lexer.next();
2529
}
2530
// Users occasionally write '()' as the 'unit' type when they actually want to use 'nil', here we'll try to give a more specific error
2531
else if (lexer.current().type != Lexeme::SkinnyArrow && generics.size == 0 && genericPacks.size == 0 && params.size == 0)
2532
{
2533
report(Location(begin.location, lexer.previousLocation()), "Expected '->' after '()' when parsing function type; did you mean 'nil'?");
2534
2535
return allocator.alloc<AstTypeReference>(begin.location, std::nullopt, nameNil, std::nullopt, begin.location);
2536
}
2537
else
2538
{
2539
expectAndConsume(Lexeme::SkinnyArrow, "function type");
2540
}
2541
2542
auto returnType = parseReturnType();
2543
LUAU_ASSERT(returnType);
2544
2545
AstTypeList paramTypes = AstTypeList{params, varargAnnotation};
2546
return allocator.alloc<AstTypeFunction>(
2547
Location(begin.location, returnType->location), attributes, generics, genericPacks, paramTypes, paramNames, returnType
2548
);
2549
}
2550
2551
static bool isTypeFollow(Lexeme::Type c)
2552
{
2553
return c == '|' || c == '?' || c == '&';
2554
}
2555
2556
// Type ::=
2557
// nil |
2558
// Name[`.' Name] [`<' namelist `>'] |
2559
// `{' [PropList] `}' |
2560
// `(' [TypeList] `)' `->` ReturnType
2561
// `typeof` Type
2562
AstType* Parser::parseTypeSuffix(AstType* type, const Location& begin)
2563
{
2564
TempVector<AstType*> parts(scratchType);
2565
TempVector<Position> separatorPositions(scratchPosition);
2566
std::optional<Position> leadingPosition = std::nullopt;
2567
2568
if (type != nullptr)
2569
parts.push_back(type);
2570
2571
incrementRecursionCounter("type annotation");
2572
2573
bool isUnion = false;
2574
bool isIntersection = false;
2575
unsigned int optionalCount = 0;
2576
2577
Location location = begin;
2578
2579
while (true)
2580
{
2581
Lexeme::Type c = lexer.current().type;
2582
Position separatorPosition = lexer.current().location.begin;
2583
if (c == '|')
2584
{
2585
nextLexeme();
2586
2587
unsigned int oldRecursionCount = recursionCounter;
2588
parts.push_back(parseSimpleType(/* allowPack= */ false).type);
2589
recursionCounter = oldRecursionCount;
2590
2591
isUnion = true;
2592
2593
if (options.storeCstData)
2594
{
2595
if (type == nullptr && !leadingPosition.has_value())
2596
leadingPosition = separatorPosition;
2597
else
2598
separatorPositions.push_back(separatorPosition);
2599
}
2600
}
2601
else if (c == '?')
2602
{
2603
LUAU_ASSERT(parts.size() >= 1);
2604
2605
Location loc = lexer.current().location;
2606
nextLexeme();
2607
2608
parts.push_back(allocator.alloc<AstTypeOptional>(Location(loc)));
2609
optionalCount++;
2610
2611
isUnion = true;
2612
}
2613
else if (c == '&')
2614
{
2615
nextLexeme();
2616
2617
unsigned int oldRecursionCount = recursionCounter;
2618
parts.push_back(parseSimpleType(/* allowPack= */ false).type);
2619
recursionCounter = oldRecursionCount;
2620
2621
isIntersection = true;
2622
2623
if (options.storeCstData)
2624
{
2625
if (type == nullptr && !leadingPosition.has_value())
2626
leadingPosition = separatorPosition;
2627
else
2628
separatorPositions.push_back(separatorPosition);
2629
}
2630
}
2631
else if (c == Lexeme::Dot3)
2632
{
2633
report(lexer.current().location, "Unexpected '...' after type annotation");
2634
nextLexeme();
2635
}
2636
else
2637
break;
2638
2639
if (parts.size() > unsigned(FInt::LuauTypeLengthLimit) + optionalCount)
2640
ParseError::raise(parts.back()->location, "Exceeded allowed type length; simplify your type annotation to make the code compile");
2641
}
2642
2643
if (parts.size() == 1 && !isUnion && !isIntersection)
2644
return parts[0];
2645
if (isUnion && isIntersection)
2646
{
2647
return reportTypeError(
2648
Location(begin, parts.back()->location),
2649
copy(parts),
2650
"Mixing union and intersection types is not allowed; consider wrapping in parentheses."
2651
);
2652
}
2653
2654
location.end = parts.back()->location.end;
2655
2656
if (isUnion)
2657
{
2658
AstTypeUnion* node = allocator.alloc<AstTypeUnion>(location, copy(parts));
2659
if (options.storeCstData)
2660
cstNodeMap[node] = allocator.alloc<CstTypeUnion>(leadingPosition, copy(separatorPositions));
2661
return node;
2662
}
2663
2664
if (isIntersection)
2665
{
2666
AstTypeIntersection* node = allocator.alloc<AstTypeIntersection>(location, copy(parts));
2667
if (options.storeCstData)
2668
cstNodeMap[node] = allocator.alloc<CstTypeIntersection>(leadingPosition, copy(separatorPositions));
2669
return node;
2670
}
2671
2672
LUAU_ASSERT(false);
2673
ParseError::raise(begin, "Composite type was not an intersection or union.");
2674
}
2675
2676
AstTypeOrPack Parser::parseSimpleTypeOrPack()
2677
{
2678
unsigned int oldRecursionCount = recursionCounter;
2679
// recursion counter is incremented in parseSimpleType
2680
2681
Location begin = lexer.current().location;
2682
2683
auto [type, typePack] = parseSimpleType(/* allowPack= */ true);
2684
2685
if (typePack)
2686
{
2687
LUAU_ASSERT(!type);
2688
return {{}, typePack};
2689
}
2690
2691
recursionCounter = oldRecursionCount;
2692
2693
return {parseTypeSuffix(type, begin), {}};
2694
}
2695
2696
AstType* Parser::parseType(bool inDeclarationContext)
2697
{
2698
unsigned int oldRecursionCount = recursionCounter;
2699
// recursion counter is incremented in parseSimpleType and/or parseTypeSuffix
2700
2701
Location begin = lexer.current().location;
2702
2703
AstType* type = nullptr;
2704
2705
Lexeme::Type c = lexer.current().type;
2706
if (c != '|' && c != '&')
2707
{
2708
type = parseSimpleType(/* allowPack= */ false, /* in declaration context */ inDeclarationContext).type;
2709
recursionCounter = oldRecursionCount;
2710
}
2711
2712
AstType* typeWithSuffix = parseTypeSuffix(type, begin);
2713
recursionCounter = oldRecursionCount;
2714
2715
return typeWithSuffix;
2716
}
2717
2718
// Type ::= nil | Name[`.' Name] [ `<' Type [`,' ...] `>' ] | `typeof' `(' expr `)' | `{' [PropList] `}'
2719
// | [`<' varlist `>'] `(' [TypeList] `)' `->` ReturnType
2720
AstTypeOrPack Parser::parseSimpleType(bool allowPack, bool inDeclarationContext)
2721
{
2722
incrementRecursionCounter("type annotation");
2723
2724
Location start = lexer.current().location;
2725
2726
AstArray<AstAttr*> attributes{nullptr, 0};
2727
2728
if (lexer.current().type == Lexeme::Attribute || lexer.current().type == Lexeme::AttributeOpen)
2729
{
2730
if (!inDeclarationContext)
2731
{
2732
return {reportTypeError(start, {}, "attributes are not allowed in declaration context")};
2733
}
2734
else
2735
{
2736
attributes = Parser::parseAttributes();
2737
return parseFunctionType(allowPack, attributes);
2738
}
2739
}
2740
else if (lexer.current().type == Lexeme::ReservedNil)
2741
{
2742
nextLexeme();
2743
return {allocator.alloc<AstTypeReference>(start, std::nullopt, nameNil, std::nullopt, start), {}};
2744
}
2745
else if (lexer.current().type == Lexeme::ReservedTrue)
2746
{
2747
nextLexeme();
2748
return {allocator.alloc<AstTypeSingletonBool>(start, true)};
2749
}
2750
else if (lexer.current().type == Lexeme::ReservedFalse)
2751
{
2752
nextLexeme();
2753
return {allocator.alloc<AstTypeSingletonBool>(start, false)};
2754
}
2755
else if (lexer.current().type == Lexeme::RawString || lexer.current().type == Lexeme::QuotedString)
2756
{
2757
CstExprConstantString::QuoteStyle style;
2758
unsigned int blockDepth = 0;
2759
if (options.storeCstData)
2760
std::tie(style, blockDepth) = extractStringDetails();
2761
2762
AstArray<char> originalString;
2763
if (std::optional<AstArray<char>> value = parseCharArray(options.storeCstData ? &originalString : nullptr))
2764
{
2765
AstArray<char> svalue = *value;
2766
auto node = allocator.alloc<AstTypeSingletonString>(start, svalue);
2767
if (options.storeCstData)
2768
cstNodeMap[node] = allocator.alloc<CstTypeSingletonString>(originalString, style, blockDepth);
2769
return {node};
2770
}
2771
else
2772
return {reportTypeError(start, {}, "String literal contains malformed escape sequence")};
2773
}
2774
else if (lexer.current().type == Lexeme::InterpStringBegin || lexer.current().type == Lexeme::InterpStringSimple)
2775
{
2776
parseInterpString();
2777
2778
return {reportTypeError(start, {}, "Interpolated string literals cannot be used as types")};
2779
}
2780
else if (lexer.current().type == Lexeme::BrokenString)
2781
{
2782
nextLexeme();
2783
return {reportTypeError(start, {}, "Malformed string; did you forget to finish it?")};
2784
}
2785
else if (lexer.current().type == Lexeme::Name)
2786
{
2787
std::optional<AstName> prefix;
2788
std::optional<Position> prefixPointPosition;
2789
std::optional<Location> prefixLocation;
2790
Name name = parseName("type name");
2791
2792
if (lexer.current().type == '.')
2793
{
2794
prefixPointPosition = lexer.current().location.begin;
2795
nextLexeme();
2796
2797
prefix = name.name;
2798
prefixLocation = name.location;
2799
name = parseIndexName("field name", *prefixPointPosition);
2800
}
2801
else if (lexer.current().type == Lexeme::Dot3)
2802
{
2803
report(lexer.current().location, "Unexpected '...' after type name; type pack is not allowed in this context");
2804
nextLexeme();
2805
}
2806
else if (name.name == "typeof")
2807
{
2808
Lexeme typeofBegin = lexer.current();
2809
expectAndConsume('(', "typeof type");
2810
2811
AstExpr* expr = parseExpr();
2812
2813
Location end = lexer.current().location;
2814
2815
expectMatchAndConsume(')', typeofBegin);
2816
2817
AstTypeTypeof* node = allocator.alloc<AstTypeTypeof>(Location(start, end), expr);
2818
if (options.storeCstData)
2819
cstNodeMap[node] = allocator.alloc<CstTypeTypeof>(typeofBegin.location.begin, end.begin);
2820
return {node, {}};
2821
}
2822
2823
bool hasParameters = false;
2824
AstArray<AstTypeOrPack> parameters{};
2825
Position parametersOpeningPosition{0, 0};
2826
TempVector<Position> parametersCommaPositions(scratchPosition);
2827
Position parametersClosingPosition{0, 0};
2828
2829
if (lexer.current().type == '<')
2830
{
2831
hasParameters = true;
2832
if (options.storeCstData)
2833
parameters = parseTypeParams(&parametersOpeningPosition, &parametersCommaPositions, &parametersClosingPosition);
2834
else
2835
parameters = parseTypeParams();
2836
}
2837
2838
Location end = lexer.previousLocation();
2839
2840
AstTypeReference* node =
2841
allocator.alloc<AstTypeReference>(Location(start, end), prefix, name.name, prefixLocation, name.location, hasParameters, parameters);
2842
if (options.storeCstData)
2843
cstNodeMap[node] = allocator.alloc<CstTypeReference>(
2844
prefixPointPosition, parametersOpeningPosition, copy(parametersCommaPositions), parametersClosingPosition
2845
);
2846
return {node, {}};
2847
}
2848
else if (lexer.current().type == '{')
2849
{
2850
return {parseTableType(/* inDeclarationContext */ inDeclarationContext), {}};
2851
}
2852
else if (lexer.current().type == '(' || lexer.current().type == '<')
2853
{
2854
return parseFunctionType(allowPack, AstArray<AstAttr*>({nullptr, 0}));
2855
}
2856
else if (lexer.current().type == Lexeme::ReservedFunction)
2857
{
2858
nextLexeme();
2859
2860
return {
2861
reportTypeError(
2862
start,
2863
{},
2864
"Using 'function' as a type annotation is not supported, consider replacing with a function type annotation e.g. '(...any) -> "
2865
"...any'"
2866
),
2867
{}
2868
};
2869
}
2870
else
2871
{
2872
// For a missing type annotation, capture 'space' between last token and the next one
2873
Location astErrorlocation(lexer.previousLocation().end, start.begin);
2874
// The parse error includes the next lexeme to make it easier to display where the error is (e.g. in an IDE or a CLI error message).
2875
// Including the current lexeme also makes the parse error consistent with other parse errors returned by Luau.
2876
Location parseErrorLocation(lexer.previousLocation().end, start.end);
2877
return {reportMissingTypeError(parseErrorLocation, astErrorlocation, "Expected type, got %s", lexer.current().toString().c_str()), {}};
2878
}
2879
}
2880
2881
AstTypePack* Parser::parseVariadicArgumentTypePack()
2882
{
2883
// Generic: a...
2884
if (lexer.current().type == Lexeme::Name && lexer.lookahead().type == Lexeme::Dot3)
2885
{
2886
Name name = parseName("generic name");
2887
Location end = lexer.current().location;
2888
2889
// This will not fail because of the lookahead guard.
2890
expectAndConsume(Lexeme::Dot3, "generic type pack annotation");
2891
AstTypePackGeneric* node = allocator.alloc<AstTypePackGeneric>(Location(name.location, end), name.name);
2892
if (options.storeCstData)
2893
cstNodeMap[node] = allocator.alloc<CstTypePackGeneric>(end.begin);
2894
return node;
2895
}
2896
// Variadic: T
2897
else
2898
{
2899
AstType* variadicAnnotation = parseType();
2900
return allocator.alloc<AstTypePackVariadic>(variadicAnnotation->location, variadicAnnotation);
2901
}
2902
}
2903
2904
AstTypePack* Parser::parseTypePack()
2905
{
2906
// Variadic: ...T
2907
if (lexer.current().type == Lexeme::Dot3)
2908
{
2909
Location start = lexer.current().location;
2910
nextLexeme();
2911
AstType* varargTy = parseType();
2912
return allocator.alloc<AstTypePackVariadic>(Location(start, varargTy->location), varargTy);
2913
}
2914
// Generic: a...
2915
else if (lexer.current().type == Lexeme::Name && lexer.lookahead().type == Lexeme::Dot3)
2916
{
2917
Name name = parseName("generic name");
2918
Location end = lexer.current().location;
2919
2920
// This will not fail because of the lookahead guard.
2921
expectAndConsume(Lexeme::Dot3, "generic type pack annotation");
2922
AstTypePackGeneric* node = allocator.alloc<AstTypePackGeneric>(Location(name.location, end), name.name);
2923
if (options.storeCstData)
2924
cstNodeMap[node] = allocator.alloc<CstTypePackGeneric>(end.begin);
2925
return node;
2926
}
2927
2928
// TODO: shouldParseTypePack can be removed and parseTypePack can be called unconditionally instead
2929
LUAU_ASSERT(!"parseTypePack can't be called if shouldParseTypePack() returned false");
2930
return nullptr;
2931
}
2932
2933
std::optional<AstExprUnary::Op> Parser::parseUnaryOp(const Lexeme& l)
2934
{
2935
if (l.type == Lexeme::ReservedNot)
2936
return AstExprUnary::Not;
2937
else if (l.type == '-')
2938
return AstExprUnary::Minus;
2939
else if (l.type == '#')
2940
return AstExprUnary::Len;
2941
else
2942
return std::nullopt;
2943
}
2944
2945
std::optional<AstExprBinary::Op> Parser::parseBinaryOp(const Lexeme& l)
2946
{
2947
if (l.type == '+')
2948
return AstExprBinary::Add;
2949
else if (l.type == '-')
2950
return AstExprBinary::Sub;
2951
else if (l.type == '*')
2952
return AstExprBinary::Mul;
2953
else if (l.type == '/')
2954
return AstExprBinary::Div;
2955
else if (l.type == Lexeme::FloorDiv)
2956
return AstExprBinary::FloorDiv;
2957
else if (l.type == '%')
2958
return AstExprBinary::Mod;
2959
else if (l.type == '^')
2960
return AstExprBinary::Pow;
2961
else if (l.type == Lexeme::Dot2)
2962
return AstExprBinary::Concat;
2963
else if (l.type == Lexeme::NotEqual)
2964
return AstExprBinary::CompareNe;
2965
else if (l.type == Lexeme::Equal)
2966
return AstExprBinary::CompareEq;
2967
else if (l.type == '<')
2968
return AstExprBinary::CompareLt;
2969
else if (l.type == Lexeme::LessEqual)
2970
return AstExprBinary::CompareLe;
2971
else if (l.type == '>')
2972
return AstExprBinary::CompareGt;
2973
else if (l.type == Lexeme::GreaterEqual)
2974
return AstExprBinary::CompareGe;
2975
else if (l.type == Lexeme::ReservedAnd)
2976
return AstExprBinary::And;
2977
else if (l.type == Lexeme::ReservedOr)
2978
return AstExprBinary::Or;
2979
else
2980
return std::nullopt;
2981
}
2982
2983
std::optional<AstExprBinary::Op> Parser::parseCompoundOp(const Lexeme& l)
2984
{
2985
if (l.type == Lexeme::AddAssign)
2986
return AstExprBinary::Add;
2987
else if (l.type == Lexeme::SubAssign)
2988
return AstExprBinary::Sub;
2989
else if (l.type == Lexeme::MulAssign)
2990
return AstExprBinary::Mul;
2991
else if (l.type == Lexeme::DivAssign)
2992
return AstExprBinary::Div;
2993
else if (l.type == Lexeme::FloorDivAssign)
2994
return AstExprBinary::FloorDiv;
2995
else if (l.type == Lexeme::ModAssign)
2996
return AstExprBinary::Mod;
2997
else if (l.type == Lexeme::PowAssign)
2998
return AstExprBinary::Pow;
2999
else if (l.type == Lexeme::ConcatAssign)
3000
return AstExprBinary::Concat;
3001
else
3002
return std::nullopt;
3003
}
3004
3005
std::optional<AstExprUnary::Op> Parser::checkUnaryConfusables()
3006
{
3007
const Lexeme& curr = lexer.current();
3008
3009
// early-out: need to check if this is a possible confusable quickly
3010
if (curr.type != '!')
3011
return {};
3012
3013
// slow path: possible confusable
3014
Location start = curr.location;
3015
3016
if (curr.type == '!')
3017
{
3018
report(start, "Unexpected '!'; did you mean 'not'?");
3019
return AstExprUnary::Not;
3020
}
3021
3022
return {};
3023
}
3024
3025
std::optional<AstExprBinary::Op> Parser::checkBinaryConfusables(const BinaryOpPriority binaryPriority[], unsigned int limit)
3026
{
3027
const Lexeme& curr = lexer.current();
3028
3029
// early-out: need to check if this is a possible confusable quickly
3030
if (curr.type != '&' && curr.type != '|' && curr.type != '!')
3031
return {};
3032
3033
// slow path: possible confusable
3034
Location start = curr.location;
3035
Lexeme next = lexer.lookahead();
3036
3037
if (curr.type == '&' && next.type == '&' && curr.location.end == next.location.begin && binaryPriority[AstExprBinary::And].left > limit)
3038
{
3039
nextLexeme();
3040
report(Location(start, next.location), "Unexpected '&&'; did you mean 'and'?");
3041
return AstExprBinary::And;
3042
}
3043
else if (curr.type == '|' && next.type == '|' && curr.location.end == next.location.begin && binaryPriority[AstExprBinary::Or].left > limit)
3044
{
3045
nextLexeme();
3046
report(Location(start, next.location), "Unexpected '||'; did you mean 'or'?");
3047
return AstExprBinary::Or;
3048
}
3049
else if (curr.type == '!' && next.type == '=' && curr.location.end == next.location.begin &&
3050
binaryPriority[AstExprBinary::CompareNe].left > limit)
3051
{
3052
nextLexeme();
3053
report(Location(start, next.location), "Unexpected '!='; did you mean '~='?");
3054
return AstExprBinary::CompareNe;
3055
}
3056
3057
return std::nullopt;
3058
}
3059
3060
// subexpr -> (asexp | unop subexpr) { binop subexpr }
3061
// where `binop' is any binary operator with a priority higher than `limit'
3062
AstExpr* Parser::parseExpr(unsigned int limit)
3063
{
3064
static const BinaryOpPriority binaryPriority[] = {
3065
{6, 6}, // '+'
3066
{6, 6}, // '-'
3067
{7, 7}, // '*'
3068
{7, 7}, // '/'
3069
{7, 7}, // '//'
3070
{7, 7}, // `%'
3071
{10, 9}, // power (right associative)
3072
{5, 4}, // concat (right associative)
3073
{3, 3}, // inequality
3074
{3, 3}, // equality
3075
{3, 3}, // '<'
3076
{3, 3}, // '<='
3077
{3, 3}, // '>'
3078
{3, 3}, // '>='
3079
{2, 2}, // logical and
3080
{1, 1} // logical or
3081
};
3082
3083
static_assert(sizeof(binaryPriority) / sizeof(binaryPriority[0]) == size_t(AstExprBinary::Op__Count), "binaryPriority needs an entry per op");
3084
3085
unsigned int oldRecursionCount = recursionCounter;
3086
3087
// this handles recursive calls to parseSubExpr/parseExpr
3088
incrementRecursionCounter("expression");
3089
3090
const unsigned int unaryPriority = 8;
3091
3092
Location start = lexer.current().location;
3093
3094
AstExpr* expr;
3095
3096
std::optional<AstExprUnary::Op> uop = parseUnaryOp(lexer.current());
3097
3098
if (!uop)
3099
uop = checkUnaryConfusables();
3100
3101
if (uop)
3102
{
3103
Position opPosition = lexer.current().location.begin;
3104
nextLexeme();
3105
3106
AstExpr* subexpr = parseExpr(unaryPriority);
3107
3108
expr = allocator.alloc<AstExprUnary>(Location(start, subexpr->location), *uop, subexpr);
3109
if (options.storeCstData)
3110
cstNodeMap[expr] = allocator.alloc<CstExprOp>(opPosition);
3111
}
3112
else
3113
{
3114
expr = parseAssertionExpr();
3115
}
3116
3117
// expand while operators have priorities higher than `limit'
3118
std::optional<AstExprBinary::Op> op = parseBinaryOp(lexer.current());
3119
3120
if (!op)
3121
op = checkBinaryConfusables(binaryPriority, limit);
3122
3123
while (op && binaryPriority[*op].left > limit)
3124
{
3125
Position opPosition = lexer.current().location.begin;
3126
nextLexeme();
3127
3128
// read sub-expression with higher priority
3129
AstExpr* next = parseExpr(binaryPriority[*op].right);
3130
3131
expr = allocator.alloc<AstExprBinary>(Location(start, next->location), *op, expr, next);
3132
if (options.storeCstData)
3133
cstNodeMap[expr] = allocator.alloc<CstExprOp>(opPosition);
3134
op = parseBinaryOp(lexer.current());
3135
3136
if (!op)
3137
op = checkBinaryConfusables(binaryPriority, limit);
3138
3139
// note: while the parser isn't recursive here, we're generating recursive structures of unbounded depth
3140
incrementRecursionCounter("expression");
3141
}
3142
3143
recursionCounter = oldRecursionCount;
3144
3145
return expr;
3146
}
3147
3148
// NAME
3149
AstExpr* Parser::parseNameExpr(const char* context)
3150
{
3151
std::optional<Name> name = parseNameOpt(context);
3152
3153
if (!name)
3154
return allocator.alloc<AstExprError>(lexer.current().location, copy<AstExpr*>({}), unsigned(parseErrors.size() - 1));
3155
3156
AstLocal* const* value = localMap.find(name->name);
3157
3158
if (value && *value)
3159
{
3160
AstLocal* local = *value;
3161
3162
if (local->functionDepth < typeFunctionDepth)
3163
return reportExprError(lexer.current().location, {}, "Type function cannot reference outer local '%s'", local->name.value);
3164
3165
return allocator.alloc<AstExprLocal>(name->location, local, local->functionDepth != functionStack.size() - 1);
3166
}
3167
3168
return allocator.alloc<AstExprGlobal>(name->location, name->name);
3169
}
3170
3171
// prefixexp -> NAME | '(' expr ')'
3172
AstExpr* Parser::parsePrefixExpr()
3173
{
3174
if (lexer.current().type == '(')
3175
{
3176
Position start = lexer.current().location.begin;
3177
3178
MatchLexeme matchParen = lexer.current();
3179
nextLexeme();
3180
3181
AstExpr* expr = parseExpr();
3182
3183
Position end = lexer.current().location.end;
3184
3185
if (lexer.current().type != ')')
3186
{
3187
const char* suggestion = (lexer.current().type == '=') ? "; did you mean to use '{' when defining a table?" : nullptr;
3188
3189
expectMatchAndConsumeFail(static_cast<Lexeme::Type>(')'), matchParen, suggestion);
3190
3191
end = lexer.previousLocation().end;
3192
}
3193
else
3194
{
3195
nextLexeme();
3196
}
3197
3198
return allocator.alloc<AstExprGroup>(Location(start, end), expr);
3199
}
3200
else
3201
{
3202
return parseNameExpr("expression");
3203
}
3204
}
3205
3206
// primaryexp -> prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs | funcargs }
3207
AstExpr* Parser::parsePrimaryExpr(bool asStatement)
3208
{
3209
Position start = lexer.current().location.begin;
3210
3211
AstExpr* expr = parsePrefixExpr();
3212
3213
unsigned int oldRecursionCount = recursionCounter;
3214
3215
while (true)
3216
{
3217
if (lexer.current().type == '.')
3218
{
3219
Position opPosition = lexer.current().location.begin;
3220
nextLexeme();
3221
3222
Name index = parseIndexName(nullptr, opPosition);
3223
3224
expr = allocator.alloc<AstExprIndexName>(Location(start, index.location.end), expr, index.name, index.location, opPosition, '.');
3225
}
3226
else if (lexer.current().type == '[')
3227
{
3228
MatchLexeme matchBracket = lexer.current();
3229
nextLexeme();
3230
3231
AstExpr* index = parseExpr();
3232
3233
Position closeBracketPosition = lexer.current().location.begin;
3234
Position end = lexer.current().location.end;
3235
3236
expectMatchAndConsume(']', matchBracket);
3237
3238
expr = allocator.alloc<AstExprIndexExpr>(Location(start, end), expr, index);
3239
if (options.storeCstData)
3240
cstNodeMap[expr] = allocator.alloc<CstExprIndexExpr>(matchBracket.position, closeBracketPosition);
3241
}
3242
else if (lexer.current().type == ':')
3243
{
3244
expr = parseMethodCall(start, expr);
3245
}
3246
else if (lexer.current().type == '(')
3247
{
3248
// This error is handled inside 'parseFunctionArgs' as well, but for better error recovery we need to break out the current loop here
3249
if (!asStatement && expr->location.end.line != lexer.current().location.begin.line)
3250
{
3251
reportAmbiguousCallError();
3252
break;
3253
}
3254
3255
expr = parseFunctionArgs(expr, false);
3256
}
3257
else if (lexer.current().type == '{' || lexer.current().type == Lexeme::RawString || lexer.current().type == Lexeme::QuotedString)
3258
{
3259
expr = parseFunctionArgs(expr, false);
3260
}
3261
else if (lexer.current().type == '<' && lexer.lookahead().type == '<')
3262
{
3263
expr = parseExplicitTypeInstantiationExpr(start, *expr);
3264
}
3265
else
3266
{
3267
break;
3268
}
3269
3270
// note: while the parser isn't recursive here, we're generating recursive structures of unbounded depth
3271
incrementRecursionCounter("expression");
3272
}
3273
3274
recursionCounter = oldRecursionCount;
3275
3276
return expr;
3277
}
3278
3279
AstExpr* Parser::parseMethodCall(Position start, AstExpr* expr)
3280
{
3281
Position opPosition = lexer.current().location.begin;
3282
nextLexeme();
3283
3284
Name index = parseIndexName("method name", opPosition);
3285
AstExpr* func = allocator.alloc<AstExprIndexName>(Location(start, index.location.end), expr, index.name, index.location, opPosition, ':');
3286
3287
AstArray<AstTypeOrPack> typeArguments;
3288
CstTypeInstantiation* cstTypeArguments = options.storeCstData ? allocator.alloc<CstTypeInstantiation>() : nullptr;
3289
3290
if (lexer.current().type == '<' && lexer.lookahead().type == '<')
3291
{
3292
typeArguments = parseTypeInstantiationExpr(cstTypeArguments);
3293
}
3294
3295
expr = parseFunctionArgs(func, true);
3296
3297
if (options.storeCstData)
3298
{
3299
CstNode** cstNode = cstNodeMap.find(expr);
3300
if (cstNode)
3301
{
3302
CstExprCall* exprCall = (*cstNode)->as<CstExprCall>();
3303
LUAU_ASSERT(exprCall);
3304
exprCall->explicitTypes = cstTypeArguments;
3305
}
3306
}
3307
3308
// If we have an AstExprCall, fill in the type arguments
3309
if (auto call = expr->as<AstExprCall>(); call && typeArguments.size > 0)
3310
call->typeArguments = typeArguments;
3311
3312
return expr;
3313
}
3314
3315
// asexp -> simpleexp [`::' Type]
3316
AstExpr* Parser::parseAssertionExpr()
3317
{
3318
Location start = lexer.current().location;
3319
AstExpr* expr = parseSimpleExpr();
3320
3321
if (lexer.current().type == Lexeme::DoubleColon)
3322
{
3323
CstExprTypeAssertion* cstNode = nullptr;
3324
if (options.storeCstData)
3325
{
3326
Position opPosition = lexer.current().location.begin;
3327
cstNode = allocator.alloc<CstExprTypeAssertion>(opPosition);
3328
}
3329
nextLexeme();
3330
AstType* annotation = parseType();
3331
AstExprTypeAssertion* node = allocator.alloc<AstExprTypeAssertion>(Location(start, annotation->location), expr, annotation);
3332
if (options.storeCstData)
3333
cstNodeMap[node] = cstNode;
3334
return node;
3335
}
3336
else
3337
return expr;
3338
}
3339
3340
static ConstantNumberParseResult parseInteger(double& result, const char* data, int base)
3341
{
3342
LUAU_ASSERT(base == 2 || base == 16);
3343
3344
char* end = nullptr;
3345
unsigned long long value = strtoull(data, &end, base);
3346
3347
if (*end != 0)
3348
return ConstantNumberParseResult::Malformed;
3349
3350
result = double(value);
3351
3352
if (value == ULLONG_MAX && errno == ERANGE)
3353
{
3354
// 'errno' might have been set before we called 'strtoull', but we don't want the overhead of resetting a TLS variable on each call
3355
// so we only reset it when we get a result that might be an out-of-range error and parse again to make sure
3356
errno = 0;
3357
value = strtoull(data, &end, base);
3358
3359
if (errno == ERANGE)
3360
return base == 2 ? ConstantNumberParseResult::BinOverflow : ConstantNumberParseResult::HexOverflow;
3361
}
3362
3363
if (value >= (1ull << 53) && static_cast<unsigned long long>(result) != value)
3364
return ConstantNumberParseResult::Imprecise;
3365
3366
return ConstantNumberParseResult::Ok;
3367
}
3368
3369
static ConstantNumberParseResult parseInteger64(int64_t& result, const char* data, int base)
3370
{
3371
LUAU_ASSERT(base == 2 || base == 10 || base == 16);
3372
3373
char* end = nullptr;
3374
3375
if (base == 10)
3376
{
3377
result = strtoll(data, &end, 10);
3378
3379
if (end == data || *end != 'i' || end[1] != '\0')
3380
return ConstantNumberParseResult::Malformed;
3381
3382
if (((result == LLONG_MIN) || (result == LLONG_MAX)) && (errno == ERANGE))
3383
{
3384
// 'errno' might have been set before we called 'strtoll', but we don't want the overhead of resetting a TLS variable on each call
3385
// so we only reset it when we get a result that might be an out-of-range error and parse again to make sure
3386
errno = 0;
3387
result = strtoll(data, &end, 10);
3388
3389
if (errno == ERANGE)
3390
return ConstantNumberParseResult::IntOverflow;
3391
}
3392
}
3393
else
3394
{
3395
// hex and binary literals represent bit patterns covering the full uint64 range
3396
unsigned long long u = strtoull(data, &end, base);
3397
3398
if (end == data || *end != 'i' || end[1] != '\0')
3399
return ConstantNumberParseResult::Malformed;
3400
3401
if ((u == ULLONG_MAX) && (errno == ERANGE))
3402
{
3403
// 'errno' might have been set before we called 'strtoull', but we don't want the overhead of resetting a TLS variable on each call
3404
// so we only reset it when we get a result that might be an out-of-range error and parse again to make sure
3405
errno = 0;
3406
u = strtoull(data, &end, base);
3407
3408
if (errno == ERANGE)
3409
return base == 2 ? ConstantNumberParseResult::BinOverflow : ConstantNumberParseResult::HexOverflow;
3410
}
3411
3412
result = (int64_t)u;
3413
}
3414
3415
return ConstantNumberParseResult::Ok;
3416
}
3417
3418
static ConstantNumberParseResult parseDouble(double& result, const char* data)
3419
{
3420
// binary literal
3421
if (data[0] == '0' && (data[1] == 'b' || data[1] == 'B') && data[2])
3422
return parseInteger(result, data + 2, 2);
3423
3424
// hexadecimal literal
3425
if (data[0] == '0' && (data[1] == 'x' || data[1] == 'X') && data[2])
3426
return parseInteger(result, data, 16); // pass in '0x' prefix, it's handled by 'strtoull'
3427
3428
char* end = nullptr;
3429
double value = strtod(data, &end);
3430
3431
// trailing non-numeric characters
3432
if (*end != 0)
3433
return ConstantNumberParseResult::Malformed;
3434
3435
result = value;
3436
3437
// for linting, we detect integer constants that are parsed imprecisely
3438
// since the check is expensive we only perform it when the number is larger than the precise integer range
3439
if (value >= double(1ull << 53) && strspn(data, "0123456789") == strlen(data))
3440
{
3441
char repr[512];
3442
snprintf(repr, sizeof(repr), "%.0f", value);
3443
3444
if (strcmp(repr, data) != 0)
3445
return ConstantNumberParseResult::Imprecise;
3446
}
3447
3448
return ConstantNumberParseResult::Ok;
3449
}
3450
3451
// simpleexp -> NUMBER | STRING | NIL | true | false | ... | constructor | [attributes] FUNCTION body | primaryexp
3452
AstExpr* Parser::parseSimpleExpr()
3453
{
3454
Location start = lexer.current().location;
3455
3456
AstArray<AstAttr*> attributes{nullptr, 0};
3457
3458
if (lexer.current().type == Lexeme::Attribute || lexer.current().type == Lexeme::AttributeOpen)
3459
{
3460
attributes = parseAttributes();
3461
3462
if (lexer.current().type != Lexeme::ReservedFunction)
3463
{
3464
return reportExprError(
3465
start, {}, "Expected 'function' declaration after attribute, but got %s instead", lexer.current().toString().c_str()
3466
);
3467
}
3468
}
3469
3470
if (lexer.current().type == Lexeme::ReservedNil)
3471
{
3472
nextLexeme();
3473
3474
return allocator.alloc<AstExprConstantNil>(start);
3475
}
3476
else if (lexer.current().type == Lexeme::ReservedTrue)
3477
{
3478
nextLexeme();
3479
3480
return allocator.alloc<AstExprConstantBool>(start, true);
3481
}
3482
else if (lexer.current().type == Lexeme::ReservedFalse)
3483
{
3484
nextLexeme();
3485
3486
return allocator.alloc<AstExprConstantBool>(start, false);
3487
}
3488
else if (lexer.current().type == Lexeme::ReservedFunction)
3489
{
3490
Lexeme matchFunction = lexer.current();
3491
nextLexeme();
3492
3493
return parseFunctionBody(false, matchFunction, AstName(), nullptr, attributes).first;
3494
}
3495
else if (lexer.current().type == Lexeme::Number)
3496
{
3497
return parseNumber();
3498
}
3499
else if (lexer.current().type == Lexeme::RawString || lexer.current().type == Lexeme::QuotedString ||
3500
lexer.current().type == Lexeme::InterpStringSimple)
3501
{
3502
return parseString();
3503
}
3504
else if (lexer.current().type == Lexeme::InterpStringBegin)
3505
{
3506
return parseInterpString();
3507
}
3508
else if (lexer.current().type == Lexeme::BrokenString)
3509
{
3510
nextLexeme();
3511
return reportExprError(start, {}, "Malformed string; did you forget to finish it?");
3512
}
3513
else if (lexer.current().type == Lexeme::BrokenInterpDoubleBrace)
3514
{
3515
nextLexeme();
3516
return reportExprError(start, {}, "Double braces are not permitted within interpolated strings; did you mean '\\{'?");
3517
}
3518
else if (lexer.current().type == Lexeme::Dot3)
3519
{
3520
if (functionStack.back().vararg)
3521
{
3522
nextLexeme();
3523
3524
return allocator.alloc<AstExprVarargs>(start);
3525
}
3526
else
3527
{
3528
nextLexeme();
3529
3530
return reportExprError(start, {}, "Cannot use '...' outside of a vararg function");
3531
}
3532
}
3533
else if (lexer.current().type == '{')
3534
{
3535
return parseTableConstructor();
3536
}
3537
else if (lexer.current().type == Lexeme::ReservedIf)
3538
{
3539
return parseIfElseExpr();
3540
}
3541
else
3542
{
3543
return parsePrimaryExpr(/* asStatement= */ false);
3544
}
3545
}
3546
3547
std::tuple<AstArray<AstExpr*>, Location, Location> Parser::parseCallList(TempVector<Position>* commaPositions)
3548
{
3549
LUAU_ASSERT(
3550
lexer.current().type == '(' || lexer.current().type == '{' || lexer.current().type == Lexeme::RawString ||
3551
lexer.current().type == Lexeme::QuotedString
3552
);
3553
if (lexer.current().type == '(')
3554
{
3555
Position argStart = lexer.current().location.end;
3556
3557
MatchLexeme matchParen = lexer.current();
3558
nextLexeme();
3559
3560
TempVector<AstExpr*> args(scratchExpr);
3561
3562
if (lexer.current().type != ')')
3563
parseExprList(args, commaPositions);
3564
3565
Location end = lexer.current().location;
3566
Position argEnd = end.end;
3567
3568
expectMatchAndConsume(')', matchParen);
3569
3570
return {copy(args), Location(argStart, argEnd), Location(matchParen.position, lexer.previousLocation().begin)};
3571
}
3572
else if (lexer.current().type == '{')
3573
{
3574
Position argStart = lexer.current().location.end;
3575
AstExpr* expr = parseTableConstructor();
3576
Position argEnd = lexer.previousLocation().end;
3577
3578
return {copy(&expr, 1), Location(argStart, argEnd), expr->location};
3579
}
3580
else
3581
{
3582
Location argLocation = lexer.current().location;
3583
AstExpr* expr = parseString();
3584
return {copy(&expr, 1), argLocation, expr->location};
3585
}
3586
}
3587
3588
// args ::= `(' [explist] `)' | tableconstructor | String
3589
AstExpr* Parser::parseFunctionArgs(AstExpr* func, bool self)
3590
{
3591
if (lexer.current().type == '(')
3592
{
3593
Position argStart = lexer.current().location.end;
3594
if (func->location.end.line != lexer.current().location.begin.line)
3595
reportAmbiguousCallError();
3596
3597
MatchLexeme matchParen = lexer.current();
3598
nextLexeme();
3599
3600
TempVector<AstExpr*> args(scratchExpr);
3601
TempVector<Position> commaPositions(scratchPosition);
3602
3603
if (lexer.current().type != ')')
3604
parseExprList(args, options.storeCstData ? &commaPositions : nullptr);
3605
3606
Location end = lexer.current().location;
3607
Position argEnd = end.end;
3608
3609
expectMatchAndConsume(')', matchParen);
3610
3611
AstExprCall* node = allocator.alloc<AstExprCall>(
3612
Location(func->location, end), func, copy(args), self, AstArray<AstTypeOrPack>{}, Location(argStart, argEnd)
3613
);
3614
if (options.storeCstData)
3615
cstNodeMap[node] = allocator.alloc<CstExprCall>(matchParen.position, lexer.previousLocation().begin, copy(commaPositions));
3616
return node;
3617
}
3618
else if (lexer.current().type == '{')
3619
{
3620
Position argStart = lexer.current().location.end;
3621
AstExpr* expr = parseTableConstructor();
3622
Position argEnd = lexer.previousLocation().end;
3623
3624
AstExprCall* node = allocator.alloc<AstExprCall>(
3625
Location(func->location, expr->location), func, copy(&expr, 1), self, AstArray<AstTypeOrPack>{}, Location(argStart, argEnd)
3626
);
3627
if (options.storeCstData)
3628
cstNodeMap[node] = allocator.alloc<CstExprCall>(std::nullopt, std::nullopt, AstArray<Position>{nullptr, 0});
3629
return node;
3630
}
3631
else if (lexer.current().type == Lexeme::RawString || lexer.current().type == Lexeme::QuotedString)
3632
{
3633
Location argLocation = lexer.current().location;
3634
AstExpr* expr = parseString();
3635
3636
AstExprCall* node = allocator.alloc<AstExprCall>(
3637
Location(func->location, expr->location), func, copy(&expr, 1), self, AstArray<AstTypeOrPack>{}, argLocation
3638
);
3639
if (options.storeCstData)
3640
cstNodeMap[node] = allocator.alloc<CstExprCall>(std::nullopt, std::nullopt, AstArray<Position>{nullptr, 0});
3641
return node;
3642
}
3643
else
3644
{
3645
return reportFunctionArgsError(func, self);
3646
}
3647
}
3648
3649
LUAU_NOINLINE AstExpr* Parser::reportFunctionArgsError(AstExpr* func, bool self)
3650
{
3651
if (self && lexer.current().location.begin.line != func->location.end.line)
3652
{
3653
return reportExprError(func->location, copy({func}), "Expected function call arguments after '('");
3654
}
3655
else
3656
{
3657
return reportExprError(
3658
Location(func->location.begin, lexer.current().location.begin),
3659
copy({func}),
3660
"Expected '(', '{' or <string> when parsing function call, got %s",
3661
lexer.current().toString().c_str()
3662
);
3663
}
3664
}
3665
3666
LUAU_NOINLINE void Parser::reportAmbiguousCallError()
3667
{
3668
report(
3669
lexer.current().location,
3670
"Ambiguous syntax: this looks like an argument list for a function call, but could also be a start of "
3671
"new statement; use ';' to separate statements"
3672
);
3673
}
3674
3675
std::optional<CstExprTable::Separator> Parser::tableSeparator()
3676
{
3677
if (lexer.current().type == ',')
3678
return CstExprTable::Comma;
3679
else if (lexer.current().type == ';')
3680
return CstExprTable::Semicolon;
3681
else
3682
return std::nullopt;
3683
}
3684
3685
// tableconstructor ::= `{' [fieldlist] `}'
3686
// fieldlist ::= field {fieldsep field} [fieldsep]
3687
// field ::= `[' exp `]' `=' exp | Name `=' exp | exp
3688
// fieldsep ::= `,' | `;'
3689
AstExpr* Parser::parseTableConstructor()
3690
{
3691
TempVector<AstExprTable::Item> items(scratchItem);
3692
TempVector<CstExprTable::Item> cstItems(scratchCstItem);
3693
3694
Location start = lexer.current().location;
3695
3696
MatchLexeme matchBrace = lexer.current();
3697
expectAndConsume('{', "table literal");
3698
unsigned lastElementIndent = 0;
3699
3700
while (lexer.current().type != '}')
3701
{
3702
lastElementIndent = lexer.current().location.begin.column;
3703
3704
if (lexer.current().type == '[')
3705
{
3706
Position indexerOpenPosition = lexer.current().location.begin;
3707
MatchLexeme matchLocationBracket = lexer.current();
3708
nextLexeme();
3709
3710
AstExpr* key = parseExpr();
3711
3712
Position indexerClosePosition = lexer.current().location.begin;
3713
expectMatchAndConsume(']', matchLocationBracket);
3714
3715
Position equalsPosition = lexer.current().location.begin;
3716
expectAndConsume('=', "table field");
3717
3718
AstExpr* value = parseExpr();
3719
3720
items.push_back({AstExprTable::Item::General, key, value});
3721
if (options.storeCstData)
3722
cstItems.push_back({indexerOpenPosition, indexerClosePosition, equalsPosition, tableSeparator(), lexer.current().location.begin});
3723
}
3724
else if (lexer.current().type == Lexeme::Name && lexer.lookahead().type == '=')
3725
{
3726
Name name = parseName("table field");
3727
3728
Position equalsPosition = lexer.current().location.begin;
3729
expectAndConsume('=', "table field");
3730
3731
AstArray<char> nameString;
3732
nameString.data = const_cast<char*>(name.name.value);
3733
nameString.size = strlen(name.name.value);
3734
3735
AstExpr* key = allocator.alloc<AstExprConstantString>(name.location, nameString, AstExprConstantString::Unquoted);
3736
AstExpr* value = parseExpr();
3737
3738
if (AstExprFunction* func = value->as<AstExprFunction>())
3739
func->debugname = name.name;
3740
3741
items.push_back({AstExprTable::Item::Record, key, value});
3742
if (options.storeCstData)
3743
cstItems.push_back({std::nullopt, std::nullopt, equalsPosition, tableSeparator(), lexer.current().location.begin});
3744
}
3745
else
3746
{
3747
AstExpr* expr = parseExpr();
3748
3749
items.push_back({AstExprTable::Item::List, nullptr, expr});
3750
if (options.storeCstData)
3751
cstItems.push_back({std::nullopt, std::nullopt, std::nullopt, tableSeparator(), lexer.current().location.begin});
3752
}
3753
3754
if (lexer.current().type == ',' || lexer.current().type == ';')
3755
{
3756
nextLexeme();
3757
}
3758
else if ((lexer.current().type == '[' || lexer.current().type == Lexeme::Name) && lexer.current().location.begin.column == lastElementIndent)
3759
{
3760
report(lexer.current().location, "Expected ',' after table constructor element");
3761
}
3762
else if (lexer.current().type != '}')
3763
{
3764
break;
3765
}
3766
}
3767
3768
Location end = lexer.current().location;
3769
3770
if (!expectMatchAndConsume('}', matchBrace))
3771
end = lexer.previousLocation();
3772
3773
AstExprTable* node = allocator.alloc<AstExprTable>(Location(start, end), copy(items));
3774
if (options.storeCstData)
3775
cstNodeMap[node] = allocator.alloc<CstExprTable>(copy(cstItems));
3776
return node;
3777
}
3778
3779
AstExpr* Parser::parseIfElseExpr()
3780
{
3781
bool hasElse = false;
3782
Location start = lexer.current().location;
3783
3784
nextLexeme(); // skip if / elseif
3785
3786
AstExpr* condition = parseExpr();
3787
3788
Position thenPosition = lexer.current().location.begin;
3789
bool hasThen = expectAndConsume(Lexeme::ReservedThen, "if then else expression");
3790
3791
AstExpr* trueExpr = parseExpr();
3792
AstExpr* falseExpr = nullptr;
3793
3794
Position elsePosition = lexer.current().location.begin;
3795
bool isElseIf = false;
3796
if (lexer.current().type == Lexeme::ReservedElseif)
3797
{
3798
unsigned int oldRecursionCount = recursionCounter;
3799
incrementRecursionCounter("expression");
3800
hasElse = true;
3801
falseExpr = parseIfElseExpr();
3802
recursionCounter = oldRecursionCount;
3803
isElseIf = true;
3804
}
3805
else
3806
{
3807
hasElse = expectAndConsume(Lexeme::ReservedElse, "if then else expression");
3808
falseExpr = parseExpr();
3809
}
3810
3811
Location end = falseExpr->location;
3812
3813
AstExprIfElse* node = allocator.alloc<AstExprIfElse>(Location(start, end), condition, hasThen, trueExpr, hasElse, falseExpr);
3814
if (options.storeCstData)
3815
cstNodeMap[node] = allocator.alloc<CstExprIfElse>(thenPosition, elsePosition, isElseIf);
3816
return node;
3817
}
3818
3819
// Name
3820
std::optional<Parser::Name> Parser::parseNameOpt(const char* context)
3821
{
3822
if (lexer.current().type != Lexeme::Name)
3823
{
3824
reportNameError(context);
3825
3826
return {};
3827
}
3828
3829
Name result(AstName(lexer.current().name), lexer.current().location);
3830
3831
nextLexeme();
3832
3833
return result;
3834
}
3835
3836
Parser::Name Parser::parseName(const char* context)
3837
{
3838
if (std::optional<Name> name = parseNameOpt(context))
3839
return *name;
3840
3841
Location location = lexer.current().location;
3842
location.end = location.begin;
3843
3844
return Name(nameError, location);
3845
}
3846
3847
Parser::Name Parser::parseIndexName(const char* context, const Position& previous)
3848
{
3849
if (std::optional<Name> name = parseNameOpt(context))
3850
return *name;
3851
3852
// If we have a reserved keyword next at the same line, assume it's an incomplete name
3853
if (lexer.current().type >= Lexeme::Reserved_BEGIN && lexer.current().type < Lexeme::Reserved_END &&
3854
lexer.current().location.begin.line == previous.line)
3855
{
3856
Name result(AstName(lexer.current().name), lexer.current().location);
3857
3858
nextLexeme();
3859
3860
return result;
3861
}
3862
3863
Location location = lexer.current().location;
3864
location.end = location.begin;
3865
3866
return Name(nameError, location);
3867
}
3868
3869
std::pair<AstArray<AstGenericType*>, AstArray<AstGenericTypePack*>> Parser::parseGenericTypeList(
3870
bool withDefaultValues,
3871
Position* openPosition,
3872
AstArray<Position>* commaPositions,
3873
Position* closePosition
3874
)
3875
{
3876
TempVector<AstGenericType*> names{scratchGenericTypes};
3877
TempVector<AstGenericTypePack*> namePacks{scratchGenericTypePacks};
3878
TempVector<Position> localCommaPositions{scratchPosition};
3879
3880
if (lexer.current().type == '<')
3881
{
3882
Lexeme begin = lexer.current();
3883
if (openPosition)
3884
*openPosition = begin.location.begin;
3885
nextLexeme();
3886
3887
bool seenPack = false;
3888
bool seenDefault = false;
3889
3890
while (true)
3891
{
3892
Location nameLocation = lexer.current().location;
3893
AstName name = parseName().name;
3894
if (lexer.current().type == Lexeme::Dot3 || seenPack)
3895
{
3896
seenPack = true;
3897
3898
Position ellipsisPosition = lexer.current().location.begin;
3899
if (lexer.current().type != Lexeme::Dot3)
3900
report(lexer.current().location, "Generic types come before generic type packs");
3901
else
3902
nextLexeme();
3903
3904
if (withDefaultValues && lexer.current().type == '=')
3905
{
3906
seenDefault = true;
3907
Position equalsPosition = lexer.current().location.begin;
3908
nextLexeme();
3909
3910
if (shouldParseTypePack(lexer))
3911
{
3912
AstTypePack* typePack = parseTypePack();
3913
3914
AstGenericTypePack* node = allocator.alloc<AstGenericTypePack>(nameLocation, name, typePack);
3915
if (options.storeCstData)
3916
cstNodeMap[node] = allocator.alloc<CstGenericTypePack>(ellipsisPosition, equalsPosition);
3917
namePacks.push_back(node);
3918
}
3919
else
3920
{
3921
auto [type, typePack] = parseSimpleTypeOrPack();
3922
3923
if (type)
3924
report(type->location, "Expected type pack after '=', got type");
3925
3926
AstGenericTypePack* node = allocator.alloc<AstGenericTypePack>(nameLocation, name, typePack);
3927
if (options.storeCstData)
3928
cstNodeMap[node] = allocator.alloc<CstGenericTypePack>(ellipsisPosition, equalsPosition);
3929
namePacks.push_back(node);
3930
}
3931
}
3932
else
3933
{
3934
if (seenDefault)
3935
report(lexer.current().location, "Expected default type pack after type pack name");
3936
3937
AstGenericTypePack* node = allocator.alloc<AstGenericTypePack>(nameLocation, name, nullptr);
3938
if (options.storeCstData)
3939
cstNodeMap[node] = allocator.alloc<CstGenericTypePack>(ellipsisPosition, std::nullopt);
3940
namePacks.push_back(node);
3941
}
3942
}
3943
else
3944
{
3945
if (withDefaultValues && lexer.current().type == '=')
3946
{
3947
seenDefault = true;
3948
Position equalsPosition = lexer.current().location.begin;
3949
nextLexeme();
3950
3951
AstType* defaultType = parseType();
3952
3953
AstGenericType* node = allocator.alloc<AstGenericType>(nameLocation, name, defaultType);
3954
if (options.storeCstData)
3955
cstNodeMap[node] = allocator.alloc<CstGenericType>(equalsPosition);
3956
names.push_back(node);
3957
}
3958
else
3959
{
3960
if (seenDefault)
3961
report(lexer.current().location, "Expected default type after type name");
3962
3963
AstGenericType* node = allocator.alloc<AstGenericType>(nameLocation, name, nullptr);
3964
if (options.storeCstData)
3965
cstNodeMap[node] = allocator.alloc<CstGenericType>(std::nullopt);
3966
names.push_back(node);
3967
}
3968
}
3969
3970
if (lexer.current().type == ',')
3971
{
3972
if (commaPositions)
3973
localCommaPositions.push_back(lexer.current().location.begin);
3974
nextLexeme();
3975
3976
if (lexer.current().type == '>')
3977
{
3978
report(lexer.current().location, "Expected type after ',' but got '>' instead");
3979
break;
3980
}
3981
}
3982
else
3983
break;
3984
}
3985
3986
if (closePosition)
3987
*closePosition = lexer.current().location.begin;
3988
expectMatchAndConsume('>', begin);
3989
}
3990
3991
if (commaPositions)
3992
*commaPositions = copy(localCommaPositions);
3993
3994
AstArray<AstGenericType*> generics = copy(names);
3995
AstArray<AstGenericTypePack*> genericPacks = copy(namePacks);
3996
return {generics, genericPacks};
3997
}
3998
3999
AstArray<AstTypeOrPack> Parser::parseTypeParams(Position* openingPosition, TempVector<Position>* commaPositions, Position* closingPosition)
4000
{
4001
TempVector<AstTypeOrPack> parameters{scratchTypeOrPack};
4002
4003
if (lexer.current().type == '<')
4004
{
4005
Lexeme begin = lexer.current();
4006
if (openingPosition)
4007
*openingPosition = begin.location.begin;
4008
nextLexeme();
4009
4010
while (true)
4011
{
4012
if (shouldParseTypePack(lexer))
4013
{
4014
AstTypePack* typePack = parseTypePack();
4015
parameters.push_back({{}, typePack});
4016
}
4017
else if (lexer.current().type == '(')
4018
{
4019
Location begin = lexer.current().location;
4020
AstType* type = nullptr;
4021
AstTypePack* typePack = nullptr;
4022
Lexeme::Type c = lexer.current().type;
4023
4024
if (c != '|' && c != '&')
4025
{
4026
auto typeOrTypePack = parseSimpleType(/* allowPack */ true, /* inDeclarationContext */ false);
4027
type = typeOrTypePack.type;
4028
typePack = typeOrTypePack.typePack;
4029
}
4030
4031
// Consider the following type:
4032
//
4033
// X<(T)>
4034
//
4035
// Is this a type pack or a parenthesized type? The
4036
// assumption will be a type pack, as that's what allows one
4037
// to express either a singular type pack or a potential
4038
// complex type.
4039
4040
if (typePack)
4041
{
4042
auto explicitTypePack = typePack->as<AstTypePackExplicit>();
4043
if (explicitTypePack && explicitTypePack->typeList.tailType == nullptr && explicitTypePack->typeList.types.size == 1 &&
4044
isTypeFollow(lexer.current().type))
4045
{
4046
// If we parsed an explicit type pack with a single
4047
// type in it (something of the form `(T)`), and
4048
// the next lexeme is one that follows a type
4049
// (&, |, ?), then assume that this was actually a
4050
// parenthesized type.
4051
auto parenthesizedType = explicitTypePack->typeList.types.data[0];
4052
parameters.push_back(
4053
{parseTypeSuffix(allocator.alloc<AstTypeGroup>(parenthesizedType->location, parenthesizedType), begin), {}}
4054
);
4055
}
4056
else
4057
{
4058
// Otherwise, it's a type pack.
4059
parameters.push_back({{}, typePack});
4060
}
4061
}
4062
else
4063
{
4064
// There's two cases in which `typePack` will be null:
4065
// - We try to parse a simple type or a type pack, and
4066
// we get a simple type: there's no ambiguity and
4067
// we attempt to parse a complex type.
4068
// - The next lexeme was a `|` or `&` indicating a
4069
// union or intersection type with a leading
4070
// separator. We just fall right into
4071
// `parseTypeSuffix`, which allows its first
4072
// argument to be `nullptr`
4073
parameters.push_back({parseTypeSuffix(type, begin), {}});
4074
}
4075
}
4076
else if (lexer.current().type == '>' && parameters.empty())
4077
{
4078
break;
4079
}
4080
else
4081
{
4082
parameters.push_back({parseType(), {}});
4083
}
4084
4085
if (lexer.current().type == ',')
4086
{
4087
if (commaPositions)
4088
commaPositions->push_back(lexer.current().location.begin);
4089
nextLexeme();
4090
}
4091
else
4092
break;
4093
}
4094
4095
if (closingPosition)
4096
*closingPosition = lexer.current().location.begin;
4097
expectMatchAndConsume('>', begin);
4098
}
4099
4100
return copy(parameters);
4101
}
4102
4103
std::optional<AstArray<char>> Parser::parseCharArray(AstArray<char>* originalString)
4104
{
4105
LUAU_ASSERT(
4106
lexer.current().type == Lexeme::QuotedString || lexer.current().type == Lexeme::RawString ||
4107
lexer.current().type == Lexeme::InterpStringSimple
4108
);
4109
4110
scratchData.assign(lexer.current().data, lexer.current().getLength());
4111
if (originalString)
4112
*originalString = copy(scratchData);
4113
4114
if (lexer.current().type == Lexeme::QuotedString || lexer.current().type == Lexeme::InterpStringSimple)
4115
{
4116
if (!Lexer::fixupQuotedString(scratchData))
4117
{
4118
nextLexeme();
4119
return std::nullopt;
4120
}
4121
}
4122
else
4123
{
4124
Lexer::fixupMultilineString(scratchData);
4125
}
4126
4127
AstArray<char> value = copy(scratchData);
4128
nextLexeme();
4129
return value;
4130
}
4131
4132
AstExpr* Parser::parseString()
4133
{
4134
Location location = lexer.current().location;
4135
4136
AstExprConstantString::QuoteStyle style;
4137
switch (lexer.current().type)
4138
{
4139
case Lexeme::QuotedString:
4140
case Lexeme::InterpStringSimple:
4141
style = AstExprConstantString::QuotedSimple;
4142
break;
4143
case Lexeme::RawString:
4144
style = AstExprConstantString::QuotedRaw;
4145
break;
4146
default:
4147
LUAU_ASSERT(false && "Invalid string type");
4148
}
4149
4150
CstExprConstantString::QuoteStyle fullStyle;
4151
unsigned int blockDepth;
4152
if (options.storeCstData)
4153
std::tie(fullStyle, blockDepth) = extractStringDetails();
4154
4155
AstArray<char> originalString;
4156
if (std::optional<AstArray<char>> value = parseCharArray(options.storeCstData ? &originalString : nullptr))
4157
{
4158
AstExprConstantString* node = allocator.alloc<AstExprConstantString>(location, *value, style);
4159
if (options.storeCstData)
4160
cstNodeMap[node] = allocator.alloc<CstExprConstantString>(originalString, fullStyle, blockDepth);
4161
return node;
4162
}
4163
else
4164
return reportExprError(location, {}, "String literal contains malformed escape sequence");
4165
}
4166
4167
AstExpr* Parser::parseInterpString()
4168
{
4169
TempVector<AstArray<char>> strings(scratchString);
4170
TempVector<AstArray<char>> sourceStrings(scratchString2);
4171
TempVector<Position> stringPositions(scratchPosition);
4172
TempVector<AstExpr*> expressions(scratchExpr);
4173
4174
Location startLocation = lexer.current().location;
4175
Location endLocation;
4176
4177
do
4178
{
4179
Lexeme currentLexeme = lexer.current();
4180
LUAU_ASSERT(
4181
currentLexeme.type == Lexeme::InterpStringBegin || currentLexeme.type == Lexeme::InterpStringMid ||
4182
currentLexeme.type == Lexeme::InterpStringEnd || currentLexeme.type == Lexeme::InterpStringSimple
4183
);
4184
4185
endLocation = currentLexeme.location;
4186
4187
scratchData.assign(currentLexeme.data, currentLexeme.getLength());
4188
4189
if (options.storeCstData)
4190
{
4191
sourceStrings.push_back(copy(scratchData));
4192
stringPositions.push_back(currentLexeme.location.begin);
4193
}
4194
4195
if (!Lexer::fixupQuotedString(scratchData))
4196
{
4197
nextLexeme();
4198
return reportExprError(Location{startLocation, endLocation}, {}, "Interpolated string literal contains malformed escape sequence");
4199
}
4200
4201
AstArray<char> chars = copy(scratchData);
4202
4203
nextLexeme();
4204
4205
strings.push_back(chars);
4206
4207
if (currentLexeme.type == Lexeme::InterpStringEnd || currentLexeme.type == Lexeme::InterpStringSimple)
4208
{
4209
break;
4210
}
4211
4212
bool errorWhileChecking = false;
4213
4214
switch (lexer.current().type)
4215
{
4216
case Lexeme::InterpStringMid:
4217
case Lexeme::InterpStringEnd:
4218
{
4219
errorWhileChecking = true;
4220
nextLexeme();
4221
expressions.push_back(reportExprError(endLocation, {}, "Malformed interpolated string, expected expression inside '{}'"));
4222
break;
4223
}
4224
case Lexeme::BrokenString:
4225
{
4226
errorWhileChecking = true;
4227
nextLexeme();
4228
expressions.push_back(reportExprError(endLocation, {}, "Malformed interpolated string; did you forget to add a '`'?"));
4229
break;
4230
}
4231
default:
4232
expressions.push_back(parseExpr());
4233
}
4234
4235
if (errorWhileChecking)
4236
{
4237
break;
4238
}
4239
4240
switch (lexer.current().type)
4241
{
4242
case Lexeme::InterpStringBegin:
4243
case Lexeme::InterpStringMid:
4244
case Lexeme::InterpStringEnd:
4245
break;
4246
case Lexeme::BrokenInterpDoubleBrace:
4247
nextLexeme();
4248
return reportExprError(endLocation, {}, "Double braces are not permitted within interpolated strings; did you mean '\\{'?");
4249
case Lexeme::BrokenString:
4250
nextLexeme();
4251
LUAU_FALLTHROUGH;
4252
case Lexeme::Eof:
4253
{
4254
AstArray<AstArray<char>> stringsArray = copy(strings);
4255
AstArray<AstExpr*> exprs = copy(expressions);
4256
AstExprInterpString* node = allocator.alloc<AstExprInterpString>(Location{startLocation, lexer.previousLocation()}, stringsArray, exprs);
4257
if (options.storeCstData)
4258
cstNodeMap[node] = allocator.alloc<CstExprInterpString>(copy(sourceStrings), copy(stringPositions));
4259
if (auto top = lexer.peekBraceStackTop())
4260
{
4261
// We are in a broken interpolated string, the top of the stack is non empty, we are missing '}'
4262
if (*top == Lexer::BraceType::InterpolatedString)
4263
report(lexer.previousLocation(), "Malformed interpolated string; did you forget to add a '}'?");
4264
}
4265
else
4266
{
4267
// We are in a broken interpolated string, the top of the stack is empty, we are missing '`'.
4268
report(lexer.previousLocation(), "Malformed interpolated string; did you forget to add a '`'?");
4269
}
4270
return node;
4271
}
4272
default:
4273
return reportExprError(endLocation, {}, "Malformed interpolated string, got %s", lexer.current().toString().c_str());
4274
}
4275
} while (true);
4276
4277
AstArray<AstArray<char>> stringsArray = copy(strings);
4278
AstArray<AstExpr*> expressionsArray = copy(expressions);
4279
AstExprInterpString* node = allocator.alloc<AstExprInterpString>(Location{startLocation, endLocation}, stringsArray, expressionsArray);
4280
if (options.storeCstData)
4281
cstNodeMap[node] = allocator.alloc<CstExprInterpString>(copy(sourceStrings), copy(stringPositions));
4282
return node;
4283
}
4284
4285
LUAU_NOINLINE AstExpr* Parser::parseExplicitTypeInstantiationExpr(Position start, AstExpr& basedOnExpr)
4286
{
4287
CstExprExplicitTypeInstantiation* cstNode = nullptr;
4288
if (options.storeCstData)
4289
{
4290
cstNode = allocator.alloc<CstExprExplicitTypeInstantiation>(CstTypeInstantiation{});
4291
}
4292
4293
Location endLocation;
4294
AstArray<AstTypeOrPack> typesOrPacks = parseTypeInstantiationExpr(cstNode ? &cstNode->instantiation : nullptr, &endLocation);
4295
4296
AstExpr* expr = allocator.alloc<AstExprInstantiate>(Location(start, endLocation.end), &basedOnExpr, typesOrPacks);
4297
4298
if (options.storeCstData)
4299
{
4300
cstNodeMap[expr] = cstNode;
4301
}
4302
4303
return expr;
4304
}
4305
4306
AstArray<AstTypeOrPack> Parser::parseTypeInstantiationExpr(CstTypeInstantiation* cstNodeOut, Location* endLocationOut)
4307
{
4308
LUAU_ASSERT(lexer.current().type == '<' && lexer.lookahead().type == '<');
4309
4310
if (cstNodeOut)
4311
{
4312
cstNodeOut->leftArrow1Position = lexer.current().location.begin;
4313
}
4314
4315
Lexeme begin = lexer.current();
4316
lexer.next();
4317
4318
TempVector<Position> commaPositions = TempVector{scratchPosition};
4319
4320
AstArray<AstTypeOrPack> typeOrPacks = parseTypeParams(
4321
cstNodeOut ? &cstNodeOut->leftArrow2Position : nullptr,
4322
cstNodeOut ? &commaPositions : nullptr,
4323
cstNodeOut ? &cstNodeOut->rightArrow1Position : nullptr
4324
);
4325
4326
if (cstNodeOut)
4327
{
4328
cstNodeOut->commaPositions = copy(commaPositions);
4329
4330
if (lexer.current().type == '>')
4331
{
4332
cstNodeOut->rightArrow2Position = lexer.current().location.begin;
4333
}
4334
}
4335
4336
if (endLocationOut)
4337
{
4338
*endLocationOut = lexer.current().location;
4339
}
4340
4341
expectMatchAndConsume('>', begin);
4342
return typeOrPacks;
4343
}
4344
4345
4346
AstExpr* Parser::parseNumber()
4347
{
4348
Location start = lexer.current().location;
4349
4350
scratchData.assign(lexer.current().data, lexer.current().getLength());
4351
AstArray<char> sourceData;
4352
if (options.storeCstData)
4353
sourceData = copy(scratchData);
4354
4355
// Remove all internal _ - they don't hold any meaning and this allows parsing code to just pass the string pointer to strtod et al
4356
if (scratchData.find('_') != std::string::npos)
4357
{
4358
scratchData.erase(std::remove(scratchData.begin(), scratchData.end(), '_'), scratchData.end());
4359
}
4360
4361
if (FFlag::LuauIntegerType && (scratchData.back() == 'i'))
4362
{
4363
int64_t value = 0;
4364
ConstantNumberParseResult result;
4365
if ((strncmp(scratchData.c_str(), "0x", 2) == 0) || (strncmp(scratchData.c_str(), "0X", 2) == 0))
4366
result = parseInteger64(value, scratchData.c_str(), 16); // pass in '0x' prefix, it's handled by strtoll
4367
else if ((strncmp(scratchData.c_str(), "0b", 2) == 0) || (strncmp(scratchData.c_str(), "0B", 2) == 0))
4368
result = parseInteger64(value, scratchData.c_str() + 2, 2);
4369
else
4370
result = parseInteger64(value, scratchData.c_str(), 10);
4371
4372
nextLexeme();
4373
4374
if (result == ConstantNumberParseResult::Malformed)
4375
return reportExprError(start, {}, "Malformed integer");
4376
4377
if (result != ConstantNumberParseResult::Ok)
4378
return reportExprError(start, {}, "Integer overflow");
4379
4380
AstExprConstantInteger* node = allocator.alloc<AstExprConstantInteger>(start, value, result);
4381
if (options.storeCstData)
4382
cstNodeMap[node] = allocator.alloc<CstExprConstantInteger>(sourceData);
4383
return node;
4384
}
4385
else
4386
{
4387
double value = 0;
4388
ConstantNumberParseResult result = parseDouble(value, scratchData.c_str());
4389
nextLexeme();
4390
4391
if (result == ConstantNumberParseResult::Malformed)
4392
return reportExprError(start, {}, "Malformed number");
4393
4394
AstExprConstantNumber* node = allocator.alloc<AstExprConstantNumber>(start, value, result);
4395
if (options.storeCstData)
4396
cstNodeMap[node] = allocator.alloc<CstExprConstantNumber>(sourceData);
4397
return node;
4398
}
4399
}
4400
4401
AstLocal* Parser::pushLocal(const Binding& binding)
4402
{
4403
const Name& name = binding.name;
4404
AstLocal*& local = localMap[name.name];
4405
4406
local = allocator.alloc<AstLocal>(
4407
name.name, name.location, /* shadow= */ local, functionStack.size() - 1, functionStack.back().loopDepth, binding.annotation, binding.isConst
4408
);
4409
4410
localStack.push_back(local);
4411
4412
return local;
4413
}
4414
4415
unsigned int Parser::saveLocals()
4416
{
4417
return unsigned(localStack.size());
4418
}
4419
4420
void Parser::restoreLocals(unsigned int offset)
4421
{
4422
for (size_t i = localStack.size(); i > offset; --i)
4423
{
4424
AstLocal* l = localStack[i - 1];
4425
4426
localMap[l->name] = l->shadow;
4427
}
4428
4429
localStack.resize(offset);
4430
}
4431
4432
bool Parser::expectAndConsume(char value, const char* context)
4433
{
4434
return expectAndConsume(static_cast<Lexeme::Type>(static_cast<unsigned char>(value)), context);
4435
}
4436
4437
bool Parser::expectAndConsume(Lexeme::Type type, const char* context)
4438
{
4439
if (lexer.current().type != type)
4440
return expectAndConsumeFailWithLookahead(type, context);
4441
4442
nextLexeme();
4443
return true;
4444
}
4445
4446
// LUAU_NOINLINE is used to limit the stack cost due to std::string objects, and to increase caller performance since this code is cold
4447
LUAU_NOINLINE bool Parser::expectAndConsumeFailWithLookahead(Lexeme::Type type, const char* context)
4448
{
4449
expectAndConsumeFail(type, context);
4450
4451
// check if this is an extra token and the expected token is next
4452
if (lexer.lookahead().type == type)
4453
{
4454
// skip invalid and consume expected
4455
nextLexeme();
4456
nextLexeme();
4457
}
4458
4459
return false;
4460
}
4461
4462
// LUAU_NOINLINE is used to limit the stack cost due to std::string objects, and to increase caller performance since this code is cold
4463
LUAU_NOINLINE void Parser::expectAndConsumeFail(Lexeme::Type type, const char* context)
4464
{
4465
std::string typeString = Lexeme(Location(Position(0, 0), 0), type).toString();
4466
std::string currLexemeString = lexer.current().toString();
4467
4468
if (context)
4469
report(lexer.current().location, "Expected %s when parsing %s, got %s", typeString.c_str(), context, currLexemeString.c_str());
4470
else
4471
report(lexer.current().location, "Expected %s, got %s", typeString.c_str(), currLexemeString.c_str());
4472
}
4473
4474
bool Parser::expectMatchAndConsume(char value, const MatchLexeme& begin, bool searchForMissing)
4475
{
4476
Lexeme::Type type = static_cast<Lexeme::Type>(static_cast<unsigned char>(value));
4477
4478
if (lexer.current().type != type)
4479
{
4480
expectMatchAndConsumeFail(type, begin);
4481
4482
return expectMatchAndConsumeRecover(value, begin, searchForMissing);
4483
}
4484
else
4485
{
4486
nextLexeme();
4487
4488
return true;
4489
}
4490
}
4491
4492
// LUAU_NOINLINE is used to limit the stack cost due to std::string objects, and to increase caller performance since this code is cold
4493
LUAU_NOINLINE bool Parser::expectMatchAndConsumeRecover(char value, const MatchLexeme& begin, bool searchForMissing)
4494
{
4495
Lexeme::Type type = static_cast<Lexeme::Type>(static_cast<unsigned char>(value));
4496
4497
if (searchForMissing)
4498
{
4499
// previous location is taken because 'current' lexeme is already the next token
4500
unsigned currentLine = lexer.previousLocation().end.line;
4501
4502
// search to the end of the line for expected token
4503
// we will also stop if we hit a token that can be handled by parsing function above the current one
4504
Lexeme::Type lexemeType = lexer.current().type;
4505
4506
while (currentLine == lexer.current().location.begin.line && lexemeType != type && matchRecoveryStopOnToken[lexemeType] == 0)
4507
{
4508
nextLexeme();
4509
lexemeType = lexer.current().type;
4510
}
4511
4512
if (lexemeType == type)
4513
{
4514
nextLexeme();
4515
4516
return true;
4517
}
4518
}
4519
else
4520
{
4521
// check if this is an extra token and the expected token is next
4522
if (lexer.lookahead().type == type)
4523
{
4524
// skip invalid and consume expected
4525
nextLexeme();
4526
nextLexeme();
4527
4528
return true;
4529
}
4530
}
4531
4532
return false;
4533
}
4534
4535
// LUAU_NOINLINE is used to limit the stack cost due to std::string objects, and to increase caller performance since this code is cold
4536
LUAU_NOINLINE void Parser::expectMatchAndConsumeFail(Lexeme::Type type, const MatchLexeme& begin, const char* extra)
4537
{
4538
std::string typeString = Lexeme(Location(Position(0, 0), 0), type).toString();
4539
std::string matchString = Lexeme(Location(Position(0, 0), 0), begin.type).toString();
4540
4541
if (lexer.current().location.begin.line == begin.position.line)
4542
report(
4543
lexer.current().location,
4544
"Expected %s (to close %s at column %d), got %s%s",
4545
typeString.c_str(),
4546
matchString.c_str(),
4547
begin.position.column + 1,
4548
lexer.current().toString().c_str(),
4549
extra ? extra : ""
4550
);
4551
else
4552
report(
4553
lexer.current().location,
4554
"Expected %s (to close %s at line %d), got %s%s",
4555
typeString.c_str(),
4556
matchString.c_str(),
4557
begin.position.line + 1,
4558
lexer.current().toString().c_str(),
4559
extra ? extra : ""
4560
);
4561
}
4562
4563
// LUAU_NOINLINE is used to limit the stack cost of callers inlining it
4564
LUAU_NOINLINE bool Parser::expectMatchEndAndConsume(Lexeme::Type type, const MatchLexeme& begin)
4565
{
4566
if (lexer.current().type != type)
4567
return expectMatchEndAndConsumeFailWithLookahead(type, begin);
4568
4569
// If the token matches on a different line and a different column, it suggests misleading indentation
4570
// This can be used to pinpoint the problem location for a possible future *actual* mismatch
4571
if (lexer.current().location.begin.line != begin.position.line && lexer.current().location.begin.column != begin.position.column &&
4572
endMismatchSuspect.position.line < begin.position.line) // Only replace the previous suspect with more recent suspects
4573
{
4574
endMismatchSuspect = begin;
4575
}
4576
4577
nextLexeme();
4578
4579
return true;
4580
}
4581
4582
// LUAU_NOINLINE is used to limit the stack cost due to std::string objects, and to increase caller performance since this code is cold
4583
LUAU_NOINLINE bool Parser::expectMatchEndAndConsumeFailWithLookahead(Lexeme::Type type, const MatchLexeme& begin)
4584
{
4585
if (endMismatchSuspect.type != Lexeme::Eof && endMismatchSuspect.position.line > begin.position.line)
4586
{
4587
std::string matchString = Lexeme(Location(Position(0, 0), 0), endMismatchSuspect.type).toString();
4588
std::string suggestion = format("; did you forget to close %s at line %d?", matchString.c_str(), endMismatchSuspect.position.line + 1);
4589
4590
expectMatchAndConsumeFail(type, begin, suggestion.c_str());
4591
}
4592
else
4593
{
4594
expectMatchAndConsumeFail(type, begin);
4595
}
4596
4597
// check if this is an extra token and the expected token is next
4598
if (lexer.lookahead().type == type)
4599
{
4600
// skip invalid and consume expected
4601
nextLexeme();
4602
nextLexeme();
4603
4604
return true;
4605
}
4606
4607
return false;
4608
}
4609
4610
template<typename T>
4611
AstArray<T> Parser::copy(const T* data, size_t size)
4612
{
4613
AstArray<T> result;
4614
4615
result.data = size ? static_cast<T*>(allocator.allocate(sizeof(T) * size)) : nullptr;
4616
result.size = size;
4617
4618
// This is equivalent to std::uninitialized_copy, but without the exception guarantee
4619
// since our types don't have destructors
4620
for (size_t i = 0; i < size; ++i)
4621
new (result.data + i) T(data[i]);
4622
4623
return result;
4624
}
4625
4626
template<typename T>
4627
AstArray<T> Parser::copy(const TempVector<T>& data)
4628
{
4629
return copy(data.empty() ? nullptr : &data[0], data.size());
4630
}
4631
4632
template<typename T>
4633
AstArray<T> Parser::copy(std::initializer_list<T> data)
4634
{
4635
return copy(data.size() == 0 ? nullptr : data.begin(), data.size());
4636
}
4637
4638
AstArray<char> Parser::copy(const std::string& data)
4639
{
4640
AstArray<char> result = copy(data.c_str(), data.size() + 1);
4641
4642
result.size = data.size();
4643
4644
return result;
4645
}
4646
4647
void Parser::incrementRecursionCounter(const char* context)
4648
{
4649
recursionCounter++;
4650
4651
if (recursionCounter > unsigned(FInt::LuauRecursionLimit))
4652
{
4653
ParseError::raise(lexer.current().location, "Exceeded allowed recursion depth; simplify your %s to make the code compile", context);
4654
}
4655
}
4656
4657
void Parser::report(const Location& location, const char* format, va_list args)
4658
{
4659
// To reduce number of errors reported to user for incomplete statements, we skip multiple errors at the same location
4660
// For example, consider 'local a = (((b + ' where multiple tokens haven't been written yet
4661
if (!parseErrors.empty() && location == parseErrors.back().getLocation())
4662
return;
4663
4664
std::string message = vformat(format, args);
4665
4666
// when limited to a single error, behave as if the error recovery is disabled
4667
if (FInt::LuauParseErrorLimit == 1)
4668
throw ParseError(location, message);
4669
4670
parseErrors.emplace_back(location, message);
4671
4672
if (parseErrors.size() >= unsigned(FInt::LuauParseErrorLimit) && !options.noErrorLimit)
4673
ParseError::raise(location, "Reached error limit (%d)", int(FInt::LuauParseErrorLimit));
4674
}
4675
4676
void Parser::report(const Location& location, const char* format, ...)
4677
{
4678
va_list args;
4679
va_start(args, format);
4680
report(location, format, args);
4681
va_end(args);
4682
}
4683
4684
LUAU_NOINLINE void Parser::reportNameError(const char* context)
4685
{
4686
if (context)
4687
report(lexer.current().location, "Expected identifier when parsing %s, got %s", context, lexer.current().toString().c_str());
4688
else
4689
report(lexer.current().location, "Expected identifier, got %s", lexer.current().toString().c_str());
4690
}
4691
4692
AstStatError* Parser::reportStatError(
4693
const Location& location,
4694
const AstArray<AstExpr*>& expressions,
4695
const AstArray<AstStat*>& statements,
4696
const char* format,
4697
...
4698
)
4699
{
4700
va_list args;
4701
va_start(args, format);
4702
report(location, format, args);
4703
va_end(args);
4704
4705
return allocator.alloc<AstStatError>(location, expressions, statements, unsigned(parseErrors.size() - 1));
4706
}
4707
4708
AstExprError* Parser::reportExprError(const Location& location, const AstArray<AstExpr*>& expressions, const char* format, ...)
4709
{
4710
va_list args;
4711
va_start(args, format);
4712
report(location, format, args);
4713
va_end(args);
4714
4715
return allocator.alloc<AstExprError>(location, expressions, unsigned(parseErrors.size() - 1));
4716
}
4717
4718
AstTypeError* Parser::reportTypeError(const Location& location, const AstArray<AstType*>& types, const char* format, ...)
4719
{
4720
va_list args;
4721
va_start(args, format);
4722
report(location, format, args);
4723
va_end(args);
4724
4725
return allocator.alloc<AstTypeError>(location, types, false, unsigned(parseErrors.size() - 1));
4726
}
4727
4728
AstTypeError* Parser::reportMissingTypeError(const Location& parseErrorLocation, const Location& astErrorLocation, const char* format, ...)
4729
{
4730
va_list args;
4731
va_start(args, format);
4732
report(parseErrorLocation, format, args);
4733
va_end(args);
4734
4735
return allocator.alloc<AstTypeError>(astErrorLocation, AstArray<AstType*>{}, true, unsigned(parseErrors.size() - 1));
4736
}
4737
4738
void Parser::nextLexeme()
4739
{
4740
Lexeme::Type type = lexer.next(/* skipComments= */ false, true).type;
4741
4742
while (type == Lexeme::BrokenComment || type == Lexeme::Comment || type == Lexeme::BlockComment)
4743
{
4744
const Lexeme& lexeme = lexer.current();
4745
4746
if (options.captureComments)
4747
commentLocations.push_back(Comment{lexeme.type, lexeme.location});
4748
4749
// Subtlety: Broken comments are weird because we record them as comments AND pass them to the parser as a lexeme.
4750
// The parser will turn this into a proper syntax error.
4751
if (lexeme.type == Lexeme::BrokenComment)
4752
return;
4753
4754
// Comments starting with ! are called "hot comments" and contain directives for type checking / linting / compiling
4755
if (lexeme.type == Lexeme::Comment && lexeme.getLength() && lexeme.data[0] == '!')
4756
{
4757
const char* text = lexeme.data;
4758
4759
unsigned int end = lexeme.getLength();
4760
while (end > 0 && isSpace(text[end - 1]))
4761
--end;
4762
4763
hotcomments.push_back({hotcommentHeader, lexeme.location, std::string(text + 1, text + end)});
4764
}
4765
4766
type = lexer.next(/* skipComments= */ false, /* updatePrevLocation= */ false).type;
4767
}
4768
}
4769
4770
} // namespace Luau
4771
4772