diff --git a/src/parse.cpp b/src/parse.cpp index cd4918a7b3..a0e1e46df4 100644 --- a/src/parse.cpp +++ b/src/parse.cpp @@ -280,9 +280,19 @@ expression* parser::parse_primary_expression(precedence prec) { if (this->peek().type == token_type::right_paren) { source_code_span right_paren_span = this->peek().span(); this->skip(); - if (this->peek().type == token_type::equal_greater) { - this->skip(); + bool is_arrow_function = this->peek().type == token_type::equal_greater; + bool is_arrow_function_without_arrow = + this->peek().type == token_type::left_curly; + if (is_arrow_function || is_arrow_function_without_arrow) { // Arrow function: () => expression-or-block + // Arrow function: () { } // Invalid. + if (is_arrow_function) { + this->skip(); + } else { + this->error_reporter_->report( + error_missing_arrow_operator_in_arrow_function{ + .where = this->peek().span()}); + } expression* ast = this->parse_arrow_function_body( function_attributes::normal, left_paren_span.begin(), /*allow_in_operator=*/prec.in_operator); diff --git a/src/quick-lint-js/error.h b/src/quick-lint-js/error.h index 93cb3e6e7e..14650cbcbf 100644 --- a/src/quick-lint-js/error.h +++ b/src/quick-lint-js/error.h @@ -666,6 +666,12 @@ expected_right_square) \ NOTE(QLJS_TRANSLATABLE("array started here"), left_square)) \ \ + QLJS_ERROR_TYPE( \ + error_missing_arrow_operator_in_arrow_function, "E176", \ + { source_code_span where; }, \ + ERROR(QLJS_TRANSLATABLE("missing arrow operator for arrow function"), \ + where)) \ + \ QLJS_ERROR_TYPE( \ error_missing_arrow_function_parameter_list, "E105", \ { source_code_span arrow; }, \ diff --git a/test/test-parse-function.cpp b/test/test-parse-function.cpp index 6bcf9e0106..97c43e3e3c 100644 --- a/test/test-parse-function.cpp +++ b/test/test-parse-function.cpp @@ -975,6 +975,38 @@ TEST(test_parse, arrow_function_with_invalid_parameters) { } } +TEST(test_parse, arrow_function_expression_without_arrow_operator) { + { + padded_string code(u8"(() {});"_sv); + spy_visitor v; + parser p(&code, &v); + p.parse_and_visit_module(v); + EXPECT_THAT(v.visits, ElementsAre("visit_enter_function_scope", // + "visit_enter_function_scope_body", // + "visit_exit_function_scope", // + "visit_end_of_module")); + EXPECT_THAT(v.errors, + ElementsAre(ERROR_TYPE_FIELD( + error_missing_arrow_operator_in_arrow_function, where, + offsets_matcher(&code, strlen(u8"(() "), u8"{")))); + } + + { + padded_string code(u8"(()\n{});"_sv); + spy_visitor v; + parser p(&code, &v); + p.parse_and_visit_module(v); + EXPECT_THAT(v.visits, ElementsAre("visit_enter_function_scope", // + "visit_enter_function_scope_body", // + "visit_exit_function_scope", // + "visit_end_of_module")); + EXPECT_THAT(v.errors, + ElementsAre(ERROR_TYPE_FIELD( + error_missing_arrow_operator_in_arrow_function, where, + offsets_matcher(&code, strlen(u8"(()\n"), u8"{")))); + } +} + TEST(test_parse, generator_function_with_misplaced_star) { { padded_string code(u8"function f*(x) { yield x; }"_sv);