Skip to content

Commit 82917f4

Browse files
committed
Warn if semicolon between else and opening brace and no newline.
1 parent 2ea8012 commit 82917f4

File tree

4 files changed

+39
-20
lines changed

4 files changed

+39
-20
lines changed

docs/errors/E220.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# E220: Semicolon after else may cause unexpected execution of its body.
2+
3+
A semicolon next to the `else` keyword can cause its body to be executed even when previous `if` statement condition is true.
4+
5+
if (true) {
6+
console.log("true");
7+
} else; {
8+
console.log("& false");
9+
}
10+
> true
11+
> & false
12+
13+
To avoid this behavior, remove the semicolon between the else keyword and the opening brace of the else block.

src/quick-lint-js/error.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,6 +1028,13 @@
10281028
QLJS_TRANSLATABLE("for-of loop expression cannot have semicolons"), \
10291029
semicolon)) \
10301030
\
1031+
QLJS_ERROR_TYPE( \
1032+
error_unexpected_semicolon_after_else, "E202", \
1033+
{ source_code_span semicolon; }, \
1034+
.warning(QLJS_TRANSLATABLE("semicolon after else may be causing " \
1035+
"unexpected behavior"), \
1036+
semicolon)) \
1037+
\
10311038
QLJS_ERROR_TYPE( \
10321039
error_no_digits_in_binary_number, "E049", \
10331040
{ source_code_span characters; }, \
@@ -1139,12 +1146,6 @@
11391146
{ source_code_span continue_statement; }, \
11401147
.error(QLJS_TRANSLATABLE("continue can only be used inside of a loop"), \
11411148
continue_statement)) \
1142-
\
1143-
QLJS_ERROR_TYPE( \
1144-
error_redundant_semicolon_after_else, "E202", \
1145-
{ source_code_span semicolon; }, \
1146-
.warning(QLJS_TRANSLATABLE("redundant semicolon after else"), \
1147-
semicolon)) \
11481149
/* END */
11491150

11501151
namespace quick_lint_js {

src/quick-lint-js/parse.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2516,14 +2516,18 @@ class parser {
25162516

25172517
if (this->peek().type == token_type::kw_else) {
25182518
this->skip();
2519-
if (this->peek().type == token_type::semicolon) {
2520-
source_code_span semicolon = this->peek().span();
2521-
this->error_reporter_->report(error_redundant_semicolon_after_else{
2522-
.semicolon = source_code_span(
2523-
semicolon.begin(), semicolon.end()),
2519+
2520+
bool semicolon_after_else = this->peek().type == token_type::semicolon;
2521+
const char8 *token_after_else_begin = this->peek().begin;
2522+
2523+
parse_and_visit_body();
2524+
if (semicolon_after_else &&
2525+
this->peek().type == token_type::left_curly &&
2526+
!this->peek().has_leading_newline) {
2527+
this->error_reporter_->report(error_unexpected_semicolon_after_else{
2528+
.semicolon = source_code_span(token_after_else_begin, token_after_else_begin+1)
25242529
});
25252530
}
2526-
parse_and_visit_body();
25272531
}
25282532
}
25292533

test/test-parse-statement.cpp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -578,19 +578,20 @@ TEST(test_parse, else_without_if) {
578578
}
579579
}
580580

581-
TEST(test_parse, else_redundant_semicolon) {
581+
TEST(test_parse, else_unexpected_semicolon) {
582582
{
583583
spy_visitor v;
584584
padded_string code(u8"if (cond) { body; } else; { body; }"_sv);
585585
parser p(&code, &v);
586586
EXPECT_TRUE(p.parse_and_visit_statement(v));
587-
EXPECT_THAT(v.visits, ElementsAre("visit_variable_use", // cond
588-
"visit_enter_block_scope", // (if)
589-
"visit_variable_use", // body
590-
"visit_exit_block_scope")); // (else)
591-
EXPECT_THAT(v.errors, ElementsAre(ERROR_TYPE_FIELD(
592-
error_redundant_semicolon_after_else, semicolon,
593-
offsets_matcher(&code, strlen(u8"if (cond) { body; } else"), u8";"))));
587+
EXPECT_THAT(v.visits, ElementsAre("visit_variable_use", // cond
588+
"visit_enter_block_scope", // (if)
589+
"visit_variable_use", // body
590+
"visit_exit_block_scope")); // (if)
591+
EXPECT_THAT(v.errors,
592+
ElementsAre(ERROR_TYPE_FIELD(
593+
error_unexpected_semicolon_after_else, semicolon,
594+
offsets_matcher(&code, strlen(u8"if (cond) { body; } else"), u8";"))));
594595
}
595596
}
596597

0 commit comments

Comments
 (0)