diff --git a/Ast/include/Luau/Lexer.h b/Ast/include/Luau/Lexer.h index 8519ca82..b91879c6 100644 --- a/Ast/include/Luau/Lexer.h +++ b/Ast/include/Luau/Lexer.h @@ -84,6 +84,9 @@ struct Lexeme BrokenString, BrokenComment, BrokenUnicode, + + BrokenInterpDoubleBrace, + Error, Reserved_BEGIN, diff --git a/Ast/src/Lexer.cpp b/Ast/src/Lexer.cpp index b310c976..19c70708 100644 --- a/Ast/src/Lexer.cpp +++ b/Ast/src/Lexer.cpp @@ -184,6 +184,9 @@ std::string Lexeme::toString() const case BrokenComment: return "unfinished comment"; + case BrokenInterpDoubleBrace: + return "'{{', which is invalid (did you mean '\\{'?)"; + case BrokenUnicode: if (codepoint) { @@ -653,6 +656,11 @@ std::optional Lexer::readInterpolatedStringSection(Position start, Lexem case '{': { + if (peekch(1) == '{') + { + return std::optional(Lexeme(Location(start, position()), Lexeme::BrokenInterpDoubleBrace)); + } + incrementInterpolatedStringDepth(); auto lexemeOutput = Lexeme(Location(start, position()), Lexeme::InterpStringBegin, &buffer[startOffset], offset - startOffset); consume(); diff --git a/Ast/src/Parser.cpp b/Ast/src/Parser.cpp index 1c0027f3..43865e4e 100644 --- a/Ast/src/Parser.cpp +++ b/Ast/src/Parser.cpp @@ -2676,9 +2676,13 @@ AstExpr* Parser::parseInterpString() lexer.decrementInterpolatedStringDepth(); auto next = lexer.nextInterpolatedString(); - if (next.type == Lexeme::BrokenString) + + switch (next.type) { + case Lexeme::BrokenString: return reportExprError(location, {}, "Malformed interpolated string"); + case Lexeme::BrokenInterpDoubleBrace: + return reportExprError(location, {}, "Double braces are not permitted within interpolated strings. Did you mean '\\{'?"); } } while (true); } diff --git a/tests/Parser.test.cpp b/tests/Parser.test.cpp index 844c588c..1ec5314b 100644 --- a/tests/Parser.test.cpp +++ b/tests/Parser.test.cpp @@ -1034,7 +1034,7 @@ TEST_CASE_FIXTURE(Fixture, "parse_compound_assignment_error_multiple") } } -TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_double_brace") +TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_double_brace_begin") { try { @@ -1045,7 +1045,23 @@ TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_double_brace") } catch (const ParseErrors& e) { - CHECK_EQ("INTERP TODO: Message", e.getErrors().front().getMessage()); + CHECK_EQ("Expected identifier when parsing expression, got '{{', which is invalid (did you mean '\\{'?)", e.getErrors().front().getMessage()); + } +} + +TEST_CASE_FIXTURE(Fixture, "parse_interpolated_string_double_brace_mid") +{ + try + { + parse(R"( + _ = `{nice} {{oops}}` + )"); + FAIL("Expected ParseErrors to be thrown"); + } + catch (const ParseErrors& e) + { + // INTERP CODE REVIEW: It's weird for these two to have separate messages, but the one created by _begin is emergent from something else. + CHECK_EQ("Double braces are not permitted within interpolated strings. Did you mean '\\{'?", e.getErrors().front().getMessage()); } }