From 2b1300bbfb2938b57eb9668f1df63da57c6c3b71 Mon Sep 17 00:00:00 2001 From: "Matthew \"strager\" Glazar" Date: Wed, 27 Dec 2023 03:50:49 -0500 Subject: [PATCH] fix(typescript): fix <42>x; and other cases being parsed as JSX --- src/quick-lint-js/fe/parse-expression.cpp | 22 ++++++++++ ...-parse-typescript-angle-type-assertion.cpp | 44 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/src/quick-lint-js/fe/parse-expression.cpp b/src/quick-lint-js/fe/parse-expression.cpp index 05c75ee9b0..619453cafc 100644 --- a/src/quick-lint-js/fe/parse-expression.cpp +++ b/src/quick-lint-js/fe/parse-expression.cpp @@ -3606,7 +3606,29 @@ Expression* Parser::parse_jsx_or_typescript_generic_expression( } break; + // // JSX. + // expr // Type assertion. + case Token_Type::kw_keyof: + case Token_Type::kw_this: + case Token_Type::kw_readonly: + case Token_Type::kw_typeof: + case Token_Type::kw_unique: + case Token_Type::number: default: + // For these uncommon cases, don't try hard to guess what the user + // meant. + this->lexer_.roll_back_transaction(std::move(transaction)); + if (this->options_.jsx) { + return this->parse_jsx_expression(v); + } else { + return this->parse_typescript_angle_type_assertion_expression( + v, prec, + /*is_invalid_due_to_jsx_ambiguity=*/false); + } + break; + + // <>hello<> // JSX. + case Token_Type::greater: break; } this->lexer_.roll_back_transaction(std::move(transaction)); diff --git a/test/test-parse-typescript-angle-type-assertion.cpp b/test/test-parse-typescript-angle-type-assertion.cpp index 0e3b42ee5c..052d3b7062 100644 --- a/test/test-parse-typescript-angle-type-assertion.cpp +++ b/test/test-parse-typescript-angle-type-assertion.cpp @@ -117,6 +117,8 @@ TEST_F(Test_Parse_TypeScript_Angle_Type_Assertion, angle_type_assertion) { u8"< & Type>(expr);"_sv, u8"<[Type]>(expr);"_sv, u8"(expr);"_sv, + u8"(expr);"_sv, + u8"(expr);"_sv, u8"<{k: Type}>(expr);"_sv, }) { SCOPED_TRACE(out_string8(code)); @@ -158,6 +160,48 @@ TEST_F(Test_Parse_TypeScript_Angle_Type_Assertion, angle_type_assertion) { })); } + { + Spy_Visitor p = test_parse_and_visit_statement( + u8"expr;"_sv, no_diags, typescript_options); + EXPECT_THAT(p.visits, ElementsAreArray({ + "visit_enter_type_scope", // < + "visit_variable_use", // v + "visit_exit_type_scope", // > + "visit_variable_use", // expr + })); + EXPECT_THAT(p.variable_uses, ElementsAreArray({u8"v"_sv, u8"expr"_sv})); + } + + { + Spy_Visitor p = test_parse_and_visit_statement( + u8"expr;"_sv, no_diags, typescript_options); + EXPECT_THAT(p.visits, ElementsAreArray({ + "visit_enter_type_scope", // < + "visit_exit_type_scope", // > + "visit_variable_use", // expr + })); + } + + { + Spy_Visitor p = test_parse_and_visit_statement(u8"expr;"_sv, no_diags, + typescript_options); + EXPECT_THAT(p.visits, ElementsAreArray({ + "visit_enter_type_scope", // < + "visit_exit_type_scope", // > + "visit_variable_use", // expr + })); + } + + { + Spy_Visitor p = test_parse_and_visit_statement(u8"<42>expr;"_sv, no_diags, + typescript_options); + EXPECT_THAT(p.visits, ElementsAreArray({ + "visit_enter_type_scope", // < + "visit_exit_type_scope", // > + "visit_variable_use", // expr + })); + } + for (const String8& type : typescript_builtin_type_keywords | typescript_special_type_keywords) { Test_Parser p(concat(u8"<"_sv, type, u8">expr;"_sv), typescript_options);