Skip to content

Commit 3a01bdd

Browse files
authored
Handle stray commas in class expressions
1 parent 4444beb commit 3a01bdd

File tree

3 files changed

+100
-0
lines changed

3 files changed

+100
-0
lines changed

src/quick-lint-js/error.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,13 @@
223223
QLJS_TRANSLATABLE("commas are not allowed after spread parameter"), \
224224
comma)) \
225225
\
226+
QLJS_ERROR_TYPE( \
227+
error_comma_not_allowed_between_class_methods, "E209", \
228+
{ source_code_span unexpected_comma; }, \
229+
.error( \
230+
QLJS_TRANSLATABLE("commas are not allowed between class methods"), \
231+
unexpected_comma)) \
232+
\
226233
QLJS_ERROR_TYPE( \
227234
error_config_json_syntax_error, "E164", { source_code_span where; }, \
228235
.error(QLJS_TRANSLATABLE("JSON syntax error"), where)) \

src/quick-lint-js/parse.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1776,6 +1776,14 @@ class parser {
17761776
this->skip();
17771777
break;
17781778

1779+
case token_type::comma:
1780+
this->error_reporter_->report(
1781+
error_comma_not_allowed_between_class_methods{
1782+
.unexpected_comma = this->peek().span(),
1783+
});
1784+
this->skip();
1785+
break;
1786+
17791787
default:
17801788
QLJS_PARSER_UNIMPLEMENTED();
17811789
break;

test/test-parse.cpp

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,91 @@ TEST(test_parse, statement_starting_with_invalid_token) {
4848
}
4949
}
5050

51+
TEST(test_parse, comma_not_allowed_between_class_methods) {
52+
{
53+
spy_visitor v;
54+
padded_string code(
55+
u8"class f { constructor() { this._a = false; }, ontext(text) { if (this._a) { process.stdout.write(text);}}}"_sv);
56+
parser p(&code, &v);
57+
EXPECT_TRUE(p.parse_and_visit_statement(v));
58+
EXPECT_THAT(v.errors,
59+
ElementsAre(ERROR_TYPE_FIELD(
60+
error_comma_not_allowed_between_class_methods,
61+
unexpected_comma, offsets_matcher(&code, 44, 45))));
62+
EXPECT_THAT(
63+
v.visits,
64+
ElementsAre("visit_variable_declaration", "visit_enter_class_scope",
65+
"visit_property_declaration", "visit_enter_function_scope",
66+
"visit_enter_function_scope_body",
67+
"visit_exit_function_scope", "visit_property_declaration",
68+
"visit_enter_function_scope", "visit_variable_declaration",
69+
"visit_enter_function_scope_body",
70+
"visit_enter_block_scope", "visit_variable_use",
71+
"visit_variable_use", "visit_exit_block_scope",
72+
"visit_exit_function_scope", "visit_exit_class_scope"));
73+
}
74+
}
75+
76+
TEST(test_parse, commas_not_allowed_between_class_methods) {
77+
{
78+
spy_visitor v;
79+
padded_string code(
80+
u8"class f { ,,, constructor() { this._a = false; },,, ontext(text) { if (this._a) { process.stdout.write(text);}},,,}"_sv);
81+
parser p(&code, &v);
82+
EXPECT_TRUE(p.parse_and_visit_statement(v));
83+
84+
EXPECT_THAT(
85+
v.errors,
86+
ElementsAre(
87+
ERROR_TYPE_FIELD(error_comma_not_allowed_between_class_methods,
88+
unexpected_comma,
89+
offsets_matcher(&code, 10, u8",")),
90+
ERROR_TYPE_FIELD(error_comma_not_allowed_between_class_methods,
91+
unexpected_comma,
92+
offsets_matcher(&code, 11, u8",")),
93+
ERROR_TYPE_FIELD(error_comma_not_allowed_between_class_methods,
94+
unexpected_comma,
95+
offsets_matcher(&code, 12, u8",")),
96+
ERROR_TYPE_FIELD(error_comma_not_allowed_between_class_methods,
97+
unexpected_comma,
98+
offsets_matcher(&code, 48, u8",")),
99+
ERROR_TYPE_FIELD(error_comma_not_allowed_between_class_methods,
100+
unexpected_comma,
101+
offsets_matcher(&code, 49, u8",")),
102+
ERROR_TYPE_FIELD(error_comma_not_allowed_between_class_methods,
103+
unexpected_comma,
104+
offsets_matcher(&code, 50, u8",")),
105+
ERROR_TYPE_FIELD(error_comma_not_allowed_between_class_methods,
106+
unexpected_comma,
107+
offsets_matcher(&code, 111, u8",")),
108+
ERROR_TYPE_FIELD(error_comma_not_allowed_between_class_methods,
109+
unexpected_comma,
110+
offsets_matcher(&code, 112, u8",")),
111+
ERROR_TYPE_FIELD(error_comma_not_allowed_between_class_methods,
112+
unexpected_comma,
113+
offsets_matcher(&code, 113, u8","))));
114+
115+
EXPECT_THAT(v.visits,
116+
ElementsAre("visit_variable_declaration", // class f
117+
"visit_enter_class_scope", // {
118+
"visit_property_declaration", // constructor
119+
"visit_enter_function_scope", // ()
120+
"visit_enter_function_scope_body", // {
121+
"visit_exit_function_scope", // }
122+
"visit_property_declaration", // ontext
123+
"visit_enter_function_scope", // (
124+
"visit_variable_declaration", // text)
125+
"visit_enter_function_scope_body", // { if
126+
"visit_enter_block_scope", // {
127+
"visit_variable_use", // this._a
128+
"visit_variable_use", // text
129+
"visit_exit_block_scope", // }
130+
"visit_exit_function_scope", // }
131+
"visit_exit_class_scope" // }
132+
));
133+
}
134+
}
135+
51136
TEST(test_parse, asi_for_statement_at_right_curly) {
52137
{
53138
spy_visitor v;

0 commit comments

Comments
 (0)