diff --git a/Ast/src/Parser.cpp b/Ast/src/Parser.cpp index e26df1fa..5ca480e8 100644 --- a/Ast/src/Parser.cpp +++ b/Ast/src/Parser.cpp @@ -17,6 +17,7 @@ LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100) // flag so that we don't break production games by reverting syntax changes. // See docs/SyntaxChanges.md for an explanation. LUAU_FASTFLAGVARIABLE(DebugLuauDeferredConstraintResolution, false) +LUAU_FASTFLAGVARIABLE(LuauLeadingBarAndAmpersand, false) namespace Luau { @@ -1523,7 +1524,11 @@ AstType* Parser::parseFunctionTypeTail(const Lexeme& begin, AstArray parts(scratchType); - parts.push_back(type); + + if (!FFlag::LuauLeadingBarAndAmpersand || type != nullptr) + { + parts.push_back(type); + } incrementRecursionCounter("type annotation"); @@ -1623,15 +1628,34 @@ AstTypeOrPack Parser::parseTypeOrPack() AstType* Parser::parseType(bool inDeclarationContext) { unsigned int oldRecursionCount = recursionCounter; - // recursion counter is incremented in parseSimpleType + // recursion counter is incremented in parseSimpleType and/or parseTypeSuffix Location begin = lexer.current().location; - AstType* type = parseSimpleType(/* allowPack= */ false, /* in declaration context */ inDeclarationContext).type; + if (FFlag::LuauLeadingBarAndAmpersand) + { + AstType* type = nullptr; - recursionCounter = oldRecursionCount; + Lexeme::Type c = lexer.current().type; + if (c != '|' && c != '&') + { + type = parseSimpleType(/* allowPack= */ false, /* in declaration context */ inDeclarationContext).type; + recursionCounter = oldRecursionCount; + } - return parseTypeSuffix(type, begin); + AstType* typeWithSuffix = parseTypeSuffix(type, begin); + recursionCounter = oldRecursionCount; + + return typeWithSuffix; + } + else + { + AstType* type = parseSimpleType(/* allowPack= */ false, /* in declaration context */ inDeclarationContext).type; + + recursionCounter = oldRecursionCount; + + return parseTypeSuffix(type, begin); + } } // Type ::= nil | Name[`.' Name] [ `<' Type [`,' ...] `>' ] | `typeof' `(' expr `)' | `{' [PropList] `}' diff --git a/tests/Parser.test.cpp b/tests/Parser.test.cpp index b178f539..6b4bcf22 100644 --- a/tests/Parser.test.cpp +++ b/tests/Parser.test.cpp @@ -16,6 +16,7 @@ LUAU_FASTINT(LuauRecursionLimit); LUAU_FASTINT(LuauTypeLengthLimit); LUAU_FASTINT(LuauParseErrorLimit); LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAG(LuauLeadingBarAndAmpersand); namespace { @@ -3167,4 +3168,26 @@ TEST_CASE_FIXTURE(Fixture, "read_write_table_properties") LUAU_ASSERT(pr.errors.size() == 0); } +TEST_CASE_FIXTURE(Fixture, "can_parse_leading_bar_unions_successfully") +{ + ScopedFastFlag sff{FFlag::LuauLeadingBarAndAmpersand, true}; + + parse(R"(type A = | "Hello" | "World")"); +} + +TEST_CASE_FIXTURE(Fixture, "can_parse_leading_ampersand_intersections_successfully") +{ + ScopedFastFlag sff{FFlag::LuauLeadingBarAndAmpersand, true}; + + parse(R"(type A = & { string } & { number })"); +} + +TEST_CASE_FIXTURE(Fixture, "mixed_leading_intersection_and_union_not_allowed") +{ + ScopedFastFlag sff{FFlag::LuauLeadingBarAndAmpersand, true}; + + matchParseError("type A = & number | string | boolean", "Mixing union and intersection types is not allowed; consider wrapping in parentheses."); + matchParseError("type A = | number & string & boolean", "Mixing union and intersection types is not allowed; consider wrapping in parentheses."); +} + TEST_SUITE_END();