Skip to content

Commit 4444beb

Browse files
wgrrstrager
authored andcommitted
parse: Fix assertion failure when function has no body
Signed-off-by: wagner riffel <[email protected]>
1 parent 6791ccf commit 4444beb

File tree

3 files changed

+189
-0
lines changed

3 files changed

+189
-0
lines changed

src/quick-lint-js/error.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,11 @@
794794
.error(QLJS_TRANSLATABLE("missing function parameter list"), \
795795
function_name)) \
796796
\
797+
QLJS_ERROR_TYPE( \
798+
error_missing_function_body, "E172", \
799+
{ source_code_span expected_body; }, \
800+
.error(QLJS_TRANSLATABLE("missing body for function"), expected_body)) \
801+
\
797802
QLJS_ERROR_TYPE( \
798803
error_missing_header_of_for_loop, "E096", { source_code_span where; }, \
799804
.error(QLJS_TRANSLATABLE("missing for loop header"), where)) \

src/quick-lint-js/parse.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1260,6 +1260,14 @@ class parser {
12601260
}
12611261
this->skip();
12621262

1263+
if (this->peek().type != token_type::left_curly) {
1264+
const char8 *expected_body = this->lexer_.end_of_previous_token();
1265+
this->error_reporter_->report(error_missing_function_body{
1266+
.expected_body = source_code_span(expected_body, expected_body)});
1267+
1268+
// Don't parse a function body.
1269+
return;
1270+
}
12631271
break;
12641272

12651273
// function f {} // Invalid.

test/test-parse-function.cpp

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,182 @@ TEST(test_parse, function_statement_without_parameter_list_or_body) {
619619
}
620620
}
621621

