Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/quick-lint-js/error.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,13 @@
.error(QLJS_TRANSLATABLE("'else' has no corresponding 'if'"), \
else_token)) \
\
QLJS_ERROR_TYPE( \
error_else_with_conditional_missing_if, "E???", \
{ source_code_span else_token; }, \
.warning(QLJS_TRANSLATABLE("'else' with condition followed by block; " \
"maybe 'else if' was intended"), \
else_token)) \
\
QLJS_ERROR_TYPE( \
error_escaped_character_disallowed_in_identifiers, "E012", \
{ source_code_span escape_sequence; }, \
Expand Down
20 changes: 19 additions & 1 deletion src/quick-lint-js/parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -2515,8 +2515,26 @@ class parser {
}

if (this->peek().type == token_type::kw_else) {
source_code_span else_span = this->peek().span();
this->skip();
parse_and_visit_body();

switch (this->peek().type) {
default:
parse_and_visit_body();
break;

case token_type::left_paren:
expression *ast = this->parse_expression(precedence{});
this->visit_expression(ast, v, variable_context::rhs);

if (this->peek().type == token_type::left_curly) {
this->error_reporter_->report(error_else_with_conditional_missing_if{
.else_token = else_span,
});
parse_and_visit_body();
}
break;
}
}
}

Expand Down
36 changes: 36 additions & 0 deletions test/test-parse-statement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,42 @@ TEST(test_parse, else_without_if) {
}
}

TEST(test_parse, else_if) {
{
spy_visitor v =
parse_and_visit_statement(u8"if (a) { b; } else if (c) { d; }"_sv);
EXPECT_THAT(v.visits, ElementsAre("visit_variable_use", // a
"visit_enter_block_scope", // <if>
"visit_variable_use", // b
"visit_exit_block_scope", // </if>
"visit_variable_use", // c
"visit_enter_block_scope", // <else>
"visit_variable_use", // d
"visit_exit_block_scope")); // </else>
}

{
spy_visitor v;
padded_string code(u8"if (a) { b; } else (c) { d; }"_sv);
parser p(&code, &v);
EXPECT_TRUE(p.parse_and_visit_statement(v));
EXPECT_THAT(v.visits, ElementsAre("visit_variable_use", // a
"visit_enter_block_scope", // <if>
"visit_variable_use", // b
"visit_exit_block_scope", // </if>
"visit_variable_use", // c
"visit_enter_block_scope", // <block>
"visit_variable_use", // d
"visit_exit_block_scope")); // </block>

EXPECT_THAT(
v.errors,
ElementsAre(ERROR_TYPE_FIELD(
error_else_with_conditional_missing_if, else_token,
offsets_matcher(&code, strlen(u8"if (a) { b; } "), u8"else"))));
}
}

TEST(test_parse, block_statement) {
{
spy_visitor v = parse_and_visit_statement(u8"{ }"_sv);
Expand Down