@@ -28,10 +28,10 @@ LUAU_FLAGVERSION(LuauExportValueSyntax, 4)
2828LUAU_FASTFLAGVARIABLE (DebugLuauNoInline)
2929LUAU_FASTFLAGVARIABLE (DebugLuauUserDefinedClasses)
3030LUAU_FASTFLAGVARIABLE (LuauAllowGlobalDeclarationToBeCalledClass)
31- LUAU_FASTFLAGVARIABLE (LuauStoreConstKeywordBegin)
3231LUAU_FASTFLAGVARIABLE (LuauTrackPrefixLocal)
3332LUAU_FASTFLAGVARIABLE (LuauNoDuplicateBinaryPrefix)
3433LUAU_FASTFLAGVARIABLE (LuauSingleTypeOptionalPackReturnsAttributeParens)
34+ LUAU_FASTFLAGVARIABLE (DebugLuauIfLocalSyntax)
3535
3636// Clip with DebugLuauReportReturnTypeVariadicWithTypeSuffix
3737bool luau_telemetry_parsed_return_type_variadic_with_type_suffix = false ;
@@ -580,6 +580,10 @@ AstStat* Parser::parseIf()
580580
581581 nextLexeme (); // if / elseif
582582
583+ if (FFlag::DebugLuauIfLocalSyntax &&
584+ (lexer.current ().type == Lexeme::ReservedLocal || (lexer.current ().type == Lexeme::Name && AstName (lexer.current ().name ) == " const" )))
585+ return parseIfLocalCondition (start);
586+
583587 AstExpr* cond = parseExpr ();
584588
585589 Lexeme matchThen = lexer.current ();
@@ -589,9 +593,62 @@ AstStat* Parser::parseIf()
589593
590594 AstStatBlock* thenbody = parseBlock ();
591595
592- AstStat* elsebody = nullptr ;
593596 Location end = start;
594597 std::optional<Location> elseLocation;
598+ AstStat* elsebody = parseElseBody (start, matchThen, thenbody, end, elseLocation);
599+
600+ return allocator.alloc <AstStatIf>(Location (start, end), cond, thenbody, elsebody, thenLocation, elseLocation);
601+ }
602+
603+ // (`if' | `elseif') (`local' | `const') binding `=' exp then block {elseif exp then block} [else block] end
604+ //
605+ // LUAU_NOINLINE keeps the `if local`/`if const` locals off parseIf's frame: parseIf recurses through
606+ // long if/elseif chains and this variant is rarely taken. `start` is the location of the already-
607+ // consumed `if`/`elseif` keyword; the current lexeme is the `local`/`const` keyword.
608+ LUAU_NOINLINE AstStat* Parser::parseIfLocalCondition (const Location& start)
609+ {
610+ LUAU_ASSERT (FFlag::DebugLuauIfLocalSyntax);
611+
612+ bool condIsConst = (lexer.current ().type == Lexeme::Name && AstName (lexer.current ().name ) == " const" );
613+ std::optional<Location> condKeywordLocation = lexer.current ().location ;
614+ nextLexeme (); // consume 'local' or 'const'
615+
616+ Binding binding = parseBinding (condIsConst);
617+
618+ if (lexer.current ().type == ' ,' )
619+ report (lexer.current ().location , " Expected '=' after variable name in 'if local', got ','; only a single binding is allowed" );
620+
621+ expectAndConsume (' =' , " if local declaration" );
622+
623+ AstExpr* cond = parseExpr ();
624+
625+ unsigned int localsBegin = saveLocals ();
626+ AstLocal* condLocal = pushLocal (binding);
627+
628+ Lexeme matchThen = lexer.current ();
629+ std::optional<Location> thenLocation;
630+ if (expectAndConsume (Lexeme::ReservedThen, " if statement" ))
631+ thenLocation = matchThen.location ;
632+
633+ AstStatBlock* thenbody = parseBlock ();
634+
635+ // Restore locals after then-block so condLocal is not visible in else/elseif
636+ restoreLocals (localsBegin);
637+
638+ Location end = start;
639+ std::optional<Location> elseLocation;
640+ AstStat* elsebody = parseElseBody (start, matchThen, thenbody, end, elseLocation);
641+
642+ return allocator.alloc <AstStatIf>(
643+ Location (start, end), cond, thenbody, elsebody, thenLocation, elseLocation, condLocal, condIsConst, condKeywordLocation
644+ );
645+ }
646+
647+ AstStat* Parser::parseElseBody (const Location& start, const Lexeme& matchThen, AstStatBlock* thenbody, Location& end, std::optional<Location>& elseLocation)
648+ {
649+ AstStat* elsebody = nullptr ;
650+ end = start;
651+ elseLocation = std::nullopt ;
595652
596653 if (lexer.current ().type == Lexeme::ReservedElseif)
597654 {
@@ -631,7 +688,7 @@ AstStat* Parser::parseIf()
631688 thenbody->hasEnd = hasEnd;
632689 }
633690
634- return allocator. alloc <AstStatIf>( Location (start, end), cond, thenbody, elsebody, thenLocation, elseLocation) ;
691+ return elsebody;
635692}
636693
637694// while exp do block end
@@ -1285,9 +1342,7 @@ AstStat* Parser::parseLocal(
12851342
12861343 Location location{start.begin , body->location .end };
12871344
1288- AstStatLocalFunction* node = allocator.alloc <AstStatLocalFunction>(
1289- location, var, body, isConst, isConst && FFlag::LuauStoreConstKeywordBegin ? keywordPosition : Position::missing ()
1290- );
1345+ AstStatLocalFunction* node = allocator.alloc <AstStatLocalFunction>(location, var, body, isConst, isConst ? keywordPosition : Position::missing ());
12911346 if (options.storeCstData )
12921347 {
12931348 cstNodeMap[node] = cstAttrLists != nullptr
@@ -1502,7 +1557,7 @@ LUAU_NOINLINE AstStat* Parser::parseClassStat(const Location& start, bool export
15021557 //
15031558 // ... must fail. This gets the job done but maybe we can do something
15041559 // slightly more performant here (e.g.: a "scratch" set).
1505- DenseHashSet2 <AstName> classMemberNamespace;
1560+ DenseHashSet <AstName> classMemberNamespace;
15061561
15071562 while (lexer.current ().type != Lexeme::ReservedEnd && lexer.current ().type != Lexeme::Eof)
15081563 {
@@ -1593,14 +1648,6 @@ LUAU_NOINLINE AstStat* Parser::parseClassStat(const Location& start, bool export
15931648 report (name.location , " Cannot use '%s' as a method name: names starting with '__' are reserved" , name.name .value );
15941649 }
15951650
1596- if (name.name == " __init" )
1597- {
1598- if (body->args .size < 1 )
1599- report (name.location , " __init must have at least one parameter" );
1600- else if (body->args .data [0 ]->name != " self" )
1601- report (body->args .data [0 ]->location , " __init's first parameter must be named self" );
1602- }
1603-
16041651 // TODO CLI-200853: We should support attributes, we do not need
16051652 // to support them prior to the full launch.
16061653 if (classMemberNamespace.contains (name.name ))
0 commit comments