622+
TEST(test_parse, named_function_statement_without_body) {
623+
{
624+
padded_string code(u8"function f()\nf;"_sv);
625+
spy_visitor v;
626+
parser p(&code, &v);
627+
EXPECT_TRUE(p.parse_and_visit_statement(v));
628+
EXPECT_THAT(v.visits, ElementsAre("visit_variable_declaration", // f
629+
"visit_enter_function_scope", //
630+
"visit_exit_function_scope")); //
631+
EXPECT_THAT(v.errors,
632+
ElementsAre(ERROR_TYPE_FIELD(
633+
error_missing_function_body, expected_body,
634+
offsets_matcher(&code, strlen(u8"function f()"), u8""))));
635+
}
636+
637+
{
638+
padded_string code(u8"function f(x)"_sv);
639+
spy_visitor v;
640+
parser p(&code, &v);
641+
EXPECT_TRUE(p.parse_and_visit_statement(v));
642+
EXPECT_THAT(v.visits, ElementsAre("visit_variable_declaration", // f
643+
"visit_enter_function_scope", //
644+
"visit_variable_declaration", // x
645+
"visit_exit_function_scope")); //
646+
EXPECT_THAT(v.errors,
647+
ElementsAre(ERROR_TYPE_FIELD(
648+
error_missing_function_body, expected_body,
649+
offsets_matcher(&code, strlen(u8"function f(x)"), u8""))));
650+
}
651+
652+
{
653+
padded_string code(u8"class f { m() }"_sv);
654+
spy_visitor v;
655+
parser p(&code, &v);
656+
EXPECT_TRUE(p.parse_and_visit_statement(v));
657+
EXPECT_THAT(v.visits, ElementsAre("visit_variable_declaration", // f
658+
"visit_enter_class_scope", //
659+
"visit_property_declaration", // m
660+
"visit_enter_function_scope", //
661+
"visit_exit_function_scope", //
662+
"visit_exit_class_scope")); //
663+
EXPECT_THAT(v.errors,
664+
ElementsAre(ERROR_TYPE_FIELD(
665+
error_missing_function_body, expected_body,
666+
offsets_matcher(&code, strlen(u8"class f { m()"), u8""))));
667+
}
668+
669+
{
670+
padded_string code(u8"class f { m(x) }"_sv);
671+
spy_visitor v;
672+
parser p(&code, &v);
673+
EXPECT_TRUE(p.parse_and_visit_statement(v));
674+
EXPECT_THAT(v.visits, ElementsAre("visit_variable_declaration", // f
675+
"visit_enter_class_scope", //
676+
"visit_property_declaration", // m
677+
"visit_enter_function_scope", //
678+
"visit_variable_declaration", // x
679+
"visit_exit_function_scope", //
680+
"visit_exit_class_scope")); //
681+
EXPECT_THAT(v.errors,
682+
ElementsAre(ERROR_TYPE_FIELD(
683+
error_missing_function_body, expected_body,
684+
offsets_matcher(&code, strlen(u8"class f { m(x)"), u8""))));
685+
}
686+
687+
{
688+
padded_string code(u8"export default function f()"_sv);
689+
spy_visitor v;
690+
parser p(&code, &v);
691+
EXPECT_TRUE(p.parse_and_visit_statement(v));
692+
EXPECT_THAT(v.visits, ElementsAre("visit_variable_declaration", // f
693+
"visit_enter_function_scope", //
694+
"visit_exit_function_scope")); //
695+
EXPECT_THAT(
696+
v.errors,
697+
ElementsAre(ERROR_TYPE_FIELD(
698+
error_missing_function_body, expected_body,
699+
offsets_matcher(&code, strlen(u8"export default function f()"),
700+
u8""))));
701+
}
702+
703+
{
704+
padded_string code(u8"function* f()"_sv);
705+
spy_visitor v;
706+
parser p(&code, &v);
707+
EXPECT_TRUE(p.parse_and_visit_statement(v));
708+
EXPECT_THAT(v.visits, ElementsAre("visit_variable_declaration", // f
709+
"visit_enter_function_scope", //
710+
"visit_exit_function_scope")); //
711+
EXPECT_THAT(v.errors,
712+
ElementsAre(ERROR_TYPE_FIELD(
713+
error_missing_function_body, expected_body,
714+
offsets_matcher(&code, strlen(u8"function* f()"), u8""))));
715+
}
716+
}
717+
718+
TEST(test_parse, unnamed_function_statement_without_body) {
719+
{
720+
padded_string code(u8"function*()"_sv);
721+
spy_visitor v;
722+
parser p(&code, &v);
723+
EXPECT_TRUE(p.parse_and_visit_statement(v));
724+
EXPECT_THAT(v.visits, ElementsAre("visit_enter_function_scope", //
725+
"visit_exit_function_scope")); //
726+
EXPECT_THAT(
727+
v.errors,
728+
ElementsAre(ERROR_TYPE_FIELD(
729+
error_missing_function_body, expected_body,
730+
offsets_matcher(&code, strlen(u8"function*()"), u8"")),
731+
ERROR_TYPE_FIELD(
732+
error_missing_name_in_function_statement, where,
733+
offsets_matcher(&code, strlen(u8""), u8"function*("))));
734+
}
735+
736+
{
737+
padded_string code(u8"function()"_sv);
738+
spy_visitor v;
739+
parser p(&code, &v);
740+
EXPECT_TRUE(p.parse_and_visit_statement(v));
741+
EXPECT_THAT(v.visits, ElementsAre("visit_enter_function_scope", //
742+
"visit_exit_function_scope")); //
743+
EXPECT_THAT(
744+
v.errors,
745+
ElementsAre(ERROR_TYPE_FIELD(
746+
error_missing_function_body, expected_body,
747+
offsets_matcher(&code, strlen(u8"function()"), u8"")),
748+
ERROR_TYPE_FIELD(
749+
error_missing_name_in_function_statement, where,
750+
offsets_matcher(&code, strlen(u8""), u8"function("))));
751+
}
752+
753+
{
754+
padded_string code(u8"export default function()"_sv);
755+
spy_visitor v;
756+
parser p(&code, &v);
757+
EXPECT_TRUE(p.parse_and_visit_statement(v));
758+
EXPECT_THAT(v.visits, ElementsAre("visit_enter_function_scope", //
759+
"visit_exit_function_scope")); //
760+
EXPECT_THAT(v.errors,
761+
ElementsAre(ERROR_TYPE_FIELD(
762+
error_missing_function_body, expected_body,
763+
offsets_matcher(
764+
&code, strlen(u8"export default function()"), u8""))));
765+
}
766+
}
767+
768+
TEST(test_parse, named_function_expression_without_body) {
769+
{
770+
padded_string code(u8"(function f())"_sv);
771+
spy_visitor v;
772+
parser p(&code, &v);
773+
EXPECT_TRUE(p.parse_and_visit_statement(v));
774+
EXPECT_THAT(v.visits, ElementsAre("visit_enter_named_function_scope", //
775+
"visit_exit_function_scope")); //
776+
EXPECT_THAT(v.errors,
777+
ElementsAre(ERROR_TYPE_FIELD(
778+
error_missing_function_body, expected_body,
779+
offsets_matcher(&code, strlen(u8"(function f()"), u8""))));
780+
}
781+
}
782+
783+
TEST(test_parse, unnamed_function_expression_without_body) {
784+
{
785+
padded_string code(u8"(function())"_sv);
786+
spy_visitor v;
787+
parser p(&code, &v);
788+
EXPECT_TRUE(p.parse_and_visit_statement(v));
789+
EXPECT_THAT(v.visits, ElementsAre("visit_enter_function_scope", //
790+
"visit_exit_function_scope")); //
791+
EXPECT_THAT(v.errors,
792+
ElementsAre(ERROR_TYPE_FIELD(
793+
error_missing_function_body, expected_body,
794+
offsets_matcher(&code, strlen(u8"(function()"), u8""))));
795+
}
796+
}
797+
622798
TEST(test_parse, arrow_function_without_parameter_list) {
623799
{
624800
padded_string code(u8"=> x + y"_sv);

0 commit comments

Comments
 (0)