-
-
Notifications
You must be signed in to change notification settings - Fork 192
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/warn missing fallthrough #1094
Changes from 6 commits
90b6ce8
9a31c29
a95534f
221cd6a
b23aa3e
731cce5
f8ed5fa
a190be5
d73c09a
2516ebc
f07f052
f121cb6
1be8e48
6cc3562
9f96079
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# E0423: missing fallthrough comment in switch case | ||
|
||
Switch Cases in javascript fallthrough to the next case if the `break` statement is not added at the end of the case. | ||
Since there is no explicit way of communication whether the fallthrough is intentional or not, it is recommended to use a comment indicating fallthrough. | ||
|
||
```javascript | ||
function test (c) { | ||
switch (c) { | ||
case 1: | ||
foo(); | ||
default: | ||
bar(); | ||
} | ||
} | ||
``` | ||
|
||
To fix this error, place a comment at the end of `case 1` indicating fallthrough | ||
|
||
```javascript | ||
function test (c) { | ||
switch (c) { | ||
case 1: | ||
foo(); | ||
//fallthrough | ||
default: | ||
bar(); | ||
} | ||
} | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -759,6 +759,13 @@ struct Diag_Duplicated_Cases_In_Switch_Statement { | |
Source_Code_Span duplicated_switch_case; | ||
}; | ||
|
||
struct Diag_Explicit_Fallthrough_Comment_In_Switch { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: We normally name the diagnostic struct after the problem, not the solution. Therefore this should be named something like |
||
[[qljs::diag("E0423", Diagnostic_Severity::warning)]] // | ||
[[qljs::message("missing fallthrough comment", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor: This message and the message in docs/errors/E0423.md should match. |
||
ARG(end_of_case))]] // | ||
Source_Code_Span end_of_case; | ||
}; | ||
|
||
struct Diag_Else_Has_No_If { | ||
[[qljs::diag("E0065", Diagnostic_Severity::error)]] // | ||
[[qljs::message("'else' has no corresponding 'if'", ARG(else_token))]] // | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2766,15 +2766,51 @@ void Parser::parse_and_visit_switch(Parse_Visitor_Base &v) { | |
|
||
bool keep_going = true; | ||
bool is_before_first_switch_case = true; | ||
Token prev_token; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: I think this variable's name is bad. Maybe |
||
Hash_Set<String8_View> cases; | ||
while (keep_going) { | ||
auto is_comment_line = [&]() -> bool { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @strager the function to find the comment, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Must fix: We should not repeat comment-parsing code like you have done here. I see two options:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are right. I went with the suggestion of adding |
||
const Char8 *th = this->lexer().end_of_previous_token(); | ||
bool f = false; | ||
int i = 0; | ||
for (;;) { | ||
switch (th[i]) { | ||
case '/': | ||
if (th[i + 1] == '/') { | ||
f = true; | ||
} | ||
goto exit_loop; | ||
// skip empty lines to reach comment | ||
case ' ': | ||
case '\n': | ||
case '\r': | ||
case '\t': | ||
case '\f': | ||
case '\v': | ||
break; | ||
default: | ||
goto exit_loop; | ||
} | ||
i++; | ||
} | ||
exit_loop:; | ||
return f; | ||
}; | ||
switch (this->peek().type) { | ||
case Token_Type::right_curly: | ||
this->skip(); | ||
keep_going = false; | ||
break; | ||
|
||
case Token_Type::kw_case: { | ||
if (!is_before_first_switch_case && | ||
prev_token.type != Token_Type::kw_break && | ||
prev_token.type != Token_Type::kw_case && !is_comment_line()) { | ||
this->diag_reporter_->report( | ||
Diag_Explicit_Fallthrough_Comment_In_Switch{.end_of_case = | ||
prev_token.span()}); | ||
} | ||
prev_token = this->peek(); | ||
is_before_first_switch_case = false; | ||
Source_Code_Span case_token_span = this->peek().span(); | ||
this->skip(); | ||
|
@@ -2806,6 +2842,13 @@ void Parser::parse_and_visit_switch(Parse_Visitor_Base &v) { | |
} | ||
|
||
case Token_Type::kw_default: | ||
if (!is_before_first_switch_case && | ||
prev_token.type != Token_Type::kw_break && | ||
prev_token.type != Token_Type::kw_case && !is_comment_line()) { | ||
this->diag_reporter_->report( | ||
Diag_Explicit_Fallthrough_Comment_In_Switch{.end_of_case = | ||
prev_token.span()}); | ||
} | ||
is_before_first_switch_case = false; | ||
this->skip(); | ||
QLJS_PARSER_UNIMPLEMENTED_IF_NOT_TOKEN(Token_Type::colon); | ||
|
@@ -2818,6 +2861,7 @@ void Parser::parse_and_visit_switch(Parse_Visitor_Base &v) { | |
.unexpected_statement = this->peek().span(), | ||
}); | ||
} | ||
prev_token = this->peek(); | ||
bool parsed_statement = this->parse_and_visit_statement( | ||
v, Parse_Statement_Options{ | ||
.possibly_followed_by_another_statement = true, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -436,6 +436,33 @@ TEST_F(Test_Parse_Warning, warn_on_xor_operation_used_as_exponentiation) { | |
test_parse_and_visit_expression(u8"4 ^ 3"_sv, no_diags); | ||
test_parse_and_visit_expression(u8"(x+2)^a"_sv, no_diags); | ||
} | ||
TEST_F(Test_Parse_Warning, Diag_Explicit_Fallthrough_Comment_In_Switch) { | ||
test_parse_and_visit_statement( | ||
u8"switch(cond1){case 1:\nfoo()\ncase 2:\nbar() //fallthrough\ndefault:}"_sv, // | ||
u8" ^^^ Diag_Explicit_Fallthrough_Comment_In_Switch"_diag); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Must fix: I think the diagnostic is in the wrong place. As a user, I would expect the diagnostic to be between the statement and the following u8"switch(cond1){case 1:\nfoo()\ncase 2:\nbar() //fallthrough\ndefault:}"_sv, //
u8" ` Diag_Explicit_Fallthrough_Comment_In_Switch"_diag); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are right. I went with the suggestion of adding |
||
test_parse_and_visit_statement( | ||
u8"switch(cond1){case 1:\nfoo()\ncase 2:\nlongBarFn()\ndefault:}"_sv, // | ||
u8" ^^^^^^^^^ Diag_Explicit_Fallthrough_Comment_In_Switch"_diag, // | ||
u8" ^^^ Diag_Explicit_Fallthrough_Comment_In_Switch"_diag); | ||
// check for false positive | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
test_parse_and_visit_statement( | ||
u8R"(switch(cond1){ | ||
case 1: | ||
case 2: | ||
default: | ||
})"_sv, | ||
no_diags); | ||
test_parse_and_visit_statement( | ||
u8R"(switch(cond1){ | ||
case 1: | ||
foo() | ||
|
||
//fallthrough | ||
case 2: | ||
bar()//fallthrough | ||
default:})"_sv, | ||
no_diags); | ||
} | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -263,6 +263,7 @@ TEST_F(Test_Parse, asi_between_expression_statement_and_switch_label) { | |
switch (x) { | ||
case a: | ||
f() | ||
//fallthrough | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
case b: | ||
g() | ||
} | ||
|
@@ -278,6 +279,7 @@ TEST_F(Test_Parse, asi_between_expression_statement_and_switch_label) { | |
switch (x) { | ||
case a: | ||
f() | ||
//fallthrough | ||
default: | ||
g() | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"missing fallthrough comment" is good, but if the programmer intended to write a 'break', this diagnostic message is misleading.
Maybe this is a better message:
For reference, here is the message from ESLint's no-fallthrough rule:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good.
Switched to
missing 'break;' or '// fallthrough' comment between statement and 'case'
FYI: Also had to switch diag code to E0427 since E0423 was used recently.