Skip to content

Commit 7e88451

Browse files
committed
feat(parser): syntax errors for decorators appearing in conflicting places (#11482)
fixes #11472 e.g. ``` @dec1 export @dec2 class C {} @dec1 export default @dec2 class {} ```
1 parent 5c32b7c commit 7e88451

File tree

12 files changed

+48
-20
lines changed

12 files changed

+48
-20
lines changed

crates/oxc_codegen/tests/integration/js.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ fn class() {
8484
"export class Test2 {\n@decorator\nproperty: ((arg: any) => any) | undefined;\n}",
8585
"export class Test2 {\n\t@decorator property: ((arg: any) => any) | undefined;\n}\n",
8686
);
87+
test_same("export @dec1 @dec2 class C {}\n");
88+
test_same("export default @dec1 @dec2 class {}\n");
8789
test_minify("class { static [computed] }", "class{static[computed]}");
8890
}
8991

crates/oxc_parser/src/cursor.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,10 +295,23 @@ impl<'a> ParserImpl<'a> {
295295
result
296296
}
297297

298+
/// Consume all decorators.
298299
pub(crate) fn consume_decorators(&mut self) -> Vec<'a, Decorator<'a>> {
299300
self.state.decorators.take_in(self.ast)
300301
}
301302

303+
/// Parse and save all decorators.
304+
pub(crate) fn parse_and_save_decorators(&mut self) {
305+
// For `@dec1 export default @dec2 class {}`
306+
for decorator in &mut self.state.decorators {
307+
self.errors.push(diagnostics::decorators_in_export_and_class(decorator.span));
308+
}
309+
while self.at(Kind::At) {
310+
let decorator = self.parse_decorator();
311+
self.state.decorators.push(decorator);
312+
}
313+
}
314+
302315
pub(crate) fn parse_normal_list<F, T>(&mut self, open: Kind, close: Kind, f: F) -> Vec<'a, T>
303316
where
304317
F: Fn(&mut Self) -> Option<T>,

crates/oxc_parser/src/diagnostics.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,3 +641,8 @@ pub fn index_signature_type_annotation(span: Span) -> OxcDiagnostic {
641641
pub fn unexpected_export(span: Span) -> OxcDiagnostic {
642642
OxcDiagnostic::error("Unexpected export.").with_label(span)
643643
}
644+
645+
#[cold]
646+
pub fn decorators_in_export_and_class(span: Span) -> OxcDiagnostic {
647+
OxcDiagnostic::error("Decorators may not appear after 'export' or 'export default' if they also appear before 'export'.").with_label(span)
648+
}

crates/oxc_parser/src/js/expression.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ impl<'a> ParserImpl<'a> {
159159
let span = self.start_span();
160160

161161
if self.at(Kind::At) {
162-
self.eat_decorators();
162+
self.parse_and_save_decorators();
163163
}
164164

165165
// FunctionExpression, GeneratorExpression

crates/oxc_parser/src/js/function.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ impl<'a> ParserImpl<'a> {
8282
fn parse_formal_parameter(&mut self) -> FormalParameter<'a> {
8383
let span = self.start_span();
8484
if self.at(Kind::At) {
85-
self.eat_decorators();
85+
self.parse_and_save_decorators();
8686
}
8787
let decorators = self.consume_decorators();
8888
let modifiers = self.parse_parameter_modifiers();

crates/oxc_parser/src/js/module.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,7 @@ impl<'a> ParserImpl<'a> {
472472
// For tc39/proposal-decorators
473473
// For more information, please refer to <https://babeljs.io/docs/babel-plugin-proposal-decorators#decoratorsbeforeexport>
474474
if self.at(Kind::At) {
475-
self.eat_decorators();
475+
self.parse_and_save_decorators();
476476
}
477477
let reserved_ctx = self.ctx;
478478
let modifiers =
@@ -510,7 +510,7 @@ impl<'a> ParserImpl<'a> {
510510
// For tc39/proposal-decorators
511511
// For more information, please refer to <https://babeljs.io/docs/babel-plugin-proposal-decorators#decoratorsbeforeexport>
512512
if self.at(Kind::At) {
513-
self.eat_decorators();
513+
self.parse_and_save_decorators();
514514
}
515515
let declaration = match self.cur_kind() {
516516
Kind::Class => ExportDefaultDeclarationKind::ClassDeclaration(

crates/oxc_parser/src/js/statement.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ impl<'a> ParserImpl<'a> {
8787
self.lexer.trivia_builder.previous_token_has_no_side_effects_comment();
8888

8989
if self.at(Kind::At) {
90-
self.eat_decorators();
90+
self.parse_and_save_decorators();
9191
}
9292

9393
let mut stmt = match self.cur_kind() {

crates/oxc_parser/src/modifiers.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ impl<'a> ParserImpl<'a> {
373373

374374
// parse leading decorators
375375
if allow_decorators && self.at(Kind::At) {
376-
self.eat_decorators();
376+
self.parse_and_save_decorators();
377377
}
378378

379379
// parse leading modifiers
@@ -393,7 +393,7 @@ impl<'a> ParserImpl<'a> {
393393

394394
// parse trailing decorators, but only if we parsed any leading modifiers
395395
if allow_decorators && has_leading_modifier && self.at(Kind::At) {
396-
self.eat_decorators();
396+
self.parse_and_save_decorators();
397397
}
398398

399399
// parse trailing modifiers, but only if we parsed any trailing decorators

crates/oxc_parser/src/state.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ impl<'a> ParserState<'a> {
2424
pub fn new(allocator: &'a Allocator) -> Self {
2525
Self {
2626
not_parenthesized_arrow: FxHashSet::default(),
27-
decorators: ArenaVec::new_in(allocator),
27+
// Create a large enough buffer for most cases.
28+
decorators: ArenaVec::with_capacity_in(4, allocator),
2829
cover_initialized_name: FxHashMap::default(),
2930
trailing_commas: FxHashMap::default(),
3031
}

crates/oxc_parser/src/ts/statement.rs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,7 @@ impl<'a> ParserImpl<'a> {
480480
let span = self.start_span();
481481
self.parse_class_element_modifiers(true);
482482
if self.at(Kind::At) {
483-
self.eat_decorators();
483+
self.parse_and_save_decorators();
484484
}
485485

486486
let this_span = self.start_span();
@@ -491,16 +491,6 @@ impl<'a> ParserImpl<'a> {
491491
self.ast.ts_this_parameter(self.end_span(span), this, type_annotation)
492492
}
493493

494-
pub(crate) fn eat_decorators(&mut self) {
495-
let mut decorators = self.ast.vec();
496-
while self.at(Kind::At) {
497-
let decorator = self.parse_decorator();
498-
decorators.push(decorator);
499-
}
500-
501-
self.state.decorators = decorators;
502-
}
503-
504494
pub(crate) fn at_start_of_ts_declaration(&mut self) -> bool {
505495
self.lookahead(Self::at_start_of_ts_declaration_worker)
506496
}

0 commit comments

Comments
 (0)