-
-
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 13 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 @@ | ||
# E0427: missing 'break;' or '// fallthrough' comment between statement and '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 |
---|---|---|
|
@@ -2815,7 +2815,26 @@ void Parser::parse_and_visit_switch(Parse_Visitor_Base &v) { | |
|
||
bool keep_going = true; | ||
bool is_before_first_switch_case = true; | ||
Token previous_statement_first_token; | ||
Hash_Set<String8_View> cases; | ||
auto is_valid_end_of_case = [](Token_Type tk) { | ||
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. 👍 Nice helper function. |
||
switch (tk) { | ||
case Token_Type::kw_return: | ||
case Token_Type::kw_continue: | ||
case Token_Type::kw_throw: | ||
case Token_Type::kw_break: | ||
case Token_Type::kw_case: | ||
// Temporarily return true to omit diag with these statments | ||
case Token_Type::kw_if: | ||
case Token_Type::kw_try: | ||
case Token_Type::kw_while: | ||
case Token_Type::kw_do: | ||
case Token_Type::kw_for: | ||
return true; | ||
default: | ||
return false; | ||
} | ||
}; | ||
while (keep_going) { | ||
switch (this->peek().type) { | ||
case Token_Type::right_curly: | ||
|
@@ -2824,6 +2843,13 @@ void Parser::parse_and_visit_switch(Parse_Visitor_Base &v) { | |
break; | ||
|
||
case Token_Type::kw_case: { | ||
if (!is_before_first_switch_case && | ||
!is_valid_end_of_case(previous_statement_first_token.type) && | ||
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. GCC reports a warning because it thinks To fix the warning, I think making |
||
!this->peek().has_leading_comment) { | ||
this->diag_reporter_->report(Diag_Fallthrough_Without_Comment_In_Switch{ | ||
.end_of_case = Source_Code_Span::unit(this->peek().begin)}); | ||
} | ||
previous_statement_first_token = this->peek(); | ||
is_before_first_switch_case = false; | ||
Source_Code_Span case_token_span = this->peek().span(); | ||
this->skip(); | ||
|
@@ -2855,6 +2881,12 @@ void Parser::parse_and_visit_switch(Parse_Visitor_Base &v) { | |
} | ||
|
||
case Token_Type::kw_default: | ||
if (!is_before_first_switch_case && | ||
!is_valid_end_of_case(previous_statement_first_token.type) && | ||
!this->peek().has_leading_comment) { | ||
this->diag_reporter_->report(Diag_Fallthrough_Without_Comment_In_Switch{ | ||
.end_of_case = Source_Code_Span::unit(this->peek().begin)}); | ||
} | ||
is_before_first_switch_case = false; | ||
this->skip(); | ||
QLJS_PARSER_UNIMPLEMENTED_IF_NOT_TOKEN(Token_Type::colon); | ||
|
@@ -2867,6 +2899,7 @@ void Parser::parse_and_visit_switch(Parse_Visitor_Base &v) { | |
.unexpected_statement = this->peek().span(), | ||
}); | ||
} | ||
previous_statement_first_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_Fallthrough_Without_Comment_In_Switch) { | ||
test_parse_and_visit_statement( | ||
u8"switch(cond1){case 1:\nfoo()\ncase 2:\nbar() //fallthrough\ndefault:}"_sv, // | ||
u8" ` Diag_Fallthrough_Without_Comment_In_Switch"_diag); | ||
test_parse_and_visit_statement( | ||
u8"switch(cond1){case 1:\nfoo()\ncase 2:\nlongBarFn()\ndefault:}"_sv, // | ||
u8" ` Diag_Fallthrough_Without_Comment_In_Switch"_diag, // | ||
u8" ` Diag_Fallthrough_Without_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); | ||
} | ||
TEST_F(Test_Parse_Warning, warn_on_unintuitive_precedence_when_using_bitshift) { | ||
test_parse_and_visit_expression( | ||
u8"var1 & 0x01 >> 0x02"_sv, | ||
|
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.
These examples assume that globals
foo
andbar
exist, but they don't. This causes warning E0057 ('use of undeclared variable: foo').To fix this, I suggest writing
console.log
calls instead. (Theconsole
global should exist in these examples.)