@@ -27,12 +27,12 @@ LUAU_FASTFLAGVARIABLE(LuauConst2)
2727// NOTE: this implicitly depends on LuauConst2
2828LUAU_FASTFLAGVARIABLE (LuauExportValueSyntax)
2929LUAU_FASTFLAGVARIABLE (DebugLuauNoInline)
30- LUAU_FASTFLAGVARIABLE (LuauExternReadWriteAttributes)
3130LUAU_FASTFLAGVARIABLE (LuauConstJustReportErrorForUnderfill)
3231LUAU_FASTFLAGVARIABLE (DebugLuauUserDefinedClasses)
3332LUAU_FASTFLAGVARIABLE (LuauAllowGlobalDeclarationToBeCalledClass)
3433LUAU_FASTFLAGVARIABLE (LuauCstExprGroup)
3534LUAU_FASTFLAGVARIABLE (LuauCstTypeGroup)
35+ LUAU_FASTFLAGVARIABLE (LuauTableEntriesDontNeedToMatchIndent)
3636
3737// Clip with DebugLuauReportReturnTypeVariadicWithTypeSuffix
3838bool luau_telemetry_parsed_return_type_variadic_with_type_suffix = false ;
@@ -501,26 +501,29 @@ AstStat* Parser::parseStat()
501501
502502 if (ident == " export" )
503503 {
504- // TODO: update export surface to support classes
505- if (FFlag::DebugLuauUserDefinedClasses && AstName (lexer.current ().name ) == " class" )
504+ if (FFlag::LuauExportValueSyntax && FFlag::LuauConst2)
506505 {
507- nextLexeme ();
508- return parseClassStat (start, /* exported*/ true );
509- }
510- else if (FFlag::LuauExportValueSyntax && FFlag::LuauConst2)
511- {
512- if (lexer.current ().type == Lexeme::ReservedLocal || lexer.current ().type == Lexeme::ReservedFunction ||
513- (lexer.current ().type == Lexeme::Name && AstName (lexer.current ().name ) == " const" ))
506+ Lexeme current = lexer.current ();
507+
508+ if (current.type == Lexeme::ReservedLocal || current.type == Lexeme::ReservedFunction ||
509+ (current.type == Lexeme::Name && AstName (current.name ) == " const" ) ||
510+ ((FFlag::DebugLuauUserDefinedClasses && current.type == Lexeme::Name) && AstName (current.name ) == " class" ))
514511 {
515512 return parseExportValue (expr->location , expr->location .begin , AstArray<AstAttr*>({nullptr , 0 }));
516513 }
517- else if (lexer. current () .type == Lexeme::Name && AstName (lexer. current () .name ) == " type" )
514+ else if (current.type == Lexeme::Name && AstName (current.name ) == " type" )
518515 {
519- Position typeKeywordPosition = lexer. current () .location .begin ;
516+ Position typeKeywordPosition = current.location .begin ;
520517 nextLexeme ();
521518 return parseTypeAlias (expr->location , /* exported= */ true , typeKeywordPosition);
522519 }
523520 }
521+ // TODO: remove with LuauExportValueSyntax
522+ else if (FFlag::DebugLuauUserDefinedClasses && AstName (lexer.current ().name ) == " class" )
523+ {
524+ nextLexeme ();
525+ return parseClassStat (start, /* exported*/ true );
526+ }
524527 else
525528 {
526529 if (lexer.current ().type == Lexeme::Name && AstName (lexer.current ().name ) == " type" )
@@ -1816,10 +1819,9 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
18161819 // global variable declaration whose name is `class`, not as a malformed class declaration. This allows
18171820 // us to support a global table like string/math/bit32 called `class`. CLI-203833 tracks the work to actually
18181821 // remove support for `declare class X [extends Y]` syntax.
1819- else if (
1820- (AstName (lexer.current ().name ) == " class" && (FFlag::LuauAllowGlobalDeclarationToBeCalledClass ? lexer.lookahead ().type != ' :' : true )) ||
1821- AstName (lexer.current ().name ) == " extern"
1822- )
1822+ else if ((AstName (lexer.current ().name ) == " class" &&
1823+ (FFlag::LuauAllowGlobalDeclarationToBeCalledClass ? lexer.lookahead ().type != ' :' : true )) ||
1824+ AstName (lexer.current ().name ) == " extern" )
18231825 {
18241826 bool foundExtern = false ;
18251827 if (AstName (lexer.current ().name ) == " extern" )
@@ -1934,25 +1936,22 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
19341936 {
19351937 AstTableAccess access = AstTableAccess::ReadWrite;
19361938
1937- if (FFlag::LuauExternReadWriteAttributes )
1939+ if (lexer. current (). type == Lexeme::Name && lexer. lookahead (). type != ' : ' )
19381940 {
1939- if (lexer.current ().type == Lexeme::Name && lexer. lookahead (). type != ' : ' )
1941+ if (AstName ( lexer.current ().name ) == " read " )
19401942 {
1941- if (AstName (lexer.current ().name ) == " read" )
1942- {
1943- access = AstTableAccess::Read;
1944- lexer.next ();
1945- }
1946- else if (AstName (lexer.current ().name ) == " write" )
1947- {
1948- access = AstTableAccess::Write;
1949- lexer.next ();
1950- }
1951- else
1952- {
1953- report (lexer.current ().location , " Expected blank or 'read' or 'write' attribute, got '%s'" , lexer.current ().name );
1954- lexer.next ();
1955- }
1943+ access = AstTableAccess::Read;
1944+ lexer.next ();
1945+ }
1946+ else if (AstName (lexer.current ().name ) == " write" )
1947+ {
1948+ access = AstTableAccess::Write;
1949+ lexer.next ();
1950+ }
1951+ else
1952+ {
1953+ report (lexer.current ().location , " Expected blank or 'read' or 'write' attribute, got '%s'" , lexer.current ().name );
1954+ lexer.next ();
19561955 }
19571956 }
19581957
@@ -2060,7 +2059,7 @@ AstStat* Parser::parseExportValue(const Location& start, const Position keywordP
20602059 return true ;
20612060 };
20622061
2063- auto exportLocalStat = [&](AstStat* stat) -> AstStat*
2062+ auto exportLocalStat = [&](AstStat* stat, const Position& keywordPosition ) -> AstStat*
20642063 {
20652064 if (AstStatLocal* localStat = stat->as <AstStatLocal>())
20662065 {
@@ -2073,6 +2072,14 @@ AstStat* Parser::parseExportValue(const Location& start, const Position keywordP
20732072
20742073 local->isExported = true ;
20752074 }
2075+
2076+ if (options.storeCstData )
2077+ {
2078+ // if storeCstData is set, then when we parsed the local the cst data should be stored
2079+ CstStatLocal* cstStatLocal = cstNodeMap[stat]->as <CstStatLocal>();
2080+ LUAU_ASSERT (cstStatLocal);
2081+ cstStatLocal->declarationKeywordPosition = keywordPosition;
2082+ }
20762083 }
20772084 else
20782085 LUAU_ASSERT (!" Expected export local/const to parse as AstStatLocal" );
@@ -2091,14 +2098,20 @@ AstStat* Parser::parseExportValue(const Location& start, const Position keywordP
20912098
20922099 if (lexer.current ().type == Lexeme::ReservedLocal)
20932100 {
2101+ Position localKeywordPosition = lexer.current ().location .begin ;
2102+
20942103 if (lexer.lookahead ().type == Lexeme::ReservedFunction)
20952104 return reportStatError (start, {}, {}, " 'export' must be followed by an identifier or 'function'; try removing 'local'" );
20962105
2097- return exportLocalStat (parseLocal (start, keywordPosition, {nullptr , 0 }, false ));
2106+ return exportLocalStat (parseLocal (start, keywordPosition, {nullptr , 0 }, false ), localKeywordPosition );
20982107 }
20992108 else if (lexer.current ().type == Lexeme::ReservedFunction)
21002109 {
21012110 auto funcStat = parseLocal (start, keywordPosition, attributes, true );
2111+ if (!funcStat->is <AstStatLocalFunction>())
2112+ // parseLocal returned a parse error
2113+ return funcStat;
2114+
21022115 auto func = funcStat->as <AstStatLocalFunction>();
21032116
21042117 if (!checkDuplicateExport (func->name ->name , func->name ->location ))
@@ -2118,7 +2131,22 @@ AstStat* Parser::parseExportValue(const Location& start, const Position keywordP
21182131 if (lexer.current ().type == Lexeme::ReservedFunction)
21192132 return reportStatError (start, {}, {}, " 'export' must be followed by an identifier or 'function'" );
21202133
2121- return exportLocalStat (parseLocal (start, constKeywordPosition, {nullptr , 0 }, true ));
2134+ return exportLocalStat (parseLocal (start, constKeywordPosition, {nullptr , 0 }, true ), constKeywordPosition);
2135+ }
2136+ else if (FFlag::DebugLuauUserDefinedClasses && lexer.current ().type == Lexeme::Name && AstName (lexer.current ().name ) == " class" )
2137+ {
2138+ nextLexeme ();
2139+ auto stat = parseClassStat (start, /* exported*/ true );
2140+ if (auto classStat = stat->as <AstStatClass>())
2141+ {
2142+ if (!checkDuplicateExport (classStat->name ->name , classStat->name ->location ))
2143+ return reportStatError (
2144+ classStat->name ->location , {}, copy<AstStat*>({classStat}), " Duplicate exported class '%s'" , classStat->name ->name .value
2145+ );
2146+
2147+ classStat->name ->isExported = true ;
2148+ }
2149+ return stat;
21222150 }
21232151
21242152 return reportStatError (start, {}, {}, " 'export' must be followed by an identifier or 'function'" );
@@ -3486,9 +3514,8 @@ std::optional<AstExprBinary::Op> Parser::checkBinaryConfusables(const BinaryOpPr
34863514 report (Location (start, next.location ), " Unexpected '||'; did you mean 'or'?" );
34873515 return AstExprBinary::Or;
34883516 }
3489- else if (
3490- curr.type == ' !' && next.type == ' =' && curr.location .end == next.location .begin && binaryPriority[AstExprBinary::CompareNe].left > limit
3491- )
3517+ else if (curr.type == ' !' && next.type == ' =' && curr.location .end == next.location .begin &&
3518+ binaryPriority[AstExprBinary::CompareNe].left > limit)
34923519 {
34933520 nextLexeme ();
34943521 report (Location (start, next.location ), " Unexpected '!='; did you mean '~='?" );
@@ -3948,10 +3975,8 @@ AstExpr* Parser::parseSimpleExpr()
39483975 {
39493976 return parseNumber ();
39503977 }
3951- else if (
3952- lexer.current ().type == Lexeme::RawString || lexer.current ().type == Lexeme::QuotedString ||
3953- lexer.current ().type == Lexeme::InterpStringSimple
3954- )
3978+ else if (lexer.current ().type == Lexeme::RawString || lexer.current ().type == Lexeme::QuotedString ||
3979+ lexer.current ().type == Lexeme::InterpStringSimple)
39553980 {
39563981 return parseString ();
39573982 }
@@ -4151,11 +4176,13 @@ AstExpr* Parser::parseTableConstructor()
41514176
41524177 MatchLexeme matchBrace = lexer.current ();
41534178 expectAndConsume (' {' , " table literal" );
4154- unsigned lastElementIndent = 0 ;
4179+ // Clip with LuauTableEntriesDontNeedToMatchIndent
4180+ unsigned lastElementIndent_DEPRECATED = 0 ;
41554181
41564182 while (lexer.current ().type != ' }' )
41574183 {
4158- lastElementIndent = lexer.current ().location .begin .column ;
4184+ if (!FFlag::LuauTableEntriesDontNeedToMatchIndent)
4185+ lastElementIndent_DEPRECATED = lexer.current ().location .begin .column ;
41594186
41604187 if (lexer.current ().type == ' [' )
41614188 {
@@ -4238,7 +4265,8 @@ AstExpr* Parser::parseTableConstructor()
42384265 {
42394266 nextLexeme ();
42404267 }
4241- else if ((lexer.current ().type == ' [' || lexer.current ().type == Lexeme::Name) && lexer.current ().location .begin .column == lastElementIndent)
4268+ else if ((lexer.current ().type == ' [' || lexer.current ().type == Lexeme::Name) &&
4269+ (FFlag::LuauTableEntriesDontNeedToMatchIndent ? true : lexer.current ().location .begin .column == lastElementIndent_DEPRECATED))
42424270 {
42434271 report (lexer.current ().location , " Expected ',' after table constructor element" );
42444272 }
0 commit comments