diff --git a/Analysis/src/AstQuery.cpp b/Analysis/src/AstQuery.cpp index cebb226a..928e5dfb 100644 --- a/Analysis/src/AstQuery.cpp +++ b/Analysis/src/AstQuery.cpp @@ -12,6 +12,7 @@ #include LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); +LUAU_FASTFLAGVARIABLE(LuauFixBindingForGlobalPos, false); namespace Luau { @@ -332,6 +333,11 @@ std::optional findExpectedTypeAtPosition(const Module& module, const Sou static std::optional findBindingLocalStatement(const SourceModule& source, const Binding& binding) { + // Bindings coming from global sources (e.g., definition files) have a zero position. + // They cannot be defined from a local statement + if (FFlag::LuauFixBindingForGlobalPos && binding.location == Location{{0, 0}, {0, 0}}) + return std::nullopt; + std::vector nodes = findAstAncestryOfPosition(source, binding.location.begin); auto iter = std::find_if(nodes.rbegin(), nodes.rend(), [](AstNode* node) { return node->is(); 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/AstQuery.test.cpp b/tests/AstQuery.test.cpp index 769637a5..c53fe731 100644 --- a/tests/AstQuery.test.cpp +++ b/tests/AstQuery.test.cpp @@ -6,6 +6,8 @@ #include "doctest.h" #include "Fixture.h" +LUAU_FASTFLAG(LuauFixBindingForGlobalPos); + using namespace Luau; struct DocumentationSymbolFixture : BuiltinsFixture @@ -331,4 +333,16 @@ TEST_CASE_FIXTURE(Fixture, "find_expr_ancestry") CHECK(ancestry.back()->is()); } +TEST_CASE_FIXTURE(BuiltinsFixture, "find_binding_at_position_global_start_of_file") +{ + ScopedFastFlag sff{FFlag::LuauFixBindingForGlobalPos, true}; + check("local x = string.char(1)"); + const Position pos(0, 12); + + std::optional binding = findBindingAtPosition(*getMainModule(), *getMainSourceModule(), pos); + + REQUIRE(binding); + CHECK_EQ(binding->location, Location{Position{0, 0}, Position{0, 0}}); +} + TEST_SUITE_END(); 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();